summaryrefslogtreecommitdiff
path: root/firmware/common/rb_namespace.c
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2022-03-03 07:37:03 -0500
committerWilliam Wilgus <wilgus.william@gmail.com>2022-03-03 18:58:07 -0500
commit9daacabd658508d2607a64b288c9bce7a635fb15 (patch)
treece96538ea82a4176f00f8eb9531711db7dea5750 /firmware/common/rb_namespace.c
parentf88ea12bacf381ad4f39ba2328c806e772c0dda8 (diff)
downloadrockbox-9daacabd658508d2607a64b288c9bce7a635fb15.tar.gz
rockbox-9daacabd658508d2607a64b288c9bce7a635fb15.zip
[RESTORED!] Allow mounting of any directory as the root directory.
Provide definitions for the macros: * RB_ROOT_VOL_HIDDEN(v) to exclude certain items from the root. * RB_ROOT_CONTENTS to return a string with the name of the directory to mount in the root. Defaults are in export/rbpaths.h It's a bit much for those that don't need the full functionality. Some conditional define can cut it back a lot to cut out things only needed if alternate root mounts are required. I'm just not bothering yet. The basic concept would be applied to all targets to keep file code from forking too much. Change-Id: I3b5a14c530ff4b10d97f67636237d96875eb8969 Author: Michael Sevakis
Diffstat (limited to 'firmware/common/rb_namespace.c')
-rw-r--r--firmware/common/rb_namespace.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/firmware/common/rb_namespace.c b/firmware/common/rb_namespace.c
new file mode 100644
index 0000000000..beaaf78bed
--- /dev/null
+++ b/firmware/common/rb_namespace.c
@@ -0,0 +1,291 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2017 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include <errno.h>
23#include "fileobj_mgr.h"
24#include "rb_namespace.h"
25#include "file_internal.h"
26
27#define ROOT_CONTENTS_INDEX (NUM_VOLUMES)
28#define NUM_ROOT_ITEMS (NUM_VOLUMES+1)
29
30static uint8_t root_entry_flags[NUM_VOLUMES+1];
31static struct file_base_binding *root_bindp;
32
33static inline unsigned int get_root_item_state(int item)
34{
35 return root_entry_flags[item];
36}
37
38static inline void set_root_item_state(int item, unsigned int state)
39{
40 root_entry_flags[item] = state;
41}
42
43static void get_mount_point_entry(IF_MV(int volume,) struct DIRENT *entry)
44{
45#ifdef HAVE_MULTIVOLUME
46 get_volume_name(volume, entry->d_name);
47#else /* */
48 strcpy(entry->d_name, PATH_ROOTSTR);
49#endif /* HAVE_MULTIVOLUME */
50#if defined(_FILESYSTEM_NATIVE_H_)
51 entry->info.attr = ATTR_MOUNT_POINT;
52 entry->info.size = 0;
53 entry->info.wrtdate = 0;
54 entry->info.wrttime = 0;
55#endif /* is dirinfo_native */
56}
57
58/* unmount the directory that enumerates into the root namespace */
59static void unmount_item(int item)
60{
61 unsigned int state = get_root_item_state(item);
62 if (!state)
63 return;
64
65 if (state & NSITEM_CONTENTS)
66 {
67 fileobj_unmount(root_bindp);
68 root_bindp = NULL;
69 }
70
71 set_root_item_state(item, 0);
72}
73
74/* mount the directory that enumerates into the root namespace */
75int root_mount_path(const char *path, unsigned int flags)
76{
77#ifdef HAVE_MULTIVOLUME
78 int volume = path_strip_volume(path, NULL, false);
79 if (volume == ROOT_VOLUME)
80 return -EINVAL;
81
82 if (!CHECK_VOL(volume))
83 return -ENOENT;
84#else
85 if (!path_is_absolute(path))
86 return -ENOENT;
87#endif /* HAVE_MULTIVOLUME */
88
89 bool contents = flags & NSITEM_CONTENTS;
90 int item = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
91 unsigned int state = get_root_item_state(item);
92
93 if (state)
94 return -EBUSY;
95
96 if (contents)
97 {
98 /* cache information about the target */
99 struct filestr_base stream;
100 struct path_component_info compinfo;
101
102 int e = errno;
103 int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
104 FF_DEVPATH, &stream, &compinfo);
105 if (rc <= 0)
106 {
107 rc = rc ? -errno : -ENOENT;
108 errno = e;
109 return rc;
110 }
111
112 if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
113 return -EBUSY;
114 }
115
116 state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
117 set_root_item_state(item, state);
118
119 return 0;
120}
121
122/* inform root that an entire volume is being unmounted */
123void root_unmount_volume(IF_MV_NONVOID(int volume))
124{
125 FOR_EACH_VOLUME(volume, item)
126 {
127 #ifdef HAVE_MULTIVOLUME
128 uint32_t state = get_root_item_state(item);
129 if (state && (volume < 0 || item == volume))
130 #endif /* HAVE_MULTIVOLUME */
131 unmount_item(item);
132 }
133
134 /* if the volume unmounted contains the root directory contents then
135 the contents must also be unmounted */
136#ifdef HAVE_MULTIVOLUME
137 uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
138 if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
139#endif
140 unmount_item(ROOT_CONTENTS_INDEX);
141}
142
143/* parse the root part of a path */
144int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
145{
146 int volume = ROOT_VOLUME;
147
148#ifdef HAVE_MULTIVOLUME
149 /* this seamlessly integrates secondary filesystems into the
150 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
151 const char *p;
152 volume = path_strip_volume(path, &p, false);
153 if (volume != ROOT_VOLUME && !CHECK_VOL(volume))
154 return -ENOENT;
155#endif /* HAVE_MULTIVOLUME */
156
157 /* set name to start at last leading separator; name of root will
158 * be returned as "/", volume specifiers as "/<fooN>" */
159 *pathp = GOBBLE_PATH_SEPCH(path) - 1;
160 *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1;
161
162#ifdef HAVE_MULTIVOLUME
163 if (*lenp > MAX_COMPNAME+1)
164 return -ENAMETOOLONG;
165#endif
166
167 return volume;
168}
169
170/* open one of the items in the root */
171int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
172 struct file_base_info *infop, uint16_t *attrp)
173{
174 unsigned int callflags = *callflagsp;
175 bool devpath = !!(callflags & FF_DEVPATH);
176#ifdef HAVE_MULTIVOLUME
177 bool sysroot = volume == ROOT_VOLUME;
178 if (devpath && sysroot)
179 return -ENOENT; /* devpath needs volume spec */
180#else
181 bool sysroot = !devpath; /* always sysroot unless devpath */
182#endif
183
184 int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
185 unsigned int state = get_root_item_state(item);
186
187 if (sysroot)
188 {
189 *attrp = ATTR_SYSTEM_ROOT;
190
191 if (state)
192 *infop = root_bindp->info;
193 else
194 *callflagsp = callflags | FF_NOFS; /* contents not mounted */
195 }
196 else
197 {
198 *attrp = ATTR_MOUNT_POINT;
199
200 if (!devpath && !state)
201 return -ENOENT; /* regular open requires having been mounted */
202#if CONFIG_PLATFORM & PLATFORM_NATIVE
203 if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
204 return -ENOENT; /* not mounted */
205#endif
206 get_rootinfo_internal(infop);
207 }
208
209 return 0;
210}
211
212/* read root directory entries */
213int root_readdir_dirent(struct filestr_base *stream,
214 struct ns_scan_info *scanp, struct DIRENT *entry)
215{
216 int rc = 0;
217
218 int item = scanp->item;
219
220 /* skip any not-mounted or hidden items */
221 unsigned int state;
222 while (1)
223 {
224 if (item >= NUM_ROOT_ITEMS)
225 goto file_eod;
226
227 state = get_root_item_state(item);
228 if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
229 break;
230
231 item++;
232 }
233
234 if (item == ROOT_CONTENTS_INDEX)
235 {
236 rc = readdir_dirent(stream, &scanp->scan, entry);
237 if (rc < 0)
238 FILE_ERROR(ERRNO, rc * 10 - 1);
239
240 if (rc == 0)
241 item++;
242 }
243 else
244 {
245 get_mount_point_entry(IF_MV(item,) entry);
246 item++;
247 rc = 1;
248 }
249
250 scanp->item = item;
251
252file_eod:
253#ifdef HAVE_DIRCACHE
254 if (rc == 0)
255 empty_dirent(entry);
256#endif
257file_error:
258 return rc;
259}
260
261/* opens a stream to enumerate items in a namespace container */
262int ns_open_stream(const char *path, unsigned int callflags,
263 struct filestr_base *stream, struct ns_scan_info *scanp)
264{
265 /* stream still needs synchronization even if we don't have a stream */
266 static struct mutex no_contents_mtx SHAREDBSS_ATTR;
267
268 int rc = open_stream_internal(path, callflags, stream, NULL);
269 if (rc < 0)
270 FILE_ERROR(ERRNO, rc * 10 - 1);
271
272 scanp->item = rc > 1 ? 0 : -1;
273
274 if (stream->flags & FDO_BUSY)
275 {
276 /* root contents are mounted */
277 fat_rewind(&stream->fatstr);
278 }
279 else
280 {
281 /* root contents not mounted */
282 mutex_init(&no_contents_mtx);
283 stream->mtx = &no_contents_mtx;
284 }
285
286 ns_dirscan_rewind(scanp);
287
288 rc = 0;
289file_error:
290 return rc;
291}