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 if (fd < 0) { 130 __set_errno(EBADF); 131 return NULL; 132 } 133 134 // Since our standard file descriptors can't be used as directory file 135 // descriptors, we have to open a fresh one explicitly. 136 int dirFD = _kern_open_dir(fd, NULL); 137 if (dirFD < 0) { 138 __set_errno(dirFD); 139 return NULL; 140 } 141 142 // Since applications are allowed to use the file descriptor after a call 143 // to fdopendir() without changing its state (like for other *at() 144 // functions), we cannot close it now. 145 // We dup2() the new FD to the previous location instead. 146 if (dup2(dirFD, fd) == -1) { 147 close(fd); 148 } else { 149 close(dirFD); 150 dirFD = fd; 151 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 152 // reset close-on-exec which is cleared by dup() 153 } 154 155 DIR* dir = __create_dir_struct(dirFD); 156 if (dir == NULL) { 157 close(dirFD); 158 return NULL; 159 } 160 161 return dir; 162 } 163 164 165 DIR* 166 opendir(const char* path) 167 { 168 DIR* dir; 169 170 int fd = _kern_open_dir(AT_FDCWD, path); 171 if (fd < 0) { 172 __set_errno(fd); 173 return NULL; 174 } 175 176 // allocate the DIR structure 177 if ((dir = __create_dir_struct(fd)) == NULL) { 178 _kern_close(fd); 179 return NULL; 180 } 181 182 return dir; 183 } 184 185 186 int 187 closedir(DIR* dir) 188 { 189 int status; 190 191 if (dir == NULL) { 192 __set_errno(B_BAD_VALUE); 193 return -1; 194 } 195 196 status = _kern_close(dir->fd); 197 198 free(dir); 199 200 RETURN_AND_SET_ERRNO(status); 201 } 202 203 204 struct dirent* 205 readdir(DIR* dir) 206 { 207 ssize_t count; 208 209 if (dir->seek_position != dir->current_position) { 210 if (do_seek_dir(dir) != 0) 211 return NULL; 212 } 213 214 if (dir->entries_left > 0) { 215 struct dirent *dirent 216 = (struct dirent *)((uint8 *)&dir->first_entry + dir->next_entry); 217 218 dir->entries_left--; 219 dir->next_entry += dirent->d_reclen; 220 dir->seek_position++; 221 dir->current_position++; 222 223 return dirent; 224 } 225 226 // we need to retrieve new entries 227 228 count = _kern_read_dir(dir->fd, &dir->first_entry, 229 (char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX); 230 if (count <= 0) { 231 if (count < 0) 232 __set_errno(count); 233 234 // end of directory 235 return NULL; 236 } 237 238 dir->entries_left = count - 1; 239 dir->next_entry = dir->first_entry.d_reclen; 240 dir->seek_position++; 241 dir->current_position++; 242 243 return &dir->first_entry; 244 } 245 246 247 int 248 readdir_r(DIR* dir, struct dirent* entry, struct dirent** _result) 249 { 250 BPrivate::ErrnoMaintainer _; 251 errno = 0; 252 253 struct dirent* dirent = readdir(dir); 254 if (dirent == NULL) { 255 *_result = NULL; 256 return errno; 257 } 258 259 memcpy(entry, dirent, dirent->d_reclen); 260 *_result = entry; 261 return 0; 262 } 263 264 265 void 266 rewinddir(DIR* dir) 267 { 268 dir->seek_position = 0; 269 } 270 271 272 void 273 seekdir(DIR* dir, long int position) 274 { 275 dir->seek_position = position; 276 } 277 278 279 long int 280 telldir(DIR* dir) 281 { 282 return dir->seek_position; 283 } 284 285 286 int 287 dirfd(DIR* dir) 288 { 289 return dir->fd; 290 } 291