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 #include <unistd.h> 29 #include <dirent.h> 30 #include <memory.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <stdlib.h> 34 #include <pthread.h> 35 #include <errno.h> 36 37 struct DIR 38 { 39 int _DIR_fd; 40 size_t _DIR_avail; 41 struct dirent* _DIR_next; 42 pthread_mutex_t _DIR_lock; 43 struct dirent _DIR_buff[15]; 44 }; 45 46 int dirfd(DIR* dirp) 47 { 48 return dirp->_DIR_fd; 49 } 50 51 DIR* opendir( const char* dirpath ) 52 { 53 DIR* dir = malloc(sizeof(DIR)); 54 55 if (!dir) 56 goto Exit; 57 58 dir->_DIR_fd = open(dirpath, O_RDONLY|O_DIRECTORY); 59 if (dir->_DIR_fd < 0) 60 { 61 free(dir); 62 dir = NULL; 63 } 64 else 65 { 66 dir->_DIR_avail = 0; 67 dir->_DIR_next = NULL; 68 pthread_mutex_init( &dir->_DIR_lock, NULL ); 69 } 70 Exit: 71 return dir; 72 } 73 74 75 DIR* fdopendir(int fd) 76 { 77 DIR* dir = malloc(sizeof(DIR)); 78 79 if (!dir) 80 return 0; 81 82 dir->_DIR_fd = fd; 83 dir->_DIR_avail = 0; 84 dir->_DIR_next = NULL; 85 pthread_mutex_init( &dir->_DIR_lock, NULL ); 86 87 return dir; 88 } 89 90 91 static struct dirent* 92 _readdir_unlocked(DIR* dir) 93 { 94 struct dirent* entry; 95 #ifndef NDEBUG 96 unsigned reclen; 97 #endif 98 99 if ( !dir->_DIR_avail ) 100 { 101 int rc; 102 103 for (;;) { 104 rc = getdents( dir->_DIR_fd, dir->_DIR_buff, sizeof(dir->_DIR_buff)); 105 if (rc >= 0 || errno != EINTR) 106 break; 107 } 108 if (rc <= 0) 109 return NULL; 110 111 dir->_DIR_avail = rc; 112 dir->_DIR_next = dir->_DIR_buff; 113 } 114 115 entry = dir->_DIR_next; 116 117 /* perform some sanity checks here */ 118 if (((long)(void*)entry & 3) != 0) 119 return NULL; 120 121 #ifndef NDEBUG 122 // paranoid testing of the interface with the kernel getdents64 system call 123 reclen = offsetof(struct dirent, d_name) + strlen(entry->d_name) + 1; 124 if ( reclen > sizeof(*entry) || reclen <= offsetof(struct dirent, d_name) ) 125 goto Bad; 126 127 if ( (char*)entry + reclen > (char*)dir->_DIR_buff + sizeof(dir->_DIR_buff) ) 128 goto Bad; 129 130 if ( !memchr( entry->d_name, 0, reclen - offsetof(struct dirent, d_name)) ) 131 goto Bad; 132 #endif 133 134 dir->_DIR_next = (struct dirent*)((char*)entry + entry->d_reclen); 135 dir->_DIR_avail -= entry->d_reclen; 136 137 return entry; 138 139 Bad: 140 errno = EINVAL; 141 return NULL; 142 } 143 144 145 struct dirent* 146 readdir(DIR * dir) 147 { 148 struct dirent *entry = NULL; 149 150 pthread_mutex_lock( &dir->_DIR_lock ); 151 entry = _readdir_unlocked(dir); 152 pthread_mutex_unlock( &dir->_DIR_lock ); 153 154 return entry; 155 } 156 157 158 int readdir_r(DIR* dir, struct dirent *entry, struct dirent **result) 159 { 160 struct dirent* ent; 161 int save_errno = errno; 162 int retval; 163 164 *result = NULL; 165 errno = 0; 166 167 pthread_mutex_lock( &dir->_DIR_lock ); 168 169 ent = _readdir_unlocked(dir); 170 retval = errno; 171 if (ent == NULL) { 172 if (!retval) { 173 errno = save_errno; 174 } 175 } else { 176 if (!retval) { 177 errno = save_errno; 178 *result = entry; 179 memcpy( entry, ent, ent->d_reclen ); 180 } 181 } 182 183 pthread_mutex_unlock( &dir->_DIR_lock ); 184 185 return retval; 186 } 187 188 189 190 int closedir(DIR *dir) 191 { 192 int rc; 193 194 rc = close(dir->_DIR_fd); 195 dir->_DIR_fd = -1; 196 197 pthread_mutex_destroy( &dir->_DIR_lock ); 198 199 free(dir); 200 return rc; 201 } 202 203 204 void rewinddir(DIR *dir) 205 { 206 pthread_mutex_lock( &dir->_DIR_lock ); 207 lseek( dir->_DIR_fd, 0, SEEK_SET ); 208 dir->_DIR_avail = 0; 209 pthread_mutex_unlock( &dir->_DIR_lock ); 210 } 211 212 213 int alphasort(const void *a, const void *b) 214 { 215 struct dirent **d1, **d2; 216 217 d1 = (struct dirent **) a; 218 d2 = (struct dirent **) b; 219 return strcmp((*d1)->d_name, (*d2)->d_name); 220 } 221 222 223 int scandir(const char *dir, struct dirent ***namelist, 224 int(*filter)(const struct dirent *), 225 int(*compar)(const struct dirent **, const struct dirent **)) 226 { 227 DIR *d; 228 int n_elem = 0; 229 struct dirent *this_de, *de; 230 struct dirent **de_list = NULL; 231 int de_list_size = 0; 232 233 d = opendir(dir); 234 if (d == NULL) { 235 return -1; 236 } 237 238 while ((this_de = readdir(d)) != NULL) { 239 if (filter && (*filter)(this_de) == 0) { 240 continue; 241 } 242 if (n_elem == 0) { 243 de_list_size = 4; 244 de_list = (struct dirent **) 245 malloc(sizeof(struct dirent *)*de_list_size); 246 if (de_list == NULL) { 247 return -1; 248 } 249 } 250 else if (n_elem == de_list_size) { 251 struct dirent **de_list_new; 252 253 de_list_size += 10; 254 de_list_new = (struct dirent **) 255 realloc(de_list, sizeof(struct dirent *)*de_list_size); 256 if (de_list_new == NULL) { 257 free(de_list); 258 return -1; 259 } 260 de_list = de_list_new; 261 } 262 de = (struct dirent *) malloc(sizeof(struct dirent)); 263 *de = *this_de; 264 de_list[n_elem++] = de; 265 } 266 closedir(d); 267 if (n_elem && compar) { 268 qsort(de_list, n_elem, sizeof(struct dirent *), 269 (int (*)(const void *, const void *)) compar); 270 } 271 *namelist = de_list; 272 return n_elem; 273 } 274