292 lines
7.9 KiB
C
292 lines
7.9 KiB
C
|
/***************************************************************************
|
||
|
* __________ __ ___.
|
||
|
* 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"
|
||
|
#include "file_internal.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 */
|
||
|
#if defined(_FILESYSTEM_NATIVE_H_)
|
||
|
entry->info.attr = ATTR_MOUNT_POINT;
|
||
|
entry->info.size = 0;
|
||
|
entry->info.wrtdate = 0;
|
||
|
entry->info.wrttime = 0;
|
||
|
#endif /* is dirinfo_native */
|
||
|
}
|
||
|
|
||
|
/* 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 CONFIG_PLATFORM & PLATFORM_NATIVE
|
||
|
if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
|
||
|
return -ENOENT; /* not mounted */
|
||
|
#endif
|
||
|
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:
|
||
|
#ifdef HAVE_DIRCACHE
|
||
|
if (rc == 0)
|
||
|
empty_dirent(entry);
|
||
|
#endif
|
||
|
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;
|
||
|
}
|