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
do_seek_dir(DIR * dir)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*
__create_dir_struct(int fd)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*
fdopendir(int fd)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*
opendir(const char * path)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
closedir(DIR * dir)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*
readdir(DIR * dir)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
readdir_r(DIR * dir,struct dirent * entry,struct dirent ** _result)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
rewinddir(DIR * dir)266 rewinddir(DIR* dir)
267 {
268 dir->seek_position = 0;
269 }
270
271
272 void
seekdir(DIR * dir,long int position)273 seekdir(DIR* dir, long int position)
274 {
275 dir->seek_position = position;
276 }
277
278
279 long int
telldir(DIR * dir)280 telldir(DIR* dir)
281 {
282 return dir->seek_position;
283 }
284
285
286 int
dirfd(DIR * dir)287 dirfd(DIR* dir)
288 {
289 return dir->fd;
290 }
291