From fdc3668a6a4d85fa6dde890ced7e9b850233bc0d Mon Sep 17 00:00:00 2001 From: William Wilgus Date: Thu, 21 Mar 2024 18:27:23 -0400 Subject: [BugFix] Multiboot Database duplicate files When the sd card is mounted into the root namespace the database picks up files through both paths previously we hid the mounted drive but this causes issues with users databases when the drive letter changes Adds a way to keep track of volumes mounted in the root namespace Hides the enumerated volume in root Database: we can just parse the root directory ('/') and get to any mounted volume but we can also enumerate a volume in the root directory when this occurs it leads to multiple entries since the files can be reached through multiple paths ex, /Foo could also be /SD1/Foo Instead we will attempt to rewrite the root with any non-hidden volumes failing that just leave the paths alone Change-Id: I7bdba8cfaf63902d2a3852d28484bcf8ca317ebd --- apps/filetree.c | 85 ++++++++++++++++++++++++--------- apps/filetree.h | 2 + apps/tagcache.c | 47 ++++++++++++++++++ apps/tree.c | 11 +---- firmware/common/dir.c | 6 +++ firmware/common/rb_namespace.c | 74 +++++++++++++++++++++------- firmware/include/dir.h | 6 +++ firmware/include/dircache_redirect.h | 5 +- firmware/include/rb_namespace.h | 3 ++ firmware/target/hosted/filesystem-app.c | 5 ++ uisimulator/common/filesystem-sim.c | 5 ++ 11 files changed, 198 insertions(+), 51 deletions(-) diff --git a/apps/filetree.c b/apps/filetree.c index eb429c83e3..4f59804686 100644 --- a/apps/filetree.c +++ b/apps/filetree.c @@ -46,6 +46,10 @@ #include "strnatcmp.h" #include "keyboard.h" +#ifdef HAVE_MULTIVOLUME +#include "mv.h" +#endif + #if CONFIG_TUNER #include "radio.h" #endif @@ -471,6 +475,45 @@ static void ft_apply_skin_file(char *buf, char *file, const int maxlen) settings_apply_skins(); } +int ft_assemble_path(char *buf, size_t bufsz, const char* currdir, const char* filename) +{ + int len; + if (!filename) + filename = ""; +#ifdef HAVE_MULTIVOLUME + if (currdir && currdir[0] && currdir[1]) /* Not in / */ + { + if (currdir[1] != VOL_START_TOK) + { + len = snprintf(buf, bufsz, "%s%s/%s", root_realpath(), currdir, filename); + } + else + len = snprintf(buf, bufsz, "%s/%s", currdir, filename); + } + else /* In / */ + { + if (filename[0] != VOL_START_TOK) + { + len = snprintf(buf, bufsz, "%s/%s",root_realpath(), filename); + } + else + len = snprintf(buf, bufsz, "/%s", filename); + } +#else + if (currdir && currdir[0] && currdir[1]) /* Not in / */ + { + len = snprintf(buf, bufsz, "%s%s/%s", root_realpath(), currdir, filename); + } + else /* In / */ + { + len = snprintf(buf, bufsz, "%s/%s",root_realpath(), filename); + } +#endif + if ((unsigned) len > bufsz) + splash(HZ, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); + return len; +} + int ft_enter(struct tree_context* c) { int rc = GO_TO_PREVIOUS; @@ -484,16 +527,7 @@ int ft_enter(struct tree_context* c) } int file_attr = file->attr; - int len; - - if (c->currdir[1]) - { - len = snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); - if ((unsigned) len > sizeof(buf)) - splash(HZ, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); - } - else - snprintf(buf,sizeof(buf),"/%s",file->name); + ft_assemble_path(buf, sizeof(buf), c->currdir, file->name); if (file_attr & ATTR_DIRECTORY) { memcpy(c->currdir, buf, sizeof(c->currdir)); @@ -537,22 +571,27 @@ int ft_enter(struct tree_context* c) PLAYLIST_INSERT_LAST, true, true); splash(HZ, ID2P(LANG_QUEUE_LAST)); } - else if (playlist_create(c->currdir, NULL) != -1) + else { - start_index = ft_build_playlist(c, c->selected_item); - if (global_settings.playlist_shuffle) + /* use the assembled path sans filename */ + char * fp = strrchr(buf, PATH_SEPCH); + if (fp) + *fp = '\0'; + if (playlist_create(buf, NULL) != -1) { - start_index = playlist_shuffle(seed, start_index); - - /* when shuffling dir.: play all files - even if the file selected by user is - not the first one */ - if (!global_settings.play_selected) - start_index = 0; + start_index = ft_build_playlist(c, c->selected_item); + if (global_settings.playlist_shuffle) + { + start_index = playlist_shuffle(seed, start_index); + /* when shuffling dir.: play all files + even if the file selected by user is + not the first one */ + if (!global_settings.play_selected) + start_index = 0; + } + playlist_start(start_index, 0, 0); + play = true; } - - playlist_start(start_index, 0, 0); - play = true; } break; } diff --git a/apps/filetree.h b/apps/filetree.h index 7931c3c454..3ec7846d2c 100644 --- a/apps/filetree.h +++ b/apps/filetree.h @@ -25,6 +25,8 @@ int ft_load(struct tree_context* c, const char* tempdir); int ft_enter(struct tree_context* c); int ft_exit(struct tree_context* c); +int ft_assemble_path(char *buf, size_t bufsz, + const char* currdir, const char* filename); int ft_build_playlist(struct tree_context* c, int start_index); bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_warn_and_bookmarks); diff --git a/apps/tagcache.c b/apps/tagcache.c index 40f9c28c33..c8900f5c38 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -4990,10 +4990,56 @@ void do_tagcache_build(const char *path[]) roots_ll[0].path = path[0]; roots_ll[0].next = NULL; + +#if defined HAVE_MULTIVOLUME && !defined(SIMULATOR) && !defined(__PCTOOL__) + extern bool ns_volume_is_visible(int volume); /*rb_namespace.c*/ + /* i is for the path vector, j for the roots_ll array */ + int i = 1, j = 1; + bool added = false; + char volnamebuf[NUM_VOLUMES][VOL_MAX_LEN + 1]; + /* we can just parse the root directory ('/') and get to any mounted + * volume but we can also enumerate a volume in the root directory + * when this occurs it leads to multiple entries since the files can + * be reached through multiple paths ex, /Foo could also be /SD1/Foo + * we used to hide the volume that was mapped but then when you switch + * from the sd to the internal the paths don't map to the right volume + * instead we will attempt to rewrite the root with any non-hidden volumes + * failing that just leave the paths alone */ + if (!strcmp(PATH_ROOTSTR, path[0])) + { + i = 0; + j = 0; + } + /* path can be skipped , but root_ll entries can't */ + for(; path[i] && j < MAX_STATIC_ROOTS; i++) + { + /* check if the link target is inside of an existing search root + * don't add if target is inside, we'll scan it later */ + if (!added && !strcmp(PATH_ROOTSTR, path[i])) + { + for (int v = 0; v < NUM_VOLUMES; v++) + { + if (ns_volume_is_visible(v)) + { + make_volume_root(v, volnamebuf[v]); + roots_ll[j].path = volnamebuf[v]; + if (j > 0) + roots_ll[j-1].next = &roots_ll[j]; + j++; + added = true; + } + } + if(!added) + j = 1; + added = true; + continue; + } +#else /* i is for the path vector, j for the roots_ll array * path can be skipped , but root_ll entries can't */ for(int i = 1, j = 1; path[i] && j < MAX_STATIC_ROOTS; i++) { +#endif /*def HAVE_MULTIVOLUME*/ if (search_root_exists(path[i])) /* skip this path */ continue; @@ -5006,6 +5052,7 @@ void do_tagcache_build(const char *path[]) /* check_dir might add new roots */ for(this = &roots_ll[0]; this; this = this->next) { + logf("Search root %s", this->path); strmemccpy(curpath, this->path, sizeof(curpath)); ret = ret && check_dir(this->path, true); } diff --git a/apps/tree.c b/apps/tree.c index 7563c1ef6e..ea2ef23e71 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -646,7 +646,6 @@ static int dirbrowse(void) { int numentries=0; char buf[MAX_PATH]; - int len; int button; int oldbutton; bool reload_root = false; @@ -857,16 +856,8 @@ static int dirbrowse(void) attr = entry->attr; - if (currdir[1]) /* Not in / */ - { - len = snprintf(buf, sizeof buf, "%s/%s", - currdir, entry->name); + ft_assemble_path(buf, sizeof(buf), currdir, entry->name); - if ((unsigned) len > sizeof(buf)) - splash(HZ, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); - } - else /* In / */ - snprintf(buf, sizeof buf, "/%s", entry->name); } onplay_result = onplay(buf, attr, curr_context, hotkey); } diff --git a/firmware/common/dir.c b/firmware/common/dir.c index 45749b8474..9a78d910a7 100644 --- a/firmware/common/dir.c +++ b/firmware/common/dir.c @@ -344,3 +344,9 @@ struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry) file_error: return (struct dirinfo){ .attribute = 0 }; } + +const char* root_realpath(void) +{ + /* Native only, for APP and SIM see respective filesystem-.c files */ + return root_get_realpath(); /* rb_namespace.c */ +} diff --git a/firmware/common/rb_namespace.c b/firmware/common/rb_namespace.c index ff5ad0f6db..52b2205f40 100644 --- a/firmware/common/rb_namespace.c +++ b/firmware/common/rb_namespace.c @@ -23,6 +23,7 @@ #include "fileobj_mgr.h" #include "rb_namespace.h" #include "file_internal.h" +#include /*snprintf*/ /* Define LOGF_ENABLE to enable logf output in this file */ //#define LOGF_ENABLE @@ -82,39 +83,48 @@ static void unmount_item(int item) 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, NULL, false); + 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 = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume); + 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 (state) - return -EBUSY; - - if (contents) + 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); @@ -124,17 +134,41 @@ int root_mount_path(const char *path, unsigned int flags) 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) + { + folder = ""; + } + else + { + /*if a folder has been enumerated don't mark the whole volume */ + if (folder[0] != '\0' && folder[1] != '\0') + flags &= ~NSITEM_CONTENTS; + } + snprintf(root_realpath_internal(), ROOT_MAX_REALPATH,"%s%s", volname, folder); + } + 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)) { @@ -154,7 +188,10 @@ void root_unmount_volume(IF_MV_NONVOID(int volume)) 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 */ @@ -268,8 +305,13 @@ int root_readdir_dirent(struct filestr_base *stream, state = get_root_item_state(item); if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED) { - logf("Found mounted item: %d %s", item, entry->d_name); - break; +#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++; diff --git a/firmware/include/dir.h b/firmware/include/dir.h index 2f78b11cf5..4599877ede 100644 --- a/firmware/include/dir.h +++ b/firmware/include/dir.h @@ -63,6 +63,9 @@ #ifndef dir_exists #define dir_exists FS_PREFIX(dir_exists) #endif +#ifndef root_realpath +#define root_realpath FS_PREFIX(root_realpath) +#endif #endif /* !DIRFUNCTIONS_DEFINED */ #ifndef DIRENT_DEFINED @@ -83,6 +86,9 @@ struct dirinfo #ifndef DIRFUNCTIONS_DECLARED /* TIP: set errno to zero before calling to see if anything failed */ struct dirinfo dir_get_info(DIR *dirp, struct DIRENT *entry); +const char* root_realpath(void); #endif /* !DIRFUNCTIONS_DECLARED */ + + #endif /* _DIR_H_ */ diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h index f51ce70690..36f68b7251 100644 --- a/firmware/include/dircache_redirect.h +++ b/firmware/include/dircache_redirect.h @@ -139,8 +139,10 @@ static inline void fileop_onsync_internal(struct filestr_base *stream) static inline void volume_onmount_internal(IF_MV_NONVOID(int volume)) { -#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER) +#if (defined(HAVE_MULTIVOLUME) || (defined(HAVE_MULTIBOOT) && !defined(BOOTLOADER))) char path[VOL_MAX_LEN+2]; +#endif +#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER) char rtpath[MAX_PATH / 2]; make_volume_root(volume, path); @@ -183,7 +185,6 @@ standard_redirect: root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS); } #elif defined(HAVE_MULTIVOLUME) - char path[VOL_MAX_LEN+2]; make_volume_root(volume, path); root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0); if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false)) diff --git a/firmware/include/rb_namespace.h b/firmware/include/rb_namespace.h index 7bc711b5a6..5cd8c2dd29 100644 --- a/firmware/include/rb_namespace.h +++ b/firmware/include/rb_namespace.h @@ -37,6 +37,8 @@ struct ns_scan_info }; /* root functions */ +#define ROOT_MAX_REALPATH 80 +const char* root_get_realpath(void); 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, @@ -49,6 +51,7 @@ 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); +bool ns_volume_is_visible(IF_MV_NONVOID(int volume)); /* closes the namespace stream */ static inline int ns_close_stream(struct filestr_base *stream) diff --git a/firmware/target/hosted/filesystem-app.c b/firmware/target/hosted/filesystem-app.c index cfe4e65fe9..09b3365a9e 100644 --- a/firmware/target/hosted/filesystem-app.c +++ b/firmware/target/hosted/filesystem-app.c @@ -600,3 +600,8 @@ int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize) return 0; } + +const char* app_root_realpath(void) +{ + return PATH_ROOTSTR; +} diff --git a/uisimulator/common/filesystem-sim.c b/uisimulator/common/filesystem-sim.c index 0a5df0c742..f4f6321b7d 100644 --- a/uisimulator/common/filesystem-sim.c +++ b/uisimulator/common/filesystem-sim.c @@ -843,3 +843,8 @@ int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize) return sim_get_os_path(buffer, tmpbuf, bufsize); } + +const char* sim_root_realpath(void) +{ + return PATH_ROOTSTR; +} -- cgit v1.2.3