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.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}