1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <dirent.h> 30 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <sys/stat.h> 34 #include <sys/types.h> 35 #include <unistd.h> 36 37 #include "private/ErrnoRestorer.h" 38 #include "private/ScopedPthreadMutexLocker.h" 39 40 struct DIR { 41 int fd_; 42 size_t available_bytes_; 43 dirent* next_; 44 pthread_mutex_t mutex_; 45 dirent buff_[15]; 46 }; 47 48 static DIR* __allocate_DIR(int fd) { 49 DIR* d = reinterpret_cast<DIR*>(malloc(sizeof(DIR))); 50 if (d == NULL) { 51 return NULL; 52 } 53 d->fd_ = fd; 54 d->available_bytes_ = 0; 55 d->next_ = NULL; 56 pthread_mutex_init(&d->mutex_, NULL); 57 return d; 58 } 59 60 int dirfd(DIR* dirp) { 61 return dirp->fd_; 62 } 63 64 DIR* fdopendir(int fd) { 65 // Is 'fd' actually a directory? 66 struct stat sb; 67 if (fstat(fd, &sb) == -1) { 68 return NULL; 69 } 70 if (!S_ISDIR(sb.st_mode)) { 71 errno = ENOTDIR; 72 return NULL; 73 } 74 75 return __allocate_DIR(fd); 76 } 77 78 DIR* opendir(const char* path) { 79 int fd = open(path, O_RDONLY | O_DIRECTORY); 80 return (fd != -1) ? __allocate_DIR(fd) : NULL; 81 } 82 83 static bool __fill_DIR(DIR* d) { 84 int rc = TEMP_FAILURE_RETRY(getdents(d->fd_, d->buff_, sizeof(d->buff_))); 85 if (rc <= 0) { 86 return false; 87 } 88 d->available_bytes_ = rc; 89 d->next_ = d->buff_; 90 return true; 91 } 92 93 static dirent* __readdir_locked(DIR* d) { 94 if (d->available_bytes_ == 0 && !__fill_DIR(d)) { 95 return NULL; 96 } 97 98 dirent* entry = d->next_; 99 d->next_ = reinterpret_cast<dirent*>(reinterpret_cast<char*>(entry) + entry->d_reclen); 100 d->available_bytes_ -= entry->d_reclen; 101 return entry; 102 } 103 104 dirent* readdir(DIR* d) { 105 ScopedPthreadMutexLocker locker(&d->mutex_); 106 return __readdir_locked(d); 107 } 108 109 int readdir_r(DIR* d, dirent* entry, dirent** result) { 110 ErrnoRestorer errno_restorer; 111 112 *result = NULL; 113 errno = 0; 114 115 ScopedPthreadMutexLocker locker(&d->mutex_); 116 117 dirent* next = __readdir_locked(d); 118 if (errno != 0 && next == NULL) { 119 return errno; 120 } 121 122 if (next != NULL) { 123 memcpy(entry, next, next->d_reclen); 124 *result = entry; 125 } 126 return 0; 127 } 128 129 int closedir(DIR* d) { 130 if (d == NULL) { 131 errno = EINVAL; 132 return -1; 133 } 134 135 int fd = d->fd_; 136 pthread_mutex_destroy(&d->mutex_); 137 free(d); 138 return close(fd); 139 } 140 141 void rewinddir(DIR* d) { 142 ScopedPthreadMutexLocker locker(&d->mutex_); 143 lseek(d->fd_, 0, SEEK_SET); 144 d->available_bytes_ = 0; 145 } 146 147 int alphasort(const dirent** a, const dirent** b) { 148 return strcoll((*a)->d_name, (*b)->d_name); 149 } 150