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