diff options
Diffstat (limited to 'firmware/common')
-rw-r--r-- | firmware/common/dir.c | 93 | ||||
-rw-r--r-- | firmware/common/dircache.c | 13 | ||||
-rw-r--r-- | firmware/common/disk.c | 2 | ||||
-rw-r--r-- | firmware/common/file.c | 2 | ||||
-rw-r--r-- | firmware/common/file_internal.c | 100 | ||||
-rw-r--r-- | firmware/common/fileobj_mgr.c | 103 | ||||
-rw-r--r-- | firmware/common/pathfuncs.c | 39 | ||||
-rw-r--r-- | firmware/common/rb_namespace.c | 291 |
8 files changed, 444 insertions, 199 deletions
diff --git a/firmware/common/dir.c b/firmware/common/dir.c index 245947b134..45749b8474 100644 --- a/firmware/common/dir.c +++ b/firmware/common/dir.c | |||
@@ -28,17 +28,14 @@ | |||
28 | #include "pathfuncs.h" | 28 | #include "pathfuncs.h" |
29 | #include "timefuncs.h" | 29 | #include "timefuncs.h" |
30 | #include "fileobj_mgr.h" | 30 | #include "fileobj_mgr.h" |
31 | #include "dircache_redirect.h" | 31 | #include "rb_namespace.h" |
32 | 32 | ||
33 | /* structure used for open directory streams */ | 33 | /* structure used for open directory streams */ |
34 | static struct dirstr_desc | 34 | static struct dirstr_desc |
35 | { | 35 | { |
36 | struct filestr_base stream; /* basic stream info (first!) */ | 36 | struct filestr_base stream; /* basic stream info (first!) */ |
37 | struct dirscan_info scan; /* directory scan cursor */ | 37 | struct ns_scan_info scan; /* directory scan cursor */ |
38 | struct dirent entry; /* current parsed entry information */ | 38 | struct dirent entry; /* current parsed entry information */ |
39 | #ifdef HAVE_MULTIVOLUME | ||
40 | int volumecounter; /* counter for root volume entries */ | ||
41 | #endif | ||
42 | } open_streams[MAX_OPEN_DIRS]; | 39 | } open_streams[MAX_OPEN_DIRS]; |
43 | 40 | ||
44 | /* check and return a struct dirstr_desc* from a DIR* */ | 41 | /* check and return a struct dirstr_desc* from a DIR* */ |
@@ -48,7 +45,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp) | |||
48 | 45 | ||
49 | if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS)) | 46 | if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS)) |
50 | dir = NULL; | 47 | dir = NULL; |
51 | else if (dir->stream.flags & FDO_BUSY) | 48 | else if (dir->stream.flags & (FDO_BUSY|FD_VALID)) |
52 | return dir; | 49 | return dir; |
53 | 50 | ||
54 | int errnum; | 51 | int errnum; |
@@ -105,50 +102,6 @@ static struct dirstr_desc * alloc_dirstr(void) | |||
105 | return NULL; | 102 | return NULL; |
106 | } | 103 | } |
107 | 104 | ||
108 | #ifdef HAVE_MULTIVOLUME | ||
109 | static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry) | ||
110 | { | ||
111 | /* Volumes (secondary file systems) get inserted into the system root | ||
112 | * directory. If the path specified volume 0, enumeration will not | ||
113 | * include other volumes, but just its own files and directories. | ||
114 | * | ||
115 | * Fake special directories, which don't really exist, that will get | ||
116 | * redirected upon opendir() | ||
117 | */ | ||
118 | while (++dir->volumecounter < NUM_VOLUMES) | ||
119 | { | ||
120 | /* on the system root */ | ||
121 | if (!fat_ismounted(dir->volumecounter)) | ||
122 | continue; | ||
123 | |||
124 | get_volume_name(dir->volumecounter, entry->d_name); | ||
125 | dir->entry.info.attr = ATTR_MOUNT_POINT; | ||
126 | dir->entry.info.size = 0; | ||
127 | dir->entry.info.wrtdate = 0; | ||
128 | dir->entry.info.wrttime = 0; | ||
129 | return 1; | ||
130 | } | ||
131 | |||
132 | /* do normal directory entry fetching */ | ||
133 | return 0; | ||
134 | } | ||
135 | #endif /* HAVE_MULTIVOLUME */ | ||
136 | |||
137 | static inline int readdir_volume(struct dirstr_desc *dir, | ||
138 | struct dirent *entry) | ||
139 | { | ||
140 | #ifdef HAVE_MULTIVOLUME | ||
141 | /* fetch virtual volume entries? */ | ||
142 | if (dir->volumecounter < NUM_VOLUMES) | ||
143 | return readdir_volume_inner(dir, entry); | ||
144 | #endif /* HAVE_MULTIVOLUME */ | ||
145 | |||
146 | /* do normal directory entry fetching */ | ||
147 | return 0; | ||
148 | (void)dir; (void)entry; | ||
149 | } | ||
150 | |||
151 | |||
152 | /** POSIX interface **/ | 105 | /** POSIX interface **/ |
153 | 106 | ||
154 | /* open a directory */ | 107 | /* open a directory */ |
@@ -166,21 +119,13 @@ DIR * opendir(const char *dirname) | |||
166 | if (!dir) | 119 | if (!dir) |
167 | FILE_ERROR(EMFILE, RC); | 120 | FILE_ERROR(EMFILE, RC); |
168 | 121 | ||
169 | rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL); | 122 | rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan); |
170 | if (rc < 0) | 123 | if (rc < 0) |
171 | { | 124 | { |
172 | DEBUGF("Open failed: %d\n", rc); | 125 | DEBUGF("Open failed: %d\n", rc); |
173 | FILE_ERROR(ERRNO, RC); | 126 | FILE_ERROR(ERRNO, RC); |
174 | } | 127 | } |
175 | 128 | ||
176 | #ifdef HAVE_MULTIVOLUME | ||
177 | /* volume counter is relevant only to the system root */ | ||
178 | dir->volumecounter = rc > 1 ? 0 : INT_MAX; | ||
179 | #endif /* HAVE_MULTIVOLUME */ | ||
180 | |||
181 | fat_rewind(&dir->stream.fatstr); | ||
182 | rewinddir_dirent(&dir->scan); | ||
183 | |||
184 | dirp = (DIR *)dir; | 129 | dirp = (DIR *)dir; |
185 | file_error: | 130 | file_error: |
186 | file_internal_unlock_WRITER(); | 131 | file_internal_unlock_WRITER(); |
@@ -205,7 +150,7 @@ int closedir(DIR *dirp) | |||
205 | FILE_ERROR(EBADF, -2); | 150 | FILE_ERROR(EBADF, -2); |
206 | } | 151 | } |
207 | 152 | ||
208 | rc = close_stream_internal(&dir->stream); | 153 | rc = ns_close_stream(&dir->stream); |
209 | if (rc < 0) | 154 | if (rc < 0) |
210 | FILE_ERROR(ERRNO, rc * 10 - 3); | 155 | FILE_ERROR(ERRNO, rc * 10 - 3); |
211 | 156 | ||
@@ -223,16 +168,11 @@ struct dirent * readdir(DIR *dirp) | |||
223 | 168 | ||
224 | struct dirent *res = NULL; | 169 | struct dirent *res = NULL; |
225 | 170 | ||
226 | int rc = readdir_volume(dir, &dir->entry); | 171 | int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &dir->entry); |
227 | if (rc == 0) | ||
228 | { | ||
229 | rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry); | ||
230 | if (rc < 0) | ||
231 | FILE_ERROR(EIO, RC); | ||
232 | } | ||
233 | |||
234 | if (rc > 0) | 172 | if (rc > 0) |
235 | res = &dir->entry; | 173 | res = &dir->entry; |
174 | else if (rc < 0) | ||
175 | FILE_ERROR(EIO, RC); | ||
236 | 176 | ||
237 | file_error: | 177 | file_error: |
238 | RELEASE_DIRSTR(READER, dir); | 178 | RELEASE_DIRSTR(READER, dir); |
@@ -259,13 +199,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) | |||
259 | if (!dir) | 199 | if (!dir) |
260 | FILE_ERROR_RETURN(ERRNO, -1); | 200 | FILE_ERROR_RETURN(ERRNO, -1); |
261 | 201 | ||
262 | int rc = readdir_volume(dir, entry); | 202 | int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry); |
263 | if (rc == 0) | 203 | if (rc < 0) |
264 | { | 204 | FILE_ERROR(EIO, rc * 10 - 4); |
265 | rc = readdir_dirent(&dir->stream, &dir->scan, entry); | ||
266 | if (rc < 0) | ||
267 | FILE_ERROR(EIO, rc * 10 - 4); | ||
268 | } | ||
269 | 205 | ||
270 | file_error: | 206 | file_error: |
271 | RELEASE_DIRSTR(READER, dir); | 207 | RELEASE_DIRSTR(READER, dir); |
@@ -289,12 +225,7 @@ void rewinddir(DIR *dirp) | |||
289 | if (!dir) | 225 | if (!dir) |
290 | FILE_ERROR_RETURN(ERRNO); | 226 | FILE_ERROR_RETURN(ERRNO); |
291 | 227 | ||
292 | rewinddir_dirent(&dir->scan); | 228 | ns_dirscan_rewind(&dir->scan); |
293 | |||
294 | #ifdef HAVE_MULTIVOLUME | ||
295 | if (dir->volumecounter != INT_MAX) | ||
296 | dir->volumecounter = 0; | ||
297 | #endif /* HAVE_MULTIVOLUME */ | ||
298 | 229 | ||
299 | RELEASE_DIRSTR(READER, dir); | 230 | RELEASE_DIRSTR(READER, dir); |
300 | } | 231 | } |
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index 3b880d3382..7a84b761a0 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c | |||
@@ -1473,7 +1473,7 @@ static void sab_process_volume(struct dircache_volume *dcvolp) | |||
1473 | */ | 1473 | */ |
1474 | int dircache_readdir_dirent(struct filestr_base *stream, | 1474 | int dircache_readdir_dirent(struct filestr_base *stream, |
1475 | struct dirscan_info *scanp, | 1475 | struct dirscan_info *scanp, |
1476 | struct dirent *entry) | 1476 | struct DIRENT *entry) |
1477 | { | 1477 | { |
1478 | struct file_base_info *dirinfop = stream->infop; | 1478 | struct file_base_info *dirinfop = stream->infop; |
1479 | 1479 | ||
@@ -1760,7 +1760,7 @@ static int sab_process_volume(IF_MV(int volume,) struct dircache_entry *ce) | |||
1760 | return sab_process_dir(ce); | 1760 | return sab_process_dir(ce); |
1761 | } | 1761 | } |
1762 | 1762 | ||
1763 | int dircache_readdir_r(struct dircache_dirscan *dir, struct dirent *result) | 1763 | int dircache_readdir_r(struct dircache_dirscan *dir, struct DIRENT *result) |
1764 | { | 1764 | { |
1765 | if (dircache_state != DIRCACHE_READY) | 1765 | if (dircache_state != DIRCACHE_READY) |
1766 | return readdir_r(dir->###########3, result, &result); | 1766 | return readdir_r(dir->###########3, result, &result); |
@@ -2541,13 +2541,10 @@ static ssize_t get_path_sub(int idx, struct get_path_sub_data *data) | |||
2541 | cename = ""; | 2541 | cename = ""; |
2542 | 2542 | ||
2543 | #ifdef HAVE_MULTIVOLUME | 2543 | #ifdef HAVE_MULTIVOLUME |
2544 | /* prepend the volume specifier */ | ||
2544 | int volume = IF_MV_VOL(-idx - 1); | 2545 | int volume = IF_MV_VOL(-idx - 1); |
2545 | if (volume > 0) | 2546 | cename = alloca(VOL_MAX_LEN+1); |
2546 | { | 2547 | get_volume_name(volume, cename); |
2547 | /* prepend the volume specifier for volumes > 0 */ | ||
2548 | cename = alloca(VOL_MAX_LEN+1); | ||
2549 | get_volume_name(volume, cename); | ||
2550 | } | ||
2551 | #endif /* HAVE_MULTIVOLUME */ | 2548 | #endif /* HAVE_MULTIVOLUME */ |
2552 | 2549 | ||
2553 | data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum, | 2550 | data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum, |
diff --git a/firmware/common/disk.c b/firmware/common/disk.c index c096878e86..c6fbc34409 100644 --- a/firmware/common/disk.c +++ b/firmware/common/disk.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include "disk_cache.h" | 27 | #include "disk_cache.h" |
28 | #include "fileobj_mgr.h" | 28 | #include "fileobj_mgr.h" |
29 | #include "dir.h" | 29 | #include "dir.h" |
30 | #include "dircache_redirect.h" | 30 | #include "rb_namespace.h" |
31 | #include "disk.h" | 31 | #include "disk.h" |
32 | 32 | ||
33 | #if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER) | 33 | #if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER) |
diff --git a/firmware/common/file.c b/firmware/common/file.c index c048d182f4..794d0059a9 100644 --- a/firmware/common/file.c +++ b/firmware/common/file.c | |||
@@ -28,7 +28,7 @@ | |||
28 | #include "file.h" | 28 | #include "file.h" |
29 | #include "fileobj_mgr.h" | 29 | #include "fileobj_mgr.h" |
30 | #include "disk_cache.h" | 30 | #include "disk_cache.h" |
31 | #include "dircache_redirect.h" | 31 | #include "rb_namespace.h" |
32 | #include "string-extra.h" | 32 | #include "string-extra.h" |
33 | 33 | ||
34 | /** | 34 | /** |
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c index b92c4ea115..14db247347 100644 --- a/firmware/common/file_internal.c +++ b/firmware/common/file_internal.c | |||
@@ -26,9 +26,7 @@ | |||
26 | #include "pathfuncs.h" | 26 | #include "pathfuncs.h" |
27 | #include "disk_cache.h" | 27 | #include "disk_cache.h" |
28 | #include "fileobj_mgr.h" | 28 | #include "fileobj_mgr.h" |
29 | #include "dir.h" | 29 | #include "rb_namespace.h" |
30 | #include "dircache_redirect.h" | ||
31 | #include "dircache.h" | ||
32 | #include "string-extra.h" | 30 | #include "string-extra.h" |
33 | #include "rbunicode.h" | 31 | #include "rbunicode.h" |
34 | 32 | ||
@@ -89,9 +87,9 @@ void file_cache_free(struct filestr_cache *cachep) | |||
89 | 87 | ||
90 | /** Stream base APIs **/ | 88 | /** Stream base APIs **/ |
91 | 89 | ||
92 | static inline void filestr_clear(struct filestr_base *stream) | 90 | static inline void filestr_clear(struct filestr_base *stream, unsigned int flags) |
93 | { | 91 | { |
94 | stream->flags = 0; | 92 | stream->flags = flags; |
95 | stream->bindp = NULL; | 93 | stream->bindp = NULL; |
96 | #if 0 | 94 | #if 0 |
97 | stream->mtx = NULL; | 95 | stream->mtx = NULL; |
@@ -155,7 +153,7 @@ void filestr_discard_cache(struct filestr_base *stream) | |||
155 | /* Initialize the base descriptor */ | 153 | /* Initialize the base descriptor */ |
156 | void filestr_base_init(struct filestr_base *stream) | 154 | void filestr_base_init(struct filestr_base *stream) |
157 | { | 155 | { |
158 | filestr_clear(stream); | 156 | filestr_clear(stream, FD_VALID); |
159 | file_cache_init(&stream->cache); | 157 | file_cache_init(&stream->cache); |
160 | stream->cachep = &stream->cache; | 158 | stream->cachep = &stream->cache; |
161 | } | 159 | } |
@@ -163,7 +161,7 @@ void filestr_base_init(struct filestr_base *stream) | |||
163 | /* free base descriptor resources */ | 161 | /* free base descriptor resources */ |
164 | void filestr_base_destroy(struct filestr_base *stream) | 162 | void filestr_base_destroy(struct filestr_base *stream) |
165 | { | 163 | { |
166 | filestr_clear(stream); | 164 | filestr_clear(stream, 0); |
167 | filestr_free_cache(stream); | 165 | filestr_free_cache(stream); |
168 | } | 166 | } |
169 | 167 | ||
@@ -229,7 +227,7 @@ void iso_decode_d_name(char *d_name) | |||
229 | 227 | ||
230 | #ifdef HAVE_DIRCACHE | 228 | #ifdef HAVE_DIRCACHE |
231 | /* nullify all the fields of the struct dirent */ | 229 | /* nullify all the fields of the struct dirent */ |
232 | void empty_dirent(struct dirent *entry) | 230 | void empty_dirent(struct DIRENT *entry) |
233 | { | 231 | { |
234 | entry->d_name[0] = '\0'; | 232 | entry->d_name[0] = '\0'; |
235 | entry->info.attr = 0; | 233 | entry->info.attr = 0; |
@@ -251,7 +249,7 @@ void fill_dirinfo_native(struct dirinfo_native *dinp) | |||
251 | 249 | ||
252 | int uncached_readdir_dirent(struct filestr_base *stream, | 250 | int uncached_readdir_dirent(struct filestr_base *stream, |
253 | struct dirscan_info *scanp, | 251 | struct dirscan_info *scanp, |
254 | struct dirent *entry) | 252 | struct DIRENT *entry) |
255 | { | 253 | { |
256 | struct fat_direntry fatent; | 254 | struct fat_direntry fatent; |
257 | int rc = fat_readdir(&stream->fatstr, &scanp->fatscan, | 255 | int rc = fat_readdir(&stream->fatstr, &scanp->fatscan, |
@@ -295,7 +293,7 @@ struct pathwalk_component | |||
295 | 293 | ||
296 | #define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */ | 294 | #define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */ |
297 | #define WALK_RC_FOUND 1 /* found and opened */ | 295 | #define WALK_RC_FOUND 1 /* found and opened */ |
298 | #define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */ | 296 | #define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */ |
299 | #define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */ | 297 | #define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */ |
300 | 298 | ||
301 | /* return another struct pathwalk_component from the pool, or NULL if the | 299 | /* return another struct pathwalk_component from the pool, or NULL if the |
@@ -399,10 +397,9 @@ static int walk_open_info(struct pathwalk *walkp, | |||
399 | 397 | ||
400 | /* make open official if not simply probing for presence - must do it here | 398 | /* make open official if not simply probing for presence - must do it here |
401 | or compp->info on stack will get destroyed before it was copied */ | 399 | or compp->info on stack will get destroyed before it was copied */ |
402 | if (!(callflags & FF_PROBE)) | 400 | if (!(callflags & (FF_PROBE|FF_NOFS))) |
403 | fileop_onopen_internal(stream, &compp->info, callflags); | 401 | fileop_onopen_internal(stream, &compp->info, callflags); |
404 | 402 | return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND; | |
405 | return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT; | ||
406 | } | 403 | } |
407 | 404 | ||
408 | /* check the component against the prefix test info */ | 405 | /* check the component against the prefix test info */ |
@@ -509,6 +506,10 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp, | |||
509 | if (len > MAX_COMPNAME) | 506 | if (len > MAX_COMPNAME) |
510 | return -ENAMETOOLONG; | 507 | return -ENAMETOOLONG; |
511 | 508 | ||
509 | /* no filesystem is mounted here */ | ||
510 | if (walkp->callflags & FF_NOFS) | ||
511 | return -ENOENT; | ||
512 | |||
512 | /* check for "." and ".." */ | 513 | /* check for "." and ".." */ |
513 | if (name[0] == '.') | 514 | if (name[0] == '.') |
514 | { | 515 | { |
@@ -577,7 +578,7 @@ int open_stream_internal(const char *path, unsigned int callflags, | |||
577 | callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX); | 578 | callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX); |
578 | 579 | ||
579 | /* This lets it be passed quietly to directory scanning */ | 580 | /* This lets it be passed quietly to directory scanning */ |
580 | stream->flags = callflags & FF_MASK; | 581 | stream->flags |= callflags & FF_MASK; |
581 | 582 | ||
582 | struct pathwalk walk; | 583 | struct pathwalk walk; |
583 | walk.path = path; | 584 | walk.path = path; |
@@ -587,81 +588,36 @@ int open_stream_internal(const char *path, unsigned int callflags, | |||
587 | 588 | ||
588 | struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL); | 589 | struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL); |
589 | rootp->nextp = NULL; | 590 | rootp->nextp = NULL; |
590 | rootp->attr = ATTR_SYSTEM_ROOT; | ||
591 | |||
592 | #ifdef HAVE_MULTIVOLUME | ||
593 | int volume = 0, rootrc = WALK_RC_FOUND; | ||
594 | #endif /* HAVE_MULTIVOLUME */ | ||
595 | 591 | ||
596 | while (1) | 592 | while (1) |
597 | { | 593 | { |
598 | const char *pathptr = walk.path; | 594 | rc = ns_parse_root(walk.path, &rootp->name, &rootp->length); |
599 | 595 | if (rc < 0) | |
600 | #ifdef HAVE_MULTIVOLUME | 596 | break; |
601 | /* this seamlessly integrates secondary filesystems into the | ||
602 | root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */ | ||
603 | const char *p; | ||
604 | volume = path_strip_volume(pathptr, &p, false); | ||
605 | if (!CHECK_VOL(volume)) | ||
606 | { | ||
607 | DEBUGF("No such device or address: %d\n", volume); | ||
608 | FILE_ERROR(ENXIO, -2); | ||
609 | } | ||
610 | |||
611 | if (p == pathptr) | ||
612 | { | ||
613 | /* the root of this subpath is the system root */ | ||
614 | rootp->attr = ATTR_SYSTEM_ROOT; | ||
615 | rootrc = WALK_RC_FOUND_ROOT; | ||
616 | } | ||
617 | else | ||
618 | { | ||
619 | /* this subpath specifies a mount point */ | ||
620 | rootp->attr = ATTR_MOUNT_POINT; | ||
621 | rootrc = WALK_RC_FOUND; | ||
622 | } | ||
623 | |||
624 | walk.path = p; | ||
625 | #endif /* HAVE_MULTIVOLUME */ | ||
626 | |||
627 | /* set name to start at last leading separator; names of volume | ||
628 | specifiers will be returned as "/<fooN>" */ | ||
629 | rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1; | ||
630 | rootp->length = | ||
631 | IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1; | ||
632 | 597 | ||
633 | rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile); | 598 | rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr); |
634 | if (rc < 0) | 599 | if (rc < 0) |
635 | { | ||
636 | /* not mounted */ | ||
637 | DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume)); | ||
638 | rc = -ENXIO; | ||
639 | break; | 600 | break; |
640 | } | ||
641 | 601 | ||
642 | get_rootinfo_internal(&rootp->info); | 602 | walk.path = rootp->name + rootp->length; |
603 | |||
643 | rc = walk_path(&walk, rootp, stream); | 604 | rc = walk_path(&walk, rootp, stream); |
644 | if (rc != WALK_RC_CONT_AT_ROOT) | 605 | if (rc != WALK_RC_CONT_AT_ROOT) |
645 | break; | 606 | break; |
646 | } | 607 | } |
647 | 608 | ||
648 | switch (rc) | 609 | if (rc >= 0) |
649 | { | 610 | { |
650 | case WALK_RC_FOUND_ROOT: | ||
651 | IF_MV( rc = rootrc; ) | ||
652 | /* fallthrough */ | ||
653 | case WALK_RC_NOT_FOUND: | ||
654 | case WALK_RC_FOUND: | ||
655 | /* FF_PROBE leaves nothing for caller to clean up */ | 611 | /* FF_PROBE leaves nothing for caller to clean up */ |
656 | if (callflags & FF_PROBE) | 612 | if (walk.callflags & FF_PROBE) |
657 | filestr_base_destroy(stream); | 613 | filestr_base_destroy(stream); |
658 | 614 | } | |
659 | break; | 615 | else |
660 | 616 | { | |
661 | default: /* utter, abject failure :`( */ | 617 | /* utter, abject failure :`( */ |
662 | DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno); | 618 | DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno); |
663 | filestr_base_destroy(stream); | 619 | filestr_base_destroy(stream); |
664 | FILE_ERROR(-rc, -3); | 620 | FILE_ERROR(-rc, -1); |
665 | } | 621 | } |
666 | 622 | ||
667 | file_error: | 623 | file_error: |
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c index e34a460e10..da82681acc 100644 --- a/firmware/common/fileobj_mgr.c +++ b/firmware/common/fileobj_mgr.c | |||
@@ -20,12 +20,13 @@ | |||
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include "config.h" | 21 | #include "config.h" |
22 | #include "system.h" | 22 | #include "system.h" |
23 | #include <errno.h> | ||
23 | #include "debug.h" | 24 | #include "debug.h" |
24 | #include "file.h" | 25 | #include "file.h" |
25 | #include "dir.h" | 26 | #include "dir.h" |
26 | #include "disk_cache.h" | 27 | #include "disk_cache.h" |
27 | #include "fileobj_mgr.h" | 28 | #include "fileobj_mgr.h" |
28 | #include "dircache_redirect.h" | 29 | #include "rb_namespace.h" |
29 | 30 | ||
30 | /** | 31 | /** |
31 | * Manages file and directory streams on all volumes | 32 | * Manages file and directory streams on all volumes |
@@ -34,8 +35,8 @@ | |||
34 | */ | 35 | */ |
35 | 36 | ||
36 | 37 | ||
37 | /* there will always be enough of these for all user handles, thus these | 38 | /* there will always be enough of these for all user handles, thus most of |
38 | functions don't return failure codes */ | 39 | these functions don't return failure codes */ |
39 | #define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS) | 40 | #define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS) |
40 | 41 | ||
41 | /* describes the file as an image on the storage medium */ | 42 | /* describes the file as an image on the storage medium */ |
@@ -84,6 +85,14 @@ static struct ll_head busy_bindings[NUM_VOLUMES]; | |||
84 | for (struct filestr_base *s = STREAM_##what(start); \ | 85 | for (struct filestr_base *s = STREAM_##what(start); \ |
85 | s; s = STREAM_NEXT(s)) | 86 | s; s = STREAM_NEXT(s)) |
86 | 87 | ||
88 | /* once a file/directory, always a file/directory; such a change | ||
89 | is a bug */ | ||
90 | #define CHECK_FO_DIRECTORY(callflags, fobp) \ | ||
91 | if (((callflags) ^ (fobp)->flags) & FO_DIRECTORY) \ | ||
92 | { \ | ||
93 | DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", \ | ||
94 | __func__, (fobp), (callflags)); \ | ||
95 | } | ||
87 | 96 | ||
88 | /* syncs information for the stream's old and new parent directory if any are | 97 | /* syncs information for the stream's old and new parent directory if any are |
89 | currently opened */ | 98 | currently opened */ |
@@ -96,6 +105,9 @@ static void fileobj_sync_parent(const struct file_base_info *infop[], | |||
96 | continue; /* not directory or removed can't be parent of anything */ | 105 | continue; /* not directory or removed can't be parent of anything */ |
97 | 106 | ||
98 | struct filestr_base *parentstrp = STREAM_FIRST(fobp); | 107 | struct filestr_base *parentstrp = STREAM_FIRST(fobp); |
108 | if (!parentstrp) | ||
109 | continue; | ||
110 | |||
99 | struct fat_file *parentfilep = &parentstrp->infop->fatfile; | 111 | struct fat_file *parentfilep = &parentstrp->infop->fatfile; |
100 | 112 | ||
101 | for (int i = 0; i < count; i++) | 113 | for (int i = 0; i < count; i++) |
@@ -111,8 +123,8 @@ static void fileobj_sync_parent(const struct file_base_info *infop[], | |||
111 | } | 123 | } |
112 | 124 | ||
113 | /* see if this file has open streams and return that fileobj_binding if so, | 125 | /* see if this file has open streams and return that fileobj_binding if so, |
114 | else grab a new one from the free list; returns true if this stream is | 126 | else grab a new one from the free list; returns true if this is new */ |
115 | the only open one */ | 127 | |
116 | static bool binding_assign(const struct file_base_info *srcinfop, | 128 | static bool binding_assign(const struct file_base_info *srcinfop, |
117 | struct fileobj_binding **fobpp) | 129 | struct fileobj_binding **fobpp) |
118 | { | 130 | { |
@@ -123,7 +135,7 @@ static bool binding_assign(const struct file_base_info *srcinfop, | |||
123 | 135 | ||
124 | if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile)) | 136 | if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile)) |
125 | { | 137 | { |
126 | /* already has open streams */ | 138 | /* already has open streams/mounts*/ |
127 | *fobpp = fobp; | 139 | *fobpp = fobp; |
128 | return false; | 140 | return false; |
129 | } | 141 | } |
@@ -143,6 +155,22 @@ static void binding_add_to_free_list(struct fileobj_binding *fobp) | |||
143 | ll_insert_last(FREE_BINDINGS(), &fobp->bind.node); | 155 | ll_insert_last(FREE_BINDINGS(), &fobp->bind.node); |
144 | } | 156 | } |
145 | 157 | ||
158 | static void bind_source_info(const struct file_base_info *srcinfop, | ||
159 | struct fileobj_binding **fobpp) | ||
160 | { | ||
161 | if (!binding_assign(srcinfop, fobpp)) | ||
162 | return; /* already in use */ | ||
163 | /* is new */ | ||
164 | (*fobpp)->bind.info = *srcinfop; | ||
165 | fileobj_bind_file(&(*fobpp)->bind); | ||
166 | } | ||
167 | |||
168 | static void release_binding(struct fileobj_binding *fobp) | ||
169 | { | ||
170 | fileobj_unbind_file(&fobp->bind); | ||
171 | binding_add_to_free_list(fobp); | ||
172 | } | ||
173 | |||
146 | /** File and directory internal interface **/ | 174 | /** File and directory internal interface **/ |
147 | 175 | ||
148 | void file_binding_insert_last(struct file_base_binding *bindp) | 176 | void file_binding_insert_last(struct file_base_binding *bindp) |
@@ -169,6 +197,33 @@ void file_binding_remove_next(struct file_base_binding *prevp, | |||
169 | } | 197 | } |
170 | #endif /* HAVE_DIRCACHE */ | 198 | #endif /* HAVE_DIRCACHE */ |
171 | 199 | ||
200 | /* mounts a file object as a target from elsewhere */ | ||
201 | bool fileobj_mount(const struct file_base_info *srcinfop, | ||
202 | unsigned int callflags, | ||
203 | struct file_base_binding **bindpp) | ||
204 | { | ||
205 | struct fileobj_binding *fobp; | ||
206 | bind_source_info(srcinfop, &fobp); | ||
207 | CHECK_FO_DIRECTORY(callflags, fobp); | ||
208 | if (fobp->flags & FO_MOUNTTARGET) | ||
209 | return false; /* already mounted */ | ||
210 | fobp->flags |= FDO_BUSY | FO_MOUNTTARGET | | ||
211 | (callflags & FO_DIRECTORY); | ||
212 | *bindpp = &fobp->bind; | ||
213 | return true; | ||
214 | } | ||
215 | /* unmounts the file object and frees it if now unusued */ | ||
216 | void fileobj_unmount(struct file_base_binding *bindp) | ||
217 | { | ||
218 | struct fileobj_binding *fobp = (struct fileobj_binding *)bindp; | ||
219 | if (!(fobp->flags & FO_MOUNTTARGET)) | ||
220 | return; /* not mounted */ | ||
221 | if (STREAM_FIRST(fobp) == NULL) | ||
222 | release_binding(fobp); /* no longer in use */ | ||
223 | else | ||
224 | fobp->flags &= ~FO_MOUNTTARGET; | ||
225 | } | ||
226 | |||
172 | /* opens the file object for a new stream and sets up the caches; | 227 | /* opens the file object for a new stream and sets up the caches; |
173 | * the stream must already be opened at the FS driver level and *stream | 228 | * the stream must already be opened at the FS driver level and *stream |
174 | * initialized. | 229 | * initialized. |
@@ -180,10 +235,13 @@ void fileobj_fileop_open(struct filestr_base *stream, | |||
180 | const struct file_base_info *srcinfop, | 235 | const struct file_base_info *srcinfop, |
181 | unsigned int callflags) | 236 | unsigned int callflags) |
182 | { | 237 | { |
238 | /* assign base file information */ | ||
183 | struct fileobj_binding *fobp; | 239 | struct fileobj_binding *fobp; |
184 | bool first = binding_assign(srcinfop, &fobp); | 240 | bind_source_info(srcinfop, &fobp); |
241 | unsigned int foflags = fobp->flags; | ||
185 | 242 | ||
186 | /* add stream to this file's list */ | 243 | /* add stream to this file's list */ |
244 | bool first = STREAM_FIRST(fobp) == NULL; | ||
187 | ll_insert_last(&fobp->list, &stream->node); | 245 | ll_insert_last(&fobp->list, &stream->node); |
188 | 246 | ||
189 | /* initiate the new stream into the enclave */ | 247 | /* initiate the new stream into the enclave */ |
@@ -197,27 +255,16 @@ void fileobj_fileop_open(struct filestr_base *stream, | |||
197 | if (first) | 255 | if (first) |
198 | { | 256 | { |
199 | /* first stream for file */ | 257 | /* first stream for file */ |
200 | fobp->bind.info = *srcinfop; | 258 | fobp->flags = foflags | FDO_BUSY | FO_SINGLE | |
201 | fobp->flags = FDO_BUSY | FO_SINGLE | | 259 | (callflags & (FO_DIRECTORY|FO_TRUNC)); |
202 | (callflags & (FO_DIRECTORY|FO_TRUNC)); | 260 | fobp->writers = 0; |
203 | fobp->writers = 0; | 261 | fobp->size = 0; |
204 | fobp->size = 0; | ||
205 | |||
206 | fileobj_bind_file(&fobp->bind); | ||
207 | } | 262 | } |
208 | else | 263 | else |
209 | { | 264 | { |
210 | /* additional stream for file */ | 265 | /* additional stream for file */ |
211 | fobp->flags &= ~FO_SINGLE; | 266 | fobp->flags = (foflags & ~FO_SINGLE) | (callflags & FO_TRUNC); |
212 | fobp->flags |= callflags & FO_TRUNC; | 267 | CHECK_FO_DIRECTORY(callflags, fobp); |
213 | |||
214 | /* once a file/directory, always a file/directory; such a change | ||
215 | is a bug */ | ||
216 | if ((callflags ^ fobp->flags) & FO_DIRECTORY) | ||
217 | { | ||
218 | DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", | ||
219 | __func__, stream, callflags); | ||
220 | } | ||
221 | } | 268 | } |
222 | 269 | ||
223 | if ((callflags & FD_WRITE) && ++fobp->writers == 1) | 270 | if ((callflags & FD_WRITE) && ++fobp->writers == 1) |
@@ -257,12 +304,14 @@ void fileobj_fileop_close(struct filestr_base *stream) | |||
257 | if (foflags & FO_SINGLE) | 304 | if (foflags & FO_SINGLE) |
258 | { | 305 | { |
259 | /* last stream for file; close everything */ | 306 | /* last stream for file; close everything */ |
260 | fileobj_unbind_file(&fobp->bind); | ||
261 | |||
262 | if (fobp->writers) | 307 | if (fobp->writers) |
263 | file_cache_free(&fobp->cache); | 308 | file_cache_free(&fobp->cache); |
264 | 309 | ||
265 | binding_add_to_free_list(fobp); | 310 | /* binding must stay valid if something is mounted to here */ |
311 | if (foflags & FO_MOUNTTARGET) | ||
312 | fobp->flags = foflags & (FDO_BUSY|FO_DIRECTORY|FO_MOUNTTARGET); | ||
313 | else | ||
314 | release_binding(fobp); | ||
266 | } | 315 | } |
267 | else | 316 | else |
268 | { | 317 | { |
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c index 2b4e6a8eb0..5242ec2d32 100644 --- a/firmware/common/pathfuncs.c +++ b/firmware/common/pathfuncs.c | |||
@@ -114,7 +114,7 @@ static const unsigned char storage_dec_indexes[STORAGE_NUM_TYPES+1] = | |||
114 | */ | 114 | */ |
115 | int path_strip_volume(const char *name, const char **nameptr, bool greedy) | 115 | int path_strip_volume(const char *name, const char **nameptr, bool greedy) |
116 | { | 116 | { |
117 | int volume = 0; | 117 | int volume = ROOT_VOLUME; |
118 | const char *t = name; | 118 | const char *t = name; |
119 | int c, v = 0; | 119 | int c, v = 0; |
120 | 120 | ||
@@ -123,9 +123,15 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy) | |||
123 | * digits within the brackets is parsed as the volume number and of | 123 | * digits within the brackets is parsed as the volume number and of |
124 | * those, only the last ones VOL_MUM_MAX allows. | 124 | * those, only the last ones VOL_MUM_MAX allows. |
125 | */ | 125 | */ |
126 | c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */ | 126 | t = GOBBLE_PATH_SEPCH(t); /* skip all leading slashes */ |
127 | if (t == name) | ||
128 | { | ||
129 | volume = -1; /* relative path; don't know */ | ||
130 | goto psv_out; | ||
131 | } | ||
132 | c = *t; | ||
127 | if (c != VOL_START_TOK) /* missing start token? no volume */ | 133 | if (c != VOL_START_TOK) /* missing start token? no volume */ |
128 | goto volume0; | 134 | goto psv_out; |
129 | 135 | ||
130 | do | 136 | do |
131 | { | 137 | { |
@@ -136,7 +142,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy) | |||
136 | break; | 142 | break; |
137 | case '\0': | 143 | case '\0': |
138 | case PATH_SEPCH: /* no closing bracket; no volume */ | 144 | case PATH_SEPCH: /* no closing bracket; no volume */ |
139 | goto volume0; | 145 | goto psv_out; |
140 | default: /* something else; reset volume */ | 146 | default: /* something else; reset volume */ |
141 | v = 0; | 147 | v = 0; |
142 | } | 148 | } |
@@ -146,7 +152,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy) | |||
146 | if (!(c = *++t)) /* no more path and no '/' is ok */ | 152 | if (!(c = *++t)) /* no more path and no '/' is ok */ |
147 | ; | 153 | ; |
148 | else if (c != PATH_SEPCH) /* more path and no separator after end */ | 154 | else if (c != PATH_SEPCH) /* more path and no separator after end */ |
149 | goto volume0; | 155 | goto psv_out; |
150 | else if (greedy) | 156 | else if (greedy) |
151 | t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */ | 157 | t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */ |
152 | 158 | ||
@@ -155,7 +161,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy) | |||
155 | 161 | ||
156 | volume = v; | 162 | volume = v; |
157 | name = t; | 163 | name = t; |
158 | volume0: | 164 | psv_out: |
159 | if (nameptr) | 165 | if (nameptr) |
160 | *nameptr = name; | 166 | *nameptr = name; |
161 | return volume; | 167 | return volume; |
@@ -166,10 +172,13 @@ volume0: | |||
166 | */ | 172 | */ |
167 | int get_volume_name(int volume, char *buffer) | 173 | int get_volume_name(int volume, char *buffer) |
168 | { | 174 | { |
169 | if (volume < 0) | 175 | if (volume < 0 || volume == ROOT_VOLUME) |
170 | { | 176 | { |
171 | *buffer = '\0'; | 177 | char *t = buffer; |
172 | return 0; | 178 | if (volume == ROOT_VOLUME) |
179 | *t++ = PATH_ROOTCHR; | ||
180 | *t = '\0'; | ||
181 | return t - buffer; | ||
173 | } | 182 | } |
174 | 183 | ||
175 | volume %= VOL_NUM_MAX; /* as path parser would have it */ | 184 | volume %= VOL_NUM_MAX; /* as path parser would have it */ |
@@ -182,8 +191,20 @@ int get_volume_name(int volume, char *buffer) | |||
182 | return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c", | 191 | return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c", |
183 | VOL_START_TOK, voldec, volume, VOL_END_TOK); | 192 | VOL_START_TOK, voldec, volume, VOL_END_TOK); |
184 | } | 193 | } |
194 | |||
195 | /* Returns volume name formatted with the root. Assumes buffer size is at | ||
196 | * least {VOL_MAX_LEN}+2 */ | ||
197 | int make_volume_root(int volume, char *buffer) | ||
198 | { | ||
199 | char *t = buffer; | ||
200 | if (volume >= 0 && volume != ROOT_VOLUME) | ||
201 | *t++ = PATH_ROOTCHR; | ||
202 | t += get_volume_name(volume, t); | ||
203 | return t - buffer; | ||
204 | } | ||
185 | #endif /* HAVE_MULTIVOLUME */ | 205 | #endif /* HAVE_MULTIVOLUME */ |
186 | 206 | ||
207 | |||
187 | /* Just like path_strip_volume() but strips a leading drive specifier and | 208 | /* Just like path_strip_volume() but strips a leading drive specifier and |
188 | * returns the drive number (A=0, B=1, etc.). -1 means no drive was found. | 209 | * returns the drive number (A=0, B=1, etc.). -1 means no drive was found. |
189 | * If 'greedy' is 'true', all separators after the volume are consumed. | 210 | * If 'greedy' is 'true', all separators after the volume are consumed. |
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 | } | ||