diff --git a/firmware/common/dir_uncached.c b/firmware/common/dir_uncached.c index 14c8522..e78440b 100644 --- a/firmware/common/dir_uncached.c +++ b/firmware/common/dir_uncached.c @@ -57,6 +57,25 @@ int release_dirs(int volume) return closed; /* return how many we did */ } +#ifdef HAVE_MULTIVOLUME +static int match_volume(const char* name) +{ + int volume = 0; + const char *temp = name; + + if (!strncmp(temp, VOL_NAMES, VOL_ENUM_POS)) + { + temp += VOL_ENUM_POS; /* behind special name */ + volume = atoi(temp); /* number is following */ + /* FIXME: Only permit actual volume names (such as + * ""), not variations like "busy = true; + entry.firstcluster = 0; +#ifdef HAVE_MULTIVOLUME + int volume = 0; +#endif + strlcpy(namecopy, name, sizeof(namecopy)); + for ( part = strtok_r(namecopy, "/", &end); ; + part = strtok_r(NULL, "/", &end)) { + if (part) { + /* Handle "." here to ensure the parent directory + * passed to fat_opendir() is correct and because + * root directories don't have "." entries. */ + if (!strcmp(part, ".")) { + continue; + } + else if (entry.firstcluster == 0) { + /* Currently in a root directory of a FAT filesystem. */ + if (!strcmp(part, "..")) { + /* this goes to the root of the whole hierarchy */ #ifdef HAVE_MULTIVOLUME - /* try to extract a heading volume name, if present */ - volume = strip_volume(name, namecopy); - pdir->volumecounter = 0; -#else - strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */ + volume = 0; #endif + continue; + } +#ifdef HAVE_MULTIVOLUME + else if (volume == 0) { + /* a directory here could be a volume mountpoint */ + volume = match_volume(part); + if (volume > 0) { + /* part matched a volume mountpoint */ + continue; + } + /* Else, part is a normal subdirectory of the root + * directory of the root filesystem. */ + } +#endif + } + } - if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) { - DEBUGF("Failed opening root dir\n"); - pdir->busy = false; - return NULL; - } + /* In reality, the parent_dir parameter of fat_opendir seems + * useless because it's sole purpose it to have a way to + * update the file metadata, but here we are only reading + * a directory so there's no need for that kind of stuff. + * However, the rmdir_uncached function uses a ugly hack to + * avoid opening a directory twice when deleting it and thus + * needs those information. That's why we pass pdir->fatdir both + * as the parent directory and the resulting one (this is safe, + * in doubt, check fat_open(dir) code) which will allow this kind of + * (ugly) things */ + + /* FIXME: Parent directory is incorrect when using "..". + * This seems to only matter in case of a race condition: + * rmdir("/1/2/3/..") is called and rmdir("/1/2/3") succeeds + * in the middle. It would have to allow the "3/.." traverse + * to complete, but remove 3 so removal of 2 can succeed. + * Solving this requires use of a stack for ".." or searching + * ".." of the directory being removed for the entry pointing + * to the directory being removed. */ + + if ( fat_opendir(IF_MV2(volume,) + &pdir->fatdir, + entry.firstcluster, + entry.firstcluster == 0 ? + NULL : &pdir->fatdir) < 0 ) { + DEBUGF("Failed opening dir at %ld\n", entry.firstcluster); + pdir->busy = false; + return NULL; + } + + /* loop exit is here to avoid duplicating fat_opendir() call */ + if (part == NULL) { + pdir->volumecounter = (entry.firstcluster == 0 && volume == 0) ? + 0 : -1; + break; + } - for ( part = strtok_r(namecopy, "/", &end); part; - part = strtok_r(NULL, "/", &end)) { /* scan dir for name */ while (1) { if ((fat_getnext(&pdir->fatdir,&entry) < 0) || @@ -112,28 +187,6 @@ DIR_UNCACHED* opendir_uncached(const char* name) } if ( (entry.attr & FAT_ATTR_DIRECTORY) && (!strcasecmp(part, entry.name)) ) { - /* In reality, the parent_dir parameter of fat_opendir seems - * useless because it's sole purpose it to have a way to - * update the file metadata, but here we are only reading - * a directory so there's no need for that kind of stuff. - * However, the rmdir_uncached function uses a ugly hack to - * avoid opening a directory twice when deleting it and thus - * needs those information. That's why we pass pdir->fatdir both - * as the parent directory and the resulting one (this is safe, - * in doubt, check fat_open(dir) code) which will allow this kind of - * (ugly) things */ - if ( fat_opendir(IF_MV2(volume,) - &pdir->fatdir, - entry.firstcluster, - &pdir->fatdir) < 0 ) { - DEBUGF("Failed opening dir '%s' (%ld)\n", - part, entry.firstcluster); - pdir->busy = false; - return NULL; - } -#ifdef HAVE_MULTIVOLUME - pdir->volumecounter = -1; /* n.a. to subdirs */ -#endif break; } } diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index 21ae71f..f342775 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c @@ -396,6 +396,7 @@ static int dircache_scan_and_build(IF_MV2(int volume,) struct dircache_entry *ce dircache_size += max_len; ce->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME; ce->info.size = 0; + ce->startcluster = volume; append_position = dircache_gen_next(ce); ce = dircache_gen_down(ce); } @@ -1194,6 +1195,24 @@ struct dirinfo* _dircache_get_entry_dirinfo(int id) return &get_entry(id)->info; } +#ifdef HAVE_MULTIVOLUME +/** + * Internal: Get the volume for the index + */ +int _dircache_get_entry_volume(int id) +{ + const struct dircache_entry *entry = get_entry(id); + + while (1) { + if (entry->info.attribute & FAT_ATTR_VOLUME) + return entry->startcluster; + if (entry->up == NULL) + return 0; + entry = entry->up; + } +} +#endif + /* * build a path from an entry upto the root using recursion * diff --git a/firmware/common/file.c b/firmware/common/file.c index cfebd06..e84eaf9 100644 --- a/firmware/common/file.c +++ b/firmware/common/file.c @@ -108,10 +108,6 @@ static int open_internal(const char* pathname, int flags, bool use_cache) #ifdef HAVE_DIRCACHE if (dircache_is_enabled() && !file->write && use_cache) { -# ifdef HAVE_MULTIVOLUME - int volume = strip_volume(pathname, pathnamecopy); -# endif - int ce = dircache_get_entry_id(pathname); if (ce < 0) { @@ -120,6 +116,10 @@ static int open_internal(const char* pathname, int flags, bool use_cache) return -7; } +# ifdef HAVE_MULTIVOLUME + int volume = _dircache_get_entry_volume(ce); +# endif + long startcluster = _dircache_get_entry_startcluster(ce); fat_open(IF_MV2(volume,) startcluster, diff --git a/firmware/include/dircache.h b/firmware/include/dircache.h index 019ccf4..039ae48 100644 --- a/firmware/include/dircache.h +++ b/firmware/include/dircache.h @@ -81,9 +81,12 @@ bool dircache_resume(void); int dircache_get_entry_id(const char *filename); size_t dircache_copy_path(int index, char *buf, size_t size); -/* the next two are internal for file.c */ +/* the next three are internal for file.c */ long _dircache_get_entry_startcluster(int id); struct dirinfo* _dircache_get_entry_dirinfo(int id); +#ifdef HAVE_MULTIVOLUME +int _dircache_get_entry_volume(int id); +#endif void dircache_bind(int fd, const char *path); void dircache_update_filesize(int fd, long newsize, long startcluster);