diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/common/dir.c | 2 | ||||
-rw-r--r-- | firmware/common/dircache.c | 351 | ||||
-rw-r--r-- | firmware/common/file.c | 57 | ||||
-rw-r--r-- | firmware/common/file_internal.c | 48 | ||||
-rw-r--r-- | firmware/common/fileobj_mgr.c | 126 | ||||
-rw-r--r-- | firmware/drivers/fat.c | 20 | ||||
-rw-r--r-- | firmware/export/fat.h | 6 | ||||
-rw-r--r-- | firmware/include/dircache.h | 40 | ||||
-rw-r--r-- | firmware/include/dircache_redirect.h | 19 | ||||
-rw-r--r-- | firmware/include/file_internal.h | 91 | ||||
-rw-r--r-- | firmware/include/fileobj_mgr.h | 5 |
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 | |||
1618 | read_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 |
2456 | static ssize_t get_path_sub(int idx, char *buf, size_t size) | 2463 | */ |
2464 | struct get_path_sub_data | ||
2465 | { | ||
2466 | char *buf; | ||
2467 | size_t size; | ||
2468 | dc_serial_t serialhash; | ||
2469 | }; | ||
2470 | |||
2471 | static 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 | */ | ||
2546 | static 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 | */ | ||
2567 | void 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 | */ |
2538 | ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf, | 2586 | ssize_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 = |
2568 | static struct dircache_entry * | ||
2569 | get_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() */ | ||
2599 | static 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) | 2621 | file_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 | */ |
2643 | int dircache_get_file(const char *path, struct dircache_file *dcfilep) | 2642 | int 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; | 2704 | file_success: |
2715 | dcfilep->serialnum = serialnum; | 2705 | rc = 0; |
2716 | 2706 | ||
2707 | file_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 */ | 191 | static void handle_truncate(struct filestr_desc * const file, file_size_t size) |
192 | void 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 | ||
308 | file_error:; | 312 | file_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 | ||
792 | void 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 | |||
641 | file_error: | 660 | file_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 */ |
248 | void fileobj_fileop_close(struct filestr_base *stream) | 243 | void 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 */ |
321 | void fileobj_fileop_sync(struct filestr_base *stream) | 315 | void 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); |
327 | void 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 */ | ||
333 | struct 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 */ |
344 | unsigned int fileobj_get_flags(const struct filestr_base *stream) | 343 | unsigned 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) */ |
353 | void fileobj_change_flags(struct filestr_base *stream, | 352 | void 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" */ |
362 | void fileobj_mgr_unmount(IF_MV_NONVOID(int volume)) | 364 | void 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 | ||
2528 | void 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 | |||
2528 | int fat_seek(struct fat_filestr *filestr, unsigned long seeksector) | 2538 | int 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: | |||
2761 | void fat_rewinddir(struct fat_dirscan_info *scan) | 2771 | void 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 | |||
100 | struct fat_dirscan_info | 102 | struct 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 */ |
109 | struct fat_file | 111 | struct 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); |
157 | void fat_rewind(struct fat_filestr *filestr); | 159 | void fat_rewind(struct fat_filestr *filestr); |
158 | int fat_seek(struct fat_filestr *filestr, unsigned long sector); | 160 | int fat_seek(struct fat_filestr *filestr, unsigned long sector); |
161 | void fat_seek_to_stream(struct fat_filestr *filestr, | ||
162 | const struct fat_filestr *filestr_seek_to); | ||
159 | int fat_truncate(const struct fat_filestr *filestr); | 163 | int 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 */ |
58 | typedef uint32_t dc_serial_t; | 58 | typedef 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 **/ |
136 | ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf, | 145 | struct dircache_fileref |
137 | size_t size); | 146 | { |
138 | int 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 | |||
151 | void dircache_fileref_init(struct dircache_fileref *dcfrefp); | ||
152 | ssize_t dircache_get_fileref_path(const struct dircache_fileref *dcfrefp, | ||
153 | char *buf, size_t size); | ||
154 | |||
155 | /* Bitflags for dircache_search() */ | ||
156 | enum 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 | |||
169 | int 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 | ||
122 | static inline void fileop_ontruncate_internal(struct filestr_base *stream) | ||
123 | { | ||
124 | fileobj_fileop_truncate(stream); | ||
125 | } | ||
126 | |||
127 | static inline void volume_onmount_internal(IF_MV_NONVOID(int volume)) | 124 | static 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 | ||
135 | static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume)) | 132 | static 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 | |||
141 | static 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); | |||
110 | enum fildes_and_obj_flags | 111 | enum 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 | |||
183 | struct filestr_base | 186 | struct 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 */ |
241 | struct path_component_info | 245 | struct 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 | ||
251 | int open_stream_internal(const char *path, unsigned int callflags, | 261 | int open_stream_internal(const char *path, unsigned int callflags, |
@@ -261,6 +271,7 @@ int remove_stream_internal(const char *path, struct filestr_base *stream, | |||
261 | int test_stream_exists_internal(const char *path, unsigned int callflags); | 271 | int test_stream_exists_internal(const char *path, unsigned int callflags); |
262 | 272 | ||
263 | int open_noiso_internal(const char *path, int oflag); /* file.c */ | 273 | int open_noiso_internal(const char *path, int oflag); /* file.c */ |
274 | void force_close_writer_internal(struct filestr_base *stream); /* file.c */ | ||
264 | 275 | ||
265 | struct dirent; | 276 | struct dirent; |
266 | int uncached_readdir_dirent(struct filestr_base *stream, | 277 | int 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, | |||
41 | void fileobj_fileop_remove(struct filestr_base *stream, | 41 | void fileobj_fileop_remove(struct filestr_base *stream, |
42 | const struct file_base_info *oldinfop); | 42 | const struct file_base_info *oldinfop); |
43 | void fileobj_fileop_sync(struct filestr_base *stream); | 43 | void fileobj_fileop_sync(struct filestr_base *stream); |
44 | void fileobj_fileop_truncate(struct filestr_base *stream); | ||
45 | extern void ftruncate_internal_callback(struct filestr_base *stream, | ||
46 | struct filestr_base *s); | ||
47 | 44 | ||
48 | file_size_t * fileobj_get_sizep(const struct filestr_base *stream); | 45 | file_size_t * fileobj_get_sizep(const struct filestr_base *stream); |
49 | unsigned int fileobj_get_flags(const struct filestr_base *stream); | 46 | unsigned int fileobj_get_flags(const struct filestr_base *stream); |
47 | struct filestr_base * fileobj_get_next_stream(const struct filestr_base *stream, | ||
48 | const struct filestr_base *s); | ||
50 | void fileobj_change_flags(struct filestr_base *stream, | 49 | void fileobj_change_flags(struct filestr_base *stream, |
51 | unsigned int flags, unsigned int mask); | 50 | unsigned int flags, unsigned int mask); |
52 | void fileobj_mgr_unmount(IF_MV_NONVOID(int volume)); | 51 | void fileobj_mgr_unmount(IF_MV_NONVOID(int volume)); |