summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2017-01-08 17:14:10 -0500
committerMichael Sevakis <jethead71@rockbox.org>2017-01-17 14:35:36 -0500
commita931c76b3a46d1884e985a3bfc82b947521dab97 (patch)
tree1141cf9a9a8c123bde3d76d147ee1e910c7c6045
parent0056ea8a256af0e2daaf451af43c00708a31d4df (diff)
downloadrockbox-a931c76b3a46d1884e985a3bfc82b947521dab97.tar.gz
rockbox-a931c76b3a46d1884e985a3bfc82b947521dab97.zip
Do some debug and preparatory work for ramcache and playlist
The file system rework introduced incompatibility between dircache and the tagcache ramcache and playlist dircache path caching. This update makes changes to filesystem code to reintegrate all that. It also fixes a couple bugs that were found when vetting all the code. The filestream cache was being reset without regard to the stream even if it was shared in write mode (made work of .playlist_control). Better handling of unmounting gives files a better go at force-closing them without risk to disk integrity. Did some miscellaneous pedantic changes. Improved efficiency of testing a file's existence (a little) since the path parser will be shared between file code and parsing for the sake of finding dircache references, not duplicated as before. This commit doesn't reenable said items just for the sake of keeping changes separate and related. Plan for the next is to enable dircache again for the playlists (easy peasy) and reenable tagcache ramcache but *without* the dircache path caching because it's rather substantial to change in itself. The ramcache will still function without dircache. Change-Id: I7e2a9910b866251fa8333e1275f72fcfc8425d2d
-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));