summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libc/Android.mk53
-rw-r--r--libc/bionic/opendir.c273
-rw-r--r--libc/bionic/opendir.cpp192
-rw-r--r--libc/bionic/sysconf.c3
-rw-r--r--libc/include/dirent.h40
-rw-r--r--libc/upstream-netbsd/libc/gen/alphasort.c59
-rw-r--r--tests/Android.mk1
-rw-r--r--tests/dirent_test.cpp191
8 files changed, 489 insertions, 323 deletions
diff --git a/libc/Android.mk b/libc/Android.mk
index a037e7a..b0220c1 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -195,7 +195,6 @@ libc_common_src_files := \
bionic/mmap.c \
bionic/openat.c \
bionic/open.c \
- bionic/opendir.c \
bionic/pathconf.c \
bionic/perror.c \
bionic/pread.c \
@@ -279,36 +278,38 @@ libc_common_src_files := \
netbsd/nameser/ns_samedomain.c \
libc_bionic_src_files := \
- bionic/assert.cpp \
- bionic/eventfd.cpp \
- bionic/__fgets_chk.cpp \
- bionic/getcwd.cpp \
- bionic/__memcpy_chk.cpp \
- bionic/__memmove_chk.cpp \
- bionic/__memset_chk.cpp \
- bionic/setlocale.cpp \
- bionic/__strcat_chk.cpp \
- bionic/__strcpy_chk.cpp \
- bionic/strerror.cpp \
- bionic/strerror_r.cpp \
- bionic/__strlcat_chk.cpp \
- bionic/__strlcpy_chk.cpp \
- bionic/__strlen_chk.cpp \
- bionic/__strncat_chk.cpp \
- bionic/__strncpy_chk.cpp \
- bionic/strsignal.cpp \
- bionic/stubs.cpp \
- bionic/tdestroy.cpp \
- bionic/tmpfile.cpp \
- bionic/__umask_chk.cpp \
- bionic/__vsnprintf_chk.cpp \
- bionic/__vsprintf_chk.cpp \
- bionic/wchar.cpp \
+ bionic/assert.cpp \
+ bionic/eventfd.cpp \
+ bionic/__fgets_chk.cpp \
+ bionic/getcwd.cpp \
+ bionic/__memcpy_chk.cpp \
+ bionic/__memmove_chk.cpp \
+ bionic/__memset_chk.cpp \
+ bionic/opendir.cpp \
+ bionic/setlocale.cpp \
+ bionic/__strcat_chk.cpp \
+ bionic/__strcpy_chk.cpp \
+ bionic/strerror.cpp \
+ bionic/strerror_r.cpp \
+ bionic/__strlcat_chk.cpp \
+ bionic/__strlcpy_chk.cpp \
+ bionic/__strlen_chk.cpp \
+ bionic/__strncat_chk.cpp \
+ bionic/__strncpy_chk.cpp \
+ bionic/strsignal.cpp \
+ bionic/stubs.cpp \
+ bionic/tdestroy.cpp \
+ bionic/tmpfile.cpp \
+ bionic/__umask_chk.cpp \
+ bionic/__vsnprintf_chk.cpp \
+ bionic/__vsprintf_chk.cpp \
+ bionic/wchar.cpp \
libc_upstream_netbsd_src_files := \
upstream-netbsd/common/lib/libc/hash/sha1/sha1.c \
upstream-netbsd/common/lib/libc/inet/inet_addr.c \
upstream-netbsd/libc/compat-43/creat.c \
+ upstream-netbsd/libc/gen/alphasort.c \
upstream-netbsd/libc/gen/ftw.c \
upstream-netbsd/libc/gen/nftw.c \
upstream-netbsd/libc/gen/nice.c \
diff --git a/libc/bionic/opendir.c b/libc/bionic/opendir.c
deleted file mode 100644
index f178bc6..0000000
--- a/libc/bionic/opendir.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <unistd.h>
-#include <dirent.h>
-#include <memory.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <errno.h>
-
-struct DIR
-{
- int _DIR_fd;
- size_t _DIR_avail;
- struct dirent* _DIR_next;
- pthread_mutex_t _DIR_lock;
- struct dirent _DIR_buff[15];
-};
-
-int dirfd(DIR* dirp)
-{
- return dirp->_DIR_fd;
-}
-
-DIR* opendir( const char* dirpath )
-{
- DIR* dir = malloc(sizeof(DIR));
-
- if (!dir)
- goto Exit;
-
- dir->_DIR_fd = open(dirpath, O_RDONLY|O_DIRECTORY);
- if (dir->_DIR_fd < 0)
- {
- free(dir);
- dir = NULL;
- }
- else
- {
- dir->_DIR_avail = 0;
- dir->_DIR_next = NULL;
- pthread_mutex_init( &dir->_DIR_lock, NULL );
- }
-Exit:
- return dir;
-}
-
-
-DIR* fdopendir(int fd)
-{
- DIR* dir = malloc(sizeof(DIR));
-
- if (!dir)
- return 0;
-
- dir->_DIR_fd = fd;
- dir->_DIR_avail = 0;
- dir->_DIR_next = NULL;
- pthread_mutex_init( &dir->_DIR_lock, NULL );
-
- return dir;
-}
-
-
-static struct dirent*
-_readdir_unlocked(DIR* dir)
-{
- struct dirent* entry;
-#ifndef NDEBUG
- unsigned reclen;
-#endif
-
- if ( !dir->_DIR_avail )
- {
- int rc;
-
- for (;;) {
- rc = getdents( dir->_DIR_fd, dir->_DIR_buff, sizeof(dir->_DIR_buff));
- if (rc >= 0 || errno != EINTR)
- break;
- }
- if (rc <= 0)
- return NULL;
-
- dir->_DIR_avail = rc;
- dir->_DIR_next = dir->_DIR_buff;
- }
-
- entry = dir->_DIR_next;
-
- /* perform some sanity checks here */
- if (((long)(void*)entry & 3) != 0)
- return NULL;
-
-#ifndef NDEBUG
- // paranoid testing of the interface with the kernel getdents64 system call
- reclen = offsetof(struct dirent, d_name) + strlen(entry->d_name) + 1;
- if ( reclen > sizeof(*entry) || reclen <= offsetof(struct dirent, d_name) )
- goto Bad;
-
- if ( (char*)entry + reclen > (char*)dir->_DIR_buff + sizeof(dir->_DIR_buff) )
- goto Bad;
-
- if ( !memchr( entry->d_name, 0, reclen - offsetof(struct dirent, d_name)) )
- goto Bad;
-#endif
-
- dir->_DIR_next = (struct dirent*)((char*)entry + entry->d_reclen);
- dir->_DIR_avail -= entry->d_reclen;
-
- return entry;
-
- Bad:
- errno = EINVAL;
- return NULL;
-}
-
-
-struct dirent*
-readdir(DIR * dir)
-{
- struct dirent *entry = NULL;
-
- pthread_mutex_lock( &dir->_DIR_lock );
- entry = _readdir_unlocked(dir);
- pthread_mutex_unlock( &dir->_DIR_lock );
-
- return entry;
-}
-
-
-int readdir_r(DIR* dir, struct dirent *entry, struct dirent **result)
-{
- struct dirent* ent;
- int save_errno = errno;
- int retval;
-
- *result = NULL;
- errno = 0;
-
- pthread_mutex_lock( &dir->_DIR_lock );
-
- ent = _readdir_unlocked(dir);
- retval = errno;
- if (ent == NULL) {
- if (!retval) {
- errno = save_errno;
- }
- } else {
- if (!retval) {
- errno = save_errno;
- *result = entry;
- memcpy( entry, ent, ent->d_reclen );
- }
- }
-
- pthread_mutex_unlock( &dir->_DIR_lock );
-
- return retval;
-}
-
-
-
-int closedir(DIR *dir)
-{
- int rc;
-
- rc = close(dir->_DIR_fd);
- dir->_DIR_fd = -1;
-
- pthread_mutex_destroy( &dir->_DIR_lock );
-
- free(dir);
- return rc;
-}
-
-
-void rewinddir(DIR *dir)
-{
- pthread_mutex_lock( &dir->_DIR_lock );
- lseek( dir->_DIR_fd, 0, SEEK_SET );
- dir->_DIR_avail = 0;
- pthread_mutex_unlock( &dir->_DIR_lock );
-}
-
-
-int alphasort(const void *a, const void *b)
-{
- struct dirent **d1, **d2;
-
- d1 = (struct dirent **) a;
- d2 = (struct dirent **) b;
- return strcmp((*d1)->d_name, (*d2)->d_name);
-}
-
-
-int scandir(const char *dir, struct dirent ***namelist,
- int(*filter)(const struct dirent *),
- int(*compar)(const struct dirent **, const struct dirent **))
-{
- DIR *d;
- int n_elem = 0;
- struct dirent *this_de, *de;
- struct dirent **de_list = NULL;
- int de_list_size = 0;
-
- d = opendir(dir);
- if (d == NULL) {
- return -1;
- }
-
- while ((this_de = readdir(d)) != NULL) {
- if (filter && (*filter)(this_de) == 0) {
- continue;
- }
- if (n_elem == 0) {
- de_list_size = 4;
- de_list = (struct dirent **)
- malloc(sizeof(struct dirent *)*de_list_size);
- if (de_list == NULL) {
- return -1;
- }
- }
- else if (n_elem == de_list_size) {
- struct dirent **de_list_new;
-
- de_list_size += 10;
- de_list_new = (struct dirent **)
- realloc(de_list, sizeof(struct dirent *)*de_list_size);
- if (de_list_new == NULL) {
- free(de_list);
- return -1;
- }
- de_list = de_list_new;
- }
- de = (struct dirent *) malloc(sizeof(struct dirent));
- *de = *this_de;
- de_list[n_elem++] = de;
- }
- closedir(d);
- if (n_elem && compar) {
- qsort(de_list, n_elem, sizeof(struct dirent *),
- (int (*)(const void *, const void *)) compar);
- }
- *namelist = de_list;
- return n_elem;
-}
diff --git a/libc/bionic/opendir.cpp b/libc/bionic/opendir.cpp
new file mode 100644
index 0000000..cd5b221
--- /dev/null
+++ b/libc/bionic/opendir.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/ScopedPthreadMutexLocker.h>
+
+struct DIR {
+ int fd_;
+ size_t available_bytes_;
+ dirent* next_;
+ pthread_mutex_t mutex_;
+ dirent buff_[15];
+};
+
+static DIR* __allocate_DIR(int fd) {
+ DIR* d = reinterpret_cast<DIR*>(malloc(sizeof(DIR)));
+ if (d == NULL) {
+ return NULL;
+ }
+ d->fd_ = fd;
+ d->available_bytes_ = 0;
+ d->next_ = NULL;
+ pthread_mutex_init(&d->mutex_, NULL);
+ return d;
+}
+
+int dirfd(DIR* dirp) {
+ return dirp->fd_;
+}
+
+DIR* fdopendir(int fd) {
+ // Is 'fd' actually a directory?
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ return NULL;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ errno = ENOTDIR;
+ return NULL;
+ }
+
+ return __allocate_DIR(fd);
+}
+
+DIR* opendir(const char* path) {
+ int fd = open(path, O_RDONLY | O_DIRECTORY);
+ return (fd != -1) ? __allocate_DIR(fd) : NULL;
+}
+
+static bool __fill_DIR(DIR* d) {
+ int rc = TEMP_FAILURE_RETRY(getdents(d->fd_, d->buff_, sizeof(d->buff_)));
+ if (rc <= 0) {
+ return false;
+ }
+ d->available_bytes_ = rc;
+ d->next_ = d->buff_;
+ return true;
+}
+
+static dirent* __readdir_locked(DIR* d) {
+ if (d->available_bytes_ == 0 && !__fill_DIR(d)) {
+ return NULL;
+ }
+
+ dirent* entry = d->next_;
+ d->next_ = reinterpret_cast<dirent*>(reinterpret_cast<char*>(entry) + entry->d_reclen);
+ d->available_bytes_ -= entry->d_reclen;
+ return entry;
+}
+
+dirent* readdir(DIR* d) {
+ ScopedPthreadMutexLocker locker(&d->mutex_);
+ return __readdir_locked(d);
+}
+
+int readdir_r(DIR* d, dirent* entry, dirent** result) {
+ int saved_errno = errno;
+
+ *result = NULL;
+ errno = 0;
+
+ ScopedPthreadMutexLocker locker(&d->mutex_);
+
+ dirent* next = __readdir_locked(d);
+ if (errno != 0 && next == NULL) {
+ return errno;
+ }
+
+ if (next != NULL) {
+ memcpy(entry, next, next->d_reclen);
+ *result = entry;
+ }
+ errno = saved_errno;
+ return 0;
+}
+
+int closedir(DIR* d) {
+ if (d == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = d->fd_;
+ pthread_mutex_destroy(&d->mutex_);
+ free(d);
+ return close(fd);
+}
+
+void rewinddir(DIR* d) {
+ ScopedPthreadMutexLocker locker(&d->mutex_);
+ lseek(d->fd_, 0, SEEK_SET);
+ d->available_bytes_ = 0;
+}
+
+int scandir(const char* path, dirent*** namelist,
+ int(*filter)(const dirent*),
+ int(*compar)(const dirent**, const dirent**))
+{
+ int n_elem = 0;
+ dirent* this_de, *de;
+ dirent** de_list = NULL;
+ int de_list_size = 0;
+
+ DIR* d = opendir(path);
+ if (d == NULL) {
+ return -1;
+ }
+
+ while ((this_de = readdir(d)) != NULL) {
+ if (filter != NULL && (*filter)(this_de) == 0) {
+ continue;
+ }
+ if (n_elem == 0) {
+ de_list_size = 4;
+ de_list = (dirent**) malloc(sizeof(dirent*) * de_list_size);
+ if (de_list == NULL) {
+ return -1;
+ }
+ } else if (n_elem == de_list_size) {
+ de_list_size += 10;
+ dirent** de_list_new = (dirent**) realloc(de_list, sizeof(dirent*) * de_list_size);
+ if (de_list_new == NULL) {
+ free(de_list);
+ return -1;
+ }
+ de_list = de_list_new;
+ }
+ de = (dirent*) malloc(sizeof(dirent));
+ *de = *this_de;
+ de_list[n_elem++] = de;
+ }
+ closedir(d);
+ if (n_elem && compar) {
+ qsort(de_list, n_elem, sizeof(dirent*), (int (*)(const void*, const void*)) compar);
+ }
+ *namelist = de_list;
+ return n_elem;
+}
diff --git a/libc/bionic/sysconf.c b/libc/bionic/sysconf.c
index 7caa4e9..d21a703 100644
--- a/libc/bionic/sysconf.c
+++ b/libc/bionic/sysconf.c
@@ -85,9 +85,8 @@ static int __get_nproc_conf(void) {
}
int result = 0;
- struct dirent de;
struct dirent* e;
- while (!readdir_r(d, &de, &e) && e != NULL) {
+ while ((e = readdir(d)) != NULL) {
if (e->d_type == DT_DIR && __matches_cpuN(e->d_name)) {
++result;
}
diff --git a/libc/include/dirent.h b/libc/include/dirent.h
index 55eef7b..78c3a5a 100644
--- a/libc/include/dirent.h
+++ b/libc/include/dirent.h
@@ -45,34 +45,30 @@ __BEGIN_DECLS
#define DT_WHT 14
#endif
-/* the following structure is really called dirent64 by the kernel
- * headers. They also define a struct dirent, but the latter lack
- * the d_type field which is required by some libraries (e.g. hotplug)
- * who assume to be able to access it directly. sad...
- */
struct dirent {
- uint64_t d_ino;
- int64_t d_off;
- unsigned short d_reclen;
- unsigned char d_type;
- char d_name[256];
+ uint64_t d_ino;
+ int64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[256];
};
-typedef struct DIR DIR;
+typedef struct DIR DIR;
-extern int getdents(unsigned int, struct dirent*, unsigned int);
-extern DIR* opendir(const char* dirpath);
+extern DIR* opendir(const char* dirpath);
extern DIR* fdopendir(int fd);
-extern struct dirent* readdir(DIR* dirp);
-extern int readdir_r(DIR* dirp, struct dirent *entry, struct dirent **result);
-extern int closedir(DIR* dirp);
-extern void rewinddir(DIR *dirp);
+extern struct dirent* readdir(DIR* dirp);
+extern int readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result);
+extern int closedir(DIR* dirp);
+extern void rewinddir(DIR* dirp);
extern int dirfd(DIR* dirp);
-extern int alphasort(const void *a, const void *b);
-extern int scandir(const char *dir, struct dirent ***namelist,
- int(*filter)(const struct dirent *),
- int(*compar)(const struct dirent **,
- const struct dirent **));
+extern int alphasort(const void* a, const void* b);
+extern int scandir(const char* dir, struct dirent*** namelist,
+ int(*filter)(const struct dirent*),
+ int(*compar)(const struct dirent**,
+ const struct dirent**));
+
+extern int getdents(unsigned int, struct dirent*, unsigned int);
__END_DECLS
diff --git a/libc/upstream-netbsd/libc/gen/alphasort.c b/libc/upstream-netbsd/libc/gen/alphasort.c
new file mode 100644
index 0000000..c8310d4
--- /dev/null
+++ b/libc/upstream-netbsd/libc/gen/alphasort.c
@@ -0,0 +1,59 @@
+/* $NetBSD: alphasort.c,v 1.2 2009/03/01 19:59:55 christos Exp $ */
+
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)scandir.c 8.3 (Berkeley) 1/2/94";
+#else
+__RCSID("$NetBSD: alphasort.c,v 1.2 2009/03/01 19:59:55 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include "namespace.h"
+
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+
+/*
+ * Alphabetic order comparison routine for those who want it.
+ */
+int
+alphasort(const void *d1, const void *d2)
+{
+
+ _DIAGASSERT(d1 != NULL);
+ _DIAGASSERT(d2 != NULL);
+
+ return (strcmp((*(const struct dirent *const *)d1)->d_name,
+ (*(const struct dirent *const *)d2)->d_name));
+}
diff --git a/tests/Android.mk b/tests/Android.mk
index e38aaf9..f948fea 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -25,6 +25,7 @@ test_c_flags = \
-Werror \
test_src_files = \
+ dirent_test.cpp \
getcwd_test.cpp \
pthread_test.cpp \
regex_test.cpp \
diff --git a/tests/dirent_test.cpp b/tests/dirent_test.cpp
new file mode 100644
index 0000000..4e364d3
--- /dev/null
+++ b/tests/dirent_test.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <set>
+#include <string>
+
+#ifdef __BIONIC__
+static int my_alphasort(const dirent** lhs, const dirent** rhs) {
+ return alphasort(lhs, rhs);
+}
+#endif
+
+static void CheckProcSelf(std::set<std::string>& names) {
+ // We have a good idea of what should be in /proc/self.
+ ASSERT_TRUE(names.find(".") != names.end());
+ ASSERT_TRUE(names.find("..") != names.end());
+ ASSERT_TRUE(names.find("cmdline") != names.end());
+ ASSERT_TRUE(names.find("fd") != names.end());
+ ASSERT_TRUE(names.find("stat") != names.end());
+}
+
+TEST(dirent, scandir) {
+ // Get everything from /proc/self...
+ dirent** entries;
+#ifdef __BIONIC__
+ int entry_count = scandir("/proc/self", &entries, NULL, my_alphasort);
+#else
+ int entry_count = scandir("/proc/self", &entries, NULL, alphasort);
+#endif
+ ASSERT_GE(entry_count, 0);
+
+ // Turn the directory entries into a set and vector of the names.
+ std::set<std::string> name_set;
+ std::vector<std::string> unsorted_name_list;
+ for (size_t i = 0; i < static_cast<size_t>(entry_count); ++i) {
+ name_set.insert(entries[i]->d_name);
+ unsorted_name_list.push_back(entries[i]->d_name);
+ free(entries[i]);
+ }
+ free(entries);
+
+ // No duplicates.
+ ASSERT_EQ(name_set.size(), unsorted_name_list.size());
+
+ // All entries sorted.
+ std::vector<std::string> sorted_name_list(unsorted_name_list);
+ std::sort(sorted_name_list.begin(), sorted_name_list.end());
+ ASSERT_EQ(sorted_name_list, unsorted_name_list);
+
+ CheckProcSelf(name_set);
+}
+
+TEST(dirent, fdopendir_invalid) {
+ ASSERT_TRUE(fdopendir(-1) == NULL);
+ ASSERT_EQ(errno, EBADF);
+
+ int fd = open("/dev/null", O_RDONLY);
+ ASSERT_NE(fd, -1);
+ ASSERT_TRUE(fdopendir(fd) == NULL);
+ ASSERT_EQ(errno, ENOTDIR);
+ close(fd);
+}
+
+TEST(dirent, fdopendir) {
+ int fd = open("/proc/self", O_RDONLY);
+ DIR* d = fdopendir(fd);
+ ASSERT_TRUE(d != NULL);
+ dirent* e = readdir(d);
+ ASSERT_STREQ(e->d_name, ".");
+ ASSERT_EQ(closedir(d), 0);
+
+ // fdopendir(3) took ownership, so closedir(3) closed our fd.
+ ASSERT_EQ(close(fd), -1);
+ ASSERT_EQ(errno, EBADF);
+}
+
+TEST(dirent, opendir_invalid) {
+ ASSERT_TRUE(opendir("/does/not/exist") == NULL);
+ ASSERT_EQ(errno, ENOENT);
+
+ ASSERT_TRUE(opendir("/dev/null") == NULL);
+ ASSERT_EQ(errno, ENOTDIR);
+}
+
+TEST(dirent, opendir) {
+ DIR* d = opendir("/proc/self");
+ ASSERT_TRUE(d != NULL);
+ dirent* e = readdir(d);
+ ASSERT_STREQ(e->d_name, ".");
+ ASSERT_EQ(closedir(d), 0);
+}
+
+TEST(dirent, closedir_invalid) {
+ DIR* d = NULL;
+ ASSERT_EQ(closedir(d), -1);
+ ASSERT_EQ(errno, EINVAL);
+}
+
+TEST(dirent, closedir) {
+ DIR* d = opendir("/proc/self");
+ ASSERT_TRUE(d != NULL);
+ ASSERT_EQ(closedir(d), 0);
+}
+
+TEST(dirent, readdir) {
+ DIR* d = opendir("/proc/self");
+ ASSERT_TRUE(d != NULL);
+ std::set<std::string> name_set;
+ errno = 0;
+ dirent* e;
+ while ((e = readdir(d)) != NULL) {
+ name_set.insert(e->d_name);
+ }
+ // Reading to the end of the directory is not an error.
+ // readdir(3) returns NULL, but leaves errno as 0.
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(closedir(d), 0);
+
+ CheckProcSelf(name_set);
+}
+
+TEST(dirent, readdir_r) {
+ DIR* d = opendir("/proc/self");
+ ASSERT_TRUE(d != NULL);
+ std::set<std::string> name_set;
+ errno = 0;
+ dirent storage;
+ dirent* e = NULL;
+ while (readdir_r(d, &storage, &e) == 0 && e != NULL) {
+ name_set.insert(e->d_name);
+ }
+ // Reading to the end of the directory is not an error.
+ // readdir_r(3) returns NULL, but leaves errno as 0.
+ ASSERT_EQ(errno, 0);
+ ASSERT_EQ(closedir(d), 0);
+
+ CheckProcSelf(name_set);
+}
+
+TEST(dirent, rewinddir) {
+ DIR* d = opendir("/proc/self");
+ ASSERT_TRUE(d != NULL);
+
+ // Get all the names once...
+ std::vector<std::string> pass1;
+ dirent* e;
+ while ((e = readdir(d)) != NULL) {
+ pass1.push_back(e->d_name);
+ }
+
+ // ...rewind...
+ rewinddir(d);
+
+ // ...and get all the names again.
+ std::vector<std::string> pass2;
+ while ((e = readdir(d)) != NULL) {
+ pass2.push_back(e->d_name);
+ }
+
+ ASSERT_EQ(closedir(d), 0);
+
+ // We should have seen the same names in the same order both times.
+ ASSERT_EQ(pass1.size(), pass2.size());
+ for (size_t i = 0; i < pass1.size(); ++i) {
+ ASSERT_EQ(pass1[i], pass2[i]);
+ }
+}