mirror of
				https://github.com/Rockbox/rockbox.git
				synced 2025-10-26 23:36:37 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			384 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			384 lines
		
	
	
	
		
			11 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"
 | |
| #include <stdio.h> /*snprintf*/
 | |
| 
 | |
| /* Define LOGF_ENABLE to enable logf output in this file */
 | |
| //#define LOGF_ENABLE
 | |
| #include "logf.h"
 | |
| 
 | |
| #if !defined(HAVE_MULTIVOLUME) && defined(LOGF_ENABLE)
 | |
|     int volume = 0;
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #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 */
 | |
|     logf("%s: vol:%d, %s", __func__, volume, entry->d_name);
 | |
| }
 | |
| 
 | |
| /* unmount the directory that enumerates into the root namespace */
 | |
| static void unmount_item(int item)
 | |
| {
 | |
|     unsigned int state = get_root_item_state(item);
 | |
|     logf("%s: state: %u", __func__, state);
 | |
|     if (!state)
 | |
|         return;
 | |
| 
 | |
|     if (item == ROOT_CONTENTS_INDEX && state & NSITEM_CONTENTS)
 | |
|     {
 | |
|         fileobj_unmount(root_bindp);
 | |
|         root_bindp = NULL;
 | |
|     }
 | |
| 
 | |
|     set_root_item_state(item, 0);
 | |
| }
 | |
| 
 | |
| static char *root_realpath_internal(void)
 | |
| {
 | |
|     static char root_realpath[ROOT_MAX_REALPATH];
 | |
|     return root_realpath;
 | |
| }
 | |
| const char* root_get_realpath(void)
 | |
| {
 | |
|     return root_realpath_internal();
 | |
| }
 | |
| 
 | |
| /* mount the directory that enumerates into the root namespace */
 | |
| int root_mount_path(const char *path, unsigned int flags)
 | |
