diff options
Diffstat (limited to 'libc/bionic/opendir.cpp')
-rw-r--r-- | libc/bionic/opendir.cpp | 192 |
1 files changed, 192 insertions, 0 deletions
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 <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; +} |