diff options
author | William Wilgus <me.theuser@yahoo.com> | 2017-02-03 17:13:58 -0500 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2020-08-20 23:08:57 +0000 |
commit | 5ef28cccf92f5eada6d502fa4b0e16a13e94be5b (patch) | |
tree | 05f9d2f8bdf3c0cc54c5893159a7dcf07c7e3e55 /firmware/common | |
parent | 31fc46ded69be7438cca2ba2c2b93c1f200165a6 (diff) |
Allow mounting of any directory as the root directory.
Provide definitions for the macros:
* RB_ROOT_VOL_HIDDEN(v) to exclude certain items from the root.
* RB_ROOT_CONTENTS to return a string with the name of the
directory to mount in the root.
Defaults are in export/rbpaths.h
It's a bit much for those that don't need the full functionality.
Some conditional define can cut it back a lot to cut out things only
needed if alternate root mounts are required. I'm just not bothering
yet. The basic concept would be applied to all targets to keep file
code from forking too much.
Change-Id: I90b5c0a1c949283d3102c16734b0b6ac73901a30
Diffstat (limited to 'firmware/common')
-rw-r--r-- | firmware/common/dir.c | 92 | ||||
-rw-r--r-- | firmware/common/dircache.c | 9 | ||||
-rw-r--r-- | firmware/common/disk.c | 2 | ||||
-rw-r--r-- | firmware/common/file.c | 2 | ||||
-rw-r--r-- | firmware/common/file_internal.c | 95 | ||||
-rw-r--r-- | firmware/common/fileobj_mgr.c | 114 | ||||
-rw-r--r-- | firmware/common/pathfuncs.c | 43 | ||||
-rw-r--r-- | firmware/common/rb_namespace.c | 289 |
8 files changed, 454 insertions, 192 deletions
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; +} |