| {
 | |
|     const char *folder = NULL; /* is a folder enumerated in the root? */
 | |
| #ifdef HAVE_MULTIVOLUME
 | |
|     int volume = path_strip_volume(path, &folder, false);
 | |
|     if (volume == ROOT_VOLUME)
 | |
|         return -EINVAL;
 | |
|     if (!CHECK_VOL(volume))
 | |
|         return -ENOENT;
 | |
|     char volname[VOL_MAX_LEN+2];
 | |
|     make_volume_root(volume, volname);
 | |
| #else
 | |
|     const char *volname = PATH_ROOTSTR;
 | |
|     if (!path_is_absolute(path))
 | |
|     {
 | |
|         logf("Path not absolute %s", path);
 | |
|         return -ENOENT;
 | |
|     }
 | |
|     path_dirname(path, &folder);
 | |
| #endif /* HAVE_MULTIVOLUME */
 | |
|     bool contents = flags & NSITEM_CONTENTS;
 | |
|     int item = IF_MV_VOL(volume);
 | |
|     unsigned int state = get_root_item_state(item);
 | |
|     logf("%s: item:%d, st:%u, %s", __func__, item, state, path);
 | |
|     if (contents && state) /* volume must be mounted to enumerate into the root namespace */
 | |
|     {
 | |
|         if (get_root_item_state(ROOT_CONTENTS_INDEX))
 | |
|             return -EBUSY; /* error something is already enumerated */
 | |
|         /* 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;
 | |
|         int root_state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
 | |
|         set_root_item_state(ROOT_CONTENTS_INDEX, root_state);
 | |
|         flags |= state; /* preserve the state of the mounted volume */
 | |
| 
 | |
|         if (folder)
 | |
|         {
 | |
|             while (*folder == PATH_SEPCH)
 | |
|                 folder++;
 | |
|             /*if a folder has been enumerated don't mark the whole volume */
 | |
|             if (folder[0] != '\0')
 | |
|                 flags &= ~NSITEM_CONTENTS;
 | |
|             else
 | |
|                 folder = NULL; /*Ensure separator is added by path_append */
 | |
|         }
 | |
| 
 | |
|         path_append(root_realpath_internal(), volname, folder, ROOT_MAX_REALPATH);
 | |
|     }
 | |
|     else if (state) /* error volume already mounted */
 | |
|         return -EBUSY;
 | |
|     state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
 | |
|     set_root_item_state(item, state);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* check if volume in path is mounted in the root namespace */
 | |
| bool ns_volume_is_visible(IF_MV_NONVOID(int volume))
 | |
| {
 | |
|     int item = IF_MV_VOL(volume);
 | |
|     if ((item == ROOT_VOLUME) || !CHECK_VOL(item))
 | |
|         return false;
 | |
|     unsigned int state = get_root_item_state(item);
 | |
|     return state && (((state & NSITEM_HIDDEN) == 0) || (state & NSITEM_CONTENTS));
 | |
| }
 | |
| 
 | |
| /* inform root that an entire volume is being unmounted */
 | |
| void root_unmount_volume(IF_MV_NONVOID(int volume))
 | |
| {
 | |
|     logf("%s: vol: %d", __func__, 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);
 | |
|         root_realpath_internal()[0] = '\0';
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* parse the root part of a path */
 | |
| int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
 | |
| {
 | |
|     logf("%s: path: %s", __func__, path);
 | |
|     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))
 | |
|     {
 | |
|         logf("vol: %d is not root", 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 LOGF_ENABLE
 | |
|     if (volume == INT_MAX)
 | |
|         logf("vol: ROOT(%d) %s", volume, *pathp);
 | |
|     else
 | |
|         logf("vol: %d %s", volume, *pathp);
 | |
| #endif
 | |
| #ifdef HAVE_MULTIVOLUME
 | |
|     if (*lenp > MAX_COMPNAME+1)
 | |
|     {
 | |
|         logf("%s: path too long %s", __func__, path);
 | |
|         return -ENAMETOOLONG;
 | |
|     }
 | |
| #endif
 | |
| #ifdef LOGF_ENABLE
 | |
|     if (volume == INT_MAX)
 | |
|         logf("%s: vol: ROOT(%d) path: %s", __func__, volume, path);
 | |
|     else
 | |
|         logf("%s: vol: %d path: %s", __func__, volume, path);
 | |
| #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);
 | |
|     logf("%s: Vol:%d St:%d", __func__, item, state);
 | |
|     if (sysroot)
 | |
|     {
 | |
|         *attrp = ATTR_SYSTEM_ROOT;
 | |
| 
 | |
|         if (state)
 | |
|             *infop = root_bindp->info;
 | |
|         else
 | |
|         {
 | |
|             logf("%s: SysRoot Vol:%d St:%d NOT mounted", __func__, item, state);
 | |
|             *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)
 | |
|         {
 | |
|             logf("%s: DevPath Vol:%d St:%d NOT mounted", __func__, item, state);
 | |
|             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;
 | |
|     logf("%s: item: %d", __func__, 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)
 | |
|         {
 | |
| #if 1 /* hide the volume enumerated into the root namespace */
 | |
|             if (item == ROOT_CONTENTS_INDEX || (state & NSITEM_CONTENTS) == 0)
 | |
|             {
 | |
|                 logf("Found mounted item: %d %s", item, entry->d_name);
 | |
|                 break;
 | |
|             }
 | |
| #endif
 | |
|         }
 | |
| 
 | |
|         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)
 | |
|         {
 | |
|             logf("Found root item: %d %s", item, entry->d_name);
 | |
|             item++;
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         get_mount_point_entry(IF_MV(item,) entry);
 | |
|         item++;
 | |
|         rc = 1;
 | |
|         logf("Found mp item:%d %s", item,  entry->d_name);
 | |
|     }
 | |
| 
 | |
|     scanp->item = item;
 | |
| 
 | |
| file_eod:
 | |
| #ifdef HAVE_DIRCACHE
 | |
|     if (rc == 0)
 | |
|         empty_dirent(entry);
 | |
| #endif
 | |
| file_error:
 | |
|     logf("%s: status: %d", __func__, rc);
 | |
|     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)
 | |
| {
 | |
|     logf("%s: path: %s", __func__, path);
 | |
|     /* 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;
 | |
| }
 |