summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 {