summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/common/dir.c2
-rw-r--r--firmware/common/dircache.c351
-rw-r--r--firmware/common/file.c57
-rw-r--r--firmware/common/file_internal.c48
-rw-r--r--firmware/common/fileobj_mgr.c126
-rw-r--r--firmware/drivers/fat.c20
-rw-r--r--firmware/export/fat.h6
-rw-r--r--firmware/include/dircache.h40
-rw-r--r--firmware/include/dircache_redirect.h19
-rw-r--r--firmware/include/file_internal.h91
-rw-r--r--firmware/include/fileobj_mgr.h5
11 files changed, 436 insertions, 329 deletions
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index da798c71d5..59f7bd747a 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -56,7 +56,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
56 { 56 {
57 errnum = EFAULT; 57 errnum = EFAULT;
58 } 58 }
59 else if (dir->stream.flags == FV_NONEXIST) 59 else if (dir->stream.flags & FD_NONEXIST)
60 { 60 {
61 DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams)); 61 DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams));
62 errnum = ENXIO; 62 errnum = ENXIO;
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,
diff --git a/firmware/common/file.c b/firmware/common/file.c
index 6444918f1b..1f93824dc8 100644
--- a/firmware/common/file.c
+++ b/firmware/common/file.c
@@ -54,7 +54,7 @@ static struct filestr_desc * get_filestr(int fildes)
54 return file; 54 return file;
55 55
56 DEBUGF("fildes %d: bad file number\n", fildes); 56 DEBUGF("fildes %d: bad file number\n", fildes);
57 errno = (file && file->stream.flags == FV_NONEXIST) ? ENXIO : EBADF; 57 errno = (file && (file->stream.flags & FD_NONEXIST)) ? ENXIO : EBADF;
58 return NULL; 58 return NULL;
59} 59}
60 60
@@ -187,24 +187,28 @@ file_error:
187 return rc; 187 return rc;
188} 188}
189 189
190/* callback for each file stream to make sure all data is in sync with new 190/* Handle syncing all file's streams to the truncation */
191 size */ 191static void handle_truncate(struct filestr_desc * const file, file_size_t size)
192void ftruncate_internal_callback(struct filestr_base *stream,
193 struct filestr_base *s)
194{ 192{
195 struct filestr_desc *file = (struct filestr_desc *)s; 193 unsigned long filesectors = filesize_sectors(size);
196 file_size_t size = *file->sizep;
197
198 /* caches with data beyond new extents are invalid */
199 unsigned long sector = file->stream.cachep->sector;
200 if (sector != INVALID_SECNUM && sector >= filesize_sectors(size))
201 filestr_discard_cache(&file->stream);
202 194
203 /* keep all positions within bounds */ 195 struct filestr_base *s = NULL;
204 if (file->offset > size) 196 while ((s = fileobj_get_next_stream(&file->stream, s)))
205 file->offset = size; 197 {
206 198 /* caches with data beyond new extents are invalid */
207 (void)stream; 199 unsigned long sector = s->cachep->sector;
200 if (sector != INVALID_SECNUM && sector >= filesectors)
201 filestr_discard_cache(s);
202
203 /* files outside bounds must be rewound */
204 if (fat_query_sectornum(&s->fatstr) > filesectors)
205 fat_seek_to_stream(&s->fatstr, &file->stream.fatstr);
206
207 /* clip file offset too if needed */
208 struct filestr_desc *f = (struct filestr_desc *)s;
209 if (f->offset > size)
210 f->offset = size;
211 }
208} 212}
209 213
210/* truncate the file to the specified length */ 214/* truncate the file to the specified length */
@@ -246,13 +250,17 @@ static int ftruncate_internal(struct filestr_desc *file, file_size_t size,
246 rc2 = fat_truncate(&file->stream.fatstr); 250 rc2 = fat_truncate(&file->stream.fatstr);
247 if (rc2 < 0) 251 if (rc2 < 0)
248 FILE_ERROR(EIO, rc2 * 10 - 3); 252 FILE_ERROR(EIO, rc2 * 10 - 3);
253
254 /* never needs to be done this way again since any data beyond the
255 cached size is now gone */
256 fileobj_change_flags(&file->stream, 0, FO_TRUNC);
249 } 257 }
250 /* else just change the cached file size */ 258 /* else just change the cached file size */
251 259
252 if (truncsize < cursize) 260 if (truncsize < cursize)
253 { 261 {
254 *file->sizep = truncsize; 262 *file->sizep = truncsize;
255 fileop_ontruncate_internal(&file->stream); 263 handle_truncate(file, truncsize);
256 } 264 }
257 265
258 /* if truncation was partially successful, it effectively destroyed 266 /* if truncation was partially successful, it effectively destroyed
@@ -299,10 +307,6 @@ static int fsync_internal(struct filestr_desc *file)
299 int rc2 = ftruncate_internal(file, size, rc == 0); 307 int rc2 = ftruncate_internal(file, size, rc == 0);
300 if (rc2 < 0) 308 if (rc2 < 0)
301 FILE_ERROR(ERRNO, rc2 * 10 - 2); 309 FILE_ERROR(ERRNO, rc2 * 10 - 2);
302
303 /* never needs to be done this way again since any data beyond the
304 cached size is now gone */
305 fileobj_change_flags(&file->stream, 0, FO_TRUNC);
306 } 310 }
307 311
308file_error:; 312file_error:;
@@ -327,8 +331,7 @@ static int close_internal(struct filestr_desc *file)
327 /* call only when holding WRITER lock (updates directory entries) */ 331 /* call only when holding WRITER lock (updates directory entries) */
328 int rc; 332 int rc;
329 333
330 if ((file->stream.flags & FD_WRITE) && 334 if ((file->stream.flags & (FD_WRITE|FD_NONEXIST)) == FD_WRITE)
331 !(fileobj_get_flags(&file->stream) & FO_REMOVED))
332 { 335 {
333 rc = fsync_internal(file); 336 rc = fsync_internal(file);
334 if (rc < 0) 337 if (rc < 0)
@@ -786,6 +789,12 @@ int open_noiso_internal(const char *path, int oflag)
786 return open_internal_locked(path, oflag, FF_ANYTYPE | FF_NOISO); 789 return open_internal_locked(path, oflag, FF_ANYTYPE | FF_NOISO);
787} 790}
788 791
792void force_close_writer_internal(struct filestr_base *stream)
793{
794 /* only we do writers so we know this is our guy */
795 close_internal((struct filestr_desc *)stream);
796}
797
789 798
790/** POSIX **/ 799/** POSIX **/
791 800
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
index f9c6bfb570..aa0edb7ebb 100644
--- a/firmware/common/file_internal.c
+++ b/firmware/common/file_internal.c
@@ -292,7 +292,7 @@ struct pathwalk_component
292 struct pathwalk_component *nextp; /* parent if in use else next free */ 292 struct pathwalk_component *nextp; /* parent if in use else next free */
293}; 293};
294 294
295#define WALK_RC_NOT_FOUND 0 /* successfully not found */ 295#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
296#define WALK_RC_FOUND 1 /* found and opened */ 296#define WALK_RC_FOUND 1 /* found and opened */
297#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */ 297#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
298#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */ 298#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
@@ -351,7 +351,10 @@ static int fill_path_compinfo(struct pathwalk *walkp,
351 compinfo->length = compp->length; 351 compinfo->length = compp->length;
352 compinfo->attr = compp->attr; 352 compinfo->attr = compp->attr;
353 compinfo->filesize = walkp->filesize; 353 compinfo->filesize = walkp->filesize;
354 compinfo->parentinfo = (compp->nextp ?: compp)->info; 354 if (!(walkp->callflags & FF_SELFINFO))
355 compinfo->parentinfo = (compp->nextp ?: compp)->info;
356 else
357 compinfo->info = compp->info;
355 } 358 }
356 359
357 return rc; 360 return rc;
@@ -393,7 +396,10 @@ static int walk_open_info(struct pathwalk *walkp,
393 else 396 else
394 callflags &= ~FO_DIRECTORY; 397 callflags &= ~FO_DIRECTORY;
395 398
396 fileop_onopen_internal(stream, &compp->info, callflags); 399 /* make open official if not simply probing for presence */
400 if (!(callflags & FF_PROBE))
401 fileop_onopen_internal(stream, &compp->info, callflags);
402
397 return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT; 403 return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
398} 404}
399 405
@@ -511,7 +517,8 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
511 { 517 {
512 /* is ".." */ 518 /* is ".." */
513 struct pathwalk_component *parentp = compp->nextp; 519 struct pathwalk_component *parentp = compp->nextp;
514 if (!parentp) 520
521 if (!parentp IF_MV( || parentp->attr == ATTR_SYSTEM_ROOT ))
515 return WALK_RC_CONT_AT_ROOT; 522 return WALK_RC_CONT_AT_ROOT;
516 523
517 compp->nextp = freep; 524 compp->nextp = freep;
@@ -567,6 +574,9 @@ int open_stream_internal(const char *path, unsigned int callflags,
567 if (!compinfo) 574 if (!compinfo)
568 callflags &= ~FF_CHECKPREFIX; 575 callflags &= ~FF_CHECKPREFIX;
569 576
577 /* This lets it be passed quietly to directory scanning */
578 stream->flags = callflags & FF_MASK;
579
570 struct pathwalk walk; 580 struct pathwalk walk;
571 walk.path = path; 581 walk.path = path;
572 walk.callflags = callflags; 582 walk.callflags = callflags;
@@ -575,7 +585,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
575 585
576 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL); 586 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
577 rootp->nextp = NULL; 587 rootp->nextp = NULL;
578 rootp->attr = ATTR_DIRECTORY; 588 rootp->attr = ATTR_SYSTEM_ROOT;
579 589
580#ifdef HAVE_MULTIVOLUME 590#ifdef HAVE_MULTIVOLUME
581 int volume = 0, rootrc = WALK_RC_FOUND; 591 int volume = 0, rootrc = WALK_RC_FOUND;
@@ -584,7 +594,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
584 while (1) 594 while (1)
585 { 595 {
586 const char *pathptr = walk.path; 596 const char *pathptr = walk.path;
587 597
588 #ifdef HAVE_MULTIVOLUME 598 #ifdef HAVE_MULTIVOLUME
589 /* this seamlessly integrates secondary filesystems into the 599 /* this seamlessly integrates secondary filesystems into the
590 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */ 600 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
@@ -596,8 +606,19 @@ int open_stream_internal(const char *path, unsigned int callflags,
596 FILE_ERROR(ENXIO, -2); 606 FILE_ERROR(ENXIO, -2);
597 } 607 }
598 608
599 /* the root of this subpath is the system root? */ 609 if (p == pathptr)
600 rootrc = p == pathptr ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND; 610 {
611 /* the root of this subpath is the system root */
612 rootp->attr = ATTR_SYSTEM_ROOT;
613 rootrc = WALK_RC_FOUND_ROOT;
614 }
615 else
616 {
617 /* this subpath specifies a mount point */
618 rootp->attr = ATTR_MOUNT_POINT;
619 rootrc = WALK_RC_FOUND;
620 }
621
601 walk.path = p; 622 walk.path = p;
602 #endif /* HAVE_MULTIVOLUME */ 623 #endif /* HAVE_MULTIVOLUME */
603 624
@@ -633,11 +654,9 @@ int open_stream_internal(const char *path, unsigned int callflags,
633 default: /* utter, abject failure :`( */ 654 default: /* utter, abject failure :`( */
634 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno); 655 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
635 filestr_base_destroy(stream); 656 filestr_base_destroy(stream);
636 FILE_ERROR(-rc, -2); 657 FILE_ERROR(-rc, -3);
637 } 658 }
638 659
639 file_cache_reset(stream->cachep);
640
641file_error: 660file_error:
642 return rc; 661 return rc;
643} 662}
@@ -757,9 +776,10 @@ int test_stream_exists_internal(const char *path, unsigned int callflags)
757{ 776{
758 /* only FF_* flags should be in callflags */ 777 /* only FF_* flags should be in callflags */
759 struct filestr_base stream; 778 struct filestr_base stream;
760 int rc = open_stream_internal(path, callflags, &stream, NULL); 779 int rc = open_stream_internal(path, callflags | FF_PROBE, &stream, NULL);
761 if (rc > 0) 780
762 close_stream_internal(&stream); 781 if (rc >= 0)
782 filestr_base_destroy(&stream);
763 783
764 return rc; 784 return rc;
765} 785}
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
index 8e7831d36c..e34a460e10 100644
--- a/firmware/common/fileobj_mgr.c
+++ b/firmware/common/fileobj_mgr.c
@@ -187,7 +187,8 @@ void fileobj_fileop_open(struct filestr_base *stream,
187 ll_insert_last(&fobp->list, &stream->node); 187 ll_insert_last(&fobp->list, &stream->node);
188 188
189 /* initiate the new stream into the enclave */ 189 /* initiate the new stream into the enclave */
190 stream->flags = FDO_BUSY | (callflags & (FD_WRITE|FD_WRONLY|FD_APPEND)); 190 stream->flags = FDO_BUSY |
191 (callflags & (FF_MASK|FD_WRITE|FD_WRONLY|FD_APPEND));
191 stream->infop = &fobp->bind.info; 192 stream->infop = &fobp->bind.info;
192 stream->fatstr.fatfilep = &fobp->bind.info.fatfile; 193 stream->fatstr.fatfilep = &fobp->bind.info.fatfile;
193 stream->bindp = &fobp->bind; 194 stream->bindp = &fobp->bind;
@@ -202,14 +203,6 @@ void fileobj_fileop_open(struct filestr_base *stream,
202 fobp->writers = 0; 203 fobp->writers = 0;
203 fobp->size = 0; 204 fobp->size = 0;
204 205
205 if (callflags & FD_WRITE)
206 {
207 /* first one is a writer */
208 fobp->writers = 1;
209 file_cache_init(&fobp->cache);
210 filestr_assign_cache(stream, &fobp->cache);
211 }
212
213 fileobj_bind_file(&fobp->bind); 206 fileobj_bind_file(&fobp->bind);
214 } 207 }
215 else 208 else
@@ -225,32 +218,33 @@ void fileobj_fileop_open(struct filestr_base *stream,
225 DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", 218 DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
226 __func__, stream, callflags); 219 __func__, stream, callflags);
227 } 220 }
221 }
228 222
229 if (fobp->writers) 223 if ((callflags & FD_WRITE) && ++fobp->writers == 1)
230 { 224 {
231 /* already writers present */ 225 /* first writer */
232 fobp->writers++; 226 file_cache_init(&fobp->cache);
233 filestr_assign_cache(stream, &fobp->cache); 227 FOR_EACH_STREAM(FIRST, fobp, s)
234 } 228 filestr_assign_cache(s, &fobp->cache);
235 else if (callflags & FD_WRITE) 229 }
236 { 230 else if (fobp->writers)
237 /* first writer */ 231 {
238 fobp->writers = 1; 232 /* already writers present */
239 file_cache_init(&fobp->cache); 233 filestr_assign_cache(stream, &fobp->cache);
240 FOR_EACH_STREAM(FIRST, fobp, s) 234 }
241 filestr_assign_cache(s, &fobp->cache); 235 else
242 } 236 {
243 /* else another reader */ 237 /* another reader and no writers present */
238 file_cache_reset(stream->cachep);
244 } 239 }
245} 240}
246 241
247/* close the stream and free associated resources */ 242/* close the stream and free associated resources */
248void fileobj_fileop_close(struct filestr_base *stream) 243void fileobj_fileop_close(struct filestr_base *stream)
249{ 244{
250 switch (stream->flags) 245 if (!(stream->flags & FDO_BUSY))
251 { 246 {
252 case 0: /* not added to manager */ 247 /* not added to manager or forced-closed by unmounting */
253 case FV_NONEXIST: /* forced-closed by unmounting */
254 filestr_base_destroy(stream); 248 filestr_base_destroy(stream);
255 return; 249 return;
256 } 250 }
@@ -260,30 +254,30 @@ void fileobj_fileop_close(struct filestr_base *stream)
260 254
261 ll_remove(&fobp->list, &stream->node); 255 ll_remove(&fobp->list, &stream->node);
262 256
263 if ((foflags & FO_SINGLE) || fobp->writers == 0) 257 if (foflags & FO_SINGLE)
264 { 258 {
265 if (foflags & FO_SINGLE) 259 /* last stream for file; close everything */
266 { 260 fileobj_unbind_file(&fobp->bind);
267 /* last stream for file; close everything */
268 fileobj_unbind_file(&fobp->bind);
269 261
270 if (fobp->writers) 262 if (fobp->writers)
271 file_cache_free(&fobp->cache); 263 file_cache_free(&fobp->cache);
272 264
273 binding_add_to_free_list(fobp); 265 binding_add_to_free_list(fobp);
274 }
275 } 266 }
276 else if ((stream->flags & FD_WRITE) && --fobp->writers == 0) 267 else
277 { 268 {
278 /* only readers remain; switch back to stream-local caching */ 269 if ((stream->flags & FD_WRITE) && --fobp->writers == 0)
279 FOR_EACH_STREAM(FIRST, fobp, s) 270 {
280 filestr_copy_cache(s, &fobp->cache); 271 /* only readers remain; switch back to stream-local caching */
272 FOR_EACH_STREAM(FIRST, fobp, s)
273 filestr_copy_cache(s, &fobp->cache);
281 274
282 file_cache_free(&fobp->cache); 275 file_cache_free(&fobp->cache);
283 } 276 }
284 277
285 if (!(foflags & FO_SINGLE) && fobp->list.head == fobp->list.tail) 278 if (fobp->list.head == fobp->list.tail)
286 fobp->flags |= FO_SINGLE; /* only one open stream remaining */ 279 fobp->flags |= FO_SINGLE; /* only one open stream remaining */
280 }
287 281
288 filestr_base_destroy(stream); 282 filestr_base_destroy(stream);
289} 283}
@@ -320,15 +314,10 @@ void fileobj_fileop_rename(struct filestr_base *stream,
320/* informs manager than directory entries have been updated */ 314/* informs manager than directory entries have been updated */
321void fileobj_fileop_sync(struct filestr_base *stream) 315void fileobj_fileop_sync(struct filestr_base *stream)
322{ 316{
323 fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1); 317 if (((struct fileobj_binding *)stream->bindp)->flags & FO_REMOVED)
324} 318 return; /* no dir to sync */
325 319
326/* inform manager that file has been truncated */ 320 fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1);
327void fileobj_fileop_truncate(struct filestr_base *stream)
328{
329 /* let caller update internal info */
330 FOR_EACH_STREAM(FIRST, (struct fileobj_binding *)stream->bindp, s)
331 ftruncate_internal_callback(stream, s);
332} 321}
333 322
334/* query for the pointer to the size storage for the file object */ 323/* query for the pointer to the size storage for the file object */
@@ -340,6 +329,16 @@ file_size_t * fileobj_get_sizep(const struct filestr_base *stream)
340 return &((struct fileobj_binding *)stream->bindp)->size; 329 return &((struct fileobj_binding *)stream->bindp)->size;
341} 330}
342 331
332/* iterate the list of streams for this stream's file */
333struct filestr_base * fileobj_get_next_stream(const struct filestr_base *stream,
334 const struct filestr_base *s)
335{
336 if (!stream->bindp)
337 return NULL;
338
339 return s ? STREAM_NEXT(s) : STREAM_FIRST((struct fileobj_binding *)stream->bindp);
340}
341
343/* query manager bitflags for the file object */ 342/* query manager bitflags for the file object */
344unsigned int fileobj_get_flags(const struct filestr_base *stream) 343unsigned int fileobj_get_flags(const struct filestr_base *stream)
345{ 344{
@@ -349,20 +348,21 @@ unsigned int fileobj_get_flags(const struct filestr_base *stream)
349 return ((struct fileobj_binding *)stream->bindp)->flags; 348 return ((struct fileobj_binding *)stream->bindp)->flags;
350} 349}
351 350
352/* change manager bitflags for the file object */ 351/* change manager bitflags for the file object (permitted only) */
353void fileobj_change_flags(struct filestr_base *stream, 352void fileobj_change_flags(struct filestr_base *stream,
354 unsigned int flags, unsigned int mask) 353 unsigned int flags, unsigned int mask)
355{ 354{
356 struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp; 355 struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp;
357 if (fobp) 356 if (!fobp)
358 fobp->flags = (fobp->flags & ~mask) | (flags & mask); 357 return;
358
359 mask &= FDO_CHG_MASK;
360 fobp->flags = (fobp->flags & ~mask) | (flags & mask);
359} 361}
360 362
361/* mark all open streams on a device as "nonexistant" */ 363/* mark all open streams on a device as "nonexistant" */
362void fileobj_mgr_unmount(IF_MV_NONVOID(int volume)) 364void fileobj_mgr_unmount(IF_MV_NONVOID(int volume))
363{ 365{
364 /* right now, there is nothing else to be freed when marking a descriptor
365 as "nonexistant" but a callback could be added if that changes */
366 FOR_EACH_VOLUME(volume, v) 366 FOR_EACH_VOLUME(volume, v)
367 { 367 {
368 struct fileobj_binding *fobp; 368 struct fileobj_binding *fobp;
@@ -371,11 +371,17 @@ void fileobj_mgr_unmount(IF_MV_NONVOID(int volume))
371 struct filestr_base *s; 371 struct filestr_base *s;
372 while ((s = STREAM_FIRST(fobp))) 372 while ((s = STREAM_FIRST(fobp)))
373 { 373 {
374 /* last ditch effort to preserve FS integrity; we could still
375 be alive (soft unmount, maybe); we get informed early */
376 fileop_onunmount_internal(s);
377
378 if (STREAM_FIRST(fobp) == s)
379 fileop_onclose_internal(s); /* above didn't close it */
380
374 /* keep it "busy" to avoid races; any valid file/directory 381 /* keep it "busy" to avoid races; any valid file/directory
375 descriptor returned by an open call should always be 382 descriptor returned by an open call should always be
376 closed by whomever opened it (of course!) */ 383 closed by whoever opened it (of course!) */
377 fileop_onclose_internal(s); 384 s->flags = (s->flags & ~FDO_BUSY) | FD_NONEXIST;
378 s->flags = FV_NONEXIST;
379 } 385 }
380 } 386 }
381 } 387 }
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index a090bd5899..fed3baffd4 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -1927,7 +1927,7 @@ static int free_direntries(struct bpb *fat_bpb, struct fat_file *file)
1927 1927
1928 /* directory entry info is now gone */ 1928 /* directory entry info is now gone */
1929 file->dircluster = 0; 1929 file->dircluster = 0;
1930 file->e.entry = FAT_RW_VAL; 1930 file->e.entry = FAT_DIRSCAN_RW_VAL;
1931 file->e.entries = 0; 1931 file->e.entries = 0;
1932 1932
1933 return 1; 1933 return 1;
@@ -2521,10 +2521,20 @@ void fat_rewind(struct fat_filestr *filestr)
2521 filestr->lastcluster = filestr->fatfilep->firstcluster; 2521 filestr->lastcluster = filestr->fatfilep->firstcluster;
2522 filestr->lastsector = 0; 2522 filestr->lastsector = 0;
2523 filestr->clusternum = 0; 2523 filestr->clusternum = 0;
2524 filestr->sectornum = FAT_RW_VAL; 2524 filestr->sectornum = FAT_FILE_RW_VAL;
2525 filestr->eof = false; 2525 filestr->eof = false;
2526} 2526}
2527 2527
2528void fat_seek_to_stream(struct fat_filestr *filestr,
2529 const struct fat_filestr *filestr_seek_to)
2530{
2531 filestr->lastcluster = filestr_seek_to->lastcluster;
2532 filestr->lastsector = filestr_seek_to->lastsector;
2533 filestr->clusternum = filestr_seek_to->clusternum;
2534 filestr->sectornum = filestr_seek_to->sectornum;
2535 filestr->eof = filestr_seek_to->eof;
2536}
2537
2528int fat_seek(struct fat_filestr *filestr, unsigned long seeksector) 2538int fat_seek(struct fat_filestr *filestr, unsigned long seeksector)
2529{ 2539{
2530 const struct fat_file * const file = filestr->fatfilep; 2540 const struct fat_file * const file = filestr->fatfilep;
@@ -2536,7 +2546,7 @@ int fat_seek(struct fat_filestr *filestr, unsigned long seeksector)
2536 long cluster = file->firstcluster; 2546 long cluster = file->firstcluster;
2537 unsigned long sector = 0; 2547 unsigned long sector = 0;
2538 long clusternum = 0; 2548 long clusternum = 0;
2539 unsigned long sectornum = FAT_RW_VAL; 2549 unsigned long sectornum = FAT_FILE_RW_VAL;
2540 2550
2541#ifdef HAVE_FAT16SUPPORT 2551#ifdef HAVE_FAT16SUPPORT
2542 if (fat_bpb->is_fat16 && cluster < 0) /* FAT16 root dir */ 2552 if (fat_bpb->is_fat16 && cluster < 0) /* FAT16 root dir */
@@ -2710,7 +2720,7 @@ int fat_readdir(struct fat_filestr *dirstr, struct fat_dirscan_info *scan,
2710 2720
2711 dc_lock_cache(); 2721 dc_lock_cache();
2712 2722
2713 while (--scan->entry != FAT_RW_VAL) /* at beginning? */ 2723 while (--scan->entry != FAT_DIRSCAN_RW_VAL) /* at beginning? */
2714 { 2724 {
2715 ent = cache_direntry(fat_bpb, dirstr, scan->entry); 2725 ent = cache_direntry(fat_bpb, dirstr, scan->entry);
2716 2726
@@ -2761,7 +2771,7 @@ fat_error:
2761void fat_rewinddir(struct fat_dirscan_info *scan) 2771void fat_rewinddir(struct fat_dirscan_info *scan)
2762{ 2772{
2763 /* rewind the directory scan counter to the beginning */ 2773 /* rewind the directory scan counter to the beginning */
2764 scan->entry = FAT_RW_VAL; 2774 scan->entry = FAT_DIRSCAN_RW_VAL;
2765 scan->entries = 0; 2775 scan->entries = 0;
2766} 2776}
2767 2777
diff --git a/firmware/export/fat.h b/firmware/export/fat.h
index 3aa1e254dc..963c1fe767 100644
--- a/firmware/export/fat.h
+++ b/firmware/export/fat.h
@@ -97,13 +97,15 @@ struct fat_direntry
97 97
98/* cursor structure used for scanning directories; holds the last-returned 98/* cursor structure used for scanning directories; holds the last-returned
99 entry information */ 99 entry information */
100#define FAT_DIRSCAN_RW_VAL (0u - 1u)
101
100struct fat_dirscan_info 102struct fat_dirscan_info
101{ 103{
102 unsigned int entry; /* short dir entry index in parent */ 104 unsigned int entry; /* short dir entry index in parent */
103 unsigned int entries; /* number of dir entries used */ 105 unsigned int entries; /* number of dir entries used */
104}; 106};
105 107
106#define FAT_RW_VAL (0u - 1) 108#define FAT_FILE_RW_VAL (0ul - 1ul)
107 109
108/* basic FAT file information about where to find a file and who houses it */ 110/* basic FAT file information about where to find a file and who houses it */
109struct fat_file 111struct fat_file
@@ -156,6 +158,8 @@ long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount,
156 void *buf, bool write); 158 void *buf, bool write);
157void fat_rewind(struct fat_filestr *filestr); 159void fat_rewind(struct fat_filestr *filestr);
158int fat_seek(struct fat_filestr *filestr, unsigned long sector); 160int fat_seek(struct fat_filestr *filestr, unsigned long sector);
161void fat_seek_to_stream(struct fat_filestr *filestr,
162 const struct fat_filestr *filestr_seek_to);
159int fat_truncate(const struct fat_filestr *filestr); 163int fat_truncate(const struct fat_filestr *filestr);
160 164
161/** Directory stream functions **/ 165/** Directory stream functions **/
diff --git a/firmware/include/dircache.h b/firmware/include/dircache.h
index 7e8c764e7f..d73c6f6e81 100644
--- a/firmware/include/dircache.h
+++ b/firmware/include/dircache.h
@@ -57,6 +57,15 @@
57 figure pessimistic */ 57 figure pessimistic */
58typedef uint32_t dc_serial_t; 58typedef uint32_t dc_serial_t;
59 59
60/* these should agree with size of dc_serial_t */
61#define DC_SERHASH_START 0xffffffff
62
63/* I was originally using FNV hash but decided this is probably okay
64 (for now) */
65#define dc_hash_serialnum(s, h) \
66 ({ dc_serial_t __x = (s); crc_32(&(__x), sizeof(dc_serial_t), (h)); })
67#define DC_SERIAL_FMT "0x%08lX"
68
60/** 69/**
61 ****************************************************************************/ 70 ****************************************************************************/
62 71
@@ -132,10 +141,33 @@ void dircache_fileop_sync(struct file_base_binding *infop,
132 const struct dirinfo_native *dinp); 141 const struct dirinfo_native *dinp);
133 142
134 143
135/** Dircache paths and files **/ 144/** Dircache paths, files and shortcuts **/
136ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf, 145struct dircache_fileref
137 size_t size); 146{
138int dircache_get_file(const char *path, struct dircache_file *dcfilep); 147 struct dircache_file dcfile;
148 dc_serial_t serialhash; /* Hash of serialnumbers to root */
149};
150
151void dircache_fileref_init(struct dircache_fileref *dcfrefp);
152ssize_t dircache_get_fileref_path(const struct dircache_fileref *dcfrefp,
153 char *buf, size_t size);
154
155/* Bitflags for dircache_search() */
156enum dircache_search_flags
157{
158 DCS_FILEREF = 0x01, /* Check fileref existence and serial number */
159 _DCS_VERIFY_FLAG = 0x02, /* Internal: Only valid with DCS_FILEREF */
160 DCS_FILEREF_VERIFY = 0x03, /* Do DCS_FILEREF check + verify serial hash */
161 DCS_CACHED_PATH = 0x04, /* Check only cache for provided path */
162 _DCS_STORAGE_FLAG = 0x08, /* Internal: Only valid with DCS_CACHED_PATH */
163 DCS_STORAGE_PATH = 0x0c, /* Read-through if needed for provided path */
164 DCS_UPDATE_FILEREF = 0x10, /* If fileref is not valid but path is found or
165 searching a path, update the reference
166 information */
167};
168
169int dircache_search(unsigned int flags, struct dircache_fileref *dcfrefp,
170 const char *path);
139 171
140 172
141/** Debug screen/info stuff **/ 173/** Debug screen/info stuff **/
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index 15fb4bc38d..9fae16b551 100644
--- a/firmware/include/dircache_redirect.h
+++ b/firmware/include/dircache_redirect.h
@@ -24,6 +24,8 @@
24 24
25/*** 25/***
26 ** Internal redirects that depend upon whether or not dircache is made 26 ** Internal redirects that depend upon whether or not dircache is made
27 **
28 ** Some stuff deals with it, some doesn't right now. This is a nexus point..
27 **/ 29 **/
28 30
29/** File binding **/ 31/** File binding **/
@@ -119,11 +121,6 @@ static inline void fileop_onsync_internal(struct filestr_base *stream)
119#endif 121#endif
120} 122}
121 123
122static inline void fileop_ontruncate_internal(struct filestr_base *stream)
123{
124 fileobj_fileop_truncate(stream);
125}
126
127static inline void volume_onmount_internal(IF_MV_NONVOID(int volume)) 124static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
128{ 125{
129#ifdef HAVE_DIRCACHE 126#ifdef HAVE_DIRCACHE
@@ -134,10 +131,20 @@ static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
134 131
135static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume)) 132static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
136{ 133{
137 fileobj_mgr_unmount(IF_MV(volume));
138#ifdef HAVE_DIRCACHE 134#ifdef HAVE_DIRCACHE
135 /* First, to avoid update of something about to be destroyed anyway */
139 dircache_unmount(IF_MV(volume)); 136 dircache_unmount(IF_MV(volume));
140#endif 137#endif
138 fileobj_mgr_unmount(IF_MV(volume));
139}
140
141static inline void fileop_onunmount_internal(struct filestr_base *stream)
142{
143
144 if (stream->flags & FD_WRITE)
145 force_close_writer_internal(stream); /* try to save stuff */
146 else
147 fileop_onclose_internal(stream); /* just readers, bye */
141} 148}
142 149
143 150
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index d1bb67406a..acec81206e 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -81,6 +81,7 @@
81#define ATTR_NEW_FILE (ATTR_ARCHIVE) 81#define ATTR_NEW_FILE (ATTR_ARCHIVE)
82#define ATTR_NEW_DIRECTORY (ATTR_DIRECTORY) 82#define ATTR_NEW_DIRECTORY (ATTR_DIRECTORY)
83 83
84#define ATTR_SYSTEM_ROOT (ATTR_SYSROOT | ATTR_DIRECTORY)
84#define ATTR_MOUNT_POINT (ATTR_VOLUME | ATTR_DIRECTORY) 85#define ATTR_MOUNT_POINT (ATTR_VOLUME | ATTR_DIRECTORY)
85 86
86/** File sector cache **/ 87/** File sector cache **/
@@ -110,33 +111,35 @@ void file_cache_free(struct filestr_cache *cachep);
110enum fildes_and_obj_flags 111enum fildes_and_obj_flags
111{ 112{
112 /* used in descriptor and common */ 113 /* used in descriptor and common */
113 FDO_BUSY = 0x0001, /* descriptor/object is in use */ 114 FDO_BUSY = 0x0001, /* descriptor/object is in use */
114 /* only used in individual stream descriptor */ 115 /* only used in individual stream descriptor */
115 FD_WRITE = 0x0002, /* descriptor has write mode */ 116 FD_WRITE = 0x0002, /* descriptor has write mode */
116 FD_WRONLY = 0x0004, /* descriptor is write mode only */ 117 FD_WRONLY = 0x0004, /* descriptor is write mode only */
117 FD_APPEND = 0x0008, /* descriptor is append mode */ 118 FD_APPEND = 0x0008, /* descriptor is append mode */
119 FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
118 /* only used as common flags */ 120 /* only used as common flags */
119 FO_DIRECTORY = 0x0010, /* fileobj is a directory */ 121 FO_DIRECTORY = 0x0010, /* fileobj is a directory */
120 FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */ 122 FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */
121 FO_REMOVED = 0x0040, /* fileobj was deleted while open */ 123 FO_REMOVED = 0x0040, /* fileobj was deleted while open */
122 FO_SINGLE = 0x0080, /* fileobj has only one stream open */ 124 FO_SINGLE = 0x0080, /* fileobj has only one stream open */
123 FDO_MASK = 0x00ff, 125 FDO_MASK = 0x00ff,
124 /* bitflags that instruct various 'open' functions how to behave */ 126 FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */
125 FF_FILE = 0x0000, /* expect file; accept file only */ 127 /* bitflags that instruct various 'open' functions how to behave;
126 FF_DIR = 0x0100, /* expect dir; accept dir only */ 128 * saved in stream flags (only) but not used by manager */
127 FF_ANYTYPE = 0x0200, /* succeed if either file or dir */ 129 FF_FILE = 0x00000000, /* expect file; accept file only */
128 FF_TYPEMASK = 0x0300, /* mask of typeflags */ 130 FF_DIR = 0x00010000, /* expect dir; accept dir only */
129 FF_CREAT = 0x0400, /* create if file doesn't exist */ 131 FF_ANYTYPE = 0x00020000, /* succeed if either file or dir */
130 FF_EXCL = 0x0800, /* fail if creating and file exists */ 132 FF_TYPEMASK = 0x00030000, /* mask of typeflags */
131 FF_CHECKPREFIX = 0x1000, /* detect if file is prefix of path */ 133 FF_CREAT = 0x00040000, /* create if file doesn't exist */
132 FF_NOISO = 0x2000, /* do not decode ISO filenames to UTF-8 */ 134 FF_EXCL = 0x00080000, /* fail if creating and file exists */
133 FF_MASK = 0x3f00, 135 FF_CHECKPREFIX = 0x00100000, /* detect if file is prefix of path */
134 /* special values used in isolation */ 136 FF_NOISO = 0x00200000, /* do not decode ISO filenames to UTF-8 */
135 FV_NONEXIST = 0x8000, /* closed but not freed (unmounted) */ 137 FF_PROBE = 0x00400000, /* only test existence; don't open */
136 FV_OPENSYSROOT = 0xc001, /* open sysroot, volume 0 not mounted */ 138 FF_CACHEONLY = 0x00800000, /* succeed only if in dircache */
139 FF_SELFINFO = 0x01000000, /* return info on self as well */
140 FF_MASK = 0x01ff0000,
137}; 141};
138 142
139
140/** Common data structures used throughout **/ 143/** Common data structures used throughout **/
141 144
142/* basic file information about its location */ 145/* basic file information about its location */
@@ -183,8 +186,7 @@ struct dirscan_info
183struct filestr_base 186struct filestr_base
184{ 187{
185 struct ll_node node; /* list item node (first!) */ 188 struct ll_node node; /* list item node (first!) */
186 uint16_t flags; /* FD_* bits of this stream */ 189 uint32_t flags; /* F[DF]_* bits of this stream */
187 uint16_t unused; /* not used */
188 struct filestr_cache cache; /* stream-local cache */ 190 struct filestr_cache cache; /* stream-local cache */
189 struct filestr_cache *cachep; /* the cache in use (local or shared) */ 191 struct filestr_cache *cachep; /* the cache in use (local or shared) */
190 struct file_base_info *infop; /* base file information */ 192 struct file_base_info *infop; /* base file information */
@@ -235,17 +237,25 @@ static inline void filestr_unlock(struct filestr_base *stream)
235#define FILESTR_UNLOCK(type, stream) \ 237#define FILESTR_UNLOCK(type, stream) \
236 ({ if (FILESTR_##type) filestr_unlock(stream); }) 238 ({ if (FILESTR_##type) filestr_unlock(stream); })
237 239
238#define ATTR_PREFIX (0x8000) /* out of the way of all ATTR_* bits */ 240/* auxilliary attributes - out of the way of regular ATTR_* bits */
241#define ATTR_SYSROOT (0x8000)
242#define ATTR_PREFIX (0x4000)
239 243
240/* structure to return detailed information about what you opened */ 244/* structure to return detailed information about what you opened */
241struct path_component_info 245struct path_component_info
242{ 246{
243 const char *name; /* pointer to name within 'path' */ 247 const char *name; /* pointer to name within 'path' (OUT) */
244 size_t length; /* length of component within 'path' */ 248 size_t length; /* length of component within 'path' */
245 file_size_t filesize; /* size of the opened file (0 if dir) */ 249 file_size_t filesize; /* size of the opened file (0 if dir) */
246 unsigned int attr; /* attributes of this component */ 250 unsigned int attr; /* attributes of this component */
247 struct file_base_info *prefixp; /* base info to check as prefix (IN) */ 251 struct file_base_info *prefixp; /* base info to check as prefix
248 struct file_base_info parentinfo; /* parent directory info of file */ 252 (IN if FF_CHECKPREFIX) */
253 union {
254 struct file_base_info parentinfo; /* parent directory base info of file
255 (if not FF_SELFINFO) */
256 struct file_base_info info; /* base info of file itself
257 (if FF_SELFINFO) */
258 };
249}; 259};
250 260
251int open_stream_internal(const char *path, unsigned int callflags, 261int open_stream_internal(const char *path, unsigned int callflags,
@@ -261,6 +271,7 @@ int remove_stream_internal(const char *path, struct filestr_base *stream,
261int test_stream_exists_internal(const char *path, unsigned int callflags); 271int test_stream_exists_internal(const char *path, unsigned int callflags);
262 272
263int open_noiso_internal(const char *path, int oflag); /* file.c */ 273int open_noiso_internal(const char *path, int oflag); /* file.c */
274void force_close_writer_internal(struct filestr_base *stream); /* file.c */
264 275
265struct dirent; 276struct dirent;
266int uncached_readdir_dirent(struct filestr_base *stream, 277int uncached_readdir_dirent(struct filestr_base *stream,
@@ -326,22 +337,26 @@ static inline void file_internal_unlock_WRITER(void)
326 * not in the macro 337 * not in the macro
327 */ 338 */
328 339
340#define FILE_SET_CODE(_name, _keepcode, _value) \
341 ({ __builtin_constant_p(_value) ? \
342 ({ if ((_value) != (_keepcode)) _name = (_value); }) : \
343 ({ _name = (_value); }); })
344
329/* set errno and rc and proceed to the "file_error:" label */ 345/* set errno and rc and proceed to the "file_error:" label */
330#define FILE_ERROR(_errno, _rc) \ 346#define FILE_ERROR(_errno, _rc) \
331 ({ __builtin_constant_p(_errno) ? \ 347 ({ FILE_SET_CODE(errno, ERRNO, (_errno)); \
332 ({ if ((_errno) != ERRNO) errno = (_errno); }) : \ 348 FILE_SET_CODE(rc, RC, (_rc)); \
333 ({ errno = (_errno); }); \
334 __builtin_constant_p(_rc) ? \
335 ({ if ((_rc) != RC) rc = (_rc); }) : \
336 ({ rc = (_rc); }); \
337 goto file_error; }) 349 goto file_error; })
338 350
339/* set errno and return a value at the point of invocation */ 351/* set errno and return a value at the point of invocation */
340#define FILE_ERROR_RETURN(_errno, _rc...) \ 352#define FILE_ERROR_RETURN(_errno, _rc...) \
341 ({ __builtin_constant_p(_errno) ? \ 353 ({ FILE_SET_CODE(errno, ERRNO, _errno); \
342 ({ if ((_errno) != ERRNO) errno = (_errno); }) : \ 354 return _rc; })
343 ({ errno = (_errno); }); \ 355
344 return _rc; }) 356/* set errno and return code, no branching */
357#define FILE_ERROR_SET(_errno, _rc) \
358 ({ FILE_SET_CODE(errno, ERRNO, (_errno)); \
359 FILE_SET_CODE(rc, RC, (_rc)); })
345 360
346 361
347/** Misc. stuff **/ 362/** Misc. stuff **/
diff --git a/firmware/include/fileobj_mgr.h b/firmware/include/fileobj_mgr.h
index c90a59bea0..627d2df341 100644
--- a/firmware/include/fileobj_mgr.h
+++ b/firmware/include/fileobj_mgr.h
@@ -41,12 +41,11 @@ void fileobj_fileop_rename(struct filestr_base *stream,
41void fileobj_fileop_remove(struct filestr_base *stream, 41void fileobj_fileop_remove(struct filestr_base *stream,
42 const struct file_base_info *oldinfop); 42 const struct file_base_info *oldinfop);
43void fileobj_fileop_sync(struct filestr_base *stream); 43void fileobj_fileop_sync(struct filestr_base *stream);
44void fileobj_fileop_truncate(struct filestr_base *stream);
45extern void ftruncate_internal_callback(struct filestr_base *stream,
46 struct filestr_base *s);
47 44
48file_size_t * fileobj_get_sizep(const struct filestr_base *stream); 45file_size_t * fileobj_get_sizep(const struct filestr_base *stream);
49unsigned int fileobj_get_flags(const struct filestr_base *stream); 46unsigned int fileobj_get_flags(const struct filestr_base *stream);
47struct filestr_base * fileobj_get_next_stream(const struct filestr_base *stream,
48 const struct filestr_base *s);
50void fileobj_change_flags(struct filestr_base *stream, 49void fileobj_change_flags(struct filestr_base *stream,
51 unsigned int flags, unsigned int mask); 50 unsigned int flags, unsigned int mask);
52void fileobj_mgr_unmount(IF_MV_NONVOID(int volume)); 51void fileobj_mgr_unmount(IF_MV_NONVOID(int volume));