summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <me.theuser@yahoo.com>2017-02-03 17:13:58 -0500
committerWilliam Wilgus <me.theuser@yahoo.com>2020-08-20 23:08:57 +0000
commit5ef28cccf92f5eada6d502fa4b0e16a13e94be5b (patch)
tree05f9d2f8bdf3c0cc54c5893159a7dcf07c7e3e55
parent31fc46ded69be7438cca2ba2c2b93c1f200165a6 (diff)
downloadrockbox-5ef28cccf92f5eada6d502fa4b0e16a13e94be5b.tar.gz
rockbox-5ef28cccf92f5eada6d502fa4b0e16a13e94be5b.zip
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: I90b5c0a1c949283d3102c16734b0b6ac73901a30
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/common/dir.c92
-rw-r--r--firmware/common/dircache.c9
-rw-r--r--firmware/common/disk.c2
-rw-r--r--firmware/common/file.c2
-rw-r--r--firmware/common/file_internal.c95
-rw-r--r--firmware/common/fileobj_mgr.c114
-rw-r--r--firmware/common/pathfuncs.c43
-rw-r--r--firmware/common/rb_namespace.c289
-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.h16
-rw-r--r--firmware/include/file_internal.h22
-rw-r--r--firmware/include/fileobj_mgr.h5
-rw-r--r--firmware/include/fs_defines.h11
-rw-r--r--firmware/include/rb_namespace.h79
-rw-r--r--uisimulator/common/filesystem-sim.c2
18 files changed, 591 insertions, 204 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index a68d10ec76..f1c7621244 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -238,6 +238,7 @@ common/dircache.c
238common/pathfuncs.c 238common/pathfuncs.c
239common/fdprintf.c 239common/fdprintf.c
240common/linked_list.c 240common/linked_list.c
241common/rb_namespace.c
241common/strcasecmp.c 242common/strcasecmp.c
242common/strcasestr.c 243common/strcasestr.c
243common/strnatcmp.c 244common/strnatcmp.c
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index f89129ae34..85e6ff316b 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -27,17 +27,14 @@
27#include "dir.h" 27#include "dir.h"
28#include "pathfuncs.h" 28#include "pathfuncs.h"
29#include "fileobj_mgr.h" 29#include "fileobj_mgr.h"
30#include "dircache_redirect.h" 30#include "rb_namespace.h"
31 31
32/* structure used for open directory streams */ 32/* structure used for open directory streams */
33static struct dirstr_desc 33static struct dirstr_desc
34{ 34{
35 struct filestr_base stream; /* basic stream info (first!) */ 35 struct filestr_base stream; /* basic stream info (first!) */
36 struct dirscan_info scan; /* directory scan cursor */ 36 struct ns_scan_info scan; /* directory scan cursor */
37 struct dirent entry; /* current parsed entry information */ 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]; 38} open_streams[MAX_OPEN_DIRS];
42 39
43/* check and return a struct dirstr_desc* from a DIR* */ 40/* check and return a struct dirstr_desc* from a DIR* */
@@ -47,7 +44,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
47 44
48 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS)) 45 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
49 dir = NULL; 46 dir = NULL;
50 else if (dir->stream.flags & FDO_BUSY) 47 else if (dir->stream.flags & (FDO_BUSY|FD_VALID))
51 return dir; 48 return dir;
52 49
53 int errnum; 50 int errnum;
@@ -104,49 +101,6 @@ static struct dirstr_desc * alloc_dirstr(void)
104 return NULL; 101 return NULL;
105} 102}
106 103
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 104
151/** POSIX interface **/ 105/** POSIX interface **/
152 106
@@ -165,21 +119,13 @@ DIR * opendir(const char *dirname)
165 if (!dir) 119 if (!dir)
166 FILE_ERROR(EMFILE, RC); 120 FILE_ERROR(EMFILE, RC);
167 121
168 rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL); 122 rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan);
169 if (rc < 0) 123 if (rc < 0)
170 { 124 {
171 DEBUGF("Open failed: %d\n", rc); 125 DEBUGF("Open failed: %d\n", rc);
172 FILE_ERROR(ERRNO, RC); 126 FILE_ERROR(ERRNO, RC);
173 } 127 }
174 128
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; 129 dirp = (DIR *)dir;
184file_error: 130file_error:
185 file_internal_unlock_WRITER(); 131 file_internal_unlock_WRITER();
@@ -204,7 +150,7 @@ int closedir(DIR *dirp)
204 FILE_ERROR(EBADF, -2); 150 FILE_ERROR(EBADF, -2);
205 } 151 }
206 152
207 rc = close_stream_internal(&dir->stream); 153 rc = ns_close_stream(&dir->stream);
208 if (rc < 0) 154 if (rc < 0)
209 FILE_ERROR(ERRNO, rc * 10 - 3); 155 FILE_ERROR(ERRNO, rc * 10 - 3);
210 156
@@ -222,16 +168,11 @@ struct dirent * readdir(DIR *dirp)
222 168
223 struct dirent *res = NULL; 169 struct dirent *res = NULL;
224 170
225 int rc = readdir_volume(dir, &dir->entry); 171 int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &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) 172 if (rc > 0)
234 res = &dir->entry; 173 res = &dir->entry;
174 else if (rc < 0)
175 FILE_ERROR(EIO, RC);
235 176
236file_error: 177file_error:
237 RELEASE_DIRSTR(READER, dir); 178 RELEASE_DIRSTR(READER, dir);
@@ -258,13 +199,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
258 if (!dir) 199 if (!dir)
259 FILE_ERROR_RETURN(ERRNO, -1); 200 FILE_ERROR_RETURN(ERRNO, -1);
260 201
261 int rc = readdir_volume(dir, entry); 202 int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry);
262 if (rc == 0) 203 if (rc < 0)
263 { 204 FILE_ERROR(EIO, rc * 10 - 4);
264 rc = readdir_dirent(&dir->stream, &dir->scan, entry);
265 if (rc < 0)
266 FILE_ERROR(EIO, rc * 10 - 4);
267 }
268 205
269file_error: 206file_error:
270 RELEASE_DIRSTR(READER, dir); 207 RELEASE_DIRSTR(READER, dir);
@@ -288,12 +225,7 @@ void rewinddir(DIR *dirp)
288 if (!dir) 225 if (!dir)
289 FILE_ERROR_RETURN(ERRNO); 226 FILE_ERROR_RETURN(ERRNO);
290 227
291 rewinddir_dirent(&dir->scan); 228 ns_dirscan_rewind(&dir->scan);
292
293#ifdef HAVE_MULTIVOLUME
294 if (dir->volumecounter != INT_MAX)
295 dir->volumecounter = 0;
296#endif /* HAVE_MULTIVOLUME */
297 229
298 RELEASE_DIRSTR(READER, dir); 230 RELEASE_DIRSTR(READER, dir);
299} 231}
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 0cdaf1bd4a..cc65d2d540 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -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 98c273b26d..3bd88f66a8 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 33
diff --git a/firmware/common/file.c b/firmware/common/file.c
index cb918c6eab..893e475a32 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 fe18f90056..45f412e166 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
@@ -87,9 +85,10 @@ void file_cache_free(struct filestr_cache *cachep)
87 85
88/** Stream base APIs **/ 86/** Stream base APIs **/
89 87
90static inline void filestr_clear(struct filestr_base *stream) 88static inline void filestr_clear(struct filestr_base *stream,
89 unsigned int flags)
91{ 90{
92 stream->flags = 0; 91 stream->flags = flags;
93 stream->bindp = NULL; 92 stream->bindp = NULL;
94#if 0 93#if 0
95 stream->mtx = NULL; 94 stream->mtx = NULL;
@@ -153,7 +152,7 @@ void filestr_discard_cache(struct filestr_base *stream)
153/* Initialize the base descriptor */ 152/* Initialize the base descriptor */
154void filestr_base_init(struct filestr_base *stream) 153void filestr_base_init(struct filestr_base *stream)
155{ 154{
156 filestr_clear(stream); 155 filestr_clear(stream, FD_VALID);
157 file_cache_init(&stream->cache); 156 file_cache_init(&stream->cache);
158 stream->cachep = &stream->cache; 157 stream->cachep = &stream->cache;
159} 158}
@@ -161,7 +160,7 @@ void filestr_base_init(struct filestr_base *stream)
161/* free base descriptor resources */ 160/* free base descriptor resources */
162void filestr_base_destroy(struct filestr_base *stream) 161void filestr_base_destroy(struct filestr_base *stream)
163{ 162{
164 filestr_clear(stream); 163 filestr_clear(stream, 0);
165 filestr_free_cache(stream); 164 filestr_free_cache(stream);
166} 165}
167 166
@@ -293,7 +292,7 @@ struct pathwalk_component
293 292
294#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */ 293#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
295#define WALK_RC_FOUND 1 /* found and opened */ 294#define WALK_RC_FOUND 1 /* found and opened */
296#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */ 295#define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */
297#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */ 296#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
298 297
299/* return another struct pathwalk_component from the pool, or NULL if the 298/* return another struct pathwalk_component from the pool, or NULL if the
@@ -397,10 +396,10 @@ static int walk_open_info(struct pathwalk *walkp,
397 396
398 /* make open official if not simply probing for presence - must do it here 397 /* make open official if not simply probing for presence - must do it here
399 or compp->info on stack will get destroyed before it was copied */ 398 or compp->info on stack will get destroyed before it was copied */
400 if (!(callflags & FF_PROBE)) 399 if (!(callflags & (FF_PROBE|FF_NOFS)))
401 fileop_onopen_internal(stream, &compp->info, callflags); 400 fileop_onopen_internal(stream, &compp->info, callflags);
402 401
403 return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT; 402 return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
404} 403}
405 404
406/* check the component against the prefix test info */ 405/* check the component against the prefix test info */
@@ -507,6 +506,10 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
507 if (len > MAX_COMPNAME) 506 if (len > MAX_COMPNAME)
508 return -ENAMETOOLONG; 507 return -ENAMETOOLONG;
509 508
509 /* no filesystem is mounted here */
510 if (walkp->callflags & FF_NOFS)
511 return -ENOENT;
512
510 /* check for "." and ".." */ 513 /* check for "." and ".." */
511 if (name[0] == '.') 514 if (name[0] == '.')
512 { 515 {
@@ -575,7 +578,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
575 callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX); 578 callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX);
576 579
577 /* This lets it be passed quietly to directory scanning */ 580 /* This lets it be passed quietly to directory scanning */
578 stream->flags = callflags & FF_MASK; 581 stream->flags |= callflags & FF_MASK;
579 582
580 struct pathwalk walk; 583 struct pathwalk walk;
581 walk.path = path; 584 walk.path = path;
@@ -585,80 +588,36 @@ int open_stream_internal(const char *path, unsigned int callflags,
585 588
586 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL); 589 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
587 rootp->nextp = NULL; 590 rootp->nextp = NULL;
588 rootp->attr = ATTR_SYSTEM_ROOT;
589
590#ifdef HAVE_MULTIVOLUME
591 int volume = 0, rootrc = WALK_RC_FOUND;
592#endif /* HAVE_MULTIVOLUME */
593 591
594 while (1) 592 while (1)
595 { 593 {
596 const char *pathptr = walk.path; 594 rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
597 595 if (rc < 0)
598 #ifdef HAVE_MULTIVOLUME 596 break;
599 /* this seamlessly integrates secondary filesystems into the
600 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
601 const char *p;
602 volume = path_strip_volume(pathptr, &p, false);
603 if (!CHECK_VOL(volume))
604 {
605 DEBUGF("No such device or address: %d\n", volume);
606 FILE_ERROR(ENXIO, -2);
607 }
608
609 if (p == pathptr)
610 {
611 /* the root of this subpath is the system root */
612 rootp->attr = ATTR_SYSTEM_ROOT;
613 rootrc = WALK_RC_FOUND_ROOT;
614 }
615 else
616 {
617 /* this subpath specifies a mount point */
618 rootp->attr = ATTR_MOUNT_POINT;
619 rootrc = WALK_RC_FOUND;
620 }
621
622 walk.path = p;
623 #endif /* HAVE_MULTIVOLUME */
624
625 /* set name to start at last leading separator; names of volume
626 specifiers will be returned as "/<fooN>" */
627 rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
628 rootp->length =
629 IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
630 597
631 rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile); 598 rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
632 if (rc < 0) 599 if (rc < 0)
633 {
634 /* not mounted */
635 DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
636 rc = -ENXIO;
637 break; 600 break;
638 }
639 601
640 get_rootinfo_internal(&rootp->info); 602 walk.path = rootp->name + rootp->length;
603
641 rc = walk_path(&walk, rootp, stream); 604 rc = walk_path(&walk, rootp, stream);
642 if (rc != WALK_RC_CONT_AT_ROOT) 605 if (rc != WALK_RC_CONT_AT_ROOT)
643 break; 606 break;
644 } 607 }
645 608
646 switch (rc) 609 if (rc >= 0)
647 { 610 {
648 case WALK_RC_FOUND_ROOT:
649 IF_MV( rc = rootrc; )
650 case WALK_RC_NOT_FOUND:
651 case WALK_RC_FOUND:
652 /* FF_PROBE leaves nothing for caller to clean up */ 611 /* FF_PROBE leaves nothing for caller to clean up */
653 if (callflags & FF_PROBE) 612 if (walk.callflags & FF_PROBE)
654 filestr_base_destroy(stream); 613 filestr_base_destroy(stream);
655 614 }
656 break; 615 else
657 616 {
658 default: /* utter, abject failure :`( */ 617 /* utter, abject failure :`( */
659 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno); 618 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
660 filestr_base_destroy(stream); 619 filestr_base_destroy(stream);
661 FILE_ERROR(-rc, -3); 620 FILE_ERROR(-rc, -1);
662 } 621 }
663 622
664file_error: 623file_error:
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
index e34a460e10..37452fbbe1 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,15 @@ 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 }
96
87 97
88/* syncs information for the stream's old and new parent directory if any are 98/* syncs information for the stream's old and new parent directory if any are
89 currently opened */ 99 currently opened */
@@ -96,6 +106,10 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
96 continue; /* not directory or removed can't be parent of anything */ 106 continue; /* not directory or removed can't be parent of anything */
97 107
98 struct filestr_base *parentstrp = STREAM_FIRST(fobp); 108 struct filestr_base *parentstrp = STREAM_FIRST(fobp);
109
110 if (!parentstrp)
111 continue;
112
99 struct fat_file *parentfilep = &parentstrp->infop->fatfile; 113 struct fat_file *parentfilep = &parentstrp->infop->fatfile;
100 114
101 for (int i = 0; i < count; i++) 115 for (int i = 0; i < count; i++)
@@ -111,8 +125,7 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
111} 125}
112 126
113/* see if this file has open streams and return that fileobj_binding if so, 127/* 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 128 else grab a new one from the free list; returns true if this is new */
115 the only open one */
116static bool binding_assign(const struct file_base_info *srcinfop, 129static bool binding_assign(const struct file_base_info *srcinfop,
117 struct fileobj_binding **fobpp) 130 struct fileobj_binding **fobpp)
118{ 131{
@@ -123,7 +136,7 @@ static bool binding_assign(const struct file_base_info *srcinfop,
123 136
124 if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile)) 137 if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
125 { 138 {
126 /* already has open streams */ 139 /* already has open streams/mounts */
127 *fobpp = fobp; 140 *fobpp = fobp;
128 return false; 141 return false;
129 } 142 }
@@ -143,6 +156,23 @@ static void binding_add_to_free_list(struct fileobj_binding *fobp)
143 ll_insert_last(FREE_BINDINGS(), &fobp->bind.node); 156 ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
144} 157}
145 158
159static void bind_source_info(const struct file_base_info *srcinfop,
160 struct fileobj_binding **fobpp)
161{
162 if (!binding_assign(srcinfop, fobpp))
163 return; /* already in use */
164
165 /* is new */
166 (*fobpp)->bind.info = *srcinfop;
167 fileobj_bind_file(&(*fobpp)->bind);
168}
169
170static void release_binding(struct fileobj_binding *fobp)
171{
172 fileobj_unbind_file(&fobp->bind);
173 binding_add_to_free_list(fobp);
174}
175
146/** File and directory internal interface **/ 176/** File and directory internal interface **/
147 177
148void file_binding_insert_last(struct file_base_binding *bindp) 178void file_binding_insert_last(struct file_base_binding *bindp)
@@ -169,6 +199,41 @@ void file_binding_remove_next(struct file_base_binding *prevp,
169} 199}
170#endif /* HAVE_DIRCACHE */ 200#endif /* HAVE_DIRCACHE */
171 201
202/* mounts a file object as a target from elsewhere */
203bool fileobj_mount(const struct file_base_info *srcinfop,
204 unsigned int callflags,
205 struct file_base_binding **bindpp)
206{
207 struct fileobj_binding *fobp;
208 bind_source_info(srcinfop, &fobp);
209
210 CHECK_FO_DIRECTORY(callflags, fobp);
211
212 if (fobp->flags & FO_MOUNTTARGET)
213 return false; /* already mounted */
214
215 fobp->flags |= FDO_BUSY | FO_MOUNTTARGET |
216 (callflags & FO_DIRECTORY);
217
218 *bindpp = &fobp->bind;
219
220 return true;
221}
222
223/* unmounts the file object and frees it if now unusued */
224void fileobj_unmount(struct file_base_binding *bindp)
225{
226 struct fileobj_binding *fobp = (struct fileobj_binding *)bindp;
227
228 if (!(fobp->flags & FO_MOUNTTARGET))
229 return; /* not mounted */
230
231 if (STREAM_FIRST(fobp) == NULL)
232 release_binding(fobp); /* no longer in use */
233 else
234 fobp->flags &= ~FO_MOUNTTARGET;
235}
236
172/* opens the file object for a new stream and sets up the caches; 237/* 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 238 * the stream must already be opened at the FS driver level and *stream
174 * initialized. 239 * initialized.
@@ -180,10 +245,14 @@ void fileobj_fileop_open(struct filestr_base *stream,
180 const struct file_base_info *srcinfop, 245 const struct file_base_info *srcinfop,
181 unsigned int callflags) 246 unsigned int callflags)
182{ 247{
248 /* assign base file information */
183 struct fileobj_binding *fobp; 249 struct fileobj_binding *fobp;
184 bool first = binding_assign(srcinfop, &fobp); 250 bind_source_info(srcinfop, &fobp);
251
252 unsigned int foflags = fobp->flags;
185 253
186 /* add stream to this file's list */ 254 /* add stream to this file's list */
255 bool first = STREAM_FIRST(fobp) == NULL;
187 ll_insert_last(&fobp->list, &stream->node); 256 ll_insert_last(&fobp->list, &stream->node);
188 257
189 /* initiate the new stream into the enclave */ 258 /* initiate the new stream into the enclave */
@@ -197,27 +266,16 @@ void fileobj_fileop_open(struct filestr_base *stream,
197 if (first) 266 if (first)
198 { 267 {
199 /* first stream for file */ 268 /* first stream for file */
200 fobp->bind.info = *srcinfop; 269 fobp->flags = foflags | FDO_BUSY | FO_SINGLE |
201 fobp->flags = FDO_BUSY | FO_SINGLE | 270 (callflags & (FO_DIRECTORY|FO_TRUNC));
202 (callflags & (FO_DIRECTORY|FO_TRUNC)); 271 fobp->writers = 0;
203 fobp->writers = 0; 272 fobp->size = 0;
204 fobp->size = 0;
205
206 fileobj_bind_file(&fobp->bind);
207 } 273 }
208 else 274 else
209 { 275 {
210 /* additional stream for file */ 276 /* additional stream for file */
211 fobp->flags &= ~FO_SINGLE; 277 fobp->flags = (foflags & ~FO_SINGLE) | (callflags & FO_TRUNC);
212 fobp->flags |= callflags & FO_TRUNC; 278 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 } 279 }
222 280
223 if ((callflags & FD_WRITE) && ++fobp->writers == 1) 281 if ((callflags & FD_WRITE) && ++fobp->writers == 1)
@@ -257,12 +315,14 @@ void fileobj_fileop_close(struct filestr_base *stream)
257 if (foflags & FO_SINGLE) 315 if (foflags & FO_SINGLE)
258 { 316 {
259 /* last stream for file; close everything */ 317 /* last stream for file; close everything */
260 fileobj_unbind_file(&fobp->bind);
261
262 if (fobp->writers) 318 if (fobp->writers)
263 file_cache_free(&fobp->cache); 319 file_cache_free(&fobp->cache);
264 320
265 binding_add_to_free_list(fobp); 321 /* binding must stay valid if something is mounted to here */
322 if (foflags & FO_MOUNTTARGET)
323 fobp->flags = foflags & (FDO_BUSY|FO_DIRECTORY|FO_MOUNTTARGET);
324 else
325 release_binding(fobp);
266 } 326 }
267 else 327 else
268 { 328 {
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c
index 0935a9a6e3..078c0b6938 100644
--- a/firmware/common/pathfuncs.c
+++ b/firmware/common/pathfuncs.c
@@ -105,7 +105,7 @@ static const unsigned char storage_dec_indexes[STORAGE_NUM_TYPES+1] =
105 */ 105 */
106int path_strip_volume(const char *name, const char **nameptr, bool greedy) 106int path_strip_volume(const char *name, const char **nameptr, bool greedy)
107{ 107{
108 int volume = 0; 108 int volume = ROOT_VOLUME;
109 const char *t = name; 109 const char *t = name;
110 int c, v = 0; 110 int c, v = 0;
111 111
@@ -114,9 +114,16 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
114 * digits within the brackets is parsed as the volume number and of 114 * digits within the brackets is parsed as the volume number and of
115 * those, only the last ones VOL_MUM_MAX allows. 115 * those, only the last ones VOL_MUM_MAX allows.
116 */ 116 */
117 c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */ 117 t = GOBBLE_PATH_SEPCH(t); /* skip all leading slashes */
118 if (t == name)
119 {
120 volume = -1; /* relative path; don't know */
121 goto psv_out;
122 }
123
124 c = *t;
118 if (c != VOL_START_TOK) /* missing start token? no volume */ 125 if (c != VOL_START_TOK) /* missing start token? no volume */
119 goto volume0; 126 goto psv_out;
120 127
121 do 128 do
122 { 129 {
@@ -127,7 +134,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
127 break; 134 break;
128 case '\0': 135 case '\0':
129 case PATH_SEPCH: /* no closing bracket; no volume */ 136 case PATH_SEPCH: /* no closing bracket; no volume */
130 goto volume0; 137 goto psv_out;
131 default: /* something else; reset volume */ 138 default: /* something else; reset volume */
132 v = 0; 139 v = 0;
133 } 140 }
@@ -137,7 +144,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
137 if (!(c = *++t)) /* no more path and no '/' is ok */ 144 if (!(c = *++t)) /* no more path and no '/' is ok */
138 ; 145 ;
139 else if (c != PATH_SEPCH) /* more path and no separator after end */ 146 else if (c != PATH_SEPCH) /* more path and no separator after end */
140 goto volume0; 147 goto psv_out;
141 else if (greedy) 148 else if (greedy)
142 t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */ 149 t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */
143 150
@@ -146,7 +153,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
146 153
147 volume = v; 154 volume = v;
148 name = t; 155 name = t;
149volume0: 156psv_out:
150 if (nameptr) 157 if (nameptr)
151 *nameptr = name; 158 *nameptr = name;
152 return volume; 159 return volume;
@@ -157,10 +164,14 @@ volume0:
157 */ 164 */
158int get_volume_name(int volume, char *buffer) 165int get_volume_name(int volume, char *buffer)
159{ 166{
160 if (volume < 0) 167 if (volume < 0 || volume == ROOT_VOLUME)
161 { 168 {
162 *buffer = '\0'; 169 char *t = buffer;
163 return 0; 170 if (volume == ROOT_VOLUME)
171 *t++ = PATH_ROOTCHR;
172
173 *t = '\0';
174 return t - buffer;
164 } 175 }
165 176
166 volume %= VOL_NUM_MAX; /* as path parser would have it */ 177 volume %= VOL_NUM_MAX; /* as path parser would have it */
@@ -173,6 +184,20 @@ int get_volume_name(int volume, char *buffer)
173 return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c", 184 return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c",
174 VOL_START_TOK, voldec, volume, VOL_END_TOK); 185 VOL_START_TOK, voldec, volume, VOL_END_TOK);
175} 186}
187
188/* Returns volume name formatted with the root. Assumes buffer size is at
189 * least {VOL_MAX_LEN}+2 */
190int make_volume_root(int volume, char *buffer)
191{
192 char *t = buffer;
193
194 if (volume >= 0 && volume != ROOT_VOLUME)
195 *t++ = PATH_ROOTCHR;
196
197 t += get_volume_name(volume, t);
198
199 return t - buffer;
200}
176#endif /* HAVE_MULTIVOLUME */ 201#endif /* HAVE_MULTIVOLUME */
177 202
178/* Just like path_strip_volume() but strips a leading drive specifier and 203/* Just like path_strip_volume() but strips a leading drive specifier and
diff --git a/firmware/common/rb_namespace.c b/firmware/common/rb_namespace.c
new file mode 100644
index 0000000000..04f92e97af
--- /dev/null
+++ b/firmware/common/rb_namespace.c
@@ -0,0 +1,289 @@
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
26#define ROOT_CONTENTS_INDEX (NUM_VOLUMES)
27#define NUM_ROOT_ITEMS (NUM_VOLUMES+1)
28
29static uint8_t root_entry_flags[NUM_VOLUMES+1];
30static struct file_base_binding *root_bindp;
31
32static inline unsigned int get_root_item_state(int item)
33{
34 return root_entry_flags[item];
35}
36
37static inline void set_root_item_state(int item, unsigned int state)
38{
39 root_entry_flags[item] = state;
40}
41
42static void get_mount_point_entry(IF_MV(int volume,) struct dirent *entry)
43{
44#ifdef HAVE_MULTIVOLUME
45 get_volume_name(volume, entry->d_name);
46#else /* */
47 strcpy(entry->d_name, PATH_ROOTSTR);
48#endif /* HAVE_MULTIVOLUME */
49
50 /* is dirinfo_native */
51 entry->info.attr = ATTR_MOUNT_POINT;
52 entry->info.size = 0;
53 entry->info.wrtdate = 0;
54 entry->info.wrttime = 0;
55}
56
57/* unmount the directory that enumerates into the root namespace */
58static void unmount_item(int item)
59{
60 unsigned int state = get_root_item_state(item);
61 if (!state)
62 return;
63
64 if (state & NSITEM_CONTENTS)
65 {
66 fileobj_unmount(root_bindp);
67 root_bindp = NULL;
68 }
69
70 set_root_item_state(item, 0);
71}
72
73/* mount the directory that enumerates into the root namespace */
74int root_mount_path(const char *path, unsigned int flags)
75{
76#ifdef HAVE_MULTIVOLUME
77 int volume = path_strip_volume(path, NULL, false);
78 if (volume == ROOT_VOLUME)
79 return -EINVAL;
80
81 if (!CHECK_VOL(volume))
82 return -ENOENT;
83#else
84 if (!path_is_absolute(path))
85 return -ENOENT;
86#endif /* HAVE_MULTIVOLUME */
87
88 bool contents = flags & NSITEM_CONTENTS;
89 int item = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
90 unsigned int state = get_root_item_state(item);
91
92 if (state)
93 return -EBUSY;
94
95 if (contents)
96 {
97 /* cache information about the target */
98 struct filestr_base stream;
99 struct path_component_info compinfo;
100
101 int e = errno;
102 int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
103 FF_DEVPATH, &stream, &compinfo);
104 if (rc <= 0)
105 {
106 rc = rc ? -errno : -ENOENT;
107 errno = e;
108 return rc;
109 }
110
111 if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
112 return -EBUSY;
113 }
114
115 state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
116 set_root_item_state(item, state);
117
118 return 0;
119}
120
121/* inform root that an entire volume is being unmounted */
122void root_unmount_volume(IF_MV_NONVOID(int volume))
123{
124 FOR_EACH_VOLUME(volume, item)
125 {
126 #ifdef HAVE_MULTIVOLUME
127 uint32_t state = get_root_item_state(item);
128 if (state && (volume < 0 || item == volume))
129 #endif /* HAVE_MULTIVOLUME */
130 unmount_item(item);
131 }
132
133 /* if the volume unmounted contains the root directory contents then
134 the contents must also be unmounted */
135#ifdef HAVE_MULTIVOLUME
136 uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
137 if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
138#endif
139 unmount_item(ROOT_CONTENTS_INDEX);
140}
141
142/* parse the root part of a path */
143int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
144{
145 int volume = ROOT_VOLUME;
146
147#ifdef HAVE_MULTIVOLUME
148 /* this seamlessly integrates secondary filesystems into the
149 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
150 const char *p;
151 volume = path_strip_volume(path, &p, false);
152 if (volume != ROOT_VOLUME && !CHECK_VOL(volume))
153 return -ENOENT;
154#endif /* HAVE_MULTIVOLUME */
155
156 /* set name to start at last leading separator; name of root will
157 * be returned as "/", volume specifiers as "/<fooN>" */
158 *pathp = GOBBLE_PATH_SEPCH(path) - 1;
159 *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1;
160
161#ifdef HAVE_MULTIVOLUME
162 if (*lenp > MAX_COMPNAME+1)
163 return -ENAMETOOLONG;
164#endif
165
166 return volume;
167}
168
169/* open one of the items in the root */
170int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
171 struct file_base_info *infop, uint16_t *attrp)
172{
173 unsigned int callflags = *callflagsp;
174 bool devpath = !!(callflags & FF_DEVPATH);
175#ifdef HAVE_MULTIVOLUME
176 bool sysroot = volume == ROOT_VOLUME;
177 if (devpath && sysroot)
178 return -ENOENT; /* devpath needs volume spec */
179#else
180 bool sysroot = !devpath; /* always sysroot unless devpath */
181#endif
182
183 int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
184 unsigned int state = get_root_item_state(item);
185
186 if (sysroot)
187 {
188 *attrp = ATTR_SYSTEM_ROOT;
189
190 if (state)
191 *infop = root_bindp->info;
192 else
193 *callflagsp = callflags | FF_NOFS; /* contents not mounted */
194 }
195 else
196 {
197 *attrp = ATTR_MOUNT_POINT;
198
199 if (!devpath && !state)
200 return -ENOENT; /* regular open requires having been mounted */
201
202 if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
203 return -ENOENT; /* not mounted */
204
205 get_rootinfo_internal(infop);
206 }
207
208 return 0;
209}
210
211/* read root directory entries */
212int root_readdir_dirent(struct filestr_base *stream,
213 struct ns_scan_info *scanp, struct dirent *entry)
214{
215 int rc = 0;
216
217 int item = scanp->item;
218
219 /* skip any not-mounted or hidden items */
220 unsigned int state;
221 while (1)
222 {
223 if (item >= NUM_ROOT_ITEMS)
224 goto file_eod;
225
226 state = get_root_item_state(item);
227 if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
228 break;
229
230 item++;
231 }
232
233 if (item == ROOT_CONTENTS_INDEX)
234 {
235 rc = readdir_dirent(stream, &scanp->scan, entry);
236 if (rc < 0)
237 FILE_ERROR(ERRNO, rc * 10 - 1);
238
239 if (rc == 0)
240 item++;
241 }
242 else
243 {
244 get_mount_point_entry(IF_MV(item,) entry);
245 item++;
246 rc = 1;
247 }
248
249 scanp->item = item;
250
251file_eod:
252 if (rc == 0)
253 empty_dirent(entry);
254
255file_error:
256 return rc;
257}
258
259/* opens a stream to enumerate items in a namespace container */
260int ns_open_stream(const char *path, unsigned int callflags,
261 struct filestr_base *stream, struct ns_scan_info *scanp)
262{
263 /* stream still needs synchronization even if we don't have a stream */
264 static struct mutex no_contents_mtx SHAREDBSS_ATTR;
265
266 int rc = open_stream_internal(path, callflags, stream, NULL);
267 if (rc < 0)
268 FILE_ERROR(ERRNO, rc * 10 - 1);
269
270 scanp->item = rc > 1 ? 0 : -1;
271
272 if (stream->flags & FDO_BUSY)
273 {
274 /* root contents are mounted */
275 fat_rewind(&stream->fatstr);
276 }
277 else
278 {
279 /* root contents not mounted */
280 mutex_init(&no_contents_mtx);
281 stream->mtx = &no_contents_mtx;
282 }
283
284 ns_dirscan_rewind(scanp);
285
286 rc = 0;
287file_error:
288 return rc;
289}
diff --git a/firmware/export/mv.h b/firmware/export/mv.h
index ec7b2efdbd..5aa0ff8b4d 100644
--- a/firmware/export/mv.h
+++ b/firmware/export/mv.h
@@ -84,6 +84,10 @@
84#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1) 84#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1)
85#define VOL_NUM_MAX 100 85#define VOL_NUM_MAX 100
86 86
87#ifndef ROOT_VOLUME
88#define ROOT_VOLUME INT_MAX
89#endif
90
87#else /* empty definitions if no multi-volume */ 91#else /* empty definitions if no multi-volume */
88#define IF_MV(x...) 92#define IF_MV(x...)
89#define IF_MV_NONVOID(x...) void 93#define IF_MV_NONVOID(x...) void
diff --git a/firmware/export/pathfuncs.h b/firmware/export/pathfuncs.h
index 92539c54c1..8858d85d24 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 de591f0ec1..458a070f92 100644
--- a/firmware/export/rbpaths.h
+++ b/firmware/export/rbpaths.h
@@ -64,6 +64,9 @@
64#define PLUGIN_DIR ROCKBOX_DIR "/rocks" 64#define PLUGIN_DIR ROCKBOX_DIR "/rocks"
65#define CODECS_DIR ROCKBOX_DIR "/codecs" 65#define CODECS_DIR ROCKBOX_DIR "/codecs"
66 66
67#define RB_ROOT_VOL_HIDDEN(v) (IF_MV_VOL(v) == 0)
68#define RB_ROOT_CONTENTS_DIR "/" IF_MV("<0>")
69
67#else /* APPLICATION */ 70#else /* APPLICATION */
68 71
69#define HOME_DIR "<HOME>" /* replaced at runtime */ 72#define HOME_DIR "<HOME>" /* replaced at runtime */
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index 9fae16b551..9a8de2fecd 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
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index d62b5a8541..f4bd8bb8c2 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -72,16 +72,18 @@ enum fildes_and_obj_flags
72 /* used in descriptor and common */ 72 /* used in descriptor and common */
73 FDO_BUSY = 0x0001, /* descriptor/object is in use */ 73 FDO_BUSY = 0x0001, /* descriptor/object is in use */
74 /* only used in individual stream descriptor */ 74 /* only used in individual stream descriptor */
75 FD_WRITE = 0x0002, /* descriptor has write mode */ 75 FD_VALID = 0x0002, /* descriptor is valid but not registered */
76 FD_WRONLY = 0x0004, /* descriptor is write mode only */ 76 FD_WRITE = 0x0004, /* descriptor has write mode */
77 FD_APPEND = 0x0008, /* descriptor is append mode */ 77 FD_WRONLY = 0x0008, /* descriptor is write mode only */
78 FD_APPEND = 0x0010, /* descriptor is append mode */
78 FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */ 79 FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
79 /* only used as common flags */ 80 /* only used as common flags */
80 FO_DIRECTORY = 0x0010, /* fileobj is a directory */ 81 FO_DIRECTORY = 0x0020, /* fileobj is a directory */
81 FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */ 82 FO_TRUNC = 0x0040, /* fileobj is opened to be truncated */
82 FO_REMOVED = 0x0040, /* fileobj was deleted while open */ 83 FO_REMOVED = 0x0080, /* fileobj was deleted while open */
83 FO_SINGLE = 0x0080, /* fileobj has only one stream open */ 84 FO_SINGLE = 0x0100, /* fileobj has only one stream open */
84 FDO_MASK = 0x00ff, 85 FO_MOUNTTARGET = 0x0200, /* fileobj kept open as a mount target */
86 FDO_MASK = 0x03ff,
85 FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */ 87 FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */
86 /* bitflags that instruct various 'open' functions how to behave; 88 /* bitflags that instruct various 'open' functions how to behave;
87 * saved in stream flags (only) but not used by manager */ 89 * saved in stream flags (only) but not used by manager */
@@ -95,7 +97,9 @@ enum fildes_and_obj_flags
95 FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */ 97 FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */
96 FF_INFO = 0x00400000, /* return info on self */ 98 FF_INFO = 0x00400000, /* return info on self */
97 FF_PARENTINFO = 0x00800000, /* return info on parent */ 99 FF_PARENTINFO = 0x00800000, /* return info on parent */
98 FF_MASK = 0x00ff0000, 100 FF_DEVPATH = 0x01000000, /* path is a device path, not root-based */
101 FF_NOFS = 0x02000000, /* no filesystem mounted here */
102 FF_MASK = 0x03ff0000,
99}; 103};
100 104
101/** Common data structures used throughout **/ 105/** Common data structures used throughout **/
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..aee6daff6a 100644
--- a/firmware/include/fs_defines.h
+++ b/firmware/include/fs_defines.h
@@ -51,12 +51,19 @@
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/* internal functions open streams as well; make sure they don't fail if all
55 user descs are busy; this needs to be at least the greatest quantity needed
56 at once by all internal functions */
57#define MOUNT_AUX_FILEOBJS 1
58
54#ifdef HAVE_DIRCACHE 59#ifdef HAVE_DIRCACHE
55#define AUX_FILEOBJS 3 60#define DIRCACHE_AUX_FILEOBJS 1
56#else 61#else
57#define AUX_FILEOBJS 2 62#define DIRCACHE_AUX_FILEOBJS 0
58#endif 63#endif
59 64
65#define AUX_FILEOBJS (2+DIRCACHE_AUX_FILEOBJS+MOUNT_AUX_FILEOBJS)
66
60/* number of components statically allocated to handle the vast majority 67/* number of components statically allocated to handle the vast majority
61 of path depths; should maybe be tuned for >= 90th percentile but for now, 68 of path depths; should maybe be tuned for >= 90th percentile but for now,
62 imma just guessing based on something like: 69 imma just guessing based on something like:
diff --git a/firmware/include/rb_namespace.h b/firmware/include/rb_namespace.h
new file mode 100644
index 0000000000..4d7a125c7b
--- /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 766beb3fda..45483b7172 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 {