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.c231
1 files changed, 168 insertions, 63 deletions
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 92eba42f65..56c408e02c 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -47,6 +47,7 @@
47#endif 47#endif
48#include "rbpaths.h" 48#include "rbpaths.h"
49 49
50
50/* Queue commands. */ 51/* Queue commands. */
51#define DIRCACHE_BUILD 1 52#define DIRCACHE_BUILD 1
52#define DIRCACHE_STOP 2 53#define DIRCACHE_STOP 2
@@ -57,9 +58,30 @@
57#define MAX_OPEN_DIRS 8 58#define MAX_OPEN_DIRS 8
58#endif 59#endif
59static DIR_CACHED opendirs[MAX_OPEN_DIRS]; 60static DIR_CACHED opendirs[MAX_OPEN_DIRS];
60 61/* Cache Layout:
62 *
63 * x - array of struct dircache_entry
64 * r - reserved buffer
65 * d - name buffer for the d_name entry of the struct dircache_entry
66 * |xxxxxx|rrrrrrrrr|dddddd|
67 *
68 * subsequent x are allocated from the front, d are allocated from the back,
69 * using the reserve buffer for entries added after initial scan
70 *
71 * after a while the cache may look like:
72 * |xxxxxxxx|rrrrr|dddddddd|
73 *
74 * after a reboot, the reserve buffer is restored in it's size, so that the
75 * total allocation size grows
76 * |xxxxxxxx|rrrrrrrrr|dddddddd|
77 */
61static struct dircache_entry *fd_bindings[MAX_OPEN_FILES]; 78static struct dircache_entry *fd_bindings[MAX_OPEN_FILES];
79/* this points to the beginnging of the buffer and the first entry */
62static struct dircache_entry *dircache_root; 80static struct dircache_entry *dircache_root;
81/* these point to the start and end of the name buffer (d above) */
82static char *d_names_start, *d_names_end;
83/* put "." and ".." into the d_names buffer to enable easy pointer logic */
84static char *dot, *dotdot;
63#ifdef HAVE_MULTIVOLUME 85#ifdef HAVE_MULTIVOLUME
64static struct dircache_entry *append_position; 86static struct dircache_entry *append_position;
65#endif 87#endif
@@ -116,20 +138,12 @@ static struct dircache_entry* allocate_entry(void)
116 return NULL; 138 return NULL;
117 } 139 }
118 140
119 next_entry = (struct dircache_entry *)((char *)dircache_root+dircache_size); 141 next_entry = &dircache_root[entry_count++];
120#ifdef ROCKBOX_STRICT_ALIGN
121 /* Make sure the entry is long aligned. */
122 if ((long)next_entry & 0x03)
123 {
124 dircache_size += 4 - ((long)next_entry & 0x03);
125 next_entry = (struct dircache_entry *)(((long)next_entry & ~0x03) + 0x04);
126 }
127#endif
128 next_entry->d_name = NULL; 142 next_entry->d_name = NULL;
129 next_entry->up = NULL; 143 next_entry->up = NULL;
130 next_entry->down = NULL; 144 next_entry->down = NULL;
131 next_entry->next = NULL; 145 next_entry->next = NULL;
132 146
133 dircache_size += sizeof(struct dircache_entry); 147 dircache_size += sizeof(struct dircache_entry);
134 148
135 return next_entry; 149 return next_entry;
@@ -230,8 +244,9 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
230 if(!strcmp(".", sab.direntry->name) || 244 if(!strcmp(".", sab.direntry->name) ||
231 !strcmp("..", sab.direntry->name)) 245 !strcmp("..", sab.direntry->name))
232 continue; 246 continue;
233 247
234 ce->d_name = ((char *)dircache_root + dircache_size); 248 size_t size = strlen(sab.direntry->name) + 1;
249 ce->d_name = (d_names_start -= size);
235 ce->startcluster = sab.direntry->firstcluster; 250 ce->startcluster = sab.direntry->firstcluster;
236 ce->info.size = sab.direntry->filesize; 251 ce->info.size = sab.direntry->filesize;
237 ce->info.attribute = sab.direntry->attr; 252 ce->info.attribute = sab.direntry->attr;
@@ -239,9 +254,7 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
239 ce->info.wrttime = sab.direntry->wrttime; 254 ce->info.wrttime = sab.direntry->wrttime;
240 255
241 strcpy(ce->d_name, sab.direntry->name); 256 strcpy(ce->d_name, sab.direntry->name);
242 dircache_size += strlen(ce->d_name) + 1; 257 dircache_size += size;
243
244 entry_count++;
245 258
246 if(ce->info.attribute & FAT_ATTR_DIRECTORY) 259 if(ce->info.attribute & FAT_ATTR_DIRECTORY)
247 dircache_gen_down(ce); 260 dircache_gen_down(ce);
@@ -261,7 +274,7 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
261 } 274 }
262 275
263 /* add "." and ".." */ 276 /* add "." and ".." */
264 ce->d_name = "."; 277 ce->d_name = dot;
265 ce->info.attribute = FAT_ATTR_DIRECTORY; 278 ce->info.attribute = FAT_ATTR_DIRECTORY;
266 ce->startcluster = startcluster; 279 ce->startcluster = startcluster;
267 ce->info.size = 0; 280 ce->info.size = 0;
@@ -269,7 +282,7 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
269 282
270 ce = dircache_gen_next(ce); 283 ce = dircache_gen_next(ce);
271 284
272 ce->d_name = ".."; 285 ce->d_name = dotdot;
273 ce->info.attribute = FAT_ATTR_DIRECTORY; 286 ce->info.attribute = FAT_ATTR_DIRECTORY;
274 ce->startcluster = (first_ce->up ? first_ce->up->startcluster : 0); 287 ce->startcluster = (first_ce->up ? first_ce->up->startcluster : 0);
275 ce->info.size = 0; 288 ce->info.size = 0;
@@ -300,7 +313,7 @@ static int dircache_scan_and_build(IF_MV2(int volume,) struct dircache_entry *ce
300#ifdef HAVE_MULTIVOLUME 313#ifdef HAVE_MULTIVOLUME
301 if (volume > 0) 314 if (volume > 0)
302 { 315 {
303 ce->d_name = ((char *)dircache_root+dircache_size); 316 ce->d_name = (d_names_start -= sizeof(VOL_NAMES));
304 size_t len = snprintf(ce->d_name, VOL_ENUM_POS + 3, VOL_NAMES, volume)+1; 317 size_t len = snprintf(ce->d_name, VOL_ENUM_POS + 3, VOL_NAMES, volume)+1;
305 dircache_size += len; 318 dircache_size += len;
306 ce->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME; 319 ce->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
@@ -338,14 +351,13 @@ static int sab_process_dir(struct dircache_entry *ce)
338 if(!strcmp(".", entry->d_name) || 351 if(!strcmp(".", entry->d_name) ||
339 !strcmp("..", entry->d_name)) 352 !strcmp("..", entry->d_name))
340 continue; 353 continue;
341 354
342 ce->d_name = ((char *)dircache_root + dircache_size); 355 size_t size = strlen(entry->d_name) + 1;
356 ce->d_name = (d_names_start -= size);
343 ce->info = entry->info; 357 ce->info = entry->info;
344 358
345 strcpy(ce->d_name, entry->d_name); 359 strcpy(ce->d_name, entry->d_name);
346 dircache_size += strlen(entry->d_name) + 1; 360 dircache_size += size;
347
348 entry_count++;
349 361
350 if(entry->info.attribute & ATTR_DIRECTORY) 362 if(entry->info.attribute & ATTR_DIRECTORY)
351 { 363 {
@@ -387,14 +399,14 @@ static int sab_process_dir(struct dircache_entry *ce)
387 } 399 }
388 400
389 /* add "." and ".." */ 401 /* add "." and ".." */
390 ce->d_name = "."; 402 ce->d_name = dot;
391 ce->info.attribute = ATTR_DIRECTORY; 403 ce->info.attribute = ATTR_DIRECTORY;
392 ce->info.size = 0; 404 ce->info.size = 0;
393 ce->down = first_ce; 405 ce->down = first_ce;
394 406
395 ce = dircache_gen_next(ce); 407 ce = dircache_gen_next(ce);
396 408
397 ce->d_name = ".."; 409 ce->d_name = dotdot;
398 ce->info.attribute = ATTR_DIRECTORY; 410 ce->info.attribute = ATTR_DIRECTORY;
399 ce->info.size = 0; 411 ce->info.size = 0;
400 ce->down = first_ce->up; 412 ce->down = first_ce->up;
@@ -498,7 +510,7 @@ static struct dircache_entry* dircache_get_entry(const char *path, bool go_down)
498int dircache_load(void) 510int dircache_load(void)
499{ 511{
500 struct dircache_maindata maindata; 512 struct dircache_maindata maindata;
501 int bytes_read; 513 ssize_t bytes_read;
502 int fd; 514 int fd;
503 515
504 if (dircache_initialized) 516 if (dircache_initialized)
@@ -520,29 +532,65 @@ int dircache_load(void)
520 remove_dircache_file(); 532 remove_dircache_file();
521 return -3; 533 return -3;
522 } 534 }
523
524 dircache_root = buffer_alloc(0);
525 if ((long)maindata.root_entry != (long)dircache_root)
526 {
527 logf("Position missmatch");
528 close(fd);
529 remove_dircache_file();
530 return -4;
531 }
532 535
533 dircache_root = buffer_alloc(maindata.size + DIRCACHE_RESERVE); 536 dircache_root = buffer_alloc(maindata.size + DIRCACHE_RESERVE);
534 entry_count = maindata.entry_count; 537 entry_count = maindata.entry_count;
535 appflags = maindata.appflags; 538 appflags = maindata.appflags;
536 bytes_read = read(fd, dircache_root, MIN(DIRCACHE_LIMIT, maindata.size)); 539
537 close(fd); 540 /* read the dircache file into memory,
538 remove_dircache_file(); 541 * start with the struct dircache_entries */
542 ssize_t bytes_to_read = entry_count*sizeof(struct dircache_entry);
543 bytes_read = read(fd, dircache_root, bytes_to_read);
539 544
540 if (bytes_read != maindata.size) 545 if (bytes_read != bytes_to_read)
541 { 546 {
542 logf("Dircache read failed"); 547 logf("Dircache read failed #1");
543 return -6; 548 return -6;
544 } 549 }
545 550
551 /* continue with the d_names. Fix up pointers to them if needed */
552 d_names_start = ((char*)&dircache_root[entry_count] + DIRCACHE_RESERVE);
553 bytes_to_read = maindata.size - bytes_to_read;
554 bytes_read = read(fd, d_names_start, bytes_to_read);
555 close(fd);
556 remove_dircache_file();
557 if (bytes_read != bytes_to_read)
558 {
559 logf("Dircache read failed #2");
560 return -7;
561 }
562
563 d_names_end = &d_names_start[bytes_read];
564 dot = d_names_end - sizeof(".");
565 dotdot = dot - sizeof("..");
566
567 /* d_names are in reverse order, so the last entry points to the first string */
568 intptr_t offset_d_names = maindata.d_names_start - d_names_start;
569 intptr_t offset_entries = maindata.root_entry - dircache_root;
570
571 /* offset_entries is less likely to differ, so check if it's 0 in the loop
572 * offset_d_names however is almost always non-zero, since dircache_save()
573 * creates a file which causes the reserve buffer to be used. since
574 * we allocate a new, empty DIRCACHE_RESERVE here, the strings are
575 * farther behind */
576 if (offset_entries != 0 || offset_d_names != 0)
577 {
578 for(unsigned i = 0; i < entry_count; i++)
579 {
580 if (dircache_root[i].d_name)
581 dircache_root[i].d_name -= offset_d_names;
582
583 if (offset_entries == 0)
584 continue;
585 if (dircache_root[i].next)
586 dircache_root[i].next -= offset_entries;
587 if (dircache_root[i].up)
588 dircache_root[i].up -= offset_entries;
589 if (dircache_root[i].down)
590 dircache_root[i].down -= offset_entries;
591 }
592 }
593
546 /* Cache successfully loaded. */ 594 /* Cache successfully loaded. */
547 dircache_size = maindata.size; 595 dircache_size = maindata.size;
548 allocated_size = dircache_size + DIRCACHE_RESERVE; 596 allocated_size = dircache_size + DIRCACHE_RESERVE;
@@ -575,6 +623,7 @@ int dircache_save(void)
575 maindata.magic = DIRCACHE_MAGIC; 623 maindata.magic = DIRCACHE_MAGIC;
576 maindata.size = dircache_size; 624 maindata.size = dircache_size;
577 maindata.root_entry = dircache_root; 625 maindata.root_entry = dircache_root;
626 maindata.d_names_start = d_names_start;
578 maindata.entry_count = entry_count; 627 maindata.entry_count = entry_count;
579 maindata.appflags = appflags; 628 maindata.appflags = appflags;
580 629
@@ -587,14 +636,26 @@ int dircache_save(void)
587 return -2; 636 return -2;
588 } 637 }
589 638
590 /* Dump whole directory cache to disk */ 639 /* Dump whole directory cache to disk
591 bytes_written = write(fd, dircache_root, dircache_size); 640 * start by writing the dircache_entries */
592 close(fd); 641 size_t bytes_to_write = entry_count*sizeof(struct dircache_entry);
593 if (bytes_written != dircache_size) 642 bytes_written = write(fd, dircache_root, bytes_to_write);
643 if (bytes_written != bytes_to_write)
594 { 644 {
595 logf("dircache: write failed #2"); 645 logf("dircache: write failed #2");
596 return -3; 646 return -3;
597 } 647 }
648
649 /* continue with the d_names */
650 bytes_to_write = d_names_end - d_names_start;
651 bytes_written = write(fd, d_names_start, bytes_to_write);
652 close(fd);
653 if (bytes_written != bytes_to_write)
654 {
655 logf("dircache: write failed #3");
656 return -4;
657 }
658
598 659
599 return 0; 660 return 0;
600} 661}
@@ -605,6 +666,7 @@ int dircache_save(void)
605 */ 666 */
606static int dircache_do_rebuild(void) 667static int dircache_do_rebuild(void)
607{ 668{
669 struct dircache_entry* root_entry;
608 unsigned int start_tick; 670 unsigned int start_tick;
609 int i; 671 int i;
610 672
@@ -612,12 +674,13 @@ static int dircache_do_rebuild(void)
612 start_tick = current_tick; 674 start_tick = current_tick;
613 dircache_initializing = true; 675 dircache_initializing = true;
614 appflags = 0; 676 appflags = 0;
677
678 /* reset dircache and alloc root entry */
615 entry_count = 0; 679 entry_count = 0;
616 680 root_entry = allocate_entry();
617 dircache_size = sizeof(struct dircache_entry);
618 681
619#ifdef HAVE_MULTIVOLUME 682#ifdef HAVE_MULTIVOLUME
620 append_position = dircache_root; 683 append_position = root_entry;
621 684
622 for (i = NUM_VOLUMES; i >= 0; i--) 685 for (i = NUM_VOLUMES; i >= 0; i--)
623 { 686 {
@@ -628,7 +691,7 @@ static int dircache_do_rebuild(void)
628#ifdef HAVE_MULTIVOLUME 691#ifdef HAVE_MULTIVOLUME
629 if (dircache_scan_and_build(IF_MV2(i,) append_position) < 0) 692 if (dircache_scan_and_build(IF_MV2(i,) append_position) < 0)
630#else 693#else
631 if (dircache_scan_and_build(IF_MV2(0,) dircache_root) < 0) 694 if (dircache_scan_and_build(IF_MV2(0,) root_entry) < 0)
632#endif /* HAVE_MULTIVOLUME */ 695#endif /* HAVE_MULTIVOLUME */
633 { 696 {
634 logf("dircache_scan_and_build failed"); 697 logf("dircache_scan_and_build failed");
@@ -660,14 +723,6 @@ static int dircache_do_rebuild(void)
660 if (allocated_size - dircache_size < DIRCACHE_RESERVE) 723 if (allocated_size - dircache_size < DIRCACHE_RESERVE)
661 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); 724 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size);
662 } 725 }
663 else
664 {
665 /* We have to long align the audiobuf to keep the buffer access fast. */
666 audiobuf += (long)((dircache_size & ~0x03) + 0x04);
667 audiobuf += DIRCACHE_RESERVE;
668 allocated_size = dircache_size + DIRCACHE_RESERVE;
669 reserve_used = 0;
670 }
671 726
672 return 1; 727 return 1;
673} 728}
@@ -712,6 +767,14 @@ static void dircache_thread(void)
712 } 767 }
713} 768}
714 769
770static void generate_dot_d_names(void)
771{
772 dot = (d_names_start -= sizeof("."));
773 dotdot = (d_names_start -= sizeof(".."));
774 dircache_size += sizeof(".") + sizeof("..");
775 strcpy(dot, ".");
776 strcpy(dotdot, "..");
777}
715/** 778/**
716 * Start scanning the disk to build the dircache. 779 * Start scanning the disk to build the dircache.
717 * Either transparent or non-transparent build method is used. 780 * Either transparent or non-transparent build method is used.
@@ -734,22 +797,63 @@ int dircache_build(int last_size)
734 queue_post(&dircache_queue, DIRCACHE_BUILD, 0); 797 queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
735 return 2; 798 return 2;
736 } 799 }
737
738 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) 800 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
739 { 801 {
740 allocated_size = last_size + DIRCACHE_RESERVE; 802 allocated_size = last_size + DIRCACHE_RESERVE;
741 dircache_root = buffer_alloc(allocated_size); 803 dircache_root = buffer_alloc(allocated_size);
804 d_names_start =
805 d_names_end = ((char*)dircache_root)+allocated_size-1;
806 dircache_size = 0;
742 thread_enabled = true; 807 thread_enabled = true;
808 generate_dot_d_names();
743 809
744 /* Start a transparent rebuild. */ 810 /* Start a transparent rebuild. */
745 queue_post(&dircache_queue, DIRCACHE_BUILD, 0); 811 queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
746 return 3; 812 return 3;
747 } 813 }
748 814
749 dircache_root = (struct dircache_entry *)(((long)audiobuf & ~0x03) + 0x04); 815 /* struct dircache_entrys are allocated from the beginning,
816 * their corresponding d_name from the end
817 * after generation the buffer will be compacted with DIRCACHE_RESERVE
818 * free bytes in between */
819 audiobuf = (char*)(((intptr_t)audiobuf & ~0x03) + 0x04);
820 dircache_root = (struct dircache_entry*)audiobuf;
821 d_names_start = d_names_end = audiobufend - 1;
822 dircache_size = 0;
823 generate_dot_d_names();
750 824
751 /* Start a non-transparent rebuild. */ 825 /* Start a non-transparent rebuild. */
752 return dircache_do_rebuild(); 826 int res = dircache_do_rebuild();
827
828 /** compact the dircache buffer **/
829 if (res >= 0)
830 {
831 char* dst = ((char*)&dircache_root[entry_count] + DIRCACHE_RESERVE);
832 ssize_t offset = d_names_start - dst;
833 if (offset > 0)
834 {
835 ssize_t size_to_move = dircache_size -
836 entry_count*sizeof(struct dircache_entry);
837 /* move d_names down, use memmove if overlap */
838 if (offset > size_to_move)
839 memcpy(dst, d_names_start, size_to_move);
840 else
841 memmove(dst, d_names_start, size_to_move);
842
843 /* fix up pointers to the d_names */
844 for(unsigned i = 0; i < entry_count; i++)
845 dircache_root[i].d_name -= offset;
846
847 d_names_end -= offset;
848 /* equivalent to dircache_size + DIRCACHE_RESERVE */
849 allocated_size = (d_names_end - (char*)dircache_root);
850 reserve_used = 0;
851 audiobuf += allocated_size;
852 }
853 else /* something went wrong */
854 return -1;
855 }
856 return res;
753} 857}
754 858
755/** 859/**
@@ -1008,14 +1112,15 @@ static struct dircache_entry* dircache_new_entry(const char *path, int attribute
1008 return NULL; 1112 return NULL;
1009 } 1113 }
1010 } 1114 }
1011 1115
1012 entry->d_name = ((char *)dircache_root+dircache_size); 1116 size_t size = strlen(new) + 1;
1117 entry->d_name = (d_names_start -= size);
1013 entry->startcluster = 0; 1118 entry->startcluster = 0;
1014 memset(&entry->info, 0, sizeof(entry->info)); 1119 memset(&entry->info, 0, sizeof(entry->info));
1015 entry->info.attribute = attribute; 1120 entry->info.attribute = attribute;
1016 1121
1017 strcpy(entry->d_name, new); 1122 strcpy(entry->d_name, new);
1018 dircache_size += strlen(entry->d_name) + 1; 1123 dircache_size += size;
1019 1124
1020 if (attribute & ATTR_DIRECTORY) 1125 if (attribute & ATTR_DIRECTORY)
1021 { 1126 {