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 <ErrnoMaintainer.h> 18#include <syscalls.h> 19#include <syscall_utils.h> 20 21 22#define DIR_BUFFER_SIZE 4096 23 24 25struct __DIR { 26 int fd; 27 short next_entry; 28 unsigned short entries_left; 29 long seek_position; 30 long current_position; 31 struct dirent first_entry; 32}; 33 34 35static int 36do_seek_dir(DIR* dir) 37{ 38 if (dir->seek_position == dir->current_position) 39 return 0; 40 41 // If the seek position lies before the current position (the usual case), 42 // rewind to the beginning. 43 if (dir->seek_position < dir->current_position) { 44 status_t status = _kern_rewind_dir(dir->fd); 45 if (status < 0) { 46 __set_errno(status); 47 return -1; 48 } 49 50 dir->current_position = 0; 51 dir->entries_left = 0; 52 } 53 54 // Now skip entries until we have reached seek_position. 55 while (dir->seek_position > dir->current_position) { 56 ssize_t count; 57 long toSkip = dir->seek_position - dir->current_position; 58 if (toSkip == dir->entries_left) { 59 // we have to skip exactly all of the currently buffered entries 60 dir->current_position = dir->seek_position; 61 dir->entries_left = 0; 62 return 0; 63 } 64 65 if (toSkip < dir->entries_left) { 66 // we have to skip only some of the buffered entries 67 for (; toSkip > 0; toSkip--) { 68 struct dirent* entry = (struct dirent*) 69 ((uint8*)&dir->first_entry + dir->next_entry); 70 dir->entries_left--; 71 dir->next_entry += entry->d_reclen; 72 } 73 74 dir->current_position = dir->seek_position; 75 return 0; 76 } 77 78 // we have to skip more than the currently buffered entries 79 dir->current_position += dir->entries_left; 80 dir->entries_left = 0; 81 82 count = _kern_read_dir(dir->fd, &dir->first_entry, 83 (char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX); 84 if (count <= 0) { 85 if (count < 0) 86 __set_errno(count); 87 88 // end of directory 89 return -1; 90 } 91 92 dir->next_entry = 0; 93 dir->entries_left = count; 94 } 95 96 return 0; 97} 98 99 100// #pragma mark - private API 101 102 103DIR* 104__create_dir_struct(int fd) 105{ 106 /* allocate the memory for the DIR structure */ 107 108 DIR* dir = (DIR*)malloc(DIR_BUFFER_SIZE); 109 if (dir == NULL) { 110 __set_errno(B_NO_MEMORY); 111 return NULL; 112 } 113 114 dir->fd = fd; 115 dir->entries_left = 0; 116 dir->seek_position = 0; 117 dir->current_position = 0; 118 119 return dir; 120} 121 122 123// #pragma mark - public API 124 125 126DIR* 127fdopendir(int fd) 128{ 129 DIR* dir; 130 131 // Since our standard file descriptors can't be used as directory file 132 // descriptors, we have to open a fresh one explicitly. 133 int dirFD = _kern_open_dir(fd, NULL); 134 if (dirFD < 0) { 135 __set_errno(dirFD); 136 return NULL; 137 } 138 139 // Since applications are allowed to use the file descriptor after a call 140 // to fdopendir() without changing its state (like for other *at() 141 // functions), we cannot close it now. 142 // We dup2() the new FD to the previous location instead. 143 if (dup2(dirFD, fd) == -1) 144 close(fd); 145 else { 146 close(dirFD); 147 dirFD = fd; 148 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 149 // reset close-on-exec which is cleared by dup() 150 } 151 152 dir = __create_dir_struct(dirFD); 153 if (dir == NULL) { 154 close(dirFD); 155 return NULL; 156 } 157 158 return dir; 159} 160 161 162DIR* 163opendir(const char* path) 164{ 165 DIR* dir; 166 167 int fd = _kern_open_dir(-1, path); 168 if (fd < 0) { 169 __set_errno(fd); 170 return NULL; 171 } 172 173 // allocate the DIR structure 174 if ((dir = __create_dir_struct(fd)) == NULL) { 175 _kern_close(fd); 176 return NULL; 177 } 178 179 return dir; 180} 181 182 183int 184closedir(DIR* dir) 185{ 186 int status; 187 188 if (dir == NULL) { 189 __set_errno(B_BAD_VALUE); 190 return -1; 191 } 192 193 status = _kern_close(dir->fd); 194 195 free(dir); 196 197 RETURN_AND_SET_ERRNO(status); 198} 199 200 201struct dirent* 202readdir(DIR* dir) 203{ 204 ssize_t count; 205 206 if (dir->seek_position != dir->current_position) { 207 if (do_seek_dir(dir) != 0) 208 return NULL; 209 } 210 211 if (dir->entries_left > 0) { 212 struct dirent *dirent 213 = (struct dirent *)((uint8 *)&dir->first_entry + dir->next_entry); 214 215 dir->entries_left--; 216 dir->next_entry += dirent->d_reclen; 217 dir->seek_position++; 218 dir->current_position++; 219 220 return dirent; 221 } 222 223 // we need to retrieve new entries 224 225 count = _kern_read_dir(dir->fd, &dir->first_entry, 226 (char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX); 227 if (count <= 0) { 228 if (count < 0) 229 __set_errno(count); 230 231 // end of directory 232 return NULL; 233 } 234 235 dir->entries_left = count - 1; 236 dir->next_entry = dir->first_entry.d_reclen; 237 dir->seek_position++; 238 dir->current_position++; 239 240 return &dir->first_entry; 241} 242 243 244int 245readdir_r(DIR* dir, struct dirent* entry, struct dirent** _result) 246{ 247 BPrivate::ErrnoMaintainer _; 248 errno = 0; 249 250 struct dirent* dirent = readdir(dir); 251 if (dirent == NULL) { 252 *_result = NULL; 253 return errno; 254 } 255 256 memcpy(entry, dirent, dirent->d_reclen); 257 *_result = entry; 258 return 0; 259} 260 261 262void 263rewinddir(DIR* dir) 264{ 265 dir->seek_position = 0; 266} 267 268 269void 270seekdir(DIR* dir, long int position) 271{ 272 dir->seek_position = position; 273} 274 275 276long int 277telldir(DIR* dir) 278{ 279 return dir->seek_position; 280} 281 282 283int 284dirfd(DIR* dir) 285{ 286 return dir->fd; 287} 288