diff --git a/libc/Android.mk b/libc/Android.mk index a037e7a1d..b0220c187 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 f178bc669..000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include - -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 000000000..cd5b2211c --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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(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(reinterpret_cast(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 7caa4e93c..d21a703ec 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 55eef7b52..78c3a5a7e 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 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 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 getdents(unsigned int, struct dirent*, unsigned int); -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 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 **)); __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 000000000..c8310d425 --- /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 +#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 +#include +#include + +/* + * 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 e38aaf9a9..f948fea74 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 000000000..4e364d304 --- /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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef __BIONIC__ +static int my_alphasort(const dirent** lhs, const dirent** rhs) { + return alphasort(lhs, rhs); +} +#endif + +static void CheckProcSelf(std::set& 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 name_set; + std::vector unsorted_name_list; + for (size_t i = 0; i < static_cast(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 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 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 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 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 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]); + } +}