Implement scandirat and scandirat64.
Bug: http://b/12612339
Change-Id: Id3b249a884fe08964b26a017ae9574961f0cb441
diff --git a/libc/bionic/scandir.cpp b/libc/bionic/scandir.cpp
index ee62fee..e55be42 100644
--- a/libc/bionic/scandir.cpp
+++ b/libc/bionic/scandir.cpp
@@ -16,9 +16,11 @@
#include <dirent.h>
+#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#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 @@
dirent** release() {
dirent** result = names_;
- names_ = NULL;
+ names_ = nullptr;
size_ = capacity_ = 0;
return result;
}
@@ -52,7 +54,7 @@
size_t new_capacity = capacity_ + 32;
dirent** new_names =
reinterpret_cast<dirent**>(realloc(names_, new_capacity * sizeof(dirent*)));
- if (new_names == NULL) {
+ if (new_names == nullptr) {
return false;
}
names_ = new_names;
@@ -60,7 +62,7 @@
}
dirent* copy = CopyDirent(entry);
- if (copy == NULL) {
+ if (copy == nullptr) {
return false;
}
names_[size_++] = copy;
@@ -69,7 +71,7 @@
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<int (*)(const void*, const void*)>(comparator));
}
@@ -91,19 +93,29 @@
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 @@
*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 63716a4..3cdfa68 100644
--- a/libc/include/dirent.h
+++ b/libc/include/dirent.h
@@ -81,8 +81,13 @@
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 fbceb83..bb67b5c 100644
--- a/libc/libc.map
+++ b/libc/libc.map
@@ -1346,6 +1346,8 @@
preadv64;
pwritev;
pwritev64;
+ scandirat;
+ scandirat64;
strchrnul;
} LIBC;
diff --git a/libc/private/ScopedReaddir.h b/libc/private/ScopedReaddir.h
index 84c1b93..3d77a40 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 214dd78..fa05ca1 100644
--- a/tests/dirent_test.cpp
+++ b/tests/dirent_test.cpp
@@ -81,6 +81,72 @@
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<std::string> name_set, name_set_at, name_set_at64;
+ std::vector<std::string> 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);