summaryrefslogtreecommitdiff
path: root/firmware/common/dircache.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/dircache.c')
-rw-r--r--firmware/common/dircache.c117
1 files changed, 74 insertions, 43 deletions
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index f844e548f6..7ff497790e 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -390,54 +390,85 @@ static int dircache_travel(IF_MV2(int volume,) struct fat_dir *dir, struct dirca
390 390
391/** 391/**
392 * Internal function to get a pointer to dircache_entry for a given filename. 392 * Internal function to get a pointer to dircache_entry for a given filename.
393 * path: Absolute path to a file or directory. 393 * path: Absolute path to a file or directory (see comment)
394 * get_before: Returns the cache pointer before the last valid entry found. 394 * go_down: Returns the first entry of the directory given by the path (see comment)
395 * only_directories: Match only filenames which are a directory type. 395 *
396 * As a a special case to handle buggy code, accept path="" as an alias for "/"
397 *
398 * * If get_down=true:
399 * If path="/", the returned entry is the first of root directory (ie dircache_root)
400 * Otherwise, if 'entry' is the returned value when get_down=false,
401 * the functions returns entry->down (which can be NULL)
402 *
403 * * If get_down=false:
404 * If path="/chunk_1/chunk_2/.../chunk_n" then this functions returns the entry
405 * root_entry()->chunk_1->chunk_2->...->chunk_(n-1)
406 * Which means that
407 * dircache_get_entry(path)->d_name == chunk_n
408 *
409 * If path="/", the returned entry is NULL.
410 * If the entry doesn't exist, return NULL
411 *
412 * NOTE: this functions silently handles double '/'
396 */ 413 */
397static struct dircache_entry* dircache_get_entry(const char *path, 414static struct dircache_entry* dircache_get_entry(const char *path, bool go_down)
398 bool get_before, bool only_directories)
399{ 415{
400 struct dircache_entry *cache_entry, *before; 416 char namecopy[MAX_PATH];
401 char namecopy[MAX_PATH*2];
402 char* part; 417 char* part;
403 char* end; 418 char* end;
404
405 strlcpy(namecopy, path, sizeof(namecopy));
406 cache_entry = dircache_root;
407 before = NULL;
408 419
409 for ( part = strtok_r(namecopy, "/", &end); part; 420 bool at_root = true;
410 part = strtok_r(NULL, "/", &end)) { 421 struct dircache_entry *cache_entry = dircache_root;
411 422
423 /* check that the path is absolute (accept empty path also) */
424 if(path[0] != '/' && path[0] != 0)
425 return NULL;
426
427 /* make a copy of the path because strok_r modifies the path */
428 /* also handle the weird "" alias for "/" */
429 if(path[0] != 0)
430 strlcpy(namecopy, path, sizeof(namecopy));
431 else
432 strlcpy(namecopy, "/", sizeof(namecopy));
433
434 for(part = strtok_r(namecopy, "/", &end); part; part = strtok_r(NULL, "/", &end))
435 {
436 /* If request another chunk, the current entry has to be directory
437 * and so cache_entry->down has to be non-NULL/
438 * Special case of root because it's already the first entry of the root directory
439 *
440 * NOTE: this is safe even if cache_entry->down is NULL */
441 if(!at_root)
442 cache_entry = cache_entry->down;
443 else
444 at_root = false;
445
412 /* scan dir for name */ 446 /* scan dir for name */
413 while (1) 447 while(cache_entry != NULL)
414 { 448 {
415 if (cache_entry == NULL) 449 /* skip unused entries */
416 { 450 if(cache_entry->name_len == 0)
417 return NULL;
418 }
419 else if (cache_entry->name_len == 0)
420 { 451 {
421 cache_entry = cache_entry->next; 452 cache_entry = cache_entry->next;
422 continue ; 453 continue;
423 } 454 }
424 455 /* compare names */
425 if (!strcasecmp(part, cache_entry->d_name)) 456 if(!strcasecmp(part, cache_entry->d_name))
426 { 457 break;
427 before = cache_entry; 458 /* go to next entry */
428 if (cache_entry->down || only_directories)
429 cache_entry = cache_entry->down;
430 break ;
431 }
432
433 cache_entry = cache_entry->next; 459 cache_entry = cache_entry->next;
434 } 460 }
461
462 /* handle not found case */
463 if(cache_entry == NULL)
464 return NULL;
435 } 465 }
436 466
437 if (get_before) 467 /* NOTE: here cache_entry!=NULL so taking ->down is safe */
438 cache_entry = before; 468 if(go_down)
439 469 return at_root ? cache_entry : cache_entry->down;
440 return cache_entry; 470 else
471 return at_root ? NULL : cache_entry;
441} 472}
442 473
443#ifdef HAVE_EEPROM_SETTINGS 474#ifdef HAVE_EEPROM_SETTINGS
@@ -873,7 +904,7 @@ const struct dircache_entry *dircache_get_entry_ptr(const char *filename)
873 if (!dircache_initialized || filename == NULL) 904 if (!dircache_initialized || filename == NULL)
874 return NULL; 905 return NULL;
875 906
876 return dircache_get_entry(filename, false, false); 907 return dircache_get_entry(filename, false);
877} 908}
878 909
879/** 910/**
@@ -941,7 +972,7 @@ static struct dircache_entry* dircache_new_entry(const char *path, int attribute
941 *new = '\0'; 972 *new = '\0';
942 new++; 973 new++;
943 974
944 entry = dircache_get_entry(basedir, false, true); 975 entry = dircache_get_entry(basedir, true);
945 if (entry == NULL) 976 if (entry == NULL)
946 { 977 {
947 logf("basedir not found!"); 978 logf("basedir not found!");
@@ -1011,7 +1042,7 @@ void dircache_bind(int fd, const char *path)
1011 return ; 1042 return ;
1012 1043
1013 logf("bind: %d/%s", fd, path); 1044 logf("bind: %d/%s", fd, path);
1014 entry = dircache_get_entry(path, false, false); 1045 entry = dircache_get_entry(path, false);
1015 if (entry == NULL) 1046 if (entry == NULL)
1016 { 1047 {
1017 logf("not found!"); 1048 logf("not found!");
@@ -1080,10 +1111,10 @@ void dircache_rmdir(const char *path)
1080 return ; 1111 return ;
1081 1112
1082 logf("rmdir: %s", path); 1113 logf("rmdir: %s", path);
1083 entry = dircache_get_entry(path, true, true); 1114 entry = dircache_get_entry(path, false);
1084 if (entry == NULL) 1115 if (entry == NULL || entry->down == NULL)
1085 { 1116 {
1086 logf("not found!"); 1117 logf("not found or not a directory!");
1087 dircache_initialized = false; 1118 dircache_initialized = false;
1088 return ; 1119 return ;
1089 } 1120 }
@@ -1102,7 +1133,7 @@ void dircache_remove(const char *name)
1102 1133
1103 logf("remove: %s", name); 1134 logf("remove: %s", name);
1104 1135
1105 entry = dircache_get_entry(name, false, false); 1136 entry = dircache_get_entry(name, false);
1106 1137
1107 if (entry == NULL) 1138 if (entry == NULL)
1108 { 1139 {
@@ -1126,7 +1157,7 @@ void dircache_rename(const char *oldpath, const char *newpath)
1126 1157
1127 logf("rename: %s->%s", oldpath, newpath); 1158 logf("rename: %s->%s", oldpath, newpath);
1128 1159
1129 entry = dircache_get_entry(oldpath, true, false); 1160 entry = dircache_get_entry(oldpath, false);
1130 if (entry == NULL) 1161 if (entry == NULL)
1131 { 1162 {
1132 logf("not found!"); 1163 logf("not found!");
@@ -1223,7 +1254,7 @@ DIR_CACHED* opendir_cached(const char* name)
1223 1254
1224 pdir->busy = true; 1255 pdir->busy = true;
1225 pdir->regulardir = NULL; 1256 pdir->regulardir = NULL;
1226 cache_entry = dircache_get_entry(name, false, true); 1257 cache_entry = dircache_get_entry(name, true);
1227 pdir->entry = cache_entry; 1258 pdir->entry = cache_entry;
1228 1259
1229 if (cache_entry == NULL) 1260 if (cache_entry == NULL)