summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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}