1/* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2004-2010, Axel D��rfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8#include <dirent.h> 9#include <dirent_private.h> 10 11#include <errno.h> 12#include <limits.h> 13#include <stdlib.h> 14#include <string.h> 15 16#include <errno_private.h> 17#include <syscalls.h> 18#include <syscall_utils.h> 19 20 21#define DIR_BUFFER_SIZE 4096 22 23 24struct __DIR { 25 int fd; 26 short next_entry; 27 unsigned short entries_left; 28 long seek_position; 29 long current_position; 30 struct dirent first_entry; 31}; 32 33 34static int 35do_seek_dir(DIR* dir) 36{ 37 if (dir->seek_position == dir->current_position) 38 return 0; 39 40 // If the seek position lies before the current position (the usual case), 41 // rewind to the beginning. 42 if (dir->seek_position < dir->current_position) { 43 status_t status = _kern_rewind_dir(dir->fd); 44 if (status < 0) { 45 __set_errno(status); 46 return -1; 47 } 48 49 dir->current_position = 0; 50 dir->entries_left = 0; 51 } 52 53 // Now skip entries until we have reached seek_position. 54 while (dir->seek_position > dir->current_position) { 55 ssize_t count; 56 long toSkip = dir->seek_position - dir->current_position; 57 if (toSkip == dir->entries_left) { 58 // we have to skip exactly all of the currently buffered entries 59 dir->current_position = dir->seek_position; 60 dir->entries_left = 0; 61 return 0; 62 } 63 64 if (toSkip < dir->entries_left) { 65 // we have to skip only some of the buffered entries 66 for (; toSkip > 0; toSkip--) { 67 struct dirent* entry = (struct dirent*) 68 ((uint8*)&dir->first_entry + dir->next_entry); 69 dir->entries_left--; 70 dir->next_entry += entry->d_reclen; 71 } 72 73 dir->current_position = dir->seek_position; 74 return 0; 75 } 76 77 // we have to skip more than the currently buffered entries 78 dir->current_position += dir->entries_left; 79 dir->entries_left = 0; 80 81 count = _kern_read_dir(dir->fd, &dir->first_entry, 82 (char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX); 83 if (count <= 0) { 84 if (count < 0) 85 __set_errno(count); 86 87 // end of directory 88 return -1; 89 } 90 91 dir->next_entry = 0; 92 dir->entries_left = count; 93 } 94 95 return 0; 96} 97 98 99// #pragma mark - private API 100 101 102DIR* 103__create_dir_struct(int fd) 104{ 105 /* allocate the memory for the DIR structure */ 106 107 DIR* dir = (DIR*)malloc(DIR_BUFFER_SIZE); 108 if (dir == NULL) { 109 __set_errno(B_NO_MEMORY); 110 return NULL; 111 } 112 113 dir->fd = fd; 114 dir->entries_left = 0; 115 dir->seek_position = 0; 116 dir->current_position = 0; 117 118 return dir; 119} 120 121 122// #pragma mark - public API 123 124 125DIR* 126fdopendir(int fd) 127{ 128 DIR* dir; 129 130 // Since our standard file descriptors can't be used as directory file 131 // descriptors, we have to open a fresh one explicitly. 132 int dirFD = _kern_open_dir(fd, NULL); 133 if (dirFD < 0) { 134 __set_errno(dirFD); 135 return NULL; 136 } 137 138 // Since applications are allowed to use the file descriptor after a call 139 // to fdopendir() without changing its state (like for other *at() 140 // functions), we cannot close it now. 141 // We dup2() the new FD to the previous location instead. 142 if (dup2(dirFD, fd) == -1) 143 close(fd); 144 else { 145 close(dirFD); 146 dirFD = fd; 147 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 148 // reset close-on-exec which is cleared by dup() 149 } 150 151 dir = __create_dir_struct(dirFD); 152 if (dir == NULL) { 153 close(dirFD); 154 return NULL; 155 } 156 157 return dir; 158} 159 160 161DIR* 162opendir(const char* path) 163{ 164 DIR* dir; 165 166 int fd = _kern_open_dir(-1, path); 167 if (fd < 0) { 168 __set_errno(fd); 169 return NULL; 170 } 171 172 // allocate the DIR structure 173 if ((dir = __create_dir_struct(fd)) == NULL) { 174 _kern_close(fd); 175 return NULL; 176 } 177 178 return dir; 179} 180 181 182int 183closedir(DIR* dir) 184{ 185 int status; 186 187 if (dir == NULL) { 188 __set_errno(B_BAD_VALUE); 189 return -1; 190 } 191 192 status = _kern_close(dir->fd); 193 194 free(dir); 195 196 RETURN_AND_SET_ERRNO(status); 197} 198 199 200struct dirent* 201readdir(DIR* dir) 202{ 203 ssize_t count; 204 205 if (dir->seek_position != dir->current_position) { 206 if (do_seek_dir(dir) != 0) 207 return NULL; 208 } 209 210 if (dir->entries_left > 0) { 211 struct dirent *dirent 212 = (struct dirent *)((uint8 *)&dir->first_entry + dir->next_entry); 213 214 dir->entries_left--; 215 dir->next_entry += dirent->d_reclen; 216 dir->seek_position++; 217 dir->current_position++; 218 219 return dirent; 220 } 221 222 // we need to retrieve new entries 223 224 count = _kern_read_dir(dir->fd, &dir->first_entry, 225 (char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX); 226 if (count <= 0) { 227 if (count < 0) 228 __set_errno(count); 229 230 // end of directory 231 return NULL; 232 } 233 234 dir->entries_left = count - 1; 235 dir->next_entry = dir->first_entry.d_reclen; 236 dir->seek_position++; 237 dir->current_position++; 238 239 return &dir->first_entry; 240} 241 242 243int 244readdir_r(DIR* dir, struct dirent* entry, struct dirent** _result) 245{ 246 ssize_t count = _kern_read_dir(dir->fd, entry, sizeof(struct dirent) 247 + B_FILE_NAME_LENGTH, 1); 248 if (count < B_OK) 249 return count; 250 251 if (count == 0) { 252 // end of directory 253 *_result = NULL; 254 } else 255 *_result = entry; 256 257 return 0; 258} 259 260 261void 262rewinddir(DIR* dir) 263{ 264 dir->seek_position = 0; 265} 266 267 268void 269seekdir(DIR* dir, long int position) 270{ 271 dir->seek_position = position; 272} 273 274 275long int 276telldir(DIR* dir) 277{ 278 return dir->seek_position; 279} 280 281 282int 283dirfd(DIR* dir) 284{ 285 return dir->fd; 286} 287 288 289int 290alphasort(const struct dirent** entry1, const struct dirent** entry2) 291{ 292 return strcmp((*entry1)->d_name, (*entry2)->d_name); 293} 294 295 296int 297scandir(const char* path, struct dirent*** _entryArray, 298 int (*selectFunc)(const struct dirent*), 299 int (*compareFunc)(const struct dirent** entry1, 300 const struct dirent** entry2)) 301{ 302 struct dirent** array = NULL; 303 size_t arrayCapacity = 0; 304 size_t arrayCount = 0; 305 306 DIR* dir = opendir(path); 307 if (dir == NULL) 308 return -1; 309 310 while (true) { 311 struct dirent* copiedEntry; 312 313 struct dirent* entry = readdir(dir); 314 if (entry == NULL) 315 break; 316 317 // Check whether or not we should include this entry 318 if (selectFunc != NULL && !selectFunc(entry)) 319 continue; 320 321 copiedEntry = malloc(entry->d_reclen); 322 if (copiedEntry == NULL) 323 goto error; 324 325 memcpy(copiedEntry, entry, entry->d_reclen); 326 327 // Put it into the array 328 329 if (arrayCount == arrayCapacity) { 330 struct dirent** newArray; 331 332 // Enlarge array 333 if (arrayCapacity == 0) 334 arrayCapacity = 64; 335 else 336 arrayCapacity *= 2; 337 338 newArray = realloc(array, arrayCapacity * sizeof(void*)); 339 if (newArray == NULL) { 340 free(copiedEntry); 341 goto error; 342 } 343 344 array = newArray; 345 } 346 347 array[arrayCount++] = copiedEntry; 348 } 349 350 closedir(dir); 351 352 if (arrayCount > 0 && compareFunc != NULL) { 353 qsort(array, arrayCount, sizeof(void*), 354 (int (*)(const void*, const void*))compareFunc); 355 } 356 357 *_entryArray = array; 358 return arrayCount; 359 360error: 361 closedir(dir); 362 363 while (arrayCount-- > 0) 364 free(array[arrayCount]); 365 free(array); 366 367 return -1; 368} 369