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 <malloc.h> 34 #include <string.h> 35 #include <sys/stat.h> 36 #include <sys/types.h> 37 #include <unistd.h> 38 39 #include "private/ErrnoRestorer.h" 40 #include "private/ScopedPthreadMutexLocker.h" 41 42 extern "C" int __getdents64(unsigned int, dirent*, unsigned int); 43 44 // Apportable decided to copy the data structure from this file 45 // and use it in their own code, but they also call into readdir. 46 // In order to avoid a lockup, the structure must be maintained in 47 // the exact same order as in L and below. New structure members 48 // need to be added to the end of this structure. 49 // See b/21037208 for more details. 50 struct DIR { 51 int fd_; 52 size_t available_bytes_; 53 dirent* next_; 54 pthread_mutex_t mutex_; 55 dirent buff_[15]; 56 long current_pos_; 57 }; 58 59 static DIR* __allocate_DIR(int fd) { 60 DIR* d = reinterpret_cast<DIR*>(malloc(sizeof(DIR))); 61 if (d == NULL) { 62 return NULL; 63 } 64 d->fd_ = fd; 65 d->available_bytes_ = 0; 66 d->next_ = NULL; 67 d->current_pos_ = 0L; 68 pthread_mutex_init(&d->mutex_, NULL); 69 return d; 70 } 71 72 int dirfd(DIR* dirp) { 73 return dirp->fd_; 74 } 75 76 DIR* fdopendir(int fd) { 77 // Is 'fd' actually a directory? 78 struct stat sb; 79 if (fstat(fd, &sb) == -1) { 80 return NULL; 81 } 82 if (!S_ISDIR(sb.st_mode)) { 83 errno = ENOTDIR; 84 return NULL; 85 } 86 87 return __allocate_DIR(fd); 88 } 89 90 DIR* opendir(const char* path) { 91 int fd = open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY); 92 return (fd != -1) ? __allocate_DIR(fd) : NULL; 93 } 94 95 static bool __fill_DIR(DIR* d) { 96 int rc = TEMP_FAILURE_RETRY(__getdents64(d->fd_, d->buff_, sizeof(d->buff_))); 97 if (rc <= 0) { 98 return false; 99 } 100 d->available_bytes_ = rc; 101 d->next_ = d->buff_; 102 return true; 103 } 104 105 static dirent* __readdir_locked(DIR* d) { 106 if (d->available_bytes_ == 0 && !__fill_DIR(d)) { 107 return NULL; 108 } 109 110 dirent* entry = d->next_; 111 d->next_ = reinterpret_cast<dirent*>(reinterpret_cast<char*>(entry) + entry->d_reclen); 112 d->available_bytes_ -= entry->d_reclen; 113 // The directory entry offset uses 0, 1, 2 instead of real file offset, 114 // so the value range of long type is enough. 115 d->current_pos_ = static_cast<long>(entry->d_off); 116 return entry; 117 } 118 119 dirent* readdir(DIR* d) { 120 ScopedPthreadMutexLocker locker(&d->mutex_); 121 return __readdir_locked(d); 122 } 123 __strong_alias(readdir64, readdir); 124 125 int readdir_r(DIR* d, dirent* entry, dirent** result) { 126 ErrnoRestorer errno_restorer; 127 128 *result = NULL; 129 errno = 0; 130 131 ScopedPthreadMutexLocker locker(&d->mutex_); 132 133 dirent* next = __readdir_locked(d); 134 if (errno != 0 && next == NULL) { 135 return errno; 136 } 137 138 if (next != NULL) { 139 memcpy(entry, next, next->d_reclen); 140 *result = entry; 141 } 142 return 0; 143 } 144 __strong_alias(readdir64_r, readdir_r); 145 146 int closedir(DIR* d) { 147 if (d == NULL) { 148 errno = EINVAL; 149 return -1; 150 } 151 152 int fd = d->fd_; 153 pthread_mutex_destroy(&d->mutex_); 154 free(d); 155 return close(fd); 156 } 157 158 void rewinddir(DIR* d) { 159 ScopedPthreadMutexLocker locker(&d->mutex_); 160 lseek(d->fd_, 0, SEEK_SET); 161 d->available_bytes_ = 0; 162 d->current_pos_ = 0L; 163 } 164 165 void seekdir(DIR* d, long offset) { 166 ScopedPthreadMutexLocker locker(&d->mutex_); 167 off_t ret = lseek(d->fd_, offset, SEEK_SET); 168 if (ret != -1L) { 169 d->available_bytes_ = 0; 170 d->current_pos_ = ret; 171 } 172 } 173 174 long telldir(DIR* d) { 175 return d->current_pos_; 176 } 177 178 int alphasort(const dirent** a, const dirent** b) { 179 return strcoll((*a)->d_name, (*b)->d_name); 180 } 181 __strong_alias(alphasort64, alphasort); 182