summaryrefslogtreecommitdiff
path: root/firmware/common
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common')
-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
5 files changed, 312 insertions, 272 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 }