diff options
Diffstat (limited to 'firmware/common')
-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 |
5 files changed, 312 insertions, 272 deletions
diff --git a/firmware/common/dir.c b/firmware/common/dir.c index da798c71d5..59f7bd747a 100644 --- a/firmware/common/dir.c +++ b/firmware/common/dir.c | |||
@@ -56,7 +56,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp) | |||
56 | { | 56 | { |
57 | errnum = EFAULT; | 57 | errnum = EFAULT; |
58 | } | 58 | } |
59 | else if (dir->stream.flags == FV_NONEXIST) | 59 | else if (dir->stream.flags & FD_NONEXIST) |
60 | { | 60 | { |
61 | DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams)); | 61 | DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams)); |
62 | errnum = ENXIO; | 62 | errnum = ENXIO; |
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index b93ee73fc6..a3538ff96f 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c | |||
@@ -41,9 +41,7 @@ | |||
41 | #include "audio.h" | 41 | #include "audio.h" |
42 | #include "rbpaths.h" | 42 | #include "rbpaths.h" |
43 | #include "linked_list.h" | 43 | #include "linked_list.h" |
44 | #ifdef HAVE_EEPROM_SETTINGS | ||
45 | #include "crc32.h" | 44 | #include "crc32.h" |
46 | #endif | ||
47 | 45 | ||
48 | /** | 46 | /** |
49 | * Cache memory layout: | 47 | * Cache memory layout: |
@@ -1457,7 +1455,7 @@ int dircache_readdir_dirent(struct filestr_base *stream, | |||
1457 | unsigned int direntry = scanp->fatscan.entry; | 1455 | unsigned int direntry = scanp->fatscan.entry; |
1458 | while (1) | 1456 | while (1) |
1459 | { | 1457 | { |
1460 | if (idx == 0 || direntry == FAT_RW_VAL) /* rewound? */ | 1458 | if (idx == 0 || direntry == FAT_DIRSCAN_RW_VAL) /* rewound? */ |
1461 | { | 1459 | { |
1462 | idx = diridx <= 0 ? dcvolp->root_down : get_entry(diridx)->down; | 1460 | idx = diridx <= 0 ? dcvolp->root_down : get_entry(diridx)->down; |
1463 | break; | 1461 | break; |
@@ -1487,7 +1485,7 @@ int dircache_readdir_dirent(struct filestr_base *stream, | |||
1487 | return 0; /* end of dir */ | 1485 | return 0; /* end of dir */ |
1488 | } | 1486 | } |
1489 | 1487 | ||
1490 | if (ce->direntry > direntry || direntry == FAT_RW_VAL) | 1488 | if (ce->direntry > direntry || direntry == FAT_DIRSCAN_RW_VAL) |
1491 | break; /* cache reader is caught up to FS scan */ | 1489 | break; /* cache reader is caught up to FS scan */ |
1492 | 1490 | ||
1493 | idx = ce->next; | 1491 | idx = ce->next; |
@@ -1549,7 +1547,12 @@ int dircache_readdir_internal(struct filestr_base *stream, | |||
1549 | 1547 | ||
1550 | /* is parent cached? if not, readthrough because nothing is here yet */ | 1548 | /* is parent cached? if not, readthrough because nothing is here yet */ |
1551 | if (!dirinfop->dcfile.serialnum) | 1549 | if (!dirinfop->dcfile.serialnum) |
1550 | { | ||
1551 | if (stream->flags & FF_CACHEONLY) | ||
1552 | goto read_eod; | ||
1553 | |||
1552 | return uncached_readdir_internal(stream, infop, fatent); | 1554 | return uncached_readdir_internal(stream, infop, fatent); |
1555 | } | ||
1553 | 1556 | ||
1554 | int diridx = dirinfop->dcfile.idx; | 1557 | int diridx = dirinfop->dcfile.idx; |
1555 | unsigned int frontier = diridx < 0 ? | 1558 | unsigned int frontier = diridx < 0 ? |
@@ -1562,7 +1565,8 @@ int dircache_readdir_internal(struct filestr_base *stream, | |||
1562 | idx = get_entry(idx)->next; | 1565 | idx = get_entry(idx)->next; |
1563 | 1566 | ||
1564 | struct dircache_entry *ce = get_entry(idx); | 1567 | struct dircache_entry *ce = get_entry(idx); |
1565 | if (frontier != FRONTIER_SETTLED) | 1568 | |
1569 | if (frontier != FRONTIER_SETTLED && !(stream->flags & FF_CACHEONLY)) | ||
1566 | { | 1570 | { |
1567 | /* the directory being read is reported to be incompletely cached; | 1571 | /* the directory being read is reported to be incompletely cached; |
1568 | readthrough and if the entry exists, return it with its binding | 1572 | readthrough and if the entry exists, return it with its binding |
@@ -1577,9 +1581,7 @@ int dircache_readdir_internal(struct filestr_base *stream, | |||
1577 | else if (!ce) | 1581 | else if (!ce) |
1578 | { | 1582 | { |
1579 | /* end of dir */ | 1583 | /* end of dir */ |
1580 | fat_empty_fat_direntry(fatent); | 1584 | goto read_eod; |
1581 | infop->fatfile.e.entries = 0; | ||
1582 | return 0; | ||
1583 | } | 1585 | } |
1584 | 1586 | ||
1585 | /* FS entry information that we maintain */ | 1587 | /* FS entry information that we maintain */ |
@@ -1612,6 +1614,11 @@ int dircache_readdir_internal(struct filestr_base *stream, | |||
1612 | } | 1614 | } |
1613 | 1615 | ||
1614 | return rc; | 1616 | return rc; |
1617 | |||
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 | } |