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 42extern "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. 50struct 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 59static 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 72int dirfd(DIR* dirp) { 73 return dirp->fd_; 74} 75 76DIR* 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 90DIR* 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 95static 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 105static 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 119dirent* readdir(DIR* d) { 120 ScopedPthreadMutexLocker locker(&d->mutex_); 121 return __readdir_locked(d); 122} 123__strong_alias(readdir64, readdir); 124 125int 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 146int 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 158void 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 165void 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 174long telldir(DIR* d) { 175 return d->current_pos_; 176} 177 178int alphasort(const dirent** a, const dirent** b) { 179 return strcoll((*a)->d_name, (*b)->d_name); 180} 181__strong_alias(alphasort64, alphasort); 182