summaryrefslogtreecommitdiff
path: root/firmware/common/rb_namespace.c
diff options
context:
space:
mode:
authorWilliam Wilgus <me.theuser@yahoo.com>2017-02-03 17:13:58 -0500
committerWilliam Wilgus <me.theuser@yahoo.com>2020-08-20 23:08:57 +0000
commit5ef28cccf92f5eada6d502fa4b0e16a13e94be5b (patch)
tree05f9d2f8bdf3c0cc54c5893159a7dcf07c7e3e55 /firmware/common/rb_namespace.c
parent31fc46ded69be7438cca2ba2c2b93c1f200165a6 (diff)
downloadrockbox-5ef28cccf92f5eada6d502fa4b0e16a13e94be5b.tar.gz
rockbox-5ef28cccf92f5eada6d502fa4b0e16a13e94be5b.zip
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: I90b5c0a1c949283d3102c16734b0b6ac73901a30
Diffstat (limited to 'firmware/common/rb_namespace.c')
-rw-r--r--firmware/common/rb_namespace.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/firmware/common/rb_namespace.c b/firmware/common/rb_namespace.c
new file mode 100644
index 0000000000..04f92e97af
--- /dev/null
+++ b/firmware/common/rb_namespace.c
@@ -0,0 +1,289 @@
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
26#define ROOT_CONTENTS_INDEX (NUM_VOLUMES)
27#define NUM_ROOT_ITEMS (NUM_VOLUMES+1)
28
29static uint8_t root_entry_flags[NUM_VOLUMES+1];
30static struct file_base_binding *root_bindp;
31
32static inline unsigned int get_root_item_state(int item)
33{
34 return root_entry_flags[item];
35}
36
37static inline void set_root_item_state(int item, unsigned int state)
38{
39 root_entry_flags[item] = state;
40}
41
42static void get_mount_point_entry(IF_MV(int volume,) struct dirent *entry)
43{
44#ifdef HAVE_MULTIVOLUME
45 get_volume_name(volume, entry->d_name);
46#else /* */
47 strcpy(entry->d_name, PATH_ROOTSTR);
48#endif /* HAVE_MULTIVOLUME */
49
50 /* is dirinfo_native */
51 entry->info.attr = ATTR_MOUNT_POINT;
52 entry->info.size = 0;
53 entry->info.wrtdate = 0;
54 entry->info.wrttime = 0;
55}
56
57/* unmount the directory that enumerates into the root namespace */
58static void unmount_item(int item)
59{
60 unsigned int state = get_root_item_state(item);
61 if (!state)
62 return;
63
64 if (state & NSITEM_CONTENTS)
65 {
66 fileobj_unmount(root_bindp);
67 root_bindp = NULL;
68 }
69
70 set_root_item_state(item, 0);
71}
72
73/* mount the directory that enumerates into the root namespace */
74int root_mount_path(const char *path, unsigned int flags)
75{
76#ifdef HAVE_MULTIVOLUME
77 int volume = path_strip_volume(path, NULL, false);
78 if (volume == ROOT_VOLUME)
79 return -EINVAL;
80
81 if (!CHECK_VOL(volume))
82 return -ENOENT;
83#else
84 if (!path_is_absolute(path))
85 return -ENOENT;
86#endif /* HAVE_MULTIVOLUME */
87
88 bool contents = flags & NSITEM_CONTENTS;
89 int item = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
90 unsigned int state = get_root_item_state(item);
91
92 if (state)
93 return -EBUSY;
94
95 if (contents)
96 {
97 /* cache information about the target */
98 struct filestr_base stream;
99 struct path_component_info compinfo;
100
101 int e = errno;
102 int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
103 FF_DEVPATH, &stream, &compinfo);
104 if (rc <= 0)
105 {
106 rc = rc ? -errno : -ENOENT;
107 errno = e;
108 return rc;
109 }
110
111 if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
112 return -EBUSY;
113 }
114
115 state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
116 set_root_item_state(item, state);
117
118 return 0;
119}
120
121/* inform root that an entire volume is being unmounted */
122void root_unmount_volume(IF_MV_NONVOID(int volume))
123{
124 FOR_EACH_VOLUME(volume, item)
125 {
126 #ifdef HAVE_MULTIVOLUME
127 uint32_t state = get_root_item_state(item);
128 if (state && (volume < 0 || item == volume))
129 #endif /* HAVE_MULTIVOLUME */
130 unmount_item(item);
131 }
132
133 /* if the volume unmounted contains the root directory contents then
134 the contents must also be unmounted */
135#ifdef HAVE_MULTIVOLUME
136 uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
137 if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
138#endif
139 unmount_item(ROOT_CONTENTS_INDEX);
140}
141
142/* parse the root part of a path */
143int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
144{
145 int volume = ROOT_VOLUME;
146
147#ifdef HAVE_MULTIVOLUME
148 /* this seamlessly integrates secondary filesystems into the
149 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
150 const char *p;
151 volume = path_strip_volume(path, &p, false);
152 if (volume != ROOT_VOLUME && !CHECK_VOL(volume))
153 return -ENOENT;
154#endif /* HAVE_MULTIVOLUME */
155
156 /* set name to start at last leading separator; name of root will
157 * be returned as "/", volume specifiers as "/<fooN>" */
158 *pathp = GOBBLE_PATH_SEPCH(path) - 1;
159 *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1;
160
161#ifdef HAVE_MULTIVOLUME
162 if (*lenp > MAX_COMPNAME+1)
163 return -ENAMETOOLONG;
164#endif
165
166 return volume;
167}
168
169/* open one of the items in the root */
170int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
171 struct file_base_info *infop, uint16_t *attrp)
172{
173 unsigned int callflags = *callflagsp;
174 bool devpath = !!(callflags & FF_DEVPATH);
175#ifdef HAVE_MULTIVOLUME
176 bool sysroot = volume == ROOT_VOLUME;
177 if (devpath && sysroot)
178 return -ENOENT; /* devpath needs volume spec */
179#else
180 bool sysroot = !devpath; /* always sysroot unless devpath */
181#endif
182
183 int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
184 unsigned int state = get_root_item_state(item);
185
186 if (sysroot)
187 {
188 *attrp = ATTR_SYSTEM_ROOT;
189
190 if (state)
191 *infop = root_bindp->info;
192 else
193 *callflagsp = callflags | FF_NOFS; /* contents not mounted */
194 }
195 else
196 {
197 *attrp = ATTR_MOUNT_POINT;
198
199 if (!devpath && !state)
200 return -ENOENT; /* regular open requires having been mounted */
201
202 if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
203 return -ENOENT; /* not mounted */
204
205 get_rootinfo_internal(infop);
206 }
207
208 return 0;
209}
210
211/* read root directory entries */
212int root_readdir_dirent(struct filestr_base *stream,
213 struct ns_scan_info *scanp, struct dirent *entry)
214{
215 int rc = 0;
216
217 int item = scanp->item;
218
219 /* skip any not-mounted or hidden items */
220 unsigned int state;
221 while (1)
222 {
223 if (item >= NUM_ROOT_ITEMS)
224 goto file_eod;
225
226 state = get_root_item_state(item);
227 if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
228 break;
229
230 item++;
231 }
232
233 if (item == ROOT_CONTENTS_INDEX)
234 {
235 rc = readdir_dirent(stream, &scanp->scan, entry);
236 if (rc < 0)
237 FILE_ERROR(ERRNO, rc * 10 - 1);
238
239 if (rc == 0)
240 item++;
241 }
242 else
243 {
244 get_mount_point_entry(IF_MV(item,) entry);
245 item++;
246 rc = 1;
247 }
248
249 scanp->item = item;
250
251file_eod:
252 if (rc == 0)
253 empty_dirent(entry);
254
255file_error:
256 return rc;
257}
258
259/* opens a stream to enumerate items in a namespace container */
260int ns_open_stream(const char *path, unsigned int callflags,
261 struct filestr_base *stream, struct ns_scan_info *scanp)
262{
263 /* stream still needs synchronization even if we don't have a stream */
264 static struct mutex no_contents_mtx SHAREDBSS_ATTR;
265
266 int rc = open_stream_internal(path, callflags, stream, NULL);
267 if (rc < 0)
268 FILE_ERROR(ERRNO, rc * 10 - 1);
269
270 scanp->item = rc > 1 ? 0 : -1;
271
272 if (stream->flags & FDO_BUSY)
273 {
274 /* root contents are mounted */
275 fat_rewind(&stream->fatstr);
276 }
277 else
278 {
279 /* root contents not mounted */
280 mutex_init(&no_contents_mtx);
281 stream->mtx = &no_contents_mtx;
282 }
283
284 ns_dirscan_rewind(scanp);
285
286 rc = 0;
287file_error:
288 return rc;
289}