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
This commit is contained in:
William Wilgus 2017-02-03 17:13:58 -05:00
parent 31fc46ded6
commit 5ef28cccf9
18 changed files with 592 additions and 205 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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,

View File

@ -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"

View File

@ -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"
/**

View File

@ -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 = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile);
rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
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);
rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
if (rc < 0)
break;
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:

View File

@ -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
{

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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));
}

View File

@ -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 **/

View File

@ -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);

View File

@ -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:

View File

@ -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 */

View File

@ -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)
{