diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -255,6 +255,14 @@ ]; } +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 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -227,6 +227,12 @@ # 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 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -15,6 +15,17 @@ .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 --- /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 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -9,6 +9,7 @@ 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(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_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 --- /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 --- /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 --- /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 +#include + +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 different 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 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -12,6 +12,15 @@ def PThreadStartT : NamedType<"__pthread_start_t">; +def InoT : NamedType<"ino_t">; +def DIR : NamedType<"DIR">; +def DIRPtr : PtrType; +def DIRRestrictedPtr : RestrictedPtrType; +def StructDirent : NamedType<"struct dirent">; +def StructDirentPtr : PtrType; +def StructDirentPtrPtr : PtrType; +def ConstStructDirentPtrPtr : ConstType; + def POSIX : StandardSpec<"POSIX"> { PtrType CharPtr = PtrType; RestrictedPtrType RestrictedCharPtr = RestrictedPtrType; @@ -578,8 +587,48 @@ ] >; + HeaderSpec Dirent = HeaderSpec< + "dirent.h", + [], // Macros + [InoT, StructDirent, DIR], // Types + [], // Enumerations + [ + FunctionSpec< + "alphasort", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "closedir", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "dirfd", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "fdopendir", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "opendir", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "readdir", + RetValSpec, + [ArgSpec] + >, + ] + >; + let Headers = [ CType, + Dirent, Errno, FCntl, PThread, diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -9,6 +9,7 @@ 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 --- a/libc/src/__support/File/CMakeLists.txt +++ b/libc/src/__support/File/CMakeLists.txt @@ -16,6 +16,17 @@ 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 @@ ) 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.h b/libc/src/__support/File/dir.h new file mode 100644 --- /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 +#include + +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 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 mehtod 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/dir.cpp b/libc/src/__support/File/dir.cpp new file mode 100644 --- /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 + +namespace __llvm_libc { + +Dir *Dir::open(const char *path) { + int fd = platform_opendir(path); + if (fd < 0) + return nullptr; + + Dir *dir = reinterpret_cast(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(buffer, BUFSIZE)); + if (fillsize == 0) + return nullptr; + readptr = 0; + } + struct dirent *d = reinterpret_cast(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/linux_dir.cpp b/libc/src/__support/File/linux_dir.cpp new file mode 100644 --- /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 +#include // For open flags +#include // 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 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 --- /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.h b/libc/src/dirent/closedir.h new file mode 100644 --- /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 + +namespace __llvm_libc { + +int closedir(::DIR *dir); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_DIRENT_CLOSEDIR_H diff --git a/libc/src/dirent/closedir.cpp b/libc/src/dirent/closedir.cpp new file mode 100644 --- /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 + +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/dirfd.h b/libc/src/dirent/dirfd.h new file mode 100644 --- /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 + +namespace __llvm_libc { + +int dirfd(::DIR *dir); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_DIRENT_DIRFD_H diff --git a/libc/src/dirent/dirfd.cpp b/libc/src/dirent/dirfd.cpp new file mode 100644 --- /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 + +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/opendir.h b/libc/src/dirent/opendir.h new file mode 100644 --- /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 + +namespace __llvm_libc { + +::DIR *opendir(const char *name); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_DIRENT_OPENDIR_H diff --git a/libc/src/dirent/opendir.cpp b/libc/src/dirent/opendir.cpp new file mode 100644 --- /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 + +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/readdir.h b/libc/src/dirent/readdir.h new file mode 100644 --- /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 + +namespace __llvm_libc { + +struct ::dirent *readdir(DIR *dir); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_DIRENT_READDIR_H diff --git a/libc/src/dirent/readdir.cpp b/libc/src/dirent/readdir.cpp new file mode 100644 --- /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 + +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/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -45,6 +45,7 @@ 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 --- /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 --- /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 +#include + +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 --- /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)