summaryrefslogtreecommitdiff
path: root/firmware/common/file_internal.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2013-08-05 22:02:45 -0400
committerMichael Sevakis <jethead71@rockbox.org>2014-08-30 03:48:23 +0200
commit7d1a47cf13726c95ac46027156cc12dd9da5b855 (patch)
treeeb20d07656806479a8e1fea25887a490ea30d1d8 /firmware/common/file_internal.c
parent95a4c3afcd53a1f8b835dec33de51f9c304de4d9 (diff)
downloadrockbox-7d1a47cf13726c95ac46027156cc12dd9da5b855.tar.gz
rockbox-7d1a47cf13726c95ac46027156cc12dd9da5b855.zip
Rewrite filesystem code (WIP)
This patch redoes the filesystem code from the FAT driver up to the clipboard code in onplay.c. Not every aspect of this is finished therefore it is still "WIP". I don't wish to do too much at once (haha!). What is left to do is get dircache back in the sim and find an implementation for the dircache indicies in the tagcache and playlist code or do something else that has the same benefit. Leaving these out for now does not make anything unusable. All the basics are done. Phone app code should probably get vetted (and app path handling just plain rewritten as environment expansions); the SDL app and Android run well. Main things addressed: 1) Thread safety: There is none right now in the trunk code. Most of what currently works is luck when multiple threads are involved or multiple descriptors to the same file are open. 2) POSIX compliance: Many of the functions behave nothing like their counterparts on a host system. This leads to inconsistent code or very different behavior from native to hosted. One huge offender was rename(). Going point by point would fill a book. 3) Actual running RAM usage: Many targets will use less RAM and less stack space (some more RAM because I upped the number of cache buffers for large memory). There's very little memory lying fallow in rarely-used areas (see 'Key core changes' below). Also, all targets may open the same number of directory streams whereas before those with less than 8MB RAM were limited to 8, not 12 implying those targets will save slightly less. 4) Performance: The test_disk plugin shows markedly improved performance, particularly in the area of (uncached) directory scanning, due partly to more optimal directory reading and to a better sector cache algorithm. Uncached times tend to be better while there is a bit of a slowdown in dircache due to it being a bit heavier of an implementation. It's not noticeable by a human as far as I can say. Key core changes: 1) Files and directories share core code and data structures. 2) The filesystem code knows which descriptors refer to same file. This ensures that changes from one stream are appropriately reflected in every open descriptor for that file (fileobj_mgr.c). 3) File and directory cache buffers are borrowed from the main sector cache. This means that when they are not in use by a file, they are not wasted, but used for the cache. Most of the time, only a few of them are needed. It also means that adding more file and directory handles is less expensive. All one must do in ensure a large enough cache to borrow from. 4) Relative path components are supported and the namespace is unified. It does not support full relative paths to an implied current directory; what is does support is use of "." and "..". Adding the former would not be very difficult. The namespace is unified in the sense that volumes may be specified several times along with relative parts, e.g.: "/<0>/foo/../../<1>/bar" :<=> "/<1>/bar". 5) Stack usage is down due to sharing of data, static allocation and less duplication of strings on the stack. This requires more serialization than I would like but since the number of threads is limited to a low number, the tradoff in favor of the stack seems reasonable. 6) Separates and heirarchicalizes (sic) the SIM and APP filesystem code. SIM path and volume handling is just like the target. Some aspects of the APP file code get more straightforward (e.g. no path hashing is needed). Dircache: Deserves its own section. Dircache is new but pays homage to the old. The old one was not compatible and so it, since it got redone, does all the stuff it always should have done such as: 1) It may be update and used at any time during the build process. No longer has one to wait for it to finish building to do basic file management (create, remove, rename, etc.). 2) It does not need to be either fully scanned or completely disabled; it can be incomplete (i.e. overfilled, missing paths), still be of benefit and be correct. 3) Handles mounting and dismounting of individual volumes which means a full rebuild is not needed just because you pop a new SD card in the slot. Now, because it reuses its freed entry data, may rebuild only that volume. 4) Much more fundamental to the file code. When it is built, it is the keeper of the master file list whether enabled or not ("disabled" is just a state of the cache). Its must always to ready to be started and bind all streams opened prior to being enabled. 5) Maintains any short filenames in OEM format which means that it does not need to be rebuilt when changing the default codepage. Miscellaneous Compatibility: 1) Update any other code that would otherwise not work such as the hotswap mounting code in various card drivers. 2) File management: Clipboard needed updating because of the behavioral changes. Still needs a little more work on some finer points. 3) Remove now-obsolete functionality such as the mutex's "no preempt" flag (which was only for the prior FAT driver). 4) struct dirinfo uses time_t rather than raw FAT directory entry time fields. I plan to follow up on genericizing everything there (i.e. no FAT attributes). 5) unicode.c needed some redoing so that the file code does not try try to load codepages during a scan, which is actually a problem with the current code. The default codepage, if any is required, is now kept in RAM separarately (bufalloced) from codepages specified to iso_decode() (which must not be bufalloced because the conversion may be done by playback threads). Brings with it some additional reusable core code: 1) Revised file functions: Reusable code that does things such as safe path concatenation and parsing without buffer limitations or data duplication. Variants that copy or alter the input path may be based off these. To do: 1) Put dircache functionality back in the sim. Treating it internally as a different kind of file system seems the best approach at this time. 2) Restore use of dircache indexes in the playlist and database or something effectively the same. Since the cache doesn't have to be complete in order to be used, not getting a hit on the cache doesn't unambiguously say if the path exists or not. Change-Id: Ia30f3082a136253e3a0eae0784e3091d138915c8 Reviewed-on: http://gerrit.rockbox.org/566 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested: Michael Sevakis <jethead71@rockbox.org>
Diffstat (limited to 'firmware/common/file_internal.c')
-rw-r--r--firmware/common/file_internal.c776
1 files changed, 776 insertions, 0 deletions
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
new file mode 100644
index 0000000000..ebe77f0c9f
--- /dev/null
+++ b/firmware/common/file_internal.c
@@ -0,0 +1,776 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include <errno.h>
23#include "system.h"
24#include "debug.h"
25#include "panic.h"
26#include "pathfuncs.h"
27#include "disk_cache.h"
28#include "fileobj_mgr.h"
29#include "dir.h"
30#include "dircache_redirect.h"
31#include "dircache.h"
32#include "string-extra.h"
33#include "rbunicode.h"
34
35/** Internal common filesystem service functions **/
36
37/* for internal functions' scanning use to save quite a bit of stack space -
38 access must be serialized by the writer lock */
39#if defined(CPU_SH) || defined(IAUDIO_M5)
40/* otherwise, out of IRAM */
41struct fat_direntry dir_fatent;
42#else
43struct fat_direntry dir_fatent IBSS_ATTR;
44#endif
45
46struct mrsw_lock file_internal_mrsw SHAREDBSS_ATTR;
47
48
49/** File stream sector caching **/
50
51/* initialize a new cache structure */
52void file_cache_init(struct filestr_cache *cachep)
53{
54 cachep->buffer = NULL;
55 cachep->sector = INVALID_SECNUM;
56 cachep->flags = 0;
57}
58
59/* discard and mark the cache buffer as unused */
60void file_cache_reset(struct filestr_cache *cachep)
61{
62 cachep->sector = INVALID_SECNUM;
63 cachep->flags = 0;
64}
65
66/* allocate resources attached to the cache */
67void file_cache_alloc(struct filestr_cache *cachep)
68{
69 /* if this fails, it is a bug; check for leaks and that the cache has
70 enough buffers for the worst case */
71 if (!cachep->buffer && !(cachep->buffer = dc_get_buffer()))
72 panicf("file_cache_alloc - OOM");
73}
74
75/* free resources attached to the cache */
76void file_cache_free(struct filestr_cache *cachep)
77{
78 if (cachep && cachep->buffer)
79 {
80 dc_release_buffer(cachep->buffer);
81 cachep->buffer = NULL;
82 }
83
84 file_cache_reset(cachep);
85}
86
87
88/** Stream base APIs **/
89
90static inline void filestr_clear(struct filestr_base *stream)
91{
92 stream->flags = 0;
93 stream->bindp = NULL;
94#if 0
95 stream->mtx = NULL;
96#endif
97}
98
99/* actually late-allocate the assigned cache */
100void filestr_alloc_cache(struct filestr_base *stream)
101{
102 file_cache_alloc(stream->cachep);
103}
104
105/* free the stream's cache buffer if it's its own */
106void filestr_free_cache(struct filestr_base *stream)
107{
108 if (stream->cachep == &stream->cache)
109 file_cache_free(stream->cachep);
110}
111
112/* assign a cache to the stream */
113void filestr_assign_cache(struct filestr_base *stream,
114 struct filestr_cache *cachep)
115{
116 if (cachep)
117 {
118 filestr_free_cache(stream);
119 stream->cachep = cachep;
120 }
121 else /* assign own cache */
122 {
123 file_cache_reset(&stream->cache);
124 stream->cachep = &stream->cache;
125 }
126}
127
128/* duplicate a cache into a stream's local cache */
129void filestr_copy_cache(struct filestr_base *stream,
130 struct filestr_cache *cachep)
131{
132 stream->cachep = &stream->cache;
133 stream->cache.sector = cachep->sector;
134 stream->cache.flags = cachep->flags;
135
136 if (cachep->buffer)
137 {
138 file_cache_alloc(&stream->cache);
139 memcpy(stream->cache.buffer, cachep->buffer, DC_CACHE_BUFSIZE);
140 }
141 else
142 {
143 file_cache_free(&stream->cache);
144 }
145}
146
147/* discard cache contents and invalidate it */
148void filestr_discard_cache(struct filestr_base *stream)
149{
150 file_cache_reset(stream->cachep);
151}
152
153/* Initialize the base descriptor */
154void filestr_base_init(struct filestr_base *stream)
155{
156 filestr_clear(stream);
157 file_cache_init(&stream->cache);
158 stream->cachep = &stream->cache;
159}
160
161/* free base descriptor resources */
162void filestr_base_destroy(struct filestr_base *stream)
163{
164 filestr_clear(stream);
165 filestr_free_cache(stream);
166}
167
168
169/** Internal directory service functions **/
170
171/* read the next directory entry and return its FS info */
172int uncached_readdir_internal(struct filestr_base *stream,
173 struct file_base_info *infop,
174 struct fat_direntry *fatent)
175{
176 return fat_readdir(&stream->fatstr, &infop->fatfile.e,
177 filestr_get_cache(stream), fatent);
178}
179
180/* rewind the FS directory to the beginning */
181void uncached_rewinddir_internal(struct file_base_info *infop)
182{
183 fat_rewinddir(&infop->fatfile.e);
184}
185
186/* check if the directory is empty (ie. only "." and/or ".." entries
187 exist at most) */
188int test_dir_empty_internal(struct filestr_base *stream)
189{
190 int rc;
191
192 struct file_base_info info;
193 fat_rewind(&stream->fatstr);
194 rewinddir_internal(&info);
195
196 while ((rc = readdir_internal(stream, &info, &dir_fatent)) > 0)
197 {
198 /* no OEM decoding is recessary for this simple check */
199 if (!is_dotdir_name(dir_fatent.name))
200 {
201 DEBUGF("Directory not empty\n");
202 FILE_ERROR_RETURN(ENOTEMPTY, -1);
203 }
204 }
205
206 if (rc < 0)
207 {
208 DEBUGF("I/O error checking directory: %d\n", rc);
209 FILE_ERROR_RETURN(EIO, rc * 10 - 2);
210 }
211
212 return 0;
213}
214
215/* iso decode the name to UTF-8 */
216void iso_decode_d_name(char *d_name)
217{
218 if (is_dotdir_name(d_name))
219 return;
220
221 char shortname[13];
222 size_t len = strlcpy(shortname, d_name, sizeof (shortname));
223 /* This MUST be the default codepage thus not something that could be
224 loaded on call */
225 iso_decode(shortname, d_name, -1, len + 1);
226}
227
228#ifdef HAVE_DIRCACHE
229/* nullify all the fields of the struct dirent */
230void empty_dirent(struct dirent *entry)
231{
232 entry->d_name[0] = '\0';
233 entry->info.attr = 0;
234 entry->info.size = 0;
235 entry->info.wrtdate = 0;
236 entry->info.wrttime = 0;
237}
238
239/* fill the native dirinfo from the static dir_fatent */
240void fill_dirinfo_native(struct dirinfo_native *dinp)
241{
242 struct fat_direntry *fatent = get_dir_fatent();
243 dinp->attr = fatent->attr;
244 dinp->size = fatent->filesize;
245 dinp->wrtdate = fatent->wrtdate;
246 dinp->wrttime = fatent->wrttime;
247}
248#endif /* HAVE_DIRCACHE */
249
250int uncached_readdir_dirent(struct filestr_base *stream,
251 struct dirscan_info *scanp,
252 struct dirent *entry)
253{
254 struct fat_direntry fatent;
255 int rc = fat_readdir(&stream->fatstr, &scanp->fatscan,
256 filestr_get_cache(stream), &fatent);
257
258 /* FAT driver clears the struct fat_dirent if nothing is returned */
259 strcpy(entry->d_name, fatent.name);
260 entry->info.attr = fatent.attr;
261 entry->info.size = fatent.filesize;
262 entry->info.wrtdate = fatent.wrtdate;
263 entry->info.wrttime = fatent.wrttime;
264
265 return rc;
266}
267
268/* rewind the FS directory pointer */
269void uncached_rewinddir_dirent(struct dirscan_info *scanp)
270{
271 fat_rewinddir(&scanp->fatscan);
272}
273
274
275/** open_stream_internal() helpers and types **/
276
277struct pathwalk
278{
279 const char *path; /* current location in input path */
280 unsigned int callflags; /* callflags parameter */
281 struct path_component_info *compinfo; /* compinfo parameter */
282 file_size_t filesize; /* size of the file */
283};
284
285struct pathwalk_component
286{
287 struct file_base_info info; /* basic file information */
288 const char *name; /* component name location in path */
289 uint16_t length; /* length of name of component */
290 uint16_t attr; /* attributes of this component */
291 struct pathwalk_component *nextp; /* parent if in use else next free */
292};
293
294#define WALK_RC_NOT_FOUND 0 /* successfully not found */
295#define WALK_RC_FOUND 1 /* found and opened */
296#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
297#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
298
299/* return another struct pathwalk_component from the pool, or NULL if the
300 pool is completely used */
301static void * pathwalk_comp_alloc_(struct pathwalk_component *parentp)
302{
303 /* static pool that goes to a depth of STATIC_COMP_NUM before allocating
304 elements from the stack */
305 static struct pathwalk_component aux_pathwalk[STATIC_PATHCOMP_NUM];
306 struct pathwalk_component *compp = NULL;
307
308 if (!parentp)
309 compp = &aux_pathwalk[0]; /* root */
310 else if (PTR_IN_ARRAY(aux_pathwalk, parentp, STATIC_PATHCOMP_NUM-1))
311 compp = parentp + 1;
312
313 return compp;
314}
315
316/* allocates components from the pool or stack depending upon the depth */
317#define pathwalk_comp_alloc(parentp) \
318 ({ \
319 void *__c = pathwalk_comp_alloc_(parentp); \
320 if (!__builtin_constant_p(parentp) && !__c) \
321 __c = alloca(sizeof (struct pathwalk_component)); \
322 (struct pathwalk_component *)__c; \
323 })
324
325/* fill in the details of the struct path_component_info for caller */
326static int fill_path_compinfo(struct pathwalk *walkp,
327 struct pathwalk_component *compp,
328 int rc)
329{
330 if (rc == -ENOENT)
331 {
332 /* this component wasn't found; see if more of them exist or path
333 has trailing separators; if it does, this component should be
334 interpreted as a directory even if it doesn't exist and it's the
335 final one; also, this has to be the last part or it's an error*/
336 const char *p = GOBBLE_PATH_SEPCH(walkp->path);
337 if (!*p)
338 {
339 if (p > walkp->path)
340 compp->attr |= ATTR_DIRECTORY;
341
342 rc = WALK_RC_NOT_FOUND; /* successfully not found */
343 }
344 }
345
346 if (rc >= 0)
347 {
348 struct path_component_info *compinfo = walkp->compinfo;
349 compinfo->name = compp->name;
350 compinfo->length = compp->length;
351 compinfo->attr = compp->attr;
352 compinfo->filesize = walkp->filesize;
353 compinfo->parentinfo = (compp->nextp ?: compp)->info;
354 }
355
356 return rc;
357}
358
359/* open the final stream itself, if found */
360static int walk_open_info(struct pathwalk *walkp,
361 struct pathwalk_component *compp,
362 int rc,
363 struct filestr_base *stream)
364{
365 /* this may make adjustments to things; do it first */
366 if (walkp->compinfo)
367 rc = fill_path_compinfo(walkp, compp, rc);
368
369 if (rc < 0 || rc == WALK_RC_NOT_FOUND)
370 return rc;
371
372 unsigned int callflags = walkp->callflags;
373 bool isdir = compp->attr & ATTR_DIRECTORY;
374
375 /* type must match what is called for */
376 switch (callflags & FF_TYPEMASK)
377 {
378 case FF_FILE:
379 if (!isdir) break;
380 DEBUGF("File is a directory\n");
381 return -EISDIR;
382 case FF_DIR:
383 if (isdir) break;
384 DEBUGF("File is not a directory\n");
385 return -ENOTDIR;
386 /* FF_ANYTYPE: basically, ignore FF_FILE/FF_DIR */
387 }
388
389 /* FO_DIRECTORY must match type */
390 if (isdir)
391 callflags |= FO_DIRECTORY;
392 else
393 callflags &= ~FO_DIRECTORY;
394
395 fileop_onopen_internal(stream, &compp->info, callflags);
396 return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
397}
398
399/* check the component against the prefix test info */
400static void walk_check_prefix(struct pathwalk *walkp,
401 struct pathwalk_component *compp)
402{
403 if (compp->attr & ATTR_PREFIX)
404 return;
405
406 if (!fat_file_is_same(&compp->info.fatfile,
407 &walkp->compinfo->prefixp->fatfile))
408 return;
409
410 compp->attr |= ATTR_PREFIX;
411}
412
413/* opens the component named by 'comp' in the directory 'parent' */
414static NO_INLINE int open_path_component(struct pathwalk *walkp,
415 struct pathwalk_component *compp,
416 struct filestr_base *stream)
417{
418 int rc;
419
420 /* create a null-terminated copy of the component name */
421 char *compname = strmemdupa(compp->name, compp->length);
422
423 unsigned int callflags = walkp->callflags;
424 struct pathwalk_component *parentp = compp->nextp;
425
426 /* children inherit the prefix coloring from the parent */
427 compp->attr = parentp->attr & ATTR_PREFIX;
428
429 /* most of the next would be abstracted elsewhere if doing other
430 filesystems */
431
432 /* scan parent for name; stream is converted to this parent */
433 file_cache_reset(stream->cachep);
434 stream->infop = &parentp->info;
435 fat_filestr_init(&stream->fatstr, &parentp->info.fatfile);
436 rewinddir_internal(&compp->info);
437
438 while ((rc = readdir_internal(stream, &compp->info, &dir_fatent)) > 0)
439 {
440 if (rc > 1 && !(callflags & FF_NOISO))
441 iso_decode_d_name(dir_fatent.name);
442
443 if (!strcasecmp(compname, dir_fatent.name))
444 break;
445 }
446
447 if (rc == 0)
448 {
449 DEBUGF("File/directory not found\n");
450 return -ENOENT;
451 }
452 else if (rc < 0)
453 {
454 DEBUGF("I/O error reading directory %d\n", rc);
455 return -EIO;
456 }
457
458 rc = fat_open(stream->fatstr.fatfilep, dir_fatent.firstcluster,
459 &compp->info.fatfile);
460 if (rc < 0)
461 {
462 DEBUGF("I/O error opening file/directory %s (%d)\n",
463 compname, rc);
464 return -EIO;
465 }
466
467 walkp->filesize = dir_fatent.filesize;
468 compp->attr |= dir_fatent.attr;
469
470 if (callflags & FF_CHECKPREFIX)
471 walk_check_prefix(walkp, compp);
472
473 return WALK_RC_FOUND;
474}
475
476/* parse a path component, open it and process the next */
477static NO_INLINE int
478walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
479 struct filestr_base *stream)
480{
481 int rc = WALK_RC_FOUND;
482
483 if (walkp->callflags & FF_CHECKPREFIX)
484 walk_check_prefix(walkp, compp);
485
486 /* alloca is used in a loop, but we reuse any blocks previously allocated
487 if we went up then back down; if the path takes us back to the root, then
488 everything is cleaned automatically */
489 struct pathwalk_component *freep = NULL;
490
491 const char *name;
492 ssize_t len;
493
494 while ((len = parse_path_component(&walkp->path, &name)))
495 {
496 /* whatever is to be a parent must be a directory */
497 if (!(compp->attr & ATTR_DIRECTORY))
498 return -ENOTDIR;
499
500 switch (len)
501 {
502 case 1:
503 case 2:
504 /* check for "." and ".." */
505 if (name[0] == '.')
506 {
507 if (len == 2 && name[1] == '.')
508 {
509 struct pathwalk_component *parentp = compp->nextp;
510 if (!parentp)
511 return WALK_RC_CONT_AT_ROOT;
512
513 compp->nextp = freep;
514 freep = compp;
515 compp = parentp;
516 }
517
518 break;
519 }
520
521 /* fallthrough */
522 default:
523 if (len >= MAX_NAME)
524 return -ENAMETOOLONG;
525
526 struct pathwalk_component *newp = freep;
527 if (!newp)
528 newp = pathwalk_comp_alloc(compp);
529 else
530 freep = freep->nextp;
531
532 newp->nextp = compp;
533 compp = newp;
534
535 compp->name = name;
536 compp->length = len;
537
538 rc = open_path_component(walkp, compp, stream);
539 if (rc < 0)
540 break;
541 }
542 }
543
544 return walk_open_info(walkp, compp, rc, stream);
545}
546
547/* open a stream given a path to the resource */
548int open_stream_internal(const char *path, unsigned int callflags,
549 struct filestr_base *stream,
550 struct path_component_info *compinfo)
551{
552 DEBUGF("%s(path=\"%s\",flg=%X,str=%p,compinfo=%p)\n", path, callflags,
553 stream, compinfo);
554 int rc;
555
556 filestr_base_init(stream);
557
558 if (!path_is_absolute(path))
559 {
560 /* while this supports relative components, there is currently no
561 current working directory concept at this level by which to
562 fully qualify the path (though that would not be excessively
563 difficult to add) */
564 DEBUGF("\"%s\" is not an absolute path\n"
565 "Only absolute paths currently supported.\n", path);
566 FILE_ERROR(path ? ENOENT : EFAULT, -1);
567 }
568
569 /* if !compinfo, then the result of this check is not visible anyway */
570 if (!compinfo)
571 callflags &= ~FF_CHECKPREFIX;
572
573 struct pathwalk walk;
574 walk.path = path;
575 walk.callflags = callflags;
576 walk.compinfo = compinfo;
577 walk.filesize = 0;
578
579 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
580 rootp->nextp = NULL;
581 rootp->attr = ATTR_DIRECTORY;
582
583#ifdef HAVE_MULTIVOLUME
584 int volume = 0, rootrc = WALK_RC_FOUND;
585#endif /* HAVE_MULTIVOLUME */
586
587 while (1)
588 {
589 const char *pathptr = walk.path;
590
591 #ifdef HAVE_MULTIVOLUME
592 /* this seamlessly integrates secondary filesystems into the
593 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
594 const char *p;
595 volume = path_strip_volume(pathptr, &p, false);
596 if (!CHECK_VOL(volume))
597 {
598 DEBUGF("No such device or address: %d\n", volume);
599 FILE_ERROR(ENXIO, -2);
600 }
601
602 /* the root of this subpath is the system root? */
603 rootrc = p == pathptr ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
604 walk.path = p;
605 #endif /* HAVE_MULTIVOLUME */
606
607 /* set name to start at last leading separator; names of volume
608 specifiers will be returned as "/<fooN>" */
609 rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
610 rootp->length =
611 IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
612
613 rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile);
614 if (rc < 0)
615 {
616 /* not mounted */
617 DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
618 rc = -ENXIO;
619 break;
620 }
621
622 get_rootinfo_internal(&rootp->info);
623 rc = walk_path(&walk, rootp, stream);
624 if (rc != WALK_RC_CONT_AT_ROOT)
625 break;
626 }
627
628 switch (rc)
629 {
630 case WALK_RC_FOUND_ROOT:
631 IF_MV( rc = rootrc; )
632 case WALK_RC_NOT_FOUND:
633 case WALK_RC_FOUND:
634 break;
635
636 default: /* utter, abject failure :`( */
637 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
638 filestr_base_destroy(stream);
639 FILE_ERROR(-rc, -2);
640 }
641
642 file_cache_reset(stream->cachep);
643
644file_error:
645 return rc;
646}
647
648/* close the stream referenced by 'stream' */
649int close_stream_internal(struct filestr_base *stream)
650{
651 int rc;
652 unsigned int foflags = fileobj_get_flags(stream);
653
654 if ((foflags & (FO_SINGLE|FO_REMOVED)) == (FO_SINGLE|FO_REMOVED))
655 {
656 /* nothing is referencing it so now remove the file's data */
657 rc = fat_remove(&stream->infop->fatfile, FAT_RM_DATA);
658 if (rc < 0)
659 {
660 DEBUGF("I/O error removing file data: %d\n", rc);
661 FILE_ERROR(EIO, rc * 10 - 1);
662 }
663 }
664
665 rc = 0;
666file_error:
667 /* close no matter what */
668 fileop_onclose_internal(stream);
669 return rc;
670}
671
672/* create a new stream in the parent directory */
673int create_stream_internal(struct file_base_info *parentinfop,
674 const char *basename, size_t length,
675 unsigned int attr, unsigned int callflags,
676 struct filestr_base *stream)
677{
678 /* assumes an attempt was made beforehand to open *stream with
679 open_stream_internal() which returned zero (successfully not found),
680 so does not initialize it here */
681 const char * const name = strmemdupa(basename, length);
682 DEBUGF("Creating \"%s\"\n", name);
683
684 struct file_base_info info;
685 int rc = fat_create_file(&parentinfop->fatfile, name, attr,
686 &info.fatfile, get_dir_fatent_dircache());
687 if (rc < 0)
688 {
689 DEBUGF("Create failed: %d\n", rc);
690 FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 1);
691 }
692
693 /* dir_fatent is implicit arg */
694 fileop_oncreate_internal(stream, &info, callflags, parentinfop, name);
695 rc = 0;
696file_error:
697 return rc;
698}
699
700/* removes files and directories - back-end to remove() and rmdir() */
701int remove_stream_internal(const char *path, struct filestr_base *stream,
702 unsigned int callflags)
703{
704 /* Only FF_* flags should be in callflags */
705 int rc;
706
707 struct filestr_base opened_stream;
708 if (!stream)
709 stream = &opened_stream;
710
711 if (stream == &opened_stream)
712 {
713 /* no stream provided so open local one */
714 rc = open_stream_internal(path, callflags, stream, NULL);
715 if (rc < 0)
716 {
717 DEBUGF("Failed opening path: %d\n", rc);
718 FILE_ERROR(ERRNO, rc * 10 - 1);
719 }
720 }
721 /* else ignore the 'path' argument */
722
723 if (callflags & FF_DIR)
724 {
725 /* directory to be removed must be empty */
726 rc = test_dir_empty_internal(stream);
727 if (rc < 0)
728 FILE_ERROR(ERRNO, rc * 10 - 2);
729 }
730
731 /* save old info since fat_remove() will destroy the dir info */
732 struct file_base_info oldinfo = *stream->infop;
733 rc = fat_remove(&stream->infop->fatfile, FAT_RM_DIRENTRIES);
734 if (rc < 0)
735 {
736 DEBUGF("I/O error removing dir entries: %d\n", rc);
737 FILE_ERROR(EIO, rc * 10 - 3);
738 }
739
740 fileop_onremove_internal(stream, &oldinfo);
741
742 rc = 0;
743file_error:
744 if (stream == &opened_stream)
745 {
746 /* will do removal of data below if this is the only reference */
747 int rc2 = close_stream_internal(stream);
748 if (rc2 < 0 && rc >= 0)
749 {
750 rc = rc2 * 10 - 4;
751 DEBUGF("Success but failed closing stream: %d\n", rc);
752 }
753 }
754
755 return rc;
756}
757
758/* test file/directory existence with constraints */
759int test_stream_exists_internal(const char *path, unsigned int callflags)
760{
761 /* only FF_* flags should be in callflags */
762 struct filestr_base stream;
763 int rc = open_stream_internal(path, callflags, &stream, NULL);
764 if (rc > 0)
765 close_stream_internal(&stream);
766
767 return rc;
768}
769
770/* one-time init at startup */
771void filesystem_init(void)
772{
773 mrsw_init(&file_internal_mrsw);
774 dc_init();
775 fileobj_mgr_init();
776}