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 25 struct __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 35 static int 36 do_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 103 DIR* 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 126 DIR* 127 fdopendir(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 162 DIR* 163 opendir(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 183 int 184 closedir(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 201 struct dirent* 202 readdir(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 244 int 245 readdir_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 262 void 263 rewinddir(DIR* dir) 264 { 265 dir->seek_position = 0; 266 } 267 268 269 void 270 seekdir(DIR* dir, long int position) 271 { 272 dir->seek_position = position; 273 } 274 275 276 long int 277 telldir(DIR* dir) 278 { 279 return dir->seek_position; 280 } 281 282 283 int 284 dirfd(DIR* dir) 285 { 286 return dir->fd; 287 } 288