summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2017-03-12 20:59:44 -0400
committerMichael Sevakis <jethead71@rockbox.org>2017-03-12 21:09:16 -0400
commit70c929179b80e0657e31558e34d2bc62e1176564 (patch)
treeb359577dabe279aa2e3995f7ef57581d583e7218
parente3081b35cdeb1e568c61369e5b3b15b6e428d3c3 (diff)
downloadrockbox-70c929179b80e0657e31558e34d2bc62e1176564.tar.gz
rockbox-70c929179b80e0657e31558e34d2bc62e1176564.zip
Dircache: Refine name allocation and error handling.
* 8 bits is enough to allow 260 character base names when five bytes is the minimum indirect storage size (0..255->5..260). * Don't truncate anything that's too long as that can lead to bad behavior, simply don't include the offending entry in the parent. * Set the .tinyname flag to 1 by default to indicate that the entry's name doesn't need freeing. Clear it only when allocating indirect storage. * Rename some things to help catch all instances Change-Id: Iff747b624acbb8e03ed26c24afdf0fc715fd9d99
-rw-r--r--firmware/common/dircache.c80
-rw-r--r--firmware/common/file_internal.c2
-rw-r--r--firmware/include/file_internal.h2
3 files changed, 49 insertions, 35 deletions
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 08457f2da1..42585e121b 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -149,15 +149,18 @@ enum
149 DCM_LAST = DCM_PROCEED, 149 DCM_LAST = DCM_PROCEED,
150}; 150};
151 151
152#define MAX_TINYNAME sizeof (uint32_t) 152#define MAX_TINYNAME sizeof (uint32_t)
153#define DC_MAX_NAME MIN(MAX_NAME, UINT8_MAX) 153#define NAMELEN_ADJ (MAX_TINYNAME+1)
154#define DC_MAX_NAME (UINT8_MAX+NAMELEN_ADJ)
155#define CE_NAMESIZE(len) ((len)+NAMELEN_ADJ)
156#define NAMESIZE_CE(size) ((size)-NAMELEN_ADJ)
154 157
155/* Throw some warnings if about the limits if things may not work */ 158/* Throw some warnings if about the limits if things may not work */
156#if MAX_NAME > UINT8_MAX 159#if MAX_COMPNAME > UINT8_MAX+5
157#warning Need more than 8 bits in name length bitfield 160#warning Need more than 8 bits in name length bitfield
158#endif 161#endif
159 162
160#if DIRCACHE_LIMIT > ((1 << 24)-255) 163#if DIRCACHE_LIMIT > ((1 << 24)-1+5)
161#warning Names may not be addressable with 24 bits 164#warning Names may not be addressable with 24 bits
162#endif 165#endif
163 166
@@ -173,7 +176,7 @@ struct dircache_entry
173 union { 176 union {
174 struct { 177 struct {
175 uint32_t name : 24; /* indirect storage (.tinyname == 0) */ 178 uint32_t name : 24; /* indirect storage (.tinyname == 0) */
176 uint32_t length : 8; /* length of name indexed by 'name' */ 179 uint32_t namelen : 8; /* length of name - (MAX_TINYNAME+1) */
177 }; 180 };
178 unsigned char namebuf[MAX_TINYNAME]; /* direct storage (.tinyname == 1) */ 181 unsigned char namebuf[MAX_TINYNAME]; /* direct storage (.tinyname == 1) */
179 }; 182 };
@@ -743,7 +746,7 @@ static void entry_name_copy(char *dst, const struct dircache_entry *ce)
743{ 746{
744 if (LIKELY(!ce->tinyname)) 747 if (LIKELY(!ce->tinyname))
745 { 748 {
746 strmemcpy(dst, get_name(ce->name), ce->length); 749 strmemcpy(dst, get_name(ce->name), CE_NAMESIZE(ce->namelen));
747 return; 750 return;
748 } 751 }
749 752
@@ -876,8 +879,8 @@ static void free_name(int nameidx, size_t size)
876/** 879/**
877 * allocate and assign a name to the entry 880 * allocate and assign a name to the entry
878 */ 881 */
879static bool entry_assign_name(struct dircache_entry *ce, 882static int entry_assign_name(struct dircache_entry *ce,
880 const unsigned char *name, size_t size) 883 const unsigned char *name, size_t size)
881{ 884{
882 unsigned char *copyto; 885 unsigned char *copyto;
883 886
@@ -893,21 +896,21 @@ static bool entry_assign_name(struct dircache_entry *ce,
893 else 896 else
894 { 897 {
895 if (size > DC_MAX_NAME) 898 if (size > DC_MAX_NAME)
896 size = DC_MAX_NAME; 899 return -ENAMETOOLONG;
897 900
898 int nameidx = alloc_name(size); 901 int nameidx = alloc_name(size);
899 if (!nameidx) 902 if (!nameidx)
900 return false; 903 return -ENOSPC;
901 904
902 copyto = get_name(nameidx); 905 copyto = get_name(nameidx);
903 906
904 ce->tinyname = 0; 907 ce->tinyname = 0;
905 ce->name = nameidx; 908 ce->name = nameidx;
906 ce->length = size; 909 ce->namelen = NAMESIZE_CE(size);
907 } 910 }
908 911
909 memcpy(copyto, name, size); 912 memcpy(copyto, name, size);
910 return true; 913 return 0;
911} 914}
912 915
913/** 916/**
@@ -915,17 +918,20 @@ static bool entry_assign_name(struct dircache_entry *ce,
915 */ 918 */
916static void entry_unassign_name(struct dircache_entry *ce) 919static void entry_unassign_name(struct dircache_entry *ce)
917{ 920{
918 if (!ce->tinyname) 921 if (ce->tinyname)
919 free_name(ce->name, ce->length); 922 return;
923
924 free_name(ce->name, CE_NAMESIZE(ce->namelen));
925 ce->tinyname = 1;
920} 926}
921 927
922/** 928/**
923 * assign a new name to the entry 929 * assign a new name to the entry
924 */ 930 */
925static bool entry_reassign_name(struct dircache_entry *ce, 931static int entry_reassign_name(struct dircache_entry *ce,
926 const unsigned char *newname) 932 const unsigned char *newname)
927{ 933{
928 size_t oldlen = ce->tinyname ? 0 : ce->length; 934 size_t oldlen = ce->tinyname ? 0 : CE_NAMESIZE(ce->namelen);
929 size_t newlen = strlen(newname); 935 size_t newlen = strlen(newname);
930 936
931 if (oldlen == newlen || (oldlen == 0 && newlen <= MAX_TINYNAME)) 937 if (oldlen == newlen || (oldlen == 0 && newlen <= MAX_TINYNAME))
@@ -934,7 +940,7 @@ static bool entry_reassign_name(struct dircache_entry *ce,
934 newname, newlen); 940 newname, newlen);
935 if (newlen < MAX_TINYNAME) 941 if (newlen < MAX_TINYNAME)
936 *p = '\0'; 942 *p = '\0';
937 return true; 943 return 0;
938 } 944 }
939 945
940 /* needs a new name allocation; if the new name fits in the freed block, 946 /* needs a new name allocation; if the new name fits in the freed block,
@@ -968,7 +974,7 @@ static int alloc_entry(struct dircache_entry **res)
968 { 974 {
969 dircache.last_size = 0; 975 dircache.last_size = 0;
970 *res = NULL; 976 *res = NULL;
971 return 0; 977 return -ENOSPC;
972 } 978 }
973 979
974 dircache.sizeused += ENTRYSIZE; 980 dircache.sizeused += ENTRYSIZE;
@@ -976,7 +982,7 @@ static int alloc_entry(struct dircache_entry **res)
976 ce->next = 0; 982 ce->next = 0;
977 ce->up = 0; 983 ce->up = 0;
978 ce->down = 0; 984 ce->down = 0;
979 ce->length = 0; 985 ce->tinyname = 1;
980 ce->frontier = FRONTIER_SETTLED; 986 ce->frontier = FRONTIER_SETTLED;
981 ce->serialnum = next_serialnum(); 987 ce->serialnum = next_serialnum();
982 988
@@ -1030,19 +1036,20 @@ static int create_entry(const char *basename,
1030 struct dircache_entry **res) 1036 struct dircache_entry **res)
1031{ 1037{
1032 int idx = alloc_entry(res); 1038 int idx = alloc_entry(res);
1033 if (!idx)
1034 {
1035 logf("size limit reached (entry)");
1036 return 0; /* full */
1037 }
1038 1039
1039 if (!entry_assign_name(*res, basename, strlen(basename))) 1040 if (idx > 0)
1040 { 1041 {
1041 free_orphan_entry(NULL, *res, idx); 1042 int rc = entry_assign_name(*res, basename, strlen(basename));
1042 logf("size limit reached (name)"); 1043 if (rc < 0)
1043 return 0; /* really full! */ 1044 {
1045 free_orphan_entry(NULL, *res, idx);
1046 idx = rc;
1047 }
1044 } 1048 }
1045 1049
1050 if (idx == -ENOSPC)
1051 logf("size limit reached");
1052
1046 return idx; 1053 return idx;
1047} 1054}
1048 1055
@@ -1304,8 +1311,15 @@ static void sab_process_sub(struct sab *sabp)
1304 } 1311 }
1305 1312
1306 int idx = create_entry(fatentp->name, &ce); 1313 int idx = create_entry(fatentp->name, &ce);
1307 if (!idx) 1314 if (idx <= 0)
1308 { 1315 {
1316 if (idx == -ENAMETOOLONG)
1317 {
1318 /* not fatal; just don't include it */
1319 establish_frontier(compp->idx, FRONTIER_ZONED);
1320 continue;
1321 }
1322
1309 sabp->quit = true; 1323 sabp->quit = true;
1310 break; 1324 break;
1311 } 1325 }
@@ -2328,7 +2342,7 @@ void dircache_fileop_create(struct file_base_info *dirinfop,
2328 2342
2329 struct dircache_entry *ce; 2343 struct dircache_entry *ce;
2330 int idx = create_entry(basename, &ce); 2344 int idx = create_entry(basename, &ce);
2331 if (idx == 0) 2345 if (idx <= 0)
2332 { 2346 {
2333 /* failed allocation; parent cache contents are not complete */ 2347 /* failed allocation; parent cache contents are not complete */
2334 establish_frontier(dirinfop->dcfile.idx, FRONTIER_ZONED); 2348 establish_frontier(dirinfop->dcfile.idx, FRONTIER_ZONED);
@@ -2427,7 +2441,7 @@ void dircache_fileop_rename(struct file_base_info *dirinfop,
2427 insert_file_entry(dirinfop, ce); 2441 insert_file_entry(dirinfop, ce);
2428 2442
2429 /* lastly, update the entry name itself */ 2443 /* lastly, update the entry name itself */
2430 if (entry_reassign_name(ce, basename)) 2444 if (entry_reassign_name(ce, basename) == 0)
2431 { 2445 {
2432 /* it's not really the same one now so re-stamp it */ 2446 /* it's not really the same one now so re-stamp it */
2433 dc_serial_t serialnum = next_serialnum(); 2447 dc_serial_t serialnum = next_serialnum();
@@ -2506,7 +2520,7 @@ static ssize_t get_path_sub(int idx, struct get_path_sub_data *data)
2506 if (len < 0) 2520 if (len < 0)
2507 return -2; 2521 return -2;
2508 2522
2509 cename = alloca(MAX_NAME + 1); 2523 cename = alloca(DC_MAX_NAME + 1);
2510 entry_name_copy(cename, ce); 2524 entry_name_copy(cename, ce);
2511 } 2525 }
2512 else /* idx < 0 */ 2526 else /* idx < 0 */
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
index 8fee802f6f..a109563092 100644
--- a/firmware/common/file_internal.c
+++ b/firmware/common/file_internal.c
@@ -505,7 +505,7 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
505 if (!(compp->attr & ATTR_DIRECTORY)) 505 if (!(compp->attr & ATTR_DIRECTORY))
506 return -ENOTDIR; 506 return -ENOTDIR;
507 507
508 if (len >= MAX_NAME) 508 if (len > MAX_COMPNAME)
509 return -ENAMETOOLONG; 509 return -ENAMETOOLONG;
510 510
511 /* check for "." and ".." */ 511 /* check for "." and ".." */
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index bb1236aed1..5893737833 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -56,7 +56,7 @@
56 root + 'Music' + 'Artist' + 'Album' + 'Disc N' + filename */ 56 root + 'Music' + 'Artist' + 'Album' + 'Disc N' + filename */
57#define STATIC_PATHCOMP_NUM 6 57#define STATIC_PATHCOMP_NUM 6
58 58
59#define MAX_NAME 255 59#define MAX_COMPNAME 260
60 60
61/* unsigned value that will also hold the off_t range we need without 61/* unsigned value that will also hold the off_t range we need without
62 overflow */ 62 overflow */