diff options
author | Amaury Pouly <pamaury@rockbox.org> | 2010-01-31 17:02:56 +0000 |
---|---|---|
committer | Amaury Pouly <pamaury@rockbox.org> | 2010-01-31 17:02:56 +0000 |
commit | 4b930df35eea4251802e5e82049f557782dc8a99 (patch) | |
tree | bcca57d8606e6347b60176954cfb7e44cdfced54 /firmware/common/dircache.c | |
parent | 1c6cada2970a5cd12a063f5c3e844a9d4a3e30de (diff) | |
download | rockbox-4b930df35eea4251802e5e82049f557782dc8a99.tar.gz rockbox-4b930df35eea4251802e5e82049f557782dc8a99.zip |
Fix a dircache bug (opening a directory opens the first file of that directory). Also clarify the behaviour of dircache_get_entry.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24420 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/common/dircache.c')
-rw-r--r-- | firmware/common/dircache.c | 117 |
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 | */ |
397 | static struct dircache_entry* dircache_get_entry(const char *path, | 414 | static 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) |