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.c351
1 files changed, 178 insertions, 173 deletions
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index b93ee73fc6..a3538ff96f 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -41,9 +41,7 @@
41#include "audio.h" 41#include "audio.h"
42#include "rbpaths.h" 42#include "rbpaths.h"
43#include "linked_list.h" 43#include "linked_list.h"
44#ifdef HAVE_EEPROM_SETTINGS
45#include "crc32.h" 44#include "crc32.h"
46#endif
47 45
48/** 46/**
49 * Cache memory layout: 47 * Cache memory layout:
@@ -1457,7 +1455,7 @@ int dircache_readdir_dirent(struct filestr_base *stream,
1457 unsigned int direntry = scanp->fatscan.entry; 1455 unsigned int direntry = scanp->fatscan.entry;
1458 while (1) 1456 while (1)
1459 { 1457 {
1460 if (idx == 0 || direntry == FAT_RW_VAL) /* rewound? */ 1458 if (idx == 0 || direntry == FAT_DIRSCAN_RW_VAL) /* rewound? */
1461 { 1459 {
1462 idx = diridx <= 0 ? dcvolp->root_down : get_entry(diridx)->down; 1460 idx = diridx <= 0 ? dcvolp->root_down : get_entry(diridx)->down;
1463 break; 1461 break;
@@ -1487,7 +1485,7 @@ int dircache_readdir_dirent(struct filestr_base *stream,
1487 return 0; /* end of dir */ 1485 return 0; /* end of dir */
1488 } 1486 }
1489 1487
1490 if (ce->direntry > direntry || direntry == FAT_RW_VAL) 1488 if (ce->direntry > direntry || direntry == FAT_DIRSCAN_RW_VAL)
1491 break; /* cache reader is caught up to FS scan */ 1489 break; /* cache reader is caught up to FS scan */
1492 1490
1493 idx = ce->next; 1491 idx = ce->next;
@@ -1549,7 +1547,12 @@ int dircache_readdir_internal(struct filestr_base *stream,
1549 1547
1550 /* is parent cached? if not, readthrough because nothing is here yet */ 1548 /* is parent cached? if not, readthrough because nothing is here yet */
1551 if (!dirinfop->dcfile.serialnum) 1549 if (!dirinfop->dcfile.serialnum)
1550 {
1551 if (stream->flags & FF_CACHEONLY)
1552 goto read_eod;
1553
1552 return uncached_readdir_internal(stream, infop, fatent); 1554 return uncached_readdir_internal(stream, infop, fatent);
1555 }
1553 1556
1554 int diridx = dirinfop->dcfile.idx; 1557 int diridx = dirinfop->dcfile.idx;
1555 unsigned int frontier = diridx < 0 ? 1558 unsigned int frontier = diridx < 0 ?
@@ -1562,7 +1565,8 @@ int dircache_readdir_internal(struct filestr_base *stream,
1562 idx = get_entry(idx)->next; 1565 idx = get_entry(idx)->next;
1563 1566
1564 struct dircache_entry *ce = get_entry(idx); 1567 struct dircache_entry *ce = get_entry(idx);
1565 if (frontier != FRONTIER_SETTLED) 1568
1569 if (frontier != FRONTIER_SETTLED && !(stream->flags & FF_CACHEONLY))
1566 { 1570 {
1567 /* the directory being read is reported to be incompletely cached; 1571 /* the directory being read is reported to be incompletely cached;
1568 readthrough and if the entry exists, return it with its binding 1572 readthrough and if the entry exists, return it with its binding
@@ -1577,9 +1581,7 @@ int dircache_readdir_internal(struct filestr_base *stream,
1577 else if (!ce) 1581 else if (!ce)
1578 { 1582 {
1579 /* end of dir */ 1583 /* end of dir */
1580 fat_empty_fat_direntry(fatent); 1584 goto read_eod;
1581 infop->fatfile.e.entries = 0;
1582 return 0;
1583 } 1585 }
1584 1586
1585 /* FS entry information that we maintain */ 1587 /* FS entry information that we maintain */
@@ -1612,6 +1614,11 @@ int dircache_readdir_internal(struct filestr_base *stream,
1612 } 1614 }
1613 1615
1614 return rc; 1616 return rc;
1617
1618read_eod:
1619 fat_empty_fat_direntry(fatent);
1620 infop->fatfile.e.entries = 0;
1621 return 0;
1615} 1622}
1616 1623
1617/** 1624/**
@@ -2451,30 +2458,41 @@ void dircache_fileop_sync(struct file_base_binding *bindp,
2451 2458
2452/** Dircache paths and files **/ 2459/** Dircache paths and files **/
2453 2460
2454#ifdef DIRCACHE_DUMPSTER 2461/**
2455/* helper for dircache_get_path() */ 2462 * helper for returning a path and serial hash represented by an index
2456static ssize_t get_path_sub(int idx, char *buf, size_t size) 2463 */
2464struct get_path_sub_data
2465{
2466 char *buf;
2467 size_t size;
2468 dc_serial_t serialhash;
2469};
2470
2471static ssize_t get_path_sub(int idx, struct get_path_sub_data *data)
2457{ 2472{
2458 if (idx == 0) 2473 if (idx == 0)
2459 return -2; /* entry is an orphan split from any root */ 2474 return -1; /* entry is an orphan split from any root */
2460 2475
2461 ssize_t offset; 2476 ssize_t len;
2462 char *cename; 2477 char *cename;
2463 2478
2464 if (idx > 0) 2479 if (idx > 0)
2465 { 2480 {
2466 /* go all the way up then move back down from the root */
2467 struct dircache_entry *ce = get_entry(idx); 2481 struct dircache_entry *ce = get_entry(idx);
2468 offset = get_path_sub(ce->up, buf, size) - 1; 2482
2469 if (offset < 0) 2483 data->serialhash = dc_hash_serialnum(ce->serialnum, data->serialhash);
2470 return -3; 2484
2485 /* go all the way up then move back down from the root */
2486 len = get_path_sub(ce->up, data) - 1;
2487 if (len < 0)
2488 return -2;
2471 2489
2472 cename = alloca(MAX_NAME + 1); 2490 cename = alloca(MAX_NAME + 1);
2473 entry_name_copy(cename, ce); 2491 entry_name_copy(cename, ce);
2474 } 2492 }
2475 else /* idx < 0 */ 2493 else /* idx < 0 */
2476 { 2494 {
2477 offset = 0; 2495 len = 0;
2478 cename = ""; 2496 cename = "";
2479 2497
2480 #ifdef HAVE_MULTIVOLUME 2498 #ifdef HAVE_MULTIVOLUME
@@ -2486,14 +2504,14 @@ static ssize_t get_path_sub(int idx, char *buf, size_t size)
2486 get_volume_name(volume, cename); 2504 get_volume_name(volume, cename);
2487 } 2505 }
2488 #endif /* HAVE_MULTIVOLUME */ 2506 #endif /* HAVE_MULTIVOLUME */
2507
2508 data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum,
2509 data->serialhash);
2489 } 2510 }
2490 2511
2491 return offset + path_append(buf + offset, PA_SEP_HARD, cename, 2512 return len + path_append(data->buf + len, PA_SEP_HARD, cename,
2492 size > (size_t)offset ? size - offset : 0); 2513 data->size > (size_t)len ? data->size - len : 0);
2493} 2514}
2494#endif /* DIRCACHE_DUMPSTER */
2495
2496#if 0
2497 2515
2498/** 2516/**
2499 * retrieve and validate the file's entry/binding serial number 2517 * retrieve and validate the file's entry/binding serial number
@@ -2523,201 +2541,173 @@ static dc_serial_t get_file_serialnum(const struct dircache_file *dcfilep)
2523} 2541}
2524 2542
2525/** 2543/**
2544 * Obtain the hash of the serial numbers of the canonical path, index to root
2545 */
2546static dc_serial_t get_file_serialhash(const struct dircache_file *dcfilep)
2547{
2548 int idx = dcfilep->idx;
2549
2550 dc_serial_t h = DC_SERHASH_START;
2551
2552 while (idx > 0)
2553 {
2554 struct dircache_entry *ce = get_entry(idx);
2555 h = dc_hash_serialnum(ce->serialnum, h);
2556 idx = ce->up;
2557 }
2558
2559 h = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum, h);
2560
2561 return h;
2562}
2563
2564/**
2565 * Initialize the fileref
2566 */
2567void dircache_fileref_init(struct dircache_fileref *dcfrefp)
2568{
2569 dircache_dcfile_init(&dcfrefp->dcfile);
2570 dcfrefp->serialhash = DC_SERHASH_START;
2571}
2572
2573/**
2526 * usermode function to construct a full absolute path from dircache into the 2574 * usermode function to construct a full absolute path from dircache into the
2527 * given buffer given the dircache file info 2575 * given buffer given the dircache file info
2528 * 2576 *
2529 * returns: 2577 * returns:
2530 * success - the length of the string, not including the trailing null 2578 * success - the length of the string, not including the trailing null or the
2579 * buffer length required if the buffer is too small (return is >=
2580 * size)
2531 * failure - a negative value 2581 * failure - a negative value
2532 * 2582 *
2533 * successful return value is as strlcpy()
2534 *
2535 * errors: 2583 * errors:
2536 * ENOENT - the file or directory does not exist 2584 * ENOENT - No such file or directory
2537 */ 2585 */
2538ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf, 2586ssize_t dircache_get_fileref_path(const struct dircache_fileref *dcfrefp, char *buf,
2539 size_t size) 2587 size_t size)
2540{ 2588{
2589 ssize_t rc;
2590
2541 /* if missing buffer space, still return what's needed a la strlcpy */ 2591 /* if missing buffer space, still return what's needed a la strlcpy */
2542 if (!buf) 2592 if (!buf)
2543 size = 0; 2593 size = 0;
2544 else if (size) 2594 else if (size)
2545 *buf = '\0'; 2595 *buf = '\0';
2546 2596
2547 ssize_t len = -1;
2548
2549 dircache_lock(); 2597 dircache_lock();
2550 2598
2551 /* first and foremost, there must be a cache and the serial number must 2599 /* first and foremost, there must be a cache and the serial number must
2552 check out */ 2600 check out */
2553 if (dircache_runinfo.handle && get_file_serialnum(dcfilep)) 2601 if (!dircache_runinfo.handle)
2554 len = get_path_sub(dcfilep->idx, buf, size); 2602 FILE_ERROR(ENOENT, -1);
2555
2556 if (len < 0)
2557 errno = ENOENT;
2558
2559 dircache_unlock();
2560 return len;
2561}
2562 2603
2563/** 2604 if (get_file_serialnum(&dcfrefp->dcfile) == 0)
2564 * searches the sublist starting at 'idx' for the named component 2605 FILE_ERROR(ENOENT, -2);
2565 */
2566 2606
2567/* helper for get_file_sub() */ 2607 struct get_path_sub_data data =
2568static struct dircache_entry *
2569get_file_sub_scan(int idx, const char *name, size_t length, int *idxp)
2570{
2571 struct dircache_entry *ce = get_entry(idx);
2572 if (ce)
2573 { 2608 {
2574 char entname[MAX_NAME+1]; 2609 .buf = buf,
2575 name = strmemdupa(name, length); 2610 .size = size,
2576 2611 .serialhash = DC_SERHASH_START,
2577 do 2612 };
2578 {
2579 entry_name_copy(entname, ce);
2580 if (!strcasecmp(entname, name))
2581 {
2582 *idxp = idx;
2583 break;
2584 }
2585
2586 idx = ce->next;
2587 }
2588 while ((ce = get_entry(idx)));
2589 }
2590
2591 return ce;
2592}
2593
2594/**
2595 * searches for the subcomponent of *pathp
2596 */
2597
2598/* helper for dircache_get_file() */
2599static int get_file_sub(const char **pathp, int *downp, int *idxp)
2600{
2601 int rc;
2602 const char *name;
2603 rc = parse_path_component(pathp, &name, false);
2604 if (rc <= 0)
2605 return rc;
2606 else if (rc >= MAX_PATH)
2607 return ENAMETOOLONG; /* that's just unpossible, man */
2608 2613
2609 struct dircache_entry *ce = get_file_sub_scan(*downp, name, rc, idxp); 2614 rc = get_path_sub(dcfrefp->dcfile.idx, &data);
2615 if (rc < 0)
2616 FILE_ERROR(ENOENT, rc * 10 - 3);
2610 2617
2611 if (!ce) 2618 if (data.serialhash != dcfrefp->serialhash)
2612 rc = RC_NOT_FOUND; /* not there; tellibry solly */ 2619 FILE_ERROR(ENOENT, -4);
2613 else if (!*pathp)
2614 rc = RC_PATH_ENDED; /* done */
2615 else if (!(ce->attr & ATTR_DIRECTORY))
2616 rc = ENOTDIR; /* a parent component must be a directory */
2617 else
2618 while ((rc = get_file_sub(pathp, &ce->down, idxp)) == RC_CONTINUE);
2619 2620
2620 switch (rc) 2621file_error:
2621 { 2622 dircache_unlock();
2622 case RC_GO_UP: /* hit ".."; drop to previous level */ 2623 return rc;
2623 return RC_CONTINUE;
2624 case RC_PATH_ENDED: /* success! */
2625 return RC_FOUND;
2626 default: /* component not found or error */
2627 return rc;
2628 }
2629} 2624}
2630 2625
2631/** 2626/**
2632 * usermode function to return dircache file info for the given path 2627 * Test a path to various levels of rigor and optionally return dircache file
2628 * info for the given path
2633 * 2629 *
2634 * returns: 2630 * returns:
2635 * success: the volume number that is specified for the file 2631 * success: 0
2636 * failure: a negative value 2632 * failure: a negative value
2637 * 2633 *
2638 * errors: 2634 * errors (including but not limited to):
2639 * ENOENT - the file or directory does not exist or path is empty 2635 * EFAULT - Bad address
2640 * ENAMETOOLONG - a component of the path is too long 2636 * EINVAL - Invalid argument
2641 * ENOTDIR - a component of the path is not a directory 2637 * ENAMETOOLONG - File or path name too long
2638 * ENOENT - No such file or directory
2639 * ENOTDIR - Not a directory
2640 * ENXIO - No such device or address
2642 */ 2641 */
2643int dircache_get_file(const char *path, struct dircache_file *dcfilep) 2642int dircache_search(unsigned int flags, struct dircache_fileref *dcfrefp, const char *path)
2644{ 2643{
2645 if (!path_is_absolute(path) || !dcfilep) 2644 int rc;
2646 { 2645
2647 errno = ENOENT; 2646 if (!(flags & (DCS_FILEREF | DCS_CACHED_PATH)))
2648 return -1; 2647 FILE_ERROR_RETURN(EINVAL, -1); /* search nothing? */
2649 }
2650 2648
2651 dircache_lock(); 2649 dircache_lock();
2652 2650
2653 if (!dircache_runinfo.handle) 2651 if (!dircache_runinfo.handle)
2652 FILE_ERROR(ENOENT, -2);
2653
2654 if (flags & DCS_FILEREF)
2654 { 2655 {
2655 dircache_unlock(); 2656 if (!dcfrefp)
2656 errno = ENOENT; 2657 FILE_ERROR(EFAULT, -3);
2657 return -2;
2658 }
2659 2658
2660 int volume = 0; 2659 if (get_file_serialnum(&dcfrefp->dcfile) != 0)
2661 int idx = 0; 2660 {
2662 dc_serial_t serialnum = 0; 2661 if (!(flags & _DCS_VERIFY_FLAG))
2663 struct dircache_volume *dcvolp = NULL; 2662 goto file_success; /* no robust verification wanted */
2664 struct dircache_entry *ce = NULL; 2663
2664 if (get_file_serialhash(&dcfrefp->dcfile) == dcfrefp->serialhash)
2665 goto file_success; /* reference is most likely still valid */
2666 }
2665 2667
2666 int rc = RC_GO_UP; 2668 if (!(flags & DCS_CACHED_PATH))
2669 FILE_ERROR(ENOENT, -4); /* no path search wanted */
2670 }
2667 2671
2668 while (rc == RC_CONTINUE || rc == RC_GO_UP) 2672 if (flags & DCS_CACHED_PATH)
2669 { 2673 {
2670 #ifdef HAVE_MULTIVOLUME 2674 const bool update = flags & DCS_UPDATE_FILEREF;
2671 if (rc == RC_GO_UP) 2675 struct path_component_info *compinfop = NULL;
2676
2677 if (update)
2672 { 2678 {
2673 volume = path_strip_volume(path, &path, false); 2679 if (!dcfrefp)
2674 if (!CHECK_VOL(volume)) 2680 FILE_ERROR(EFAULT, -5);
2675 {
2676 rc = ENXIO;
2677 break;
2678 }
2679 }
2680 #endif /* HAVE_MULTIVOLUME */
2681 2681
2682 dcvolp = DCVOL(volume); 2682 compinfop = alloca(sizeof (*compinfop));
2683 }
2683 2684
2684 int *downp = &dcvolp->root_down; 2685 struct filestr_base stream;
2685 if (*downp <= 0) 2686 rc = open_stream_internal(path, FF_ANYTYPE | FF_PROBE | FF_SELFINFO |
2687 ((flags & _DCS_STORAGE_FLAG) ? 0 : FF_CACHEONLY),
2688 &stream, compinfop);
2689 if (rc <= 0)
2686 { 2690 {
2687 rc = ENXIO; 2691 if (update)
2688 break; 2692 dircache_fileref_init(dcfrefp);
2693
2694 FILE_ERROR(rc ? ERRNO : ENOENT, rc * 10 - 6);
2689 } 2695 }
2690 2696
2691 rc = get_file_sub(&path, downp, &idx); 2697 if (update)
2692 } 2698 {
2693 2699 dcfrefp->dcfile = compinfop->info.dcfile;
2694 switch (rc) 2700 dcfrefp->serialhash = get_file_serialhash(&compinfop->info.dcfile);
2695 { 2701 }
2696 case RC_FOUND: /* hit: component found */
2697 serialnum = ce->serialnum;
2698 rc = volume;
2699 break;
2700 case RC_PATH_ENDED: /* hit: it's a root (volume or system) */
2701 idx = -volume - 1;
2702 serialnum = dcvolp->serialnum;
2703 rc = volume;
2704 break;
2705 case RC_NOT_FOUND: /* miss */
2706 rc = ENOENT;
2707 default:
2708 idx = 0;
2709 errno = rc;
2710 rc = -3;
2711 break;
2712 } 2702 }
2713 2703
2714 dcfilep->idx = idx; 2704file_success:
2715 dcfilep->serialnum = serialnum; 2705 rc = 0;
2716 2706
2707file_error:
2717 dircache_unlock(); 2708 dircache_unlock();
2718 return rc; 2709 return rc;
2719} 2710}
2720#endif /* 0 */
2721 2711
2722 2712
2723/** Debug screen/info stuff **/ 2713/** Debug screen/info stuff **/
@@ -2737,13 +2727,12 @@ void dircache_get_info(struct dircache_info *info)
2737 if (!info) 2727 if (!info)
2738 return; 2728 return;
2739 2729
2740 memset(info, 0, sizeof (*info));
2741
2742 dircache_lock(); 2730 dircache_lock();
2743 2731
2744 enum dircache_status status = DIRCACHE_IDLE; 2732 enum dircache_status status = DIRCACHE_IDLE;
2733 info->build_ticks = 0;
2745 2734
2746 for (unsigned int volume = 0; volume < NUM_VOLUMES; volume++) 2735 FOR_EACH_VOLUME(-1, volume)
2747 { 2736 {
2748 struct dircache_volume *dcvolp = DCVOL(volume); 2737 struct dircache_volume *dcvolp = DCVOL(volume);
2749 enum dircache_status volstatus = dcvolp->status; 2738 enum dircache_status volstatus = dcvolp->status;
@@ -2781,11 +2770,18 @@ void dircache_get_info(struct dircache_info *info)
2781 /* report usage only if there is something ready or being built */ 2770 /* report usage only if there is something ready or being built */
2782 if (status != DIRCACHE_IDLE) 2771 if (status != DIRCACHE_IDLE)
2783 { 2772 {
2784 info->reserve_used = reserve_buf_used();
2785 info->size = dircache.size; 2773 info->size = dircache.size;
2786 info->sizeused = dircache.sizeused; 2774 info->sizeused = dircache.sizeused;
2775 info->reserve_used = reserve_buf_used();
2787 info->entry_count = dircache.numentries; 2776 info->entry_count = dircache.numentries;
2788 } 2777 }
2778 else
2779 {
2780 info->size = 0;
2781 info->sizeused = 0;
2782 info->reserve_used = 0;
2783 info->entry_count = 0;
2784 }
2789 2785
2790 dircache_unlock(); 2786 dircache_unlock();
2791} 2787}
@@ -2817,7 +2813,7 @@ void dircache_dump(void)
2817 dircache_runinfo.bufsize + 1); 2813 dircache_runinfo.bufsize + 1);
2818 2814
2819 /* CSV */ 2815 /* CSV */
2820 fdprintf(fdcsv, "\"Index\",\"Serialnum\"," 2816 fdprintf(fdcsv, "\"Index\",\"Serialnum\",\"Serialhash\","
2821 "\"Path\",\"Frontier\"," 2817 "\"Path\",\"Frontier\","
2822 "\"Attribute\",\"File Size\"," 2818 "\"Attribute\",\"File Size\","
2823 "\"Mod Date\",\"Mod Time\"\n"); 2819 "\"Mod Date\",\"Mod Time\"\n");
@@ -2833,11 +2829,12 @@ void dircache_dump(void)
2833 get_volume_name(volume, name); 2829 get_volume_name(volume, name);
2834 #endif 2830 #endif
2835 fdprintf(fdcsv, 2831 fdprintf(fdcsv,
2836 "%d,%lu," 2832 "%d," DC_SERIAL_FMT "," DC_SERIAL_FMT ","
2837 "\"%c" IF_MV("%s") "\",%u," 2833 "\"%c" IF_MV("%s") "\",%u,"
2838 "0x%08X,0," 2834 "0x%08X,0,"
2839 "\"\",\"\"\n", 2835 "\"\",\"\"\n",
2840 -volume-1, dcvolp->serialnum, 2836 -volume-1, dcvolp->serialnum,
2837 dc_hash_serialnum(dcvolp->serialnum, DC_SERHASH_START),
2841 PATH_SEPCH, IF_MV(name,) dcvolp->frontier, 2838 PATH_SEPCH, IF_MV(name,) dcvolp->frontier,
2842 ATTR_DIRECTORY | ATTR_VOLUME); 2839 ATTR_DIRECTORY | ATTR_VOLUME);
2843 } 2840 }
@@ -2855,15 +2852,23 @@ void dircache_dump(void)
2855 char buf[DC_MAX_NAME + 2]; 2852 char buf[DC_MAX_NAME + 2];
2856 *buf = '\0'; 2853 *buf = '\0';
2857 int idx = get_index(ce); 2854 int idx = get_index(ce);
2858 get_path_sub(idx, buf, sizeof (buf)); 2855
2856 struct get_path_sub_data data =
2857 {
2858 .buf = buf,
2859 .size = sizeof (buf),
2860 .serialhash = DC_SERHASH_START,
2861 };
2862
2863 get_path_sub(idx, &data);
2859 2864
2860 fdprintf(fdcsv, 2865 fdprintf(fdcsv,
2861 "%d,%lu," 2866 "%d," DC_SERIAL_FMT "," DC_SERIAL_FMT ","
2862 "\"%s\",%u," 2867 "\"%s\",%u,"
2863 "0x%08X,%lu," 2868 "0x%08X,%lu,"
2864 "%04d/%02d/%02d," 2869 "%04d/%02d/%02d,"
2865 "%02d:%02d:%02d\n", 2870 "%02d:%02d:%02d\n",
2866 idx, ce->serialnum, 2871 idx, ce->serialnum, data.serialhash,
2867 buf, ce->frontier, 2872 buf, ce->frontier,
2868 ce->attr, (ce->attr & ATTR_DIRECTORY) ? 2873 ce->attr, (ce->attr & ATTR_DIRECTORY) ?
2869 0ul : (unsigned long)ce->filesize, 2874 0ul : (unsigned long)ce->filesize,