summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2024-03-21 18:27:23 -0400
committerWilliam Wilgus <wilgus.william@gmail.com>2024-03-23 01:03:33 -0400
commitfdc3668a6a4d85fa6dde890ced7e9b850233bc0d (patch)
tree5b686359a60acaf753a66f2d09078607b9b11b45
parentae01ea7fd93d4ed3e1a97f9c9b25ca072ebbc431 (diff)
downloadrockbox-fdc3668a6a4d85fa6dde890ced7e9b850233bc0d.tar.gz
rockbox-fdc3668a6a4d85fa6dde890ced7e9b850233bc0d.zip
[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
-rw-r--r--apps/filetree.c85
-rw-r--r--apps/filetree.h2
-rw-r--r--apps/tagcache.c47
-rw-r--r--apps/tree.c11
-rw-r--r--firmware/common/dir.c6
-rw-r--r--firmware/common/rb_namespace.c74
-rw-r--r--firmware/include/dir.h6
-rw-r--r--firmware/include/dircache_redirect.h5
-rw-r--r--firmware/include/rb_namespace.h3
-rw-r--r--firmware/target/hosted/filesystem-app.c5
-rw-r--r--uisimulator/common/filesystem-sim.c5
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 @@
46#include "strnatcmp.h" 46#include "strnatcmp.h"
47#include "keyboard.h" 47#include "keyboard.h"
48 48
49#ifdef HAVE_MULTIVOLUME
50#include "mv.h"
51#endif
52
49#if CONFIG_TUNER 53#if CONFIG_TUNER
50#include "radio.h" 54#include "radio.h"
51#endif 55#endif
@@ -471,6 +475,45 @@ static void ft_apply_skin_file(char *buf, char *file, const int maxlen)
471 settings_apply_skins(); 475 settings_apply_skins();
472} 476}
473 477
478int ft_assemble_path(char *buf, size_t bufsz, const char* currdir, const char* filename)
479{
480 int len;
481 if (!filename)
482 filename = "";
483#ifdef HAVE_MULTIVOLUME
484 if (currdir && currdir[0] && currdir[1]) /* Not in / */
485 {
486 if (currdir[1] != VOL_START_TOK)
487 {
488 len = snprintf(buf, bufsz, "%s%s/%s", root_realpath(), currdir, filename);
489 }
490 else
491 len = snprintf(buf, bufsz, "%s/%s", currdir, filename);
492 }
493 else /* In / */
494 {
495 if (filename[0] != VOL_START_TOK)
496 {
497 len = snprintf(buf, bufsz, "%s/%s",root_realpath(), filename);
498 }
499 else
500 len = snprintf(buf, bufsz, "/%s", filename);
501 }
502#else
503 if (currdir && currdir[0] && currdir[1]) /* Not in / */
504 {
505 len = snprintf(buf, bufsz, "%s%s/%s", root_realpath(), currdir, filename);
506 }
507 else /* In / */
508 {
509 len = snprintf(buf, bufsz, "%s/%s",root_realpath(), filename);
510 }
511#endif
512 if ((unsigned) len > bufsz)
513 splash(HZ, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
514 return len;
515}
516
474int ft_enter(struct tree_context* c) 517int ft_enter(struct tree_context* c)
475{ 518{
476 int rc = GO_TO_PREVIOUS; 519 int rc = GO_TO_PREVIOUS;
@@ -484,16 +527,7 @@ int ft_enter(struct tree_context* c)
484 } 527 }
485 528
486 int file_attr = file->attr; 529 int file_attr = file->attr;
487 int len; 530 ft_assemble_path(buf, sizeof(buf), c->currdir, file->name);
488
489 if (c->currdir[1])
490 {
491 len = snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name);
492 if ((unsigned) len > sizeof(buf))
493 splash(HZ, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
494 }
495 else
496 snprintf(buf,sizeof(buf),"/%s",file->name);
497 531
498 if (file_attr & ATTR_DIRECTORY) { 532 if (file_attr & ATTR_DIRECTORY) {
499 memcpy(c->currdir, buf, sizeof(c->currdir)); 533 memcpy(c->currdir, buf, sizeof(c->currdir));
@@ -537,22 +571,27 @@ int ft_enter(struct tree_context* c)
537 PLAYLIST_INSERT_LAST, true, true); 571 PLAYLIST_INSERT_LAST, true, true);
538 splash(HZ, ID2P(LANG_QUEUE_LAST)); 572 splash(HZ, ID2P(LANG_QUEUE_LAST));
539 } 573 }
540 else if (playlist_create(c->currdir, NULL) != -1) 574 else
541 { 575 {
542 start_index = ft_build_playlist(c, c->selected_item); 576 /* use the assembled path sans filename */
543 if (global_settings.playlist_shuffle) 577 char * fp = strrchr(buf, PATH_SEPCH);
578 if (fp)
579 *fp = '\0';
580 if (playlist_create(buf, NULL) != -1)
544 { 581 {
545 start_index = playlist_shuffle(seed, start_index); 582 start_index = ft_build_playlist(c, c->selected_item);
546 583 if (global_settings.playlist_shuffle)
547 /* when shuffling dir.: play all files 584 {
548 even if the file selected by user is 585 start_index = playlist_shuffle(seed, start_index);
549 not the first one */ 586 /* when shuffling dir.: play all files
550 if (!global_settings.play_selected) 587 even if the file selected by user is
551 start_index = 0; 588 not the first one */
589 if (!global_settings.play_selected)
590 start_index = 0;
591 }
592 playlist_start(start_index, 0, 0);
593 play = true;
552 } 594 }
553
554 playlist_start(start_index, 0, 0);
555 play = true;
556 } 595 }
557 break; 596 break;
558 } 597 }
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 @@
25int ft_load(struct tree_context* c, const char* tempdir); 25int ft_load(struct tree_context* c, const char* tempdir);
26int ft_enter(struct tree_context* c); 26int ft_enter(struct tree_context* c);
27int ft_exit(struct tree_context* c); 27int ft_exit(struct tree_context* c);
28int ft_assemble_path(char *buf, size_t bufsz,
29 const char* currdir, const char* filename);
28int ft_build_playlist(struct tree_context* c, int start_index); 30int ft_build_playlist(struct tree_context* c, int start_index);
29bool ft_play_playlist(char* pathname, char* dirname, 31bool ft_play_playlist(char* pathname, char* dirname,
30 char* filename, bool skip_warn_and_bookmarks); 32 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[])
4990 4990
4991 roots_ll[0].path = path[0]; 4991 roots_ll[0].path = path[0];
4992 roots_ll[0].next = NULL; 4992 roots_ll[0].next = NULL;
4993
4994#if defined HAVE_MULTIVOLUME && !defined(SIMULATOR) && !defined(__PCTOOL__)
4995 extern bool ns_volume_is_visible(int volume); /*rb_namespace.c*/
4996 /* i is for the path vector, j for the roots_ll array */
4997 int i = 1, j = 1;
4998 bool added = false;
4999 char volnamebuf[NUM_VOLUMES][VOL_MAX_LEN + 1];
5000 /* we can just parse the root directory ('/') and get to any mounted
5001 * volume but we can also enumerate a volume in the root directory
5002 * when this occurs it leads to multiple entries since the files can
5003 * be reached through multiple paths ex, /Foo could also be /SD1/Foo
5004 * we used to hide the volume that was mapped but then when you switch
5005 * from the sd to the internal the paths don't map to the right volume
5006 * instead we will attempt to rewrite the root with any non-hidden volumes
5007 * failing that just leave the paths alone */
5008 if (!strcmp(PATH_ROOTSTR, path[0]))
5009 {
5010 i = 0;
5011 j = 0;
5012 }
5013 /* path can be skipped , but root_ll entries can't */
5014 for(; path[i] && j < MAX_STATIC_ROOTS; i++)
5015 {
5016 /* check if the link target is inside of an existing search root
5017 * don't add if target is inside, we'll scan it later */
5018 if (!added && !strcmp(PATH_ROOTSTR, path[i]))
5019 {
5020 for (int v = 0; v < NUM_VOLUMES; v++)
5021 {
5022 if (ns_volume_is_visible(v))
5023 {
5024 make_volume_root(v, volnamebuf[v]);
5025 roots_ll[j].path = volnamebuf[v];
5026 if (j > 0)
5027 roots_ll[j-1].next = &roots_ll[j];
5028 j++;
5029 added = true;
5030 }
5031 }
5032 if(!added)
5033 j = 1;
5034 added = true;
5035 continue;
5036 }
5037#else
4993 /* i is for the path vector, j for the roots_ll array 5038 /* i is for the path vector, j for the roots_ll array
4994 * path can be skipped , but root_ll entries can't */ 5039 * path can be skipped , but root_ll entries can't */
4995 for(int i = 1, j = 1; path[i] && j < MAX_STATIC_ROOTS; i++) 5040 for(int i = 1, j = 1; path[i] && j < MAX_STATIC_ROOTS; i++)
4996 { 5041 {
5042#endif /*def HAVE_MULTIVOLUME*/
4997 if (search_root_exists(path[i])) /* skip this path */ 5043 if (search_root_exists(path[i])) /* skip this path */
4998 continue; 5044 continue;
4999 5045
@@ -5006,6 +5052,7 @@ void do_tagcache_build(const char *path[])
5006 /* check_dir might add new roots */ 5052 /* check_dir might add new roots */
5007 for(this = &roots_ll[0]; this; this = this->next) 5053 for(this = &roots_ll[0]; this; this = this->next)
5008 { 5054 {
5055 logf("Search root %s", this->path);
5009 strmemccpy(curpath, this->path, sizeof(curpath)); 5056 strmemccpy(curpath, this->path, sizeof(curpath));
5010 ret = ret && check_dir(this->path, true); 5057 ret = ret && check_dir(this->path, true);
5011 } 5058 }
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)
646{ 646{
647 int numentries=0; 647 int numentries=0;
648 char buf[MAX_PATH]; 648 char buf[MAX_PATH];
649 int len;
650 int button; 649 int button;
651 int oldbutton; 650 int oldbutton;
652 bool reload_root = false; 651 bool reload_root = false;
@@ -857,16 +856,8 @@ static int dirbrowse(void)
857 856
858 attr = entry->attr; 857 attr = entry->attr;
859 858
860 if (currdir[1]) /* Not in / */ 859 ft_assemble_path(buf, sizeof(buf), currdir, entry->name);
861 {
862 len = snprintf(buf, sizeof buf, "%s/%s",
863 currdir, entry->name);
864 860
865 if ((unsigned) len > sizeof(buf))
866 splash(HZ, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
867 }
868 else /* In / */
869 snprintf(buf, sizeof buf, "/%s", entry->name);
870 } 861 }
871 onplay_result = onplay(buf, attr, curr_context, hotkey); 862 onplay_result = onplay(buf, attr, curr_context, hotkey);
872 } 863 }
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)
344file_error: 344file_error:
345 return (struct dirinfo){ .attribute = 0 }; 345 return (struct dirinfo){ .attribute = 0 };
346} 346}
347
348const char* root_realpath(void)
349{
350 /* Native only, for APP and SIM see respective filesystem-.c files */
351 return root_get_realpath(); /* rb_namespace.c */
352}
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 @@
23#include "fileobj_mgr.h" 23#include "fileobj_mgr.h"
24#include "rb_namespace.h" 24#include "rb_namespace.h"
25#include "file_internal.h" 25#include "file_internal.h"
26#include <stdio.h> /*snprintf*/
26 27
27/* Define LOGF_ENABLE to enable logf output in this file */ 28/* Define LOGF_ENABLE to enable logf output in this file */
28//#define LOGF_ENABLE 29//#define LOGF_ENABLE
@@ -82,39 +83,48 @@ static void unmount_item(int item)
82 set_root_item_state(item, 0); 83 set_root_item_state(item, 0);
83} 84}
84 85
86static char *root_realpath_internal(void)
87{
88 static char root_realpath[ROOT_MAX_REALPATH];
89 return root_realpath;
90}
91const char* root_get_realpath(void)
92{
93 return root_realpath_internal();
94}
95
85/* mount the directory that enumerates into the root namespace */ 96/* mount the directory that enumerates into the root namespace */
86int root_mount_path(const char *path, unsigned int flags) 97int root_mount_path(const char *path, unsigned int flags)
87{ 98{
99 const char *folder = NULL; /* is a folder enumerated in the root? */
88#ifdef HAVE_MULTIVOLUME 100#ifdef HAVE_MULTIVOLUME
89 int volume = path_strip_volume(path, NULL, false); 101 int volume = path_strip_volume(path, &folder, false);
90 if (volume == ROOT_VOLUME) 102 if (volume == ROOT_VOLUME)
91 return -EINVAL; 103 return -EINVAL;
92
93 if (!CHECK_VOL(volume)) 104 if (!CHECK_VOL(volume))
94 return -ENOENT; 105 return -ENOENT;
106 char volname[VOL_MAX_LEN+2];
107 make_volume_root(volume, volname);
95#else 108#else
109 const char volname = PATH_ROOTSTR;
96 if (!path_is_absolute(path)) 110 if (!path_is_absolute(path))
97 { 111 {
98 logf("Path not absolute %s", path); 112 logf("Path not absolute %s", path);
99 return -ENOENT; 113 return -ENOENT;
100 } 114 }
115 path_dirname(path, &folder);
101#endif /* HAVE_MULTIVOLUME */ 116#endif /* HAVE_MULTIVOLUME */
102
103 bool contents = flags & NSITEM_CONTENTS; 117 bool contents = flags & NSITEM_CONTENTS;
104 int item = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume); 118 int item = IF_MV_VOL(volume);
105 unsigned int state = get_root_item_state(item); 119 unsigned int state = get_root_item_state(item);
106
107 logf("%s: item:%d, st:%u, %s", __func__, item, state, path); 120 logf("%s: item:%d, st:%u, %s", __func__, item, state, path);
108 121 if (contents && state) /* volume must be mounted to enumerate into the root namespace */
109 if (state)
110 return -EBUSY;
111
112 if (contents)
113 { 122 {
123 if (get_root_item_state(ROOT_CONTENTS_INDEX))
124 return -EBUSY; /* error something is already enumerated */
114 /* cache information about the target */ 125 /* cache information about the target */
115 struct filestr_base stream; 126 struct filestr_base stream;
116 struct path_component_info compinfo; 127 struct path_component_info compinfo;
117
118 int e = errno; 128 int e = errno;
119 int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO | 129 int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
120 FF_DEVPATH, &stream, &compinfo); 130 FF_DEVPATH, &stream, &compinfo);
@@ -124,17 +134,41 @@ int root_mount_path(const char *path, unsigned int flags)
124 errno = e; 134 errno = e;
125 return rc; 135 return rc;
126 } 136 }
127
128 if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp)) 137 if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
129 return -EBUSY; 138 return -EBUSY;
130 } 139 int root_state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
140 set_root_item_state(ROOT_CONTENTS_INDEX, root_state);
141 flags |= state; /* preserve the state of the mounted volume */
142 if (!folder)
143 {
144 folder = "";
145 }
146 else
147 {
148 /*if a folder has been enumerated don't mark the whole volume */
149 if (folder[0] != '\0' && folder[1] != '\0')
150 flags &= ~NSITEM_CONTENTS;
131 151
152 }
153 snprintf(root_realpath_internal(), ROOT_MAX_REALPATH,"%s%s", volname, folder);
154 }
155 else if (state) /* error volume already mounted */
156 return -EBUSY;
132 state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS)); 157 state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
133 set_root_item_state(item, state); 158 set_root_item_state(item, state);
134
135 return 0; 159 return 0;
136} 160}
137 161
162/* check if volume in path is mounted in the root namespace */
163bool ns_volume_is_visible(IF_MV_NONVOID(int volume))
164{
165 int item = IF_MV_VOL(volume);
166 if ((item == ROOT_VOLUME) || !CHECK_VOL(item))
167 return false;
168 unsigned int state = get_root_item_state(item);
169 return state && (((state & NSITEM_HIDDEN) == 0) || (state & NSITEM_CONTENTS));
170}
171
138/* inform root that an entire volume is being unmounted */ 172/* inform root that an entire volume is being unmounted */
139void root_unmount_volume(IF_MV_NONVOID(int volume)) 173void root_unmount_volume(IF_MV_NONVOID(int volume))
140{ 174{
@@ -154,7 +188,10 @@ void root_unmount_volume(IF_MV_NONVOID(int volume))
154 uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX); 188 uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
155 if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume)) 189 if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
156#endif 190#endif
191 {
157 unmount_item(ROOT_CONTENTS_INDEX); 192 unmount_item(ROOT_CONTENTS_INDEX);
193 root_realpath_internal()[0] = '\0';
194 }
158} 195}
159 196
160/* parse the root part of a path */ 197/* parse the root part of a path */
@@ -268,8 +305,13 @@ int root_readdir_dirent(struct filestr_base *stream,
268 state = get_root_item_state(item); 305 state = get_root_item_state(item);
269 if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED) 306 if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
270 { 307 {
271 logf("Found mounted item: %d %s", item, entry->d_name); 308#if 1 /* hide the volume enumerated into the root namespace */
272 break; 309 if (item == ROOT_CONTENTS_INDEX || (state & NSITEM_CONTENTS) == 0)
310 {
311 logf("Found mounted item: %d %s", item, entry->d_name);
312 break;
313 }
314#endif
273 } 315 }
274 316
275 item++; 317 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 @@
63#ifndef dir_exists 63#ifndef dir_exists
64#define dir_exists FS_PREFIX(dir_exists) 64#define dir_exists FS_PREFIX(dir_exists)
65#endif 65#endif
66#ifndef root_realpath
67#define root_realpath FS_PREFIX(root_realpath)
68#endif
66#endif /* !DIRFUNCTIONS_DEFINED */ 69#endif /* !DIRFUNCTIONS_DEFINED */
67 70
68#ifndef DIRENT_DEFINED 71#ifndef DIRENT_DEFINED
@@ -83,6 +86,9 @@ struct dirinfo
83#ifndef DIRFUNCTIONS_DECLARED 86#ifndef DIRFUNCTIONS_DECLARED
84/* TIP: set errno to zero before calling to see if anything failed */ 87/* TIP: set errno to zero before calling to see if anything failed */
85struct dirinfo dir_get_info(DIR *dirp, struct DIRENT *entry); 88struct dirinfo dir_get_info(DIR *dirp, struct DIRENT *entry);
89const char* root_realpath(void);
86#endif /* !DIRFUNCTIONS_DECLARED */ 90#endif /* !DIRFUNCTIONS_DECLARED */
87 91
92
93
88#endif /* _DIR_H_ */ 94#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)
139 139
140static inline void volume_onmount_internal(IF_MV_NONVOID(int volume)) 140static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
141{ 141{
142#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER) 142#if (defined(HAVE_MULTIVOLUME) || (defined(HAVE_MULTIBOOT) && !defined(BOOTLOADER)))
143 char path[VOL_MAX_LEN+2]; 143 char path[VOL_MAX_LEN+2];
144#endif
145#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER)
144 char rtpath[MAX_PATH / 2]; 146 char rtpath[MAX_PATH / 2];
145 make_volume_root(volume, path); 147 make_volume_root(volume, path);
146 148
@@ -183,7 +185,6 @@ standard_redirect:
183 root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS); 185 root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
184 } 186 }
185#elif defined(HAVE_MULTIVOLUME) 187#elif defined(HAVE_MULTIVOLUME)
186 char path[VOL_MAX_LEN+2];
187 make_volume_root(volume, path); 188 make_volume_root(volume, path);
188 root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0); 189 root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
189 if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false)) 190 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
37}; 37};
38 38
39/* root functions */ 39/* root functions */
40#define ROOT_MAX_REALPATH 80
41const char* root_get_realpath(void);
40int root_mount_path(const char *path, unsigned int flags); 42int root_mount_path(const char *path, unsigned int flags);
41void root_unmount_volume(IF_MV_NONVOID(int volume)); 43void root_unmount_volume(IF_MV_NONVOID(int volume));
42int root_readdir_dirent(struct filestr_base *stream, 44int root_readdir_dirent(struct filestr_base *stream,
@@ -49,6 +51,7 @@ int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
49 struct file_base_info *infop, uint16_t *attrp); 51 struct file_base_info *infop, uint16_t *attrp);
50int ns_open_stream(const char *path, unsigned int callflags, 52int ns_open_stream(const char *path, unsigned int callflags,
51 struct filestr_base *stream, struct ns_scan_info *scanp); 53 struct filestr_base *stream, struct ns_scan_info *scanp);
54bool ns_volume_is_visible(IF_MV_NONVOID(int volume));
52 55
53/* closes the namespace stream */ 56/* closes the namespace stream */
54static inline int ns_close_stream(struct filestr_base *stream) 57static 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)
600 600
601 return 0; 601 return 0;
602} 602}
603
604const char* app_root_realpath(void)
605{
606 return PATH_ROOTSTR;
607}
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)
843 843
844 return sim_get_os_path(buffer, tmpbuf, bufsize); 844 return sim_get_os_path(buffer, tmpbuf, bufsize);
845} 845}
846
847const char* sim_root_realpath(void)
848{
849 return PATH_ROOTSTR;
850}