[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