summaryrefslogtreecommitdiff
path: root/firmware/common/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/dir.c')
-rw-r--r--firmware/common/dir.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
new file mode 100644
index 0000000000..da798c71d5
--- /dev/null
+++ b/firmware/common/dir.c
@@ -0,0 +1,413 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Björn Stenberg
11 * Copyright (C) 2014 by Michael Sevakis
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#define DIRFUNCTIONS_DEFINED
23#include "config.h"
24#include <errno.h>
25#include <string.h>
26#include "debug.h"
27#include "dir.h"
28#include "pathfuncs.h"
29#include "fileobj_mgr.h"
30#include "dircache_redirect.h"
31
32/* structure used for open directory streams */
33static struct dirstr_desc
34{
35 struct filestr_base stream; /* basic stream info (first!) */
36 struct dirscan_info scan; /* directory scan cursor */
37 struct dirent entry; /* current parsed entry information */
38#ifdef HAVE_MULTIVOLUME
39 int volumecounter; /* counter for root volume entries */
40#endif
41} open_streams[MAX_OPEN_DIRS];
42
43/* check and return a struct dirstr_desc* from a DIR* */
44static struct dirstr_desc * get_dirstr(DIR *dirp)
45{
46 struct dirstr_desc *dir = (struct dirstr_desc *)dirp;
47
48 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
49 dir = NULL;
50 else if (dir->stream.flags & FDO_BUSY)
51 return dir;
52
53 int errnum;
54
55 if (!dir)
56 {
57 errnum = EFAULT;
58 }
59 else if (dir->stream.flags == FV_NONEXIST)
60 {
61 DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams));
62 errnum = ENXIO;
63 }
64 else
65 {
66 DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams));
67 errnum = EBADF;
68 }
69
70 errno = errnum;
71 return NULL;
72}
73
74#define GET_DIRSTR(type, dirp) \
75 ({ \
76 file_internal_lock_##type(); \
77 struct dirstr_desc *_dir = get_dirstr(dirp); \
78 if (_dir) \
79 FILESTR_LOCK(type, &_dir->stream); \
80 else \
81 file_internal_unlock_##type(); \
82 _dir; \
83 })
84
85/* release the lock on the dirstr_desc* */
86#define RELEASE_DIRSTR(type, dir) \
87 ({ \
88 FILESTR_UNLOCK(type, &(dir)->stream); \
89 file_internal_unlock_##type(); \
90 })
91
92
93/* find a free dir stream descriptor */
94static struct dirstr_desc * alloc_dirstr(void)
95{
96 for (unsigned int dd = 0; dd < MAX_OPEN_DIRS; dd++)
97 {
98 struct dirstr_desc *dir = &open_streams[dd];
99 if (!dir->stream.flags)
100 return dir;
101 }
102
103 DEBUGF("Too many dirs open\n");
104 return NULL;
105}
106
107#ifdef HAVE_MULTIVOLUME
108static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry)
109{
110 /* Volumes (secondary file systems) get inserted into the system root
111 * directory. If the path specified volume 0, enumeration will not
112 * include other volumes, but just its own files and directories.
113 *
114 * Fake special directories, which don't really exist, that will get
115 * redirected upon opendir()
116 */
117 while (++dir->volumecounter < NUM_VOLUMES)
118 {
119 /* on the system root */
120 if (!fat_ismounted(dir->volumecounter))
121 continue;
122
123 get_volume_name(dir->volumecounter, entry->d_name);
124 dir->entry.info.attr = ATTR_MOUNT_POINT;
125 dir->entry.info.size = 0;
126 dir->entry.info.wrtdate = 0;
127 dir->entry.info.wrttime = 0;
128 return 1;
129 }
130
131 /* do normal directory entry fetching */
132 return 0;
133}
134#endif /* HAVE_MULTIVOLUME */
135
136static inline int readdir_volume(struct dirstr_desc *dir,
137 struct dirent *entry)
138{
139#ifdef HAVE_MULTIVOLUME
140 /* fetch virtual volume entries? */
141 if (dir->volumecounter < NUM_VOLUMES)
142 return readdir_volume_inner(dir, entry);
143#endif /* HAVE_MULTIVOLUME */
144
145 /* do normal directory entry fetching */
146 return 0;
147 (void)dir; (void)entry;
148}
149
150
151/** POSIX interface **/
152
153/* open a directory */
154DIR * opendir(const char *dirname)
155{
156 DEBUGF("opendir(dirname=\"%s\"\n", dirname);
157
158 DIR *dirp = NULL;
159
160 file_internal_lock_WRITER();
161
162 int rc;
163
164 struct dirstr_desc * const dir = alloc_dirstr();
165 if (!dir)
166 FILE_ERROR(EMFILE, RC);
167
168 rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL);
169 if (rc < 0)
170 {
171 DEBUGF("Open failed: %d\n", rc);
172 FILE_ERROR(ERRNO, RC);
173 }
174
175#ifdef HAVE_MULTIVOLUME
176 /* volume counter is relevant only to the system root */
177 dir->volumecounter = rc > 1 ? 0 : INT_MAX;
178#endif /* HAVE_MULTIVOLUME */
179
180 fat_rewind(&dir->stream.fatstr);
181 rewinddir_dirent(&dir->scan);
182
183 dirp = (DIR *)dir;
184file_error:
185 file_internal_unlock_WRITER();
186 return dirp;
187}
188
189/* close a directory stream */
190int closedir(DIR *dirp)
191{
192 int rc;
193
194 file_internal_lock_WRITER();
195
196 /* needs to work even if marked "nonexistant" */
197 struct dirstr_desc * const dir = (struct dirstr_desc *)dirp;
198 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
199 FILE_ERROR(EFAULT, -1);
200
201 if (!dir->stream.flags)
202 {
203 DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams));
204 FILE_ERROR(EBADF, -2);
205 }
206
207 rc = close_stream_internal(&dir->stream);
208 if (rc < 0)
209 FILE_ERROR(ERRNO, rc * 10 - 3);
210
211file_error:
212 file_internal_unlock_WRITER();
213 return rc;
214}
215
216/* read a directory */
217struct dirent * readdir(DIR *dirp)
218{
219 struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
220 if (!dir)
221 FILE_ERROR_RETURN(ERRNO, NULL);
222
223 struct dirent *res = NULL;
224
225 int rc = readdir_volume(dir, &dir->entry);
226 if (rc == 0)
227 {
228 rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
229 if (rc < 0)
230 FILE_ERROR(EIO, RC);
231 }
232
233 if (rc > 0)
234 res = &dir->entry;
235
236file_error:
237 RELEASE_DIRSTR(READER, dir);
238
239 if (rc > 1)
240 iso_decode_d_name(res->d_name);
241
242 return res;
243}
244
245#if 0 /* not included now but probably should be */
246/* read a directory (reentrant) */
247int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
248{
249 if (!result)
250 FILE_ERROR_RETURN(EFAULT, -2);
251
252 *result = NULL;
253
254 if (!entry)
255 FILE_ERROR_RETURN(EFAULT, -3);
256
257 struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
258 if (!dir)
259 FILE_ERROR_RETURN(ERRNO, -1);
260
261 int rc = readdir_volume(dir, entry);
262 if (rc == 0)
263 {
264 rc = readdir_dirent(&dir->stream, &dir->scan, entry);
265 if (rc < 0)
266 FILE_ERROR(EIO, rc * 10 - 4);
267 }
268
269file_error:
270 RELEASE_DIRSTR(READER, dir);
271
272 if (rc > 0)
273 {
274 if (rc > 1)
275 iso_decode_d_name(entry->d_name);
276
277 *result = entry;
278 rc = 0;
279 }
280
281 return rc;
282}
283
284/* reset the position of a directory stream to the beginning of a directory */
285void rewinddir(DIR *dirp)
286{
287 struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
288 if (!dir)
289 FILE_ERROR_RETURN(ERRNO);
290
291 rewinddir_dirent(&dir->scan);
292
293#ifdef HAVE_MULTIVOLUME
294 if (dir->volumecounter != INT_MAX)
295 dir->volumecounter = 0;
296#endif /* HAVE_MULTIVOLUME */
297
298 RELEASE_DIRSTR(READER, dir);
299}
300
301#endif /* 0 */
302
303/* make a directory */
304int mkdir(const char *path)
305{
306 DEBUGF("mkdir(path=\"%s\")\n", path);
307
308 int rc;
309
310 file_internal_lock_WRITER();
311
312 struct filestr_base stream;
313 struct path_component_info compinfo;
314 rc = open_stream_internal(path, FF_DIR, &stream, &compinfo);
315 if (rc < 0)
316 {
317 DEBUGF("Can't open parent dir or path is not a directory\n");
318 FILE_ERROR(ERRNO, rc * 10 - 1);
319 }
320 else if (rc > 0)
321 {
322 DEBUGF("File exists\n");
323 FILE_ERROR(EEXIST, -2);
324 }
325
326 rc = create_stream_internal(&compinfo.parentinfo, compinfo.name,
327 compinfo.length, ATTR_NEW_DIRECTORY,
328 FO_DIRECTORY, &stream);
329 if (rc < 0)
330 FILE_ERROR(ERRNO, rc * 10 - 3);
331
332 rc = 0;
333file_error:
334 close_stream_internal(&stream);
335 file_internal_unlock_WRITER();
336 return rc;
337}
338
339/* remove a directory */
340int rmdir(const char *name)
341{
342 DEBUGF("rmdir(name=\"%s\")\n", name);
343
344 if (name)
345 {
346 /* path may not end with "." */
347 const char *basename;
348 size_t len = path_basename(name, &basename);
349 if (basename[0] == '.' && len == 1)
350 {
351 DEBUGF("Invalid path; last component is \".\"\n");
352 FILE_ERROR_RETURN(EINVAL, -9);
353 }
354 }
355
356 file_internal_lock_WRITER();
357 int rc = remove_stream_internal(name, NULL, FF_DIR);
358 file_internal_unlock_WRITER();
359 return rc;
360}
361
362
363/** Extended interface **/
364
365/* return if two directory streams refer to the same directory */
366int samedir(DIR *dirp1, DIR *dirp2)
367{
368 struct dirstr_desc * const dir1 = GET_DIRSTR(WRITER, dirp1);
369 if (!dir1)
370 FILE_ERROR_RETURN(ERRNO, -1);
371
372 int rc = -2;
373
374 struct dirstr_desc * const dir2 = get_dirstr(dirp2);
375 if (dir2)
376 rc = dir1->stream.bindp == dir2->stream.bindp ? 1 : 0;
377
378 RELEASE_DIRSTR(WRITER, dir1);
379 return rc;
380}
381
382/* test directory existence (returns 'false' if a file) */
383bool dir_exists(const char *dirname)
384{
385 file_internal_lock_WRITER();
386 bool rc = test_stream_exists_internal(dirname, FF_DIR) > 0;
387 file_internal_unlock_WRITER();
388 return rc;
389}
390
391/* get the portable info from the native entry */
392struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry)
393{
394 int rc;
395 if (!dirp || !entry)
396 FILE_ERROR(EFAULT, RC);
397
398 if (entry->d_name[0] == '\0')
399 FILE_ERROR(ENOENT, RC);
400
401 if ((file_size_t)entry->info.size > FILE_SIZE_MAX)
402 FILE_ERROR(EOVERFLOW, RC);
403
404 return (struct dirinfo)
405 {
406 .attribute = entry->info.attr,
407 .size = entry->info.size,
408 .mtime = fattime_mktime(entry->info.wrtdate, entry->info.wrttime),
409 };
410
411file_error:
412 return (struct dirinfo){ .attribute = 0 };
413}