diff options
Diffstat (limited to 'firmware/common/rb_namespace.c')
-rw-r--r-- | firmware/common/rb_namespace.c | 289 |
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 | |||
29 | static uint8_t root_entry_flags[NUM_VOLUMES+1]; | ||
30 | static struct file_base_binding *root_bindp; | ||
31 | |||
32 | static inline unsigned int get_root_item_state(int item) | ||
33 | { | ||
34 | return root_entry_flags[item]; | ||
35 | } | ||
36 | |||
37 | static inline void set_root_item_state(int item, unsigned int state) | ||
38 | { | ||
39 | root_entry_flags[item] = state; | ||
40 | } | ||
41 | |||
42 | static 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 */ | ||
58 | static 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 */ | ||
74 | int 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 */ | ||
122 | void 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 */ | ||
143 | int 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 */ | ||
170 | int 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 */ | ||
212 | int 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 | |||
251 | file_eod: | ||
252 | if (rc == 0) | ||
253 | empty_dirent(entry); | ||
254 | |||
255 | file_error: | ||
256 | return rc; | ||
257 | } | ||
258 | |||
259 | /* opens a stream to enumerate items in a namespace container */ | ||
260 | int 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; | ||
287 | file_error: | ||
288 | return rc; | ||
289 | } | ||