56 changes: 56 additions & 0 deletions libc/src/__support/File/linux_dir.cpp
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions libc/src/dirent/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
23 changes: 23 additions & 0 deletions libc/src/dirent/closedir.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/dirent/closedir.h
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions libc/src/dirent/dirfd.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/dirent/dirfd.h
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions libc/src/dirent/opendir.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/dirent/opendir.h
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions libc/src/dirent/readdir.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/dirent/readdir.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions libc/test/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions libc/test/src/dirent/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)

70 changes: 70 additions & 0 deletions libc/test/src/dirent/dirent_test.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 4 additions & 0 deletions libc/test/src/dirent/testdata/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)