diff --git a/libc/bionic/scandir.cpp b/libc/bionic/scandir.cpp index ee62feee4..e55be4209 100644 --- a/libc/bionic/scandir.cpp +++ b/libc/bionic/scandir.cpp @@ -16,9 +16,11 @@ #include +#include #include #include #include +#include #include "private/bionic_macros.h" #include "private/ScopedReaddir.h" @@ -26,7 +28,7 @@ // A smart pointer to the scandir dirent**. class ScandirResult { public: - ScandirResult() : names_(NULL), size_(0), capacity_(0) { + ScandirResult() : names_(nullptr), size_(0), capacity_(0) { } ~ScandirResult() { @@ -42,7 +44,7 @@ class ScandirResult { dirent** release() { dirent** result = names_; - names_ = NULL; + names_ = nullptr; size_ = capacity_ = 0; return result; } @@ -52,7 +54,7 @@ class ScandirResult { size_t new_capacity = capacity_ + 32; dirent** new_names = reinterpret_cast(realloc(names_, new_capacity * sizeof(dirent*))); - if (new_names == NULL) { + if (new_names == nullptr) { return false; } names_ = new_names; @@ -60,7 +62,7 @@ class ScandirResult { } dirent* copy = CopyDirent(entry); - if (copy == NULL) { + if (copy == nullptr) { return false; } names_[size_++] = copy; @@ -69,7 +71,7 @@ class ScandirResult { void Sort(int (*comparator)(const dirent**, const dirent**)) { // If we have entries and a comparator, sort them. - if (size_ > 0 && comparator != NULL) { + if (size_ > 0 && comparator != nullptr) { qsort(names_, size_, sizeof(dirent*), reinterpret_cast(comparator)); } @@ -91,19 +93,29 @@ class ScandirResult { DISALLOW_COPY_AND_ASSIGN(ScandirResult); }; -int scandir(const char* dirname, dirent*** name_list, - int (*filter)(const dirent*), - int (*comparator)(const dirent**, const dirent**)) { - ScopedReaddir reader(dirname); +int scandirat(int parent_fd, const char* dir_name, dirent*** name_list, + int (*filter)(const dirent*), + int (*comparator)(const dirent**, const dirent**)) { + DIR* dir = nullptr; + if (parent_fd == AT_FDCWD) { + dir = opendir(dir_name); + } else { + int dir_fd = openat(parent_fd, dir_name, O_CLOEXEC | O_DIRECTORY | O_RDONLY); + if (dir_fd != -1) { + dir = fdopendir(dir_fd); + } + } + + ScopedReaddir reader(dir); if (reader.IsBad()) { return -1; } ScandirResult names; dirent* entry; - while ((entry = reader.ReadEntry()) != NULL) { + while ((entry = reader.ReadEntry()) != nullptr) { // If we have a filter, skip names that don't match. - if (filter != NULL && !(*filter)(entry)) { + if (filter != nullptr && !(*filter)(entry)) { continue; } names.Add(entry); @@ -115,4 +127,11 @@ int scandir(const char* dirname, dirent*** name_list, *name_list = names.release(); return size; } +__strong_alias(scandirat64, scandirat); + +int scandir(const char* dir_path, dirent*** name_list, + int (*filter)(const dirent*), + int (*comparator)(const dirent**, const dirent**)) { + return scandirat(AT_FDCWD, dir_path, name_list, filter, comparator); +} __strong_alias(scandir64, scandir); diff --git a/libc/include/dirent.h b/libc/include/dirent.h index 63716a4b7..3cdfa68e7 100644 --- a/libc/include/dirent.h +++ b/libc/include/dirent.h @@ -81,8 +81,13 @@ extern long telldir(DIR*); extern int dirfd(DIR*); extern int alphasort(const struct dirent**, const struct dirent**); extern int alphasort64(const struct dirent64**, const struct dirent64**); -extern int scandir(const char*, struct dirent***, int (*)(const struct dirent*), int (*)(const struct dirent**, const struct dirent**)); extern int scandir64(const char*, struct dirent64***, int (*)(const struct dirent64*), int (*)(const struct dirent64**, const struct dirent64**)); +extern int scandir(const char*, struct dirent***, int (*)(const struct dirent*), int (*)(const struct dirent**, const struct dirent**)); + +#if defined(__USE_GNU) +int scandirat64(int, const char*, struct dirent64***, int (*)(const struct dirent64*), int (*)(const struct dirent64**, const struct dirent64**)); +int scandirat(int, const char*, struct dirent***, int (*)(const struct dirent*), int (*)(const struct dirent**, const struct dirent**)); +#endif __END_DECLS diff --git a/libc/libc.map b/libc/libc.map index fbceb832a..bb67b5c2e 100644 --- a/libc/libc.map +++ b/libc/libc.map @@ -1346,6 +1346,8 @@ LIBC_N { preadv64; pwritev; pwritev64; + scandirat; + scandirat64; strchrnul; } LIBC; diff --git a/libc/private/ScopedReaddir.h b/libc/private/ScopedReaddir.h index 84c1b93a4..3d77a40b6 100644 --- a/libc/private/ScopedReaddir.h +++ b/libc/private/ScopedReaddir.h @@ -23,8 +23,11 @@ class ScopedReaddir { public: - ScopedReaddir(const char* path) { - dir_ = opendir(path); + ScopedReaddir(const char* path) : ScopedReaddir(opendir(path)) { + } + + ScopedReaddir(DIR* dir) { + dir_ = dir; } ~ScopedReaddir() { diff --git a/tests/dirent_test.cpp b/tests/dirent_test.cpp index 214dd78c8..fa05ca169 100644 --- a/tests/dirent_test.cpp +++ b/tests/dirent_test.cpp @@ -81,6 +81,72 @@ TEST(dirent, scandir_scandir64) { CheckProcSelf(name_set); } +TEST(dirent, scandirat_scandirat64) { + // Get everything from /proc/self... + dirent** entries; + int entry_count = scandir("/proc/self", &entries, NULL, alphasort); + ASSERT_GE(entry_count, 0); + + int proc_fd = open("/proc", O_DIRECTORY); + ASSERT_NE(-1, proc_fd); + + dirent** entries_at; + int entry_count_at = scandirat(proc_fd, "self", &entries_at, NULL, alphasort); + ASSERT_EQ(entry_count, entry_count_at); + + dirent64** entries_at64; + int entry_count_at64 = scandirat64(proc_fd, "self", &entries_at64, NULL, alphasort64); + ASSERT_EQ(entry_count, entry_count_at64); + + close(proc_fd); + + // scandirat and scandirat64 should return the same results as scandir. + std::set name_set, name_set_at, name_set_at64; + std::vector unsorted_name_list, unsorted_name_list_at, unsorted_name_list_at64; + ScanEntries(entries, entry_count, name_set, unsorted_name_list); + ScanEntries(entries_at, entry_count_at, name_set_at, unsorted_name_list_at); + ScanEntries(entries_at64, entry_count_at64, name_set_at64, unsorted_name_list_at64); + + ASSERT_EQ(name_set, name_set_at); + ASSERT_EQ(name_set, name_set_at64); + ASSERT_EQ(unsorted_name_list, unsorted_name_list_at); + ASSERT_EQ(unsorted_name_list, unsorted_name_list_at64); +} + +TEST(dirent, scandir_ENOENT) { + dirent** entries; + errno = 0; + ASSERT_EQ(-1, scandir("/does-not-exist", &entries, nullptr, nullptr)); + ASSERT_EQ(ENOENT, errno); +} + +TEST(dirent, scandir64_ENOENT) { + dirent64** entries; + errno = 0; + ASSERT_EQ(-1, scandir64("/does-not-exist", &entries, nullptr, nullptr)); + ASSERT_EQ(ENOENT, errno); +} + +TEST(dirent, scandirat_ENOENT) { + int root_fd = open("/", O_DIRECTORY | O_RDONLY); + ASSERT_NE(-1, root_fd); + dirent** entries; + errno = 0; + ASSERT_EQ(-1, scandirat(root_fd, "does-not-exist", &entries, nullptr, nullptr)); + ASSERT_EQ(ENOENT, errno); + close(root_fd); +} + +TEST(dirent, scandirat64_ENOENT) { + int root_fd = open("/", O_DIRECTORY | O_RDONLY); + ASSERT_NE(-1, root_fd); + dirent64** entries; + errno = 0; + ASSERT_EQ(-1, scandirat64(root_fd, "does-not-exist", &entries, nullptr, nullptr)); + ASSERT_EQ(ENOENT, errno); + close(root_fd); +} + TEST(dirent, fdopendir_invalid) { ASSERT_TRUE(fdopendir(-1) == NULL); ASSERT_EQ(EBADF, errno);