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