[libc-commits] [libc] 35ea84a - [libc] Add dirent.h functions opendir, readdir, closedir and dirfd.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Mon Jul 25 13:23:46 PDT 2022
Author: Siva Chandra Reddy
Date: 2022-07-25T20:23:25Z
New Revision: 35ea84ad6ae302ba4ac1f02a6fed9f0af5815ece
URL: https://github.com/llvm/llvm-project/commit/35ea84ad6ae302ba4ac1f02a6fed9f0af5815ece
DIFF: https://github.com/llvm/llvm-project/commit/35ea84ad6ae302ba4ac1f02a6fed9f0af5815ece.diff
LOG: [libc] Add dirent.h functions opendir, readdir, closedir and dirfd.
Reviewed By: lntue
Differential Revision: https://reviews.llvm.org/D130459
Added:
libc/include/dirent.h.def
libc/include/llvm-libc-types/DIR.h
libc/include/llvm-libc-types/ino_t.h
libc/include/llvm-libc-types/struct_dirent.h
libc/src/__support/File/dir.cpp
libc/src/__support/File/dir.h
libc/src/__support/File/linux_dir.cpp
libc/src/dirent/CMakeLists.txt
libc/src/dirent/closedir.cpp
libc/src/dirent/closedir.h
libc/src/dirent/dirfd.cpp
libc/src/dirent/dirfd.h
libc/src/dirent/opendir.cpp
libc/src/dirent/opendir.h
libc/src/dirent/readdir.cpp
libc/src/dirent/readdir.h
libc/test/src/dirent/CMakeLists.txt
libc/test/src/dirent/dirent_test.cpp
libc/test/src/dirent/testdata/CMakeLists.txt
Modified:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/include/CMakeLists.txt
libc/include/llvm-libc-types/CMakeLists.txt
libc/spec/posix.td
libc/src/CMakeLists.txt
libc/src/__support/File/CMakeLists.txt
libc/test/src/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 10b23d31fa39..e7fe144b622d 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -255,6 +255,14 @@ def PThreadAPI : PublicAPI<"pthread.h"> {
];
}
+def DirentAPI : PublicAPI<"dirent.h"> {
+ let Types = [
+ "ino_t",
+ "DIR",
+ "struct dirent",
+ ];
+}
+
def UniStdAPI : PublicAPI<"unistd.h"> {
let Types = ["off_t", "size_t", "ssize_t"];
}
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 4fd16ef0871b..686d4de0a17e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -227,6 +227,12 @@ if(LLVM_LIBC_FULL_BUILD)
# assert.h entrypoints
# libc.src.assert.__assert_fail
+ # dirent.h entrypoints
+ libc.src.dirent.closedir
+ libc.src.dirent.dirfd
+ libc.src.dirent.opendir
+ libc.src.dirent.readdir
+
# pthread.h entrypoints
libc.src.pthread.pthread_attr_destroy
libc.src.pthread.pthread_attr_init
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index afd4972120d4..229abe25d23b 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -15,6 +15,17 @@ add_gen_header(
.llvm_libc_common_h
)
+add_gen_header(
+ dirent
+ DEF_FILE dirent.h.def
+ GEN_HDR dirent.h
+ DEPENDS
+ .llvm_libc_common_h
+ .llvm-libc-types.DIR
+ .llvm-libc-types.ino_t
+ .llvm-libc-types.struct_dirent
+)
+
add_gen_header(
fcntl
DEF_FILE fcntl.h.def
diff --git a/libc/include/dirent.h.def b/libc/include/dirent.h.def
new file mode 100644
index 000000000000..3de8b1c6713f
--- /dev/null
+++ b/libc/include/dirent.h.def
@@ -0,0 +1,16 @@
+//===-- POSIX header dirent.h ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_DIRENT_H
+#define LLVM_LIBC_DIRENT_H
+
+#include <__llvm-libc-common.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_DIRENT_H
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 9ca82ad81128..c0e389eb355e 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -9,6 +9,7 @@ add_header(__thread_type HDR __thread_type.h)
add_header(cnd_t HDR cnd_t.h)
add_header(cookie_io_functions_t HDR cookie_io_functions_t.h DEPENDS .off64_t)
add_header(double_t HDR double_t.h)
+add_header(DIR HDR DIR.h)
add_header(div_t HDR div_t.h)
add_header(ldiv_t HDR ldiv_t.h)
add_header(lldiv_t HDR lldiv_t.h)
@@ -17,6 +18,7 @@ add_header(fenv_t HDR fenv_t.h)
add_header(fexcept_t HDR fexcept_t.h)
add_header(float_t HDR float_t.h)
add_header(imaxdiv_t HDR imaxdiv_t.h)
+add_header(ino_t HDR ino_t.h)
add_header(mode_t HDR mode_t.h)
add_header(mtx_t HDR mtx_t.h DEPENDS .__futex_word .__mutex_type)
add_header(off_t HDR off_t.h)
@@ -28,6 +30,7 @@ add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type)
add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h)
add_header(size_t HDR size_t.h)
add_header(ssize_t HDR ssize_t.h)
+add_header(struct_dirent HDR struct_dirent.h DEPENDS .ino_t .off_t)
add_header(struct_sigaction HDR struct_sigaction.h)
add_header(struct_tm HDR struct_tm.h)
add_header(thrd_start_t HDR thrd_start_t.h)
diff --git a/libc/include/llvm-libc-types/DIR.h b/libc/include/llvm-libc-types/DIR.h
new file mode 100644
index 000000000000..0a2cf27d2485
--- /dev/null
+++ b/libc/include/llvm-libc-types/DIR.h
@@ -0,0 +1,14 @@
+//===-- Definition of the type DIR ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_DIR_H__
+#define __LLVM_LIBC_TYPES_DIR_H__
+
+typedef struct DIR DIR;
+
+#endif // __LLVM_LIBC_TYPES_DIR_H__
diff --git a/libc/include/llvm-libc-types/ino_t.h b/libc/include/llvm-libc-types/ino_t.h
new file mode 100644
index 000000000000..3531dd3829e9
--- /dev/null
+++ b/libc/include/llvm-libc-types/ino_t.h
@@ -0,0 +1,14 @@
+//===-- Definition of type ino_t ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_INO_T_H__
+#define __LLVM_LIBC_TYPES_INO_T_H__
+
+typedef unsigned long ino_t;
+
+#endif // __LLVM_LIBC_TYPES_INO_T_H__
diff --git a/libc/include/llvm-libc-types/struct_dirent.h b/libc/include/llvm-libc-types/struct_dirent.h
new file mode 100644
index 000000000000..44bda4caae45
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_dirent.h
@@ -0,0 +1,28 @@
+//===-- Definition of type struct dirent ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_STRUCT_DIRENT_H__
+#define __LLVM_LIBC_TYPES_STRUCT_DIRENT_H__
+
+#include <llvm-libc-types/ino_t.h>
+#include <llvm-libc-types/off_t.h>
+
+struct dirent {
+ ino_t d_ino;
+#ifdef __unix__
+ off_t d_off;
+ unsigned short d_reclen;
+#endif
+ // The user code should use strlen to determine actual the size of d_name.
+ // Likewise, it is incorrect and prohibited by the POSIX standard to detemine
+ // the size of struct dirent type using sizeof. The size should be got using
+ // a
diff erent method, for example, from the d_reclen field on Linux.
+ char d_name[1];
+};
+
+#endif // __LLVM_LIBC_TYPES_STRUCT_DIRENT_H__
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 2840b5d91142..4a8d08d9f7eb 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -12,6 +12,15 @@ def ConstRestrictedStructSigactionPtr : ConstType<RestrictedStructSigactionPtr>;
def PThreadStartT : NamedType<"__pthread_start_t">;
+def InoT : NamedType<"ino_t">;
+def DIR : NamedType<"DIR">;
+def DIRPtr : PtrType<DIR>;
+def DIRRestrictedPtr : RestrictedPtrType<DIR>;
+def StructDirent : NamedType<"struct dirent">;
+def StructDirentPtr : PtrType<StructDirent>;
+def StructDirentPtrPtr : PtrType<StructDirentPtr>;
+def ConstStructDirentPtrPtr : ConstType<StructDirentPtrPtr>;
+
def POSIX : StandardSpec<"POSIX"> {
PtrType CharPtr = PtrType<CharType>;
RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
@@ -578,8 +587,48 @@ def POSIX : StandardSpec<"POSIX"> {
]
>;
+ HeaderSpec Dirent = HeaderSpec<
+ "dirent.h",
+ [], // Macros
+ [InoT, StructDirent, DIR], // Types
+ [], // Enumerations
+ [
+ FunctionSpec<
+ "alphasort",
+ RetValSpec<IntType>,
+ [ArgSpec<ConstStructDirentPtrPtr>, ArgSpec<ConstStructDirentPtrPtr>]
+ >,
+ FunctionSpec<
+ "closedir",
+ RetValSpec<IntType>,
+ [ArgSpec<DIRPtr>]
+ >,
+ FunctionSpec<
+ "dirfd",
+ RetValSpec<IntType>,
+ [ArgSpec<DIRPtr>]
+ >,
+ FunctionSpec<
+ "fdopendir",
+ RetValSpec<DIRPtr>,
+ [ArgSpec<IntType>]
+ >,
+ FunctionSpec<
+ "opendir",
+ RetValSpec<DIRPtr>,
+ [ArgSpec<ConstCharPtr>]
+ >,
+ FunctionSpec<
+ "readdir",
+ RetValSpec<StructDirentPtr>,
+ [ArgSpec<DIRPtr>]
+ >,
+ ]
+ >;
+
let Headers = [
CType,
+ Dirent,
Errno,
FCntl,
PThread,
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index a53e382fcaeb..19664ec13844 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -9,6 +9,7 @@ add_subdirectory(string)
add_subdirectory(stdlib)
if(${LIBC_TARGET_OS} STREQUAL "linux")
+ add_subdirectory(dirent)
add_subdirectory(fcntl)
add_subdirectory(pthread)
add_subdirectory(sys)
diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt
index 5870ebb556a6..b6ffcf9ae45f 100644
--- a/libc/src/__support/File/CMakeLists.txt
+++ b/libc/src/__support/File/CMakeLists.txt
@@ -16,6 +16,17 @@ add_object_library(
libc.src.errno.errno
)
+add_object_library(
+ dir
+ SRCS
+ dir.cpp
+ HDRS
+ dir.h
+ DEPENDS
+ libc.src.__support.CPP.array_ref
+ libc.src.__support.threads.mutex
+)
+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}_file.cpp)
add_object_library(
platform_file
@@ -32,3 +43,17 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}_file.cpp)
)
endif()
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}_dir.cpp)
+ add_object_library(
+ platform_dir
+ SRCS
+ ${LIBC_TARGET_OS}_dir.cpp
+ DEPENDS
+ .dir
+ libc.include.errno
+ libc.include.fcntl
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+ )
+endif()
diff --git a/libc/src/__support/File/dir.cpp b/libc/src/__support/File/dir.cpp
new file mode 100644
index 000000000000..0c0cc6551136
--- /dev/null
+++ b/libc/src/__support/File/dir.cpp
@@ -0,0 +1,59 @@
+//===--- Implementation of a platform independent Dir data structure ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "dir.h"
+
+#include <stdlib.h>
+
+namespace __llvm_libc {
+
+Dir *Dir::open(const char *path) {
+ int fd = platform_opendir(path);
+ if (fd < 0)
+ return nullptr;
+
+ Dir *dir = reinterpret_cast<Dir *>(malloc(sizeof(Dir)));
+ dir->fd = fd;
+ dir->readptr = 0;
+ dir->fillsize = 0;
+ Mutex::init(&dir->mutex, false, false, false);
+
+ return dir;
+}
+
+struct ::dirent *Dir::read() {
+ MutexLock lock(&mutex);
+ if (readptr >= fillsize) {
+ fillsize = platform_fetch_dirents(
+ fd, cpp::MutableArrayRef<uint8_t>(buffer, BUFSIZE));
+ if (fillsize == 0)
+ return nullptr;
+ readptr = 0;
+ }
+ struct ::dirent *d = reinterpret_cast<struct ::dirent *>(buffer + readptr);
+#ifdef __unix__
+ // The d_reclen field is available on Linux but not required by POSIX.
+ readptr += d->d_reclen;
+#else
+ // Other platforms have to implement how the read pointer is to be updated.
+#error "DIR read pointer update is missing."
+#endif
+ return d;
+}
+
+int Dir::close() {
+ {
+ MutexLock lock(&mutex);
+ if (!platform_closedir(fd))
+ return -1;
+ }
+ free(this);
+ return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/__support/File/dir.h b/libc/src/__support/File/dir.h
new file mode 100644
index 000000000000..b16791af33cf
--- /dev/null
+++ b/libc/src/__support/File/dir.h
@@ -0,0 +1,70 @@
+//===--- A platform independent Dir class ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SUPPORT_FILE_DIR_H
+#define LLVM_LIBC_SRC_SUPPORT_FILE_DIR_H
+
+#include "src/__support/CPP/ArrayRef.h"
+#include "src/__support/threads/mutex.h"
+
+#include <dirent.h>
+#include <stdlib.h>
+
+namespace __llvm_libc {
+
+// Platform specific function which will open the directory |name|
+// and return its file descriptor. Upon failure, this function sets the errno
+// value as suitable.
+int platform_opendir(const char *name);
+
+// Platform specific function which will close the directory with
+// file descriptor |fd|. Returns true on success, false on failure.
+bool platform_closedir(int fd);
+
+// Platform specific function which will fetch dirents in to buffer.
+// Returns the number of bytes written into buffer
+size_t platform_fetch_dirents(int fd, cpp::MutableArrayRef<uint8_t> buffer);
+
+// This class is designed to allow implementation of the POSIX dirent.h API.
+// By itself, it is platform independent but calls platform specific
+// functions to perform OS operations.
+class Dir {
+ static constexpr size_t BUFSIZE = 1024;
+ int fd;
+ size_t readptr = 0; // The current read pointer.
+ size_t fillsize = 0; // The number of valid bytes availabe in the buffer.
+
+ // This is a buffer of struct dirent values which will be fetched
+ // from the OS. Since the d_name of struct dirent can be of a variable
+ // size, we store the data in a byte array.
+ uint8_t buffer[BUFSIZE];
+
+ Mutex mutex;
+
+public:
+ // A directory is to be opened by the static method open and closed
+ // by the close method. So, all constructors and destructor are declared
+ // as deleted.
+ Dir() = delete;
+ Dir(const Dir &) = delete;
+ ~Dir() = delete;
+
+ Dir &operator=(const Dir &) = delete;
+
+ static Dir *open(const char *path);
+
+ struct ::dirent *read();
+
+ int close();
+
+ int getfd() { return fd; }
+};
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SUPPORT_FILE_DIR_H
diff --git a/libc/src/__support/File/linux_dir.cpp b/libc/src/__support/File/linux_dir.cpp
new file mode 100644
index 000000000000..bde75c7c12c1
--- /dev/null
+++ b/libc/src/__support/File/linux_dir.cpp
@@ -0,0 +1,56 @@
+//===--- Linux implementation of the Dir helpers --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "dir.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+
+#include <errno.h>
+#include <fcntl.h> // For open flags
+#include <sys/syscall.h> // For syscall numbers
+
+namespace __llvm_libc {
+
+int platform_opendir(const char *name) {
+ int open_flags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
+#ifdef SYS_open
+ int fd = __llvm_libc::syscall(SYS_open, name, open_flags);
+#elif defined(SYS_openat)
+ int fd = __llvm_libc::syscall(SYS_openat, AT_FDCWD, name, open_flags);
+#else
+#error \
+ "SYS_open and SYS_openat syscalls not available to perform an open operation."
+#endif
+
+ if (fd < 0) {
+ errno = -fd;
+ return -1;
+ }
+ return fd;
+}
+
+size_t platform_fetch_dirents(int fd, cpp::MutableArrayRef<uint8_t> buffer) {
+ long size =
+ __llvm_libc::syscall(SYS_getdents, fd, buffer.data(), buffer.size());
+ if (size < 0) {
+ errno = -size;
+ return 0;
+ }
+ return size;
+}
+
+bool platform_closedir(int fd) {
+ long ret = __llvm_libc::syscall(SYS_close, fd);
+ if (ret < 0) {
+ errno = -ret;
+ return false;
+ }
+ return true;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
new file mode 100644
index 000000000000..e4227a63747c
--- /dev/null
+++ b/libc/src/dirent/CMakeLists.txt
@@ -0,0 +1,47 @@
+add_entrypoint_object(
+ opendir
+ SRCS
+ opendir.cpp
+ HDRS
+ opendir.h
+ DEPENDS
+ libc.include.dirent
+ libc.src.__support.File.dir
+ libc.src.__support.File.platform_dir
+)
+
+add_entrypoint_object(
+ dirfd
+ SRCS
+ dirfd.cpp
+ HDRS
+ dirfd.h
+ DEPENDS
+ libc.include.dirent
+ libc.src.__support.File.dir
+ libc.src.__support.File.platform_dir
+)
+
+add_entrypoint_object(
+ closedir
+ SRCS
+ closedir.cpp
+ HDRS
+ closedir.h
+ DEPENDS
+ libc.include.dirent
+ libc.src.__support.File.dir
+ libc.src.__support.File.platform_dir
+)
+
+add_entrypoint_object(
+ readdir
+ SRCS
+ readdir.cpp
+ HDRS
+ readdir.h
+ DEPENDS
+ libc.include.dirent
+ libc.src.__support.File.dir
+ libc.src.__support.File.platform_dir
+)
diff --git a/libc/src/dirent/closedir.cpp b/libc/src/dirent/closedir.cpp
new file mode 100644
index 000000000000..f4925db00c7a
--- /dev/null
+++ b/libc/src/dirent/closedir.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of closedir ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "closedir.h"
+
+#include "src/__support/File/dir.h"
+#include "src/__support/common.h"
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, closedir, (::DIR * dir)) {
+ auto *d = reinterpret_cast<__llvm_libc::Dir *>(dir);
+ return d->close();
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/dirent/closedir.h b/libc/src/dirent/closedir.h
new file mode 100644
index 000000000000..c5ee7ae59f1e
--- /dev/null
+++ b/libc/src/dirent/closedir.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of closedir -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_DIRENT_CLOSEDIR_H
+#define LLVM_LIBC_SRC_DIRENT_CLOSEDIR_H
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+int closedir(::DIR *dir);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_DIRENT_CLOSEDIR_H
diff --git a/libc/src/dirent/dirfd.cpp b/libc/src/dirent/dirfd.cpp
new file mode 100644
index 000000000000..214c5c585203
--- /dev/null
+++ b/libc/src/dirent/dirfd.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of dirfd -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "dirfd.h"
+
+#include "src/__support/File/dir.h"
+#include "src/__support/common.h"
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, dirfd, (::DIR * dir)) {
+ auto *d = reinterpret_cast<__llvm_libc::Dir *>(dir);
+ return d->getfd();
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/dirent/dirfd.h b/libc/src/dirent/dirfd.h
new file mode 100644
index 000000000000..8590e3e159d3
--- /dev/null
+++ b/libc/src/dirent/dirfd.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of dirfd --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_DIRENT_DIRFD_H
+#define LLVM_LIBC_SRC_DIRENT_DIRFD_H
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+int dirfd(::DIR *dir);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_DIRENT_DIRFD_H
diff --git a/libc/src/dirent/opendir.cpp b/libc/src/dirent/opendir.cpp
new file mode 100644
index 000000000000..0222944de453
--- /dev/null
+++ b/libc/src/dirent/opendir.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of opendir -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "opendir.h"
+
+#include "src/__support/File/dir.h"
+#include "src/__support/common.h"
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(::DIR *, opendir, (const char *name)) {
+ return reinterpret_cast<::DIR *>(Dir::open(name));
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/dirent/opendir.h b/libc/src/dirent/opendir.h
new file mode 100644
index 000000000000..e183587999a9
--- /dev/null
+++ b/libc/src/dirent/opendir.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of opendir ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_DIRENT_OPENDIR_H
+#define LLVM_LIBC_SRC_DIRENT_OPENDIR_H
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+::DIR *opendir(const char *name);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_DIRENT_OPENDIR_H
diff --git a/libc/src/dirent/readdir.cpp b/libc/src/dirent/readdir.cpp
new file mode 100644
index 000000000000..968985061d5d
--- /dev/null
+++ b/libc/src/dirent/readdir.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of readdir -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "readdir.h"
+
+#include "src/__support/File/dir.h"
+#include "src/__support/common.h"
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(struct ::dirent *, readdir, (::DIR * dir)) {
+ auto *d = reinterpret_cast<__llvm_libc::Dir *>(dir);
+ return d->read();
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/dirent/readdir.h b/libc/src/dirent/readdir.h
new file mode 100644
index 000000000000..b78150bf6489
--- /dev/null
+++ b/libc/src/dirent/readdir.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of readdir ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_DIRENT_READDIR_H
+#define LLVM_LIBC_SRC_DIRENT_READDIR_H
+
+#include <dirent.h>
+
+namespace __llvm_libc {
+
+struct ::dirent *readdir(DIR *dir);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_DIRENT_READDIR_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 3d96014182ff..03aac2669092 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -45,6 +45,7 @@ if(NOT LLVM_LIBC_FULL_BUILD)
return()
endif()
+add_subdirectory(dirent)
# The signal API is currently disabled as signal.h is incorrect.
# since assert uses the signal API, we disable assert also.
# add_subdirectory(assert)
diff --git a/libc/test/src/dirent/CMakeLists.txt b/libc/test/src/dirent/CMakeLists.txt
new file mode 100644
index 000000000000..cb96dd510265
--- /dev/null
+++ b/libc/test/src/dirent/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_subdirectory(testdata)
+add_libc_testsuite(libc_dirent_unittests)
+
+add_libc_unittest(
+ dirent_test
+ SUITE
+ libc_dirent_unittests
+ SRCS
+ dirent_test.cpp
+ DEPENDS
+ libc.include.dirent
+ libc.src.__support.CPP.string_view
+ libc.src.dirent.closedir
+ libc.src.dirent.dirfd
+ libc.src.dirent.opendir
+ libc.src.dirent.readdir
+)
+
diff --git a/libc/test/src/dirent/dirent_test.cpp b/libc/test/src/dirent/dirent_test.cpp
new file mode 100644
index 000000000000..6826c283a5ca
--- /dev/null
+++ b/libc/test/src/dirent/dirent_test.cpp
@@ -0,0 +1,70 @@
+//===-- Unittests for functions from POSIX dirent.h -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/StringView.h"
+#include "src/dirent/closedir.h"
+#include "src/dirent/dirfd.h"
+#include "src/dirent/opendir.h"
+#include "src/dirent/readdir.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+using StringView = __llvm_libc::cpp::StringView;
+
+TEST(LlvmLibcDirentTest, SimpleOpenAndRead) {
+ ::DIR *dir = __llvm_libc::opendir("testdata");
+ ASSERT_TRUE(dir != nullptr);
+ // The file descriptors 0, 1 and 2 are reserved for standard streams.
+ // So, the file descriptor for the newly opened directory should be
+ // greater than 2.
+ ASSERT_GT(__llvm_libc::dirfd(dir), 2);
+
+ struct ::dirent *file1, *file2, *dir1, *dir2;
+ while (true) {
+ struct ::dirent *d = __llvm_libc::readdir(dir);
+ if (d == nullptr)
+ break;
+ if (StringView(d->d_name).equals("file1.txt"))
+ file1 = d;
+ if (StringView(d->d_name).equals("file2.txt"))
+ file2 = d;
+ if (StringView(d->d_name).equals("dir1"))
+ dir1 = d;
+ if (StringView(d->d_name).equals("dir2.txt"))
+ dir2 = d;
+ }
+
+ // Verify that we don't break out of the above loop in error.
+ ASSERT_EQ(errno, 0);
+
+ ASSERT_TRUE(file1 != nullptr);
+ ASSERT_TRUE(file2 != nullptr);
+ ASSERT_TRUE(dir1 != nullptr);
+ ASSERT_TRUE(dir2 != nullptr);
+
+ ASSERT_EQ(__llvm_libc::closedir(dir), 0);
+}
+
+TEST(LlvmLibcDirentTest, OpenNonExistentDir) {
+ errno = 0;
+ ::DIR *dir = __llvm_libc::opendir("___xyz123__.non_existent__");
+ ASSERT_TRUE(dir == nullptr);
+ ASSERT_EQ(errno, ENOENT);
+ errno = 0;
+}
+
+TEST(LlvmLibcDirentTest, OpenFile) {
+ errno = 0;
+ ::DIR *dir = __llvm_libc::opendir("testdata/file1.txt");
+ ASSERT_TRUE(dir == nullptr);
+ ASSERT_EQ(errno, ENOTDIR);
+ errno = 0;
+}
diff --git a/libc/test/src/dirent/testdata/CMakeLists.txt b/libc/test/src/dirent/testdata/CMakeLists.txt
new file mode 100644
index 000000000000..3a6f9443d8b1
--- /dev/null
+++ b/libc/test/src/dirent/testdata/CMakeLists.txt
@@ -0,0 +1,4 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/file1.txt)
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/file2.txt)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dir1)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dir2)
More information about the libc-commits
mailing list