summaryrefslogtreecommitdiff
path: root/firmware/common/rb_namespace.c
diff options
context:
space:
mode:
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}