xref: /haiku/src/system/libroot/posix/dirent.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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