Skip to content

Commit d557c44

Browse files
HDFS-16463. Make dirent cross platform compatible (#4370)
* jnihelper.c in HDFS native client uses dirent.h. This header file isn't available on Windows. * This PR provides a cross platform compatible implementation for dirent under the XPlatform library.
1 parent 6e11c94 commit d557c44

15 files changed

Lines changed: 735 additions & 3 deletions

File tree

hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs-tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ add_library(native_mini_dfs
3535
../libhdfs/jni_helper.c
3636
${OS_DIR}/mutexes.c
3737
${OS_DIR}/thread_local_storage.c
38+
$<TARGET_OBJECTS:x_platform_obj>
39+
$<TARGET_OBJECTS:x_platform_obj_c_api>
3840
)
3941

4042
add_executable(test_native_mini_dfs test_native_mini_dfs.c)

hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ hadoop_add_dual_library(hdfs
3939
jclasses.c
4040
${OS_DIR}/mutexes.c
4141
${OS_DIR}/thread_local_storage.c
42+
$<TARGET_OBJECTS:x_platform_obj>
43+
$<TARGET_OBJECTS:x_platform_obj_c_api>
4244
)
4345
if(NEED_LINK_DL)
4446
set(LIB_DL dl)

hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/jni_helper.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
#include "platform.h"
2424
#include "os/mutexes.h"
2525
#include "os/thread_local_storage.h"
26+
#include "x-platform/c-api/dirent.h"
2627
#include "x-platform/types.h"
2728

2829
#include <errno.h>
29-
#include <dirent.h>
3030
#include <stdio.h>
3131
#include <string.h>
3232

hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/x-platform/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ else()
2222
set(SYSCALL_SRC syscall_linux.cc)
2323
endif()
2424

25-
add_library(x_platform_obj OBJECT ${SYSCALL_SRC} utils.cc)
26-
add_library(x_platform_obj_c_api OBJECT $<TARGET_OBJECTS:x_platform_obj> c-api/syscall.cc)
25+
add_library(x_platform_obj OBJECT ${SYSCALL_SRC} utils.cc dirent.cc)
26+
add_library(x_platform_obj_c_api OBJECT $<TARGET_OBJECTS:x_platform_obj> c-api/syscall.cc c-api/dirent.cc)
27+
target_compile_definitions(x_platform_obj_c_api PRIVATE USE_X_PLATFORM_DIRENT)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#ifndef NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_C_API_CORE_DIRENT_H
20+
#define NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_C_API_CORE_DIRENT_H
21+
22+
/**
23+
* DIR struct holds the pointer to XPlatform::Dirent instance. Since this will
24+
* be used in C, we can't hold the pointer to XPlatform::Dirent. We're working
25+
* around this by using a void pointer and casting it to XPlatform::Dirent when
26+
* needed in C++.
27+
*/
28+
typedef struct DIR {
29+
void *x_platform_dirent_ptr;
30+
} DIR;
31+
32+
/**
33+
* dirent struct contains the name of the file/folder while iterating through
34+
* the directory's children.
35+
*/
36+
struct dirent {
37+
char d_name[256];
38+
};
39+
40+
/**
41+
* Opens a directory for iteration. Internally, it instantiates DIR struct for
42+
* the given path. closedir must be called on the returned pointer to DIR struct
43+
* when done.
44+
*
45+
* @param dir_path The path to the directory to iterate through.
46+
* @return A pointer to the DIR struct.
47+
*/
48+
DIR *opendir(const char *dir_path);
49+
50+
/**
51+
* For iterating through the children of the directory pointed to by the DIR
52+
* struct pointer.
53+
*
54+
* @param dir The pointer to the DIR struct.
55+
* @return A pointer to dirent struct containing the name of the current child
56+
* file/folder.
57+
*/
58+
struct dirent *readdir(DIR *dir);
59+
60+
/**
61+
* De-allocates the XPlatform::Dirent instance pointed to by the DIR pointer.
62+
*
63+
* @param dir The pointer to DIR struct to close.
64+
* @return 0 if successful.
65+
*/
66+
int closedir(DIR *dir);
67+
68+
#endif
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include <algorithm>
20+
#include <cerrno>
21+
#include <iostream>
22+
#include <iterator>
23+
#include <system_error>
24+
#include <variant>
25+
26+
#include "x-platform/c-api/dirent.h"
27+
#include "x-platform/dirent.h"
28+
29+
DIR *opendir(const char *dir_path) {
30+
const auto dir = new DIR;
31+
dir->x_platform_dirent_ptr = new XPlatform::Dirent(dir_path);
32+
return dir;
33+
}
34+
35+
struct dirent *readdir(DIR *dir) {
36+
/*
37+
* We will use a static variable to hold the dirent, so that we align with the
38+
* readdir's implementation in dirent.h header file in Linux.
39+
*/
40+
static struct dirent static_dir_entry;
41+
42+
// Get the XPlatform::Dirent instance and move the iterator.
43+
const auto x_platform_dirent =
44+
static_cast<XPlatform::Dirent *>(dir->x_platform_dirent_ptr);
45+
const auto dir_entry = x_platform_dirent->NextFile();
46+
47+
// End of iteration.
48+
if (std::holds_alternative<std::monostate>(dir_entry)) {
49+
return nullptr;
50+
}
51+
52+
// Error in iteration.
53+
if (std::holds_alternative<std::error_code>(dir_entry)) {
54+
const auto err = std::get<std::error_code>(dir_entry);
55+
errno = err.value();
56+
57+
#ifdef X_PLATFORM_C_API_DIRENT_DEBUG
58+
std::cerr << "Error in listing directory: " << err.message() << std::endl;
59+
#endif
60+
61+
return nullptr;
62+
}
63+
64+
// Return the current child file/folder's name.
65+
if (std::holds_alternative<std::filesystem::directory_entry>(dir_entry)) {
66+
const auto entry = std::get<std::filesystem::directory_entry>(dir_entry);
67+
const auto filename = entry.path().filename().string();
68+
69+
// The file name's length shouldn't exceed 256.
70+
if (filename.length() >= 256) {
71+
errno = 1;
72+
return nullptr;
73+
}
74+
75+
std::fill(std::begin(static_dir_entry.d_name),
76+
std::end(static_dir_entry.d_name), '\0');
77+
std::copy(filename.begin(), filename.end(),
78+
std::begin(static_dir_entry.d_name));
79+
}
80+
return &static_dir_entry;
81+
}
82+
83+
int closedir(DIR *dir) {
84+
const auto x_platform_dirent =
85+
static_cast<XPlatform::Dirent *>(dir->x_platform_dirent_ptr);
86+
delete x_platform_dirent;
87+
delete dir;
88+
89+
// We can't use the void return type for closedir since we want to align the
90+
// closedir method's signature in dirent.h header file in Linux.
91+
return 0;
92+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#ifndef NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_C_API_DIRENT_H
20+
#define NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_C_API_DIRENT_H
21+
22+
#if !(defined(WIN32) || defined(USE_X_PLATFORM_DIRENT))
23+
24+
/*
25+
* For non-Windows environments, we use the dirent.h header itself.
26+
*/
27+
#include <dirent.h>
28+
29+
#else
30+
31+
/*
32+
* If it's a Windows environment or if the macro USE_X_PLATFORM_DIRENT is
33+
* defined, we switch to using dirent from the XPlatform library.
34+
*/
35+
#include "x-platform/c-api/extern/dirent.h"
36+
37+
#endif
38+
39+
#endif
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#ifndef NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_C_API_EXTERN_DIRENT_H
20+
#define NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_C_API_EXTERN_DIRENT_H
21+
22+
/*
23+
* We will use extern "C" only on Windows.
24+
*/
25+
#if defined(WIN32) && defined(__cplusplus)
26+
extern "C" {
27+
#endif
28+
29+
#include "x-platform/c-api/core/dirent.h"
30+
31+
#if defined(WIN32) && defined(__cplusplus)
32+
}
33+
#endif
34+
35+
#endif
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include <filesystem>
20+
#include <system_error>
21+
#include <variant>
22+
23+
#include "dirent.h"
24+
25+
std::variant<std::monostate, std::filesystem::directory_entry, std::error_code>
26+
XPlatform::Dirent::NextFile() {
27+
if (dir_it_err_) {
28+
return dir_it_err_;
29+
}
30+
31+
if (dir_it_ == std::filesystem::end(dir_it_)) {
32+
return std::monostate();
33+
}
34+
35+
const std::filesystem::directory_entry dir_entry = *dir_it_;
36+
dir_it_ = dir_it_.increment(dir_it_err_);
37+
return dir_entry;
38+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#ifndef NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_DIRENT
20+
#define NATIVE_LIBHDFSPP_LIB_CROSS_PLATFORM_DIRENT
21+
22+
#include <filesystem>
23+
#include <string>
24+
#include <system_error>
25+
#include <variant>
26+
27+
namespace XPlatform {
28+
/**
29+
* {@class XPlatform::Dirent} provides the functionality to perform a one-time
30+
* iteration per {@link XPlatform::Dirent} through the child files or folders
31+
* under a given path.
32+
*/
33+
class Dirent {
34+
public:
35+
Dirent(const std::string &path)
36+
: dir_it_{std::filesystem::path{path}, dir_it_err_} {}
37+
38+
// Abiding to the Rule of 5
39+
Dirent(const Dirent &) = default;
40+
Dirent(Dirent &&) = default;
41+
Dirent &operator=(const Dirent &) = default;
42+
Dirent &operator=(Dirent &&) = default;
43+
~Dirent() = default;
44+
45+
/**
46+
* Advances the iterator {@link XPlatform::Dirent#dir_it_} to the next file in
47+
* the given path.
48+
*
49+
* @return An {@link std::variant} comprising of any one of the following
50+
* types:
51+
* 1. {@link std::monostate} which indicates the end of iteration of all the
52+
* files in the given path.
53+
* 2. {@link std::filesystem::directory_entry} which is the directory entry of
54+
* the current file.
55+
* 3. {@link std::error_code} which corresponds to the error in retrieving the
56+
* file.
57+
*/
58+
std::variant<std::monostate, std::filesystem::directory_entry,
59+
std::error_code>
60+
NextFile();
61+
62+
private:
63+
/**
64+
* Indicates the error corresponding to the most recent invocation of
65+
* directory iteration by {@link XPlatform::Dirent#dir_it_}.
66+
*/
67+
std::error_code dir_it_err_{};
68+
69+
/**
70+
* The iterator used for iterating through the files or folders under the
71+
* given path.
72+
*/
73+
std::filesystem::directory_iterator dir_it_;
74+
};
75+
} // namespace XPlatform
76+
77+
#endif

0 commit comments

Comments
 (0)