summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
common/pathfuncs.c
common/fdprintf.c
common/linked_list.c
+common/rb_namespace.c
common/strcasecmp.c
common/strcasestr.c
common/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 @@
#include "dir.h"
#include "pathfuncs.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/* structure used for open directory streams */
static struct dirstr_desc
{
struct filestr_base stream; /* basic stream info (first!) */
- struct dirscan_info scan; /* directory scan cursor */
+ struct ns_scan_info scan; /* directory scan cursor */
struct dirent entry; /* current parsed entry information */
-#ifdef HAVE_MULTIVOLUME
- int volumecounter; /* counter for root volume entries */
-#endif
} open_streams[MAX_OPEN_DIRS];
/* check and return a struct dirstr_desc* from a DIR* */
@@ -47,7 +44,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
dir = NULL;
- else if (dir->stream.flags & FDO_BUSY)
+ else if (dir->stream.flags & (FDO_BUSY|FD_VALID))
return dir;
int errnum;
@@ -104,49 +101,6 @@ static struct dirstr_desc * alloc_dirstr(void)
return NULL;
}
-#ifdef HAVE_MULTIVOLUME
-static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry)
-{
- /* Volumes (secondary file systems) get inserted into the system root
- * directory. If the path specified volume 0, enumeration will not
- * include other volumes, but just its own files and directories.
- *
- * Fake special directories, which don't really exist, that will get
- * redirected upon opendir()
- */
- while (++dir->volumecounter < NUM_VOLUMES)
- {
- /* on the system root */
- if (!fat_ismounted(dir->volumecounter))
- continue;
-
- get_volume_name(dir->volumecounter, entry->d_name);
- dir->entry.info.attr = ATTR_MOUNT_POINT;
- dir->entry.info.size = 0;
- dir->entry.info.wrtdate = 0;
- dir->entry.info.wrttime = 0;
- return 1;
- }
-
- /* do normal directory entry fetching */
- return 0;
-}
-#endif /* HAVE_MULTIVOLUME */
-
-static inline int readdir_volume(struct dirstr_desc *dir,
- struct dirent *entry)
-{
-#ifdef HAVE_MULTIVOLUME
- /* fetch virtual volume entries? */
- if (dir->volumecounter < NUM_VOLUMES)
- return readdir_volume_inner(dir, entry);
-#endif /* HAVE_MULTIVOLUME */
-
- /* do normal directory entry fetching */
- return 0;
- (void)dir; (void)entry;
-}
-
/** POSIX interface **/
@@ -165,21 +119,13 @@ DIR * opendir(const char *dirname)
if (!dir)
FILE_ERROR(EMFILE, RC);
- rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL);
+ rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan);
if (rc < 0)
{
DEBUGF("Open failed: %d\n", rc);
FILE_ERROR(ERRNO, RC);
}
-#ifdef HAVE_MULTIVOLUME
- /* volume counter is relevant only to the system root */
- dir->volumecounter = rc > 1 ? 0 : INT_MAX;
-#endif /* HAVE_MULTIVOLUME */
-
- fat_rewind(&dir->stream.fatstr);
- rewinddir_dirent(&dir->scan);
-
dirp = (DIR *)dir;
file_error:
file_internal_unlock_WRITER();
@@ -204,7 +150,7 @@ int closedir(DIR *dirp)
FILE_ERROR(EBADF, -2);
}
- rc = close_stream_internal(&dir->stream);
+ rc = ns_close_stream(&dir->stream);
if (rc < 0)
FILE_ERROR(ERRNO, rc * 10 - 3);
@@ -222,16 +168,11 @@ struct dirent * readdir(DIR *dirp)
struct dirent *res = NULL;
- int rc = readdir_volume(dir, &dir->entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
- if (rc < 0)
- FILE_ERROR(EIO, RC);
- }
-
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
if (rc > 0)
res = &dir->entry;
+ else if (rc < 0)
+ FILE_ERROR(EIO, RC);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -258,13 +199,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
if (!dir)
FILE_ERROR_RETURN(ERRNO, -1);
- int rc = readdir_volume(dir, entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, entry);
- if (rc < 0)
- FILE_ERROR(EIO, rc * 10 - 4);
- }
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry);
+ if (rc < 0)
+ FILE_ERROR(EIO, rc * 10 - 4);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -288,12 +225,7 @@ void rewinddir(DIR *dirp)
if (!dir)
FILE_ERROR_RETURN(ERRNO);
- rewinddir_dirent(&dir->scan);
-
-#ifdef HAVE_MULTIVOLUME
- if (dir->volumecounter != INT_MAX)
- dir->volumecounter = 0;
-#endif /* HAVE_MULTIVOLUME */
+ ns_dirscan_rewind(&dir->scan);
RELEASE_DIRSTR(READER, dir);
}
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)
cename = "";
#ifdef HAVE_MULTIVOLUME
+ /* prepend the volume specifier */
int volume = IF_MV_VOL(-idx - 1);
- if (volume > 0)
- {
- /* prepend the volume specifier for volumes > 0 */
- cename = alloca(VOL_MAX_LEN+1);
- get_volume_name(volume, cename);
- }
+ cename = alloca(VOL_MAX_LEN+1);
+ get_volume_name(volume, cename);
#endif /* HAVE_MULTIVOLUME */
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 @@
#include "disk_cache.h"
#include "fileobj_mgr.h"
#include "dir.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
#include "disk.h"
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 @@
#include "file.h"
#include "fileobj_mgr.h"
#include "disk_cache.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
#include "string-extra.h"
/**
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 @@
#include "pathfuncs.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dir.h"
-#include "dircache_redirect.h"
-#include "dircache.h"
+#include "rb_namespace.h"
#include "string-extra.h"
#include "rbunicode.h"
@@ -87,9 +85,10 @@ void file_cache_free(struct filestr_cache *cachep)
/** Stream base APIs **/
-static inline void filestr_clear(struct filestr_base *stream)
+static inline void filestr_clear(struct filestr_base *stream,
+ unsigned int flags)
{
- stream->flags = 0;
+ stream->flags = flags;
stream->bindp = NULL;
#if 0
stream->mtx = NULL;
@@ -153,7 +152,7 @@ void filestr_discard_cache(struct filestr_base *stream)
/* Initialize the base descriptor */
void filestr_base_init(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, FD_VALID);
file_cache_init(&stream->cache);
stream->cachep = &stream->cache;
}
@@ -161,7 +160,7 @@ void filestr_base_init(struct filestr_base *stream)
/* free base descriptor resources */
void filestr_base_destroy(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, 0);
filestr_free_cache(stream);
}
@@ -293,7 +292,7 @@ struct pathwalk_component
#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
#define WALK_RC_FOUND 1 /* found and opened */
-#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
+#define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */
#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
/* return another struct pathwalk_component from the pool, or NULL if the
@@ -397,10 +396,10 @@ static int walk_open_info(struct pathwalk *walkp,
/* make open official if not simply probing for presence - must do it here
or compp->info on stack will get destroyed before it was copied */
- if (!(callflags & FF_PROBE))
+ if (!(callflags & (FF_PROBE|FF_NOFS)))
fileop_onopen_internal(stream, &compp->info, callflags);
- return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
+ return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
}
/* check the component against the prefix test info */
@@ -507,6 +506,10 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
if (len > MAX_COMPNAME)
return -ENAMETOOLONG;
+ /* no filesystem is mounted here */
+ if (walkp->callflags & FF_NOFS)
+ return -ENOENT;
+
/* check for "." and ".." */
if (name[0] == '.')
{
@@ -575,7 +578,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX);
/* This lets it be passed quietly to directory scanning */
- stream->flags = callflags & FF_MASK;
+ stream->flags |= callflags & FF_MASK;
struct pathwalk walk;
walk.path = path;
@@ -585,80 +588,36 @@ int open_stream_internal(const char *path, unsigned int callflags,
struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
rootp->nextp = NULL;
- rootp->attr = ATTR_SYSTEM_ROOT;
-
-#ifdef HAVE_MULTIVOLUME
- int volume = 0, rootrc = WALK_RC_FOUND;
-#endif /* HAVE_MULTIVOLUME */
while (1)
{
- const char *pathptr = walk.path;
-
- #ifdef HAVE_MULTIVOLUME
- /* this seamlessly integrates secondary filesystems into the
- root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
- const char *p;
- volume = path_strip_volume(pathptr, &p, false);
- if (!CHECK_VOL(volume))
- {
- DEBUGF("No such device or address: %d\n", volume);
- FILE_ERROR(ENXIO, -2);
- }
-
- if (p == pathptr)
- {
- /* the root of this subpath is the system root */
- rootp->attr = ATTR_SYSTEM_ROOT;
- rootrc = WALK_RC_FOUND_ROOT;
- }
- else
- {
- /* this subpath specifies a mount point */
- rootp->attr = ATTR_MOUNT_POINT;
- rootrc = WALK_RC_FOUND;
- }
-
- walk.path = p;
- #endif /* HAVE_MULTIVOLUME */
-
- /* set name to start at last leading separator; names of volume
- specifiers will be returned as "/<fooN>" */
- rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
- rootp->length =
- IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
+ rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
+ if (rc < 0)
+ break;
- rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile);
+ rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
if (rc < 0)
- {
- /* not mounted */
- DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
- rc = -ENXIO;
break;
- }
- get_rootinfo_internal(&rootp->info);
+ walk.path = rootp->name + rootp->length;
+
rc = walk_path(&walk, rootp, stream);
if (rc != WALK_RC_CONT_AT_ROOT)
break;
}
- switch (rc)
+ if (rc >= 0)
{
- case WALK_RC_FOUND_ROOT:
- IF_MV( rc = rootrc; )
- case WALK_RC_NOT_FOUND:
- case WALK_RC_FOUND:
/* FF_PROBE leaves nothing for caller to clean up */
- if (callflags & FF_PROBE)
+ if (walk.callflags & FF_PROBE)
filestr_base_destroy(stream);
-
- break;
-
- default: /* utter, abject failure :`( */
+ }
+ else
+ {
+ /* utter, abject failure :`( */
DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
filestr_base_destroy(stream);
- FILE_ERROR(-rc, -3);
+ FILE_ERROR(-rc, -1);
}
file_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 @@
****************************************************************************/
#include "config.h"
#include "system.h"
+#include <errno.h>
#include "debug.h"
#include "file.h"
#include "dir.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/**
* Manages file and directory streams on all volumes
@@ -34,8 +35,8 @@
*/
-/* there will always be enough of these for all user handles, thus these
- functions don't return failure codes */
+/* there will always be enough of these for all user handles, thus most of
+ these functions don't return failure codes */
#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS)
/* describes the file as an image on the storage medium */
@@ -84,6 +85,15 @@ static struct ll_head busy_bindings[NUM_VOLUMES];
for (struct filestr_base *s = STREAM_##what(start); \
s; s = STREAM_NEXT(s))
+/* once a file/directory, always a file/directory; such a change
+ is a bug */
+#define CHECK_FO_DIRECTORY(callflags, fobp) \
+ if (((callflags) ^ (fobp)->flags) & FO_DIRECTORY) \
+ { \
+ DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", \
+ __func__, (fobp), (callflags)); \
+ }
+
/* syncs information for the stream's old and new parent directory if any are
currently opened */
@@ -96,6 +106,10 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
continue; /* not directory or removed can't be parent of anything */
struct filestr_base *parentstrp = STREAM_FIRST(fobp);
+
+ if (!parentstrp)
+ continue;
+
struct fat_file *parentfilep = &parentstrp->infop->fatfile;
for (int i = 0; i < count; i++)
@@ -111,8 +125,7 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
}
/* see if this file has open streams and return that fileobj_binding if so,
- else grab a new one from the free list; returns true if this stream is
- the only open one */
+ else grab a new one from the free list; returns true if this is new */
static bool binding_assign(const struct file_base_info *srcinfop,
struct fileobj_binding **fobpp)
{
@@ -123,7 +136,7 @@ static bool binding_assign(const struct file_base_info *srcinfop,
if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
{
- /* already has open streams */
+ /* already has open streams/mounts */
*fobpp = fobp;
return false;
}
@@ -143,6 +156,23 @@ static void binding_add_to_free_list(struct fileobj_binding *fobp)
ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
}
+static void bind_source_info(const struct file_base_info *srcinfop,
+ struct fileobj_binding **fobpp)
+{
+ if (!binding_assign(srcinfop, fobpp))
+ return; /* already in use */
+
+ /* is new */
+ (*fobpp)->bind.info = *srcinfop;
+ fileobj_bind_file(&(*fobpp)->bind);
+}
+
+static void release_binding(struct fileobj_binding *fobp)
+{
+ fileobj_unbind_file(&fobp->bind);
+ binding_add_to_free_list(fobp);
+}
+
/** File and directory internal interface **/
void file_binding_insert_last(struct file_base_binding *bindp)
@@ -169,6 +199,41 @@ void file_binding_remove_next(struct file_base_binding *prevp,
}
#endif /* HAVE_DIRCACHE */
+/* mounts a file object as a target from elsewhere */
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp)
+{
+ struct fileobj_binding *fobp;
+ bind_source_info(srcinfop, &fobp);
+
+ CHECK_FO_DIRECTORY(callflags, fobp);
+
+ if (fobp->flags & FO_MOUNTTARGET)
+ return false; /* already mounted */
+
+ fobp->flags |= FDO_BUSY | FO_MOUNTTARGET |
+ (callflags & FO_DIRECTORY);
+
+ *bindpp = &fobp->bind;
+
+ return true;
+}
+
+/* unmounts the file object and frees it if now unusued */
+void fileobj_unmount(struct file_base_binding *bindp)
+{
+ struct fileobj_binding *fobp = (struct fileobj_binding *)bindp;
+
+ if (!(fobp->flags & FO_MOUNTTARGET))
+ return; /* not mounted */
+
+ if (STREAM_FIRST(fobp) == NULL)
+ release_binding(fobp); /* no longer in use */
+ else
+ fobp->flags &= ~FO_MOUNTTARGET;
+}
+
/* opens the file object for a new stream and sets up the caches;
* the stream must already be opened at the FS driver level and *stream
* initialized.
@@ -180,10 +245,14 @@ void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags)
{
+ /* assign base file information */
struct fileobj_binding *fobp;
- bool first = binding_assign(srcinfop, &fobp);
+ bind_source_info(srcinfop, &fobp);
+
+ unsigned int foflags = fobp->flags;
/* add stream to this file's list */
+ bool first = STREAM_FIRST(fobp) == NULL;
ll_insert_last(&fobp->list, &stream->node);
/* initiate the new stream into the enclave */
@@ -197,27 +266,16 @@ void fileobj_fileop_open(struct filestr_base *stream,
if (first)
{
/* first stream for file */
- fobp->bind.info = *srcinfop;
- fobp->flags = FDO_BUSY | FO_SINGLE |
- (callflags & (FO_DIRECTORY|FO_TRUNC));
- fobp->writers = 0;
- fobp->size = 0;
-
- fileobj_bind_file(&fobp->bind);
+ fobp->flags = foflags | FDO_BUSY | FO_SINGLE |
+ (callflags & (FO_DIRECTORY|FO_TRUNC));
+ fobp->writers = 0;
+ fobp->size = 0;
}
else
{
/* additional stream for file */
- fobp->flags &= ~FO_SINGLE;
- fobp->flags |= callflags & FO_TRUNC;
-
- /* once a file/directory, always a file/directory; such a change
- is a bug */
- if ((callflags ^ fobp->flags) & FO_DIRECTORY)
- {
- DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
- __func__, stream, callflags);
- }
+ fobp->flags = (foflags & ~FO_SINGLE) | (callflags & FO_TRUNC);
+ CHECK_FO_DIRECTORY(callflags, fobp);
}
if ((callflags & FD_WRITE) && ++fobp->writers == 1)
@@ -257,12 +315,14 @@ void fileobj_fileop_close(struct filestr_base *stream)
if (foflags & FO_SINGLE)
{
/* last stream for file; close everything */
- fileobj_unbind_file(&fobp->bind);
-
if (fobp->writers)
file_cache_free(&fobp->cache);
- binding_add_to_free_list(fobp);
+ /* binding must stay valid if something is mounted to here */
+ if (foflags & FO_MOUNTTARGET)
+ fobp->flags = foflags & (FDO_BUSY|FO_DIRECTORY|FO_MOUNTTARGET);
+ else
+ release_binding(fobp);
}
else
{
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] =
*/
int path_strip_volume(const char *name, const char **nameptr, bool greedy)
{
- int volume = 0;
+ int volume = ROOT_VOLUME;
const char *t = name;
int c, v = 0;
@@ -114,9 +114,16 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
* digits within the brackets is parsed as the volume number and of
* those, only the last ones VOL_MUM_MAX allows.
*/
- c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */
+ t = GOBBLE_PATH_SEPCH(t); /* skip all leading slashes */
+ if (t == name)
+ {
+ volume = -1; /* relative path; don't know */
+ goto psv_out;
+ }
+
+ c = *t;
if (c != VOL_START_TOK) /* missing start token? no volume */
- goto volume0;
+ goto psv_out;
do
{
@@ -127,7 +134,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
break;
case '\0':
case PATH_SEPCH: /* no closing bracket; no volume */
- goto volume0;
+ goto psv_out;
default: /* something else; reset volume */
v = 0;
}
@@ -137,7 +144,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
if (!(c = *++t)) /* no more path and no '/' is ok */
;
else if (c != PATH_SEPCH) /* more path and no separator after end */
- goto volume0;
+ goto psv_out;
else if (greedy)
t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */
@@ -146,7 +153,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
volume = v;
name = t;
-volume0:
+psv_out:
if (nameptr)
*nameptr = name;
return volume;
@@ -157,10 +164,14 @@ volume0:
*/
int get_volume_name(int volume, char *buffer)
{
- if (volume < 0)
+ if (volume < 0 || volume == ROOT_VOLUME)
{
- *buffer = '\0';
- return 0;
+ char *t = buffer;
+ if (volume == ROOT_VOLUME)
+ *t++ = PATH_ROOTCHR;
+
+ *t = '\0';
+ return t - buffer;
}
volume %= VOL_NUM_MAX; /* as path parser would have it */
@@ -173,6 +184,20 @@ int get_volume_name(int volume, char *buffer)
return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c",
VOL_START_TOK, voldec, volume, VOL_END_TOK);
}
+
+/* Returns volume name formatted with the root. Assumes buffer size is at
+ * least {VOL_MAX_LEN}+2 */
+int make_volume_root(int volume, char *buffer)
+{
+ char *t = buffer;
+
+ if (volume >= 0 && volume != ROOT_VOLUME)
+ *t++ = PATH_ROOTCHR;
+
+ t += get_volume_name(volume, t);
+
+ return t - buffer;
+}
#endif /* HAVE_MULTIVOLUME */
/* 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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2017 by Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include <errno.h>
+#include "fileobj_mgr.h"
+#include "rb_namespace.h"
+
+#define ROOT_CONTENTS_INDEX (NUM_VOLUMES)
+#define NUM_ROOT_ITEMS (NUM_VOLUMES+1)
+
+static uint8_t root_entry_flags[NUM_VOLUMES+1];
+static struct file_base_binding *root_bindp;
+
+static inline unsigned int get_root_item_state(int item)
+{
+ return root_entry_flags[item];
+}
+
+static inline void set_root_item_state(int item, unsigned int state)
+{
+ root_entry_flags[item] = state;
+}
+
+static void get_mount_point_entry(IF_MV(int volume,) struct dirent *entry)
+{
+#ifdef HAVE_MULTIVOLUME
+ get_volume_name(volume, entry->d_name);
+#else /* */
+ strcpy(entry->d_name, PATH_ROOTSTR);
+#endif /* HAVE_MULTIVOLUME */
+
+ /* is dirinfo_native */
+ entry->info.attr = ATTR_MOUNT_POINT;
+ entry->info.size = 0;
+ entry->info.wrtdate = 0;
+ entry->info.wrttime = 0;
+}
+
+/* unmount the directory that enumerates into the root namespace */
+static void unmount_item(int item)
+{
+ unsigned int state = get_root_item_state(item);
+ if (!state)
+ return;
+
+ if (state & NSITEM_CONTENTS)
+ {
+ fileobj_unmount(root_bindp);
+ root_bindp = NULL;
+ }
+
+ set_root_item_state(item, 0);
+}
+
+/* mount the directory that enumerates into the root namespace */
+int root_mount_path(const char *path, unsigned int flags)
+{
+#ifdef HAVE_MULTIVOLUME
+ int volume = path_strip_volume(path, NULL, false);
+ if (volume == ROOT_VOLUME)
+ return -EINVAL;
+
+ if (!CHECK_VOL(volume))
+ return -ENOENT;
+#else
+ if (!path_is_absolute(path))
+ return -ENOENT;
+#endif /* HAVE_MULTIVOLUME */
+
+ bool contents = flags & NSITEM_CONTENTS;
+ int item = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
+ unsigned int state = get_root_item_state(item);
+
+ if (state)
+ return -EBUSY;
+
+ if (contents)
+ {
+ /* cache information about the target */
+ struct filestr_base stream;
+ struct path_component_info compinfo;
+
+ int e = errno;
+ int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
+ FF_DEVPATH, &stream, &compinfo);
+ if (rc <= 0)
+ {
+ rc = rc ? -errno : -ENOENT;
+ errno = e;
+ return rc;
+ }
+
+ if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
+ return -EBUSY;
+ }
+
+ state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
+ set_root_item_state(item, state);
+
+ return 0;
+}
+
+/* inform root that an entire volume is being unmounted */
+void root_unmount_volume(IF_MV_NONVOID(int volume))
+{
+ FOR_EACH_VOLUME(volume, item)
+ {
+ #ifdef HAVE_MULTIVOLUME
+ uint32_t state = get_root_item_state(item);
+ if (state && (volume < 0 || item == volume))
+ #endif /* HAVE_MULTIVOLUME */
+ unmount_item(item);
+ }
+
+ /* if the volume unmounted contains the root directory contents then
+ the contents must also be unmounted */
+#ifdef HAVE_MULTIVOLUME
+ uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
+ if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
+#endif
+ unmount_item(ROOT_CONTENTS_INDEX);
+}
+
+/* parse the root part of a path */
+int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
+{
+ int volume = ROOT_VOLUME;
+
+#ifdef HAVE_MULTIVOLUME
+ /* this seamlessly integrates secondary filesystems into the
+ root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
+ const char *p;
+ volume = path_strip_volume(path, &p, false);
+ if (volume != ROOT_VOLUME && !CHECK_VOL(volume))
+ return -ENOENT;
+#endif /* HAVE_MULTIVOLUME */
+
+ /* set name to start at last leading separator; name of root will
+ * be returned as "/", volume specifiers as "/<fooN>" */
+ *pathp = GOBBLE_PATH_SEPCH(path) - 1;
+ *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1;
+
+#ifdef HAVE_MULTIVOLUME
+ if (*lenp > MAX_COMPNAME+1)
+ return -ENAMETOOLONG;
+#endif
+
+ return volume;
+}
+
+/* open one of the items in the root */
+int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
+ struct file_base_info *infop, uint16_t *attrp)
+{
+ unsigned int callflags = *callflagsp;
+ bool devpath = !!(callflags & FF_DEVPATH);
+#ifdef HAVE_MULTIVOLUME
+ bool sysroot = volume == ROOT_VOLUME;
+ if (devpath && sysroot)
+ return -ENOENT; /* devpath needs volume spec */
+#else
+ bool sysroot = !devpath; /* always sysroot unless devpath */
+#endif
+
+ int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
+ unsigned int state = get_root_item_state(item);
+
+ if (sysroot)
+ {
+ *attrp = ATTR_SYSTEM_ROOT;
+
+ if (state)
+ *infop = root_bindp->info;
+ else
+ *callflagsp = callflags | FF_NOFS; /* contents not mounted */
+ }
+ else
+ {
+ *attrp = ATTR_MOUNT_POINT;
+
+ if (!devpath && !state)
+ return -ENOENT; /* regular open requires having been mounted */
+
+ if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
+ return -ENOENT; /* not mounted */
+
+ get_rootinfo_internal(infop);
+ }
+
+ return 0;
+}
+
+/* read root directory entries */
+int root_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp, struct dirent *entry)
+{
+ int rc = 0;
+
+ int item = scanp->item;
+
+ /* skip any not-mounted or hidden items */
+ unsigned int state;
+ while (1)
+ {
+ if (item >= NUM_ROOT_ITEMS)
+ goto file_eod;
+
+ state = get_root_item_state(item);
+ if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
+ break;
+
+ item++;
+ }
+
+ if (item == ROOT_CONTENTS_INDEX)
+ {
+ rc = readdir_dirent(stream, &scanp->scan, entry);
+ if (rc < 0)
+ FILE_ERROR(ERRNO, rc * 10 - 1);
+
+ if (rc == 0)
+ item++;
+ }
+ else
+ {
+ get_mount_point_entry(IF_MV(item,) entry);
+ item++;
+ rc = 1;
+ }
+
+ scanp->item = item;
+
+file_eod:
+ if (rc == 0)
+ empty_dirent(entry);
+
+file_error:
+ return rc;
+}
+
+/* opens a stream to enumerate items in a namespace container */
+int ns_open_stream(const char *path, unsigned int callflags,
+ struct filestr_base *stream, struct ns_scan_info *scanp)
+{
+ /* stream still needs synchronization even if we don't have a stream */
+ static struct mutex no_contents_mtx SHAREDBSS_ATTR;
+
+ int rc = open_stream_internal(path, callflags, stream, NULL);
+ if (rc < 0)
+ FILE_ERROR(ERRNO, rc * 10 - 1);
+
+ scanp->item = rc > 1 ? 0 : -1;
+
+ if (stream->flags & FDO_BUSY)
+ {
+ /* root contents are mounted */
+ fat_rewind(&stream->fatstr);
+ }
+ else
+ {
+ /* root contents not mounted */
+ mutex_init(&no_contents_mtx);
+ stream->mtx = &no_contents_mtx;
+ }
+
+ ns_dirscan_rewind(scanp);
+
+ rc = 0;
+file_error:
+ return rc;
+}
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 @@
#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1)
#define VOL_NUM_MAX 100
+#ifndef ROOT_VOLUME
+#define ROOT_VOLUME INT_MAX
+#endif
+
#else /* empty definitions if no multi-volume */
#define IF_MV(x...)
#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 @@
/* useful char constants that could be reconfigured if desired */
#define PATH_SEPCH '/'
#define PATH_SEPSTR "/"
+#define PATH_ROOTCHR '/'
#define PATH_ROOTSTR "/"
#define PATH_BADSEPCH '\\'
#define PATH_DRVSEPCH ':'
+#ifndef ROOT_VOLUME
+#define ROOT_VOLUME INT_MAX
+#endif
+
/* a nicer way to check for "." and ".." than two strcmp() calls */
static inline bool is_dotdir_name(const char *name)
{
@@ -75,6 +80,7 @@ static inline bool name_is_dot_dot(const char *name)
#ifdef HAVE_MULTIVOLUME
int path_strip_volume(const char *name, const char **nameptr, bool greedy);
int get_volume_name(int volume, char *name);
+int make_volume_root(int volume, char *dst);
#endif
int 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 @@
#define PLUGIN_DIR ROCKBOX_DIR "/rocks"
#define CODECS_DIR ROCKBOX_DIR "/codecs"
+#define RB_ROOT_VOL_HIDDEN(v) (IF_MV_VOL(v) == 0)
+#define RB_ROOT_CONTENTS_DIR "/" IF_MV("<0>")
+
#else /* APPLICATION */
#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 @@
****************************************************************************/
#ifndef _DIRCACHE_REDIRECT_H_
+#include "rbpaths.h"
+#include "pathfuncs.h"
#include "dir.h"
+#include "dircache.h"
/***
** 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)
static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
{
+#ifdef HAVE_MULTIVOLUME
+ char path[VOL_MAX_LEN+2];
+ make_volume_root(volume, path);
+#else
+ const char *path = PATH_ROOTSTR;
+#endif
+ root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
+#ifdef HAVE_MULTIVOLUME
+ if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false))
+#endif
+ root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
#ifdef HAVE_DIRCACHE
dircache_mount();
#endif
- IF_MV( (void)volume; )
}
static 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))
/* First, to avoid update of something about to be destroyed anyway */
dircache_unmount(IF_MV(volume));
#endif
+ root_unmount_volume(IF_MV(volume));
fileobj_mgr_unmount(IF_MV(volume));
}
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
/* used in descriptor and common */
FDO_BUSY = 0x0001, /* descriptor/object is in use */
/* only used in individual stream descriptor */
- FD_WRITE = 0x0002, /* descriptor has write mode */
- FD_WRONLY = 0x0004, /* descriptor is write mode only */
- FD_APPEND = 0x0008, /* descriptor is append mode */
+ FD_VALID = 0x0002, /* descriptor is valid but not registered */
+ FD_WRITE = 0x0004, /* descriptor has write mode */
+ FD_WRONLY = 0x0008, /* descriptor is write mode only */
+ FD_APPEND = 0x0010, /* descriptor is append mode */
FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
/* only used as common flags */
- FO_DIRECTORY = 0x0010, /* fileobj is a directory */
- FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */
- FO_REMOVED = 0x0040, /* fileobj was deleted while open */
- FO_SINGLE = 0x0080, /* fileobj has only one stream open */
- FDO_MASK = 0x00ff,
+ FO_DIRECTORY = 0x0020, /* fileobj is a directory */
+ FO_TRUNC = 0x0040, /* fileobj is opened to be truncated */
+ FO_REMOVED = 0x0080, /* fileobj was deleted while open */
+ FO_SINGLE = 0x0100, /* fileobj has only one stream open */
+ FO_MOUNTTARGET = 0x0200, /* fileobj kept open as a mount target */
+ FDO_MASK = 0x03ff,
FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */
/* bitflags that instruct various 'open' functions how to behave;
* saved in stream flags (only) but not used by manager */
@@ -95,7 +97,9 @@ enum fildes_and_obj_flags
FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */
FF_INFO = 0x00400000, /* return info on self */
FF_PARENTINFO = 0x00800000, /* return info on parent */
- FF_MASK = 0x00ff0000,
+ FF_DEVPATH = 0x01000000, /* path is a device path, not root-based */
+ FF_NOFS = 0x02000000, /* no filesystem mounted here */
+ FF_MASK = 0x03ff0000,
};
/** 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);
void file_binding_remove_next(struct file_base_binding *prevp,
struct file_base_binding *bindp);
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp);
+void fileobj_unmount(struct file_base_binding *bindp);
+
void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
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 @@
/* internal functions open streams as well; make sure they don't fail if all
user descs are busy; this needs to be at least the greatest quantity needed
at once by all internal functions */
+/* internal functions open streams as well; make sure they don't fail if all
+ user descs are busy; this needs to be at least the greatest quantity needed
+ at once by all internal functions */
+#define MOUNT_AUX_FILEOBJS 1
+
#ifdef HAVE_DIRCACHE
-#define AUX_FILEOBJS 3
+#define DIRCACHE_AUX_FILEOBJS 1
#else
-#define AUX_FILEOBJS 2
+#define DIRCACHE_AUX_FILEOBJS 0
#endif
+#define AUX_FILEOBJS (2+DIRCACHE_AUX_FILEOBJS+MOUNT_AUX_FILEOBJS)
+
/* number of components statically allocated to handle the vast majority
of path depths; should maybe be tuned for >= 90th percentile but for now,
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2017 by Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef RB_NAMESPACE_H
+#define RB_NAMESPACE_H
+
+#include "file_internal.h"
+
+enum ns_item_flags
+{
+ NSITEM_MOUNTED = 0x01, /* item is mounted */
+ NSITEM_HIDDEN = 0x02, /* item is not enumerated */
+ NSITEM_CONTENTS = 0x04, /* contents enumerate */
+};
+
+struct ns_scan_info
+{
+ struct dirscan_info scan; /* dirscan info - first! */
+ int item; /* current item in parent */
+};
+
+/* root functions */
+int root_mount_path(const char *path, unsigned int flags);
+void root_unmount_volume(IF_MV_NONVOID(int volume));
+int root_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp,
+ struct dirent *entry);
+
+/* namespace functions */
+int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp);
+int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
+ struct file_base_info *infop, uint16_t *attrp);
+int ns_open_stream(const char *path, unsigned int callflags,
+ struct filestr_base *stream, struct ns_scan_info *scanp);
+
+/* closes the namespace stream */
+static inline int ns_close_stream(struct filestr_base *stream)
+{
+ return close_stream_internal(stream);
+}
+
+#include "dircache_redirect.h"
+
+static inline void ns_dirscan_rewind(struct ns_scan_info *scanp)
+{
+ rewinddir_dirent(&scanp->scan);
+ if (scanp->item != -1)
+ scanp->item = 0;
+}
+
+static inline int ns_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp,
+ struct dirent *entry)
+
+{
+ if (scanp->item == -1)
+ return readdir_dirent(stream, &scanp->scan, entry);
+ else
+ return root_readdir_dirent(stream, scanp, entry);
+}
+
+#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)
const char *next;
volume = path_strip_volume(p, &next, true);
+ if (volume == ROOT_VOLUME)
+ volume = 0; /* FIXME: root no longer implies volume 0 */
if (next > p)
{