summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2022-03-03 07:37:03 -0500
committerWilliam Wilgus <wilgus.william@gmail.com>2022-03-03 18:58:07 -0500
commit9daacabd658508d2607a64b288c9bce7a635fb15 (patch)
treece96538ea82a4176f00f8eb9531711db7dea5750
parentf88ea12bacf381ad4f39ba2328c806e772c0dda8 (diff)
downloadrockbox-9daacabd658508d2607a64b288c9bce7a635fb15.tar.gz
rockbox-9daacabd658508d2607a64b288c9bce7a635fb15.zip
[RESTORED!] Allow mounting of any directory as the root directory.
Provide definitions for the macros: * RB_ROOT_VOL_HIDDEN(v) to exclude certain items from the root. * RB_ROOT_CONTENTS to return a string with the name of the directory to mount in the root. Defaults are in export/rbpaths.h It's a bit much for those that don't need the full functionality. Some conditional define can cut it back a lot to cut out things only needed if alternate root mounts are required. I'm just not bothering yet. The basic concept would be applied to all targets to keep file code from forking too much. Change-Id: I3b5a14c530ff4b10d97f67636237d96875eb8969 Author: Michael Sevakis
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/common/dir.c93
-rw-r--r--firmware/common/dircache.c13
-rw-r--r--firmware/common/disk.c2
-rw-r--r--firmware/common/file.c2
-rw-r--r--firmware/common/file_internal.c100
-rw-r--r--firmware/common/fileobj_mgr.c103
-rw-r--r--firmware/common/pathfuncs.c39
-rw-r--r--firmware/common/rb_namespace.c291
-rw-r--r--firmware/export/mv.h4
-rw-r--r--firmware/export/pathfuncs.h6
-rw-r--r--firmware/export/rbpaths.h3
-rw-r--r--firmware/include/dircache_redirect.h18
-rw-r--r--firmware/include/file_internal.h32
-rw-r--r--firmware/include/fileobj_mgr.h5
-rw-r--r--firmware/include/fs_defines.h6
-rw-r--r--firmware/include/rb_namespace.h79
-rw-r--r--uisimulator/common/filesystem-sim.c2
18 files changed, 583 insertions, 216 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index e8f9da1a07..4ea922af1b 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -259,6 +259,7 @@ common/dircache.c
259common/pathfuncs.c 259common/pathfuncs.c
260common/fdprintf.c 260common/fdprintf.c
261common/linked_list.c 261common/linked_list.c
262common/rb_namespace.c
262common/strcasecmp.c 263common/strcasecmp.c
263common/strcasestr.c 264common/strcasestr.c
264common/strnatcmp.c 265common/strnatcmp.c
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index 245947b134..45749b8474 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -28,17 +28,14 @@
28#include "pathfuncs.h" 28#include "pathfuncs.h"
29#include "timefuncs.h" 29#include "timefuncs.h"
30#include "fileobj_mgr.h" 30#include "fileobj_mgr.h"
31#include "dircache_redirect.h" 31#include "rb_namespace.h"
32 32
33/* structure used for open directory streams */ 33/* structure used for open directory streams */
34static struct dirstr_desc 34static struct dirstr_desc
35{ 35{
36 struct filestr_base stream; /* basic stream info (first!) */ 36 struct filestr_base stream; /* basic stream info (first!) */
37 struct dirscan_info scan; /* directory scan cursor */ 37 struct ns_scan_info scan; /* directory scan cursor */
38 struct dirent entry; /* current parsed entry information */ 38 struct dirent entry; /* current parsed entry information */
39#ifdef HAVE_MULTIVOLUME
40 int volumecounter; /* counter for root volume entries */
41#endif
42} open_streams[MAX_OPEN_DIRS]; 39} open_streams[MAX_OPEN_DIRS];
43 40
44/* check and return a struct dirstr_desc* from a DIR* */ 41/* check and return a struct dirstr_desc* from a DIR* */
@@ -48,7 +45,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
48 45
49 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS)) 46 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
50 dir = NULL; 47 dir = NULL;
51 else if (dir->stream.flags & FDO_BUSY) 48 else if (dir->stream.flags & (FDO_BUSY|FD_VALID))
52 return dir; 49 return dir;
53 50
54 int errnum; 51 int errnum;
@@ -105,50 +102,6 @@ static struct dirstr_desc * alloc_dirstr(void)
105 return NULL; 102 return NULL;
106} 103}
107 104
108#ifdef HAVE_MULTIVOLUME
109static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry)
110{
111 /* Volumes (secondary file systems) get inserted into the system root
112 * directory. If the path specified volume 0, enumeration will not
113 * include other volumes, but just its own files and directories.
114 *
115 * Fake special directories, which don't really exist, that will get
116 * redirected upon opendir()
117 */
118 while (++dir->volumecounter < NUM_VOLUMES)
119 {
120 /* on the system root */
121 if (!fat_ismounted(dir->volumecounter))
122 continue;
123
124 get_volume_name(dir->volumecounter, entry->d_name);
125 dir->entry.info.attr = ATTR_MOUNT_POINT;
126 dir->entry.info.size = 0;
127 dir->entry.info.wrtdate = 0;
128 dir->entry.info.wrttime = 0;
129 return 1;
130 }
131
132 /* do normal directory entry fetching */
133 return 0;
134}
135#endif /* HAVE_MULTIVOLUME */
136
137static inline int readdir_volume(struct dirstr_desc *dir,
138 struct dirent *entry)
139{
140#ifdef HAVE_MULTIVOLUME
141 /* fetch virtual volume entries? */
142 if (dir->volumecounter < NUM_VOLUMES)
143 return readdir_volume_inner(dir, entry);
144#endif /* HAVE_MULTIVOLUME */
145
146 /* do normal directory entry fetching */
147 return 0;
148 (void)dir; (void)entry;
149}
150
151
152/** POSIX interface **/ 105/** POSIX interface **/
153 106
154/* open a directory */ 107/* open a directory */
@@ -166,21 +119,13 @@ DIR * opendir(const char *dirname)
166 if (!dir) 119 if (!dir)
167 FILE_ERROR(EMFILE, RC); 120 FILE_ERROR(EMFILE, RC);
168 121
169 rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL); 122 rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan);
170 if (rc < 0) 123 if (rc < 0)
171 { 124 {
172 DEBUGF("Open failed: %d\n", rc); 125 DEBUGF("Open failed: %d\n", rc);
173 FILE_ERROR(ERRNO, RC); 126 FILE_ERROR(ERRNO, RC);
174 } 127 }
175 128
176#ifdef HAVE_MULTIVOLUME
177 /* volume counter is relevant only to the system root */
178 dir->volumecounter = rc > 1 ? 0 : INT_MAX;
179#endif /* HAVE_MULTIVOLUME */
180
181 fat_rewind(&dir->stream.fatstr);
182 rewinddir_dirent(&dir->scan);
183
184 dirp = (DIR *)dir; 129 dirp = (DIR *)dir;
185file_error: 130file_error:
186 file_internal_unlock_WRITER(); 131 file_internal_unlock_WRITER();
@@ -205,7 +150,7 @@ int closedir(DIR *dirp)
205 FILE_ERROR(EBADF, -2); 150 FILE_ERROR(EBADF, -2);
206 } 151 }
207 152
208 rc = close_stream_internal(&dir->stream); 153 rc = ns_close_stream(&dir->stream);
209 if (rc < 0) 154 if (rc < 0)
210 FILE_ERROR(ERRNO, rc * 10 - 3); 155 FILE_ERROR(ERRNO, rc * 10 - 3);
211 156
@@ -223,16 +168,11 @@ struct dirent * readdir(DIR *dirp)
223 168
224 struct dirent *res = NULL; 169 struct dirent *res = NULL;
225 170
226 int rc = readdir_volume(dir, &dir->entry); 171 int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
227 if (rc == 0)
228 {
229 rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
230 if (rc < 0)
231 FILE_ERROR(EIO, RC);
232 }
233
234 if (rc > 0) 172 if (rc > 0)
235 res = &dir->entry; 173 res = &dir->entry;
174 else if (rc < 0)
175 FILE_ERROR(EIO, RC);
236 176
237file_error: 177file_error:
238 RELEASE_DIRSTR(READER, dir); 178 RELEASE_DIRSTR(READER, dir);
@@ -259,13 +199,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
259 if (!dir) 199 if (!dir)
260 FILE_ERROR_RETURN(ERRNO, -1); 200 FILE_ERROR_RETURN(ERRNO, -1);
261 201
262 int rc = readdir_volume(dir, entry); 202 int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry);
263 if (rc == 0) 203 if (rc < 0)
264 { 204 FILE_ERROR(EIO, rc * 10 - 4);
265 rc = readdir_dirent(&dir->stream, &dir->scan, entry);
266 if (rc < 0)
267 FILE_ERROR(EIO, rc * 10 - 4);
268 }
269 205
270file_error: 206file_error:
271 RELEASE_DIRSTR(READER, dir); 207 RELEASE_DIRSTR(READER, dir);
@@ -289,12 +225,7 @@ void rewinddir(DIR *dirp)
289 if (!dir) 225 if (!dir)
290 FILE_ERROR_RETURN(ERRNO); 226 FILE_ERROR_RETURN(ERRNO);
291 227
292 rewinddir_dirent(&dir->scan); 228 ns_dirscan_rewind(&dir->scan);
293
294#ifdef HAVE_MULTIVOLUME
295 if (dir->volumecounter != INT_MAX)
296 dir->volumecounter = 0;
297#endif /* HAVE_MULTIVOLUME */
298 229
299 RELEASE_DIRSTR(READER, dir); 230 RELEASE_DIRSTR(READER, dir);
300} 231}
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 3b880d3382..7a84b761a0 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -1473,7 +1473,7 @@ static void sab_process_volume(struct dircache_volume *dcvolp)
1473 */ 1473 */
1474int dircache_readdir_dirent(struct filestr_base *stream, 1474int dircache_readdir_dirent(struct filestr_base *stream,
1475 struct dirscan_info *scanp, 1475 struct dirscan_info *scanp,
1476 struct dirent *entry) 1476 struct DIRENT *entry)
1477{ 1477{
1478 struct file_base_info *dirinfop = stream->infop; 1478 struct file_base_info *dirinfop = stream->infop;
1479 1479
@@ -1760,7 +1760,7 @@ static int sab_process_volume(IF_MV(int volume,) struct dircache_entry *ce)
1760 return sab_process_dir(ce); 1760 return sab_process_dir(ce);
1761} 1761}
1762 1762
1763int dircache_readdir_r(struct dircache_dirscan *dir, struct dirent *result) 1763int dircache_readdir_r(struct dircache_dirscan *dir, struct DIRENT *result)
1764{ 1764{
1765 if (dircache_state != DIRCACHE_READY) 1765 if (dircache_state != DIRCACHE_READY)
1766 return readdir_r(dir->###########3, result, &result); 1766 return readdir_r(dir->###########3, result, &result);
@@ -2541,13 +2541,10 @@ static ssize_t get_path_sub(int idx, struct get_path_sub_data *data)
2541 cename = ""; 2541 cename = "";
2542 2542
2543 #ifdef HAVE_MULTIVOLUME 2543 #ifdef HAVE_MULTIVOLUME
2544 /* prepend the volume specifier */
2544 int volume = IF_MV_VOL(-idx - 1); 2545 int volume = IF_MV_VOL(-idx - 1);
2545 if (volume > 0) 2546 cename = alloca(VOL_MAX_LEN+1);
2546 { 2547 get_volume_name(volume, cename);
2547 /* prepend the volume specifier for volumes > 0 */
2548 cename = alloca(VOL_MAX_LEN+1);
2549 get_volume_name(volume, cename);
2550 }
2551 #endif /* HAVE_MULTIVOLUME */ 2548 #endif /* HAVE_MULTIVOLUME */
2552 2549
2553 data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum, 2550 data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum,
diff --git a/firmware/common/disk.c b/firmware/common/disk.c
index c096878e86..c6fbc34409 100644
--- a/firmware/common/disk.c
+++ b/firmware/common/disk.c
@@ -27,7 +27,7 @@
27#include "disk_cache.h" 27#include "disk_cache.h"
28#include "fileobj_mgr.h" 28#include "fileobj_mgr.h"
29#include "dir.h" 29#include "dir.h"
30#include "dircache_redirect.h" 30#include "rb_namespace.h"
31#include "disk.h" 31#include "disk.h"
32 32
33#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER) 33#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER)
diff --git a/firmware/common/file.c b/firmware/common/file.c
index c048d182f4..794d0059a9 100644
--- a/firmware/common/file.c
+++ b/firmware/common/file.c
@@ -28,7 +28,7 @@
28#include "file.h" 28#include "file.h"
29#include "fileobj_mgr.h" 29#include "fileobj_mgr.h"
30#include "disk_cache.h" 30#include "disk_cache.h"
31#include "dircache_redirect.h" 31#include "rb_namespace.h"
32#include "string-extra.h" 32#include "string-extra.h"
33 33
34/** 34/**
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
index b92c4ea115..14db247347 100644
--- a/firmware/common/file_internal.c
+++ b/firmware/common/file_internal.c
@@ -26,9 +26,7 @@
26#include "pathfuncs.h" 26#include "pathfuncs.h"
27#include "disk_cache.h" 27#include "disk_cache.h"
28#include "fileobj_mgr.h" 28#include "fileobj_mgr.h"
29#include "dir.h" 29#include "rb_namespace.h"
30#include "dircache_redirect.h"
31#include "dircache.h"
32#include "string-extra.h" 30#include "string-extra.h"
33#include "rbunicode.h" 31#include "rbunicode.h"
34 32
@@ -89,9 +87,9 @@ void file_cache_free(struct filestr_cache *cachep)
89 87
90/** Stream base APIs **/ 88/** Stream base APIs **/
91 89
92static inline void filestr_clear(struct filestr_base *stream) 90static inline void filestr_clear(struct filestr_base *stream, unsigned int flags)
93{ 91{
94 stream->flags = 0; 92 stream->flags = flags;
95 stream->bindp = NULL; 93 stream->bindp = NULL;
96#if 0 94#if 0
97 stream->mtx = NULL; 95 stream->mtx = NULL;
@@ -155,7 +153,7 @@ void filestr_discard_cache(struct filestr_base *stream)
155/* Initialize the base descriptor */ 153/* Initialize the base descriptor */
156void filestr_base_init(struct filestr_base *stream) 154void filestr_base_init(struct filestr_base *stream)
157{ 155{
158 filestr_clear(stream); 156 filestr_clear(stream, FD_VALID);
159 file_cache_init(&stream->cache); 157 file_cache_init(&stream->cache);
160 stream->cachep = &stream->cache; 158 stream->cachep = &stream->cache;
161} 159}
@@ -163,7 +161,7 @@ void filestr_base_init(struct filestr_base *stream)
163/* free base descriptor resources */ 161/* free base descriptor resources */
164void filestr_base_destroy(struct filestr_base *stream) 162void filestr_base_destroy(struct filestr_base *stream)
165{ 163{
166 filestr_clear(stream); 164 filestr_clear(stream, 0);
167 filestr_free_cache(stream); 165 filestr_free_cache(stream);
168} 166}
169 167
@@ -229,7 +227,7 @@ void iso_decode_d_name(char *d_name)
229 227
230#ifdef HAVE_DIRCACHE 228#ifdef HAVE_DIRCACHE
231/* nullify all the fields of the struct dirent */ 229/* nullify all the fields of the struct dirent */
232void empty_dirent(struct dirent *entry) 230void empty_dirent(struct DIRENT *entry)
233{ 231{
234 entry->d_name[0] = '\0'; 232 entry->d_name[0] = '\0';
235 entry->info.attr = 0; 233 entry->info.attr = 0;
@@ -251,7 +249,7 @@ void fill_dirinfo_native(struct dirinfo_native *dinp)
251 249
252int uncached_readdir_dirent(struct filestr_base *stream, 250int uncached_readdir_dirent(struct filestr_base *stream,
253 struct dirscan_info *scanp, 251 struct dirscan_info *scanp,
254 struct dirent *entry) 252 struct DIRENT *entry)
255{ 253{
256 struct fat_direntry fatent; 254 struct fat_direntry fatent;
257 int rc = fat_readdir(&stream->fatstr, &scanp->fatscan, 255 int rc = fat_readdir(&stream->fatstr, &scanp->fatscan,
@@ -295,7 +293,7 @@ struct pathwalk_component
295 293
296#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */ 294#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
297#define WALK_RC_FOUND 1 /* found and opened */ 295#define WALK_RC_FOUND 1 /* found and opened */
298#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */ 296#define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */
299#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */ 297#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
300 298
301/* return another struct pathwalk_component from the pool, or NULL if the 299/* return another struct pathwalk_component from the pool, or NULL if the
@@ -399,10 +397,9 @@ static int walk_open_info(struct pathwalk *walkp,
399 397
400 /* make open official if not simply probing for presence - must do it here 398 /* make open official if not simply probing for presence - must do it here
401 or compp->info on stack will get destroyed before it was copied */ 399 or compp->info on stack will get destroyed before it was copied */
402 if (!(callflags & FF_PROBE)) 400 if (!(callflags & (FF_PROBE|FF_NOFS)))
403 fileop_onopen_internal(stream, &compp->info, callflags); 401 fileop_onopen_internal(stream, &compp->info, callflags);
404 402 return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
405 return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
406} 403}
407 404
408/* check the component against the prefix test info */ 405/* check the component against the prefix test info */
@@ -509,6 +506,10 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
509 if (len > MAX_COMPNAME) 506 if (len > MAX_COMPNAME)
510 return -ENAMETOOLONG; 507 return -ENAMETOOLONG;
511 508
509 /* no filesystem is mounted here */
510 if (walkp->callflags & FF_NOFS)
511 return -ENOENT;
512
512 /* check for "." and ".." */ 513 /* check for "." and ".." */
513 if (name[0] == '.') 514 if (name[0] == '.')
514 { 515 {
@@ -577,7 +578,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
577 callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX); 578 callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX);
578 579
579 /* This lets it be passed quietly to directory scanning */ 580 /* This lets it be passed quietly to directory scanning */
580 stream->flags = callflags & FF_MASK; 581 stream->flags |= callflags & FF_MASK;
581 582
582 struct pathwalk walk; 583 struct pathwalk walk;
583 walk.path = path; 584 walk.path = path;
@@ -587,81 +588,36 @@ int open_stream_internal(const char *path, unsigned int callflags,
587 588
588 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL); 589 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
589 rootp->nextp = NULL; 590 rootp->nextp = NULL;
590 rootp->attr = ATTR_SYSTEM_ROOT;
591
592#ifdef HAVE_MULTIVOLUME
593 int volume = 0, rootrc = WALK_RC_FOUND;
594#endif /* HAVE_MULTIVOLUME */
595 591
596 while (1) 592 while (1)
597 { 593 {
598 const char *pathptr = walk.path; 594 rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
599 595 if (rc < 0)
600 #ifdef HAVE_MULTIVOLUME 596 break;
601 /* this seamlessly integrates secondary filesystems into the
602 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
603 const char *p;
604 volume = path_strip_volume(pathptr, &p, false);
605 if (!CHECK_VOL(volume))
606 {
607 DEBUGF("No such device or address: %d\n", volume);
608 FILE_ERROR(ENXIO, -2);
609 }
610
611 if (p == pathptr)
612 {
613 /* the root of this subpath is the system root */
614 rootp->attr = ATTR_SYSTEM_ROOT;
615 rootrc = WALK_RC_FOUND_ROOT;
616 }
617 else
618 {
619 /* this subpath specifies a mount point */
620 rootp->attr = ATTR_MOUNT_POINT;
621 rootrc = WALK_RC_FOUND;
622 }
623
624 walk.path = p;
625 #endif /* HAVE_MULTIVOLUME */
626
627 /* set name to start at last leading separator; names of volume
628 specifiers will be returned as "/<fooN>" */
629 rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
630 rootp->length =
631 IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
632 597
633 rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile); 598 rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
634 if (rc < 0) 599 if (rc < 0)
635 {
636 /* not mounted */
637 DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
638 rc = -ENXIO;
639 break; 600 break;
640 }
641 601
642 get_rootinfo_internal(&rootp->info); 602 walk.path = rootp->name + rootp->length;
603
643 rc = walk_path(&walk, rootp, stream); 604 rc = walk_path(&walk, rootp, stream);
644 if (rc != WALK_RC_CONT_AT_ROOT) 605 if (rc != WALK_RC_CONT_AT_ROOT)
645 break; 606 break;
646 } 607 }
647 608
648 switch (rc) 609 if (rc >= 0)
649 { 610 {
650 case WALK_RC_FOUND_ROOT:
651 IF_MV( rc = rootrc; )
652 /* fallthrough */
653 case WALK_RC_NOT_FOUND:
654 case WALK_RC_FOUND:
655 /* FF_PROBE leaves nothing for caller to clean up */ 611 /* FF_PROBE leaves nothing for caller to clean up */
656 if (callflags & FF_PROBE) 612 if (walk.callflags & FF_PROBE)
657 filestr_base_destroy(stream); 613 filestr_base_destroy(stream);
658 614 }
659 break; 615 else
660 616 {
661 default: /* utter, abject failure :`( */ 617 /* utter, abject failure :`( */
662 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno); 618 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
663 filestr_base_destroy(stream); 619 filestr_base_destroy(stream);
664 FILE_ERROR(-rc, -3); 620 FILE_ERROR(-rc, -1);
665 } 621 }
666 622
667file_error: 623file_error:
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
index e34a460e10..da82681acc 100644
--- a/firmware/common/fileobj_mgr.c
+++ b/firmware/common/fileobj_mgr.c
@@ -20,12 +20,13 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "config.h" 21#include "config.h"
22#include "system.h" 22#include "system.h"
23#include <errno.h>
23#include "debug.h" 24#include "debug.h"
24#include "file.h" 25#include "file.h"
25#include "dir.h" 26#include "dir.h"
26#include "disk_cache.h" 27#include "disk_cache.h"
27#include "fileobj_mgr.h" 28#include "fileobj_mgr.h"
28#include "dircache_redirect.h" 29#include "rb_namespace.h"
29 30
30/** 31/**
31 * Manages file and directory streams on all volumes 32 * Manages file and directory streams on all volumes
@@ -34,8 +35,8 @@
34 */ 35 */
35 36
36 37
37/* there will always be enough of these for all user handles, thus these 38/* there will always be enough of these for all user handles, thus most of
38 functions don't return failure codes */ 39 these functions don't return failure codes */
39#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS) 40#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS)
40 41
41/* describes the file as an image on the storage medium */ 42/* describes the file as an image on the storage medium */
@@ -84,6 +85,14 @@ static struct ll_head busy_bindings[NUM_VOLUMES];
84 for (struct filestr_base *s = STREAM_##what(start); \ 85 for (struct filestr_base *s = STREAM_##what(start); \
85 s; s = STREAM_NEXT(s)) 86 s; s = STREAM_NEXT(s))
86 87
88/* once a file/directory, always a file/directory; such a change
89 is a bug */
90#define CHECK_FO_DIRECTORY(callflags, fobp) \
91 if (((callflags) ^ (fobp)->flags) & FO_DIRECTORY) \
92 { \
93 DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", \
94 __func__, (fobp), (callflags)); \
95 }
87 96
88/* syncs information for the stream's old and new parent directory if any are 97/* syncs information for the stream's old and new parent directory if any are
89 currently opened */ 98 currently opened */
@@ -96,6 +105,9 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
96 continue; /* not directory or removed can't be parent of anything */ 105 continue; /* not directory or removed can't be parent of anything */
97 106
98 struct filestr_base *parentstrp = STREAM_FIRST(fobp); 107 struct filestr_base *parentstrp = STREAM_FIRST(fobp);
108 if (!parentstrp)
109 continue;
110
99 struct fat_file *parentfilep = &parentstrp->infop->fatfile; 111 struct fat_file *parentfilep = &parentstrp->infop->fatfile;
100 112
101 for (int i = 0; i < count; i++) 113 for (int i = 0; i < count; i++)
@@ -111,8 +123,8 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
111} 123}
112 124
113/* see if this file has open streams and return that fileobj_binding if so, 125/* see if this file has open streams and return that fileobj_binding if so,
114 else grab a new one from the free list; returns true if this stream is 126 else grab a new one from the free list; returns true if this is new */
115 the only open one */ 127
116static bool binding_assign(const struct file_base_info *srcinfop, 128static bool binding_assign(const struct file_base_info *srcinfop,
117 struct fileobj_binding **fobpp) 129 struct fileobj_binding **fobpp)
118{ 130{
@@ -123,7 +135,7 @@ static bool binding_assign(const struct file_base_info *srcinfop,
123 135
124 if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile)) 136 if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
125 { 137 {
126 /* already has open streams */ 138 /* already has open streams/mounts*/
127 *fobpp = fobp; 139 *fobpp = fobp;
128 return false; 140 return false;
129 } 141 }
@@ -143,6 +155,22 @@ static void binding_add_to_free_list(struct fileobj_binding *fobp)
143 ll_insert_last(FREE_BINDINGS(), &fobp->bind.node); 155 ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
144} 156}
145 157
158static void bind_source_info(const struct file_base_info *srcinfop,
159 struct fileobj_binding **fobpp)
160{
161 if (!binding_assign(srcinfop, fobpp))
162 return; /* already in use */
163 /* is new */
164 (*fobpp)->bind.info = *srcinfop;
165 fileobj_bind_file(&(*fobpp)->bind);
166}
167
168static void release_binding(struct fileobj_binding *fobp)
169{
170 fileobj_unbind_file(&fobp->bind);
171 binding_add_to_free_list(fobp);
172}
173
146/** File and directory internal interface **/ 174/** File and directory internal interface **/
147 175
148void file_binding_insert_last(struct file_base_binding *bindp) 176void file_binding_insert_last(struct file_base_binding *bindp)
@@ -169,6 +197,33 @@ void file_binding_remove_next(struct file_base_binding *prevp,
169} 197}
170#endif /* HAVE_DIRCACHE */ 198#endif /* HAVE_DIRCACHE */
171 199
200/* mounts a file object as a target from elsewhere */
201bool fileobj_mount(const struct file_base_info *srcinfop,
202 unsigned int callflags,
203 struct file_base_binding **bindpp)
204{
205 struct fileobj_binding *fobp;
206 bind_source_info(srcinfop, &fobp);
207 CHECK_FO_DIRECTORY(callflags, fobp);
208 if (fobp->flags & FO_MOUNTTARGET)
209 return false; /* already mounted */
210 fobp->flags |= FDO_BUSY | FO_MOUNTTARGET |
211 (callflags & FO_DIRECTORY);
212 *bindpp = &fobp->bind;
213 return true;
214}
215/* unmounts the file object and frees it if now unusued */
216void fileobj_unmount(struct file_base_binding *bindp)
217{
218 struct fileobj_binding *fobp = (struct fileobj_binding *)bindp;
219 if (!(fobp->flags & FO_MOUNTTARGET))
220 return; /* not mounted */
221 if (STREAM_FIRST(fobp) == NULL)
222 release_binding(fobp); /* no longer in use */
223 else
224 fobp->flags &= ~FO_MOUNTTARGET;
225}
226
172/* opens the file object for a new stream and sets up the caches; 227/* opens the file object for a new stream and sets up the caches;
173 * the stream must already be opened at the FS driver level and *stream 228 * the stream must already be opened at the FS driver level and *stream
174 * initialized. 229 * initialized.
@@ -180,10 +235,13 @@ void fileobj_fileop_open(struct filestr_base *stream,
180 const struct file_base_info *srcinfop, 235 const struct file_base_info *srcinfop,
181 unsigned int callflags) 236 unsigned int callflags)
182{ 237{
238 /* assign base file information */
183 struct fileobj_binding *fobp; 239 struct fileobj_binding *fobp;
184 bool first = binding_assign(srcinfop, &fobp); 240 bind_source_info(srcinfop, &fobp);
241 unsigned int foflags = fobp->flags;
185 242
186 /* add stream to this file's list */ 243 /* add stream to this file's list */
244 bool first = STREAM_FIRST(fobp) == NULL;
187 ll_insert_last(&fobp->list, &stream->node); 245 ll_insert_last(&fobp->list, &stream->node);
188 246
189 /* initiate the new stream into the enclave */ 247 /* initiate the new stream into the enclave */
@@ -197,27 +255,16 @@ void fileobj_fileop_open(struct filestr_base *stream,
197 if (first) 255 if (first)
198 { 256 {
199 /* first stream for file */ 257 /* first stream for file */
200 fobp->bind.info = *srcinfop; 258 fobp->flags = foflags | FDO_BUSY | FO_SINGLE |
201 fobp->flags = FDO_BUSY | FO_SINGLE | 259 (callflags & (FO_DIRECTORY|FO_TRUNC));
202 (callflags & (FO_DIRECTORY|FO_TRUNC)); 260 fobp->writers = 0;
203 fobp->writers = 0; 261 fobp->size = 0;
204 fobp->size = 0;
205
206 fileobj_bind_file(&fobp->bind);
207 } 262 }
208 else 263 else
209 { 264 {
210 /* additional stream for file */ 265 /* additional stream for file */
211 fobp->flags &= ~FO_SINGLE; 266 fobp->flags = (foflags & ~FO_SINGLE) | (callflags & FO_TRUNC);
212 fobp->flags |= callflags & FO_TRUNC; 267 CHECK_FO_DIRECTORY(callflags, fobp);
213
214 /* once a file/directory, always a file/directory; such a change
215 is a bug */
216 if ((callflags ^ fobp->flags) & FO_DIRECTORY)
217 {
218 DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
219 __func__, stream, callflags);
220 }
221 } 268 }
222 269
223 if ((callflags & FD_WRITE) && ++fobp->writers == 1) 270 if ((callflags & FD_WRITE) && ++fobp->writers == 1)
@@ -257,12 +304,14 @@ void fileobj_fileop_close(struct filestr_base *stream)
257 if (foflags & FO_SINGLE) 304 if (foflags & FO_SINGLE)
258 { 305 {
259 /* last stream for file; close everything */ 306 /* last stream for file; close everything */
260 fileobj_unbind_file(&fobp->bind);
261
262 if (fobp->writers) 307 if (fobp->writers)
263 file_cache_free(&fobp->cache); 308 file_cache_free(&fobp->cache);
264 309
265 binding_add_to_free_list(fobp); 310 /* binding must stay valid if something is mounted to here */
311 if (foflags & FO_MOUNTTARGET)
312 fobp->flags = foflags & (FDO_BUSY|FO_DIRECTORY|FO_MOUNTTARGET);
313 else
314 release_binding(fobp);
266 } 315 }
267 else 316 else
268 { 317 {
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c
index 2b4e6a8eb0..5242ec2d32 100644
--- a/firmware/common/pathfuncs.c
+++ b/firmware/common/pathfuncs.c
@@ -114,7 +114,7 @@ static const unsigned char storage_dec_indexes[STORAGE_NUM_TYPES+1] =
114 */ 114 */
115int path_strip_volume(const char *name, const char **nameptr, bool greedy) 115int path_strip_volume(const char *name, const char **nameptr, bool greedy)
116{ 116{
117 int volume = 0; 117 int volume = ROOT_VOLUME;
118 const char *t = name; 118 const char *t = name;
119 int c, v = 0; 119 int c, v = 0;
120 120
@@ -123,9 +123,15 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
123 * digits within the brackets is parsed as the volume number and of 123 * digits within the brackets is parsed as the volume number and of
124 * those, only the last ones VOL_MUM_MAX allows. 124 * those, only the last ones VOL_MUM_MAX allows.
125 */ 125 */
126 c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */ 126 t = GOBBLE_PATH_SEPCH(t); /* skip all leading slashes */
127 if (t == name)
128 {
129 volume = -1; /* relative path; don't know */
130 goto psv_out;
131 }
132 c = *t;
127 if (c != VOL_START_TOK) /* missing start token? no volume */ 133 if (c != VOL_START_TOK) /* missing start token? no volume */
128 goto volume0; 134 goto psv_out;
129 135
130 do 136 do
131 { 137 {
@@ -136,7 +142,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
136 break; 142 break;
137 case '\0': 143 case '\0':
138 case PATH_SEPCH: /* no closing bracket; no volume */ 144 case PATH_SEPCH: /* no closing bracket; no volume */
139 goto volume0; 145 goto psv_out;
140 default: /* something else; reset volume */ 146 default: /* something else; reset volume */
141 v = 0; 147 v = 0;
142 } 148 }
@@ -146,7 +152,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
146 if (!(c = *++t)) /* no more path and no '/' is ok */ 152 if (!(c = *++t)) /* no more path and no '/' is ok */
147 ; 153 ;
148 else if (c != PATH_SEPCH) /* more path and no separator after end */ 154 else if (c != PATH_SEPCH) /* more path and no separator after end */
149 goto volume0; 155 goto psv_out;
150 else if (greedy) 156 else if (greedy)
151 t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */ 157 t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */
152 158
@@ -155,7 +161,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
155 161
156 volume = v; 162 volume = v;
157 name = t; 163 name = t;
158volume0: 164psv_out:
159 if (nameptr) 165 if (nameptr)
160 *nameptr = name; 166 *nameptr = name;
161 return volume; 167 return volume;
@@ -166,10 +172,13 @@ volume0:
166 */ 172 */
167int get_volume_name(int volume, char *buffer) 173int get_volume_name(int volume, char *buffer)
168{ 174{
169 if (volume < 0) 175 if (volume < 0 || volume == ROOT_VOLUME)
170 { 176 {
171 *buffer = '\0'; 177 char *t = buffer;
172 return 0; 178 if (volume == ROOT_VOLUME)
179 *t++ = PATH_ROOTCHR;
180 *t = '\0';
181 return t - buffer;
173 } 182 }
174 183
175 volume %= VOL_NUM_MAX; /* as path parser would have it */ 184 volume %= VOL_NUM_MAX; /* as path parser would have it */
@@ -182,8 +191,20 @@ int get_volume_name(int volume, char *buffer)
182 return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c", 191 return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c",
183 VOL_START_TOK, voldec, volume, VOL_END_TOK); 192 VOL_START_TOK, voldec, volume, VOL_END_TOK);
184} 193}
194
195/* Returns volume name formatted with the root. Assumes buffer size is at
196 * least {VOL_MAX_LEN}+2 */
197int make_volume_root(int volume, char *buffer)
198{
199 char *t = buffer;
200 if (volume >= 0 && volume != ROOT_VOLUME)
201 *t++ = PATH_ROOTCHR;
202 t += get_volume_name(volume, t);
203 return t - buffer;
204}
185#endif /* HAVE_MULTIVOLUME */ 205#endif /* HAVE_MULTIVOLUME */
186 206
207
187/* Just like path_strip_volume() but strips a leading drive specifier and 208/* Just like path_strip_volume() but strips a leading drive specifier and
188 * returns the drive number (A=0, B=1, etc.). -1 means no drive was found. 209 * returns the drive number (A=0, B=1, etc.). -1 means no drive was found.
189 * If 'greedy' is 'true', all separators after the volume are consumed. 210 * If 'greedy' is 'true', all separators after the volume are consumed.
diff --git a/firmware/common/rb_namespace.c b/firmware/common/rb_namespace.c
new file mode 100644
index 0000000000..beaaf78bed
--- /dev/null
+++ b/firmware/common/rb_namespace.c
@@ -0,0 +1,291 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2017 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 "fileobj_mgr.h"
24#include "rb_namespace.h"
25#include "file_internal.h"
26
27#define ROOT_CONTENTS_INDEX (NUM_VOLUMES)
28#define NUM_ROOT_ITEMS (NUM_VOLUMES+1)
29
30static uint8_t root_entry_flags[NUM_VOLUMES+1];
31static struct file_base_binding *root_bindp;
32
33static inline unsigned int get_root_item_state(int item)
34{
35 return root_entry_flags[item];
36}
37
38static inline void set_root_item_state(int item, unsigned int state)
39{
40 root_entry_flags[item] = state;
41}
42
43static void get_mount_point_entry(IF_MV(int volume,) struct DIRENT *entry)
44{
45#ifdef HAVE_MULTIVOLUME
46 get_volume_name(volume, entry->d_name);
47#else /* */
48 strcpy(entry->d_name, PATH_ROOTSTR);
49#endif /* HAVE_MULTIVOLUME */
50#if defined(_FILESYSTEM_NATIVE_H_)
51 entry->info.attr = ATTR_MOUNT_POINT;
52 entry->info.size = 0;
53 entry->info.wrtdate = 0;
54 entry->info.wrttime = 0;
55#endif /* is dirinfo_native */
56}
57
58/* unmount the directory that enumerates into the root namespace */
59static void unmount_item(int item)
60{
61 unsigned int state = get_root_item_state(item);
62 if (!state)
63 return;
64
65 if (state & NSITEM_CONTENTS)
66 {
67 fileobj_unmount(root_bindp);
68 root_bindp = NULL;
69 }
70
71 set_root_item_state(item, 0);
72}
73
74/* mount the directory that enumerates into the root namespace */
75int root_mount_path(const char *path, unsigned int flags)
76{
77#ifdef HAVE_MULTIVOLUME
78 int volume = path_strip_volume(path, NULL, false);
79 if (volume == ROOT_VOLUME)
80 return -EINVAL;
81
82 if (!CHECK_VOL(volume))
83 return -ENOENT;
84#else
85 if (!path_is_absolute(path))
86 return -ENOENT;
87#endif /* HAVE_MULTIVOLUME */
88
89 bool contents = flags & NSITEM_CONTENTS;
90 int item = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
91 unsigned int state = get_root_item_state(item);
92
93 if (state)
94 return -EBUSY;
95
96 if (contents)
97 {
98 /* cache information about the target */
99 struct filestr_base stream;
100 struct path_component_info compinfo;
101
102 int e = errno;
103 int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
104 FF_DEVPATH, &stream, &compinfo);
105 if (rc <= 0)
106 {
107 rc = rc ? -errno : -ENOENT;
108 errno = e;
109 return rc;
110 }
111
112 if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
113 return -EBUSY;
114 }
115
116 state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
117 set_root_item_state(item, state);
118
119 return 0;
120}
121
122/* inform root that an entire volume is being unmounted */
123void root_unmount_volume(IF_MV_NONVOID(int volume))
124{
125 FOR_EACH_VOLUME(volume, item)
126 {
127 #ifdef HAVE_MULTIVOLUME
128 uint32_t state = get_root_item_state(item);
129 if (state && (volume < 0 || item == volume))
130 #endif /* HAVE_MULTIVOLUME */
131 unmount_item(item);
132 }
133
134 /* if the volume unmounted contains the root directory contents then
135 the contents must also be unmounted */
136#ifdef HAVE_MULTIVOLUME
137 uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
138 if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
139#endif
140 unmount_item(ROOT_CONTENTS_INDEX);
141}
142
143/* parse the root part of a path */
144int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
145{
146 int volume = ROOT_VOLUME;
147
148#ifdef HAVE_MULTIVOLUME
149 /* this seamlessly integrates secondary filesystems into the
150 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
151 const char *p;
152 volume = path_strip_volume(path, &p, false);
153 if (volume != ROOT_VOLUME && !CHECK_VOL(volume))
154 return -ENOENT;
155#endif /* HAVE_MULTIVOLUME */
156
157 /* set name to start at last leading separator; name of root will
158 * be returned as "/", volume specifiers as "/<fooN>" */
159 *pathp = GOBBLE_PATH_SEPCH(path) - 1;
160 *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1;
161
162#ifdef HAVE_MULTIVOLUME
163 if (*lenp > MAX_COMPNAME+1)
164 return -ENAMETOOLONG;
165#endif
166
167 return volume;
168}
169
170/* open one of the items in the root */
171int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
172 struct file_base_info *infop, uint16_t *attrp)
173{
174 unsigned int callflags = *callflagsp;
175 bool devpath = !!(callflags & FF_DEVPATH);
176#ifdef HAVE_MULTIVOLUME
177 bool sysroot = volume == ROOT_VOLUME;
178 if (devpath && sysroot)
179 return -ENOENT; /* devpath needs volume spec */
180#else
181 bool sysroot = !devpath; /* always sysroot unless devpath */
182#endif
183
184 int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
185 unsigned int state = get_root_item_state(item);
186
187 if (sysroot)
188 {
189 *attrp = ATTR_SYSTEM_ROOT;
190
191 if (state)
192 *infop = root_bindp->info;
193 else
194 *callflagsp = callflags | FF_NOFS; /* contents not mounted */
195 }
196 else
197 {
198 *attrp = ATTR_MOUNT_POINT;
199
200 if (!devpath && !state)
201 return -ENOENT; /* regular open requires having been mounted */
202#if CONFIG_PLATFORM & PLATFORM_NATIVE
203 if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
204 return -ENOENT; /* not mounted */
205#endif
206 get_rootinfo_internal(infop);
207 }
208
209 return 0;
210}
211
212/* read root directory entries */
213int root_readdir_dirent(struct filestr_base *stream,
214 struct ns_scan_info *scanp, struct DIRENT *entry)
215{
216 int rc = 0;
217
218 int item = scanp->item;
219
220 /* skip any not-mounted or hidden items */
221 unsigned int state;
222 while (1)
223 {
224 if (item >= NUM_ROOT_ITEMS)
225 goto file_eod;
226
227 state = get_root_item_state(item);
228 if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
229 break;
230
231 item++;
232 }
233
234 if (item == ROOT_CONTENTS_INDEX)
235 {
236 rc = readdir_dirent(stream, &scanp->scan, entry);
237 if (rc < 0)
238 FILE_ERROR(ERRNO, rc * 10 - 1);
239
240 if (rc == 0)
241 item++;
242 }
243 else
244 {
245 get_mount_point_entry(IF_MV(item,) entry);
246 item++;
247 rc = 1;
248 }
249
250 scanp->item = item;
251
252file_eod:
253#ifdef HAVE_DIRCACHE
254 if (rc == 0)
255 empty_dirent(entry);
256#endif
257file_error:
258 return rc;
259}
260
261/* opens a stream to enumerate items in a namespace container */
262int ns_open_stream(const char *path, unsigned int callflags,
263 struct filestr_base *stream, struct ns_scan_info *scanp)
264{
265 /* stream still needs synchronization even if we don't have a stream */
266 static struct mutex no_contents_mtx SHAREDBSS_ATTR;
267
268 int rc = open_stream_internal(path, callflags, stream, NULL);
269 if (rc < 0)
270 FILE_ERROR(ERRNO, rc * 10 - 1);
271
272 scanp->item = rc > 1 ? 0 : -1;
273
274 if (stream->flags & FDO_BUSY)
275 {
276 /* root contents are mounted */
277 fat_rewind(&stream->fatstr);
278 }
279 else
280 {
281 /* root contents not mounted */
282 mutex_init(&no_contents_mtx);
283 stream->mtx = &no_contents_mtx;
284 }
285
286 ns_dirscan_rewind(scanp);
287
288 rc = 0;
289file_error:
290 return rc;
291}
diff --git a/firmware/export/mv.h b/firmware/export/mv.h
index 3657ef6c98..08daf50b34 100644
--- a/firmware/export/mv.h
+++ b/firmware/export/mv.h
@@ -87,6 +87,10 @@
87#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1) 87#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1)
88#define VOL_NUM_MAX 100 88#define VOL_NUM_MAX 100
89 89
90#ifndef ROOT_VOLUME
91#define ROOT_VOLUME INT_MAX
92#endif
93
90#else /* empty definitions if no multi-volume */ 94#else /* empty definitions if no multi-volume */
91#define IF_MV(x...) 95#define IF_MV(x...)
92#define IF_MV_NONVOID(x...) void 96#define IF_MV_NONVOID(x...) void
diff --git a/firmware/export/pathfuncs.h b/firmware/export/pathfuncs.h
index 350dd4e548..385d534714 100644
--- a/firmware/export/pathfuncs.h
+++ b/firmware/export/pathfuncs.h
@@ -30,10 +30,15 @@
30/* useful char constants that could be reconfigured if desired */ 30/* useful char constants that could be reconfigured if desired */
31#define PATH_SEPCH '/' 31#define PATH_SEPCH '/'
32#define PATH_SEPSTR "/" 32#define PATH_SEPSTR "/"
33#define PATH_ROOTCHR '/'
33#define PATH_ROOTSTR "/" 34#define PATH_ROOTSTR "/"
34#define PATH_BADSEPCH '\\' 35#define PATH_BADSEPCH '\\'
35#define PATH_DRVSEPCH ':' 36#define PATH_DRVSEPCH ':'
36 37
38#ifndef ROOT_VOLUME
39#define ROOT_VOLUME INT_MAX
40#endif
41
37/* a nicer way to check for "." and ".." than two strcmp() calls */ 42/* a nicer way to check for "." and ".." than two strcmp() calls */
38static inline bool is_dotdir_name(const char *name) 43static inline bool is_dotdir_name(const char *name)
39{ 44{
@@ -75,6 +80,7 @@ static inline bool name_is_dot_dot(const char *name)
75#ifdef HAVE_MULTIVOLUME 80#ifdef HAVE_MULTIVOLUME
76int path_strip_volume(const char *name, const char **nameptr, bool greedy); 81int path_strip_volume(const char *name, const char **nameptr, bool greedy);
77int get_volume_name(int volume, char *name); 82int get_volume_name(int volume, char *name);
83int make_volume_root(int volume, char *dst);
78#endif 84#endif
79 85
80int path_strip_drive(const char *name, const char **nameptr, bool greedy); 86int path_strip_drive(const char *name, const char **nameptr, bool greedy);
diff --git a/firmware/export/rbpaths.h b/firmware/export/rbpaths.h
index a3042d80bc..9dd0a24c6f 100644
--- a/firmware/export/rbpaths.h
+++ b/firmware/export/rbpaths.h
@@ -52,6 +52,9 @@
52#define PLUGIN_DIR ROCKBOX_DIR "/rocks" 52#define PLUGIN_DIR ROCKBOX_DIR "/rocks"
53#define CODECS_DIR ROCKBOX_DIR "/codecs" 53#define CODECS_DIR ROCKBOX_DIR "/codecs"
54 54
55#define RB_ROOT_VOL_HIDDEN(v) (IF_MV_VOL(v) == 0)
56#define RB_ROOT_CONTENTS_DIR "/" IF_MV("<0>")
57
55#else /* APPLICATION */ 58#else /* APPLICATION */
56 59
57#define HOME_DIR "<HOME>" /* replaced at runtime */ 60#define HOME_DIR "<HOME>" /* replaced at runtime */
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index 9fae16b551..d7b9626c01 100644
--- a/firmware/include/dircache_redirect.h
+++ b/firmware/include/dircache_redirect.h
@@ -20,7 +20,10 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21#ifndef _DIRCACHE_REDIRECT_H_ 21#ifndef _DIRCACHE_REDIRECT_H_
22 22
23#include "rbpaths.h"
24#include "pathfuncs.h"
23#include "dir.h" 25#include "dir.h"
26#include "dircache.h"
24 27
25/*** 28/***
26 ** Internal redirects that depend upon whether or not dircache is made 29 ** Internal redirects that depend upon whether or not dircache is made
@@ -123,10 +126,20 @@ static inline void fileop_onsync_internal(struct filestr_base *stream)
123 126
124static inline void volume_onmount_internal(IF_MV_NONVOID(int volume)) 127static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
125{ 128{
129#ifdef HAVE_MULTIVOLUME
130 char path[VOL_MAX_LEN+2];
131 make_volume_root(volume, path);
132#else
133 const char *path = PATH_ROOTSTR;
134#endif
135 root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
136#ifdef HAVE_MULTIVOLUME
137 if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false))
138#endif
139 root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
126#ifdef HAVE_DIRCACHE 140#ifdef HAVE_DIRCACHE
127 dircache_mount(); 141 dircache_mount();
128#endif 142#endif
129 IF_MV( (void)volume; )
130} 143}
131 144
132static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume)) 145static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
@@ -135,6 +148,7 @@ static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
135 /* First, to avoid update of something about to be destroyed anyway */ 148 /* First, to avoid update of something about to be destroyed anyway */
136 dircache_unmount(IF_MV(volume)); 149 dircache_unmount(IF_MV(volume));
137#endif 150#endif
151 root_unmount_volume(IF_MV(volume));
138 fileobj_mgr_unmount(IF_MV(volume)); 152 fileobj_mgr_unmount(IF_MV(volume));
139} 153}
140 154
@@ -152,7 +166,7 @@ static inline void fileop_onunmount_internal(struct filestr_base *stream)
152 166
153static inline int readdir_dirent(struct filestr_base *stream, 167static inline int readdir_dirent(struct filestr_base *stream,
154 struct dirscan_info *scanp, 168 struct dirscan_info *scanp,
155 struct dirent *entry) 169 struct DIRENT *entry)
156{ 170{
157#ifdef HAVE_DIRCACHE 171#ifdef HAVE_DIRCACHE
158 return dircache_readdir_dirent(stream, scanp, entry); 172 return dircache_readdir_dirent(stream, scanp, entry);
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index d62b5a8541..ec0c5d28f0 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -30,6 +30,7 @@
30#include "fs_attr.h" 30#include "fs_attr.h"
31#include "fs_defines.h" 31#include "fs_defines.h"
32#include "fat.h" 32#include "fat.h"
33#include "dir.h"
33#ifdef HAVE_DIRCACHE 34#ifdef HAVE_DIRCACHE
34#include "dircache.h" 35#include "dircache.h"
35#endif 36#endif
@@ -72,17 +73,20 @@ enum fildes_and_obj_flags
72 /* used in descriptor and common */ 73 /* used in descriptor and common */
73 FDO_BUSY = 0x0001, /* descriptor/object is in use */ 74 FDO_BUSY = 0x0001, /* descriptor/object is in use */
74 /* only used in individual stream descriptor */ 75 /* only used in individual stream descriptor */
75 FD_WRITE = 0x0002, /* descriptor has write mode */ 76 FD_VALID = 0x0002, /* descriptor is valid but not registered */
76 FD_WRONLY = 0x0004, /* descriptor is write mode only */ 77 FD_WRITE = 0x0004, /* descriptor has write mode */
77 FD_APPEND = 0x0008, /* descriptor is append mode */ 78 FD_WRONLY = 0x0008, /* descriptor is write mode only */
79 FD_APPEND = 0x0010, /* descriptor is append mode */
78 FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */ 80 FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
79 /* only used as common flags */ 81 /* only used as common flags */
80 FO_DIRECTORY = 0x0010, /* fileobj is a directory */ 82 FO_DIRECTORY = 0x0020, /* fileobj is a directory */
81 FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */ 83 FO_TRUNC = 0x0040, /* fileobj is opened to be truncated */
82 FO_REMOVED = 0x0040, /* fileobj was deleted while open */ 84 FO_REMOVED = 0x0080, /* fileobj was deleted while open */
83 FO_SINGLE = 0x0080, /* fileobj has only one stream open */ 85 FO_SINGLE = 0x0100, /* fileobj has only one stream open */
84 FDO_MASK = 0x00ff, 86 FO_MOUNTTARGET = 0x0200, /* fileobj kept open as a mount target */
85 FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */ 87 FDO_MASK = 0x03ff,
88 FDO_CHG_MASK = FO_TRUNC,
89 /* fileobj permitted external change */ /* fileobj permitted external change */
86 /* bitflags that instruct various 'open' functions how to behave; 90 /* bitflags that instruct various 'open' functions how to behave;
87 * saved in stream flags (only) but not used by manager */ 91 * saved in stream flags (only) but not used by manager */
88 FF_FILE = 0x00000000, /* expect file; accept file only */ 92 FF_FILE = 0x00000000, /* expect file; accept file only */
@@ -95,7 +99,9 @@ enum fildes_and_obj_flags
95 FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */ 99 FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */
96 FF_INFO = 0x00400000, /* return info on self */ 100 FF_INFO = 0x00400000, /* return info on self */
97 FF_PARENTINFO = 0x00800000, /* return info on parent */ 101 FF_PARENTINFO = 0x00800000, /* return info on parent */
98 FF_MASK = 0x00ff0000, 102 FF_DEVPATH = 0x01000000, /* path is a device path, not root-based */
103 FF_NOFS = 0x02000000, /* no filesystem mounted here */
104 FF_MASK = 0x03ff0000,
99}; 105};
100 106
101/** Common data structures used throughout **/ 107/** Common data structures used throughout **/
@@ -229,10 +235,10 @@ int test_stream_exists_internal(const char *path, unsigned int callflags);
229int open_noiso_internal(const char *path, int oflag); /* file.c */ 235int open_noiso_internal(const char *path, int oflag); /* file.c */
230void force_close_writer_internal(struct filestr_base *stream); /* file.c */ 236void force_close_writer_internal(struct filestr_base *stream); /* file.c */
231 237
232struct dirent; 238struct DIRENT;
233int uncached_readdir_dirent(struct filestr_base *stream, 239int uncached_readdir_dirent(struct filestr_base *stream,
234 struct dirscan_info *scanp, 240 struct dirscan_info *scanp,
235 struct dirent *entry); 241 struct DIRENT *entry);
236void uncached_rewinddir_dirent(struct dirscan_info *scanp); 242void uncached_rewinddir_dirent(struct dirscan_info *scanp);
237 243
238int uncached_readdir_internal(struct filestr_base *stream, 244int uncached_readdir_internal(struct filestr_base *stream,
@@ -333,7 +339,7 @@ static inline struct fat_direntry *get_dir_fatent(void)
333void iso_decode_d_name(char *d_name); 339void iso_decode_d_name(char *d_name);
334 340
335#ifdef HAVE_DIRCACHE 341#ifdef HAVE_DIRCACHE
336void empty_dirent(struct dirent *entry); 342void empty_dirent(struct DIRENT *entry);
337void fill_dirinfo_native(struct dirinfo_native *din); 343void fill_dirinfo_native(struct dirinfo_native *din);
338#endif /* HAVE_DIRCACHE */ 344#endif /* HAVE_DIRCACHE */
339 345
diff --git a/firmware/include/fileobj_mgr.h b/firmware/include/fileobj_mgr.h
index 627d2df341..0db3520d34 100644
--- a/firmware/include/fileobj_mgr.h
+++ b/firmware/include/fileobj_mgr.h
@@ -29,6 +29,11 @@ void file_binding_remove(struct file_base_binding *bindp);
29void file_binding_remove_next(struct file_base_binding *prevp, 29void file_binding_remove_next(struct file_base_binding *prevp,
30 struct file_base_binding *bindp); 30 struct file_base_binding *bindp);
31 31
32bool fileobj_mount(const struct file_base_info *srcinfop,
33 unsigned int callflags,
34 struct file_base_binding **bindpp);
35void fileobj_unmount(struct file_base_binding *bindp);
36
32void fileobj_fileop_open(struct filestr_base *stream, 37void fileobj_fileop_open(struct filestr_base *stream,
33 const struct file_base_info *srcinfop, 38 const struct file_base_info *srcinfop,
34 unsigned int callflags); 39 unsigned int callflags);
diff --git a/firmware/include/fs_defines.h b/firmware/include/fs_defines.h
index 538c4b36cd..a1938c56b8 100644
--- a/firmware/include/fs_defines.h
+++ b/firmware/include/fs_defines.h
@@ -51,11 +51,13 @@
51/* internal functions open streams as well; make sure they don't fail if all 51/* internal functions open streams as well; make sure they don't fail if all
52 user descs are busy; this needs to be at least the greatest quantity needed 52 user descs are busy; this needs to be at least the greatest quantity needed
53 at once by all internal functions */ 53 at once by all internal functions */
54#define MOUNT_AUX_FILEOBJS 1
54#ifdef HAVE_DIRCACHE 55#ifdef HAVE_DIRCACHE
55#define AUX_FILEOBJS 3 56#define DIRCACHE_AUX_FILEOBJS 1
56#else 57#else
57#define AUX_FILEOBJS 2 58#define DIRCACHE_AUX_FILEOBJS 0
58#endif 59#endif
60#define AUX_FILEOBJS (2+DIRCACHE_AUX_FILEOBJS+MOUNT_AUX_FILEOBJS)
59 61
60/* number of components statically allocated to handle the vast majority 62/* number of components statically allocated to handle the vast majority
61 of path depths; should maybe be tuned for >= 90th percentile but for now, 63 of path depths; should maybe be tuned for >= 90th percentile but for now,
diff --git a/firmware/include/rb_namespace.h b/firmware/include/rb_namespace.h
new file mode 100644
index 0000000000..7bc711b5a6
--- /dev/null
+++ b/firmware/include/rb_namespace.h
@@ -0,0 +1,79 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2017 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#ifndef RB_NAMESPACE_H
22#define RB_NAMESPACE_H
23
24#include "file_internal.h"
25
26enum ns_item_flags
27{
28 NSITEM_MOUNTED = 0x01, /* item is mounted */
29 NSITEM_HIDDEN = 0x02, /* item is not enumerated */
30 NSITEM_CONTENTS = 0x04, /* contents enumerate */
31};
32
33struct ns_scan_info
34{
35 struct dirscan_info scan; /* dirscan info - first! */
36 int item; /* current item in parent */
37};
38
39/* root functions */
40int root_mount_path(const char *path, unsigned int flags);
41void root_unmount_volume(IF_MV_NONVOID(int volume));
42int root_readdir_dirent(struct filestr_base *stream,
43 struct ns_scan_info *scanp,
44 struct DIRENT *entry);
45
46/* namespace functions */
47int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp);
48int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
49 struct file_base_info *infop, uint16_t *attrp);
50int ns_open_stream(const char *path, unsigned int callflags,
51 struct filestr_base *stream, struct ns_scan_info *scanp);
52
53/* closes the namespace stream */
54static inline int ns_close_stream(struct filestr_base *stream)
55{
56 return close_stream_internal(stream);
57}
58
59#include "dircache_redirect.h"
60
61static inline void ns_dirscan_rewind(struct ns_scan_info *scanp)
62{
63 rewinddir_dirent(&scanp->scan);
64 if (scanp->item != -1)
65 scanp->item = 0;
66}
67
68static inline int ns_readdir_dirent(struct filestr_base *stream,
69 struct ns_scan_info *scanp,
70 struct dirent *entry)
71
72{
73 if (scanp->item == -1)
74 return readdir_dirent(stream, &scanp->scan, entry);
75 else
76 return root_readdir_dirent(stream, scanp, entry);
77}
78
79#endif /* RB_NAMESPACE_H */
diff --git a/uisimulator/common/filesystem-sim.c b/uisimulator/common/filesystem-sim.c
index 8d7fb14931..0a5df0c742 100644
--- a/uisimulator/common/filesystem-sim.c
+++ b/uisimulator/common/filesystem-sim.c
@@ -309,6 +309,8 @@ int sim_get_os_path(char *buffer, const char *path, size_t bufsize)
309 309
310 const char *next; 310 const char *next;
311 volume = path_strip_volume(p, &next, true); 311 volume = path_strip_volume(p, &next, true);
312 if (volume == ROOT_VOLUME)
313 volume = 0; /* FIXME: root no longer implies volume 0 */
312 314
313 if (next > p) 315 if (next > p)
314 { 316 {