diff options
Diffstat (limited to 'firmware/common/rb_namespace.c')
-rw-r--r-- | firmware/common/rb_namespace.c | 291 |
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 | |||
30 | static uint8_t root_entry_flags[NUM_VOLUMES+1]; | ||
31 | static struct file_base_binding *root_bindp; | ||
32 | |||
33 | static inline unsigned int get_root_item_state(int item) | ||
34 | { | ||
35 | return root_entry_flags[item]; | ||
36 | } | ||
37 | |||
38 | static inline void set_root_item_state(int item, unsigned int state) | ||
39 | { | ||
40 | root_entry_flags[item] = state; | ||
41 | } | ||
42 | |||
43 | static 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 */ | ||
59 | static 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 */ | ||
75 | int 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 */ | ||
123 | void 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 */ | ||
144 | int 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 */ | ||
171 | int 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 */ | ||
213 | int 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 | |||
252 | file_eod: | ||
253 | #ifdef HAVE_DIRCACHE | ||
254 | if (rc == 0) | ||
255 | empty_dirent(entry); | ||
256 | #endif | ||
257 | file_error: | ||
258 | return rc; | ||
259 | } | ||
260 | |||
261 | /* opens a stream to enumerate items in a namespace container */ | ||
262 | int 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; | ||
289 | file_error: | ||
290 | return rc; | ||
291 | } | ||