From 8d4fd46a4c211f0f51e332b54e073b58c6685378 Mon Sep 17 00:00:00 2001 From: geky bot Date: Thu, 11 Apr 2019 02:08:05 +0000 Subject: [PATCH] Generated v2 prefixes --- .gitignore | 2 +- .travis.yml | 64 +- Makefile | 6 +- README.md | 40 +- SPEC.md | 34 +- emubd/{lfs_emubd.c => lfs2_emubd.c} | 104 +- emubd/{lfs_emubd.h => lfs2_emubd.h} | 46 +- lfs.c | 4459 --------------------------- lfs2.c | 4459 +++++++++++++++++++++++++++ lfs.h => lfs2.h | 440 +-- lfs_util.c => lfs2_util.c | 8 +- lfs_util.h => lfs2_util.h | 118 +- scripts/prefix.py | 4 +- tests/template.fmt | 80 +- tests/test.py | 6 +- tests/test_alloc.sh | 404 +-- tests/test_attrs.sh | 266 +- tests/test_corrupt.sh | 56 +- tests/test_dirs.sh | 538 ++-- tests/test_entries.sh | 120 +- tests/test_files.sh | 104 +- tests/test_format.sh | 28 +- tests/test_interspersed.sh | 192 +- tests/test_move.sh | 370 +-- tests/test_orphan.sh | 34 +- tests/test_paths.sh | 214 +- tests/test_seek.sh | 320 +- tests/test_truncate.sh | 80 +- 28 files changed, 6298 insertions(+), 6298 deletions(-) rename emubd/{lfs_emubd.c => lfs2_emubd.c} (66%) rename emubd/{lfs_emubd.h => lfs2_emubd.h} (51%) delete mode 100644 lfs.c create mode 100644 lfs2.c rename lfs.h => lfs2.h (56%) rename lfs_util.c => lfs2_util.c (84%) rename lfs_util.h => lfs2_util.h (59%) diff --git a/.gitignore b/.gitignore index 36f92cd..30a43f9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ # Testing things blocks/ -lfs +lfs2 test.c diff --git a/.travis.yml b/.travis.yml index a69ecea..3b8292b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,17 +18,17 @@ script: - make test QUIET=1 # run tests with a few different configurations - - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=4" - - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16" - - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256" + - make test QUIET=1 CFLAGS+="-DLFS2_READ_SIZE=1 -DLFS2_CACHE_SIZE=4" + - make test QUIET=1 CFLAGS+="-DLFS2_READ_SIZE=512 -DLFS2_CACHE_SIZE=512 -DLFS2_BLOCK_CYCLES=16" + - make test QUIET=1 CFLAGS+="-DLFS2_BLOCK_COUNT=1023 -DLFS2_LOOKAHEAD_SIZE=256" - - make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0" - - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" + - make clean test QUIET=1 CFLAGS+="-DLFS2_INLINE_MAX=0" + - make clean test QUIET=1 CFLAGS+="-DLFS2_NO_INTRINSICS" # compile and find the code size with the smallest configuration - make clean size - OBJ="$(ls lfs*.o | tr '\n' ' ')" - CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR" + OBJ="$(ls lfs2*.o | tr '\n' ' ')" + CFLAGS+="-DLFS2_NO_ASSERT -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR" | tee sizes # update status if we succeeded, compare with master if possible @@ -120,8 +120,8 @@ jobs: # self-host test - make -C littlefs-fuse - - littlefs-fuse/lfs --format /dev/loop0 - - littlefs-fuse/lfs /dev/loop0 mount + - littlefs-fuse/lfs2 --format /dev/loop0 + - littlefs-fuse/lfs2 /dev/loop0 mount - ls mount - mkdir mount/littlefs @@ -157,8 +157,8 @@ jobs: - make -C v2 # run self-host test with v1 - - v1/lfs --format /dev/loop0 - - v1/lfs /dev/loop0 mount + - v1/lfs2 --format /dev/loop0 + - v1/lfs2 /dev/loop0 mount - ls mount - mkdir mount/littlefs @@ -172,8 +172,8 @@ jobs: - cd ../.. - fusermount -u mount - - v2/lfs --migrate /dev/loop0 - - v2/lfs /dev/loop0 mount + - v2/lfs2 --migrate /dev/loop0 + - v2/lfs2 /dev/loop0 mount # run self-host test with v2 right where we left off - ls mount @@ -191,55 +191,55 @@ jobs: - | bash << 'SCRIPT' set -ev - # Find version defined in lfs.h - LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3) - LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16))) - LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >> 0))) + # Find version defined in lfs2.h + LFS2_VERSION=$(grep -ox '#define LFS2_VERSION .*' lfs2.h | cut -d ' ' -f3) + LFS2_VERSION_MAJOR=$((0xffff & ($LFS2_VERSION >> 16))) + LFS2_VERSION_MINOR=$((0xffff & ($LFS2_VERSION >> 0))) # Grab latests patch from repo tags, default to 0, needs finagling # to get past github's pagination api - PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR. + PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS2_VERSION_MAJOR.$LFS2_VERSION_MINOR. PREV_URL=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" -I \ | sed -n '/^Link/{s/.*<\(.*\)>; rel="last"/\1/;p;q0};$q1' \ || echo $PREV_URL) - LFS_VERSION_PATCH=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" \ + LFS2_VERSION_PATCH=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" \ | jq 'map(.ref | match("\\bv.*\\..*\\.(.*)$";"g") .captures[].string | tonumber) | max + 1' \ || echo 0) # We have our new version - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH" - echo "VERSION $LFS_VERSION" + LFS2_VERSION="v$LFS2_VERSION_MAJOR.$LFS2_VERSION_MINOR.$LFS2_VERSION_PATCH" + echo "VERSION $LFS2_VERSION" # Check that we're the most recent commit CURRENT_COMMIT=$(curl -f -u "$GEKY_BOT_RELEASES" \ https://api.github.com/repos/$TRAVIS_REPO_SLUG/commits/master \ | jq -re '.sha') [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] || exit 0 # Create major branch - git branch v$LFS_VERSION_MAJOR HEAD + git branch v$LFS2_VERSION_MAJOR HEAD # Create major prefix branch git config user.name "geky bot" git config user.email "bot@geky.net" git fetch https://github.com/$TRAVIS_REPO_SLUG.git \ - --depth=50 v$LFS_VERSION_MAJOR-prefix || true - ./scripts/prefix.py lfs$LFS_VERSION_MAJOR - git branch v$LFS_VERSION_MAJOR-prefix $( \ + --depth=50 v$LFS2_VERSION_MAJOR-prefix || true + ./scripts/prefix.py lfs2$LFS2_VERSION_MAJOR + git branch v$LFS2_VERSION_MAJOR-prefix $( \ git commit-tree $(git write-tree) \ $(git rev-parse --verify -q FETCH_HEAD | sed -e 's/^/-p /') \ -p HEAD \ - -m "Generated v$LFS_VERSION_MAJOR prefixes") + -m "Generated v$LFS2_VERSION_MAJOR prefixes") git reset --hard # Update major version branches (vN and vN-prefix) git push https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \ - v$LFS_VERSION_MAJOR \ - v$LFS_VERSION_MAJOR-prefix + v$LFS2_VERSION_MAJOR \ + v$LFS2_VERSION_MAJOR-prefix # Create patch version tag (vN.N.N) curl -f -u "$GEKY_BOT_RELEASES" -X POST \ https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \ -d "{ - \"ref\": \"refs/tags/$LFS_VERSION\", + \"ref\": \"refs/tags/$LFS2_VERSION\", \"sha\": \"$TRAVIS_COMMIT\" }" # Create minor release? - [[ "$LFS_VERSION" == *.0 ]] || exit 0 + [[ "$LFS2_VERSION" == *.0 ]] || exit 0 # Build release notes PREV=$(git tag --sort=-v:refname -l "v*.0" | head -1) if [ ! -z "$PREV" ] @@ -253,8 +253,8 @@ jobs: curl -f -u "$GEKY_BOT_RELEASES" -X POST \ https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \ -d "{ - \"tag_name\": \"$LFS_VERSION\", - \"name\": \"${LFS_VERSION%.0}\", + \"tag_name\": \"$LFS2_VERSION\", + \"name\": \"${LFS2_VERSION%.0}\", \"draft\": true, \"body\": $(jq -sR '.' <<< "$CHANGES") }" #" diff --git a/Makefile b/Makefile index 185d8e5..a22428b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -TARGET = lfs.a +TARGET = lfs2.a ifneq ($(wildcard test.c main.c),) -override TARGET = lfs +override TARGET = lfs2 endif CC ?= gcc @@ -53,7 +53,7 @@ endif -include $(DEP) -lfs: $(OBJ) +lfs2: $(OBJ) $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ %.a: $(OBJ) diff --git a/README.md b/README.md index b50dd31..ea27c40 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,14 @@ main runs. The program can be interrupted at any time without losing track of how many times it has been booted and without corrupting the filesystem: ``` c -#include "lfs.h" +#include "lfs2.h" // variables used by the filesystem -lfs_t lfs; -lfs_file_t file; +lfs2_t lfs2; +lfs2_file_t file; // configuration of the filesystem is provided by this struct -const struct lfs_config cfg = { +const struct lfs2_config cfg = { // block device operations .read = user_provided_block_device_read, .prog = user_provided_block_device_prog, @@ -58,30 +58,30 @@ const struct lfs_config cfg = { // entry point int main(void) { // mount the filesystem - int err = lfs_mount(&lfs, &cfg); + int err = lfs2_mount(&lfs2, &cfg); // reformat if we can't mount the filesystem // this should only happen on the first boot if (err) { - lfs_format(&lfs, &cfg); - lfs_mount(&lfs, &cfg); + lfs2_format(&lfs2, &cfg); + lfs2_mount(&lfs2, &cfg); } // read current count uint32_t boot_count = 0; - lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); - lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count)); + lfs2_file_open(&lfs2, &file, "boot_count", LFS2_O_RDWR | LFS2_O_CREAT); + lfs2_file_read(&lfs2, &file, &boot_count, sizeof(boot_count)); // update boot count boot_count += 1; - lfs_file_rewind(&lfs, &file); - lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count)); + lfs2_file_rewind(&lfs2, &file); + lfs2_file_write(&lfs2, &file, &boot_count, sizeof(boot_count)); // remember the storage is not updated until the file is closed successfully - lfs_file_close(&lfs, &file); + lfs2_file_close(&lfs2, &file); // release any resources we were using - lfs_unmount(&lfs); + lfs2_unmount(&lfs2); // print the boot count printf("boot_count: %d\n", boot_count); @@ -91,7 +91,7 @@ int main(void) { ## Usage Detailed documentation (or at least as much detail as is currently available) -can be found in the comments in [lfs.h](lfs.h). +can be found in the comments in [lfs2.h](lfs2.h). littlefs takes in a configuration structure that defines how the filesystem operates. The configuration struct provides the filesystem with the block @@ -99,9 +99,9 @@ device operations and dimensions, tweakable parameters that tradeoff memory usage for performance, and optional static buffers if the user wants to avoid dynamic memory. -The state of the littlefs is stored in the `lfs_t` type which is left up +The state of the littlefs is stored in the `lfs2_t` type which is left up to the user to allocate, allowing multiple filesystems to be in use -simultaneously. With the `lfs_t` and configuration struct, a user can +simultaneously. With the `lfs2_t` and configuration struct, a user can format a block device or mount the filesystem. Once mounted, the littlefs provides a full set of POSIX-like file and @@ -115,11 +115,11 @@ the filesystem until sync or close is called on the file. ## Other notes All littlefs calls have the potential to return a negative error code. The -errors can be either one of those found in the `enum lfs_error` in -[lfs.h](lfs.h), or an error returned by the user's block device operations. +errors can be either one of those found in the `enum lfs2_error` in +[lfs2.h](lfs2.h), or an error returned by the user's block device operations. In the configuration struct, the `prog` and `erase` function provided by the -user may return a `LFS_ERR_CORRUPT` error if the implementation already can +user may return a `LFS2_ERR_CORRUPT` error if the implementation already can detect corrupt blocks. However, the wear leveling does not depend on the return code of these functions, instead all data is read back and checked for integrity. @@ -188,7 +188,7 @@ More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and ## Testing The littlefs comes with a test suite designed to run on a PC using the -[emulated block device](emubd/lfs_emubd.h) found in the emubd directory. +[emulated block device](emubd/lfs2_emubd.h) found in the emubd directory. The tests assume a Linux environment and can be started with make: ``` bash diff --git a/SPEC.md b/SPEC.md index e6622d3..d2e7ae6 100644 --- a/SPEC.md +++ b/SPEC.md @@ -253,7 +253,7 @@ Metadata tag fields: What follows is an exhaustive list of metadata in littlefs. --- -#### `0x401` LFS_TYPE_CREATE +#### `0x401` LFS2_TYPE_CREATE Creates a new file with this id. Note that files in a metadata block don't necessarily need a create tag. All a create does is move over any @@ -264,14 +264,14 @@ The create and delete tags allow littlefs to keep files in a directory ordered alphabetically by filename. --- -#### `0x4ff` LFS_TYPE_DELETE +#### `0x4ff` LFS2_TYPE_DELETE Deletes the file with this id. An inverse to create, this tag moves over any files neighboring this id similar to a deletion from an imaginary array of files. --- -#### `0x0xx` LFS_TYPE_NAME +#### `0x0xx` LFS2_TYPE_NAME Associates the id with a file name and file type. @@ -304,14 +304,14 @@ Name fields: 2. **file name** - File name stored as an ASCII string. --- -#### `0x001` LFS_TYPE_REG +#### `0x001` LFS2_TYPE_REG Initializes the id + name as a regular file. How each file is stored depends on its struct tag, which is described below. --- -#### `0x002` LFS_TYPE_DIR +#### `0x002` LFS2_TYPE_DIR Initializes the id + name as a directory. @@ -320,7 +320,7 @@ each pair containing any number of files in alphabetical order. A pointer to the directory is stored in the struct tag, which is described below. --- -#### `0x0ff` LFS_TYPE_SUPERBLOCK +#### `0x0ff` LFS2_TYPE_SUPERBLOCK Initializes the id as a superblock entry. @@ -405,7 +405,7 @@ as be the first entry written to the block. This means that the superblock entry can be read from a device using offsets alone. --- -#### `0x2xx` LFS_TYPE_STRUCT +#### `0x2xx` LFS2_TYPE_STRUCT Associates the id with an on-disk data structure. @@ -416,7 +416,7 @@ Any type of struct supersedes all other structs associated with the id. For example, appending a ctz-struct replaces an inline-struct on the same file. --- -#### `0x200` LFS_TYPE_DIRSTRUCT +#### `0x200` LFS2_TYPE_DIRSTRUCT Gives the id a directory data structure. @@ -458,7 +458,7 @@ Dir-struct fields: in the directory. --- -#### `0x201` LFS_TYPE_INLINESTRUCT +#### `0x201` LFS2_TYPE_INLINESTRUCT Gives the id an inline data structure. @@ -482,7 +482,7 @@ Inline-struct fields: 1. **Inline data** - File data stored directly in the metadata-pair. --- -#### `0x202` LFS_TYPE_CTZSTRUCT +#### `0x202` LFS2_TYPE_CTZSTRUCT Gives the id a CTZ skip-list data structure. @@ -537,7 +537,7 @@ CTZ-struct fields: 2. **File size (32-bits)** - Size of the file in bytes. --- -#### `0x3xx` LFS_TYPE_USERATTR +#### `0x3xx` LFS2_TYPE_USERATTR Attaches a user attribute to an id. @@ -571,7 +571,7 @@ User-attr fields: 2. **Attr data** - The data associated with the user attribute. --- -#### `0x6xx` LFS_TYPE_TAIL +#### `0x6xx` LFS2_TYPE_TAIL Provides the tail pointer for the metadata pair itself. @@ -637,7 +637,7 @@ Tail fields: 2. **Metadata pair (8-bytes)** - Pointer to the next metadata-pair. --- -#### `0x600` LFS_TYPE_SOFTTAIL +#### `0x600` LFS2_TYPE_SOFTTAIL Provides a tail pointer that points to the next metadata pair in the filesystem. @@ -646,7 +646,7 @@ In this case, the next metadata pair is not a part of our current directory and should only be followed when traversing the entire filesystem. --- -#### `0x601` LFS_TYPE_HARDTAIL +#### `0x601` LFS2_TYPE_HARDTAIL Provides a tail pointer that points to the next metadata pair in the directory. @@ -657,7 +657,7 @@ metadata pair should only contain filenames greater than any filename in the current pair. --- -#### `0x7xx` LFS_TYPE_GSTATE +#### `0x7xx` LFS2_TYPE_GSTATE Provides delta bits for global state entries. @@ -687,7 +687,7 @@ is stored in the chunk field. Currently, the only global state is move state, which is outlined below. --- -#### `0x7ff` LFS_TYPE_MOVESTATE +#### `0x7ff` LFS2_TYPE_MOVESTATE Provides delta bits for the global move state. @@ -740,7 +740,7 @@ Move state fields: the move. --- -#### `0x5xx` LFS_TYPE_CRC +#### `0x5xx` LFS2_TYPE_CRC Last but not least, the CRC tag marks the end of a commit and provides a checksum for any commits to the metadata block. diff --git a/emubd/lfs_emubd.c b/emubd/lfs2_emubd.c similarity index 66% rename from emubd/lfs_emubd.c rename to emubd/lfs2_emubd.c index 3f31bfa..3ff87e7 100644 --- a/emubd/lfs_emubd.c +++ b/emubd/lfs2_emubd.c @@ -4,7 +4,7 @@ * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ -#include "emubd/lfs_emubd.h" +#include "emubd/lfs2_emubd.h" #include #include @@ -20,42 +20,42 @@ // Emulated block device utils -static inline void lfs_emubd_tole32(lfs_emubd_t *emu) { - emu->cfg.read_size = lfs_tole32(emu->cfg.read_size); - emu->cfg.prog_size = lfs_tole32(emu->cfg.prog_size); - emu->cfg.block_size = lfs_tole32(emu->cfg.block_size); - emu->cfg.block_count = lfs_tole32(emu->cfg.block_count); +static inline void lfs2_emubd_tole32(lfs2_emubd_t *emu) { + emu->cfg.read_size = lfs2_tole32(emu->cfg.read_size); + emu->cfg.prog_size = lfs2_tole32(emu->cfg.prog_size); + emu->cfg.block_size = lfs2_tole32(emu->cfg.block_size); + emu->cfg.block_count = lfs2_tole32(emu->cfg.block_count); - emu->stats.read_count = lfs_tole32(emu->stats.read_count); - emu->stats.prog_count = lfs_tole32(emu->stats.prog_count); - emu->stats.erase_count = lfs_tole32(emu->stats.erase_count); + emu->stats.read_count = lfs2_tole32(emu->stats.read_count); + emu->stats.prog_count = lfs2_tole32(emu->stats.prog_count); + emu->stats.erase_count = lfs2_tole32(emu->stats.erase_count); for (unsigned i = 0; i < sizeof(emu->history.blocks) / sizeof(emu->history.blocks[0]); i++) { - emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]); + emu->history.blocks[i] = lfs2_tole32(emu->history.blocks[i]); } } -static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) { - emu->cfg.read_size = lfs_fromle32(emu->cfg.read_size); - emu->cfg.prog_size = lfs_fromle32(emu->cfg.prog_size); - emu->cfg.block_size = lfs_fromle32(emu->cfg.block_size); - emu->cfg.block_count = lfs_fromle32(emu->cfg.block_count); +static inline void lfs2_emubd_fromle32(lfs2_emubd_t *emu) { + emu->cfg.read_size = lfs2_fromle32(emu->cfg.read_size); + emu->cfg.prog_size = lfs2_fromle32(emu->cfg.prog_size); + emu->cfg.block_size = lfs2_fromle32(emu->cfg.block_size); + emu->cfg.block_count = lfs2_fromle32(emu->cfg.block_count); - emu->stats.read_count = lfs_fromle32(emu->stats.read_count); - emu->stats.prog_count = lfs_fromle32(emu->stats.prog_count); - emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count); + emu->stats.read_count = lfs2_fromle32(emu->stats.read_count); + emu->stats.prog_count = lfs2_fromle32(emu->stats.prog_count); + emu->stats.erase_count = lfs2_fromle32(emu->stats.erase_count); for (unsigned i = 0; i < sizeof(emu->history.blocks) / sizeof(emu->history.blocks[0]); i++) { - emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]); + emu->history.blocks[i] = lfs2_fromle32(emu->history.blocks[i]); } } // Block device emulated on existing filesystem -int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { - lfs_emubd_t *emu = cfg->context; +int lfs2_emubd_create(const struct lfs2_config *cfg, const char *path) { + lfs2_emubd_t *emu = cfg->context; emu->cfg.read_size = cfg->read_size; emu->cfg.prog_size = cfg->prog_size; emu->cfg.block_size = cfg->block_size; @@ -63,7 +63,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { // Allocate buffer for creating children files size_t pathlen = strlen(path); - emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1); + emu->path = malloc(pathlen + 1 + LFS2_NAME_MAX + 1); if (!emu->path) { return -ENOMEM; } @@ -71,7 +71,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { strcpy(emu->path, path); emu->path[pathlen] = '/'; emu->child = &emu->path[pathlen+1]; - memset(emu->child, '\0', LFS_NAME_MAX+1); + memset(emu->child, '\0', LFS2_NAME_MAX+1); // Create directory if it doesn't exist int err = mkdir(path, 0777); @@ -80,13 +80,13 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { } // Load stats to continue incrementing - snprintf(emu->child, LFS_NAME_MAX, ".stats"); + snprintf(emu->child, LFS2_NAME_MAX, ".stats"); FILE *f = fopen(emu->path, "r"); if (!f) { memset(&emu->stats, 0, sizeof(emu->stats)); } else { size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); - lfs_emubd_fromle32(emu); + lfs2_emubd_fromle32(emu); if (res < 1) { return -errno; } @@ -98,13 +98,13 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { } // Load history - snprintf(emu->child, LFS_NAME_MAX, ".history"); + snprintf(emu->child, LFS2_NAME_MAX, ".history"); f = fopen(emu->path, "r"); if (!f) { memset(&emu->history, 0, sizeof(emu->history)); } else { size_t res = fread(&emu->history, sizeof(emu->history), 1, f); - lfs_emubd_fromle32(emu); + lfs2_emubd_fromle32(emu); if (res < 1) { return -errno; } @@ -118,16 +118,16 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { return 0; } -void lfs_emubd_destroy(const struct lfs_config *cfg) { - lfs_emubd_sync(cfg); +void lfs2_emubd_destroy(const struct lfs2_config *cfg) { + lfs2_emubd_sync(cfg); - lfs_emubd_t *emu = cfg->context; + lfs2_emubd_t *emu = cfg->context; free(emu->path); } -int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size) { - lfs_emubd_t *emu = cfg->context; +int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block, + lfs2_off_t off, void *buffer, lfs2_size_t size) { + lfs2_emubd_t *emu = cfg->context; uint8_t *data = buffer; // Check if read is valid @@ -139,7 +139,7 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, memset(data, 0, size); // Read data - snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); + snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block); FILE *f = fopen(emu->path, "rb"); if (!f && errno != ENOENT) { @@ -167,9 +167,9 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, return 0; } -int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size) { - lfs_emubd_t *emu = cfg->context; +int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block, + lfs2_off_t off, const void *buffer, lfs2_size_t size) { + lfs2_emubd_t *emu = cfg->context; const uint8_t *data = buffer; // Check if write is valid @@ -178,7 +178,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, assert(block < cfg->block_count); // Program data - snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); + snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block); FILE *f = fopen(emu->path, "r+b"); if (!f) { @@ -225,14 +225,14 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, return 0; } -int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { - lfs_emubd_t *emu = cfg->context; +int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block) { + lfs2_emubd_t *emu = cfg->context; // Check if erase is valid assert(block < cfg->block_count); // Erase the block - snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); + snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block); struct stat st; int err = stat(emu->path, &st); if (err && errno != ENOENT) { @@ -262,19 +262,19 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { return 0; } -int lfs_emubd_sync(const struct lfs_config *cfg) { - lfs_emubd_t *emu = cfg->context; +int lfs2_emubd_sync(const struct lfs2_config *cfg) { + lfs2_emubd_t *emu = cfg->context; // Just write out info/stats for later lookup - snprintf(emu->child, LFS_NAME_MAX, ".config"); + snprintf(emu->child, LFS2_NAME_MAX, ".config"); FILE *f = fopen(emu->path, "w"); if (!f) { return -errno; } - lfs_emubd_tole32(emu); + lfs2_emubd_tole32(emu); size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); - lfs_emubd_fromle32(emu); + lfs2_emubd_fromle32(emu); if (res < 1) { return -errno; } @@ -284,15 +284,15 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { return -errno; } - snprintf(emu->child, LFS_NAME_MAX, ".stats"); + snprintf(emu->child, LFS2_NAME_MAX, ".stats"); f = fopen(emu->path, "w"); if (!f) { return -errno; } - lfs_emubd_tole32(emu); + lfs2_emubd_tole32(emu); res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); - lfs_emubd_fromle32(emu); + lfs2_emubd_fromle32(emu); if (res < 1) { return -errno; } @@ -302,15 +302,15 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { return -errno; } - snprintf(emu->child, LFS_NAME_MAX, ".history"); + snprintf(emu->child, LFS2_NAME_MAX, ".history"); f = fopen(emu->path, "w"); if (!f) { return -errno; } - lfs_emubd_tole32(emu); + lfs2_emubd_tole32(emu); res = fwrite(&emu->history, sizeof(emu->history), 1, f); - lfs_emubd_fromle32(emu); + lfs2_emubd_fromle32(emu); if (res < 1) { return -errno; } diff --git a/emubd/lfs_emubd.h b/emubd/lfs2_emubd.h similarity index 51% rename from emubd/lfs_emubd.h rename to emubd/lfs2_emubd.h index 64afa3e..42dbfe5 100644 --- a/emubd/lfs_emubd.h +++ b/emubd/lfs2_emubd.h @@ -4,11 +4,11 @@ * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef LFS_EMUBD_H -#define LFS_EMUBD_H +#ifndef LFS2_EMUBD_H +#define LFS2_EMUBD_H -#include "lfs.h" -#include "lfs_util.h" +#include "lfs2.h" +#include "lfs2_util.h" #ifdef __cplusplus extern "C" @@ -17,25 +17,25 @@ extern "C" // Config options -#ifndef LFS_EMUBD_READ_SIZE -#define LFS_EMUBD_READ_SIZE 1 +#ifndef LFS2_EMUBD_READ_SIZE +#define LFS2_EMUBD_READ_SIZE 1 #endif -#ifndef LFS_EMUBD_PROG_SIZE -#define LFS_EMUBD_PROG_SIZE 1 +#ifndef LFS2_EMUBD_PROG_SIZE +#define LFS2_EMUBD_PROG_SIZE 1 #endif -#ifndef LFS_EMUBD_ERASE_SIZE -#define LFS_EMUBD_ERASE_SIZE 512 +#ifndef LFS2_EMUBD_ERASE_SIZE +#define LFS2_EMUBD_ERASE_SIZE 512 #endif -#ifndef LFS_EMUBD_TOTAL_SIZE -#define LFS_EMUBD_TOTAL_SIZE 524288 +#ifndef LFS2_EMUBD_TOTAL_SIZE +#define LFS2_EMUBD_TOTAL_SIZE 524288 #endif // The emu bd state -typedef struct lfs_emubd { +typedef struct lfs2_emubd { char *path; char *child; @@ -46,7 +46,7 @@ typedef struct lfs_emubd { } stats; struct { - lfs_block_t blocks[4]; + lfs2_block_t blocks[4]; } history; struct { @@ -55,33 +55,33 @@ typedef struct lfs_emubd { uint32_t block_size; uint32_t block_count; } cfg; -} lfs_emubd_t; +} lfs2_emubd_t; // Create a block device using path for the directory to store blocks -int lfs_emubd_create(const struct lfs_config *cfg, const char *path); +int lfs2_emubd_create(const struct lfs2_config *cfg, const char *path); // Clean up memory associated with emu block device -void lfs_emubd_destroy(const struct lfs_config *cfg); +void lfs2_emubd_destroy(const struct lfs2_config *cfg); // Read a block -int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size); +int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block, + lfs2_off_t off, void *buffer, lfs2_size_t size); // Program a block // // The block must have previously been erased. -int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size); +int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block, + lfs2_off_t off, const void *buffer, lfs2_size_t size); // Erase a block // // A block must be erased before being programmed. The // state of an erased block is undefined. -int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block); +int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block); // Sync the block device -int lfs_emubd_sync(const struct lfs_config *cfg); +int lfs2_emubd_sync(const struct lfs2_config *cfg); #ifdef __cplusplus diff --git a/lfs.c b/lfs.c deleted file mode 100644 index 25c88c6..0000000 --- a/lfs.c +++ /dev/null @@ -1,4459 +0,0 @@ -/* - * The little filesystem - * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "lfs.h" -#include "lfs_util.h" - - -/// Caching block device operations /// -static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) { - // do not zero, cheaper if cache is readonly or only going to be - // written with identical data (during relocates) - (void)lfs; - rcache->block = 0xffffffff; -} - -static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) { - // zero to avoid information leak - memset(pcache->buffer, 0xff, lfs->cfg->prog_size); - pcache->block = 0xffffffff; -} - -static int lfs_bd_read(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_block_t block, lfs_off_t off, - void *buffer, lfs_size_t size) { - uint8_t *data = buffer; - LFS_ASSERT(block != 0xffffffff); - if (off+size > lfs->cfg->block_size) { - return LFS_ERR_CORRUPT; - } - - while (size > 0) { - lfs_size_t diff = size; - - if (pcache && block == pcache->block && - off < pcache->off + pcache->size) { - if (off >= pcache->off) { - // is already in pcache? - diff = lfs_min(diff, pcache->size - (off-pcache->off)); - memcpy(data, &pcache->buffer[off-pcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // pcache takes priority - diff = lfs_min(diff, pcache->off-off); - } - - if (block == rcache->block && - off < rcache->off + rcache->size) { - if (off >= rcache->off) { - // is already in rcache? - diff = lfs_min(diff, rcache->size - (off-rcache->off)); - memcpy(data, &rcache->buffer[off-rcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // rcache takes priority - diff = lfs_min(diff, rcache->off-off); - } - - if (size >= hint && off % lfs->cfg->read_size == 0 && - size >= lfs->cfg->read_size) { - // bypass cache? - diff = lfs_aligndown(diff, lfs->cfg->read_size); - int err = lfs->cfg->read(lfs->cfg, block, off, data, diff); - if (err) { - return err; - } - - data += diff; - off += diff; - size -= diff; - continue; - } - - // load to cache, first condition can no longer fail - LFS_ASSERT(block < lfs->cfg->block_count); - rcache->block = block; - rcache->off = lfs_aligndown(off, lfs->cfg->read_size); - rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size), - lfs_min(lfs->cfg->block_size - rcache->off, - lfs->cfg->cache_size)); - int err = lfs->cfg->read(lfs->cfg, rcache->block, - rcache->off, rcache->buffer, rcache->size); - if (err) { - return err; - } - } - - return 0; -} - -enum { - LFS_CMP_EQ = 0, - LFS_CMP_LT = 1, - LFS_CMP_GT = 2, -}; - -static int lfs_bd_cmp(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_block_t block, lfs_off_t off, - const void *buffer, lfs_size_t size) { - const uint8_t *data = buffer; - - for (lfs_off_t i = 0; i < size; i++) { - uint8_t dat; - int err = lfs_bd_read(lfs, - pcache, rcache, hint-i, - block, off+i, &dat, 1); - if (err) { - return err; - } - - if (dat != data[i]) { - return (dat < data[i]) ? LFS_CMP_LT : LFS_CMP_GT; - } - } - - return LFS_CMP_EQ; -} - -static int lfs_bd_flush(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { - if (pcache->block != 0xffffffff && pcache->block != 0xfffffffe) { - LFS_ASSERT(pcache->block < lfs->cfg->block_count); - lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size); - int err = lfs->cfg->prog(lfs->cfg, pcache->block, - pcache->off, pcache->buffer, diff); - if (err) { - return err; - } - - if (validate) { - // check data on disk - lfs_cache_drop(lfs, rcache); - int res = lfs_bd_cmp(lfs, - NULL, rcache, diff, - pcache->block, pcache->off, pcache->buffer, diff); - if (res < 0) { - return res; - } - - if (res != LFS_CMP_EQ) { - return LFS_ERR_CORRUPT; - } - } - - lfs_cache_zero(lfs, pcache); - } - - return 0; -} - -static int lfs_bd_sync(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { - lfs_cache_drop(lfs, rcache); - - int err = lfs_bd_flush(lfs, pcache, rcache, validate); - if (err) { - return err; - } - - return lfs->cfg->sync(lfs->cfg); -} - -static int lfs_bd_prog(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate, - lfs_block_t block, lfs_off_t off, - const void *buffer, lfs_size_t size) { - const uint8_t *data = buffer; - LFS_ASSERT(block != 0xffffffff); - LFS_ASSERT(off + size <= lfs->cfg->block_size); - - while (size > 0) { - if (block == pcache->block && - off >= pcache->off && - off < pcache->off + lfs->cfg->cache_size) { - // already fits in pcache? - lfs_size_t diff = lfs_min(size, - lfs->cfg->cache_size - (off-pcache->off)); - memcpy(&pcache->buffer[off-pcache->off], data, diff); - - data += diff; - off += diff; - size -= diff; - - pcache->size = off - pcache->off; - if (pcache->size == lfs->cfg->cache_size) { - // eagerly flush out pcache if we fill up - int err = lfs_bd_flush(lfs, pcache, rcache, validate); - if (err) { - return err; - } - } - - continue; - } - - // pcache must have been flushed, either by programming and - // entire block or manually flushing the pcache - LFS_ASSERT(pcache->block == 0xffffffff); - - // prepare pcache, first condition can no longer fail - pcache->block = block; - pcache->off = lfs_aligndown(off, lfs->cfg->prog_size); - pcache->size = 0; - } - - return 0; -} - -static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { - LFS_ASSERT(block < lfs->cfg->block_count); - return lfs->cfg->erase(lfs->cfg, block); -} - - -/// Small type-level utilities /// -// operations on block pairs -static inline void lfs_pair_swap(lfs_block_t pair[2]) { - lfs_block_t t = pair[0]; - pair[0] = pair[1]; - pair[1] = t; -} - -static inline bool lfs_pair_isnull(const lfs_block_t pair[2]) { - return pair[0] == 0xffffffff || pair[1] == 0xffffffff; -} - -static inline int lfs_pair_cmp( - const lfs_block_t paira[2], - const lfs_block_t pairb[2]) { - return !(paira[0] == pairb[0] || paira[1] == pairb[1] || - paira[0] == pairb[1] || paira[1] == pairb[0]); -} - -static inline bool lfs_pair_sync( - const lfs_block_t paira[2], - const lfs_block_t pairb[2]) { - return (paira[0] == pairb[0] && paira[1] == pairb[1]) || - (paira[0] == pairb[1] && paira[1] == pairb[0]); -} - -static inline void lfs_pair_fromle32(lfs_block_t pair[2]) { - pair[0] = lfs_fromle32(pair[0]); - pair[1] = lfs_fromle32(pair[1]); -} - -static inline void lfs_pair_tole32(lfs_block_t pair[2]) { - pair[0] = lfs_tole32(pair[0]); - pair[1] = lfs_tole32(pair[1]); -} - -// operations on 32-bit entry tags -typedef uint32_t lfs_tag_t; -typedef int32_t lfs_stag_t; - -#define LFS_MKTAG(type, id, size) \ - (((lfs_tag_t)(type) << 20) | ((lfs_tag_t)(id) << 10) | (lfs_tag_t)(size)) - -static inline bool lfs_tag_isvalid(lfs_tag_t tag) { - return !(tag & 0x80000000); -} - -static inline bool lfs_tag_isdelete(lfs_tag_t tag) { - return ((int32_t)(tag << 22) >> 22) == -1; -} - -static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { - return (tag & 0x70000000) >> 20; -} - -static inline uint16_t lfs_tag_type3(lfs_tag_t tag) { - return (tag & 0x7ff00000) >> 20; -} - -static inline uint8_t lfs_tag_chunk(lfs_tag_t tag) { - return (tag & 0x0ff00000) >> 20; -} - -static inline int8_t lfs_tag_splice(lfs_tag_t tag) { - return (int8_t)lfs_tag_chunk(tag); -} - -static inline uint16_t lfs_tag_id(lfs_tag_t tag) { - return (tag & 0x000ffc00) >> 10; -} - -static inline lfs_size_t lfs_tag_size(lfs_tag_t tag) { - return tag & 0x000003ff; -} - -static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { - return sizeof(tag) + lfs_tag_size(tag + lfs_tag_isdelete(tag)); -} - -// operations on attributes in attribute lists -struct lfs_mattr { - lfs_tag_t tag; - const void *buffer; -}; - -struct lfs_diskoff { - lfs_block_t block; - lfs_off_t off; -}; - -#define LFS_MKATTRS(...) \ - (struct lfs_mattr[]){__VA_ARGS__}, \ - sizeof((struct lfs_mattr[]){__VA_ARGS__}) / sizeof(struct lfs_mattr) - -// operations on global state -static inline void lfs_gstate_xor(struct lfs_gstate *a, - const struct lfs_gstate *b) { - for (int i = 0; i < 3; i++) { - ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; - } -} - -static inline bool lfs_gstate_iszero(const struct lfs_gstate *a) { - for (int i = 0; i < 3; i++) { - if (((uint32_t*)a)[i] != 0) { - return false; - } - } - return true; -} - -static inline bool lfs_gstate_hasorphans(const struct lfs_gstate *a) { - return lfs_tag_size(a->tag); -} - -static inline uint8_t lfs_gstate_getorphans(const struct lfs_gstate *a) { - return lfs_tag_size(a->tag); -} - -static inline bool lfs_gstate_hasmove(const struct lfs_gstate *a) { - return lfs_tag_type1(a->tag); -} - -static inline bool lfs_gstate_hasmovehere(const struct lfs_gstate *a, - const lfs_block_t *pair) { - return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0; -} - -static inline void lfs_gstate_xororphans(struct lfs_gstate *a, - const struct lfs_gstate *b, bool orphans) { - a->tag ^= LFS_MKTAG(0x800, 0, 0) & (b->tag ^ (orphans << 31)); -} - -static inline void lfs_gstate_xormove(struct lfs_gstate *a, - const struct lfs_gstate *b, uint16_t id, const lfs_block_t pair[2]) { - a->tag ^= LFS_MKTAG(0x7ff, 0x3ff, 0) & (b->tag ^ ( - (id != 0x3ff) ? LFS_MKTAG(LFS_TYPE_DELETE, id, 0) : 0)); - a->pair[0] ^= b->pair[0] ^ ((id != 0x3ff) ? pair[0] : 0); - a->pair[1] ^= b->pair[1] ^ ((id != 0x3ff) ? pair[1] : 0); -} - -static inline void lfs_gstate_fromle32(struct lfs_gstate *a) { - a->tag = lfs_fromle32(a->tag); - a->pair[0] = lfs_fromle32(a->pair[0]); - a->pair[1] = lfs_fromle32(a->pair[1]); -} - -static inline void lfs_gstate_tole32(struct lfs_gstate *a) { - a->tag = lfs_tole32(a->tag); - a->pair[0] = lfs_tole32(a->pair[0]); - a->pair[1] = lfs_tole32(a->pair[1]); -} - -// other endianness operations -static void lfs_ctz_fromle32(struct lfs_ctz *ctz) { - ctz->head = lfs_fromle32(ctz->head); - ctz->size = lfs_fromle32(ctz->size); -} - -static void lfs_ctz_tole32(struct lfs_ctz *ctz) { - ctz->head = lfs_tole32(ctz->head); - ctz->size = lfs_tole32(ctz->size); -} - -static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) { - superblock->version = lfs_fromle32(superblock->version); - superblock->block_size = lfs_fromle32(superblock->block_size); - superblock->block_count = lfs_fromle32(superblock->block_count); - superblock->name_max = lfs_fromle32(superblock->name_max); - superblock->file_max = lfs_fromle32(superblock->file_max); - superblock->attr_max = lfs_fromle32(superblock->attr_max); -} - -static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) { - superblock->version = lfs_tole32(superblock->version); - superblock->block_size = lfs_tole32(superblock->block_size); - superblock->block_count = lfs_tole32(superblock->block_count); - superblock->name_max = lfs_tole32(superblock->name_max); - superblock->file_max = lfs_tole32(superblock->file_max); - superblock->attr_max = lfs_tole32(superblock->attr_max); -} - - -/// Internal operations predeclared here /// -static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs, int attrcount); -static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t begin, uint16_t end); -static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file); -static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file); -static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); -static void lfs_fs_prepmove(lfs_t *lfs, - uint16_t id, const lfs_block_t pair[2]); -static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], - lfs_mdir_t *pdir); -static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], - lfs_mdir_t *parent); -static int lfs_fs_relocate(lfs_t *lfs, - const lfs_block_t oldpair[2], lfs_block_t newpair[2]); -static int lfs_fs_forceconsistency(lfs_t *lfs); -static int lfs_deinit(lfs_t *lfs); -#ifdef LFS_MIGRATE -static int lfs1_traverse(lfs_t *lfs, - int (*cb)(void*, lfs_block_t), void *data); -#endif - -/// Block allocator /// -static int lfs_alloc_lookahead(void *p, lfs_block_t block) { - lfs_t *lfs = (lfs_t*)p; - lfs_block_t off = ((block - lfs->free.off) - + lfs->cfg->block_count) % lfs->cfg->block_count; - - if (off < lfs->free.size) { - lfs->free.buffer[off / 32] |= 1U << (off % 32); - } - - return 0; -} - -static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { - while (true) { - while (lfs->free.i != lfs->free.size) { - lfs_block_t off = lfs->free.i; - lfs->free.i += 1; - lfs->free.ack -= 1; - - if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { - // found a free block - *block = (lfs->free.off + off) % lfs->cfg->block_count; - - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs->free.i != lfs->free.size && - (lfs->free.buffer[lfs->free.i / 32] - & (1U << (lfs->free.i % 32)))) { - lfs->free.i += 1; - lfs->free.ack -= 1; - } - - return 0; - } - } - - // check if we have looked at all blocks since last ack - if (lfs->free.ack == 0) { - LFS_WARN("No more free space %"PRIu32, - lfs->free.i + lfs->free.off); - return LFS_ERR_NOSPC; - } - - lfs->free.off = (lfs->free.off + lfs->free.size) - % lfs->cfg->block_count; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack); - lfs->free.i = 0; - - // find mask of free blocks from tree - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - int err = lfs_fs_traverse(lfs, lfs_alloc_lookahead, lfs); - if (err) { - return err; - } - } -} - -static void lfs_alloc_ack(lfs_t *lfs) { - lfs->free.ack = lfs->cfg->block_count; -} - - -/// Metadata pair and directory operations /// -static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_tag_t gmask, lfs_tag_t gtag, - lfs_off_t goff, void *gbuffer, lfs_size_t gsize) { - lfs_off_t off = dir->off; - lfs_tag_t ntag = dir->etag; - lfs_stag_t gdiff = 0; - - if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair) && - lfs_tag_id(gtag) <= lfs_tag_id(lfs->gstate.tag)) { - // synthetic moves - gdiff -= LFS_MKTAG(0, 1, 0); - } - - // iterate over dir block backwards (for faster lookups) - while (off >= sizeof(lfs_tag_t) + lfs_tag_dsize(ntag)) { - off -= lfs_tag_dsize(ntag); - lfs_tag_t tag = ntag; - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(ntag), - dir->pair[0], off, &ntag, sizeof(ntag)); - if (err) { - return err; - } - - ntag = (lfs_frombe32(ntag) ^ tag) & 0x7fffffff; - - if (lfs_tag_id(gmask) != 0 && - lfs_tag_type1(tag) == LFS_TYPE_SPLICE && - lfs_tag_id(tag) <= lfs_tag_id(gtag - gdiff)) { - if (tag == (LFS_MKTAG(LFS_TYPE_CREATE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & (gtag - gdiff)))) { - // found where we were created - return LFS_ERR_NOENT; - } - - // move around splices - gdiff += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - - if ((gmask & tag) == (gmask & (gtag - gdiff))) { - if (lfs_tag_isdelete(tag)) { - return LFS_ERR_NOENT; - } - - lfs_size_t diff = lfs_min(lfs_tag_size(tag), gsize); - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, diff, - dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff); - if (err) { - return err; - } - - memset((uint8_t*)gbuffer + diff, 0, gsize - diff); - - return tag + gdiff; - } - } - - return LFS_ERR_NOENT; -} - -static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_tag_t gmask, lfs_tag_t gtag, void *buffer) { - return lfs_dir_getslice(lfs, dir, - gmask, gtag, - 0, buffer, lfs_tag_size(gtag)); -} - -static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_tag_t gmask, lfs_tag_t gtag, - lfs_off_t off, void *buffer, lfs_size_t size) { - uint8_t *data = buffer; - if (off+size > lfs->cfg->block_size) { - return LFS_ERR_CORRUPT; - } - - while (size > 0) { - lfs_size_t diff = size; - - if (pcache && pcache->block == 0xfffffffe && - off < pcache->off + pcache->size) { - if (off >= pcache->off) { - // is already in pcache? - diff = lfs_min(diff, pcache->size - (off-pcache->off)); - memcpy(data, &pcache->buffer[off-pcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // pcache takes priority - diff = lfs_min(diff, pcache->off-off); - } - - if (rcache->block == 0xfffffffe && - off < rcache->off + rcache->size) { - if (off >= rcache->off) { - // is already in rcache? - diff = lfs_min(diff, rcache->size - (off-rcache->off)); - memcpy(data, &rcache->buffer[off-rcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // rcache takes priority - diff = lfs_min(diff, rcache->off-off); - } - - // load to cache, first condition can no longer fail - rcache->block = 0xfffffffe; - rcache->off = lfs_aligndown(off, lfs->cfg->read_size); - rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size), - lfs->cfg->cache_size); - int err = lfs_dir_getslice(lfs, dir, gmask, gtag, - rcache->off, rcache->buffer, rcache->size); - if (err) { - return err; - } - } - - return 0; -} - -static int lfs_dir_traverse_filter(void *p, - lfs_tag_t tag, const void *buffer) { - lfs_tag_t *filtertag = p; - (void)buffer; - - // check for redundancy - uint32_t mask = LFS_MKTAG(0x7ff, 0x3ff, 0); - if ((mask & tag) == (mask & *filtertag) || - (mask & tag) == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { - return true; - } - - // check if we need to adjust for created/deleted tags - if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE && - lfs_tag_id(tag) <= lfs_tag_id(*filtertag)) { - *filtertag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - - return false; -} - -static int lfs_dir_traverse(lfs_t *lfs, - const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag, - const struct lfs_mattr *attrs, int attrcount, bool hasseenmove, - lfs_tag_t tmask, lfs_tag_t ttag, - uint16_t begin, uint16_t end, int16_t diff, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // iterate over directory and attrs - while (true) { - lfs_tag_t tag; - const void *buffer; - struct lfs_diskoff disk; - if (off+lfs_tag_dsize(ptag) < dir->off) { - off += lfs_tag_dsize(ptag); - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), - dir->pair[0], off, &tag, sizeof(tag)); - if (err) { - return err; - } - - tag = (lfs_frombe32(tag) ^ ptag) | 0x80000000; - disk.block = dir->pair[0]; - disk.off = off+sizeof(lfs_tag_t); - buffer = &disk; - ptag = tag; - } else if (attrcount > 0) { - tag = attrs[0].tag; - buffer = attrs[0].buffer; - attrs += 1; - attrcount -= 1; - } else if (!hasseenmove && - lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { - // Wait, we have pending move? Handle this here (we need to - // or else we risk letting moves fall out of date) - tag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0); - buffer = NULL; - hasseenmove = true; - } else { - return 0; - } - - lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); - if ((mask & tmask & tag) != (mask & tmask & ttag)) { - continue; - } - - // do we need to filter? inlining the filtering logic here allows - // for some minor optimizations - if (lfs_tag_id(tmask) != 0) { - // scan for duplicates and update tag based on creates/deletes - int filter = lfs_dir_traverse(lfs, - dir, off, ptag, attrs, attrcount, hasseenmove, - 0, 0, 0, 0, 0, - lfs_dir_traverse_filter, &tag); - if (filter < 0) { - return filter; - } - - if (filter) { - continue; - } - - // in filter range? - if (!(lfs_tag_id(tag) >= begin && lfs_tag_id(tag) < end)) { - continue; - } - } - - // handle special cases for mcu-side operations - if (lfs_tag_type3(tag) == LFS_FROM_NOOP) { - // do nothing - } else if (lfs_tag_type3(tag) == LFS_FROM_MOVE) { - uint16_t fromid = lfs_tag_size(tag); - uint16_t toid = lfs_tag_id(tag); - int err = lfs_dir_traverse(lfs, - buffer, 0, 0xffffffff, NULL, 0, true, - LFS_MKTAG(0x600, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), - fromid, fromid+1, toid-fromid+diff, - cb, data); - if (err) { - return err; - } - } else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) { - for (unsigned i = 0; i < lfs_tag_size(tag); i++) { - const struct lfs_attr *a = buffer; - int err = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a[i].type, - lfs_tag_id(tag) + diff, a[i].size), a[i].buffer); - if (err) { - return err; - } - } - } else { - int err = cb(data, tag + LFS_MKTAG(0, diff, 0), buffer); - if (err) { - return err; - } - } - } -} - -static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_block_t pair[2], - lfs_tag_t fmask, lfs_tag_t ftag, uint16_t *id, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // we can find tag very efficiently during a fetch, since we're already - // scanning the entire directory - lfs_stag_t besttag = -1; - - // find the block with the most recent revision - uint32_t revs[2] = {0, 0}; - int r = 0; - for (int i = 0; i < 2; i++) { - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(revs[i]), - pair[i], 0, &revs[i], sizeof(revs[i])); - revs[i] = lfs_fromle32(revs[i]); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err != LFS_ERR_CORRUPT && - lfs_scmp(revs[i], revs[(i+1)%2]) > 0) { - r = i; - } - } - - dir->pair[0] = pair[(r+0)%2]; - dir->pair[1] = pair[(r+1)%2]; - dir->rev = revs[(r+0)%2]; - dir->off = 0; // nonzero = found some commits - - // now scan tags to fetch the actual dir and find possible match - for (int i = 0; i < 2; i++) { - lfs_off_t off = 0; - lfs_tag_t ptag = 0xffffffff; - - uint16_t tempcount = 0; - lfs_block_t temptail[2] = {0xffffffff, 0xffffffff}; - bool tempsplit = false; - lfs_stag_t tempbesttag = besttag; - - dir->rev = lfs_tole32(dir->rev); - uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - - while (true) { - // extract next tag - lfs_tag_t tag; - off += lfs_tag_dsize(ptag); - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off, &tag, sizeof(tag)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - // can't continue? - dir->erased = false; - break; - } - return err; - } - - crc = lfs_crc(crc, &tag, sizeof(tag)); - tag = lfs_frombe32(tag) ^ ptag; - - // next commit not yet programmed or we're not in valid range - if (!lfs_tag_isvalid(tag) || - off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { - dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC && - dir->off % lfs->cfg->prog_size == 0); - break; - } - - ptag = tag; - - if (lfs_tag_type1(tag) == LFS_TYPE_CRC) { - // check the crc attr - uint32_t dcrc; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } - dcrc = lfs_fromle32(dcrc); - - if (crc != dcrc) { - dir->erased = false; - break; - } - - // reset the next bit if we need to - ptag ^= (lfs_tag_chunk(tag) & 1U) << 31; - - // toss our crc into the filesystem seed for - // pseudorandom numbers - lfs->seed ^= crc; - - // update with what's found so far - besttag = tempbesttag; - dir->off = off + lfs_tag_dsize(tag); - dir->etag = ptag; - dir->count = tempcount; - dir->tail[0] = temptail[0]; - dir->tail[1] = temptail[1]; - dir->split = tempsplit; - - // reset crc - crc = 0xffffffff; - continue; - } - - // crc the entry first, hopefully leaving it in the cache - for (lfs_off_t j = sizeof(tag); j < lfs_tag_dsize(tag); j++) { - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+j, &dat, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } - - crc = lfs_crc(crc, &dat, 1); - } - - // directory modification tags? - if (lfs_tag_type1(tag) == LFS_TYPE_NAME) { - // increase count of files if necessary - if (lfs_tag_id(tag) >= tempcount) { - tempcount = lfs_tag_id(tag) + 1; - } - } else if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE) { - tempcount += lfs_tag_splice(tag); - - if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) { - tempbesttag |= 0x80000000; - } else if (tempbesttag != -1 && - lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { - tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { - tempsplit = (lfs_tag_chunk(tag) & 1); - - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), &temptail, 8); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - } - lfs_pair_fromle32(temptail); - } - - // found a match for our fetcher? - if ((fmask & tag) == (fmask & ftag)) { - int res = cb(data, tag, &(struct lfs_diskoff){ - dir->pair[0], off+sizeof(tag)}); - if (res < 0) { - if (res == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return res; - } - - if (res == LFS_CMP_EQ) { - // found a match - tempbesttag = tag; - } else if (res == LFS_CMP_GT && - lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { - // found a greater match, keep track to keep things sorted - tempbesttag = tag | 0x80000000; - } - } - } - - // consider what we have good enough - if (dir->off > 0) { - // synthetic move - if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair)) { - if (lfs_tag_id(lfs->gstate.tag) == lfs_tag_id(besttag)) { - besttag |= 0x80000000; - } else if (besttag != -1 && - lfs_tag_id(lfs->gstate.tag) < lfs_tag_id(besttag)) { - besttag -= LFS_MKTAG(0, 1, 0); - } - } - - // found tag? or found best id? - if (id) { - *id = lfs_min(lfs_tag_id(besttag), dir->count); - } - - if (lfs_tag_isvalid(besttag)) { - return besttag; - } else if (lfs_tag_id(besttag) < dir->count) { - return LFS_ERR_NOENT; - } else { - return 0; - } - } - - // failed, try the other block? - lfs_pair_swap(dir->pair); - dir->rev = revs[(r+1)%2]; - } - - LFS_ERROR("Corrupted dir pair at %"PRIu32" %"PRIu32, - dir->pair[0], dir->pair[1]); - return LFS_ERR_CORRUPT; -} - -static int lfs_dir_fetch(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_block_t pair[2]) { - // note, mask=-1, tag=0 can never match a tag since this - // pattern has the invalid bit set - return lfs_dir_fetchmatch(lfs, dir, pair, -1, 0, NULL, NULL, NULL); -} - -static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir, - struct lfs_gstate *gstate) { - struct lfs_gstate temp; - lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0), - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp); - if (res < 0 && res != LFS_ERR_NOENT) { - return res; - } - - if (res != LFS_ERR_NOENT) { - // xor together to find resulting gstate - lfs_gstate_fromle32(&temp); - lfs_gstate_xor(gstate, &temp); - } - - return 0; -} - -static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir, - uint16_t id, struct lfs_info *info) { - if (id == 0x3ff) { - // special case for root - strcpy(info->name, "/"); - info->type = LFS_TYPE_DIR; - return 0; - } - - lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name); - if (tag < 0) { - return tag; - } - - info->type = lfs_tag_type3(tag); - - struct lfs_ctz ctz; - tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); - if (tag < 0) { - return tag; - } - lfs_ctz_fromle32(&ctz); - - if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { - info->size = ctz.size; - } else if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { - info->size = lfs_tag_size(tag); - } - - return 0; -} - -struct lfs_dir_find_match { - lfs_t *lfs; - const void *name; - lfs_size_t size; -}; - -static int lfs_dir_find_match(void *data, - lfs_tag_t tag, const void *buffer) { - struct lfs_dir_find_match *name = data; - lfs_t *lfs = name->lfs; - const struct lfs_diskoff *disk = buffer; - - // compare with disk - lfs_size_t diff = lfs_min(name->size, lfs_tag_size(tag)); - int res = lfs_bd_cmp(lfs, - NULL, &lfs->rcache, diff, - disk->block, disk->off, name->name, diff); - if (res != LFS_CMP_EQ) { - return res; - } - - // only equal if our size is still the same - if (name->size != lfs_tag_size(tag)) { - return (name->size < lfs_tag_size(tag)) ? LFS_CMP_LT : LFS_CMP_GT; - } - - // found a match! - return LFS_CMP_EQ; -} - -static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, - const char **path, uint16_t *id) { - // we reduce path to a single name if we can find it - const char *name = *path; - if (id) { - *id = 0x3ff; - } - - // default to root dir - lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); - dir->tail[0] = lfs->root[0]; - dir->tail[1] = lfs->root[1]; - - while (true) { -nextname: - // skip slashes - name += strspn(name, "/"); - lfs_size_t namelen = strcspn(name, "/"); - - // skip '.' and root '..' - if ((namelen == 1 && memcmp(name, ".", 1) == 0) || - (namelen == 2 && memcmp(name, "..", 2) == 0)) { - name += namelen; - goto nextname; - } - - // skip if matched by '..' in name - const char *suffix = name + namelen; - lfs_size_t sufflen; - int depth = 1; - while (true) { - suffix += strspn(suffix, "/"); - sufflen = strcspn(suffix, "/"); - if (sufflen == 0) { - break; - } - - if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { - depth -= 1; - if (depth == 0) { - name = suffix + sufflen; - goto nextname; - } - } else { - depth += 1; - } - - suffix += sufflen; - } - - // found path - if (name[0] == '\0') { - return tag; - } - - // update what we've found so far - *path = name; - - // only continue if we hit a directory - if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { - return LFS_ERR_NOTDIR; - } - - // grab the entry data - if (lfs_tag_id(tag) != 0x3ff) { - lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail); - if (res < 0) { - return res; - } - lfs_pair_fromle32(dir->tail); - } - - // find entry matching name - while (true) { - tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, - LFS_MKTAG(0x780, 0, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), - // are we last name? - (strchr(name, '/') == NULL) ? id : NULL, - lfs_dir_find_match, &(struct lfs_dir_find_match){ - lfs, name, namelen}); - if (tag < 0) { - return tag; - } - - if (tag) { - break; - } - - if (!dir->split) { - return LFS_ERR_NOENT; - } - } - - // to next name - name += namelen; - } -} - -// commit logic -struct lfs_commit { - lfs_block_t block; - lfs_off_t off; - lfs_tag_t ptag; - uint32_t crc; - - lfs_off_t begin; - lfs_off_t end; -}; - -static int lfs_dir_commitprog(lfs_t *lfs, struct lfs_commit *commit, - const void *buffer, lfs_size_t size) { - int err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off , - (const uint8_t*)buffer, size); - if (err) { - return err; - } - - commit->crc = lfs_crc(commit->crc, buffer, size); - commit->off += size; - return 0; -} - -static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit, - lfs_tag_t tag, const void *buffer) { - // check if we fit - lfs_size_t dsize = lfs_tag_dsize(tag); - if (commit->off + dsize > commit->end) { - return LFS_ERR_NOSPC; - } - - // write out tag - lfs_tag_t ntag = lfs_tobe32((tag & 0x7fffffff) ^ commit->ptag); - int err = lfs_dir_commitprog(lfs, commit, &ntag, sizeof(ntag)); - if (err) { - return err; - } - - if (!(tag & 0x80000000)) { - // from memory - err = lfs_dir_commitprog(lfs, commit, buffer, dsize-sizeof(tag)); - if (err) { - return err; - } - } else { - // from disk - const struct lfs_diskoff *disk = buffer; - for (lfs_off_t i = 0; i < dsize-sizeof(tag); i++) { - // rely on caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, dsize-sizeof(tag)-i, - disk->block, disk->off+i, &dat, 1); - if (err) { - return err; - } - - err = lfs_dir_commitprog(lfs, commit, &dat, 1); - if (err) { - return err; - } - } - } - - commit->ptag = tag & 0x7fffffff; - return 0; -} - -static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { - // align to program units - lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t), - lfs->cfg->prog_size); - - // read erased state from next program unit - lfs_tag_t tag; - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), - commit->block, off, &tag, sizeof(tag)); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // build crc tag - bool reset = ~lfs_frombe32(tag) >> 31; - tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, - off - (commit->off+sizeof(lfs_tag_t))); - - // write out crc - uint32_t footer[2]; - footer[0] = lfs_tobe32(tag ^ commit->ptag); - commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0])); - footer[1] = lfs_tole32(commit->crc); - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off, &footer, sizeof(footer)); - if (err) { - return err; - } - commit->off += sizeof(tag)+lfs_tag_size(tag); - commit->ptag = tag ^ (reset << 31); - - // flush buffers - err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); - if (err) { - return err; - } - - // successful commit, check checksum to make sure - uint32_t crc = 0xffffffff; - lfs_size_t size = commit->off - lfs_tag_size(tag) - commit->begin; - for (lfs_off_t i = 0; i < size; i++) { - // leave it up to caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, size-i, - commit->block, commit->begin+i, &dat, 1); - if (err) { - return err; - } - - crc = lfs_crc(crc, &dat, 1); - } - - if (err) { - return err; - } - - if (crc != commit->crc) { - return LFS_ERR_CORRUPT; - } - - return 0; -} - -static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { - // allocate pair of dir blocks (backwards, so we write block 1 first) - for (int i = 0; i < 2; i++) { - int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); - if (err) { - return err; - } - } - - // rather than clobbering one of the blocks we just pretend - // the revision may be valid - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(dir->rev), - dir->pair[0], 0, &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // make sure we don't immediately evict - dir->rev += dir->rev & 1; - - // set defaults - dir->off = sizeof(dir->rev); - dir->etag = 0xffffffff; - dir->count = 0; - dir->tail[0] = 0xffffffff; - dir->tail[1] = 0xffffffff; - dir->erased = false; - dir->split = false; - - // don't write out yet, let caller take care of that - return 0; -} - -static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { - // steal state - int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta); - if (err) { - return err; - } - - // steal tail - lfs_pair_tole32(tail->tail); - err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); - lfs_pair_fromle32(tail->tail); - if (err) { - return err; - } - - return 0; -} - -static int lfs_dir_split(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t split, uint16_t end) { - // create tail directory - lfs_mdir_t tail; - int err = lfs_dir_alloc(lfs, &tail); - if (err) { - return err; - } - - tail.split = dir->split; - tail.tail[0] = dir->tail[0]; - tail.tail[1] = dir->tail[1]; - - err = lfs_dir_compact(lfs, &tail, attrs, attrcount, source, split, end); - if (err) { - return err; - } - - dir->tail[0] = tail.pair[0]; - dir->tail[1] = tail.pair[1]; - dir->split = true; - - // update root if needed - if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && split == 0) { - lfs->root[0] = tail.pair[0]; - lfs->root[1] = tail.pair[1]; - } - - return 0; -} - -static int lfs_dir_commit_size(void *p, lfs_tag_t tag, const void *buffer) { - lfs_size_t *size = p; - (void)buffer; - - *size += lfs_tag_dsize(tag); - return 0; -} - -struct lfs_dir_commit_commit { - lfs_t *lfs; - struct lfs_commit *commit; -}; - -static int lfs_dir_commit_commit(void *p, lfs_tag_t tag, const void *buffer) { - struct lfs_dir_commit_commit *commit = p; - return lfs_dir_commitattr(commit->lfs, commit->commit, tag, buffer); -} - -static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t begin, uint16_t end) { - // save some state in case block is bad - const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; - bool relocated = false; - bool exhausted = false; - - // should we split? - while (end - begin > 1) { - // find size - lfs_size_t size = 0; - int err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, false, - LFS_MKTAG(0x400, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, 0), - begin, end, -begin, - lfs_dir_commit_size, &size); - if (err) { - return err; - } - - // space is complicated, we need room for tail, crc, gstate, - // cleanup delete, and we cap at half a block to give room - // for metadata updates. - if (end - begin < 0xff && - size <= lfs_min(lfs->cfg->block_size - 36, - lfs_alignup(lfs->cfg->block_size/2, - lfs->cfg->prog_size))) { - break; - } - - // can't fit, need to split, we should really be finding the - // largest size that fits with a small binary search, but right now - // it's not worth the code size - uint16_t split = (end - begin) / 2; - err = lfs_dir_split(lfs, dir, attrs, attrcount, - source, begin+split, end); - if (err) { - // if we fail to split, we may be able to overcompact, unless - // we're too big for even the full block, in which case our - // only option is to error - if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 36) { - break; - } - return err; - } - - end = begin + split; - } - - // increment revision count - dir->rev += 1; - if (lfs->cfg->block_cycles && - (dir->rev % (lfs->cfg->block_cycles+1) == 0)) { - if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { - // oh no! we're writing too much to the superblock, - // should we expand? - lfs_ssize_t res = lfs_fs_size(lfs); - if (res < 0) { - return res; - } - - // do we have extra space? littlefs can't reclaim this space - // by itself, so expand cautiously - if ((lfs_size_t)res < lfs->cfg->block_count/2) { - LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); - int err = lfs_dir_split(lfs, dir, attrs, attrcount, - source, begin, end); - if (err && err != LFS_ERR_NOSPC) { - return err; - } - - // welp, we tried, if we ran out of space there's not much - // we can do, we'll error later if we've become frozen - if (!err) { - end = begin; - } - } - } else { - // we're writing too much, time to relocate - exhausted = true; - goto relocate; - } - } - - // begin loop to commit compaction to blocks until a compact sticks - while (true) { - { - // There's nothing special about our global delta, so feed it into - // our local global delta - int err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta); - if (err) { - return err; - } - - // setup commit state - struct lfs_commit commit = { - .block = dir->pair[1], - .off = 0, - .ptag = 0xffffffff, - .crc = 0xffffffff, - - .begin = 0, - .end = lfs->cfg->block_size - 8, - }; - - // erase block to write to - err = lfs_bd_erase(lfs, dir->pair[1]); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // write out header - dir->rev = lfs_tole32(dir->rev); - err = lfs_dir_commitprog(lfs, &commit, - &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // traverse the directory, this time writing out all unique tags - err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, false, - LFS_MKTAG(0x400, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, 0), - begin, end, -begin, - lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ - lfs, &commit}); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // commit tail, which may be new after last size check - if (!lfs_pair_isnull(dir->tail)) { - lfs_pair_tole32(dir->tail); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), - dir->tail); - lfs_pair_fromle32(dir->tail); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - if (!relocated && !lfs_gstate_iszero(&lfs->gdelta)) { - // commit any globals, unless we're relocating, - // in which case our parent will steal our globals - lfs_gstate_tole32(&lfs->gdelta); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, - sizeof(lfs->gdelta)), &lfs->gdelta); - lfs_gstate_fromle32(&lfs->gdelta); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - err = lfs_dir_commitcrc(lfs, &commit); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // successful compaction, swap dir pair to indicate most recent - lfs_pair_swap(dir->pair); - dir->count = end - begin; - dir->off = commit.off; - dir->etag = commit.ptag; - dir->erased = (dir->off % lfs->cfg->prog_size == 0); - // note we able to have already handled move here - if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { - lfs_gstate_xormove(&lfs->gpending, - &lfs->gpending, 0x3ff, NULL); - } - } - break; - -relocate: - // commit was corrupted, drop caches and prepare to relocate block - relocated = true; - lfs_cache_drop(lfs, &lfs->pcache); - if (!exhausted) { - LFS_DEBUG("Bad block at %"PRIu32, dir->pair[1]); - } - - // can't relocate superblock, filesystem is now frozen - if (lfs_pair_cmp(oldpair, (const lfs_block_t[2]){0, 1}) == 0) { - LFS_WARN("Superblock %"PRIu32" has become unwritable", oldpair[1]); - return LFS_ERR_NOSPC; - } - - // relocate half of pair - int err = lfs_alloc(lfs, &dir->pair[1]); - if (err && (err != LFS_ERR_NOSPC && !exhausted)) { - return err; - } - - continue; - } - - if (!relocated) { - lfs->gstate = lfs->gpending; - lfs->gdelta = (struct lfs_gstate){0}; - } else { - // update references if we relocated - LFS_DEBUG("Relocating %"PRIu32" %"PRIu32" to %"PRIu32" %"PRIu32, - oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); - int err = lfs_fs_relocate(lfs, oldpair, dir->pair); - if (err) { - return err; - } - } - - return 0; -} - -static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs, int attrcount) { - // check for any inline files that aren't RAM backed and - // forcefully evict them, needed for filesystem consistency - for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { - if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 && - f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) && - f->ctz.size > lfs->cfg->cache_size) { - f->flags &= ~LFS_F_READING; - f->off = 0; - - lfs_alloc_ack(lfs); - int err = lfs_file_relocate(lfs, f); - if (err) { - return err; - } - - err = lfs_file_flush(lfs, f); - if (err) { - return err; - } - } - } - - // calculate changes to the directory - lfs_tag_t deletetag = 0xffffffff; - lfs_tag_t createtag = 0xffffffff; - for (int i = 0; i < attrcount; i++) { - if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) { - createtag = attrs[i].tag; - dir->count += 1; - } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE) { - deletetag = attrs[i].tag; - LFS_ASSERT(dir->count > 0); - dir->count -= 1; - } else if (lfs_tag_type1(attrs[i].tag) == LFS_TYPE_TAIL) { - dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0]; - dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1]; - dir->split = (lfs_tag_chunk(attrs[i].tag) & 1); - lfs_pair_fromle32(dir->tail); - } - } - - // do we have a pending move? - if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { - deletetag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0); - LFS_ASSERT(dir->count > 0); - dir->count -= 1; - - // mark gdelta so we reflect the move we will fix - lfs_gstate_xormove(&lfs->gdelta, &lfs->gpending, 0x3ff, NULL); - } - - // should we actually drop the directory block? - if (lfs_tag_isvalid(deletetag) && dir->count == 0) { - lfs_mdir_t pdir; - int err = lfs_fs_pred(lfs, dir->pair, &pdir); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - if (err != LFS_ERR_NOENT && pdir.split) { - return lfs_dir_drop(lfs, &pdir, dir); - } - } - - if (dir->erased || dir->count >= 0xff) { - // try to commit - struct lfs_commit commit = { - .block = dir->pair[0], - .off = dir->off, - .ptag = dir->etag, - .crc = 0xffffffff, - - .begin = dir->off, - .end = lfs->cfg->block_size - 8, - }; - - // traverse attrs that need to be written out - lfs_pair_tole32(dir->tail); - int err = lfs_dir_traverse(lfs, - dir, dir->off, dir->etag, attrs, attrcount, false, - 0, 0, 0, 0, 0, - lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ - lfs, &commit}); - lfs_pair_fromle32(dir->tail); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - return err; - } - - // commit any global diffs if we have any - if (!lfs_gstate_iszero(&lfs->gdelta)) { - err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta); - if (err) { - return err; - } - - lfs_gstate_tole32(&lfs->gdelta); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, - sizeof(lfs->gdelta)), &lfs->gdelta); - lfs_gstate_fromle32(&lfs->gdelta); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - return err; - } - } - - // finalize commit with the crc - err = lfs_dir_commitcrc(lfs, &commit); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - return err; - } - - // successful commit, update dir - dir->off = commit.off; - dir->etag = commit.ptag; - - // note we able to have already handled move here - if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { - lfs_gstate_xormove(&lfs->gpending, &lfs->gpending, 0x3ff, NULL); - } - - // update gstate - lfs->gstate = lfs->gpending; - lfs->gdelta = (struct lfs_gstate){0}; - } else { -compact: - // fall back to compaction - lfs_cache_drop(lfs, &lfs->pcache); - - int err = lfs_dir_compact(lfs, dir, attrs, attrcount, - dir, 0, dir->count); - if (err) { - return err; - } - } - - // update any directories that are affected - lfs_mdir_t copy = *dir; - - // two passes, once for things that aren't us, and one - // for things that are - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (lfs_pair_cmp(d->m.pair, copy.pair) == 0) { - d->m = *dir; - if (d->id == lfs_tag_id(deletetag)) { - d->m.pair[0] = 0xffffffff; - d->m.pair[1] = 0xffffffff; - } else if (d->id > lfs_tag_id(deletetag)) { - d->id -= 1; - if (d->type == LFS_TYPE_DIR) { - ((lfs_dir_t*)d)->pos -= 1; - } - } else if (&d->m != dir && d->id >= lfs_tag_id(createtag)) { - d->id += 1; - if (d->type == LFS_TYPE_DIR) { - ((lfs_dir_t*)d)->pos += 1; - } - } - - while (d->id >= d->m.count && d->m.split) { - // we split and id is on tail now - d->id -= d->m.count; - int err = lfs_dir_fetch(lfs, &d->m, d->m.tail); - if (err) { - return err; - } - } - } - } - - return 0; -} - - -/// Top level directory operations /// -int lfs_mkdir(lfs_t *lfs, const char *path) { - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - return err; - } - - lfs_mdir_t cwd; - uint16_t id; - err = lfs_dir_find(lfs, &cwd, &path, &id); - if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { - return (err < 0) ? err : LFS_ERR_EXIST; - } - - // check that name fits - lfs_size_t nlen = strlen(path); - if (nlen > lfs->name_max) { - return LFS_ERR_NAMETOOLONG; - } - - // build up new directory - lfs_alloc_ack(lfs); - lfs_mdir_t dir; - err = lfs_dir_alloc(lfs, &dir); - if (err) { - return err; - } - - // find end of list - lfs_mdir_t pred = cwd; - while (pred.split) { - err = lfs_dir_fetch(lfs, &pred, pred.tail); - if (err) { - return err; - } - } - - // setup dir - lfs_pair_tole32(pred.tail); - err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); - lfs_pair_fromle32(pred.tail); - if (err) { - return err; - } - - // current block end of list? - if (cwd.split) { - // update tails, this creates a desync - lfs_fs_preporphans(lfs, +1); - lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); - lfs_pair_fromle32(dir.pair); - if (err) { - return err; - } - lfs_fs_preporphans(lfs, -1); - } - - // now insert into our parent block - lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, - {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair}, - {!cwd.split - ? LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8) - : LFS_MKTAG(LFS_FROM_NOOP, 0, 0), dir.pair})); - lfs_pair_fromle32(dir.pair); - if (err) { - return err; - } - - return 0; -} - -int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { - lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL); - if (tag < 0) { - return tag; - } - - if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { - return LFS_ERR_NOTDIR; - } - - lfs_block_t pair[2]; - if (lfs_tag_id(tag) == 0x3ff) { - // handle root dir separately - pair[0] = lfs->root[0]; - pair[1] = lfs->root[1]; - } else { - // get dir pair from parent - lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); - if (res < 0) { - return res; - } - lfs_pair_fromle32(pair); - } - - // fetch first pair - int err = lfs_dir_fetch(lfs, &dir->m, pair); - if (err) { - return err; - } - - // setup entry - dir->head[0] = dir->m.pair[0]; - dir->head[1] = dir->m.pair[1]; - dir->id = 0; - dir->pos = 0; - - // add to list of mdirs - dir->type = LFS_TYPE_DIR; - dir->next = (lfs_dir_t*)lfs->mlist; - lfs->mlist = (struct lfs_mlist*)dir; - - return 0; -} - -int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { - // remove from list of mdirs - for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs_mlist*)dir) { - *p = (*p)->next; - break; - } - } - - return 0; -} - -int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { - memset(info, 0, sizeof(*info)); - - // special offset for '.' and '..' - if (dir->pos == 0) { - info->type = LFS_TYPE_DIR; - strcpy(info->name, "."); - dir->pos += 1; - return 1; - } else if (dir->pos == 1) { - info->type = LFS_TYPE_DIR; - strcpy(info->name, ".."); - dir->pos += 1; - return 1; - } - - while (true) { - if (dir->id == dir->m.count) { - if (!dir->m.split) { - return false; - } - - int err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); - if (err) { - return err; - } - - dir->id = 0; - } - - int err = lfs_dir_getinfo(lfs, &dir->m, dir->id, info); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - dir->id += 1; - if (err != LFS_ERR_NOENT) { - break; - } - } - - dir->pos += 1; - return true; -} - -int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { - // simply walk from head dir - int err = lfs_dir_rewind(lfs, dir); - if (err) { - return err; - } - - // first two for ./.. - dir->pos = lfs_min(2, off); - off -= dir->pos; - - while (off != 0) { - dir->id = lfs_min(dir->m.count, off); - dir->pos += dir->id; - off -= dir->id; - - if (dir->id == dir->m.count) { - if (!dir->m.split) { - return LFS_ERR_INVAL; - } - - err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); - if (err) { - return err; - } - } - } - - return 0; -} - -lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) { - (void)lfs; - return dir->pos; -} - -int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) { - // reload the head dir - int err = lfs_dir_fetch(lfs, &dir->m, dir->head); - if (err) { - return err; - } - - dir->m.pair[0] = dir->head[0]; - dir->m.pair[1] = dir->head[1]; - dir->id = 0; - dir->pos = 0; - return 0; -} - - -/// File index list operations /// -static int lfs_ctz_index(lfs_t *lfs, lfs_off_t *off) { - lfs_off_t size = *off; - lfs_off_t b = lfs->cfg->block_size - 2*4; - lfs_off_t i = size / b; - if (i == 0) { - return 0; - } - - i = (size - 4*(lfs_popc(i-1)+2)) / b; - *off = size - b*i - 4*lfs_popc(i); - return i; -} - -static int lfs_ctz_find(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) { - if (size == 0) { - *block = 0xffffffff; - *off = 0; - return 0; - } - - lfs_off_t current = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); - lfs_off_t target = lfs_ctz_index(lfs, &pos); - - while (current > target) { - lfs_size_t skip = lfs_min( - lfs_npw2(current-target+1) - 1, - lfs_ctz(current)); - - int err = lfs_bd_read(lfs, - pcache, rcache, sizeof(head), - head, 4*skip, &head, sizeof(head)); - head = lfs_fromle32(head); - if (err) { - return err; - } - - LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count); - current -= 1 << skip; - } - - *block = head; - *off = pos; - return 0; -} - -static int lfs_ctz_extend(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - lfs_block_t *block, lfs_off_t *off) { - while (true) { - // go ahead and grab a block - lfs_block_t nblock; - int err = lfs_alloc(lfs, &nblock); - if (err) { - return err; - } - LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count); - - { - err = lfs_bd_erase(lfs, nblock); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - if (size == 0) { - *block = nblock; - *off = 0; - return 0; - } - - size -= 1; - lfs_off_t index = lfs_ctz_index(lfs, &size); - size += 1; - - // just copy out the last block if it is incomplete - if (size != lfs->cfg->block_size) { - for (lfs_off_t i = 0; i < size; i++) { - uint8_t data; - err = lfs_bd_read(lfs, - NULL, rcache, size-i, - head, i, &data, 1); - if (err) { - return err; - } - - err = lfs_bd_prog(lfs, - pcache, rcache, true, - nblock, i, &data, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - *block = nblock; - *off = size; - return 0; - } - - // append block - index += 1; - lfs_size_t skips = lfs_ctz(index) + 1; - - for (lfs_off_t i = 0; i < skips; i++) { - head = lfs_tole32(head); - err = lfs_bd_prog(lfs, pcache, rcache, true, - nblock, 4*i, &head, 4); - head = lfs_fromle32(head); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - if (i != skips-1) { - err = lfs_bd_read(lfs, - NULL, rcache, sizeof(head), - head, 4*i, &head, sizeof(head)); - head = lfs_fromle32(head); - if (err) { - return err; - } - } - - LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count); - } - - *block = nblock; - *off = 4*skips; - return 0; - } - -relocate: - LFS_DEBUG("Bad block at %"PRIu32, nblock); - - // just clear cache and try a new block - lfs_cache_drop(lfs, pcache); - } -} - -static int lfs_ctz_traverse(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - int (*cb)(void*, lfs_block_t), void *data) { - if (size == 0) { - return 0; - } - - lfs_off_t index = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); - - while (true) { - int err = cb(data, head); - if (err) { - return err; - } - - if (index == 0) { - return 0; - } - - lfs_block_t heads[2]; - int count = 2 - (index & 1); - err = lfs_bd_read(lfs, - pcache, rcache, count*sizeof(head), - head, 0, &heads, count*sizeof(head)); - heads[0] = lfs_fromle32(heads[0]); - heads[1] = lfs_fromle32(heads[1]); - if (err) { - return err; - } - - for (int i = 0; i < count-1; i++) { - err = cb(data, heads[i]); - if (err) { - return err; - } - } - - head = heads[count-1]; - index -= count; - } -} - - -/// Top level file operations /// -int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags, - const struct lfs_file_config *cfg) { - // deorphan if we haven't yet, needed at most once after poweron - if ((flags & 3) != LFS_O_RDONLY) { - int err = lfs_fs_forceconsistency(lfs); - if (err) { - return err; - } - } - - // setup simple file details - int err; - file->cfg = cfg; - file->flags = flags; - file->pos = 0; - file->cache.buffer = NULL; - - // allocate entry for file if it doesn't exist - lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id); - if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) { - err = tag; - goto cleanup; - } - - // get id, add to list of mdirs to catch update changes - file->type = LFS_TYPE_REG; - file->next = (lfs_file_t*)lfs->mlist; - lfs->mlist = (struct lfs_mlist*)file; - - if (tag == LFS_ERR_NOENT) { - if (!(flags & LFS_O_CREAT)) { - err = LFS_ERR_NOENT; - goto cleanup; - } - - // check that name fits - lfs_size_t nlen = strlen(path); - if (nlen > lfs->name_max) { - err = LFS_ERR_NAMETOOLONG; - goto cleanup; - } - - // get next slot and create entry to remember name - err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL})); - if (err) { - err = LFS_ERR_NAMETOOLONG; - goto cleanup; - } - - tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0); - } else if (flags & LFS_O_EXCL) { - err = LFS_ERR_EXIST; - goto cleanup; - } else if (lfs_tag_type3(tag) != LFS_TYPE_REG) { - err = LFS_ERR_ISDIR; - goto cleanup; - } else if (flags & LFS_O_TRUNC) { - // truncate if requested - tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0); - file->flags |= LFS_F_DIRTY; - } else { - // try to load what's on disk, if it's inlined we'll fix it later - tag = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz); - if (tag < 0) { - err = tag; - goto cleanup; - } - lfs_ctz_fromle32(&file->ctz); - } - - // fetch attrs - for (unsigned i = 0; i < file->cfg->attr_count; i++) { - if ((file->flags & 3) != LFS_O_WRONLY) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, - LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_USERATTR + file->cfg->attrs[i].type, - file->id, file->cfg->attrs[i].size), - file->cfg->attrs[i].buffer); - if (res < 0 && res != LFS_ERR_NOENT) { - err = res; - goto cleanup; - } - } - - if ((file->flags & 3) != LFS_O_RDONLY) { - if (file->cfg->attrs[i].size > lfs->attr_max) { - err = LFS_ERR_NOSPC; - goto cleanup; - } - - file->flags |= LFS_F_DIRTY; - } - } - - // allocate buffer if needed - if (file->cfg->buffer) { - file->cache.buffer = file->cfg->buffer; - } else { - file->cache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!file->cache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // zero to avoid information leak - lfs_cache_zero(lfs, &file->cache); - - if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { - // load inline files - file->ctz.head = 0xfffffffe; - file->ctz.size = lfs_tag_size(tag); - file->flags |= LFS_F_INLINE; - file->cache.block = file->ctz.head; - file->cache.off = 0; - file->cache.size = lfs->cfg->cache_size; - - // don't always read (may be new/trunc file) - if (file->ctz.size > 0) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, - LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, file->id, - lfs_min(file->cache.size, 0x3fe)), - file->cache.buffer); - if (res < 0) { - err = res; - goto cleanup; - } - } - } - - return 0; - -cleanup: - // clean up lingering resources - file->flags |= LFS_F_ERRED; - lfs_file_close(lfs, file); - return err; -} - -int lfs_file_open(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags) { - static const struct lfs_file_config defaults = {0}; - return lfs_file_opencfg(lfs, file, path, flags, &defaults); -} - -int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { - int err = lfs_file_sync(lfs, file); - - // remove from list of mdirs - for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs_mlist*)file) { - *p = (*p)->next; - break; - } - } - - // clean up memory - if (!file->cfg->buffer) { - lfs_free(file->cache.buffer); - } - - return err; -} - -static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { - while (true) { - // just relocate what exists into new block - lfs_block_t nblock; - int err = lfs_alloc(lfs, &nblock); - if (err) { - return err; - } - - err = lfs_bd_erase(lfs, nblock); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // either read from dirty cache or disk - for (lfs_off_t i = 0; i < file->off; i++) { - uint8_t data; - if (file->flags & LFS_F_INLINE) { - err = lfs_dir_getread(lfs, &file->m, - // note we evict inline files before they can be dirty - NULL, &file->cache, file->off-i, - LFS_MKTAG(0xfff, 0x1ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), - i, &data, 1); - if (err) { - return err; - } - } else { - err = lfs_bd_read(lfs, - &file->cache, &lfs->rcache, file->off-i, - file->block, i, &data, 1); - if (err) { - return err; - } - } - - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, true, - nblock, i, &data, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - // copy over new state of file - memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->cache_size); - file->cache.block = lfs->pcache.block; - file->cache.off = lfs->pcache.off; - file->cache.size = lfs->pcache.size; - lfs_cache_zero(lfs, &lfs->pcache); - - file->block = nblock; - file->flags &= ~LFS_F_INLINE; - file->flags |= LFS_F_WRITING; - return 0; - -relocate: - LFS_DEBUG("Bad block at %"PRIu32, nblock); - - // just clear cache and try a new block - lfs_cache_drop(lfs, &lfs->pcache); - } -} - -static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { - if (file->flags & LFS_F_READING) { - if (!(file->flags & LFS_F_INLINE)) { - lfs_cache_drop(lfs, &file->cache); - } - file->flags &= ~LFS_F_READING; - } - - if (file->flags & LFS_F_WRITING) { - lfs_off_t pos = file->pos; - - if (!(file->flags & LFS_F_INLINE)) { - // copy over anything after current branch - lfs_file_t orig = { - .ctz.head = file->ctz.head, - .ctz.size = file->ctz.size, - .flags = LFS_O_RDONLY, - .pos = file->pos, - .cache = lfs->rcache, - }; - lfs_cache_drop(lfs, &lfs->rcache); - - while (file->pos < file->ctz.size) { - // copy over a byte at a time, leave it up to caching - // to make this efficient - uint8_t data; - lfs_ssize_t res = lfs_file_read(lfs, &orig, &data, 1); - if (res < 0) { - return res; - } - - res = lfs_file_write(lfs, file, &data, 1); - if (res < 0) { - return res; - } - - // keep our reference to the rcache in sync - if (lfs->rcache.block != 0xffffffff) { - lfs_cache_drop(lfs, &orig.cache); - lfs_cache_drop(lfs, &lfs->rcache); - } - } - - // write out what we have - while (true) { - int err = lfs_bd_flush(lfs, &file->cache, &lfs->rcache, true); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - break; - -relocate: - LFS_DEBUG("Bad block at %"PRIu32, file->block); - err = lfs_file_relocate(lfs, file); - if (err) { - return err; - } - } - } else { - file->ctz.size = lfs_max(file->pos, file->ctz.size); - } - - // actual file updates - file->ctz.head = file->block; - file->ctz.size = file->pos; - file->flags &= ~LFS_F_WRITING; - file->flags |= LFS_F_DIRTY; - - file->pos = pos; - } - - return 0; -} - -int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { - while (true) { - int err = lfs_file_flush(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - return err; - } - - if ((file->flags & LFS_F_DIRTY) && - !(file->flags & LFS_F_ERRED) && - !lfs_pair_isnull(file->m.pair)) { - // update dir entry - uint16_t type; - const void *buffer; - lfs_size_t size; - struct lfs_ctz ctz; - if (file->flags & LFS_F_INLINE) { - // inline the whole file - type = LFS_TYPE_INLINESTRUCT; - buffer = file->cache.buffer; - size = file->ctz.size; - } else { - // update the ctz reference - type = LFS_TYPE_CTZSTRUCT; - // copy ctz so alloc will work during a relocate - ctz = file->ctz; - lfs_ctz_tole32(&ctz); - buffer = &ctz; - size = sizeof(ctz); - } - - // commit file data and attributes - err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( - {LFS_MKTAG(type, file->id, size), buffer}, - {LFS_MKTAG(LFS_FROM_USERATTRS, file->id, - file->cfg->attr_count), file->cfg->attrs})); - if (err) { - if (err == LFS_ERR_NOSPC && (file->flags & LFS_F_INLINE)) { - goto relocate; - } - file->flags |= LFS_F_ERRED; - return err; - } - - file->flags &= ~LFS_F_DIRTY; - } - - return 0; - -relocate: - // inline file doesn't fit anymore - file->off = file->pos; - err = lfs_file_relocate(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - return err; - } - } -} - -lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, - void *buffer, lfs_size_t size) { - uint8_t *data = buffer; - lfs_size_t nsize = size; - - if ((file->flags & 3) == LFS_O_WRONLY) { - return LFS_ERR_BADF; - } - - if (file->flags & LFS_F_WRITING) { - // flush out any writes - int err = lfs_file_flush(lfs, file); - if (err) { - return err; - } - } - - if (file->pos >= file->ctz.size) { - // eof if past end - return 0; - } - - size = lfs_min(size, file->ctz.size - file->pos); - nsize = size; - - while (nsize > 0) { - // check if we need a new block - if (!(file->flags & LFS_F_READING) || - file->off == lfs->cfg->block_size) { - if (!(file->flags & LFS_F_INLINE)) { - int err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - file->pos, &file->block, &file->off); - if (err) { - return err; - } - } else { - file->block = 0xfffffffe; - file->off = file->pos; - } - - file->flags |= LFS_F_READING; - } - - // read as much as we can in current block - lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); - if (file->flags & LFS_F_INLINE) { - int err = lfs_dir_getread(lfs, &file->m, - NULL, &file->cache, lfs->cfg->block_size, - LFS_MKTAG(0xfff, 0x1ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), - file->off, data, diff); - if (err) { - return err; - } - } else { - int err = lfs_bd_read(lfs, - NULL, &file->cache, lfs->cfg->block_size, - file->block, file->off, data, diff); - if (err) { - return err; - } - } - - file->pos += diff; - file->off += diff; - data += diff; - nsize -= diff; - } - - return size; -} - -lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, - const void *buffer, lfs_size_t size) { - const uint8_t *data = buffer; - lfs_size_t nsize = size; - - if ((file->flags & 3) == LFS_O_RDONLY) { - return LFS_ERR_BADF; - } - - if (file->flags & LFS_F_READING) { - // drop any reads - int err = lfs_file_flush(lfs, file); - if (err) { - return err; - } - } - - if ((file->flags & LFS_O_APPEND) && file->pos < file->ctz.size) { - file->pos = file->ctz.size; - } - - if (file->pos + size > lfs->file_max) { - // Larger than file limit? - return LFS_ERR_FBIG; - } - - if (!(file->flags & LFS_F_WRITING) && file->pos > file->ctz.size) { - // fill with zeros - lfs_off_t pos = file->pos; - file->pos = file->ctz.size; - - while (file->pos < pos) { - lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); - if (res < 0) { - return res; - } - } - } - - if ((file->flags & LFS_F_INLINE) && - lfs_max(file->pos+nsize, file->ctz.size) > - lfs_min(LFS_ATTR_MAX, lfs_min( - lfs->cfg->cache_size, lfs->cfg->block_size/8))) { - // inline file doesn't fit anymore - file->off = file->pos; - lfs_alloc_ack(lfs); - int err = lfs_file_relocate(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - return err; - } - } - - while (nsize > 0) { - // check if we need a new block - if (!(file->flags & LFS_F_WRITING) || - file->off == lfs->cfg->block_size) { - if (!(file->flags & LFS_F_INLINE)) { - if (!(file->flags & LFS_F_WRITING) && file->pos > 0) { - // find out which block we're extending from - int err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - file->pos-1, &file->block, &file->off); - if (err) { - file->flags |= LFS_F_ERRED; - return err; - } - - // mark cache as dirty since we may have read data into it - lfs_cache_zero(lfs, &file->cache); - } - - // extend file with new blocks - lfs_alloc_ack(lfs); - int err = lfs_ctz_extend(lfs, &file->cache, &lfs->rcache, - file->block, file->pos, - &file->block, &file->off); - if (err) { - file->flags |= LFS_F_ERRED; - return err; - } - } else { - file->block = 0xfffffffe; - file->off = file->pos; - } - - file->flags |= LFS_F_WRITING; - } - - // program as much as we can in current block - lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); - while (true) { - int err = lfs_bd_prog(lfs, &file->cache, &lfs->rcache, true, - file->block, file->off, data, diff); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - file->flags |= LFS_F_ERRED; - return err; - } - - break; -relocate: - err = lfs_file_relocate(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - return err; - } - } - - file->pos += diff; - file->off += diff; - data += diff; - nsize -= diff; - - lfs_alloc_ack(lfs); - } - - file->flags &= ~LFS_F_ERRED; - return size; -} - -lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, - lfs_soff_t off, int whence) { - // write out everything beforehand, may be noop if rdonly - int err = lfs_file_flush(lfs, file); - if (err) { - return err; - } - - // find new pos - lfs_off_t npos = file->pos; - if (whence == LFS_SEEK_SET) { - npos = off; - } else if (whence == LFS_SEEK_CUR) { - npos = file->pos + off; - } else if (whence == LFS_SEEK_END) { - npos = file->ctz.size + off; - } - - if (npos > lfs->file_max) { - // file position out of range - return LFS_ERR_INVAL; - } - - // update pos - file->pos = npos; - return npos; -} - -int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { - if ((file->flags & 3) == LFS_O_RDONLY) { - return LFS_ERR_BADF; - } - - lfs_off_t oldsize = lfs_file_size(lfs, file); - if (size < oldsize) { - // need to flush since directly changing metadata - int err = lfs_file_flush(lfs, file); - if (err) { - return err; - } - - // lookup new head in ctz skip list - err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - size, &file->ctz.head, &(lfs_off_t){0}); - if (err) { - return err; - } - - file->ctz.size = size; - file->flags |= LFS_F_DIRTY; - } else if (size > oldsize) { - lfs_off_t pos = file->pos; - - // flush+seek if not already at end - if (file->pos != oldsize) { - int err = lfs_file_seek(lfs, file, 0, LFS_SEEK_END); - if (err < 0) { - return err; - } - } - - // fill with zeros - while (file->pos < size) { - lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); - if (res < 0) { - return res; - } - } - - // restore pos - int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET); - if (err < 0) { - return err; - } - } - - return 0; -} - -lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { - (void)lfs; - return file->pos; -} - -int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { - lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET); - if (res < 0) { - return res; - } - - return 0; -} - -lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { - (void)lfs; - if (file->flags & LFS_F_WRITING) { - return lfs_max(file->pos, file->ctz.size); - } else { - return file->ctz.size; - } -} - - -/// General fs operations /// -int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - return tag; - } - - return lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info); -} - -int lfs_remove(lfs_t *lfs, const char *path) { - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - return err; - } - - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0 || lfs_tag_id(tag) == 0x3ff) { - return (tag < 0) ? tag : LFS_ERR_INVAL; - } - - lfs_mdir_t dir; - if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { - // must be empty before removal - lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); - if (res < 0) { - return res; - } - lfs_pair_fromle32(pair); - - err = lfs_dir_fetch(lfs, &dir, pair); - if (err) { - return err; - } - - if (dir.count > 0 || dir.split) { - return LFS_ERR_NOTEMPTY; - } - - // mark fs as orphaned - lfs_fs_preporphans(lfs, +1); - } - - // delete the entry - err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0), NULL})); - if (err) { - return err; - } - - if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { - // fix orphan - lfs_fs_preporphans(lfs, -1); - - err = lfs_fs_pred(lfs, dir.pair, &cwd); - if (err) { - return err; - } - - err = lfs_dir_drop(lfs, &cwd, &dir); - if (err) { - return err; - } - } - - return 0; -} - -int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - return err; - } - - // find old entry - lfs_mdir_t oldcwd; - lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL); - if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) { - return (oldtag < 0) ? oldtag : LFS_ERR_INVAL; - } - - // find new entry - lfs_mdir_t newcwd; - uint16_t newid; - lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid); - if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) && - !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) { - return (prevtag < 0) ? prevtag : LFS_ERR_INVAL; - } - - lfs_mdir_t prevdir; - if (prevtag == LFS_ERR_NOENT) { - // check that name fits - lfs_size_t nlen = strlen(newpath); - if (nlen > lfs->name_max) { - return LFS_ERR_NAMETOOLONG; - } - } else if (lfs_tag_type3(prevtag) != lfs_tag_type3(oldtag)) { - return LFS_ERR_ISDIR; - } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { - // must be empty before removal - lfs_block_t prevpair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair); - if (res < 0) { - return res; - } - lfs_pair_fromle32(prevpair); - - // must be empty before removal - err = lfs_dir_fetch(lfs, &prevdir, prevpair); - if (err) { - return err; - } - - if (prevdir.count > 0 || prevdir.split) { - return LFS_ERR_NOTEMPTY; - } - - // mark fs as orphaned - lfs_fs_preporphans(lfs, +1); - } - - // create move to fix later - uint16_t newoldtagid = lfs_tag_id(oldtag); - if (lfs_pair_cmp(oldcwd.pair, newcwd.pair) == 0 && - prevtag == LFS_ERR_NOENT && newid <= newoldtagid) { - // there is a small chance we are being renamed in the same directory - // to an id less than our old id, the global update to handle this - // is a bit messy - newoldtagid += 1; - } - - lfs_fs_prepmove(lfs, newoldtagid, oldcwd.pair); - - // move over all attributes - err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS( - {prevtag != LFS_ERR_NOENT - ? LFS_MKTAG(LFS_TYPE_DELETE, newid, 0) - : LFS_MKTAG(LFS_FROM_NOOP, 0, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL}, - {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), - newpath}, - {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd})); - if (err) { - return err; - } - - // let commit clean up after move (if we're different! otherwise move - // logic already fixed it for us) - if (lfs_pair_cmp(oldcwd.pair, newcwd.pair) != 0) { - err = lfs_dir_commit(lfs, &oldcwd, NULL, 0); - if (err) { - return err; - } - } - - if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { - // fix orphan - lfs_fs_preporphans(lfs, -1); - - err = lfs_fs_pred(lfs, prevdir.pair, &newcwd); - if (err) { - return err; - } - - err = lfs_dir_drop(lfs, &newcwd, &prevdir); - if (err) { - return err; - } - } - - return 0; -} - -lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, - uint8_t type, void *buffer, lfs_size_t size) { - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - return tag; - } - - uint16_t id = lfs_tag_id(tag); - if (id == 0x3ff) { - // special case for root - id = 0; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - } - - tag = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_USERATTR + type, - id, lfs_min(size, lfs->attr_max)), - buffer); - if (tag < 0) { - if (tag == LFS_ERR_NOENT) { - return LFS_ERR_NOATTR; - } - return tag; - } - - return lfs_tag_size(tag); -} - -static int lfs_commitattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size) { - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - return tag; - } - - uint16_t id = lfs_tag_id(tag); - if (id == 0x3ff) { - // special case for root - id = 0; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - } - - return lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_USERATTR + type, id, size), buffer})); -} - -int lfs_setattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size) { - if (size > lfs->attr_max) { - return LFS_ERR_NOSPC; - } - - return lfs_commitattr(lfs, path, type, buffer, size); -} - -int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) { - return lfs_commitattr(lfs, path, type, NULL, 0x3ff); -} - - -/// Filesystem operations /// -static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { - lfs->cfg = cfg; - int err = 0; - - // check that block size is a multiple of cache size is a multiple - // of prog and read sizes - LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->read_size == 0); - LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->prog_size == 0); - LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0); - - // check that the block size is large enough to fit ctz pointers - LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4)) - <= lfs->cfg->block_size); - - // we don't support some corner cases - LFS_ASSERT(lfs->cfg->block_cycles < 0xffffffff); - - // setup read cache - if (lfs->cfg->read_buffer) { - lfs->rcache.buffer = lfs->cfg->read_buffer; - } else { - lfs->rcache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!lfs->rcache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // setup program cache - if (lfs->cfg->prog_buffer) { - lfs->pcache.buffer = lfs->cfg->prog_buffer; - } else { - lfs->pcache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!lfs->pcache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // zero to avoid information leaks - lfs_cache_zero(lfs, &lfs->rcache); - lfs_cache_zero(lfs, &lfs->pcache); - - // setup lookahead, must be multiple of 64-bits - LFS_ASSERT(lfs->cfg->lookahead_size > 0); - LFS_ASSERT(lfs->cfg->lookahead_size % 8 == 0 && - (uintptr_t)lfs->cfg->lookahead_buffer % 8 == 0); - if (lfs->cfg->lookahead_buffer) { - lfs->free.buffer = lfs->cfg->lookahead_buffer; - } else { - lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead_size); - if (!lfs->free.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // check that the size limits are sane - LFS_ASSERT(lfs->cfg->name_max <= LFS_NAME_MAX); - lfs->name_max = lfs->cfg->name_max; - if (!lfs->name_max) { - lfs->name_max = LFS_NAME_MAX; - } - - LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX); - lfs->file_max = lfs->cfg->file_max; - if (!lfs->file_max) { - lfs->file_max = LFS_FILE_MAX; - } - - LFS_ASSERT(lfs->cfg->attr_max <= LFS_ATTR_MAX); - lfs->attr_max = lfs->cfg->attr_max; - if (!lfs->attr_max) { - lfs->attr_max = LFS_ATTR_MAX; - } - - // setup default state - lfs->root[0] = 0xffffffff; - lfs->root[1] = 0xffffffff; - lfs->mlist = NULL; - lfs->seed = 0; - lfs->gstate = (struct lfs_gstate){0}; - lfs->gpending = (struct lfs_gstate){0}; - lfs->gdelta = (struct lfs_gstate){0}; -#ifdef LFS_MIGRATE - lfs->lfs1 = NULL; -#endif - - return 0; - -cleanup: - lfs_deinit(lfs); - return err; -} - -static int lfs_deinit(lfs_t *lfs) { - // free allocated memory - if (!lfs->cfg->read_buffer) { - lfs_free(lfs->rcache.buffer); - } - - if (!lfs->cfg->prog_buffer) { - lfs_free(lfs->pcache.buffer); - } - - if (!lfs->cfg->lookahead_buffer) { - lfs_free(lfs->free.buffer); - } - - return 0; -} - -int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { - int err = 0; - { - err = lfs_init(lfs, cfg); - if (err) { - return err; - } - - // create free lookahead - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - lfs->free.off = 0; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, - lfs->cfg->block_count); - lfs->free.i = 0; - lfs_alloc_ack(lfs); - - // create root dir - lfs_mdir_t root; - err = lfs_dir_alloc(lfs, &root); - if (err) { - goto cleanup; - } - - // write one superblock - lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, - .block_size = lfs->cfg->block_size, - .block_count = lfs->cfg->block_count, - .name_max = lfs->name_max, - .file_max = lfs->file_max, - .attr_max = lfs->attr_max, - }; - - lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock})); - if (err) { - goto cleanup; - } - - // sanity check that fetch works - err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - } - -cleanup: - lfs_deinit(lfs); - return err; -} - -int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { - int err = lfs_init(lfs, cfg); - if (err) { - return err; - } - - // scan directory blocks for superblock and any global updates - lfs_mdir_t dir = {.tail = {0, 1}}; - while (!lfs_pair_isnull(dir.tail)) { - // fetch next block in tail list - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, - LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), - NULL, - lfs_dir_find_match, &(struct lfs_dir_find_match){ - lfs, "littlefs", 8}); - if (tag < 0) { - err = tag; - goto cleanup; - } - - // has superblock? - if (tag && !lfs_tag_isdelete(tag)) { - // update root - lfs->root[0] = dir.pair[0]; - lfs->root[1] = dir.pair[1]; - - // grab superblock - lfs_superblock_t superblock; - tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock); - if (tag < 0) { - err = tag; - goto cleanup; - } - lfs_superblock_fromle32(&superblock); - - // check version - uint16_t major_version = (0xffff & (superblock.version >> 16)); - uint16_t minor_version = (0xffff & (superblock.version >> 0)); - if ((major_version != LFS_DISK_VERSION_MAJOR || - minor_version > LFS_DISK_VERSION_MINOR)) { - LFS_ERROR("Invalid version %"PRIu16".%"PRIu16, - major_version, minor_version); - err = LFS_ERR_INVAL; - goto cleanup; - } - - // check superblock configuration - if (superblock.name_max) { - if (superblock.name_max > lfs->name_max) { - LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")", - superblock.name_max, lfs->name_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->name_max = superblock.name_max; - } - - if (superblock.file_max) { - if (superblock.file_max > lfs->file_max) { - LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")", - superblock.file_max, lfs->file_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->file_max = superblock.file_max; - } - - if (superblock.attr_max) { - if (superblock.attr_max > lfs->attr_max) { - LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")", - superblock.attr_max, lfs->attr_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->attr_max = superblock.attr_max; - } - } - - // has gstate? - err = lfs_dir_getgstate(lfs, &dir, &lfs->gpending); - if (err) { - return err; - } - } - - // found superblock? - if (lfs_pair_isnull(lfs->root)) { - err = LFS_ERR_INVAL; - goto cleanup; - } - - // update littlefs with gstate - lfs->gpending.tag += !lfs_tag_isvalid(lfs->gpending.tag); - lfs->gstate = lfs->gpending; - if (lfs_gstate_hasmove(&lfs->gstate)) { - LFS_DEBUG("Found move %"PRIu32" %"PRIu32" %"PRIu16, - lfs->gstate.pair[0], - lfs->gstate.pair[1], - lfs_tag_id(lfs->gstate.tag)); - } - - // setup free lookahead - lfs->free.off = lfs->seed % lfs->cfg->block_size; - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); - - return 0; - -cleanup: - lfs_unmount(lfs); - return err; -} - -int lfs_unmount(lfs_t *lfs) { - return lfs_deinit(lfs); -} - - -/// Filesystem filesystem operations /// -int lfs_fs_traverse(lfs_t *lfs, - int (*cb)(void *data, lfs_block_t block), void *data) { - // iterate over metadata pairs - lfs_mdir_t dir = {.tail = {0, 1}}; - -#ifdef LFS_MIGRATE - // also consider v1 blocks during migration - if (lfs->lfs1) { - int err = lfs1_traverse(lfs, cb, data); - if (err) { - return err; - } - - dir.tail[0] = lfs->root[0]; - dir.tail[1] = lfs->root[1]; - } -#endif - - while (!lfs_pair_isnull(dir.tail)) { - for (int i = 0; i < 2; i++) { - int err = cb(data, dir.tail[i]); - if (err) { - return err; - } - } - - // iterate through ids in directory - int err = lfs_dir_fetch(lfs, &dir, dir.tail); - if (err) { - return err; - } - - for (uint16_t id = 0; id < dir.count; id++) { - struct lfs_ctz ctz; - lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); - if (tag < 0) { - if (tag == LFS_ERR_NOENT) { - continue; - } - return tag; - } - lfs_ctz_fromle32(&ctz); - - if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { - err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, - ctz.head, ctz.size, cb, data); - if (err) { - return err; - } - } - } - } - - // iterate over any open files - for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { - if (f->type != LFS_TYPE_REG) { - continue; - } - - if ((f->flags & LFS_F_DIRTY) && !(f->flags & LFS_F_INLINE)) { - int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, - f->ctz.head, f->ctz.size, cb, data); - if (err) { - return err; - } - } - - if ((f->flags & LFS_F_WRITING) && !(f->flags & LFS_F_INLINE)) { - int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, - f->block, f->pos, cb, data); - if (err) { - return err; - } - } - } - - return 0; -} - -static int lfs_fs_pred(lfs_t *lfs, - const lfs_block_t pair[2], lfs_mdir_t *pdir) { - // iterate over all directory directory entries - pdir->tail[0] = 0; - pdir->tail[1] = 1; - while (!lfs_pair_isnull(pdir->tail)) { - if (lfs_pair_cmp(pdir->tail, pair) == 0) { - return 0; - } - - int err = lfs_dir_fetch(lfs, pdir, pdir->tail); - if (err) { - return err; - } - } - - return LFS_ERR_NOENT; -} - -struct lfs_fs_parent_match { - lfs_t *lfs; - const lfs_block_t pair[2]; -}; - -static int lfs_fs_parent_match(void *data, - lfs_tag_t tag, const void *buffer) { - struct lfs_fs_parent_match *find = data; - lfs_t *lfs = find->lfs; - const struct lfs_diskoff *disk = buffer; - (void)tag; - - lfs_block_t child[2]; - int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - disk->block, disk->off, &child, sizeof(child)); - if (err) { - return err; - } - - lfs_pair_fromle32(child); - return (lfs_pair_cmp(child, find->pair) == 0) ? LFS_CMP_EQ : LFS_CMP_LT; -} - -static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], - lfs_mdir_t *parent) { - // use fetchmatch with callback to find pairs - parent->tail[0] = 0; - parent->tail[1] = 1; - while (!lfs_pair_isnull(parent->tail)) { - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, - LFS_MKTAG(0x7ff, 0, 0x3ff), - LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), - NULL, - lfs_fs_parent_match, &(struct lfs_fs_parent_match){ - lfs, {pair[0], pair[1]}}); - if (tag && tag != LFS_ERR_NOENT) { - return tag; - } - } - - return LFS_ERR_NOENT; -} - -static int lfs_fs_relocate(lfs_t *lfs, - const lfs_block_t oldpair[2], lfs_block_t newpair[2]) { - // update internal root - if (lfs_pair_cmp(oldpair, lfs->root) == 0) { - LFS_DEBUG("Relocating root %"PRIu32" %"PRIu32, - newpair[0], newpair[1]); - lfs->root[0] = newpair[0]; - lfs->root[1] = newpair[1]; - } - - // update internally tracked dirs - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (lfs_pair_cmp(oldpair, d->m.pair) == 0) { - d->m.pair[0] = newpair[0]; - d->m.pair[1] = newpair[1]; - } - } - - // find parent - lfs_mdir_t parent; - lfs_stag_t tag = lfs_fs_parent(lfs, oldpair, &parent); - if (tag < 0 && tag != LFS_ERR_NOENT) { - return tag; - } - - if (tag != LFS_ERR_NOENT) { - // update disk, this creates a desync - lfs_fs_preporphans(lfs, +1); - - lfs_pair_tole32(newpair); - int err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS({tag, newpair})); - lfs_pair_fromle32(newpair); - if (err) { - return err; - } - - // next step, clean up orphans - lfs_fs_preporphans(lfs, -1); - } - - // find pred - int err = lfs_fs_pred(lfs, oldpair, &parent); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - // if we can't find dir, it must be new - if (err != LFS_ERR_NOENT) { - // replace bad pair, either we clean up desync, or no desync occured - lfs_pair_tole32(newpair); - err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); - lfs_pair_fromle32(newpair); - if (err) { - return err; - } - } - - return 0; -} - -static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) { - lfs->gpending.tag += orphans; - lfs_gstate_xororphans(&lfs->gdelta, &lfs->gpending, - lfs_gstate_hasorphans(&lfs->gpending)); - lfs_gstate_xororphans(&lfs->gpending, &lfs->gpending, - lfs_gstate_hasorphans(&lfs->gpending)); -} - -static void lfs_fs_prepmove(lfs_t *lfs, - uint16_t id, const lfs_block_t pair[2]) { - lfs_gstate_xormove(&lfs->gdelta, &lfs->gpending, id, pair); - lfs_gstate_xormove(&lfs->gpending, &lfs->gpending, id, pair); -} - - -static int lfs_fs_demove(lfs_t *lfs) { - if (!lfs_gstate_hasmove(&lfs->gstate)) { - return 0; - } - - // Fix bad moves - LFS_DEBUG("Fixing move %"PRIu32" %"PRIu32" %"PRIu16, - lfs->gstate.pair[0], - lfs->gstate.pair[1], - lfs_tag_id(lfs->gstate.tag)); - - // fetch and delete the moved entry - lfs_mdir_t movedir; - int err = lfs_dir_fetch(lfs, &movedir, lfs->gstate.pair); - if (err) { - return err; - } - - // rely on cancel logic inside commit - err = lfs_dir_commit(lfs, &movedir, NULL, 0); - if (err) { - return err; - } - - return 0; -} - -static int lfs_fs_deorphan(lfs_t *lfs) { - if (!lfs_gstate_hasorphans(&lfs->gstate)) { - return 0; - } - - // Fix any orphans - lfs_mdir_t pdir = {.split = true}; - lfs_mdir_t dir = {.tail = {0, 1}}; - - // iterate over all directory directory entries - while (!lfs_pair_isnull(dir.tail)) { - int err = lfs_dir_fetch(lfs, &dir, dir.tail); - if (err) { - return err; - } - - // check head blocks for orphans - if (!pdir.split) { - // check if we have a parent - lfs_mdir_t parent; - lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent); - if (tag < 0 && tag != LFS_ERR_NOENT) { - return tag; - } - - if (tag == LFS_ERR_NOENT) { - // we are an orphan - LFS_DEBUG("Fixing orphan %"PRIu32" %"PRIu32, - pdir.tail[0], pdir.tail[1]); - - err = lfs_dir_drop(lfs, &pdir, &dir); - if (err) { - return err; - } - - break; - } - - lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &parent, - LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair); - if (res < 0) { - return res; - } - lfs_pair_fromle32(pair); - - if (!lfs_pair_sync(pair, pdir.tail)) { - // we have desynced - LFS_DEBUG("Fixing half-orphan %"PRIu32" %"PRIu32, - pair[0], pair[1]); - - lfs_pair_tole32(pair); - err = lfs_dir_commit(lfs, &pdir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pair})); - lfs_pair_fromle32(pair); - if (err) { - return err; - } - - break; - } - } - - memcpy(&pdir, &dir, sizeof(pdir)); - } - - // mark orphans as fixed - lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate)); - lfs->gstate = lfs->gpending; - return 0; -} - -static int lfs_fs_forceconsistency(lfs_t *lfs) { - int err = lfs_fs_demove(lfs); - if (err) { - return err; - } - - err = lfs_fs_deorphan(lfs); - if (err) { - return err; - } - - return 0; -} - -static int lfs_fs_size_count(void *p, lfs_block_t block) { - (void)block; - lfs_size_t *size = p; - *size += 1; - return 0; -} - -lfs_ssize_t lfs_fs_size(lfs_t *lfs) { - lfs_size_t size = 0; - int err = lfs_fs_traverse(lfs, lfs_fs_size_count, &size); - if (err) { - return err; - } - - return size; -} - -#ifdef LFS_MIGRATE -////// Migration from littelfs v1 below this ////// - -/// Version info /// - -// Software library version -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS1_VERSION 0x00010007 -#define LFS1_VERSION_MAJOR (0xffff & (LFS1_VERSION >> 16)) -#define LFS1_VERSION_MINOR (0xffff & (LFS1_VERSION >> 0)) - -// Version of On-disk data structures -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS1_DISK_VERSION 0x00010001 -#define LFS1_DISK_VERSION_MAJOR (0xffff & (LFS1_DISK_VERSION >> 16)) -#define LFS1_DISK_VERSION_MINOR (0xffff & (LFS1_DISK_VERSION >> 0)) - - -/// v1 Definitions /// - -// File types -enum lfs1_type { - LFS1_TYPE_REG = 0x11, - LFS1_TYPE_DIR = 0x22, - LFS1_TYPE_SUPERBLOCK = 0x2e, -}; - -typedef struct lfs1 { - lfs_block_t root[2]; -} lfs1_t; - -typedef struct lfs1_entry { - lfs_off_t off; - - struct lfs1_disk_entry { - uint8_t type; - uint8_t elen; - uint8_t alen; - uint8_t nlen; - union { - struct { - lfs_block_t head; - lfs_size_t size; - } file; - lfs_block_t dir[2]; - } u; - } d; -} lfs1_entry_t; - -typedef struct lfs1_dir { - struct lfs1_dir *next; - lfs_block_t pair[2]; - lfs_off_t off; - - lfs_block_t head[2]; - lfs_off_t pos; - - struct lfs1_disk_dir { - uint32_t rev; - lfs_size_t size; - lfs_block_t tail[2]; - } d; -} lfs1_dir_t; - -typedef struct lfs1_superblock { - lfs_off_t off; - - struct lfs1_disk_superblock { - uint8_t type; - uint8_t elen; - uint8_t alen; - uint8_t nlen; - lfs_block_t root[2]; - uint32_t block_size; - uint32_t block_count; - uint32_t version; - char magic[8]; - } d; -} lfs1_superblock_t; - - -/// Low-level wrappers v1->v2 /// -void lfs1_crc(uint32_t *crc, const void *buffer, size_t size) { - *crc = lfs_crc(*crc, buffer, size); -} - -static int lfs1_bd_read(lfs_t *lfs, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size) { - // if we ever do more than writes to alternating pairs, - // this may need to consider pcache - return lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, size, - block, off, buffer, size); -} - -static int lfs1_bd_crc(lfs_t *lfs, lfs_block_t block, - lfs_off_t off, lfs_size_t size, uint32_t *crc) { - for (lfs_off_t i = 0; i < size; i++) { - uint8_t c; - int err = lfs1_bd_read(lfs, block, off+i, &c, 1); - if (err) { - return err; - } - - lfs1_crc(crc, &c, 1); - } - - return 0; -} - - -/// Endian swapping functions /// -static void lfs1_dir_fromle32(struct lfs1_disk_dir *d) { - d->rev = lfs_fromle32(d->rev); - d->size = lfs_fromle32(d->size); - d->tail[0] = lfs_fromle32(d->tail[0]); - d->tail[1] = lfs_fromle32(d->tail[1]); -} - -static void lfs1_dir_tole32(struct lfs1_disk_dir *d) { - d->rev = lfs_tole32(d->rev); - d->size = lfs_tole32(d->size); - d->tail[0] = lfs_tole32(d->tail[0]); - d->tail[1] = lfs_tole32(d->tail[1]); -} - -static void lfs1_entry_fromle32(struct lfs1_disk_entry *d) { - d->u.dir[0] = lfs_fromle32(d->u.dir[0]); - d->u.dir[1] = lfs_fromle32(d->u.dir[1]); -} - -static void lfs1_entry_tole32(struct lfs1_disk_entry *d) { - d->u.dir[0] = lfs_tole32(d->u.dir[0]); - d->u.dir[1] = lfs_tole32(d->u.dir[1]); -} - -static void lfs1_superblock_fromle32(struct lfs1_disk_superblock *d) { - d->root[0] = lfs_fromle32(d->root[0]); - d->root[1] = lfs_fromle32(d->root[1]); - d->block_size = lfs_fromle32(d->block_size); - d->block_count = lfs_fromle32(d->block_count); - d->version = lfs_fromle32(d->version); -} - - -///// Metadata pair and directory operations /// -static inline lfs_size_t lfs1_entry_size(const lfs1_entry_t *entry) { - return 4 + entry->d.elen + entry->d.alen + entry->d.nlen; -} - -static int lfs1_dir_fetch(lfs_t *lfs, - lfs1_dir_t *dir, const lfs_block_t pair[2]) { - // copy out pair, otherwise may be aliasing dir - const lfs_block_t tpair[2] = {pair[0], pair[1]}; - bool valid = false; - - // check both blocks for the most recent revision - for (int i = 0; i < 2; i++) { - struct lfs1_disk_dir test; - int err = lfs1_bd_read(lfs, tpair[i], 0, &test, sizeof(test)); - lfs1_dir_fromle32(&test); - if (err) { - if (err == LFS_ERR_CORRUPT) { - continue; - } - return err; - } - - if (valid && lfs_scmp(test.rev, dir->d.rev) < 0) { - continue; - } - - if ((0x7fffffff & test.size) < sizeof(test)+4 || - (0x7fffffff & test.size) > lfs->cfg->block_size) { - continue; - } - - uint32_t crc = 0xffffffff; - lfs1_dir_tole32(&test); - lfs1_crc(&crc, &test, sizeof(test)); - lfs1_dir_fromle32(&test); - err = lfs1_bd_crc(lfs, tpair[i], sizeof(test), - (0x7fffffff & test.size) - sizeof(test), &crc); - if (err) { - if (err == LFS_ERR_CORRUPT) { - continue; - } - return err; - } - - if (crc != 0) { - continue; - } - - valid = true; - - // setup dir in case it's valid - dir->pair[0] = tpair[(i+0) % 2]; - dir->pair[1] = tpair[(i+1) % 2]; - dir->off = sizeof(dir->d); - dir->d = test; - } - - if (!valid) { - LFS_ERROR("Corrupted dir pair at %" PRIu32 " %" PRIu32 , - tpair[0], tpair[1]); - return LFS_ERR_CORRUPT; - } - - return 0; -} - -static int lfs1_dir_next(lfs_t *lfs, lfs1_dir_t *dir, lfs1_entry_t *entry) { - while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) { - if (!(0x80000000 & dir->d.size)) { - entry->off = dir->off; - return LFS_ERR_NOENT; - } - - int err = lfs1_dir_fetch(lfs, dir, dir->d.tail); - if (err) { - return err; - } - - dir->off = sizeof(dir->d); - dir->pos += sizeof(dir->d) + 4; - } - - int err = lfs1_bd_read(lfs, dir->pair[0], dir->off, - &entry->d, sizeof(entry->d)); - lfs1_entry_fromle32(&entry->d); - if (err) { - return err; - } - - entry->off = dir->off; - dir->off += lfs1_entry_size(entry); - dir->pos += lfs1_entry_size(entry); - return 0; -} - -/// littlefs v1 specific operations /// -int lfs1_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { - if (lfs_pair_isnull(lfs->lfs1->root)) { - return 0; - } - - // iterate over metadata pairs - lfs1_dir_t dir; - lfs1_entry_t entry; - lfs_block_t cwd[2] = {0, 1}; - - while (true) { - for (int i = 0; i < 2; i++) { - int err = cb(data, cwd[i]); - if (err) { - return err; - } - } - - int err = lfs1_dir_fetch(lfs, &dir, cwd); - if (err) { - return err; - } - - // iterate over contents - while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) { - err = lfs1_bd_read(lfs, dir.pair[0], dir.off, - &entry.d, sizeof(entry.d)); - lfs1_entry_fromle32(&entry.d); - if (err) { - return err; - } - - dir.off += lfs1_entry_size(&entry); - if ((0x70 & entry.d.type) == (0x70 & LFS1_TYPE_REG)) { - err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, - entry.d.u.file.head, entry.d.u.file.size, cb, data); - if (err) { - return err; - } - } - } - - // we also need to check if we contain a threaded v2 directory - lfs_mdir_t dir2 = {.split=true, .tail={cwd[0], cwd[1]}}; - while (dir2.split) { - err = lfs_dir_fetch(lfs, &dir2, dir2.tail); - if (err) { - break; - } - - for (int i = 0; i < 2; i++) { - err = cb(data, dir2.pair[i]); - if (err) { - return err; - } - } - } - - cwd[0] = dir.d.tail[0]; - cwd[1] = dir.d.tail[1]; - - if (lfs_pair_isnull(cwd)) { - break; - } - } - - return 0; -} - -static int lfs1_moved(lfs_t *lfs, const void *e) { - if (lfs_pair_isnull(lfs->lfs1->root)) { - return 0; - } - - // skip superblock - lfs1_dir_t cwd; - int err = lfs1_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1}); - if (err) { - return err; - } - - // iterate over all directory directory entries - lfs1_entry_t entry; - while (!lfs_pair_isnull(cwd.d.tail)) { - err = lfs1_dir_fetch(lfs, &cwd, cwd.d.tail); - if (err) { - return err; - } - - while (true) { - err = lfs1_dir_next(lfs, &cwd, &entry); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - if (err == LFS_ERR_NOENT) { - break; - } - - if (!(0x80 & entry.d.type) && - memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { - return true; - } - } - } - - return false; -} - -/// Filesystem operations /// -static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1, - const struct lfs_config *cfg) { - int err = 0; - { - err = lfs_init(lfs, cfg); - if (err) { - return err; - } - - lfs->lfs1 = lfs1; - lfs->lfs1->root[0] = 0xffffffff; - lfs->lfs1->root[1] = 0xffffffff; - - // setup free lookahead - lfs->free.off = 0; - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); - - // load superblock - lfs1_dir_t dir; - lfs1_superblock_t superblock; - err = lfs1_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); - if (err && err != LFS_ERR_CORRUPT) { - goto cleanup; - } - - if (!err) { - err = lfs1_bd_read(lfs, dir.pair[0], sizeof(dir.d), - &superblock.d, sizeof(superblock.d)); - lfs1_superblock_fromle32(&superblock.d); - if (err) { - goto cleanup; - } - - lfs->lfs1->root[0] = superblock.d.root[0]; - lfs->lfs1->root[1] = superblock.d.root[1]; - } - - if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { - LFS_ERROR("Invalid superblock at %d %d", 0, 1); - err = LFS_ERR_CORRUPT; - goto cleanup; - } - - uint16_t major_version = (0xffff & (superblock.d.version >> 16)); - uint16_t minor_version = (0xffff & (superblock.d.version >> 0)); - if ((major_version != LFS1_DISK_VERSION_MAJOR || - minor_version > LFS1_DISK_VERSION_MINOR)) { - LFS_ERROR("Invalid version %d.%d", major_version, minor_version); - err = LFS_ERR_INVAL; - goto cleanup; - } - - return 0; - } - -cleanup: - lfs_deinit(lfs); - return err; -} - -static int lfs1_unmount(lfs_t *lfs) { - return lfs_deinit(lfs); -} - -/// v1 migration /// -int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { - struct lfs1 lfs1; - int err = lfs1_mount(lfs, &lfs1, cfg); - if (err) { - return err; - } - - { - // iterate through each directory, copying over entries - // into new directory - lfs1_dir_t dir1; - lfs_mdir_t dir2; - dir1.d.tail[0] = lfs->lfs1->root[0]; - dir1.d.tail[1] = lfs->lfs1->root[1]; - while (!lfs_pair_isnull(dir1.d.tail)) { - // iterate old dir - err = lfs1_dir_fetch(lfs, &dir1, dir1.d.tail); - if (err) { - goto cleanup; - } - - // create new dir and bind as temporary pretend root - err = lfs_dir_alloc(lfs, &dir2); - if (err) { - goto cleanup; - } - - dir2.rev = dir1.d.rev; - dir1.head[0] = dir1.pair[0]; - dir1.head[1] = dir1.pair[1]; - lfs->root[0] = dir2.pair[0]; - lfs->root[1] = dir2.pair[1]; - - err = lfs_dir_commit(lfs, &dir2, NULL, 0); - if (err) { - goto cleanup; - } - - while (true) { - lfs1_entry_t entry1; - err = lfs1_dir_next(lfs, &dir1, &entry1); - if (err && err != LFS_ERR_NOENT) { - goto cleanup; - } - - if (err == LFS_ERR_NOENT) { - break; - } - - // check that entry has not been moved - if (entry1.d.type & 0x80) { - int moved = lfs1_moved(lfs, &entry1.d.u); - if (moved < 0) { - err = moved; - goto cleanup; - } - - if (moved) { - continue; - } - - entry1.d.type &= ~0x80; - } - - // also fetch name - char name[LFS_NAME_MAX+1]; - memset(name, 0, sizeof(name)); - err = lfs1_bd_read(lfs, dir1.pair[0], - entry1.off + 4+entry1.d.elen+entry1.d.alen, - name, entry1.d.nlen); - if (err) { - goto cleanup; - } - - bool isdir = (entry1.d.type == LFS1_TYPE_DIR); - - // create entry in new dir - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - uint16_t id; - err = lfs_dir_find(lfs, &dir2, &(const char*){name}, &id); - if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { - err = (err < 0) ? err : LFS_ERR_EXIST; - goto cleanup; - } - - lfs1_entry_tole32(&entry1.d); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, - {LFS_MKTAG( - isdir ? LFS_TYPE_DIR : LFS_TYPE_REG, - id, entry1.d.nlen), name}, - {LFS_MKTAG( - isdir ? LFS_TYPE_DIRSTRUCT : LFS_TYPE_CTZSTRUCT, - id, sizeof(&entry1.d.u)), &entry1.d.u})); - lfs1_entry_fromle32(&entry1.d); - if (err) { - goto cleanup; - } - } - - if (!lfs_pair_isnull(dir1.d.tail)) { - // find last block and update tail to thread into fs - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - while (dir2.split) { - err = lfs_dir_fetch(lfs, &dir2, dir2.tail); - if (err) { - goto cleanup; - } - } - - lfs_pair_tole32(dir2.pair); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 0), - dir1.d.tail})); - lfs_pair_fromle32(dir2.pair); - if (err) { - goto cleanup; - } - } - - // Copy over first block to thread into fs. Unfortunately - // if this fails there is not much we can do. - LFS_DEBUG("Migrating %"PRIu32" %"PRIu32" -> %"PRIu32" %"PRIu32, - lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]); - - err = lfs_bd_erase(lfs, dir1.head[1]); - if (err) { - goto cleanup; - } - - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - for (lfs_off_t i = 0; i < dir2.off; i++) { - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, dir2.off, - dir2.pair[0], i, &dat, 1); - if (err) { - goto cleanup; - } - - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, true, - dir1.head[1], i, &dat, 1); - if (err) { - goto cleanup; - } - } - } - - // Create new superblock. This marks a successful migration! - err = lfs1_dir_fetch(lfs, &dir1, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - - dir2.pair[0] = dir1.pair[0]; - dir2.pair[1] = dir1.pair[1]; - dir2.rev = dir1.d.rev; - dir2.off = sizeof(dir2.rev); - dir2.etag = 0xffffffff; - dir2.count = 0; - dir2.tail[0] = lfs->lfs1->root[0]; - dir2.tail[1] = lfs->lfs1->root[1]; - dir2.erased = false; - dir2.split = true; - - lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, - .block_size = lfs->cfg->block_size, - .block_count = lfs->cfg->block_count, - .name_max = lfs->name_max, - .file_max = lfs->file_max, - .attr_max = lfs->attr_max, - }; - - lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock})); - if (err) { - goto cleanup; - } - - // sanity check that fetch works - err = lfs_dir_fetch(lfs, &dir2, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - } - -cleanup: - lfs1_unmount(lfs); - return err; -} - -#endif diff --git a/lfs2.c b/lfs2.c new file mode 100644 index 0000000..8b58a48 --- /dev/null +++ b/lfs2.c @@ -0,0 +1,4459 @@ +/* + * The little filesystem + * + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "lfs2.h" +#include "lfs2_util.h" + + +/// Caching block device operations /// +static inline void lfs2_cache_drop(lfs2_t *lfs2, lfs2_cache_t *rcache) { + // do not zero, cheaper if cache is readonly or only going to be + // written with identical data (during relocates) + (void)lfs2; + rcache->block = 0xffffffff; +} + +static inline void lfs2_cache_zero(lfs2_t *lfs2, lfs2_cache_t *pcache) { + // zero to avoid information leak + memset(pcache->buffer, 0xff, lfs2->cfg->prog_size); + pcache->block = 0xffffffff; +} + +static int lfs2_bd_read(lfs2_t *lfs2, + const lfs2_cache_t *pcache, lfs2_cache_t *rcache, lfs2_size_t hint, + lfs2_block_t block, lfs2_off_t off, + void *buffer, lfs2_size_t size) { + uint8_t *data = buffer; + LFS2_ASSERT(block != 0xffffffff); + if (off+size > lfs2->cfg->block_size) { + return LFS2_ERR_CORRUPT; + } + + while (size > 0) { + lfs2_size_t diff = size; + + if (pcache && block == pcache->block && + off < pcache->off + pcache->size) { + if (off >= pcache->off) { + // is already in pcache? + diff = lfs2_min(diff, pcache->size - (off-pcache->off)); + memcpy(data, &pcache->buffer[off-pcache->off], diff); + + data += diff; + off += diff; + size -= diff; + continue; + } + + // pcache takes priority + diff = lfs2_min(diff, pcache->off-off); + } + + if (block == rcache->block && + off < rcache->off + rcache->size) { + if (off >= rcache->off) { + // is already in rcache? + diff = lfs2_min(diff, rcache->size - (off-rcache->off)); + memcpy(data, &rcache->buffer[off-rcache->off], diff); + + data += diff; + off += diff; + size -= diff; + continue; + } + + // rcache takes priority + diff = lfs2_min(diff, rcache->off-off); + } + + if (size >= hint && off % lfs2->cfg->read_size == 0 && + size >= lfs2->cfg->read_size) { + // bypass cache? + diff = lfs2_aligndown(diff, lfs2->cfg->read_size); + int err = lfs2->cfg->read(lfs2->cfg, block, off, data, diff); + if (err) { + return err; + } + + data += diff; + off += diff; + size -= diff; + continue; + } + + // load to cache, first condition can no longer fail + LFS2_ASSERT(block < lfs2->cfg->block_count); + rcache->block = block; + rcache->off = lfs2_aligndown(off, lfs2->cfg->read_size); + rcache->size = lfs2_min(lfs2_alignup(off+hint, lfs2->cfg->read_size), + lfs2_min(lfs2->cfg->block_size - rcache->off, + lfs2->cfg->cache_size)); + int err = lfs2->cfg->read(lfs2->cfg, rcache->block, + rcache->off, rcache->buffer, rcache->size); + if (err) { + return err; + } + } + + return 0; +} + +enum { + LFS2_CMP_EQ = 0, + LFS2_CMP_LT = 1, + LFS2_CMP_GT = 2, +}; + +static int lfs2_bd_cmp(lfs2_t *lfs2, + const lfs2_cache_t *pcache, lfs2_cache_t *rcache, lfs2_size_t hint, + lfs2_block_t block, lfs2_off_t off, + const void *buffer, lfs2_size_t size) { + const uint8_t *data = buffer; + + for (lfs2_off_t i = 0; i < size; i++) { + uint8_t dat; + int err = lfs2_bd_read(lfs2, + pcache, rcache, hint-i, + block, off+i, &dat, 1); + if (err) { + return err; + } + + if (dat != data[i]) { + return (dat < data[i]) ? LFS2_CMP_LT : LFS2_CMP_GT; + } + } + + return LFS2_CMP_EQ; +} + +static int lfs2_bd_flush(lfs2_t *lfs2, + lfs2_cache_t *pcache, lfs2_cache_t *rcache, bool validate) { + if (pcache->block != 0xffffffff && pcache->block != 0xfffffffe) { + LFS2_ASSERT(pcache->block < lfs2->cfg->block_count); + lfs2_size_t diff = lfs2_alignup(pcache->size, lfs2->cfg->prog_size); + int err = lfs2->cfg->prog(lfs2->cfg, pcache->block, + pcache->off, pcache->buffer, diff); + if (err) { + return err; + } + + if (validate) { + // check data on disk + lfs2_cache_drop(lfs2, rcache); + int res = lfs2_bd_cmp(lfs2, + NULL, rcache, diff, + pcache->block, pcache->off, pcache->buffer, diff); + if (res < 0) { + return res; + } + + if (res != LFS2_CMP_EQ) { + return LFS2_ERR_CORRUPT; + } + } + + lfs2_cache_zero(lfs2, pcache); + } + + return 0; +} + +static int lfs2_bd_sync(lfs2_t *lfs2, + lfs2_cache_t *pcache, lfs2_cache_t *rcache, bool validate) { + lfs2_cache_drop(lfs2, rcache); + + int err = lfs2_bd_flush(lfs2, pcache, rcache, validate); + if (err) { + return err; + } + + return lfs2->cfg->sync(lfs2->cfg); +} + +static int lfs2_bd_prog(lfs2_t *lfs2, + lfs2_cache_t *pcache, lfs2_cache_t *rcache, bool validate, + lfs2_block_t block, lfs2_off_t off, + const void *buffer, lfs2_size_t size) { + const uint8_t *data = buffer; + LFS2_ASSERT(block != 0xffffffff); + LFS2_ASSERT(off + size <= lfs2->cfg->block_size); + + while (size > 0) { + if (block == pcache->block && + off >= pcache->off && + off < pcache->off + lfs2->cfg->cache_size) { + // already fits in pcache? + lfs2_size_t diff = lfs2_min(size, + lfs2->cfg->cache_size - (off-pcache->off)); + memcpy(&pcache->buffer[off-pcache->off], data, diff); + + data += diff; + off += diff; + size -= diff; + + pcache->size = off - pcache->off; + if (pcache->size == lfs2->cfg->cache_size) { + // eagerly flush out pcache if we fill up + int err = lfs2_bd_flush(lfs2, pcache, rcache, validate); + if (err) { + return err; + } + } + + continue; + } + + // pcache must have been flushed, either by programming and + // entire block or manually flushing the pcache + LFS2_ASSERT(pcache->block == 0xffffffff); + + // prepare pcache, first condition can no longer fail + pcache->block = block; + pcache->off = lfs2_aligndown(off, lfs2->cfg->prog_size); + pcache->size = 0; + } + + return 0; +} + +static int lfs2_bd_erase(lfs2_t *lfs2, lfs2_block_t block) { + LFS2_ASSERT(block < lfs2->cfg->block_count); + return lfs2->cfg->erase(lfs2->cfg, block); +} + + +/// Small type-level utilities /// +// operations on block pairs +static inline void lfs2_pair_swap(lfs2_block_t pair[2]) { + lfs2_block_t t = pair[0]; + pair[0] = pair[1]; + pair[1] = t; +} + +static inline bool lfs2_pair_isnull(const lfs2_block_t pair[2]) { + return pair[0] == 0xffffffff || pair[1] == 0xffffffff; +} + +static inline int lfs2_pair_cmp( + const lfs2_block_t paira[2], + const lfs2_block_t pairb[2]) { + return !(paira[0] == pairb[0] || paira[1] == pairb[1] || + paira[0] == pairb[1] || paira[1] == pairb[0]); +} + +static inline bool lfs2_pair_sync( + const lfs2_block_t paira[2], + const lfs2_block_t pairb[2]) { + return (paira[0] == pairb[0] && paira[1] == pairb[1]) || + (paira[0] == pairb[1] && paira[1] == pairb[0]); +} + +static inline void lfs2_pair_fromle32(lfs2_block_t pair[2]) { + pair[0] = lfs2_fromle32(pair[0]); + pair[1] = lfs2_fromle32(pair[1]); +} + +static inline void lfs2_pair_tole32(lfs2_block_t pair[2]) { + pair[0] = lfs2_tole32(pair[0]); + pair[1] = lfs2_tole32(pair[1]); +} + +// operations on 32-bit entry tags +typedef uint32_t lfs2_tag_t; +typedef int32_t lfs2_stag_t; + +#define LFS2_MKTAG(type, id, size) \ + (((lfs2_tag_t)(type) << 20) | ((lfs2_tag_t)(id) << 10) | (lfs2_tag_t)(size)) + +static inline bool lfs2_tag_isvalid(lfs2_tag_t tag) { + return !(tag & 0x80000000); +} + +static inline bool lfs2_tag_isdelete(lfs2_tag_t tag) { + return ((int32_t)(tag << 22) >> 22) == -1; +} + +static inline uint16_t lfs2_tag_type1(lfs2_tag_t tag) { + return (tag & 0x70000000) >> 20; +} + +static inline uint16_t lfs2_tag_type3(lfs2_tag_t tag) { + return (tag & 0x7ff00000) >> 20; +} + +static inline uint8_t lfs2_tag_chunk(lfs2_tag_t tag) { + return (tag & 0x0ff00000) >> 20; +} + +static inline int8_t lfs2_tag_splice(lfs2_tag_t tag) { + return (int8_t)lfs2_tag_chunk(tag); +} + +static inline uint16_t lfs2_tag_id(lfs2_tag_t tag) { + return (tag & 0x000ffc00) >> 10; +} + +static inline lfs2_size_t lfs2_tag_size(lfs2_tag_t tag) { + return tag & 0x000003ff; +} + +static inline lfs2_size_t lfs2_tag_dsize(lfs2_tag_t tag) { + return sizeof(tag) + lfs2_tag_size(tag + lfs2_tag_isdelete(tag)); +} + +// operations on attributes in attribute lists +struct lfs2_mattr { + lfs2_tag_t tag; + const void *buffer; +}; + +struct lfs2_diskoff { + lfs2_block_t block; + lfs2_off_t off; +}; + +#define LFS2_MKATTRS(...) \ + (struct lfs2_mattr[]){__VA_ARGS__}, \ + sizeof((struct lfs2_mattr[]){__VA_ARGS__}) / sizeof(struct lfs2_mattr) + +// operations on global state +static inline void lfs2_gstate_xor(struct lfs2_gstate *a, + const struct lfs2_gstate *b) { + for (int i = 0; i < 3; i++) { + ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; + } +} + +static inline bool lfs2_gstate_iszero(const struct lfs2_gstate *a) { + for (int i = 0; i < 3; i++) { + if (((uint32_t*)a)[i] != 0) { + return false; + } + } + return true; +} + +static inline bool lfs2_gstate_hasorphans(const struct lfs2_gstate *a) { + return lfs2_tag_size(a->tag); +} + +static inline uint8_t lfs2_gstate_getorphans(const struct lfs2_gstate *a) { + return lfs2_tag_size(a->tag); +} + +static inline bool lfs2_gstate_hasmove(const struct lfs2_gstate *a) { + return lfs2_tag_type1(a->tag); +} + +static inline bool lfs2_gstate_hasmovehere(const struct lfs2_gstate *a, + const lfs2_block_t *pair) { + return lfs2_tag_type1(a->tag) && lfs2_pair_cmp(a->pair, pair) == 0; +} + +static inline void lfs2_gstate_xororphans(struct lfs2_gstate *a, + const struct lfs2_gstate *b, bool orphans) { + a->tag ^= LFS2_MKTAG(0x800, 0, 0) & (b->tag ^ (orphans << 31)); +} + +static inline void lfs2_gstate_xormove(struct lfs2_gstate *a, + const struct lfs2_gstate *b, uint16_t id, const lfs2_block_t pair[2]) { + a->tag ^= LFS2_MKTAG(0x7ff, 0x3ff, 0) & (b->tag ^ ( + (id != 0x3ff) ? LFS2_MKTAG(LFS2_TYPE_DELETE, id, 0) : 0)); + a->pair[0] ^= b->pair[0] ^ ((id != 0x3ff) ? pair[0] : 0); + a->pair[1] ^= b->pair[1] ^ ((id != 0x3ff) ? pair[1] : 0); +} + +static inline void lfs2_gstate_fromle32(struct lfs2_gstate *a) { + a->tag = lfs2_fromle32(a->tag); + a->pair[0] = lfs2_fromle32(a->pair[0]); + a->pair[1] = lfs2_fromle32(a->pair[1]); +} + +static inline void lfs2_gstate_tole32(struct lfs2_gstate *a) { + a->tag = lfs2_tole32(a->tag); + a->pair[0] = lfs2_tole32(a->pair[0]); + a->pair[1] = lfs2_tole32(a->pair[1]); +} + +// other endianness operations +static void lfs2_ctz_fromle32(struct lfs2_ctz *ctz) { + ctz->head = lfs2_fromle32(ctz->head); + ctz->size = lfs2_fromle32(ctz->size); +} + +static void lfs2_ctz_tole32(struct lfs2_ctz *ctz) { + ctz->head = lfs2_tole32(ctz->head); + ctz->size = lfs2_tole32(ctz->size); +} + +static inline void lfs2_superblock_fromle32(lfs2_superblock_t *superblock) { + superblock->version = lfs2_fromle32(superblock->version); + superblock->block_size = lfs2_fromle32(superblock->block_size); + superblock->block_count = lfs2_fromle32(superblock->block_count); + superblock->name_max = lfs2_fromle32(superblock->name_max); + superblock->file_max = lfs2_fromle32(superblock->file_max); + superblock->attr_max = lfs2_fromle32(superblock->attr_max); +} + +static inline void lfs2_superblock_tole32(lfs2_superblock_t *superblock) { + superblock->version = lfs2_tole32(superblock->version); + superblock->block_size = lfs2_tole32(superblock->block_size); + superblock->block_count = lfs2_tole32(superblock->block_count); + superblock->name_max = lfs2_tole32(superblock->name_max); + superblock->file_max = lfs2_tole32(superblock->file_max); + superblock->attr_max = lfs2_tole32(superblock->attr_max); +} + + +/// Internal operations predeclared here /// +static int lfs2_dir_commit(lfs2_t *lfs2, lfs2_mdir_t *dir, + const struct lfs2_mattr *attrs, int attrcount); +static int lfs2_dir_compact(lfs2_t *lfs2, + lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, + lfs2_mdir_t *source, uint16_t begin, uint16_t end); +static int lfs2_file_relocate(lfs2_t *lfs2, lfs2_file_t *file); +static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file); +static void lfs2_fs_preporphans(lfs2_t *lfs2, int8_t orphans); +static void lfs2_fs_prepmove(lfs2_t *lfs2, + uint16_t id, const lfs2_block_t pair[2]); +static int lfs2_fs_pred(lfs2_t *lfs2, const lfs2_block_t dir[2], + lfs2_mdir_t *pdir); +static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t dir[2], + lfs2_mdir_t *parent); +static int lfs2_fs_relocate(lfs2_t *lfs2, + const lfs2_block_t oldpair[2], lfs2_block_t newpair[2]); +static int lfs2_fs_forceconsistency(lfs2_t *lfs2); +static int lfs2_deinit(lfs2_t *lfs2); +#ifdef LFS2_MIGRATE +static int lfs21_traverse(lfs2_t *lfs2, + int (*cb)(void*, lfs2_block_t), void *data); +#endif + +/// Block allocator /// +static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) { + lfs2_t *lfs2 = (lfs2_t*)p; + lfs2_block_t off = ((block - lfs2->free.off) + + lfs2->cfg->block_count) % lfs2->cfg->block_count; + + if (off < lfs2->free.size) { + lfs2->free.buffer[off / 32] |= 1U << (off % 32); + } + + return 0; +} + +static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { + while (true) { + while (lfs2->free.i != lfs2->free.size) { + lfs2_block_t off = lfs2->free.i; + lfs2->free.i += 1; + lfs2->free.ack -= 1; + + if (!(lfs2->free.buffer[off / 32] & (1U << (off % 32)))) { + // found a free block + *block = (lfs2->free.off + off) % lfs2->cfg->block_count; + + // eagerly find next off so an alloc ack can + // discredit old lookahead blocks + while (lfs2->free.i != lfs2->free.size && + (lfs2->free.buffer[lfs2->free.i / 32] + & (1U << (lfs2->free.i % 32)))) { + lfs2->free.i += 1; + lfs2->free.ack -= 1; + } + + return 0; + } + } + + // check if we have looked at all blocks since last ack + if (lfs2->free.ack == 0) { + LFS2_WARN("No more free space %"PRIu32, + lfs2->free.i + lfs2->free.off); + return LFS2_ERR_NOSPC; + } + + lfs2->free.off = (lfs2->free.off + lfs2->free.size) + % lfs2->cfg->block_count; + lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->free.ack); + lfs2->free.i = 0; + + // find mask of free blocks from tree + memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); + int err = lfs2_fs_traverse(lfs2, lfs2_alloc_lookahead, lfs2); + if (err) { + return err; + } + } +} + +static void lfs2_alloc_ack(lfs2_t *lfs2) { + lfs2->free.ack = lfs2->cfg->block_count; +} + + +/// Metadata pair and directory operations /// +static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, + lfs2_tag_t gmask, lfs2_tag_t gtag, + lfs2_off_t goff, void *gbuffer, lfs2_size_t gsize) { + lfs2_off_t off = dir->off; + lfs2_tag_t ntag = dir->etag; + lfs2_stag_t gdiff = 0; + + if (lfs2_gstate_hasmovehere(&lfs2->gstate, dir->pair) && + lfs2_tag_id(gtag) <= lfs2_tag_id(lfs2->gstate.tag)) { + // synthetic moves + gdiff -= LFS2_MKTAG(0, 1, 0); + } + + // iterate over dir block backwards (for faster lookups) + while (off >= sizeof(lfs2_tag_t) + lfs2_tag_dsize(ntag)) { + off -= lfs2_tag_dsize(ntag); + lfs2_tag_t tag = ntag; + int err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, sizeof(ntag), + dir->pair[0], off, &ntag, sizeof(ntag)); + if (err) { + return err; + } + + ntag = (lfs2_frombe32(ntag) ^ tag) & 0x7fffffff; + + if (lfs2_tag_id(gmask) != 0 && + lfs2_tag_type1(tag) == LFS2_TYPE_SPLICE && + lfs2_tag_id(tag) <= lfs2_tag_id(gtag - gdiff)) { + if (tag == (LFS2_MKTAG(LFS2_TYPE_CREATE, 0, 0) | + (LFS2_MKTAG(0, 0x3ff, 0) & (gtag - gdiff)))) { + // found where we were created + return LFS2_ERR_NOENT; + } + + // move around splices + gdiff += LFS2_MKTAG(0, lfs2_tag_splice(tag), 0); + } + + if ((gmask & tag) == (gmask & (gtag - gdiff))) { + if (lfs2_tag_isdelete(tag)) { + return LFS2_ERR_NOENT; + } + + lfs2_size_t diff = lfs2_min(lfs2_tag_size(tag), gsize); + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, diff, + dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff); + if (err) { + return err; + } + + memset((uint8_t*)gbuffer + diff, 0, gsize - diff); + + return tag + gdiff; + } + } + + return LFS2_ERR_NOENT; +} + +static lfs2_stag_t lfs2_dir_get(lfs2_t *lfs2, const lfs2_mdir_t *dir, + lfs2_tag_t gmask, lfs2_tag_t gtag, void *buffer) { + return lfs2_dir_getslice(lfs2, dir, + gmask, gtag, + 0, buffer, lfs2_tag_size(gtag)); +} + +static int lfs2_dir_getread(lfs2_t *lfs2, const lfs2_mdir_t *dir, + const lfs2_cache_t *pcache, lfs2_cache_t *rcache, lfs2_size_t hint, + lfs2_tag_t gmask, lfs2_tag_t gtag, + lfs2_off_t off, void *buffer, lfs2_size_t size) { + uint8_t *data = buffer; + if (off+size > lfs2->cfg->block_size) { + return LFS2_ERR_CORRUPT; + } + + while (size > 0) { + lfs2_size_t diff = size; + + if (pcache && pcache->block == 0xfffffffe && + off < pcache->off + pcache->size) { + if (off >= pcache->off) { + // is already in pcache? + diff = lfs2_min(diff, pcache->size - (off-pcache->off)); + memcpy(data, &pcache->buffer[off-pcache->off], diff); + + data += diff; + off += diff; + size -= diff; + continue; + } + + // pcache takes priority + diff = lfs2_min(diff, pcache->off-off); + } + + if (rcache->block == 0xfffffffe && + off < rcache->off + rcache->size) { + if (off >= rcache->off) { + // is already in rcache? + diff = lfs2_min(diff, rcache->size - (off-rcache->off)); + memcpy(data, &rcache->buffer[off-rcache->off], diff); + + data += diff; + off += diff; + size -= diff; + continue; + } + + // rcache takes priority + diff = lfs2_min(diff, rcache->off-off); + } + + // load to cache, first condition can no longer fail + rcache->block = 0xfffffffe; + rcache->off = lfs2_aligndown(off, lfs2->cfg->read_size); + rcache->size = lfs2_min(lfs2_alignup(off+hint, lfs2->cfg->read_size), + lfs2->cfg->cache_size); + int err = lfs2_dir_getslice(lfs2, dir, gmask, gtag, + rcache->off, rcache->buffer, rcache->size); + if (err) { + return err; + } + } + + return 0; +} + +static int lfs2_dir_traverse_filter(void *p, + lfs2_tag_t tag, const void *buffer) { + lfs2_tag_t *filtertag = p; + (void)buffer; + + // check for redundancy + uint32_t mask = LFS2_MKTAG(0x7ff, 0x3ff, 0); + if ((mask & tag) == (mask & *filtertag) || + (mask & tag) == (LFS2_MKTAG(LFS2_TYPE_DELETE, 0, 0) | + (LFS2_MKTAG(0, 0x3ff, 0) & *filtertag))) { + return true; + } + + // check if we need to adjust for created/deleted tags + if (lfs2_tag_type1(tag) == LFS2_TYPE_SPLICE && + lfs2_tag_id(tag) <= lfs2_tag_id(*filtertag)) { + *filtertag += LFS2_MKTAG(0, lfs2_tag_splice(tag), 0); + } + + return false; +} + +static int lfs2_dir_traverse(lfs2_t *lfs2, + const lfs2_mdir_t *dir, lfs2_off_t off, lfs2_tag_t ptag, + const struct lfs2_mattr *attrs, int attrcount, bool hasseenmove, + lfs2_tag_t tmask, lfs2_tag_t ttag, + uint16_t begin, uint16_t end, int16_t diff, + int (*cb)(void *data, lfs2_tag_t tag, const void *buffer), void *data) { + // iterate over directory and attrs + while (true) { + lfs2_tag_t tag; + const void *buffer; + struct lfs2_diskoff disk; + if (off+lfs2_tag_dsize(ptag) < dir->off) { + off += lfs2_tag_dsize(ptag); + int err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, sizeof(tag), + dir->pair[0], off, &tag, sizeof(tag)); + if (err) { + return err; + } + + tag = (lfs2_frombe32(tag) ^ ptag) | 0x80000000; + disk.block = dir->pair[0]; + disk.off = off+sizeof(lfs2_tag_t); + buffer = &disk; + ptag = tag; + } else if (attrcount > 0) { + tag = attrs[0].tag; + buffer = attrs[0].buffer; + attrs += 1; + attrcount -= 1; + } else if (!hasseenmove && + lfs2_gstate_hasmovehere(&lfs2->gpending, dir->pair)) { + // Wait, we have pending move? Handle this here (we need to + // or else we risk letting moves fall out of date) + tag = lfs2->gpending.tag & LFS2_MKTAG(0x7ff, 0x3ff, 0); + buffer = NULL; + hasseenmove = true; + } else { + return 0; + } + + lfs2_tag_t mask = LFS2_MKTAG(0x7ff, 0, 0); + if ((mask & tmask & tag) != (mask & tmask & ttag)) { + continue; + } + + // do we need to filter? inlining the filtering logic here allows + // for some minor optimizations + if (lfs2_tag_id(tmask) != 0) { + // scan for duplicates and update tag based on creates/deletes + int filter = lfs2_dir_traverse(lfs2, + dir, off, ptag, attrs, attrcount, hasseenmove, + 0, 0, 0, 0, 0, + lfs2_dir_traverse_filter, &tag); + if (filter < 0) { + return filter; + } + + if (filter) { + continue; + } + + // in filter range? + if (!(lfs2_tag_id(tag) >= begin && lfs2_tag_id(tag) < end)) { + continue; + } + } + + // handle special cases for mcu-side operations + if (lfs2_tag_type3(tag) == LFS2_FROM_NOOP) { + // do nothing + } else if (lfs2_tag_type3(tag) == LFS2_FROM_MOVE) { + uint16_t fromid = lfs2_tag_size(tag); + uint16_t toid = lfs2_tag_id(tag); + int err = lfs2_dir_traverse(lfs2, + buffer, 0, 0xffffffff, NULL, 0, true, + LFS2_MKTAG(0x600, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, 0, 0), + fromid, fromid+1, toid-fromid+diff, + cb, data); + if (err) { + return err; + } + } else if (lfs2_tag_type3(tag) == LFS2_FROM_USERATTRS) { + for (unsigned i = 0; i < lfs2_tag_size(tag); i++) { + const struct lfs2_attr *a = buffer; + int err = cb(data, LFS2_MKTAG(LFS2_TYPE_USERATTR + a[i].type, + lfs2_tag_id(tag) + diff, a[i].size), a[i].buffer); + if (err) { + return err; + } + } + } else { + int err = cb(data, tag + LFS2_MKTAG(0, diff, 0), buffer); + if (err) { + return err; + } + } + } +} + +static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, + lfs2_mdir_t *dir, const lfs2_block_t pair[2], + lfs2_tag_t fmask, lfs2_tag_t ftag, uint16_t *id, + int (*cb)(void *data, lfs2_tag_t tag, const void *buffer), void *data) { + // we can find tag very efficiently during a fetch, since we're already + // scanning the entire directory + lfs2_stag_t besttag = -1; + + // find the block with the most recent revision + uint32_t revs[2] = {0, 0}; + int r = 0; + for (int i = 0; i < 2; i++) { + int err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, sizeof(revs[i]), + pair[i], 0, &revs[i], sizeof(revs[i])); + revs[i] = lfs2_fromle32(revs[i]); + if (err && err != LFS2_ERR_CORRUPT) { + return err; + } + + if (err != LFS2_ERR_CORRUPT && + lfs2_scmp(revs[i], revs[(i+1)%2]) > 0) { + r = i; + } + } + + dir->pair[0] = pair[(r+0)%2]; + dir->pair[1] = pair[(r+1)%2]; + dir->rev = revs[(r+0)%2]; + dir->off = 0; // nonzero = found some commits + + // now scan tags to fetch the actual dir and find possible match + for (int i = 0; i < 2; i++) { + lfs2_off_t off = 0; + lfs2_tag_t ptag = 0xffffffff; + + uint16_t tempcount = 0; + lfs2_block_t temptail[2] = {0xffffffff, 0xffffffff}; + bool tempsplit = false; + lfs2_stag_t tempbesttag = besttag; + + dir->rev = lfs2_tole32(dir->rev); + uint32_t crc = lfs2_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); + dir->rev = lfs2_fromle32(dir->rev); + + while (true) { + // extract next tag + lfs2_tag_t tag; + off += lfs2_tag_dsize(ptag); + int err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->block_size, + dir->pair[0], off, &tag, sizeof(tag)); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + // can't continue? + dir->erased = false; + break; + } + return err; + } + + crc = lfs2_crc(crc, &tag, sizeof(tag)); + tag = lfs2_frombe32(tag) ^ ptag; + + // next commit not yet programmed or we're not in valid range + if (!lfs2_tag_isvalid(tag) || + off + lfs2_tag_dsize(tag) > lfs2->cfg->block_size) { + dir->erased = (lfs2_tag_type1(ptag) == LFS2_TYPE_CRC && + dir->off % lfs2->cfg->prog_size == 0); + break; + } + + ptag = tag; + + if (lfs2_tag_type1(tag) == LFS2_TYPE_CRC) { + // check the crc attr + uint32_t dcrc; + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->block_size, + dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + dir->erased = false; + break; + } + return err; + } + dcrc = lfs2_fromle32(dcrc); + + if (crc != dcrc) { + dir->erased = false; + break; + } + + // reset the next bit if we need to + ptag ^= (lfs2_tag_chunk(tag) & 1U) << 31; + + // toss our crc into the filesystem seed for + // pseudorandom numbers + lfs2->seed ^= crc; + + // update with what's found so far + besttag = tempbesttag; + dir->off = off + lfs2_tag_dsize(tag); + dir->etag = ptag; + dir->count = tempcount; + dir->tail[0] = temptail[0]; + dir->tail[1] = temptail[1]; + dir->split = tempsplit; + + // reset crc + crc = 0xffffffff; + continue; + } + + // crc the entry first, hopefully leaving it in the cache + for (lfs2_off_t j = sizeof(tag); j < lfs2_tag_dsize(tag); j++) { + uint8_t dat; + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->block_size, + dir->pair[0], off+j, &dat, 1); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + dir->erased = false; + break; + } + return err; + } + + crc = lfs2_crc(crc, &dat, 1); + } + + // directory modification tags? + if (lfs2_tag_type1(tag) == LFS2_TYPE_NAME) { + // increase count of files if necessary + if (lfs2_tag_id(tag) >= tempcount) { + tempcount = lfs2_tag_id(tag) + 1; + } + } else if (lfs2_tag_type1(tag) == LFS2_TYPE_SPLICE) { + tempcount += lfs2_tag_splice(tag); + + if (tag == (LFS2_MKTAG(LFS2_TYPE_DELETE, 0, 0) | + (LFS2_MKTAG(0, 0x3ff, 0) & tempbesttag))) { + tempbesttag |= 0x80000000; + } else if (tempbesttag != -1 && + lfs2_tag_id(tag) <= lfs2_tag_id(tempbesttag)) { + tempbesttag += LFS2_MKTAG(0, lfs2_tag_splice(tag), 0); + } + } else if (lfs2_tag_type1(tag) == LFS2_TYPE_TAIL) { + tempsplit = (lfs2_tag_chunk(tag) & 1); + + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->block_size, + dir->pair[0], off+sizeof(tag), &temptail, 8); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + dir->erased = false; + break; + } + } + lfs2_pair_fromle32(temptail); + } + + // found a match for our fetcher? + if ((fmask & tag) == (fmask & ftag)) { + int res = cb(data, tag, &(struct lfs2_diskoff){ + dir->pair[0], off+sizeof(tag)}); + if (res < 0) { + if (res == LFS2_ERR_CORRUPT) { + dir->erased = false; + break; + } + return res; + } + + if (res == LFS2_CMP_EQ) { + // found a match + tempbesttag = tag; + } else if (res == LFS2_CMP_GT && + lfs2_tag_id(tag) <= lfs2_tag_id(tempbesttag)) { + // found a greater match, keep track to keep things sorted + tempbesttag = tag | 0x80000000; + } + } + } + + // consider what we have good enough + if (dir->off > 0) { + // synthetic move + if (lfs2_gstate_hasmovehere(&lfs2->gstate, dir->pair)) { + if (lfs2_tag_id(lfs2->gstate.tag) == lfs2_tag_id(besttag)) { + besttag |= 0x80000000; + } else if (besttag != -1 && + lfs2_tag_id(lfs2->gstate.tag) < lfs2_tag_id(besttag)) { + besttag -= LFS2_MKTAG(0, 1, 0); + } + } + + // found tag? or found best id? + if (id) { + *id = lfs2_min(lfs2_tag_id(besttag), dir->count); + } + + if (lfs2_tag_isvalid(besttag)) { + return besttag; + } else if (lfs2_tag_id(besttag) < dir->count) { + return LFS2_ERR_NOENT; + } else { + return 0; + } + } + + // failed, try the other block? + lfs2_pair_swap(dir->pair); + dir->rev = revs[(r+1)%2]; + } + + LFS2_ERROR("Corrupted dir pair at %"PRIu32" %"PRIu32, + dir->pair[0], dir->pair[1]); + return LFS2_ERR_CORRUPT; +} + +static int lfs2_dir_fetch(lfs2_t *lfs2, + lfs2_mdir_t *dir, const lfs2_block_t pair[2]) { + // note, mask=-1, tag=0 can never match a tag since this + // pattern has the invalid bit set + return lfs2_dir_fetchmatch(lfs2, dir, pair, -1, 0, NULL, NULL, NULL); +} + +static int lfs2_dir_getgstate(lfs2_t *lfs2, const lfs2_mdir_t *dir, + struct lfs2_gstate *gstate) { + struct lfs2_gstate temp; + lfs2_stag_t res = lfs2_dir_get(lfs2, dir, LFS2_MKTAG(0x7ff, 0, 0), + LFS2_MKTAG(LFS2_TYPE_MOVESTATE, 0, sizeof(temp)), &temp); + if (res < 0 && res != LFS2_ERR_NOENT) { + return res; + } + + if (res != LFS2_ERR_NOENT) { + // xor together to find resulting gstate + lfs2_gstate_fromle32(&temp); + lfs2_gstate_xor(gstate, &temp); + } + + return 0; +} + +static int lfs2_dir_getinfo(lfs2_t *lfs2, lfs2_mdir_t *dir, + uint16_t id, struct lfs2_info *info) { + if (id == 0x3ff) { + // special case for root + strcpy(info->name, "/"); + info->type = LFS2_TYPE_DIR; + return 0; + } + + lfs2_stag_t tag = lfs2_dir_get(lfs2, dir, LFS2_MKTAG(0x780, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_NAME, id, lfs2->name_max+1), info->name); + if (tag < 0) { + return tag; + } + + info->type = lfs2_tag_type3(tag); + + struct lfs2_ctz ctz; + tag = lfs2_dir_get(lfs2, dir, LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, id, sizeof(ctz)), &ctz); + if (tag < 0) { + return tag; + } + lfs2_ctz_fromle32(&ctz); + + if (lfs2_tag_type3(tag) == LFS2_TYPE_CTZSTRUCT) { + info->size = ctz.size; + } else if (lfs2_tag_type3(tag) == LFS2_TYPE_INLINESTRUCT) { + info->size = lfs2_tag_size(tag); + } + + return 0; +} + +struct lfs2_dir_find_match { + lfs2_t *lfs2; + const void *name; + lfs2_size_t size; +}; + +static int lfs2_dir_find_match(void *data, + lfs2_tag_t tag, const void *buffer) { + struct lfs2_dir_find_match *name = data; + lfs2_t *lfs2 = name->lfs2; + const struct lfs2_diskoff *disk = buffer; + + // compare with disk + lfs2_size_t diff = lfs2_min(name->size, lfs2_tag_size(tag)); + int res = lfs2_bd_cmp(lfs2, + NULL, &lfs2->rcache, diff, + disk->block, disk->off, name->name, diff); + if (res != LFS2_CMP_EQ) { + return res; + } + + // only equal if our size is still the same + if (name->size != lfs2_tag_size(tag)) { + return (name->size < lfs2_tag_size(tag)) ? LFS2_CMP_LT : LFS2_CMP_GT; + } + + // found a match! + return LFS2_CMP_EQ; +} + +static int lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, + const char **path, uint16_t *id) { + // we reduce path to a single name if we can find it + const char *name = *path; + if (id) { + *id = 0x3ff; + } + + // default to root dir + lfs2_stag_t tag = LFS2_MKTAG(LFS2_TYPE_DIR, 0x3ff, 0); + dir->tail[0] = lfs2->root[0]; + dir->tail[1] = lfs2->root[1]; + + while (true) { +nextname: + // skip slashes + name += strspn(name, "/"); + lfs2_size_t namelen = strcspn(name, "/"); + + // skip '.' and root '..' + if ((namelen == 1 && memcmp(name, ".", 1) == 0) || + (namelen == 2 && memcmp(name, "..", 2) == 0)) { + name += namelen; + goto nextname; + } + + // skip if matched by '..' in name + const char *suffix = name + namelen; + lfs2_size_t sufflen; + int depth = 1; + while (true) { + suffix += strspn(suffix, "/"); + sufflen = strcspn(suffix, "/"); + if (sufflen == 0) { + break; + } + + if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { + depth -= 1; + if (depth == 0) { + name = suffix + sufflen; + goto nextname; + } + } else { + depth += 1; + } + + suffix += sufflen; + } + + // found path + if (name[0] == '\0') { + return tag; + } + + // update what we've found so far + *path = name; + + // only continue if we hit a directory + if (lfs2_tag_type3(tag) != LFS2_TYPE_DIR) { + return LFS2_ERR_NOTDIR; + } + + // grab the entry data + if (lfs2_tag_id(tag) != 0x3ff) { + lfs2_stag_t res = lfs2_dir_get(lfs2, dir, LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, lfs2_tag_id(tag), 8), dir->tail); + if (res < 0) { + return res; + } + lfs2_pair_fromle32(dir->tail); + } + + // find entry matching name + while (true) { + tag = lfs2_dir_fetchmatch(lfs2, dir, dir->tail, + LFS2_MKTAG(0x780, 0, 0), + LFS2_MKTAG(LFS2_TYPE_NAME, 0, namelen), + // are we last name? + (strchr(name, '/') == NULL) ? id : NULL, + lfs2_dir_find_match, &(struct lfs2_dir_find_match){ + lfs2, name, namelen}); + if (tag < 0) { + return tag; + } + + if (tag) { + break; + } + + if (!dir->split) { + return LFS2_ERR_NOENT; + } + } + + // to next name + name += namelen; + } +} + +// commit logic +struct lfs2_commit { + lfs2_block_t block; + lfs2_off_t off; + lfs2_tag_t ptag; + uint32_t crc; + + lfs2_off_t begin; + lfs2_off_t end; +}; + +static int lfs2_dir_commitprog(lfs2_t *lfs2, struct lfs2_commit *commit, + const void *buffer, lfs2_size_t size) { + int err = lfs2_bd_prog(lfs2, + &lfs2->pcache, &lfs2->rcache, false, + commit->block, commit->off , + (const uint8_t*)buffer, size); + if (err) { + return err; + } + + commit->crc = lfs2_crc(commit->crc, buffer, size); + commit->off += size; + return 0; +} + +static int lfs2_dir_commitattr(lfs2_t *lfs2, struct lfs2_commit *commit, + lfs2_tag_t tag, const void *buffer) { + // check if we fit + lfs2_size_t dsize = lfs2_tag_dsize(tag); + if (commit->off + dsize > commit->end) { + return LFS2_ERR_NOSPC; + } + + // write out tag + lfs2_tag_t ntag = lfs2_tobe32((tag & 0x7fffffff) ^ commit->ptag); + int err = lfs2_dir_commitprog(lfs2, commit, &ntag, sizeof(ntag)); + if (err) { + return err; + } + + if (!(tag & 0x80000000)) { + // from memory + err = lfs2_dir_commitprog(lfs2, commit, buffer, dsize-sizeof(tag)); + if (err) { + return err; + } + } else { + // from disk + const struct lfs2_diskoff *disk = buffer; + for (lfs2_off_t i = 0; i < dsize-sizeof(tag); i++) { + // rely on caching to make this efficient + uint8_t dat; + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, dsize-sizeof(tag)-i, + disk->block, disk->off+i, &dat, 1); + if (err) { + return err; + } + + err = lfs2_dir_commitprog(lfs2, commit, &dat, 1); + if (err) { + return err; + } + } + } + + commit->ptag = tag & 0x7fffffff; + return 0; +} + +static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) { + // align to program units + lfs2_off_t off = lfs2_alignup(commit->off + 2*sizeof(uint32_t), + lfs2->cfg->prog_size); + + // read erased state from next program unit + lfs2_tag_t tag; + int err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, sizeof(tag), + commit->block, off, &tag, sizeof(tag)); + if (err && err != LFS2_ERR_CORRUPT) { + return err; + } + + // build crc tag + bool reset = ~lfs2_frombe32(tag) >> 31; + tag = LFS2_MKTAG(LFS2_TYPE_CRC + reset, 0x3ff, + off - (commit->off+sizeof(lfs2_tag_t))); + + // write out crc + uint32_t footer[2]; + footer[0] = lfs2_tobe32(tag ^ commit->ptag); + commit->crc = lfs2_crc(commit->crc, &footer[0], sizeof(footer[0])); + footer[1] = lfs2_tole32(commit->crc); + err = lfs2_bd_prog(lfs2, + &lfs2->pcache, &lfs2->rcache, false, + commit->block, commit->off, &footer, sizeof(footer)); + if (err) { + return err; + } + commit->off += sizeof(tag)+lfs2_tag_size(tag); + commit->ptag = tag ^ (reset << 31); + + // flush buffers + err = lfs2_bd_sync(lfs2, &lfs2->pcache, &lfs2->rcache, false); + if (err) { + return err; + } + + // successful commit, check checksum to make sure + uint32_t crc = 0xffffffff; + lfs2_size_t size = commit->off - lfs2_tag_size(tag) - commit->begin; + for (lfs2_off_t i = 0; i < size; i++) { + // leave it up to caching to make this efficient + uint8_t dat; + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, size-i, + commit->block, commit->begin+i, &dat, 1); + if (err) { + return err; + } + + crc = lfs2_crc(crc, &dat, 1); + } + + if (err) { + return err; + } + + if (crc != commit->crc) { + return LFS2_ERR_CORRUPT; + } + + return 0; +} + +static int lfs2_dir_alloc(lfs2_t *lfs2, lfs2_mdir_t *dir) { + // allocate pair of dir blocks (backwards, so we write block 1 first) + for (int i = 0; i < 2; i++) { + int err = lfs2_alloc(lfs2, &dir->pair[(i+1)%2]); + if (err) { + return err; + } + } + + // rather than clobbering one of the blocks we just pretend + // the revision may be valid + int err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, sizeof(dir->rev), + dir->pair[0], 0, &dir->rev, sizeof(dir->rev)); + dir->rev = lfs2_fromle32(dir->rev); + if (err && err != LFS2_ERR_CORRUPT) { + return err; + } + + // make sure we don't immediately evict + dir->rev += dir->rev & 1; + + // set defaults + dir->off = sizeof(dir->rev); + dir->etag = 0xffffffff; + dir->count = 0; + dir->tail[0] = 0xffffffff; + dir->tail[1] = 0xffffffff; + dir->erased = false; + dir->split = false; + + // don't write out yet, let caller take care of that + return 0; +} + +static int lfs2_dir_drop(lfs2_t *lfs2, lfs2_mdir_t *dir, lfs2_mdir_t *tail) { + // steal state + int err = lfs2_dir_getgstate(lfs2, tail, &lfs2->gdelta); + if (err) { + return err; + } + + // steal tail + lfs2_pair_tole32(tail->tail); + err = lfs2_dir_commit(lfs2, dir, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); + lfs2_pair_fromle32(tail->tail); + if (err) { + return err; + } + + return 0; +} + +static int lfs2_dir_split(lfs2_t *lfs2, + lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, + lfs2_mdir_t *source, uint16_t split, uint16_t end) { + // create tail directory + lfs2_mdir_t tail; + int err = lfs2_dir_alloc(lfs2, &tail); + if (err) { + return err; + } + + tail.split = dir->split; + tail.tail[0] = dir->tail[0]; + tail.tail[1] = dir->tail[1]; + + err = lfs2_dir_compact(lfs2, &tail, attrs, attrcount, source, split, end); + if (err) { + return err; + } + + dir->tail[0] = tail.pair[0]; + dir->tail[1] = tail.pair[1]; + dir->split = true; + + // update root if needed + if (lfs2_pair_cmp(dir->pair, lfs2->root) == 0 && split == 0) { + lfs2->root[0] = tail.pair[0]; + lfs2->root[1] = tail.pair[1]; + } + + return 0; +} + +static int lfs2_dir_commit_size(void *p, lfs2_tag_t tag, const void *buffer) { + lfs2_size_t *size = p; + (void)buffer; + + *size += lfs2_tag_dsize(tag); + return 0; +} + +struct lfs2_dir_commit_commit { + lfs2_t *lfs2; + struct lfs2_commit *commit; +}; + +static int lfs2_dir_commit_commit(void *p, lfs2_tag_t tag, const void *buffer) { + struct lfs2_dir_commit_commit *commit = p; + return lfs2_dir_commitattr(commit->lfs2, commit->commit, tag, buffer); +} + +static int lfs2_dir_compact(lfs2_t *lfs2, + lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, + lfs2_mdir_t *source, uint16_t begin, uint16_t end) { + // save some state in case block is bad + const lfs2_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; + bool relocated = false; + bool exhausted = false; + + // should we split? + while (end - begin > 1) { + // find size + lfs2_size_t size = 0; + int err = lfs2_dir_traverse(lfs2, + source, 0, 0xffffffff, attrs, attrcount, false, + LFS2_MKTAG(0x400, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_NAME, 0, 0), + begin, end, -begin, + lfs2_dir_commit_size, &size); + if (err) { + return err; + } + + // space is complicated, we need room for tail, crc, gstate, + // cleanup delete, and we cap at half a block to give room + // for metadata updates. + if (end - begin < 0xff && + size <= lfs2_min(lfs2->cfg->block_size - 36, + lfs2_alignup(lfs2->cfg->block_size/2, + lfs2->cfg->prog_size))) { + break; + } + + // can't fit, need to split, we should really be finding the + // largest size that fits with a small binary search, but right now + // it's not worth the code size + uint16_t split = (end - begin) / 2; + err = lfs2_dir_split(lfs2, dir, attrs, attrcount, + source, begin+split, end); + if (err) { + // if we fail to split, we may be able to overcompact, unless + // we're too big for even the full block, in which case our + // only option is to error + if (err == LFS2_ERR_NOSPC && size <= lfs2->cfg->block_size - 36) { + break; + } + return err; + } + + end = begin + split; + } + + // increment revision count + dir->rev += 1; + if (lfs2->cfg->block_cycles && + (dir->rev % (lfs2->cfg->block_cycles+1) == 0)) { + if (lfs2_pair_cmp(dir->pair, (const lfs2_block_t[2]){0, 1}) == 0) { + // oh no! we're writing too much to the superblock, + // should we expand? + lfs2_ssize_t res = lfs2_fs_size(lfs2); + if (res < 0) { + return res; + } + + // do we have extra space? littlefs can't reclaim this space + // by itself, so expand cautiously + if ((lfs2_size_t)res < lfs2->cfg->block_count/2) { + LFS2_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); + int err = lfs2_dir_split(lfs2, dir, attrs, attrcount, + source, begin, end); + if (err && err != LFS2_ERR_NOSPC) { + return err; + } + + // welp, we tried, if we ran out of space there's not much + // we can do, we'll error later if we've become frozen + if (!err) { + end = begin; + } + } + } else { + // we're writing too much, time to relocate + exhausted = true; + goto relocate; + } + } + + // begin loop to commit compaction to blocks until a compact sticks + while (true) { + { + // There's nothing special about our global delta, so feed it into + // our local global delta + int err = lfs2_dir_getgstate(lfs2, dir, &lfs2->gdelta); + if (err) { + return err; + } + + // setup commit state + struct lfs2_commit commit = { + .block = dir->pair[1], + .off = 0, + .ptag = 0xffffffff, + .crc = 0xffffffff, + + .begin = 0, + .end = lfs2->cfg->block_size - 8, + }; + + // erase block to write to + err = lfs2_bd_erase(lfs2, dir->pair[1]); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + // write out header + dir->rev = lfs2_tole32(dir->rev); + err = lfs2_dir_commitprog(lfs2, &commit, + &dir->rev, sizeof(dir->rev)); + dir->rev = lfs2_fromle32(dir->rev); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + // traverse the directory, this time writing out all unique tags + err = lfs2_dir_traverse(lfs2, + source, 0, 0xffffffff, attrs, attrcount, false, + LFS2_MKTAG(0x400, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_NAME, 0, 0), + begin, end, -begin, + lfs2_dir_commit_commit, &(struct lfs2_dir_commit_commit){ + lfs2, &commit}); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + // commit tail, which may be new after last size check + if (!lfs2_pair_isnull(dir->tail)) { + lfs2_pair_tole32(dir->tail); + err = lfs2_dir_commitattr(lfs2, &commit, + LFS2_MKTAG(LFS2_TYPE_TAIL + dir->split, 0x3ff, 8), + dir->tail); + lfs2_pair_fromle32(dir->tail); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + } + + if (!relocated && !lfs2_gstate_iszero(&lfs2->gdelta)) { + // commit any globals, unless we're relocating, + // in which case our parent will steal our globals + lfs2_gstate_tole32(&lfs2->gdelta); + err = lfs2_dir_commitattr(lfs2, &commit, + LFS2_MKTAG(LFS2_TYPE_MOVESTATE, 0x3ff, + sizeof(lfs2->gdelta)), &lfs2->gdelta); + lfs2_gstate_fromle32(&lfs2->gdelta); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + } + + err = lfs2_dir_commitcrc(lfs2, &commit); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + // successful compaction, swap dir pair to indicate most recent + lfs2_pair_swap(dir->pair); + dir->count = end - begin; + dir->off = commit.off; + dir->etag = commit.ptag; + dir->erased = (dir->off % lfs2->cfg->prog_size == 0); + // note we able to have already handled move here + if (lfs2_gstate_hasmovehere(&lfs2->gpending, dir->pair)) { + lfs2_gstate_xormove(&lfs2->gpending, + &lfs2->gpending, 0x3ff, NULL); + } + } + break; + +relocate: + // commit was corrupted, drop caches and prepare to relocate block + relocated = true; + lfs2_cache_drop(lfs2, &lfs2->pcache); + if (!exhausted) { + LFS2_DEBUG("Bad block at %"PRIu32, dir->pair[1]); + } + + // can't relocate superblock, filesystem is now frozen + if (lfs2_pair_cmp(oldpair, (const lfs2_block_t[2]){0, 1}) == 0) { + LFS2_WARN("Superblock %"PRIu32" has become unwritable", oldpair[1]); + return LFS2_ERR_NOSPC; + } + + // relocate half of pair + int err = lfs2_alloc(lfs2, &dir->pair[1]); + if (err && (err != LFS2_ERR_NOSPC && !exhausted)) { + return err; + } + + continue; + } + + if (!relocated) { + lfs2->gstate = lfs2->gpending; + lfs2->gdelta = (struct lfs2_gstate){0}; + } else { + // update references if we relocated + LFS2_DEBUG("Relocating %"PRIu32" %"PRIu32" to %"PRIu32" %"PRIu32, + oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); + int err = lfs2_fs_relocate(lfs2, oldpair, dir->pair); + if (err) { + return err; + } + } + + return 0; +} + +static int lfs2_dir_commit(lfs2_t *lfs2, lfs2_mdir_t *dir, + const struct lfs2_mattr *attrs, int attrcount) { + // check for any inline files that aren't RAM backed and + // forcefully evict them, needed for filesystem consistency + for (lfs2_file_t *f = (lfs2_file_t*)lfs2->mlist; f; f = f->next) { + if (dir != &f->m && lfs2_pair_cmp(f->m.pair, dir->pair) == 0 && + f->type == LFS2_TYPE_REG && (f->flags & LFS2_F_INLINE) && + f->ctz.size > lfs2->cfg->cache_size) { + f->flags &= ~LFS2_F_READING; + f->off = 0; + + lfs2_alloc_ack(lfs2); + int err = lfs2_file_relocate(lfs2, f); + if (err) { + return err; + } + + err = lfs2_file_flush(lfs2, f); + if (err) { + return err; + } + } + } + + // calculate changes to the directory + lfs2_tag_t deletetag = 0xffffffff; + lfs2_tag_t createtag = 0xffffffff; + for (int i = 0; i < attrcount; i++) { + if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_CREATE) { + createtag = attrs[i].tag; + dir->count += 1; + } else if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_DELETE) { + deletetag = attrs[i].tag; + LFS2_ASSERT(dir->count > 0); + dir->count -= 1; + } else if (lfs2_tag_type1(attrs[i].tag) == LFS2_TYPE_TAIL) { + dir->tail[0] = ((lfs2_block_t*)attrs[i].buffer)[0]; + dir->tail[1] = ((lfs2_block_t*)attrs[i].buffer)[1]; + dir->split = (lfs2_tag_chunk(attrs[i].tag) & 1); + lfs2_pair_fromle32(dir->tail); + } + } + + // do we have a pending move? + if (lfs2_gstate_hasmovehere(&lfs2->gpending, dir->pair)) { + deletetag = lfs2->gpending.tag & LFS2_MKTAG(0x7ff, 0x3ff, 0); + LFS2_ASSERT(dir->count > 0); + dir->count -= 1; + + // mark gdelta so we reflect the move we will fix + lfs2_gstate_xormove(&lfs2->gdelta, &lfs2->gpending, 0x3ff, NULL); + } + + // should we actually drop the directory block? + if (lfs2_tag_isvalid(deletetag) && dir->count == 0) { + lfs2_mdir_t pdir; + int err = lfs2_fs_pred(lfs2, dir->pair, &pdir); + if (err && err != LFS2_ERR_NOENT) { + return err; + } + + if (err != LFS2_ERR_NOENT && pdir.split) { + return lfs2_dir_drop(lfs2, &pdir, dir); + } + } + + if (dir->erased || dir->count >= 0xff) { + // try to commit + struct lfs2_commit commit = { + .block = dir->pair[0], + .off = dir->off, + .ptag = dir->etag, + .crc = 0xffffffff, + + .begin = dir->off, + .end = lfs2->cfg->block_size - 8, + }; + + // traverse attrs that need to be written out + lfs2_pair_tole32(dir->tail); + int err = lfs2_dir_traverse(lfs2, + dir, dir->off, dir->etag, attrs, attrcount, false, + 0, 0, 0, 0, 0, + lfs2_dir_commit_commit, &(struct lfs2_dir_commit_commit){ + lfs2, &commit}); + lfs2_pair_fromle32(dir->tail); + if (err) { + if (err == LFS2_ERR_NOSPC || err == LFS2_ERR_CORRUPT) { + goto compact; + } + return err; + } + + // commit any global diffs if we have any + if (!lfs2_gstate_iszero(&lfs2->gdelta)) { + err = lfs2_dir_getgstate(lfs2, dir, &lfs2->gdelta); + if (err) { + return err; + } + + lfs2_gstate_tole32(&lfs2->gdelta); + err = lfs2_dir_commitattr(lfs2, &commit, + LFS2_MKTAG(LFS2_TYPE_MOVESTATE, 0x3ff, + sizeof(lfs2->gdelta)), &lfs2->gdelta); + lfs2_gstate_fromle32(&lfs2->gdelta); + if (err) { + if (err == LFS2_ERR_NOSPC || err == LFS2_ERR_CORRUPT) { + goto compact; + } + return err; + } + } + + // finalize commit with the crc + err = lfs2_dir_commitcrc(lfs2, &commit); + if (err) { + if (err == LFS2_ERR_NOSPC || err == LFS2_ERR_CORRUPT) { + goto compact; + } + return err; + } + + // successful commit, update dir + dir->off = commit.off; + dir->etag = commit.ptag; + + // note we able to have already handled move here + if (lfs2_gstate_hasmovehere(&lfs2->gpending, dir->pair)) { + lfs2_gstate_xormove(&lfs2->gpending, &lfs2->gpending, 0x3ff, NULL); + } + + // update gstate + lfs2->gstate = lfs2->gpending; + lfs2->gdelta = (struct lfs2_gstate){0}; + } else { +compact: + // fall back to compaction + lfs2_cache_drop(lfs2, &lfs2->pcache); + + int err = lfs2_dir_compact(lfs2, dir, attrs, attrcount, + dir, 0, dir->count); + if (err) { + return err; + } + } + + // update any directories that are affected + lfs2_mdir_t copy = *dir; + + // two passes, once for things that aren't us, and one + // for things that are + for (struct lfs2_mlist *d = lfs2->mlist; d; d = d->next) { + if (lfs2_pair_cmp(d->m.pair, copy.pair) == 0) { + d->m = *dir; + if (d->id == lfs2_tag_id(deletetag)) { + d->m.pair[0] = 0xffffffff; + d->m.pair[1] = 0xffffffff; + } else if (d->id > lfs2_tag_id(deletetag)) { + d->id -= 1; + if (d->type == LFS2_TYPE_DIR) { + ((lfs2_dir_t*)d)->pos -= 1; + } + } else if (&d->m != dir && d->id >= lfs2_tag_id(createtag)) { + d->id += 1; + if (d->type == LFS2_TYPE_DIR) { + ((lfs2_dir_t*)d)->pos += 1; + } + } + + while (d->id >= d->m.count && d->m.split) { + // we split and id is on tail now + d->id -= d->m.count; + int err = lfs2_dir_fetch(lfs2, &d->m, d->m.tail); + if (err) { + return err; + } + } + } + } + + return 0; +} + + +/// Top level directory operations /// +int lfs2_mkdir(lfs2_t *lfs2, const char *path) { + // deorphan if we haven't yet, needed at most once after poweron + int err = lfs2_fs_forceconsistency(lfs2); + if (err) { + return err; + } + + lfs2_mdir_t cwd; + uint16_t id; + err = lfs2_dir_find(lfs2, &cwd, &path, &id); + if (!(err == LFS2_ERR_NOENT && id != 0x3ff)) { + return (err < 0) ? err : LFS2_ERR_EXIST; + } + + // check that name fits + lfs2_size_t nlen = strlen(path); + if (nlen > lfs2->name_max) { + return LFS2_ERR_NAMETOOLONG; + } + + // build up new directory + lfs2_alloc_ack(lfs2); + lfs2_mdir_t dir; + err = lfs2_dir_alloc(lfs2, &dir); + if (err) { + return err; + } + + // find end of list + lfs2_mdir_t pred = cwd; + while (pred.split) { + err = lfs2_dir_fetch(lfs2, &pred, pred.tail); + if (err) { + return err; + } + } + + // setup dir + lfs2_pair_tole32(pred.tail); + err = lfs2_dir_commit(lfs2, &dir, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); + lfs2_pair_fromle32(pred.tail); + if (err) { + return err; + } + + // current block end of list? + if (cwd.split) { + // update tails, this creates a desync + lfs2_fs_preporphans(lfs2, +1); + lfs2_pair_tole32(dir.pair); + err = lfs2_dir_commit(lfs2, &pred, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); + lfs2_pair_fromle32(dir.pair); + if (err) { + return err; + } + lfs2_fs_preporphans(lfs2, -1); + } + + // now insert into our parent block + lfs2_pair_tole32(dir.pair); + err = lfs2_dir_commit(lfs2, &cwd, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_CREATE, id, 0), NULL}, + {LFS2_MKTAG(LFS2_TYPE_DIR, id, nlen), path}, + {LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, id, 8), dir.pair}, + {!cwd.split + ? LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 8) + : LFS2_MKTAG(LFS2_FROM_NOOP, 0, 0), dir.pair})); + lfs2_pair_fromle32(dir.pair); + if (err) { + return err; + } + + return 0; +} + +int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { + lfs2_stag_t tag = lfs2_dir_find(lfs2, &dir->m, &path, NULL); + if (tag < 0) { + return tag; + } + + if (lfs2_tag_type3(tag) != LFS2_TYPE_DIR) { + return LFS2_ERR_NOTDIR; + } + + lfs2_block_t pair[2]; + if (lfs2_tag_id(tag) == 0x3ff) { + // handle root dir separately + pair[0] = lfs2->root[0]; + pair[1] = lfs2->root[1]; + } else { + // get dir pair from parent + lfs2_stag_t res = lfs2_dir_get(lfs2, &dir->m, LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, lfs2_tag_id(tag), 8), pair); + if (res < 0) { + return res; + } + lfs2_pair_fromle32(pair); + } + + // fetch first pair + int err = lfs2_dir_fetch(lfs2, &dir->m, pair); + if (err) { + return err; + } + + // setup entry + dir->head[0] = dir->m.pair[0]; + dir->head[1] = dir->m.pair[1]; + dir->id = 0; + dir->pos = 0; + + // add to list of mdirs + dir->type = LFS2_TYPE_DIR; + dir->next = (lfs2_dir_t*)lfs2->mlist; + lfs2->mlist = (struct lfs2_mlist*)dir; + + return 0; +} + +int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir) { + // remove from list of mdirs + for (struct lfs2_mlist **p = &lfs2->mlist; *p; p = &(*p)->next) { + if (*p == (struct lfs2_mlist*)dir) { + *p = (*p)->next; + break; + } + } + + return 0; +} + +int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { + memset(info, 0, sizeof(*info)); + + // special offset for '.' and '..' + if (dir->pos == 0) { + info->type = LFS2_TYPE_DIR; + strcpy(info->name, "."); + dir->pos += 1; + return 1; + } else if (dir->pos == 1) { + info->type = LFS2_TYPE_DIR; + strcpy(info->name, ".."); + dir->pos += 1; + return 1; + } + + while (true) { + if (dir->id == dir->m.count) { + if (!dir->m.split) { + return false; + } + + int err = lfs2_dir_fetch(lfs2, &dir->m, dir->m.tail); + if (err) { + return err; + } + + dir->id = 0; + } + + int err = lfs2_dir_getinfo(lfs2, &dir->m, dir->id, info); + if (err && err != LFS2_ERR_NOENT) { + return err; + } + + dir->id += 1; + if (err != LFS2_ERR_NOENT) { + break; + } + } + + dir->pos += 1; + return true; +} + +int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { + // simply walk from head dir + int err = lfs2_dir_rewind(lfs2, dir); + if (err) { + return err; + } + + // first two for ./.. + dir->pos = lfs2_min(2, off); + off -= dir->pos; + + while (off != 0) { + dir->id = lfs2_min(dir->m.count, off); + dir->pos += dir->id; + off -= dir->id; + + if (dir->id == dir->m.count) { + if (!dir->m.split) { + return LFS2_ERR_INVAL; + } + + err = lfs2_dir_fetch(lfs2, &dir->m, dir->m.tail); + if (err) { + return err; + } + } + } + + return 0; +} + +lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir) { + (void)lfs2; + return dir->pos; +} + +int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir) { + // reload the head dir + int err = lfs2_dir_fetch(lfs2, &dir->m, dir->head); + if (err) { + return err; + } + + dir->m.pair[0] = dir->head[0]; + dir->m.pair[1] = dir->head[1]; + dir->id = 0; + dir->pos = 0; + return 0; +} + + +/// File index list operations /// +static int lfs2_ctz_index(lfs2_t *lfs2, lfs2_off_t *off) { + lfs2_off_t size = *off; + lfs2_off_t b = lfs2->cfg->block_size - 2*4; + lfs2_off_t i = size / b; + if (i == 0) { + return 0; + } + + i = (size - 4*(lfs2_popc(i-1)+2)) / b; + *off = size - b*i - 4*lfs2_popc(i); + return i; +} + +static int lfs2_ctz_find(lfs2_t *lfs2, + const lfs2_cache_t *pcache, lfs2_cache_t *rcache, + lfs2_block_t head, lfs2_size_t size, + lfs2_size_t pos, lfs2_block_t *block, lfs2_off_t *off) { + if (size == 0) { + *block = 0xffffffff; + *off = 0; + return 0; + } + + lfs2_off_t current = lfs2_ctz_index(lfs2, &(lfs2_off_t){size-1}); + lfs2_off_t target = lfs2_ctz_index(lfs2, &pos); + + while (current > target) { + lfs2_size_t skip = lfs2_min( + lfs2_npw2(current-target+1) - 1, + lfs2_ctz(current)); + + int err = lfs2_bd_read(lfs2, + pcache, rcache, sizeof(head), + head, 4*skip, &head, sizeof(head)); + head = lfs2_fromle32(head); + if (err) { + return err; + } + + LFS2_ASSERT(head >= 2 && head <= lfs2->cfg->block_count); + current -= 1 << skip; + } + + *block = head; + *off = pos; + return 0; +} + +static int lfs2_ctz_extend(lfs2_t *lfs2, + lfs2_cache_t *pcache, lfs2_cache_t *rcache, + lfs2_block_t head, lfs2_size_t size, + lfs2_block_t *block, lfs2_off_t *off) { + while (true) { + // go ahead and grab a block + lfs2_block_t nblock; + int err = lfs2_alloc(lfs2, &nblock); + if (err) { + return err; + } + LFS2_ASSERT(nblock >= 2 && nblock <= lfs2->cfg->block_count); + + { + err = lfs2_bd_erase(lfs2, nblock); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + if (size == 0) { + *block = nblock; + *off = 0; + return 0; + } + + size -= 1; + lfs2_off_t index = lfs2_ctz_index(lfs2, &size); + size += 1; + + // just copy out the last block if it is incomplete + if (size != lfs2->cfg->block_size) { + for (lfs2_off_t i = 0; i < size; i++) { + uint8_t data; + err = lfs2_bd_read(lfs2, + NULL, rcache, size-i, + head, i, &data, 1); + if (err) { + return err; + } + + err = lfs2_bd_prog(lfs2, + pcache, rcache, true, + nblock, i, &data, 1); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + } + + *block = nblock; + *off = size; + return 0; + } + + // append block + index += 1; + lfs2_size_t skips = lfs2_ctz(index) + 1; + + for (lfs2_off_t i = 0; i < skips; i++) { + head = lfs2_tole32(head); + err = lfs2_bd_prog(lfs2, pcache, rcache, true, + nblock, 4*i, &head, 4); + head = lfs2_fromle32(head); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + if (i != skips-1) { + err = lfs2_bd_read(lfs2, + NULL, rcache, sizeof(head), + head, 4*i, &head, sizeof(head)); + head = lfs2_fromle32(head); + if (err) { + return err; + } + } + + LFS2_ASSERT(head >= 2 && head <= lfs2->cfg->block_count); + } + + *block = nblock; + *off = 4*skips; + return 0; + } + +relocate: + LFS2_DEBUG("Bad block at %"PRIu32, nblock); + + // just clear cache and try a new block + lfs2_cache_drop(lfs2, pcache); + } +} + +static int lfs2_ctz_traverse(lfs2_t *lfs2, + const lfs2_cache_t *pcache, lfs2_cache_t *rcache, + lfs2_block_t head, lfs2_size_t size, + int (*cb)(void*, lfs2_block_t), void *data) { + if (size == 0) { + return 0; + } + + lfs2_off_t index = lfs2_ctz_index(lfs2, &(lfs2_off_t){size-1}); + + while (true) { + int err = cb(data, head); + if (err) { + return err; + } + + if (index == 0) { + return 0; + } + + lfs2_block_t heads[2]; + int count = 2 - (index & 1); + err = lfs2_bd_read(lfs2, + pcache, rcache, count*sizeof(head), + head, 0, &heads, count*sizeof(head)); + heads[0] = lfs2_fromle32(heads[0]); + heads[1] = lfs2_fromle32(heads[1]); + if (err) { + return err; + } + + for (int i = 0; i < count-1; i++) { + err = cb(data, heads[i]); + if (err) { + return err; + } + } + + head = heads[count-1]; + index -= count; + } +} + + +/// Top level file operations /// +int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, + const char *path, int flags, + const struct lfs2_file_config *cfg) { + // deorphan if we haven't yet, needed at most once after poweron + if ((flags & 3) != LFS2_O_RDONLY) { + int err = lfs2_fs_forceconsistency(lfs2); + if (err) { + return err; + } + } + + // setup simple file details + int err; + file->cfg = cfg; + file->flags = flags; + file->pos = 0; + file->cache.buffer = NULL; + + // allocate entry for file if it doesn't exist + lfs2_stag_t tag = lfs2_dir_find(lfs2, &file->m, &path, &file->id); + if (tag < 0 && !(tag == LFS2_ERR_NOENT && file->id != 0x3ff)) { + err = tag; + goto cleanup; + } + + // get id, add to list of mdirs to catch update changes + file->type = LFS2_TYPE_REG; + file->next = (lfs2_file_t*)lfs2->mlist; + lfs2->mlist = (struct lfs2_mlist*)file; + + if (tag == LFS2_ERR_NOENT) { + if (!(flags & LFS2_O_CREAT)) { + err = LFS2_ERR_NOENT; + goto cleanup; + } + + // check that name fits + lfs2_size_t nlen = strlen(path); + if (nlen > lfs2->name_max) { + err = LFS2_ERR_NAMETOOLONG; + goto cleanup; + } + + // get next slot and create entry to remember name + err = lfs2_dir_commit(lfs2, &file->m, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_CREATE, file->id, 0), NULL}, + {LFS2_MKTAG(LFS2_TYPE_REG, file->id, nlen), path}, + {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0), NULL})); + if (err) { + err = LFS2_ERR_NAMETOOLONG; + goto cleanup; + } + + tag = LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, 0); + } else if (flags & LFS2_O_EXCL) { + err = LFS2_ERR_EXIST; + goto cleanup; + } else if (lfs2_tag_type3(tag) != LFS2_TYPE_REG) { + err = LFS2_ERR_ISDIR; + goto cleanup; + } else if (flags & LFS2_O_TRUNC) { + // truncate if requested + tag = LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0); + file->flags |= LFS2_F_DIRTY; + } else { + // try to load what's on disk, if it's inlined we'll fix it later + tag = lfs2_dir_get(lfs2, &file->m, LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, file->id, 8), &file->ctz); + if (tag < 0) { + err = tag; + goto cleanup; + } + lfs2_ctz_fromle32(&file->ctz); + } + + // fetch attrs + for (unsigned i = 0; i < file->cfg->attr_count; i++) { + if ((file->flags & 3) != LFS2_O_WRONLY) { + lfs2_stag_t res = lfs2_dir_get(lfs2, &file->m, + LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_USERATTR + file->cfg->attrs[i].type, + file->id, file->cfg->attrs[i].size), + file->cfg->attrs[i].buffer); + if (res < 0 && res != LFS2_ERR_NOENT) { + err = res; + goto cleanup; + } + } + + if ((file->flags & 3) != LFS2_O_RDONLY) { + if (file->cfg->attrs[i].size > lfs2->attr_max) { + err = LFS2_ERR_NOSPC; + goto cleanup; + } + + file->flags |= LFS2_F_DIRTY; + } + } + + // allocate buffer if needed + if (file->cfg->buffer) { + file->cache.buffer = file->cfg->buffer; + } else { + file->cache.buffer = lfs2_malloc(lfs2->cfg->cache_size); + if (!file->cache.buffer) { + err = LFS2_ERR_NOMEM; + goto cleanup; + } + } + + // zero to avoid information leak + lfs2_cache_zero(lfs2, &file->cache); + + if (lfs2_tag_type3(tag) == LFS2_TYPE_INLINESTRUCT) { + // load inline files + file->ctz.head = 0xfffffffe; + file->ctz.size = lfs2_tag_size(tag); + file->flags |= LFS2_F_INLINE; + file->cache.block = file->ctz.head; + file->cache.off = 0; + file->cache.size = lfs2->cfg->cache_size; + + // don't always read (may be new/trunc file) + if (file->ctz.size > 0) { + lfs2_stag_t res = lfs2_dir_get(lfs2, &file->m, + LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, file->id, + lfs2_min(file->cache.size, 0x3fe)), + file->cache.buffer); + if (res < 0) { + err = res; + goto cleanup; + } + } + } + + return 0; + +cleanup: + // clean up lingering resources + file->flags |= LFS2_F_ERRED; + lfs2_file_close(lfs2, file); + return err; +} + +int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, + const char *path, int flags) { + static const struct lfs2_file_config defaults = {0}; + return lfs2_file_opencfg(lfs2, file, path, flags, &defaults); +} + +int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file) { + int err = lfs2_file_sync(lfs2, file); + + // remove from list of mdirs + for (struct lfs2_mlist **p = &lfs2->mlist; *p; p = &(*p)->next) { + if (*p == (struct lfs2_mlist*)file) { + *p = (*p)->next; + break; + } + } + + // clean up memory + if (!file->cfg->buffer) { + lfs2_free(file->cache.buffer); + } + + return err; +} + +static int lfs2_file_relocate(lfs2_t *lfs2, lfs2_file_t *file) { + while (true) { + // just relocate what exists into new block + lfs2_block_t nblock; + int err = lfs2_alloc(lfs2, &nblock); + if (err) { + return err; + } + + err = lfs2_bd_erase(lfs2, nblock); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + // either read from dirty cache or disk + for (lfs2_off_t i = 0; i < file->off; i++) { + uint8_t data; + if (file->flags & LFS2_F_INLINE) { + err = lfs2_dir_getread(lfs2, &file->m, + // note we evict inline files before they can be dirty + NULL, &file->cache, file->off-i, + LFS2_MKTAG(0xfff, 0x1ff, 0), + LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0), + i, &data, 1); + if (err) { + return err; + } + } else { + err = lfs2_bd_read(lfs2, + &file->cache, &lfs2->rcache, file->off-i, + file->block, i, &data, 1); + if (err) { + return err; + } + } + + err = lfs2_bd_prog(lfs2, + &lfs2->pcache, &lfs2->rcache, true, + nblock, i, &data, 1); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + } + + // copy over new state of file + memcpy(file->cache.buffer, lfs2->pcache.buffer, lfs2->cfg->cache_size); + file->cache.block = lfs2->pcache.block; + file->cache.off = lfs2->pcache.off; + file->cache.size = lfs2->pcache.size; + lfs2_cache_zero(lfs2, &lfs2->pcache); + + file->block = nblock; + file->flags &= ~LFS2_F_INLINE; + file->flags |= LFS2_F_WRITING; + return 0; + +relocate: + LFS2_DEBUG("Bad block at %"PRIu32, nblock); + + // just clear cache and try a new block + lfs2_cache_drop(lfs2, &lfs2->pcache); + } +} + +static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { + if (file->flags & LFS2_F_READING) { + if (!(file->flags & LFS2_F_INLINE)) { + lfs2_cache_drop(lfs2, &file->cache); + } + file->flags &= ~LFS2_F_READING; + } + + if (file->flags & LFS2_F_WRITING) { + lfs2_off_t pos = file->pos; + + if (!(file->flags & LFS2_F_INLINE)) { + // copy over anything after current branch + lfs2_file_t orig = { + .ctz.head = file->ctz.head, + .ctz.size = file->ctz.size, + .flags = LFS2_O_RDONLY, + .pos = file->pos, + .cache = lfs2->rcache, + }; + lfs2_cache_drop(lfs2, &lfs2->rcache); + + while (file->pos < file->ctz.size) { + // copy over a byte at a time, leave it up to caching + // to make this efficient + uint8_t data; + lfs2_ssize_t res = lfs2_file_read(lfs2, &orig, &data, 1); + if (res < 0) { + return res; + } + + res = lfs2_file_write(lfs2, file, &data, 1); + if (res < 0) { + return res; + } + + // keep our reference to the rcache in sync + if (lfs2->rcache.block != 0xffffffff) { + lfs2_cache_drop(lfs2, &orig.cache); + lfs2_cache_drop(lfs2, &lfs2->rcache); + } + } + + // write out what we have + while (true) { + int err = lfs2_bd_flush(lfs2, &file->cache, &lfs2->rcache, true); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + return err; + } + + break; + +relocate: + LFS2_DEBUG("Bad block at %"PRIu32, file->block); + err = lfs2_file_relocate(lfs2, file); + if (err) { + return err; + } + } + } else { + file->ctz.size = lfs2_max(file->pos, file->ctz.size); + } + + // actual file updates + file->ctz.head = file->block; + file->ctz.size = file->pos; + file->flags &= ~LFS2_F_WRITING; + file->flags |= LFS2_F_DIRTY; + + file->pos = pos; + } + + return 0; +} + +int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file) { + while (true) { + int err = lfs2_file_flush(lfs2, file); + if (err) { + file->flags |= LFS2_F_ERRED; + return err; + } + + if ((file->flags & LFS2_F_DIRTY) && + !(file->flags & LFS2_F_ERRED) && + !lfs2_pair_isnull(file->m.pair)) { + // update dir entry + uint16_t type; + const void *buffer; + lfs2_size_t size; + struct lfs2_ctz ctz; + if (file->flags & LFS2_F_INLINE) { + // inline the whole file + type = LFS2_TYPE_INLINESTRUCT; + buffer = file->cache.buffer; + size = file->ctz.size; + } else { + // update the ctz reference + type = LFS2_TYPE_CTZSTRUCT; + // copy ctz so alloc will work during a relocate + ctz = file->ctz; + lfs2_ctz_tole32(&ctz); + buffer = &ctz; + size = sizeof(ctz); + } + + // commit file data and attributes + err = lfs2_dir_commit(lfs2, &file->m, LFS2_MKATTRS( + {LFS2_MKTAG(type, file->id, size), buffer}, + {LFS2_MKTAG(LFS2_FROM_USERATTRS, file->id, + file->cfg->attr_count), file->cfg->attrs})); + if (err) { + if (err == LFS2_ERR_NOSPC && (file->flags & LFS2_F_INLINE)) { + goto relocate; + } + file->flags |= LFS2_F_ERRED; + return err; + } + + file->flags &= ~LFS2_F_DIRTY; + } + + return 0; + +relocate: + // inline file doesn't fit anymore + file->off = file->pos; + err = lfs2_file_relocate(lfs2, file); + if (err) { + file->flags |= LFS2_F_ERRED; + return err; + } + } +} + +lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, + void *buffer, lfs2_size_t size) { + uint8_t *data = buffer; + lfs2_size_t nsize = size; + + if ((file->flags & 3) == LFS2_O_WRONLY) { + return LFS2_ERR_BADF; + } + + if (file->flags & LFS2_F_WRITING) { + // flush out any writes + int err = lfs2_file_flush(lfs2, file); + if (err) { + return err; + } + } + + if (file->pos >= file->ctz.size) { + // eof if past end + return 0; + } + + size = lfs2_min(size, file->ctz.size - file->pos); + nsize = size; + + while (nsize > 0) { + // check if we need a new block + if (!(file->flags & LFS2_F_READING) || + file->off == lfs2->cfg->block_size) { + if (!(file->flags & LFS2_F_INLINE)) { + int err = lfs2_ctz_find(lfs2, NULL, &file->cache, + file->ctz.head, file->ctz.size, + file->pos, &file->block, &file->off); + if (err) { + return err; + } + } else { + file->block = 0xfffffffe; + file->off = file->pos; + } + + file->flags |= LFS2_F_READING; + } + + // read as much as we can in current block + lfs2_size_t diff = lfs2_min(nsize, lfs2->cfg->block_size - file->off); + if (file->flags & LFS2_F_INLINE) { + int err = lfs2_dir_getread(lfs2, &file->m, + NULL, &file->cache, lfs2->cfg->block_size, + LFS2_MKTAG(0xfff, 0x1ff, 0), + LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0), + file->off, data, diff); + if (err) { + return err; + } + } else { + int err = lfs2_bd_read(lfs2, + NULL, &file->cache, lfs2->cfg->block_size, + file->block, file->off, data, diff); + if (err) { + return err; + } + } + + file->pos += diff; + file->off += diff; + data += diff; + nsize -= diff; + } + + return size; +} + +lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, + const void *buffer, lfs2_size_t size) { + const uint8_t *data = buffer; + lfs2_size_t nsize = size; + + if ((file->flags & 3) == LFS2_O_RDONLY) { + return LFS2_ERR_BADF; + } + + if (file->flags & LFS2_F_READING) { + // drop any reads + int err = lfs2_file_flush(lfs2, file); + if (err) { + return err; + } + } + + if ((file->flags & LFS2_O_APPEND) && file->pos < file->ctz.size) { + file->pos = file->ctz.size; + } + + if (file->pos + size > lfs2->file_max) { + // Larger than file limit? + return LFS2_ERR_FBIG; + } + + if (!(file->flags & LFS2_F_WRITING) && file->pos > file->ctz.size) { + // fill with zeros + lfs2_off_t pos = file->pos; + file->pos = file->ctz.size; + + while (file->pos < pos) { + lfs2_ssize_t res = lfs2_file_write(lfs2, file, &(uint8_t){0}, 1); + if (res < 0) { + return res; + } + } + } + + if ((file->flags & LFS2_F_INLINE) && + lfs2_max(file->pos+nsize, file->ctz.size) > + lfs2_min(LFS2_ATTR_MAX, lfs2_min( + lfs2->cfg->cache_size, lfs2->cfg->block_size/8))) { + // inline file doesn't fit anymore + file->off = file->pos; + lfs2_alloc_ack(lfs2); + int err = lfs2_file_relocate(lfs2, file); + if (err) { + file->flags |= LFS2_F_ERRED; + return err; + } + } + + while (nsize > 0) { + // check if we need a new block + if (!(file->flags & LFS2_F_WRITING) || + file->off == lfs2->cfg->block_size) { + if (!(file->flags & LFS2_F_INLINE)) { + if (!(file->flags & LFS2_F_WRITING) && file->pos > 0) { + // find out which block we're extending from + int err = lfs2_ctz_find(lfs2, NULL, &file->cache, + file->ctz.head, file->ctz.size, + file->pos-1, &file->block, &file->off); + if (err) { + file->flags |= LFS2_F_ERRED; + return err; + } + + // mark cache as dirty since we may have read data into it + lfs2_cache_zero(lfs2, &file->cache); + } + + // extend file with new blocks + lfs2_alloc_ack(lfs2); + int err = lfs2_ctz_extend(lfs2, &file->cache, &lfs2->rcache, + file->block, file->pos, + &file->block, &file->off); + if (err) { + file->flags |= LFS2_F_ERRED; + return err; + } + } else { + file->block = 0xfffffffe; + file->off = file->pos; + } + + file->flags |= LFS2_F_WRITING; + } + + // program as much as we can in current block + lfs2_size_t diff = lfs2_min(nsize, lfs2->cfg->block_size - file->off); + while (true) { + int err = lfs2_bd_prog(lfs2, &file->cache, &lfs2->rcache, true, + file->block, file->off, data, diff); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + goto relocate; + } + file->flags |= LFS2_F_ERRED; + return err; + } + + break; +relocate: + err = lfs2_file_relocate(lfs2, file); + if (err) { + file->flags |= LFS2_F_ERRED; + return err; + } + } + + file->pos += diff; + file->off += diff; + data += diff; + nsize -= diff; + + lfs2_alloc_ack(lfs2); + } + + file->flags &= ~LFS2_F_ERRED; + return size; +} + +lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, + lfs2_soff_t off, int whence) { + // write out everything beforehand, may be noop if rdonly + int err = lfs2_file_flush(lfs2, file); + if (err) { + return err; + } + + // find new pos + lfs2_off_t npos = file->pos; + if (whence == LFS2_SEEK_SET) { + npos = off; + } else if (whence == LFS2_SEEK_CUR) { + npos = file->pos + off; + } else if (whence == LFS2_SEEK_END) { + npos = file->ctz.size + off; + } + + if (npos > lfs2->file_max) { + // file position out of range + return LFS2_ERR_INVAL; + } + + // update pos + file->pos = npos; + return npos; +} + +int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { + if ((file->flags & 3) == LFS2_O_RDONLY) { + return LFS2_ERR_BADF; + } + + lfs2_off_t oldsize = lfs2_file_size(lfs2, file); + if (size < oldsize) { + // need to flush since directly changing metadata + int err = lfs2_file_flush(lfs2, file); + if (err) { + return err; + } + + // lookup new head in ctz skip list + err = lfs2_ctz_find(lfs2, NULL, &file->cache, + file->ctz.head, file->ctz.size, + size, &file->ctz.head, &(lfs2_off_t){0}); + if (err) { + return err; + } + + file->ctz.size = size; + file->flags |= LFS2_F_DIRTY; + } else if (size > oldsize) { + lfs2_off_t pos = file->pos; + + // flush+seek if not already at end + if (file->pos != oldsize) { + int err = lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_END); + if (err < 0) { + return err; + } + } + + // fill with zeros + while (file->pos < size) { + lfs2_ssize_t res = lfs2_file_write(lfs2, file, &(uint8_t){0}, 1); + if (res < 0) { + return res; + } + } + + // restore pos + int err = lfs2_file_seek(lfs2, file, pos, LFS2_SEEK_SET); + if (err < 0) { + return err; + } + } + + return 0; +} + +lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file) { + (void)lfs2; + return file->pos; +} + +int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file) { + lfs2_soff_t res = lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_SET); + if (res < 0) { + return res; + } + + return 0; +} + +lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file) { + (void)lfs2; + if (file->flags & LFS2_F_WRITING) { + return lfs2_max(file->pos, file->ctz.size); + } else { + return file->ctz.size; + } +} + + +/// General fs operations /// +int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { + lfs2_mdir_t cwd; + lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); + if (tag < 0) { + return tag; + } + + return lfs2_dir_getinfo(lfs2, &cwd, lfs2_tag_id(tag), info); +} + +int lfs2_remove(lfs2_t *lfs2, const char *path) { + // deorphan if we haven't yet, needed at most once after poweron + int err = lfs2_fs_forceconsistency(lfs2); + if (err) { + return err; + } + + lfs2_mdir_t cwd; + lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); + if (tag < 0 || lfs2_tag_id(tag) == 0x3ff) { + return (tag < 0) ? tag : LFS2_ERR_INVAL; + } + + lfs2_mdir_t dir; + if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) { + // must be empty before removal + lfs2_block_t pair[2]; + lfs2_stag_t res = lfs2_dir_get(lfs2, &cwd, LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, lfs2_tag_id(tag), 8), pair); + if (res < 0) { + return res; + } + lfs2_pair_fromle32(pair); + + err = lfs2_dir_fetch(lfs2, &dir, pair); + if (err) { + return err; + } + + if (dir.count > 0 || dir.split) { + return LFS2_ERR_NOTEMPTY; + } + + // mark fs as orphaned + lfs2_fs_preporphans(lfs2, +1); + } + + // delete the entry + err = lfs2_dir_commit(lfs2, &cwd, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_DELETE, lfs2_tag_id(tag), 0), NULL})); + if (err) { + return err; + } + + if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) { + // fix orphan + lfs2_fs_preporphans(lfs2, -1); + + err = lfs2_fs_pred(lfs2, dir.pair, &cwd); + if (err) { + return err; + } + + err = lfs2_dir_drop(lfs2, &cwd, &dir); + if (err) { + return err; + } + } + + return 0; +} + +int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { + // deorphan if we haven't yet, needed at most once after poweron + int err = lfs2_fs_forceconsistency(lfs2); + if (err) { + return err; + } + + // find old entry + lfs2_mdir_t oldcwd; + lfs2_stag_t oldtag = lfs2_dir_find(lfs2, &oldcwd, &oldpath, NULL); + if (oldtag < 0 || lfs2_tag_id(oldtag) == 0x3ff) { + return (oldtag < 0) ? oldtag : LFS2_ERR_INVAL; + } + + // find new entry + lfs2_mdir_t newcwd; + uint16_t newid; + lfs2_stag_t prevtag = lfs2_dir_find(lfs2, &newcwd, &newpath, &newid); + if ((prevtag < 0 || lfs2_tag_id(prevtag) == 0x3ff) && + !(prevtag == LFS2_ERR_NOENT && newid != 0x3ff)) { + return (prevtag < 0) ? prevtag : LFS2_ERR_INVAL; + } + + lfs2_mdir_t prevdir; + if (prevtag == LFS2_ERR_NOENT) { + // check that name fits + lfs2_size_t nlen = strlen(newpath); + if (nlen > lfs2->name_max) { + return LFS2_ERR_NAMETOOLONG; + } + } else if (lfs2_tag_type3(prevtag) != lfs2_tag_type3(oldtag)) { + return LFS2_ERR_ISDIR; + } else if (lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR) { + // must be empty before removal + lfs2_block_t prevpair[2]; + lfs2_stag_t res = lfs2_dir_get(lfs2, &newcwd, LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, newid, 8), prevpair); + if (res < 0) { + return res; + } + lfs2_pair_fromle32(prevpair); + + // must be empty before removal + err = lfs2_dir_fetch(lfs2, &prevdir, prevpair); + if (err) { + return err; + } + + if (prevdir.count > 0 || prevdir.split) { + return LFS2_ERR_NOTEMPTY; + } + + // mark fs as orphaned + lfs2_fs_preporphans(lfs2, +1); + } + + // create move to fix later + uint16_t newoldtagid = lfs2_tag_id(oldtag); + if (lfs2_pair_cmp(oldcwd.pair, newcwd.pair) == 0 && + prevtag == LFS2_ERR_NOENT && newid <= newoldtagid) { + // there is a small chance we are being renamed in the same directory + // to an id less than our old id, the global update to handle this + // is a bit messy + newoldtagid += 1; + } + + lfs2_fs_prepmove(lfs2, newoldtagid, oldcwd.pair); + + // move over all attributes + err = lfs2_dir_commit(lfs2, &newcwd, LFS2_MKATTRS( + {prevtag != LFS2_ERR_NOENT + ? LFS2_MKTAG(LFS2_TYPE_DELETE, newid, 0) + : LFS2_MKTAG(LFS2_FROM_NOOP, 0, 0), NULL}, + {LFS2_MKTAG(LFS2_TYPE_CREATE, newid, 0), NULL}, + {LFS2_MKTAG(lfs2_tag_type3(oldtag), newid, strlen(newpath)), + newpath}, + {LFS2_MKTAG(LFS2_FROM_MOVE, newid, lfs2_tag_id(oldtag)), &oldcwd})); + if (err) { + return err; + } + + // let commit clean up after move (if we're different! otherwise move + // logic already fixed it for us) + if (lfs2_pair_cmp(oldcwd.pair, newcwd.pair) != 0) { + err = lfs2_dir_commit(lfs2, &oldcwd, NULL, 0); + if (err) { + return err; + } + } + + if (prevtag != LFS2_ERR_NOENT && lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR) { + // fix orphan + lfs2_fs_preporphans(lfs2, -1); + + err = lfs2_fs_pred(lfs2, prevdir.pair, &newcwd); + if (err) { + return err; + } + + err = lfs2_dir_drop(lfs2, &newcwd, &prevdir); + if (err) { + return err; + } + } + + return 0; +} + +lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, + uint8_t type, void *buffer, lfs2_size_t size) { + lfs2_mdir_t cwd; + lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); + if (tag < 0) { + return tag; + } + + uint16_t id = lfs2_tag_id(tag); + if (id == 0x3ff) { + // special case for root + id = 0; + int err = lfs2_dir_fetch(lfs2, &cwd, lfs2->root); + if (err) { + return err; + } + } + + tag = lfs2_dir_get(lfs2, &cwd, LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_USERATTR + type, + id, lfs2_min(size, lfs2->attr_max)), + buffer); + if (tag < 0) { + if (tag == LFS2_ERR_NOENT) { + return LFS2_ERR_NOATTR; + } + return tag; + } + + return lfs2_tag_size(tag); +} + +static int lfs2_commitattr(lfs2_t *lfs2, const char *path, + uint8_t type, const void *buffer, lfs2_size_t size) { + lfs2_mdir_t cwd; + lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); + if (tag < 0) { + return tag; + } + + uint16_t id = lfs2_tag_id(tag); + if (id == 0x3ff) { + // special case for root + id = 0; + int err = lfs2_dir_fetch(lfs2, &cwd, lfs2->root); + if (err) { + return err; + } + } + + return lfs2_dir_commit(lfs2, &cwd, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_USERATTR + type, id, size), buffer})); +} + +int lfs2_setattr(lfs2_t *lfs2, const char *path, + uint8_t type, const void *buffer, lfs2_size_t size) { + if (size > lfs2->attr_max) { + return LFS2_ERR_NOSPC; + } + + return lfs2_commitattr(lfs2, path, type, buffer, size); +} + +int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type) { + return lfs2_commitattr(lfs2, path, type, NULL, 0x3ff); +} + + +/// Filesystem operations /// +static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { + lfs2->cfg = cfg; + int err = 0; + + // check that block size is a multiple of cache size is a multiple + // of prog and read sizes + LFS2_ASSERT(lfs2->cfg->cache_size % lfs2->cfg->read_size == 0); + LFS2_ASSERT(lfs2->cfg->cache_size % lfs2->cfg->prog_size == 0); + LFS2_ASSERT(lfs2->cfg->block_size % lfs2->cfg->cache_size == 0); + + // check that the block size is large enough to fit ctz pointers + LFS2_ASSERT(4*lfs2_npw2(0xffffffff / (lfs2->cfg->block_size-2*4)) + <= lfs2->cfg->block_size); + + // we don't support some corner cases + LFS2_ASSERT(lfs2->cfg->block_cycles < 0xffffffff); + + // setup read cache + if (lfs2->cfg->read_buffer) { + lfs2->rcache.buffer = lfs2->cfg->read_buffer; + } else { + lfs2->rcache.buffer = lfs2_malloc(lfs2->cfg->cache_size); + if (!lfs2->rcache.buffer) { + err = LFS2_ERR_NOMEM; + goto cleanup; + } + } + + // setup program cache + if (lfs2->cfg->prog_buffer) { + lfs2->pcache.buffer = lfs2->cfg->prog_buffer; + } else { + lfs2->pcache.buffer = lfs2_malloc(lfs2->cfg->cache_size); + if (!lfs2->pcache.buffer) { + err = LFS2_ERR_NOMEM; + goto cleanup; + } + } + + // zero to avoid information leaks + lfs2_cache_zero(lfs2, &lfs2->rcache); + lfs2_cache_zero(lfs2, &lfs2->pcache); + + // setup lookahead, must be multiple of 64-bits + LFS2_ASSERT(lfs2->cfg->lookahead_size > 0); + LFS2_ASSERT(lfs2->cfg->lookahead_size % 8 == 0 && + (uintptr_t)lfs2->cfg->lookahead_buffer % 8 == 0); + if (lfs2->cfg->lookahead_buffer) { + lfs2->free.buffer = lfs2->cfg->lookahead_buffer; + } else { + lfs2->free.buffer = lfs2_malloc(lfs2->cfg->lookahead_size); + if (!lfs2->free.buffer) { + err = LFS2_ERR_NOMEM; + goto cleanup; + } + } + + // check that the size limits are sane + LFS2_ASSERT(lfs2->cfg->name_max <= LFS2_NAME_MAX); + lfs2->name_max = lfs2->cfg->name_max; + if (!lfs2->name_max) { + lfs2->name_max = LFS2_NAME_MAX; + } + + LFS2_ASSERT(lfs2->cfg->file_max <= LFS2_FILE_MAX); + lfs2->file_max = lfs2->cfg->file_max; + if (!lfs2->file_max) { + lfs2->file_max = LFS2_FILE_MAX; + } + + LFS2_ASSERT(lfs2->cfg->attr_max <= LFS2_ATTR_MAX); + lfs2->attr_max = lfs2->cfg->attr_max; + if (!lfs2->attr_max) { + lfs2->attr_max = LFS2_ATTR_MAX; + } + + // setup default state + lfs2->root[0] = 0xffffffff; + lfs2->root[1] = 0xffffffff; + lfs2->mlist = NULL; + lfs2->seed = 0; + lfs2->gstate = (struct lfs2_gstate){0}; + lfs2->gpending = (struct lfs2_gstate){0}; + lfs2->gdelta = (struct lfs2_gstate){0}; +#ifdef LFS2_MIGRATE + lfs2->lfs21 = NULL; +#endif + + return 0; + +cleanup: + lfs2_deinit(lfs2); + return err; +} + +static int lfs2_deinit(lfs2_t *lfs2) { + // free allocated memory + if (!lfs2->cfg->read_buffer) { + lfs2_free(lfs2->rcache.buffer); + } + + if (!lfs2->cfg->prog_buffer) { + lfs2_free(lfs2->pcache.buffer); + } + + if (!lfs2->cfg->lookahead_buffer) { + lfs2_free(lfs2->free.buffer); + } + + return 0; +} + +int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { + int err = 0; + { + err = lfs2_init(lfs2, cfg); + if (err) { + return err; + } + + // create free lookahead + memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); + lfs2->free.off = 0; + lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, + lfs2->cfg->block_count); + lfs2->free.i = 0; + lfs2_alloc_ack(lfs2); + + // create root dir + lfs2_mdir_t root; + err = lfs2_dir_alloc(lfs2, &root); + if (err) { + goto cleanup; + } + + // write one superblock + lfs2_superblock_t superblock = { + .version = LFS2_DISK_VERSION, + .block_size = lfs2->cfg->block_size, + .block_count = lfs2->cfg->block_count, + .name_max = lfs2->name_max, + .file_max = lfs2->file_max, + .attr_max = lfs2->attr_max, + }; + + lfs2_superblock_tole32(&superblock); + err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_CREATE, 0, 0), NULL}, + {LFS2_MKTAG(LFS2_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, + {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock})); + if (err) { + goto cleanup; + } + + // sanity check that fetch works + err = lfs2_dir_fetch(lfs2, &root, (const lfs2_block_t[2]){0, 1}); + if (err) { + goto cleanup; + } + } + +cleanup: + lfs2_deinit(lfs2); + return err; +} + +int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { + int err = lfs2_init(lfs2, cfg); + if (err) { + return err; + } + + // scan directory blocks for superblock and any global updates + lfs2_mdir_t dir = {.tail = {0, 1}}; + while (!lfs2_pair_isnull(dir.tail)) { + // fetch next block in tail list + lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, &dir, dir.tail, + LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_SUPERBLOCK, 0, 8), + NULL, + lfs2_dir_find_match, &(struct lfs2_dir_find_match){ + lfs2, "littlefs", 8}); + if (tag < 0) { + err = tag; + goto cleanup; + } + + // has superblock? + if (tag && !lfs2_tag_isdelete(tag)) { + // update root + lfs2->root[0] = dir.pair[0]; + lfs2->root[1] = dir.pair[1]; + + // grab superblock + lfs2_superblock_t superblock; + tag = lfs2_dir_get(lfs2, &dir, LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock); + if (tag < 0) { + err = tag; + goto cleanup; + } + lfs2_superblock_fromle32(&superblock); + + // check version + uint16_t major_version = (0xffff & (superblock.version >> 16)); + uint16_t minor_version = (0xffff & (superblock.version >> 0)); + if ((major_version != LFS2_DISK_VERSION_MAJOR || + minor_version > LFS2_DISK_VERSION_MINOR)) { + LFS2_ERROR("Invalid version %"PRIu16".%"PRIu16, + major_version, minor_version); + err = LFS2_ERR_INVAL; + goto cleanup; + } + + // check superblock configuration + if (superblock.name_max) { + if (superblock.name_max > lfs2->name_max) { + LFS2_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")", + superblock.name_max, lfs2->name_max); + err = LFS2_ERR_INVAL; + goto cleanup; + } + + lfs2->name_max = superblock.name_max; + } + + if (superblock.file_max) { + if (superblock.file_max > lfs2->file_max) { + LFS2_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")", + superblock.file_max, lfs2->file_max); + err = LFS2_ERR_INVAL; + goto cleanup; + } + + lfs2->file_max = superblock.file_max; + } + + if (superblock.attr_max) { + if (superblock.attr_max > lfs2->attr_max) { + LFS2_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")", + superblock.attr_max, lfs2->attr_max); + err = LFS2_ERR_INVAL; + goto cleanup; + } + + lfs2->attr_max = superblock.attr_max; + } + } + + // has gstate? + err = lfs2_dir_getgstate(lfs2, &dir, &lfs2->gpending); + if (err) { + return err; + } + } + + // found superblock? + if (lfs2_pair_isnull(lfs2->root)) { + err = LFS2_ERR_INVAL; + goto cleanup; + } + + // update littlefs with gstate + lfs2->gpending.tag += !lfs2_tag_isvalid(lfs2->gpending.tag); + lfs2->gstate = lfs2->gpending; + if (lfs2_gstate_hasmove(&lfs2->gstate)) { + LFS2_DEBUG("Found move %"PRIu32" %"PRIu32" %"PRIu16, + lfs2->gstate.pair[0], + lfs2->gstate.pair[1], + lfs2_tag_id(lfs2->gstate.tag)); + } + + // setup free lookahead + lfs2->free.off = lfs2->seed % lfs2->cfg->block_size; + lfs2->free.size = 0; + lfs2->free.i = 0; + lfs2_alloc_ack(lfs2); + + return 0; + +cleanup: + lfs2_unmount(lfs2); + return err; +} + +int lfs2_unmount(lfs2_t *lfs2) { + return lfs2_deinit(lfs2); +} + + +/// Filesystem filesystem operations /// +int lfs2_fs_traverse(lfs2_t *lfs2, + int (*cb)(void *data, lfs2_block_t block), void *data) { + // iterate over metadata pairs + lfs2_mdir_t dir = {.tail = {0, 1}}; + +#ifdef LFS2_MIGRATE + // also consider v1 blocks during migration + if (lfs2->lfs21) { + int err = lfs21_traverse(lfs2, cb, data); + if (err) { + return err; + } + + dir.tail[0] = lfs2->root[0]; + dir.tail[1] = lfs2->root[1]; + } +#endif + + while (!lfs2_pair_isnull(dir.tail)) { + for (int i = 0; i < 2; i++) { + int err = cb(data, dir.tail[i]); + if (err) { + return err; + } + } + + // iterate through ids in directory + int err = lfs2_dir_fetch(lfs2, &dir, dir.tail); + if (err) { + return err; + } + + for (uint16_t id = 0; id < dir.count; id++) { + struct lfs2_ctz ctz; + lfs2_stag_t tag = lfs2_dir_get(lfs2, &dir, LFS2_MKTAG(0x700, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_STRUCT, id, sizeof(ctz)), &ctz); + if (tag < 0) { + if (tag == LFS2_ERR_NOENT) { + continue; + } + return tag; + } + lfs2_ctz_fromle32(&ctz); + + if (lfs2_tag_type3(tag) == LFS2_TYPE_CTZSTRUCT) { + err = lfs2_ctz_traverse(lfs2, NULL, &lfs2->rcache, + ctz.head, ctz.size, cb, data); + if (err) { + return err; + } + } + } + } + + // iterate over any open files + for (lfs2_file_t *f = (lfs2_file_t*)lfs2->mlist; f; f = f->next) { + if (f->type != LFS2_TYPE_REG) { + continue; + } + + if ((f->flags & LFS2_F_DIRTY) && !(f->flags & LFS2_F_INLINE)) { + int err = lfs2_ctz_traverse(lfs2, &f->cache, &lfs2->rcache, + f->ctz.head, f->ctz.size, cb, data); + if (err) { + return err; + } + } + + if ((f->flags & LFS2_F_WRITING) && !(f->flags & LFS2_F_INLINE)) { + int err = lfs2_ctz_traverse(lfs2, &f->cache, &lfs2->rcache, + f->block, f->pos, cb, data); + if (err) { + return err; + } + } + } + + return 0; +} + +static int lfs2_fs_pred(lfs2_t *lfs2, + const lfs2_block_t pair[2], lfs2_mdir_t *pdir) { + // iterate over all directory directory entries + pdir->tail[0] = 0; + pdir->tail[1] = 1; + while (!lfs2_pair_isnull(pdir->tail)) { + if (lfs2_pair_cmp(pdir->tail, pair) == 0) { + return 0; + } + + int err = lfs2_dir_fetch(lfs2, pdir, pdir->tail); + if (err) { + return err; + } + } + + return LFS2_ERR_NOENT; +} + +struct lfs2_fs_parent_match { + lfs2_t *lfs2; + const lfs2_block_t pair[2]; +}; + +static int lfs2_fs_parent_match(void *data, + lfs2_tag_t tag, const void *buffer) { + struct lfs2_fs_parent_match *find = data; + lfs2_t *lfs2 = find->lfs2; + const struct lfs2_diskoff *disk = buffer; + (void)tag; + + lfs2_block_t child[2]; + int err = lfs2_bd_read(lfs2, + &lfs2->pcache, &lfs2->rcache, lfs2->cfg->block_size, + disk->block, disk->off, &child, sizeof(child)); + if (err) { + return err; + } + + lfs2_pair_fromle32(child); + return (lfs2_pair_cmp(child, find->pair) == 0) ? LFS2_CMP_EQ : LFS2_CMP_LT; +} + +static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2], + lfs2_mdir_t *parent) { + // use fetchmatch with callback to find pairs + parent->tail[0] = 0; + parent->tail[1] = 1; + while (!lfs2_pair_isnull(parent->tail)) { + lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, parent, parent->tail, + LFS2_MKTAG(0x7ff, 0, 0x3ff), + LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 0, 8), + NULL, + lfs2_fs_parent_match, &(struct lfs2_fs_parent_match){ + lfs2, {pair[0], pair[1]}}); + if (tag && tag != LFS2_ERR_NOENT) { + return tag; + } + } + + return LFS2_ERR_NOENT; +} + +static int lfs2_fs_relocate(lfs2_t *lfs2, + const lfs2_block_t oldpair[2], lfs2_block_t newpair[2]) { + // update internal root + if (lfs2_pair_cmp(oldpair, lfs2->root) == 0) { + LFS2_DEBUG("Relocating root %"PRIu32" %"PRIu32, + newpair[0], newpair[1]); + lfs2->root[0] = newpair[0]; + lfs2->root[1] = newpair[1]; + } + + // update internally tracked dirs + for (struct lfs2_mlist *d = lfs2->mlist; d; d = d->next) { + if (lfs2_pair_cmp(oldpair, d->m.pair) == 0) { + d->m.pair[0] = newpair[0]; + d->m.pair[1] = newpair[1]; + } + } + + // find parent + lfs2_mdir_t parent; + lfs2_stag_t tag = lfs2_fs_parent(lfs2, oldpair, &parent); + if (tag < 0 && tag != LFS2_ERR_NOENT) { + return tag; + } + + if (tag != LFS2_ERR_NOENT) { + // update disk, this creates a desync + lfs2_fs_preporphans(lfs2, +1); + + lfs2_pair_tole32(newpair); + int err = lfs2_dir_commit(lfs2, &parent, LFS2_MKATTRS({tag, newpair})); + lfs2_pair_fromle32(newpair); + if (err) { + return err; + } + + // next step, clean up orphans + lfs2_fs_preporphans(lfs2, -1); + } + + // find pred + int err = lfs2_fs_pred(lfs2, oldpair, &parent); + if (err && err != LFS2_ERR_NOENT) { + return err; + } + + // if we can't find dir, it must be new + if (err != LFS2_ERR_NOENT) { + // replace bad pair, either we clean up desync, or no desync occured + lfs2_pair_tole32(newpair); + err = lfs2_dir_commit(lfs2, &parent, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); + lfs2_pair_fromle32(newpair); + if (err) { + return err; + } + } + + return 0; +} + +static void lfs2_fs_preporphans(lfs2_t *lfs2, int8_t orphans) { + lfs2->gpending.tag += orphans; + lfs2_gstate_xororphans(&lfs2->gdelta, &lfs2->gpending, + lfs2_gstate_hasorphans(&lfs2->gpending)); + lfs2_gstate_xororphans(&lfs2->gpending, &lfs2->gpending, + lfs2_gstate_hasorphans(&lfs2->gpending)); +} + +static void lfs2_fs_prepmove(lfs2_t *lfs2, + uint16_t id, const lfs2_block_t pair[2]) { + lfs2_gstate_xormove(&lfs2->gdelta, &lfs2->gpending, id, pair); + lfs2_gstate_xormove(&lfs2->gpending, &lfs2->gpending, id, pair); +} + + +static int lfs2_fs_demove(lfs2_t *lfs2) { + if (!lfs2_gstate_hasmove(&lfs2->gstate)) { + return 0; + } + + // Fix bad moves + LFS2_DEBUG("Fixing move %"PRIu32" %"PRIu32" %"PRIu16, + lfs2->gstate.pair[0], + lfs2->gstate.pair[1], + lfs2_tag_id(lfs2->gstate.tag)); + + // fetch and delete the moved entry + lfs2_mdir_t movedir; + int err = lfs2_dir_fetch(lfs2, &movedir, lfs2->gstate.pair); + if (err) { + return err; + } + + // rely on cancel logic inside commit + err = lfs2_dir_commit(lfs2, &movedir, NULL, 0); + if (err) { + return err; + } + + return 0; +} + +static int lfs2_fs_deorphan(lfs2_t *lfs2) { + if (!lfs2_gstate_hasorphans(&lfs2->gstate)) { + return 0; + } + + // Fix any orphans + lfs2_mdir_t pdir = {.split = true}; + lfs2_mdir_t dir = {.tail = {0, 1}}; + + // iterate over all directory directory entries + while (!lfs2_pair_isnull(dir.tail)) { + int err = lfs2_dir_fetch(lfs2, &dir, dir.tail); + if (err) { + return err; + } + + // check head blocks for orphans + if (!pdir.split) { + // check if we have a parent + lfs2_mdir_t parent; + lfs2_stag_t tag = lfs2_fs_parent(lfs2, pdir.tail, &parent); + if (tag < 0 && tag != LFS2_ERR_NOENT) { + return tag; + } + + if (tag == LFS2_ERR_NOENT) { + // we are an orphan + LFS2_DEBUG("Fixing orphan %"PRIu32" %"PRIu32, + pdir.tail[0], pdir.tail[1]); + + err = lfs2_dir_drop(lfs2, &pdir, &dir); + if (err) { + return err; + } + + break; + } + + lfs2_block_t pair[2]; + lfs2_stag_t res = lfs2_dir_get(lfs2, &parent, + LFS2_MKTAG(0x7ff, 0x3ff, 0), tag, pair); + if (res < 0) { + return res; + } + lfs2_pair_fromle32(pair); + + if (!lfs2_pair_sync(pair, pdir.tail)) { + // we have desynced + LFS2_DEBUG("Fixing half-orphan %"PRIu32" %"PRIu32, + pair[0], pair[1]); + + lfs2_pair_tole32(pair); + err = lfs2_dir_commit(lfs2, &pdir, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 8), pair})); + lfs2_pair_fromle32(pair); + if (err) { + return err; + } + + break; + } + } + + memcpy(&pdir, &dir, sizeof(pdir)); + } + + // mark orphans as fixed + lfs2_fs_preporphans(lfs2, -lfs2_gstate_getorphans(&lfs2->gstate)); + lfs2->gstate = lfs2->gpending; + return 0; +} + +static int lfs2_fs_forceconsistency(lfs2_t *lfs2) { + int err = lfs2_fs_demove(lfs2); + if (err) { + return err; + } + + err = lfs2_fs_deorphan(lfs2); + if (err) { + return err; + } + + return 0; +} + +static int lfs2_fs_size_count(void *p, lfs2_block_t block) { + (void)block; + lfs2_size_t *size = p; + *size += 1; + return 0; +} + +lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2) { + lfs2_size_t size = 0; + int err = lfs2_fs_traverse(lfs2, lfs2_fs_size_count, &size); + if (err) { + return err; + } + + return size; +} + +#ifdef LFS2_MIGRATE +////// Migration from littelfs v1 below this ////// + +/// Version info /// + +// Software library version +// Major (top-nibble), incremented on backwards incompatible changes +// Minor (bottom-nibble), incremented on feature additions +#define LFS21_VERSION 0x00010007 +#define LFS21_VERSION_MAJOR (0xffff & (LFS21_VERSION >> 16)) +#define LFS21_VERSION_MINOR (0xffff & (LFS21_VERSION >> 0)) + +// Version of On-disk data structures +// Major (top-nibble), incremented on backwards incompatible changes +// Minor (bottom-nibble), incremented on feature additions +#define LFS21_DISK_VERSION 0x00010001 +#define LFS21_DISK_VERSION_MAJOR (0xffff & (LFS21_DISK_VERSION >> 16)) +#define LFS21_DISK_VERSION_MINOR (0xffff & (LFS21_DISK_VERSION >> 0)) + + +/// v1 Definitions /// + +// File types +enum lfs21_type { + LFS21_TYPE_REG = 0x11, + LFS21_TYPE_DIR = 0x22, + LFS21_TYPE_SUPERBLOCK = 0x2e, +}; + +typedef struct lfs21 { + lfs2_block_t root[2]; +} lfs21_t; + +typedef struct lfs21_entry { + lfs2_off_t off; + + struct lfs21_disk_entry { + uint8_t type; + uint8_t elen; + uint8_t alen; + uint8_t nlen; + union { + struct { + lfs2_block_t head; + lfs2_size_t size; + } file; + lfs2_block_t dir[2]; + } u; + } d; +} lfs21_entry_t; + +typedef struct lfs21_dir { + struct lfs21_dir *next; + lfs2_block_t pair[2]; + lfs2_off_t off; + + lfs2_block_t head[2]; + lfs2_off_t pos; + + struct lfs21_disk_dir { + uint32_t rev; + lfs2_size_t size; + lfs2_block_t tail[2]; + } d; +} lfs21_dir_t; + +typedef struct lfs21_superblock { + lfs2_off_t off; + + struct lfs21_disk_superblock { + uint8_t type; + uint8_t elen; + uint8_t alen; + uint8_t nlen; + lfs2_block_t root[2]; + uint32_t block_size; + uint32_t block_count; + uint32_t version; + char magic[8]; + } d; +} lfs21_superblock_t; + + +/// Low-level wrappers v1->v2 /// +void lfs21_crc(uint32_t *crc, const void *buffer, size_t size) { + *crc = lfs2_crc(*crc, buffer, size); +} + +static int lfs21_bd_read(lfs2_t *lfs2, lfs2_block_t block, + lfs2_off_t off, void *buffer, lfs2_size_t size) { + // if we ever do more than writes to alternating pairs, + // this may need to consider pcache + return lfs2_bd_read(lfs2, &lfs2->pcache, &lfs2->rcache, size, + block, off, buffer, size); +} + +static int lfs21_bd_crc(lfs2_t *lfs2, lfs2_block_t block, + lfs2_off_t off, lfs2_size_t size, uint32_t *crc) { + for (lfs2_off_t i = 0; i < size; i++) { + uint8_t c; + int err = lfs21_bd_read(lfs2, block, off+i, &c, 1); + if (err) { + return err; + } + + lfs21_crc(crc, &c, 1); + } + + return 0; +} + + +/// Endian swapping functions /// +static void lfs21_dir_fromle32(struct lfs21_disk_dir *d) { + d->rev = lfs2_fromle32(d->rev); + d->size = lfs2_fromle32(d->size); + d->tail[0] = lfs2_fromle32(d->tail[0]); + d->tail[1] = lfs2_fromle32(d->tail[1]); +} + +static void lfs21_dir_tole32(struct lfs21_disk_dir *d) { + d->rev = lfs2_tole32(d->rev); + d->size = lfs2_tole32(d->size); + d->tail[0] = lfs2_tole32(d->tail[0]); + d->tail[1] = lfs2_tole32(d->tail[1]); +} + +static void lfs21_entry_fromle32(struct lfs21_disk_entry *d) { + d->u.dir[0] = lfs2_fromle32(d->u.dir[0]); + d->u.dir[1] = lfs2_fromle32(d->u.dir[1]); +} + +static void lfs21_entry_tole32(struct lfs21_disk_entry *d) { + d->u.dir[0] = lfs2_tole32(d->u.dir[0]); + d->u.dir[1] = lfs2_tole32(d->u.dir[1]); +} + +static void lfs21_superblock_fromle32(struct lfs21_disk_superblock *d) { + d->root[0] = lfs2_fromle32(d->root[0]); + d->root[1] = lfs2_fromle32(d->root[1]); + d->block_size = lfs2_fromle32(d->block_size); + d->block_count = lfs2_fromle32(d->block_count); + d->version = lfs2_fromle32(d->version); +} + + +///// Metadata pair and directory operations /// +static inline lfs2_size_t lfs21_entry_size(const lfs21_entry_t *entry) { + return 4 + entry->d.elen + entry->d.alen + entry->d.nlen; +} + +static int lfs21_dir_fetch(lfs2_t *lfs2, + lfs21_dir_t *dir, const lfs2_block_t pair[2]) { + // copy out pair, otherwise may be aliasing dir + const lfs2_block_t tpair[2] = {pair[0], pair[1]}; + bool valid = false; + + // check both blocks for the most recent revision + for (int i = 0; i < 2; i++) { + struct lfs21_disk_dir test; + int err = lfs21_bd_read(lfs2, tpair[i], 0, &test, sizeof(test)); + lfs21_dir_fromle32(&test); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + continue; + } + return err; + } + + if (valid && lfs2_scmp(test.rev, dir->d.rev) < 0) { + continue; + } + + if ((0x7fffffff & test.size) < sizeof(test)+4 || + (0x7fffffff & test.size) > lfs2->cfg->block_size) { + continue; + } + + uint32_t crc = 0xffffffff; + lfs21_dir_tole32(&test); + lfs21_crc(&crc, &test, sizeof(test)); + lfs21_dir_fromle32(&test); + err = lfs21_bd_crc(lfs2, tpair[i], sizeof(test), + (0x7fffffff & test.size) - sizeof(test), &crc); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + continue; + } + return err; + } + + if (crc != 0) { + continue; + } + + valid = true; + + // setup dir in case it's valid + dir->pair[0] = tpair[(i+0) % 2]; + dir->pair[1] = tpair[(i+1) % 2]; + dir->off = sizeof(dir->d); + dir->d = test; + } + + if (!valid) { + LFS2_ERROR("Corrupted dir pair at %" PRIu32 " %" PRIu32 , + tpair[0], tpair[1]); + return LFS2_ERR_CORRUPT; + } + + return 0; +} + +static int lfs21_dir_next(lfs2_t *lfs2, lfs21_dir_t *dir, lfs21_entry_t *entry) { + while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) { + if (!(0x80000000 & dir->d.size)) { + entry->off = dir->off; + return LFS2_ERR_NOENT; + } + + int err = lfs21_dir_fetch(lfs2, dir, dir->d.tail); + if (err) { + return err; + } + + dir->off = sizeof(dir->d); + dir->pos += sizeof(dir->d) + 4; + } + + int err = lfs21_bd_read(lfs2, dir->pair[0], dir->off, + &entry->d, sizeof(entry->d)); + lfs21_entry_fromle32(&entry->d); + if (err) { + return err; + } + + entry->off = dir->off; + dir->off += lfs21_entry_size(entry); + dir->pos += lfs21_entry_size(entry); + return 0; +} + +/// littlefs v1 specific operations /// +int lfs21_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data) { + if (lfs2_pair_isnull(lfs2->lfs21->root)) { + return 0; + } + + // iterate over metadata pairs + lfs21_dir_t dir; + lfs21_entry_t entry; + lfs2_block_t cwd[2] = {0, 1}; + + while (true) { + for (int i = 0; i < 2; i++) { + int err = cb(data, cwd[i]); + if (err) { + return err; + } + } + + int err = lfs21_dir_fetch(lfs2, &dir, cwd); + if (err) { + return err; + } + + // iterate over contents + while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) { + err = lfs21_bd_read(lfs2, dir.pair[0], dir.off, + &entry.d, sizeof(entry.d)); + lfs21_entry_fromle32(&entry.d); + if (err) { + return err; + } + + dir.off += lfs21_entry_size(&entry); + if ((0x70 & entry.d.type) == (0x70 & LFS21_TYPE_REG)) { + err = lfs2_ctz_traverse(lfs2, NULL, &lfs2->rcache, + entry.d.u.file.head, entry.d.u.file.size, cb, data); + if (err) { + return err; + } + } + } + + // we also need to check if we contain a threaded v2 directory + lfs2_mdir_t dir2 = {.split=true, .tail={cwd[0], cwd[1]}}; + while (dir2.split) { + err = lfs2_dir_fetch(lfs2, &dir2, dir2.tail); + if (err) { + break; + } + + for (int i = 0; i < 2; i++) { + err = cb(data, dir2.pair[i]); + if (err) { + return err; + } + } + } + + cwd[0] = dir.d.tail[0]; + cwd[1] = dir.d.tail[1]; + + if (lfs2_pair_isnull(cwd)) { + break; + } + } + + return 0; +} + +static int lfs21_moved(lfs2_t *lfs2, const void *e) { + if (lfs2_pair_isnull(lfs2->lfs21->root)) { + return 0; + } + + // skip superblock + lfs21_dir_t cwd; + int err = lfs21_dir_fetch(lfs2, &cwd, (const lfs2_block_t[2]){0, 1}); + if (err) { + return err; + } + + // iterate over all directory directory entries + lfs21_entry_t entry; + while (!lfs2_pair_isnull(cwd.d.tail)) { + err = lfs21_dir_fetch(lfs2, &cwd, cwd.d.tail); + if (err) { + return err; + } + + while (true) { + err = lfs21_dir_next(lfs2, &cwd, &entry); + if (err && err != LFS2_ERR_NOENT) { + return err; + } + + if (err == LFS2_ERR_NOENT) { + break; + } + + if (!(0x80 & entry.d.type) && + memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { + return true; + } + } + } + + return false; +} + +/// Filesystem operations /// +static int lfs21_mount(lfs2_t *lfs2, struct lfs21 *lfs21, + const struct lfs2_config *cfg) { + int err = 0; + { + err = lfs2_init(lfs2, cfg); + if (err) { + return err; + } + + lfs2->lfs21 = lfs21; + lfs2->lfs21->root[0] = 0xffffffff; + lfs2->lfs21->root[1] = 0xffffffff; + + // setup free lookahead + lfs2->free.off = 0; + lfs2->free.size = 0; + lfs2->free.i = 0; + lfs2_alloc_ack(lfs2); + + // load superblock + lfs21_dir_t dir; + lfs21_superblock_t superblock; + err = lfs21_dir_fetch(lfs2, &dir, (const lfs2_block_t[2]){0, 1}); + if (err && err != LFS2_ERR_CORRUPT) { + goto cleanup; + } + + if (!err) { + err = lfs21_bd_read(lfs2, dir.pair[0], sizeof(dir.d), + &superblock.d, sizeof(superblock.d)); + lfs21_superblock_fromle32(&superblock.d); + if (err) { + goto cleanup; + } + + lfs2->lfs21->root[0] = superblock.d.root[0]; + lfs2->lfs21->root[1] = superblock.d.root[1]; + } + + if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { + LFS2_ERROR("Invalid superblock at %d %d", 0, 1); + err = LFS2_ERR_CORRUPT; + goto cleanup; + } + + uint16_t major_version = (0xffff & (superblock.d.version >> 16)); + uint16_t minor_version = (0xffff & (superblock.d.version >> 0)); + if ((major_version != LFS21_DISK_VERSION_MAJOR || + minor_version > LFS21_DISK_VERSION_MINOR)) { + LFS2_ERROR("Invalid version %d.%d", major_version, minor_version); + err = LFS2_ERR_INVAL; + goto cleanup; + } + + return 0; + } + +cleanup: + lfs2_deinit(lfs2); + return err; +} + +static int lfs21_unmount(lfs2_t *lfs2) { + return lfs2_deinit(lfs2); +} + +/// v1 migration /// +int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { + struct lfs21 lfs21; + int err = lfs21_mount(lfs2, &lfs21, cfg); + if (err) { + return err; + } + + { + // iterate through each directory, copying over entries + // into new directory + lfs21_dir_t dir1; + lfs2_mdir_t dir2; + dir1.d.tail[0] = lfs2->lfs21->root[0]; + dir1.d.tail[1] = lfs2->lfs21->root[1]; + while (!lfs2_pair_isnull(dir1.d.tail)) { + // iterate old dir + err = lfs21_dir_fetch(lfs2, &dir1, dir1.d.tail); + if (err) { + goto cleanup; + } + + // create new dir and bind as temporary pretend root + err = lfs2_dir_alloc(lfs2, &dir2); + if (err) { + goto cleanup; + } + + dir2.rev = dir1.d.rev; + dir1.head[0] = dir1.pair[0]; + dir1.head[1] = dir1.pair[1]; + lfs2->root[0] = dir2.pair[0]; + lfs2->root[1] = dir2.pair[1]; + + err = lfs2_dir_commit(lfs2, &dir2, NULL, 0); + if (err) { + goto cleanup; + } + + while (true) { + lfs21_entry_t entry1; + err = lfs21_dir_next(lfs2, &dir1, &entry1); + if (err && err != LFS2_ERR_NOENT) { + goto cleanup; + } + + if (err == LFS2_ERR_NOENT) { + break; + } + + // check that entry has not been moved + if (entry1.d.type & 0x80) { + int moved = lfs21_moved(lfs2, &entry1.d.u); + if (moved < 0) { + err = moved; + goto cleanup; + } + + if (moved) { + continue; + } + + entry1.d.type &= ~0x80; + } + + // also fetch name + char name[LFS2_NAME_MAX+1]; + memset(name, 0, sizeof(name)); + err = lfs21_bd_read(lfs2, dir1.pair[0], + entry1.off + 4+entry1.d.elen+entry1.d.alen, + name, entry1.d.nlen); + if (err) { + goto cleanup; + } + + bool isdir = (entry1.d.type == LFS21_TYPE_DIR); + + // create entry in new dir + err = lfs2_dir_fetch(lfs2, &dir2, lfs2->root); + if (err) { + goto cleanup; + } + + uint16_t id; + err = lfs2_dir_find(lfs2, &dir2, &(const char*){name}, &id); + if (!(err == LFS2_ERR_NOENT && id != 0x3ff)) { + err = (err < 0) ? err : LFS2_ERR_EXIST; + goto cleanup; + } + + lfs21_entry_tole32(&entry1.d); + err = lfs2_dir_commit(lfs2, &dir2, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_CREATE, id, 0), NULL}, + {LFS2_MKTAG( + isdir ? LFS2_TYPE_DIR : LFS2_TYPE_REG, + id, entry1.d.nlen), name}, + {LFS2_MKTAG( + isdir ? LFS2_TYPE_DIRSTRUCT : LFS2_TYPE_CTZSTRUCT, + id, sizeof(&entry1.d.u)), &entry1.d.u})); + lfs21_entry_fromle32(&entry1.d); + if (err) { + goto cleanup; + } + } + + if (!lfs2_pair_isnull(dir1.d.tail)) { + // find last block and update tail to thread into fs + err = lfs2_dir_fetch(lfs2, &dir2, lfs2->root); + if (err) { + goto cleanup; + } + + while (dir2.split) { + err = lfs2_dir_fetch(lfs2, &dir2, dir2.tail); + if (err) { + goto cleanup; + } + } + + lfs2_pair_tole32(dir2.pair); + err = lfs2_dir_commit(lfs2, &dir2, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 0), + dir1.d.tail})); + lfs2_pair_fromle32(dir2.pair); + if (err) { + goto cleanup; + } + } + + // Copy over first block to thread into fs. Unfortunately + // if this fails there is not much we can do. + LFS2_DEBUG("Migrating %"PRIu32" %"PRIu32" -> %"PRIu32" %"PRIu32, + lfs2->root[0], lfs2->root[1], dir1.head[0], dir1.head[1]); + + err = lfs2_bd_erase(lfs2, dir1.head[1]); + if (err) { + goto cleanup; + } + + err = lfs2_dir_fetch(lfs2, &dir2, lfs2->root); + if (err) { + goto cleanup; + } + + for (lfs2_off_t i = 0; i < dir2.off; i++) { + uint8_t dat; + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, dir2.off, + dir2.pair[0], i, &dat, 1); + if (err) { + goto cleanup; + } + + err = lfs2_bd_prog(lfs2, + &lfs2->pcache, &lfs2->rcache, true, + dir1.head[1], i, &dat, 1); + if (err) { + goto cleanup; + } + } + } + + // Create new superblock. This marks a successful migration! + err = lfs21_dir_fetch(lfs2, &dir1, (const lfs2_block_t[2]){0, 1}); + if (err) { + goto cleanup; + } + + dir2.pair[0] = dir1.pair[0]; + dir2.pair[1] = dir1.pair[1]; + dir2.rev = dir1.d.rev; + dir2.off = sizeof(dir2.rev); + dir2.etag = 0xffffffff; + dir2.count = 0; + dir2.tail[0] = lfs2->lfs21->root[0]; + dir2.tail[1] = lfs2->lfs21->root[1]; + dir2.erased = false; + dir2.split = true; + + lfs2_superblock_t superblock = { + .version = LFS2_DISK_VERSION, + .block_size = lfs2->cfg->block_size, + .block_count = lfs2->cfg->block_count, + .name_max = lfs2->name_max, + .file_max = lfs2->file_max, + .attr_max = lfs2->attr_max, + }; + + lfs2_superblock_tole32(&superblock); + err = lfs2_dir_commit(lfs2, &dir2, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_CREATE, 0, 0), NULL}, + {LFS2_MKTAG(LFS2_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, + {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock})); + if (err) { + goto cleanup; + } + + // sanity check that fetch works + err = lfs2_dir_fetch(lfs2, &dir2, (const lfs2_block_t[2]){0, 1}); + if (err) { + goto cleanup; + } + } + +cleanup: + lfs21_unmount(lfs2); + return err; +} + +#endif diff --git a/lfs.h b/lfs2.h similarity index 56% rename from lfs.h rename to lfs2.h index ae991e0..d4ee66b 100644 --- a/lfs.h +++ b/lfs2.h @@ -4,8 +4,8 @@ * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef LFS_H -#define LFS_H +#ifndef LFS2_H +#define LFS2_H #include #include @@ -21,174 +21,174 @@ extern "C" // Software library version // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS_VERSION 0x00020000 -#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) -#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) +#define LFS2_VERSION 0x00020000 +#define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16)) +#define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0)) // Version of On-disk data structures // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS_DISK_VERSION 0x00020000 -#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) -#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0)) +#define LFS2_DISK_VERSION 0x00020000 +#define LFS2_DISK_VERSION_MAJOR (0xffff & (LFS2_DISK_VERSION >> 16)) +#define LFS2_DISK_VERSION_MINOR (0xffff & (LFS2_DISK_VERSION >> 0)) /// Definitions /// // Type definitions -typedef uint32_t lfs_size_t; -typedef uint32_t lfs_off_t; +typedef uint32_t lfs2_size_t; +typedef uint32_t lfs2_off_t; -typedef int32_t lfs_ssize_t; -typedef int32_t lfs_soff_t; +typedef int32_t lfs2_ssize_t; +typedef int32_t lfs2_soff_t; -typedef uint32_t lfs_block_t; +typedef uint32_t lfs2_block_t; // Maximum name size in bytes, may be redefined to reduce the size of the // info struct. Limited to <= 1022. Stored in superblock and must be // respected by other littlefs drivers. -#ifndef LFS_NAME_MAX -#define LFS_NAME_MAX 255 +#ifndef LFS2_NAME_MAX +#define LFS2_NAME_MAX 255 #endif // Maximum size of a file in bytes, may be redefined to limit to support other // drivers. Limited on disk to <= 4294967296. However, above 2147483647 the -// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return +// functions lfs2_file_seek, lfs2_file_size, and lfs2_file_tell will return // incorrect values due to using signed integers. Stored in superblock and // must be respected by other littlefs drivers. -#ifndef LFS_FILE_MAX -#define LFS_FILE_MAX 2147483647 +#ifndef LFS2_FILE_MAX +#define LFS2_FILE_MAX 2147483647 #endif // Maximum size of custom attributes in bytes, may be redefined, but there is -// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. -#ifndef LFS_ATTR_MAX -#define LFS_ATTR_MAX 1022 +// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022. +#ifndef LFS2_ATTR_MAX +#define LFS2_ATTR_MAX 1022 #endif // Possible error codes, these are negative to allow // valid positive return values -enum lfs_error { - LFS_ERR_OK = 0, // No error - LFS_ERR_IO = -5, // Error during device operation - LFS_ERR_CORRUPT = -84, // Corrupted - LFS_ERR_NOENT = -2, // No directory entry - LFS_ERR_EXIST = -17, // Entry already exists - LFS_ERR_NOTDIR = -20, // Entry is not a dir - LFS_ERR_ISDIR = -21, // Entry is a dir - LFS_ERR_NOTEMPTY = -39, // Dir is not empty - LFS_ERR_BADF = -9, // Bad file number - LFS_ERR_FBIG = -27, // File too large - LFS_ERR_INVAL = -22, // Invalid parameter - LFS_ERR_NOSPC = -28, // No space left on device - LFS_ERR_NOMEM = -12, // No more memory available - LFS_ERR_NOATTR = -61, // No data/attr available - LFS_ERR_NAMETOOLONG = -36, // File name too long +enum lfs2_error { + LFS2_ERR_OK = 0, // No error + LFS2_ERR_IO = -5, // Error during device operation + LFS2_ERR_CORRUPT = -84, // Corrupted + LFS2_ERR_NOENT = -2, // No directory entry + LFS2_ERR_EXIST = -17, // Entry already exists + LFS2_ERR_NOTDIR = -20, // Entry is not a dir + LFS2_ERR_ISDIR = -21, // Entry is a dir + LFS2_ERR_NOTEMPTY = -39, // Dir is not empty + LFS2_ERR_BADF = -9, // Bad file number + LFS2_ERR_FBIG = -27, // File too large + LFS2_ERR_INVAL = -22, // Invalid parameter + LFS2_ERR_NOSPC = -28, // No space left on device + LFS2_ERR_NOMEM = -12, // No more memory available + LFS2_ERR_NOATTR = -61, // No data/attr available + LFS2_ERR_NAMETOOLONG = -36, // File name too long }; // File types -enum lfs_type { +enum lfs2_type { // file types - LFS_TYPE_REG = 0x001, - LFS_TYPE_DIR = 0x002, + LFS2_TYPE_REG = 0x001, + LFS2_TYPE_DIR = 0x002, // internally used types - LFS_TYPE_SPLICE = 0x400, - LFS_TYPE_NAME = 0x000, - LFS_TYPE_STRUCT = 0x200, - LFS_TYPE_USERATTR = 0x300, - LFS_TYPE_FROM = 0x100, - LFS_TYPE_TAIL = 0x600, - LFS_TYPE_GLOBALS = 0x700, - LFS_TYPE_CRC = 0x500, + LFS2_TYPE_SPLICE = 0x400, + LFS2_TYPE_NAME = 0x000, + LFS2_TYPE_STRUCT = 0x200, + LFS2_TYPE_USERATTR = 0x300, + LFS2_TYPE_FROM = 0x100, + LFS2_TYPE_TAIL = 0x600, + LFS2_TYPE_GLOBALS = 0x700, + LFS2_TYPE_CRC = 0x500, // internally used type specializations - LFS_TYPE_CREATE = 0x401, - LFS_TYPE_DELETE = 0x4ff, - LFS_TYPE_SUPERBLOCK = 0x0ff, - LFS_TYPE_DIRSTRUCT = 0x200, - LFS_TYPE_CTZSTRUCT = 0x202, - LFS_TYPE_INLINESTRUCT = 0x201, - LFS_TYPE_SOFTTAIL = 0x600, - LFS_TYPE_HARDTAIL = 0x601, - LFS_TYPE_MOVESTATE = 0x7ff, + LFS2_TYPE_CREATE = 0x401, + LFS2_TYPE_DELETE = 0x4ff, + LFS2_TYPE_SUPERBLOCK = 0x0ff, + LFS2_TYPE_DIRSTRUCT = 0x200, + LFS2_TYPE_CTZSTRUCT = 0x202, + LFS2_TYPE_INLINESTRUCT = 0x201, + LFS2_TYPE_SOFTTAIL = 0x600, + LFS2_TYPE_HARDTAIL = 0x601, + LFS2_TYPE_MOVESTATE = 0x7ff, // internal chip sources - LFS_FROM_NOOP = 0x000, - LFS_FROM_MOVE = 0x101, - LFS_FROM_USERATTRS = 0x102, + LFS2_FROM_NOOP = 0x000, + LFS2_FROM_MOVE = 0x101, + LFS2_FROM_USERATTRS = 0x102, }; // File open flags -enum lfs_open_flags { +enum lfs2_open_flags { // open flags - LFS_O_RDONLY = 1, // Open a file as read only - LFS_O_WRONLY = 2, // Open a file as write only - LFS_O_RDWR = 3, // Open a file as read and write - LFS_O_CREAT = 0x0100, // Create a file if it does not exist - LFS_O_EXCL = 0x0200, // Fail if a file already exists - LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size - LFS_O_APPEND = 0x0800, // Move to end of file on every write + LFS2_O_RDONLY = 1, // Open a file as read only + LFS2_O_WRONLY = 2, // Open a file as write only + LFS2_O_RDWR = 3, // Open a file as read and write + LFS2_O_CREAT = 0x0100, // Create a file if it does not exist + LFS2_O_EXCL = 0x0200, // Fail if a file already exists + LFS2_O_TRUNC = 0x0400, // Truncate the existing file to zero size + LFS2_O_APPEND = 0x0800, // Move to end of file on every write // internally used flags - LFS_F_DIRTY = 0x010000, // File does not match storage - LFS_F_WRITING = 0x020000, // File has been written since last flush - LFS_F_READING = 0x040000, // File has been read since last flush - LFS_F_ERRED = 0x080000, // An error occured during write - LFS_F_INLINE = 0x100000, // Currently inlined in directory entry + LFS2_F_DIRTY = 0x010000, // File does not match storage + LFS2_F_WRITING = 0x020000, // File has been written since last flush + LFS2_F_READING = 0x040000, // File has been read since last flush + LFS2_F_ERRED = 0x080000, // An error occured during write + LFS2_F_INLINE = 0x100000, // Currently inlined in directory entry }; // File seek flags -enum lfs_whence_flags { - LFS_SEEK_SET = 0, // Seek relative to an absolute position - LFS_SEEK_CUR = 1, // Seek relative to the current file position - LFS_SEEK_END = 2, // Seek relative to the end of the file +enum lfs2_whence_flags { + LFS2_SEEK_SET = 0, // Seek relative to an absolute position + LFS2_SEEK_CUR = 1, // Seek relative to the current file position + LFS2_SEEK_END = 2, // Seek relative to the end of the file }; // Configuration provided during initialization of the littlefs -struct lfs_config { +struct lfs2_config { // Opaque user provided context that can be used to pass // information to the block device operations void *context; // Read a region in a block. Negative error codes are propogated // to the user. - int (*read)(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size); + int (*read)(const struct lfs2_config *c, lfs2_block_t block, + lfs2_off_t off, void *buffer, lfs2_size_t size); // Program a region in a block. The block must have previously // been erased. Negative error codes are propogated to the user. - // May return LFS_ERR_CORRUPT if the block should be considered bad. - int (*prog)(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size); + // May return LFS2_ERR_CORRUPT if the block should be considered bad. + int (*prog)(const struct lfs2_config *c, lfs2_block_t block, + lfs2_off_t off, const void *buffer, lfs2_size_t size); // Erase a block. A block must be erased before being programmed. // The state of an erased block is undefined. Negative error codes // are propogated to the user. - // May return LFS_ERR_CORRUPT if the block should be considered bad. - int (*erase)(const struct lfs_config *c, lfs_block_t block); + // May return LFS2_ERR_CORRUPT if the block should be considered bad. + int (*erase)(const struct lfs2_config *c, lfs2_block_t block); // Sync the state of the underlying block device. Negative error codes // are propogated to the user. - int (*sync)(const struct lfs_config *c); + int (*sync)(const struct lfs2_config *c); // Minimum size of a block read. All read operations will be a // multiple of this value. - lfs_size_t read_size; + lfs2_size_t read_size; // Minimum size of a block program. All program operations will be a // multiple of this value. - lfs_size_t prog_size; + lfs2_size_t prog_size; // Size of an erasable block. This does not impact ram consumption and // may be larger than the physical erase size. However, non-inlined files // take up at minimum one block. Must be a multiple of the read // and program sizes. - lfs_size_t block_size; + lfs2_size_t block_size; // Number of erasable blocks on the device. - lfs_size_t block_count; + lfs2_size_t block_count; // Number of erase cycles before we should move data to another block. // May be zero, in which case no block-level wear-leveling is performed. @@ -199,62 +199,62 @@ struct lfs_config { // cache per file. Larger caches can improve performance by storing more // data and reducing the number of disk accesses. Must be a multiple of // the read and program sizes, and a factor of the block size. - lfs_size_t cache_size; + lfs2_size_t cache_size; // Size of the lookahead buffer in bytes. A larger lookahead buffer // increases the number of blocks found during an allocation pass. The // lookahead buffer is stored as a compact bitmap, so each byte of RAM // can track 8 blocks. Must be a multiple of 4. - lfs_size_t lookahead_size; + lfs2_size_t lookahead_size; // Optional statically allocated read buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. + // By default lfs2_malloc is used to allocate this buffer. void *read_buffer; // Optional statically allocated program buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. + // By default lfs2_malloc is used to allocate this buffer. void *prog_buffer; // Optional statically allocated lookahead buffer. Must be lookahead_size - // and aligned to a 64-bit boundary. By default lfs_malloc is used to + // and aligned to a 64-bit boundary. By default lfs2_malloc is used to // allocate this buffer. void *lookahead_buffer; // Optional upper limit on length of file names in bytes. No downside for // larger names except the size of the info struct which is controlled by - // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in + // the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX when zero. Stored in // superblock and must be respected by other littlefs drivers. - lfs_size_t name_max; + lfs2_size_t name_max; // Optional upper limit on files in bytes. No downside for larger files - // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored + // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX when zero. Stored // in superblock and must be respected by other littlefs drivers. - lfs_size_t file_max; + lfs2_size_t file_max; // Optional upper limit on custom attributes in bytes. No downside for - // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to - // LFS_ATTR_MAX when zero. - lfs_size_t attr_max; + // larger attributes size but must be <= LFS2_ATTR_MAX. Defaults to + // LFS2_ATTR_MAX when zero. + lfs2_size_t attr_max; }; // File info structure -struct lfs_info { - // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR +struct lfs2_info { + // Type of the file, either LFS2_TYPE_REG or LFS2_TYPE_DIR uint8_t type; // Size of the file, only valid for REG files. Limited to 32-bits. - lfs_size_t size; + lfs2_size_t size; // Name of the file stored as a null-terminated string. Limited to - // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to - // reduce RAM. LFS_NAME_MAX is stored in superblock and must be + // LFS2_NAME_MAX+1, which can be changed by redefining LFS2_NAME_MAX to + // reduce RAM. LFS2_NAME_MAX is stored in superblock and must be // respected by other littlefs drivers. - char name[LFS_NAME_MAX+1]; + char name[LFS2_NAME_MAX+1]; }; // Custom attribute structure, used to describe custom attributes // committed atomically during file writes. -struct lfs_attr { +struct lfs2_attr { // 8-bit type of attribute, provided by user and used to // identify the attribute uint8_t type; @@ -262,14 +262,14 @@ struct lfs_attr { // Pointer to buffer containing the attribute void *buffer; - // Size of attribute in bytes, limited to LFS_ATTR_MAX - lfs_size_t size; + // Size of attribute in bytes, limited to LFS2_ATTR_MAX + lfs2_size_t size; }; -// Optional configuration provided during lfs_file_opencfg -struct lfs_file_config { +// Optional configuration provided during lfs2_file_opencfg +struct lfs2_file_config { // Optional statically allocated file buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. + // By default lfs2_malloc is used to allocate this buffer. void *buffer; // Optional list of custom attributes related to the file. If the file @@ -279,113 +279,113 @@ struct lfs_file_config { // write occurs atomically with update to the file's contents. // // Custom attributes are uniquely identified by an 8-bit type and limited - // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller + // to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller // than the buffer, it will be padded with zeros. If the stored attribute // is larger, then it will be silently truncated. If the attribute is not // found, it will be created implicitly. - struct lfs_attr *attrs; + struct lfs2_attr *attrs; // Number of custom attributes in the list - lfs_size_t attr_count; + lfs2_size_t attr_count; }; /// internal littlefs data structures /// -typedef struct lfs_cache { - lfs_block_t block; - lfs_off_t off; - lfs_size_t size; +typedef struct lfs2_cache { + lfs2_block_t block; + lfs2_off_t off; + lfs2_size_t size; uint8_t *buffer; -} lfs_cache_t; +} lfs2_cache_t; -typedef struct lfs_mdir { - lfs_block_t pair[2]; +typedef struct lfs2_mdir { + lfs2_block_t pair[2]; uint32_t rev; - lfs_off_t off; + lfs2_off_t off; uint32_t etag; uint16_t count; bool erased; bool split; - lfs_block_t tail[2]; -} lfs_mdir_t; + lfs2_block_t tail[2]; +} lfs2_mdir_t; // littlefs directory type -typedef struct lfs_dir { - struct lfs_dir *next; +typedef struct lfs2_dir { + struct lfs2_dir *next; uint16_t id; uint8_t type; - lfs_mdir_t m; + lfs2_mdir_t m; - lfs_off_t pos; - lfs_block_t head[2]; -} lfs_dir_t; + lfs2_off_t pos; + lfs2_block_t head[2]; +} lfs2_dir_t; // littlefs file type -typedef struct lfs_file { - struct lfs_file *next; +typedef struct lfs2_file { + struct lfs2_file *next; uint16_t id; uint8_t type; - lfs_mdir_t m; + lfs2_mdir_t m; - struct lfs_ctz { - lfs_block_t head; - lfs_size_t size; + struct lfs2_ctz { + lfs2_block_t head; + lfs2_size_t size; } ctz; uint32_t flags; - lfs_off_t pos; - lfs_block_t block; - lfs_off_t off; - lfs_cache_t cache; + lfs2_off_t pos; + lfs2_block_t block; + lfs2_off_t off; + lfs2_cache_t cache; - const struct lfs_file_config *cfg; -} lfs_file_t; + const struct lfs2_file_config *cfg; +} lfs2_file_t; -typedef struct lfs_superblock { +typedef struct lfs2_superblock { uint32_t version; - lfs_size_t block_size; - lfs_size_t block_count; - lfs_size_t name_max; - lfs_size_t file_max; - lfs_size_t attr_max; -} lfs_superblock_t; + lfs2_size_t block_size; + lfs2_size_t block_count; + lfs2_size_t name_max; + lfs2_size_t file_max; + lfs2_size_t attr_max; +} lfs2_superblock_t; // The littlefs filesystem type -typedef struct lfs { - lfs_cache_t rcache; - lfs_cache_t pcache; +typedef struct lfs2 { + lfs2_cache_t rcache; + lfs2_cache_t pcache; - lfs_block_t root[2]; - struct lfs_mlist { - struct lfs_mlist *next; + lfs2_block_t root[2]; + struct lfs2_mlist { + struct lfs2_mlist *next; uint16_t id; uint8_t type; - lfs_mdir_t m; + lfs2_mdir_t m; } *mlist; uint32_t seed; - struct lfs_gstate { + struct lfs2_gstate { uint32_t tag; - lfs_block_t pair[2]; + lfs2_block_t pair[2]; } gstate, gpending, gdelta; - struct lfs_free { - lfs_block_t off; - lfs_block_t size; - lfs_block_t i; - lfs_block_t ack; + struct lfs2_free { + lfs2_block_t off; + lfs2_block_t size; + lfs2_block_t i; + lfs2_block_t ack; uint32_t *buffer; } free; - const struct lfs_config *cfg; - lfs_size_t name_max; - lfs_size_t file_max; - lfs_size_t attr_max; + const struct lfs2_config *cfg; + lfs2_size_t name_max; + lfs2_size_t file_max; + lfs2_size_t attr_max; -#ifdef LFS_MIGRATE - struct lfs1 *lfs1; +#ifdef LFS2_MIGRATE + struct lfs21 *lfs21; #endif -} lfs_t; +} lfs2_t; /// Filesystem functions /// @@ -397,23 +397,23 @@ typedef struct lfs { // be zeroed for defaults and backwards compatibility. // // Returns a negative error code on failure. -int lfs_format(lfs_t *lfs, const struct lfs_config *config); +int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *config); // Mounts a littlefs // // Requires a littlefs object and config struct. Multiple filesystems // may be mounted simultaneously with multiple littlefs objects. Both -// lfs and config must be allocated while mounted. The config struct must +// lfs2 and config must be allocated while mounted. The config struct must // be zeroed for defaults and backwards compatibility. // // Returns a negative error code on failure. -int lfs_mount(lfs_t *lfs, const struct lfs_config *config); +int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *config); // Unmounts a littlefs // // Does nothing besides releasing any allocated resources. // Returns a negative error code on failure. -int lfs_unmount(lfs_t *lfs); +int lfs2_unmount(lfs2_t *lfs2); /// General operations /// @@ -421,7 +421,7 @@ int lfs_unmount(lfs_t *lfs); // // If removing a directory, the directory must be empty. // Returns a negative error code on failure. -int lfs_remove(lfs_t *lfs, const char *path); +int lfs2_remove(lfs2_t *lfs2, const char *path); // Rename or move a file or directory // @@ -429,45 +429,45 @@ int lfs_remove(lfs_t *lfs, const char *path); // If the destination is a directory, the directory must be empty. // // Returns a negative error code on failure. -int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); +int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath); // Find info about a file or directory // // Fills out the info structure, based on the specified file or directory. // Returns a negative error code on failure. -int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); +int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info); // Get a custom attribute // // Custom attributes are uniquely identified by an 8-bit type and limited -// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than +// to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller than // the buffer, it will be padded with zeros. If the stored attribute is larger, // then it will be silently truncated. If no attribute is found, the error -// LFS_ERR_NOATTR is returned and the buffer is filled with zeros. +// LFS2_ERR_NOATTR is returned and the buffer is filled with zeros. // // Returns the size of the attribute, or a negative error code on failure. // Note, the returned size is the size of the attribute on disk, irrespective // of the size of the buffer. This can be used to dynamically allocate a buffer // or check for existance. -lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, - uint8_t type, void *buffer, lfs_size_t size); +lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, + uint8_t type, void *buffer, lfs2_size_t size); // Set custom attributes // // Custom attributes are uniquely identified by an 8-bit type and limited -// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be +// to LFS2_ATTR_MAX bytes. If an attribute is not found, it will be // implicitly created. // // Returns a negative error code on failure. -int lfs_setattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size); +int lfs2_setattr(lfs2_t *lfs2, const char *path, + uint8_t type, const void *buffer, lfs2_size_t size); // Removes a custom attribute // // If an attribute is not found, nothing happens. // // Returns a negative error code on failure. -int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); +int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type); /// File operations /// @@ -475,25 +475,25 @@ int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); // Open a file // // The mode that the file is opened in is determined by the flags, which -// are values from the enum lfs_open_flags that are bitwise-ored together. +// are values from the enum lfs2_open_flags that are bitwise-ored together. // // Returns a negative error code on failure. -int lfs_file_open(lfs_t *lfs, lfs_file_t *file, +int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags); // Open a file with extra configuration // // The mode that the file is opened in is determined by the flags, which -// are values from the enum lfs_open_flags that are bitwise-ored together. +// are values from the enum lfs2_open_flags that are bitwise-ored together. // // The config struct provides additional config options per file as described // above. The config struct must be allocated while the file is open, and the // config struct must be zeroed for defaults and backwards compatibility. // // Returns a negative error code on failure. -int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, +int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags, - const struct lfs_file_config *config); + const struct lfs2_file_config *config); // Close a file // @@ -501,20 +501,20 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, // sync had been called and releases any allocated resources. // // Returns a negative error code on failure. -int lfs_file_close(lfs_t *lfs, lfs_file_t *file); +int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file); // Synchronize a file on storage // // Any pending writes are written out to storage. // Returns a negative error code on failure. -int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); +int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file); // Read data from file // // Takes a buffer and size indicating where to store the read data. // Returns the number of bytes read, or a negative error code on failure. -lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, - void *buffer, lfs_size_t size); +lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, + void *buffer, lfs2_size_t size); // Write data to file // @@ -522,38 +522,38 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, // actually be updated on the storage until either sync or close is called. // // Returns the number of bytes written, or a negative error code on failure. -lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, - const void *buffer, lfs_size_t size); +lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, + const void *buffer, lfs2_size_t size); // Change the position of the file // // The change in position is determined by the offset and whence flag. // Returns the old position of the file, or a negative error code on failure. -lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, - lfs_soff_t off, int whence); +lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, + lfs2_soff_t off, int whence); // Truncates the size of the file to the specified size // // Returns a negative error code on failure. -int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); +int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size); // Return the position of the file // -// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) +// Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_CUR) // Returns the position of the file, or a negative error code on failure. -lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); +lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file); // Change the position of the file to the beginning of the file // -// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) +// Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_CUR) // Returns a negative error code on failure. -int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); +int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file); // Return the size of the file // -// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END) +// Similar to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_END) // Returns the size of the file, or a negative error code on failure. -lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); +lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file); /// Directory operations /// @@ -561,25 +561,25 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); // Create a directory // // Returns a negative error code on failure. -int lfs_mkdir(lfs_t *lfs, const char *path); +int lfs2_mkdir(lfs2_t *lfs2, const char *path); // Open a directory // // Once open a directory can be used with read to iterate over files. // Returns a negative error code on failure. -int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path); +int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path); // Close a directory // // Releases any allocated resources. // Returns a negative error code on failure. -int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); +int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir); // Read an entry in the directory // // Fills out the info structure, based on the specified file or directory. // Returns a negative error code on failure. -int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); +int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info); // Change the position of the directory // @@ -587,7 +587,7 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); // an absolute offset in the directory seek. // // Returns a negative error code on failure. -int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); +int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off); // Return the position of the directory // @@ -595,12 +595,12 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); // sense, but does indicate the current position in the directory iteration. // // Returns the position of the directory, or a negative error code on failure. -lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); +lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir); // Change the position of the directory to the beginning of the directory // // Returns a negative error code on failure. -int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); +int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir); /// Filesystem-level filesystem operations @@ -611,7 +611,7 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); // size may be larger than the filesystem actually is. // // Returns the number of allocated blocks, or a negative error code on failure. -lfs_ssize_t lfs_fs_size(lfs_t *lfs); +lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2); // Traverse through all blocks in use by the filesystem // @@ -620,12 +620,12 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs); // blocks are in use or how much of the storage is available. // // Returns a negative error code on failure. -int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); +int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); -#ifdef LFS_MIGRATE +#ifdef LFS2_MIGRATE // Attempts to migrate a previous version of littlefs // -// Behaves similarly to the lfs_format function. Attempts to mount +// Behaves similarly to the lfs2_format function. Attempts to mount // the previous version of littlefs and update the filesystem so it can be // mounted with the current version of littlefs. // @@ -634,7 +634,7 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); // be zeroed for defaults and backwards compatibility. // // Returns a negative error code on failure. -int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); +int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg); #endif diff --git a/lfs_util.c b/lfs2_util.c similarity index 84% rename from lfs_util.c rename to lfs2_util.c index 0b60e3b..083a99c 100644 --- a/lfs_util.c +++ b/lfs2_util.c @@ -1,17 +1,17 @@ /* - * lfs util functions + * lfs2 util functions * * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ -#include "lfs_util.h" +#include "lfs2_util.h" // Only compile if user does not provide custom config -#ifndef LFS_CONFIG +#ifndef LFS2_CONFIG // Software CRC implementation with small lookup table -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { +uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { static const uint32_t rtable[16] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, diff --git a/lfs_util.h b/lfs2_util.h similarity index 59% rename from lfs_util.h rename to lfs2_util.h index 28b1400..c8cc316 100644 --- a/lfs_util.h +++ b/lfs2_util.h @@ -1,22 +1,22 @@ /* - * lfs utility functions + * lfs2 utility functions * * Copyright (c) 2017, Arm Limited. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef LFS_UTIL_H -#define LFS_UTIL_H +#ifndef LFS2_UTIL_H +#define LFS2_UTIL_H -// Users can override lfs_util.h with their own configuration by defining -// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). +// Users can override lfs2_util.h with their own configuration by defining +// LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h). // -// If LFS_CONFIG is used, none of the default utils will be emitted and must be -// provided by the config file. To start, I would suggest copying lfs_util.h +// If LFS2_CONFIG is used, none of the default utils will be emitted and must be +// provided by the config file. To start, I would suggest copying lfs2_util.h // and modifying as needed. -#ifdef LFS_CONFIG -#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) -#define LFS_STRINGIZE2(x) #x -#include LFS_STRINGIZE(LFS_CONFIG) +#ifdef LFS2_CONFIG +#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x) +#define LFS2_STRINGIZE2(x) #x +#include LFS2_STRINGIZE(LFS2_CONFIG) #else // System includes @@ -25,13 +25,13 @@ #include #include -#ifndef LFS_NO_MALLOC +#ifndef LFS2_NO_MALLOC #include #endif -#ifndef LFS_NO_ASSERT +#ifndef LFS2_NO_ASSERT #include #endif -#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) +#if !defined(LFS2_NO_DEBUG) || !defined(LFS2_NO_WARN) || !defined(LFS2_NO_ERROR) #include #endif @@ -46,60 +46,60 @@ extern "C" // code footprint // Logging functions -#ifndef LFS_NO_DEBUG -#define LFS_DEBUG(fmt, ...) \ - printf("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#ifndef LFS2_NO_DEBUG +#define LFS2_DEBUG(fmt, ...) \ + printf("lfs2 debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) #else -#define LFS_DEBUG(fmt, ...) +#define LFS2_DEBUG(fmt, ...) #endif -#ifndef LFS_NO_WARN -#define LFS_WARN(fmt, ...) \ - printf("lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#ifndef LFS2_NO_WARN +#define LFS2_WARN(fmt, ...) \ + printf("lfs2 warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) #else -#define LFS_WARN(fmt, ...) +#define LFS2_WARN(fmt, ...) #endif -#ifndef LFS_NO_ERROR -#define LFS_ERROR(fmt, ...) \ - printf("lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#ifndef LFS2_NO_ERROR +#define LFS2_ERROR(fmt, ...) \ + printf("lfs2 error:%d: " fmt "\n", __LINE__, __VA_ARGS__) #else -#define LFS_ERROR(fmt, ...) +#define LFS2_ERROR(fmt, ...) #endif // Runtime assertions -#ifndef LFS_NO_ASSERT -#define LFS_ASSERT(test) assert(test) +#ifndef LFS2_NO_ASSERT +#define LFS2_ASSERT(test) assert(test) #else -#define LFS_ASSERT(test) +#define LFS2_ASSERT(test) #endif // Builtin functions, these may be replaced by more efficient -// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more +// toolchain-specific implementations. LFS2_NO_INTRINSICS falls back to a more // expensive basic C implementation for debugging purposes // Min/max functions for unsigned 32-bit numbers -static inline uint32_t lfs_max(uint32_t a, uint32_t b) { +static inline uint32_t lfs2_max(uint32_t a, uint32_t b) { return (a > b) ? a : b; } -static inline uint32_t lfs_min(uint32_t a, uint32_t b) { +static inline uint32_t lfs2_min(uint32_t a, uint32_t b) { return (a < b) ? a : b; } // Align to nearest multiple of a size -static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { +static inline uint32_t lfs2_aligndown(uint32_t a, uint32_t alignment) { return a - (a % alignment); } -static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { - return lfs_aligndown(a + alignment-1, alignment); +static inline uint32_t lfs2_alignup(uint32_t a, uint32_t alignment) { + return lfs2_aligndown(a + alignment-1, alignment); } // Find the next smallest power of 2 less than or equal to a -static inline uint32_t lfs_npw2(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) +static inline uint32_t lfs2_npw2(uint32_t a) { +#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) return 32 - __builtin_clz(a-1); #else uint32_t r = 0; @@ -114,18 +114,18 @@ static inline uint32_t lfs_npw2(uint32_t a) { } // Count the number of trailing binary zeros in a -// lfs_ctz(0) may be undefined -static inline uint32_t lfs_ctz(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) +// lfs2_ctz(0) may be undefined +static inline uint32_t lfs2_ctz(uint32_t a) { +#if !defined(LFS2_NO_INTRINSICS) && defined(__GNUC__) return __builtin_ctz(a); #else - return lfs_npw2((a & -a) + 1) - 1; + return lfs2_npw2((a & -a) + 1) - 1; #endif } // Count the number of binary ones in a -static inline uint32_t lfs_popc(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) +static inline uint32_t lfs2_popc(uint32_t a) { +#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) return __builtin_popcount(a); #else a = a - ((a >> 1) & 0x55555555); @@ -136,18 +136,18 @@ static inline uint32_t lfs_popc(uint32_t a) { // Find the sequence comparison of a and b, this is the distance // between a and b ignoring overflow -static inline int lfs_scmp(uint32_t a, uint32_t b) { +static inline int lfs2_scmp(uint32_t a, uint32_t b) { return (int)(unsigned)(a - b); } // Convert between 32-bit little-endian and native order -static inline uint32_t lfs_fromle32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ +static inline uint32_t lfs2_fromle32(uint32_t a) { +#if !defined(LFS2_NO_INTRINSICS) && ( \ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) return a; -#elif !defined(LFS_NO_INTRINSICS) && ( \ +#elif !defined(LFS2_NO_INTRINSICS) && ( \ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) @@ -160,18 +160,18 @@ static inline uint32_t lfs_fromle32(uint32_t a) { #endif } -static inline uint32_t lfs_tole32(uint32_t a) { - return lfs_fromle32(a); +static inline uint32_t lfs2_tole32(uint32_t a) { + return lfs2_fromle32(a); } // Convert between 32-bit big-endian and native order -static inline uint32_t lfs_frombe32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ +static inline uint32_t lfs2_frombe32(uint32_t a) { +#if !defined(LFS2_NO_INTRINSICS) && ( \ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) return __builtin_bswap32(a); -#elif !defined(LFS_NO_INTRINSICS) && ( \ +#elif !defined(LFS2_NO_INTRINSICS) && ( \ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) @@ -184,17 +184,17 @@ static inline uint32_t lfs_frombe32(uint32_t a) { #endif } -static inline uint32_t lfs_tobe32(uint32_t a) { - return lfs_frombe32(a); +static inline uint32_t lfs2_tobe32(uint32_t a) { + return lfs2_frombe32(a); } // Calculate CRC-32 with polynomial = 0x04c11db7 -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); +uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size); // Allocate memory, only used if buffers are not provided to littlefs // Note, memory must be 64-bit aligned -static inline void *lfs_malloc(size_t size) { -#ifndef LFS_NO_MALLOC +static inline void *lfs2_malloc(size_t size) { +#ifndef LFS2_NO_MALLOC return malloc(size); #else (void)size; @@ -203,8 +203,8 @@ static inline void *lfs_malloc(size_t size) { } // Deallocate memory, only used if buffers are not provided to littlefs -static inline void lfs_free(void *p) { -#ifndef LFS_NO_MALLOC +static inline void lfs2_free(void *p) { +#ifndef LFS2_NO_MALLOC free(p); #else (void)p; diff --git a/scripts/prefix.py b/scripts/prefix.py index ca547b6..3a046ca 100755 --- a/scripts/prefix.py +++ b/scripts/prefix.py @@ -5,7 +5,7 @@ # conflict at compile time. # # example: -# $ ./scripts/prefix.py lfs2 +# $ ./scripts/prefix.py lfs22 import os import os.path @@ -16,7 +16,7 @@ import tempfile import shutil import subprocess -DEFAULT_PREFIX = "lfs" +DEFAULT_PREFIX = "lfs2" def subn(from_prefix, to_prefix, name): name, count1 = re.subn('\\b'+from_prefix, to_prefix, name) diff --git a/tests/template.fmt b/tests/template.fmt index 7fdec7c..ad01eaa 100644 --- a/tests/template.fmt +++ b/tests/template.fmt @@ -1,6 +1,6 @@ /// AUTOGENERATED TEST /// -#include "lfs.h" -#include "emubd/lfs_emubd.h" +#include "lfs2.h" +#include "emubd/lfs2_emubd.h" #include #include #include @@ -37,7 +37,7 @@ static void test_assert(const char *file, unsigned line, // utility functions for traversals -static int __attribute__((used)) test_count(void *p, lfs_block_t b) {{ +static int __attribute__((used)) test_count(void *p, lfs2_block_t b) {{ (void)b; unsigned *u = (unsigned*)p; *u += 1; @@ -45,72 +45,72 @@ static int __attribute__((used)) test_count(void *p, lfs_block_t b) {{ }} -// lfs declarations -lfs_t lfs; -lfs_emubd_t bd; -lfs_file_t file[4]; -lfs_dir_t dir[4]; -struct lfs_info info; +// lfs2 declarations +lfs2_t lfs2; +lfs2_emubd_t bd; +lfs2_file_t file[4]; +lfs2_dir_t dir[4]; +struct lfs2_info info; uint8_t buffer[1024]; uint8_t wbuffer[1024]; uint8_t rbuffer[1024]; -lfs_size_t size; -lfs_size_t wsize; -lfs_size_t rsize; +lfs2_size_t size; +lfs2_size_t wsize; +lfs2_size_t rsize; uintmax_t test; -#ifndef LFS_READ_SIZE -#define LFS_READ_SIZE 16 +#ifndef LFS2_READ_SIZE +#define LFS2_READ_SIZE 16 #endif -#ifndef LFS_PROG_SIZE -#define LFS_PROG_SIZE LFS_READ_SIZE +#ifndef LFS2_PROG_SIZE +#define LFS2_PROG_SIZE LFS2_READ_SIZE #endif -#ifndef LFS_BLOCK_SIZE -#define LFS_BLOCK_SIZE 512 +#ifndef LFS2_BLOCK_SIZE +#define LFS2_BLOCK_SIZE 512 #endif -#ifndef LFS_BLOCK_COUNT -#define LFS_BLOCK_COUNT 1024 +#ifndef LFS2_BLOCK_COUNT +#define LFS2_BLOCK_COUNT 1024 #endif -#ifndef LFS_BLOCK_CYCLES -#define LFS_BLOCK_CYCLES 1024 +#ifndef LFS2_BLOCK_CYCLES +#define LFS2_BLOCK_CYCLES 1024 #endif -#ifndef LFS_CACHE_SIZE -#define LFS_CACHE_SIZE 64 +#ifndef LFS2_CACHE_SIZE +#define LFS2_CACHE_SIZE 64 #endif -#ifndef LFS_LOOKAHEAD_SIZE -#define LFS_LOOKAHEAD_SIZE 16 +#ifndef LFS2_LOOKAHEAD_SIZE +#define LFS2_LOOKAHEAD_SIZE 16 #endif -const struct lfs_config cfg = {{ +const struct lfs2_config cfg = {{ .context = &bd, - .read = &lfs_emubd_read, - .prog = &lfs_emubd_prog, - .erase = &lfs_emubd_erase, - .sync = &lfs_emubd_sync, + .read = &lfs2_emubd_read, + .prog = &lfs2_emubd_prog, + .erase = &lfs2_emubd_erase, + .sync = &lfs2_emubd_sync, - .read_size = LFS_READ_SIZE, - .prog_size = LFS_PROG_SIZE, - .block_size = LFS_BLOCK_SIZE, - .block_count = LFS_BLOCK_COUNT, - .block_cycles = LFS_BLOCK_CYCLES, - .cache_size = LFS_CACHE_SIZE, - .lookahead_size = LFS_LOOKAHEAD_SIZE, + .read_size = LFS2_READ_SIZE, + .prog_size = LFS2_PROG_SIZE, + .block_size = LFS2_BLOCK_SIZE, + .block_count = LFS2_BLOCK_COUNT, + .block_cycles = LFS2_BLOCK_CYCLES, + .cache_size = LFS2_CACHE_SIZE, + .lookahead_size = LFS2_LOOKAHEAD_SIZE, }}; // Entry point int main(void) {{ - lfs_emubd_create(&cfg, "blocks"); + lfs2_emubd_create(&cfg, "blocks"); {tests} - lfs_emubd_destroy(&cfg); + lfs2_emubd_destroy(&cfg); }} diff --git a/tests/test.py b/tests/test.py index e93ccec..306a146 100755 --- a/tests/test.py +++ b/tests/test.py @@ -28,7 +28,7 @@ def generate(test): # Remove build artifacts to force rebuild try: os.remove('test.o') - os.remove('lfs') + os.remove('lfs2') except OSError: pass @@ -39,9 +39,9 @@ def compile(): def execute(): if 'EXEC' in os.environ: - subprocess.check_call([os.environ['EXEC'], "./lfs"]) + subprocess.check_call([os.environ['EXEC'], "./lfs2"]) else: - subprocess.check_call(["./lfs"]) + subprocess.check_call(["./lfs2"]) def main(test=None): if test and not test.startswith('-'): diff --git a/tests/test_alloc.sh b/tests/test_alloc.sh index 3f993e9..36019a9 100755 --- a/tests/test_alloc.sh +++ b/tests/test_alloc.sh @@ -4,481 +4,481 @@ set -eu echo "=== Allocator tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST SIZE=15000 -lfs_mkdir() { +lfs2_mkdir() { tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "$1") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "$1") => 0; + lfs2_unmount(&lfs2) => 0; TEST } -lfs_remove() { +lfs2_remove() { tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_remove(&lfs, "$1/eggs") => 0; - lfs_remove(&lfs, "$1/bacon") => 0; - lfs_remove(&lfs, "$1/pancakes") => 0; - lfs_remove(&lfs, "$1") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_remove(&lfs2, "$1/eggs") => 0; + lfs2_remove(&lfs2, "$1/bacon") => 0; + lfs2_remove(&lfs2, "$1/pancakes") => 0; + lfs2_remove(&lfs2, "$1") => 0; + lfs2_unmount(&lfs2) => 0; TEST } -lfs_alloc_singleproc() { +lfs2_alloc_singleproc() { tests/test.py << TEST const char *names[] = {"bacon", "eggs", "pancakes"}; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { sprintf((char*)buffer, "$1/%s", names[n]); - lfs_file_open(&lfs, &file[n], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; + lfs2_file_open(&lfs2, &file[n], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; } for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { size = strlen(names[n]); for (int i = 0; i < $SIZE; i++) { - lfs_file_write(&lfs, &file[n], names[n], size) => size; + lfs2_file_write(&lfs2, &file[n], names[n], size) => size; } } for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { - lfs_file_close(&lfs, &file[n]) => 0; + lfs2_file_close(&lfs2, &file[n]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST } -lfs_alloc_multiproc() { +lfs2_alloc_multiproc() { for name in bacon eggs pancakes do tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "$1/$name", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "$1/$name", + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; size = strlen("$name"); memcpy(buffer, "$name", size); for (int i = 0; i < $SIZE; i++) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST done } -lfs_verify() { +lfs2_verify() { for name in bacon eggs pancakes do tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "$1/$name", LFS_O_RDONLY) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "$1/$name", LFS2_O_RDONLY) => 0; size = strlen("$name"); for (int i = 0; i < $SIZE; i++) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "$name", size) => 0; } - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST done } echo "--- Single-process allocation test ---" -lfs_mkdir singleproc -lfs_alloc_singleproc singleproc -lfs_verify singleproc +lfs2_mkdir singleproc +lfs2_alloc_singleproc singleproc +lfs2_verify singleproc echo "--- Multi-process allocation test ---" -lfs_mkdir multiproc -lfs_alloc_multiproc multiproc -lfs_verify multiproc -lfs_verify singleproc +lfs2_mkdir multiproc +lfs2_alloc_multiproc multiproc +lfs2_verify multiproc +lfs2_verify singleproc echo "--- Single-process reuse test ---" -lfs_remove singleproc -lfs_mkdir singleprocreuse -lfs_alloc_singleproc singleprocreuse -lfs_verify singleprocreuse -lfs_verify multiproc +lfs2_remove singleproc +lfs2_mkdir singleprocreuse +lfs2_alloc_singleproc singleprocreuse +lfs2_verify singleprocreuse +lfs2_verify multiproc echo "--- Multi-process reuse test ---" -lfs_remove multiproc -lfs_mkdir multiprocreuse -lfs_alloc_singleproc multiprocreuse -lfs_verify multiprocreuse -lfs_verify singleprocreuse +lfs2_remove multiproc +lfs2_mkdir multiprocreuse +lfs2_alloc_singleproc multiprocreuse +lfs2_verify multiprocreuse +lfs2_verify singleprocreuse echo "--- Cleanup ---" -lfs_remove multiprocreuse -lfs_remove singleprocreuse +lfs2_remove multiprocreuse +lfs2_remove singleprocreuse echo "--- Exhaustion test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); size = strlen("exhaustion"); memcpy(buffer, "exhaustion", size); - lfs_file_write(&lfs, &file[0], buffer, size) => size; - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; + lfs2_file_sync(&lfs2, &file[0]) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - lfs_ssize_t res; + lfs2_ssize_t res; while (true) { - res = lfs_file_write(&lfs, &file[0], buffer, size); + res = lfs2_file_write(&lfs2, &file[0], buffer, size); if (res < 0) { break; } res => size; } - res => LFS_ERR_NOSPC; + res => LFS2_ERR_NOSPC; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_RDONLY); size = strlen("exhaustion"); - lfs_file_size(&lfs, &file[0]) => size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_size(&lfs2, &file[0]) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "exhaustion", size) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Exhaustion wraparound test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_remove(&lfs, "exhaustion") => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_remove(&lfs2, "exhaustion") => 0; - lfs_file_open(&lfs, &file[0], "padding", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "padding", LFS2_O_WRONLY | LFS2_O_CREAT); size = strlen("buffering"); memcpy(buffer, "buffering", size); for (int i = 0; i < $SIZE; i++) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; - lfs_remove(&lfs, "padding") => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_remove(&lfs2, "padding") => 0; - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); size = strlen("exhaustion"); memcpy(buffer, "exhaustion", size); - lfs_file_write(&lfs, &file[0], buffer, size) => size; - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; + lfs2_file_sync(&lfs2, &file[0]) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - lfs_ssize_t res; + lfs2_ssize_t res; while (true) { - res = lfs_file_write(&lfs, &file[0], buffer, size); + res = lfs2_file_write(&lfs2, &file[0], buffer, size); if (res < 0) { break; } res => size; } - res => LFS_ERR_NOSPC; + res => LFS2_ERR_NOSPC; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_RDONLY); size = strlen("exhaustion"); - lfs_file_size(&lfs, &file[0]) => size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_size(&lfs2, &file[0]) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "exhaustion", size) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_remove(&lfs, "exhaustion") => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_remove(&lfs2, "exhaustion") => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Dir exhaustion test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // find out max file size - lfs_mkdir(&lfs, "exhaustiondir") => 0; + lfs2_mkdir(&lfs2, "exhaustiondir") => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); int count = 0; int err; while (true) { - err = lfs_file_write(&lfs, &file[0], buffer, size); + err = lfs2_file_write(&lfs2, &file[0], buffer, size); if (err < 0) { break; } count += 1; } - err => LFS_ERR_NOSPC; - lfs_file_close(&lfs, &file[0]) => 0; + err => LFS2_ERR_NOSPC; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_remove(&lfs, "exhaustion") => 0; - lfs_remove(&lfs, "exhaustiondir") => 0; + lfs2_remove(&lfs2, "exhaustion") => 0; + lfs2_remove(&lfs2, "exhaustiondir") => 0; // see if dir fits with max file size - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); for (int i = 0; i < count; i++) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_mkdir(&lfs, "exhaustiondir") => 0; - lfs_remove(&lfs, "exhaustiondir") => 0; - lfs_remove(&lfs, "exhaustion") => 0; + lfs2_mkdir(&lfs2, "exhaustiondir") => 0; + lfs2_remove(&lfs2, "exhaustiondir") => 0; + lfs2_remove(&lfs2, "exhaustion") => 0; // see if dir fits with > max file size - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); for (int i = 0; i < count+1; i++) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; + lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC; - lfs_remove(&lfs, "exhaustion") => 0; - lfs_unmount(&lfs) => 0; + lfs2_remove(&lfs2, "exhaustion") => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Chained dir exhaustion test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // find out max file size - lfs_mkdir(&lfs, "exhaustiondir") => 0; + lfs2_mkdir(&lfs2, "exhaustiondir") => 0; for (int i = 0; i < 10; i++) { sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); - lfs_mkdir(&lfs, (char*)buffer) => 0; + lfs2_mkdir(&lfs2, (char*)buffer) => 0; } size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); int count = 0; int err; while (true) { - err = lfs_file_write(&lfs, &file[0], buffer, size); + err = lfs2_file_write(&lfs2, &file[0], buffer, size); if (err < 0) { break; } count += 1; } - err => LFS_ERR_NOSPC; - lfs_file_close(&lfs, &file[0]) => 0; + err => LFS2_ERR_NOSPC; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_remove(&lfs, "exhaustion") => 0; - lfs_remove(&lfs, "exhaustiondir") => 0; + lfs2_remove(&lfs2, "exhaustion") => 0; + lfs2_remove(&lfs2, "exhaustiondir") => 0; for (int i = 0; i < 10; i++) { sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); - lfs_remove(&lfs, (char*)buffer) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; } // see that chained dir fails - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); for (int i = 0; i < count+1; i++) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_sync(&lfs2, &file[0]) => 0; for (int i = 0; i < 10; i++) { sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); - lfs_mkdir(&lfs, (char*)buffer) => 0; + lfs2_mkdir(&lfs2, (char*)buffer) => 0; } - lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; + lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC; // shorten file to try a second chained dir while (true) { - err = lfs_mkdir(&lfs, "exhaustiondir"); - if (err != LFS_ERR_NOSPC) { + err = lfs2_mkdir(&lfs2, "exhaustiondir"); + if (err != LFS2_ERR_NOSPC) { break; } - lfs_ssize_t filesize = lfs_file_size(&lfs, &file[0]); + lfs2_ssize_t filesize = lfs2_file_size(&lfs2, &file[0]); filesize > 0 => true; - lfs_file_truncate(&lfs, &file[0], filesize - size) => 0; - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_truncate(&lfs2, &file[0], filesize - size) => 0; + lfs2_file_sync(&lfs2, &file[0]) => 0; } err => 0; - lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC; + lfs2_mkdir(&lfs2, "exhaustiondir2") => LFS2_ERR_NOSPC; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Split dir test ---" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // create one block hole for half a directory - lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; - for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { + lfs2_file_open(&lfs2, &file[0], "bump", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + for (lfs2_size_t i = 0; i < cfg.block_size; i += 2) { memcpy(&buffer[i], "hi", 2); } - lfs_file_write(&lfs, &file[0], buffer, cfg.block_size) => cfg.block_size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], buffer, cfg.block_size) => cfg.block_size; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); + lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < (cfg.block_count-4)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; // remount to force reset of lookahead - lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_unmount(&lfs2) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // open hole - lfs_remove(&lfs, "bump") => 0; + lfs2_remove(&lfs2, "bump") => 0; - lfs_mkdir(&lfs, "splitdir") => 0; - lfs_file_open(&lfs, &file[0], "splitdir/bump", - LFS_O_WRONLY | LFS_O_CREAT) => 0; - for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { + lfs2_mkdir(&lfs2, "splitdir") => 0; + lfs2_file_open(&lfs2, &file[0], "splitdir/bump", + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + for (lfs2_size_t i = 0; i < cfg.block_size; i += 2) { memcpy(&buffer[i], "hi", 2); } - lfs_file_write(&lfs, &file[0], buffer, 2*cfg.block_size) => LFS_ERR_NOSPC; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], buffer, 2*cfg.block_size) => LFS2_ERR_NOSPC; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Outdated lookahead test ---" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // fill completely with two files - lfs_file_open(&lfs, &file[0], "exhaustion1", - LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion1", + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < ((cfg.block_count-2)/2)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_file_open(&lfs, &file[0], "exhaustion2", - LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion2", + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; // remount to force reset of lookahead - lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_unmount(&lfs2) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // rewrite one file - lfs_file_open(&lfs, &file[0], "exhaustion1", - LFS_O_WRONLY | LFS_O_TRUNC) => 0; - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion1", + LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; + lfs2_file_sync(&lfs2, &file[0]) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < ((cfg.block_count-2)/2)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; // rewrite second file, this requires lookahead does not // use old population - lfs_file_open(&lfs, &file[0], "exhaustion2", - LFS_O_WRONLY | LFS_O_TRUNC) => 0; - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion2", + LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; + lfs2_file_sync(&lfs2, &file[0]) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; TEST echo "--- Outdated lookahead and split dir test ---" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // fill completely with two files - lfs_file_open(&lfs, &file[0], "exhaustion1", - LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion1", + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < ((cfg.block_count-2)/2)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_file_open(&lfs, &file[0], "exhaustion2", - LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion2", + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; // remount to force reset of lookahead - lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_unmount(&lfs2) => 0; + lfs2_mount(&lfs2, &cfg) => 0; // rewrite one file with a hole of one block - lfs_file_open(&lfs, &file[0], "exhaustion1", - LFS_O_WRONLY | LFS_O_TRUNC) => 0; - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_open(&lfs2, &file[0], "exhaustion1", + LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; + lfs2_file_sync(&lfs2, &file[0]) => 0; size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); - for (lfs_size_t i = 0; + for (lfs2_size_t i = 0; i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8); i += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; // try to allocate a directory, should fail! - lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC; + lfs2_mkdir(&lfs2, "split") => LFS2_ERR_NOSPC; // file should not fail - lfs_file_open(&lfs, &file[0], "notasplit", - LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_write(&lfs, &file[0], "hi", 2) => 2; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_open(&lfs2, &file[0], "notasplit", + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_write(&lfs2, &file[0], "hi", 2) => 2; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_attrs.sh b/tests/test_attrs.sh index e4ff4ce..02041cd 100755 --- a/tests/test_attrs.sh +++ b/tests/test_attrs.sh @@ -4,282 +4,282 @@ set -eu echo "=== Attr tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "hello") => 0; - lfs_file_open(&lfs, &file[0], "hello/hello", - LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_write(&lfs, &file[0], "hello", strlen("hello")) + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "hello") => 0; + lfs2_file_open(&lfs2, &file[0], "hello/hello", + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_write(&lfs2, &file[0], "hello", strlen("hello")) => strlen("hello"); - lfs_file_close(&lfs, &file[0]); - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]); + lfs2_unmount(&lfs2) => 0; TEST echo "--- Set/get attribute ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_setattr(&lfs, "hello", 'A', "aaaa", 4) => 0; - lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0; - lfs_setattr(&lfs, "hello", 'C', "ccccc", 5) => 0; - lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6; - lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_setattr(&lfs2, "hello", 'A', "aaaa", 4) => 0; + lfs2_setattr(&lfs2, "hello", 'B', "bbbbbb", 6) => 0; + lfs2_setattr(&lfs2, "hello", 'C', "ccccc", 5) => 0; + lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6; + lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "bbbbbb", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "hello", 'B', "", 0) => 0; - lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 0; - lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; + lfs2_setattr(&lfs2, "hello", 'B', "", 0) => 0; + lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 0; + lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_removeattr(&lfs, "hello", 'B') => 0; - lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => LFS_ERR_NOATTR; - lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; + lfs2_removeattr(&lfs2, "hello", 'B') => 0; + lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => LFS2_ERR_NOATTR; + lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0; - lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6; - lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; + lfs2_setattr(&lfs2, "hello", 'B', "dddddd", 6) => 0; + lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6; + lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "dddddd", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0; - lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 3; - lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; + lfs2_setattr(&lfs2, "hello", 'B', "eee", 3) => 0; + lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 3; + lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "eee\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; - lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0; - lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 9; - lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; + lfs2_setattr(&lfs2, "hello", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC; + lfs2_setattr(&lfs2, "hello", 'B', "fffffffff", 9) => 0; + lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 9; + lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9; - lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 9) => 9; + lfs2_getattr(&lfs2, "hello", 'C', buffer+13, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "fffffffff", 9) => 0; memcmp(buffer+13, "ccccc", 5) => 0; - lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello"); + lfs2_file_open(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, sizeof(buffer)) => strlen("hello"); memcmp(buffer, "hello", strlen("hello")) => 0; - lfs_file_close(&lfs, &file[0]); - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]); + lfs2_unmount(&lfs2) => 0; TEST echo "--- Set/get root attribute ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_setattr(&lfs, "/", 'A', "aaaa", 4) => 0; - lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0; - lfs_setattr(&lfs, "/", 'C', "ccccc", 5) => 0; - lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6; - lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_setattr(&lfs2, "/", 'A', "aaaa", 4) => 0; + lfs2_setattr(&lfs2, "/", 'B', "bbbbbb", 6) => 0; + lfs2_setattr(&lfs2, "/", 'C', "ccccc", 5) => 0; + lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6; + lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "bbbbbb", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "/", 'B', "", 0) => 0; - lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 0; - lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; + lfs2_setattr(&lfs2, "/", 'B', "", 0) => 0; + lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 0; + lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_removeattr(&lfs, "/", 'B') => 0; - lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => LFS_ERR_NOATTR; - lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; + lfs2_removeattr(&lfs2, "/", 'B') => 0; + lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => LFS2_ERR_NOATTR; + lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0; - lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6; - lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; + lfs2_setattr(&lfs2, "/", 'B', "dddddd", 6) => 0; + lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6; + lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "dddddd", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0; - lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 3; - lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; + lfs2_setattr(&lfs2, "/", 'B', "eee", 3) => 0; + lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 3; + lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "eee\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; - lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0; - lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 9; - lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; - lfs_unmount(&lfs) => 0; + lfs2_setattr(&lfs2, "/", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC; + lfs2_setattr(&lfs2, "/", 'B', "fffffffff", 9) => 0; + lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 9; + lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; - lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9; - lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4; + lfs2_getattr(&lfs2, "/", 'B', buffer+4, 9) => 9; + lfs2_getattr(&lfs2, "/", 'C', buffer+13, 5) => 5; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "fffffffff", 9) => 0; memcmp(buffer+13, "ccccc", 5) => 0; - lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello"); + lfs2_file_open(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, sizeof(buffer)) => strlen("hello"); memcmp(buffer, "hello", strlen("hello")) => 0; - lfs_file_close(&lfs, &file[0]); - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]); + lfs2_unmount(&lfs2) => 0; TEST echo "--- Set/get file attribute ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - struct lfs_attr attrs1[] = { + lfs2_mount(&lfs2, &cfg) => 0; + struct lfs2_attr attrs1[] = { {'A', buffer, 4}, {'B', buffer+4, 6}, {'C', buffer+10, 5}, }; - struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; + struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; memcpy(buffer, "aaaa", 4); memcpy(buffer+4, "bbbbbb", 6); memcpy(buffer+10, "ccccc", 5); - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memset(buffer, 0, 15); - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "bbbbbb", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; attrs1[1].size = 0; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memset(buffer, 0, 15); attrs1[1].size = 6; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; attrs1[1].size = 6; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; memcpy(buffer+4, "dddddd", 6); - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memset(buffer, 0, 15); attrs1[1].size = 6; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "dddddd", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; attrs1[1].size = 3; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; memcpy(buffer+4, "eee", 3); - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memset(buffer, 0, 15); attrs1[1].size = 6; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "eee\0\0\0", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - attrs1[0].size = LFS_ATTR_MAX+1; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) - => LFS_ERR_NOSPC; + attrs1[0].size = LFS2_ATTR_MAX+1; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) + => LFS2_ERR_NOSPC; - struct lfs_attr attrs2[] = { + struct lfs2_attr attrs2[] = { {'A', buffer, 4}, {'B', buffer+4, 9}, {'C', buffer+13, 5}, }; - struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDWR, &cfg2) => 0; + struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDWR, &cfg2) => 0; memcpy(buffer+4, "fffffffff", 9); - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; attrs1[0].size = 4; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - struct lfs_attr attrs2[] = { + lfs2_mount(&lfs2, &cfg) => 0; + struct lfs2_attr attrs2[] = { {'A', buffer, 4}, {'B', buffer+4, 9}, {'C', buffer+13, 5}, }; - struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; + struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg2) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg2) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; memcmp(buffer, "aaaa", 4) => 0; memcmp(buffer+4, "fffffffff", 9) => 0; memcmp(buffer+13, "ccccc", 5) => 0; - lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello"); + lfs2_file_open(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, sizeof(buffer)) => strlen("hello"); memcmp(buffer, "hello", strlen("hello")) => 0; - lfs_file_close(&lfs, &file[0]); - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]); + lfs2_unmount(&lfs2) => 0; TEST echo "--- Deferred file attributes ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - struct lfs_attr attrs1[] = { + lfs2_mount(&lfs2, &cfg) => 0; + struct lfs2_attr attrs1[] = { {'B', "gggg", 4}, {'C', "", 0}, {'D', "hhhh", 4}, }; - struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; + struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; - lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; + lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; - lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 9; - lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 5; - lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => LFS_ERR_NOATTR; + lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 9; + lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 5; + lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => LFS2_ERR_NOATTR; memcmp(buffer, "fffffffff", 9) => 0; memcmp(buffer+9, "ccccc\0\0\0\0", 9) => 0; memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0; - lfs_file_sync(&lfs, &file[0]) => 0; - lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 4; - lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 0; - lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4; + lfs2_file_sync(&lfs2, &file[0]) => 0; + lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 4; + lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 0; + lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => 4; memcmp(buffer, "gggg\0\0\0\0\0", 9) => 0; memcmp(buffer+9, "\0\0\0\0\0\0\0\0\0", 9) => 0; memcmp(buffer+18, "hhhh\0\0\0\0\0", 9) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_corrupt.sh b/tests/test_corrupt.sh index 81b0674..f2f823c 100755 --- a/tests/test_corrupt.sh +++ b/tests/test_corrupt.sh @@ -6,71 +6,71 @@ echo "=== Corrupt tests ===" NAMEMULT=64 FILEMULT=1 -lfs_mktree() { +lfs2_mktree() { tests/test.py ${1:-} << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (int i = 1; i < 10; i++) { for (int j = 0; j < $NAMEMULT; j++) { buffer[j] = '0'+i; } buffer[$NAMEMULT] = '\0'; - lfs_mkdir(&lfs, (char*)buffer) => 0; + lfs2_mkdir(&lfs2, (char*)buffer) => 0; buffer[$NAMEMULT] = '/'; for (int j = 0; j < $NAMEMULT; j++) { buffer[j+$NAMEMULT+1] = '0'+i; } buffer[2*$NAMEMULT+1] = '\0'; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = $NAMEMULT; for (int j = 0; j < i*$FILEMULT; j++) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST } -lfs_chktree() { +lfs2_chktree() { tests/test.py ${1:-} << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (int i = 1; i < 10; i++) { for (int j = 0; j < $NAMEMULT; j++) { buffer[j] = '0'+i; } buffer[$NAMEMULT] = '\0'; - lfs_stat(&lfs, (char*)buffer, &info) => 0; - info.type => LFS_TYPE_DIR; + lfs2_stat(&lfs2, (char*)buffer, &info) => 0; + info.type => LFS2_TYPE_DIR; buffer[$NAMEMULT] = '/'; for (int j = 0; j < $NAMEMULT; j++) { buffer[j+$NAMEMULT+1] = '0'+i; } buffer[2*$NAMEMULT+1] = '\0'; - lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0; size = $NAMEMULT; for (int j = 0; j < i*$FILEMULT; j++) { - lfs_file_read(&lfs, &file[0], rbuffer, size) => size; + lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size; memcmp(buffer, rbuffer, size) => 0; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST } echo "--- Sanity check ---" rm -rf blocks -lfs_mktree -lfs_chktree +lfs2_mktree +lfs2_chktree BLOCKS="$(ls blocks | grep -vw '[01]')" echo "--- Block corruption ---" @@ -79,8 +79,8 @@ do rm -rf blocks mkdir blocks ln -s /dev/zero blocks/$b - lfs_mktree - lfs_chktree + lfs2_mktree + lfs2_chktree done echo "--- Block persistance ---" @@ -88,10 +88,10 @@ for b in $BLOCKS do rm -rf blocks mkdir blocks - lfs_mktree + lfs2_mktree chmod a-w blocks/$b || true - lfs_mktree - lfs_chktree + lfs2_mktree + lfs2_chktree done echo "--- Big region corruption ---" @@ -101,8 +101,8 @@ for i in {2..512} do ln -s /dev/zero blocks/$(printf '%x' $i) done -lfs_mktree -lfs_chktree +lfs2_mktree +lfs2_chktree echo "--- Alternating corruption ---" rm -rf blocks @@ -111,8 +111,8 @@ for i in {2..1024..2} do ln -s /dev/zero blocks/$(printf '%x' $i) done -lfs_mktree -lfs_chktree +lfs2_mktree +lfs2_chktree echo "--- Results ---" tests/stats.py diff --git a/tests/test_dirs.sh b/tests/test_dirs.sh index 5f2020f..c293b6c 100755 --- a/tests/test_dirs.sh +++ b/tests/test_dirs.sh @@ -6,294 +6,294 @@ LARGESIZE=128 echo "=== Directory tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST echo "--- Root directory ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Directory creation ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "potato") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "potato") => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- File creation ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "burito", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Directory iteration ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "burito") => 0; - info.type => LFS_TYPE_REG; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_REG; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "potato") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Directory failures ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST; - lfs_dir_open(&lfs, &dir[0], "tomato") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "burito") => LFS_ERR_NOTDIR; - lfs_file_open(&lfs, &file[0], "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT; - lfs_file_open(&lfs, &file[0], "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "potato") => LFS2_ERR_EXIST; + lfs2_dir_open(&lfs2, &dir[0], "tomato") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "burito") => LFS2_ERR_NOTDIR; + lfs2_file_open(&lfs2, &file[0], "tomato", LFS2_O_RDONLY) => LFS2_ERR_NOENT; + lfs2_file_open(&lfs2, &file[0], "potato", LFS2_O_RDONLY) => LFS2_ERR_ISDIR; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Nested directories ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "potato/baked") => 0; - lfs_mkdir(&lfs, "potato/sweet") => 0; - lfs_mkdir(&lfs, "potato/fried") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "potato/baked") => 0; + lfs2_mkdir(&lfs2, "potato/sweet") => 0; + lfs2_mkdir(&lfs2, "potato/fried") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "potato") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "potato") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "baked") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "fried") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "sweet") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Multi-block directory ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "cactus") => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "cactus") => 0; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "cactus/test%03d", i); - lfs_mkdir(&lfs, (char*)buffer) => 0; + lfs2_mkdir(&lfs2, (char*)buffer) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "cactus") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "cactus") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; + info.type => LFS2_TYPE_DIR; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "test%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - info.type => LFS_TYPE_DIR; + info.type => LFS2_TYPE_DIR; } - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Directory remove ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_remove(&lfs, "potato") => LFS_ERR_NOTEMPTY; - lfs_remove(&lfs, "potato/sweet") => 0; - lfs_remove(&lfs, "potato/baked") => 0; - lfs_remove(&lfs, "potato/fried") => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_remove(&lfs2, "potato") => LFS2_ERR_NOTEMPTY; + lfs2_remove(&lfs2, "potato/sweet") => 0; + lfs2_remove(&lfs2, "potato/baked") => 0; + lfs2_remove(&lfs2, "potato/fried") => 0; - lfs_dir_open(&lfs, &dir[0], "potato") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "potato") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; - lfs_remove(&lfs, "potato") => 0; + lfs2_remove(&lfs2, "potato") => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "burito") => 0; - info.type => LFS_TYPE_REG; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_REG; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "cactus") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "burito") => 0; - info.type => LFS_TYPE_REG; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_REG; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "cactus") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Directory rename ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "coldpotato") => 0; - lfs_mkdir(&lfs, "coldpotato/baked") => 0; - lfs_mkdir(&lfs, "coldpotato/sweet") => 0; - lfs_mkdir(&lfs, "coldpotato/fried") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "coldpotato") => 0; + lfs2_mkdir(&lfs2, "coldpotato/baked") => 0; + lfs2_mkdir(&lfs2, "coldpotato/sweet") => 0; + lfs2_mkdir(&lfs2, "coldpotato/fried") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "coldpotato", "hotpotato") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "coldpotato", "hotpotato") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "hotpotato") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "baked") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "fried") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "sweet") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "warmpotato") => 0; - lfs_mkdir(&lfs, "warmpotato/mushy") => 0; - lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERR_NOTEMPTY; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "warmpotato") => 0; + lfs2_mkdir(&lfs2, "warmpotato/mushy") => 0; + lfs2_rename(&lfs2, "hotpotato", "warmpotato") => LFS2_ERR_NOTEMPTY; - lfs_remove(&lfs, "warmpotato/mushy") => 0; - lfs_rename(&lfs, "hotpotato", "warmpotato") => 0; + lfs2_remove(&lfs2, "warmpotato/mushy") => 0; + lfs2_rename(&lfs2, "hotpotato", "warmpotato") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "warmpotato") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "baked") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "fried") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "sweet") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "coldpotato") => 0; - lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0; - lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0; - lfs_rename(&lfs, "warmpotato/fried", "coldpotato/fried") => 0; - lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY; - lfs_remove(&lfs, "warmpotato") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "coldpotato") => 0; + lfs2_rename(&lfs2, "warmpotato/baked", "coldpotato/baked") => 0; + lfs2_rename(&lfs2, "warmpotato/sweet", "coldpotato/sweet") => 0; + lfs2_rename(&lfs2, "warmpotato/fried", "coldpotato/fried") => 0; + lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY; + lfs2_remove(&lfs2, "warmpotato") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "coldpotato") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "baked") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "fried") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "sweet") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Recursive remove ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY; - lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "coldpotato") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; while (true) { - int err = lfs_dir_read(&lfs, &dir[0], &info); + int err = lfs2_dir_read(&lfs2, &dir[0], &info); err >= 0 => 1; if (err == 0) { break; @@ -301,183 +301,183 @@ tests/test.py << TEST strcpy((char*)buffer, "coldpotato/"); strcat((char*)buffer, info.name); - lfs_remove(&lfs, (char*)buffer) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; } - lfs_remove(&lfs, "coldpotato") => 0; + lfs2_remove(&lfs2, "coldpotato") => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "burito") => 0; - info.type => LFS_TYPE_REG; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_REG; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "cactus") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Multi-block rename ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "cactus/test%03d", i); sprintf((char*)wbuffer, "cactus/tedd%03d", i); - lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; + lfs2_rename(&lfs2, (char*)buffer, (char*)wbuffer) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "cactus") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "cactus") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; + info.type => LFS2_TYPE_DIR; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "tedd%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - info.type => LFS_TYPE_DIR; + info.type => LFS2_TYPE_DIR; } - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Multi-block remove ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_remove(&lfs2, "cactus") => LFS2_ERR_NOTEMPTY; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "cactus/tedd%03d", i); - lfs_remove(&lfs, (char*)buffer) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; } - lfs_remove(&lfs, "cactus") => 0; - lfs_unmount(&lfs) => 0; + lfs2_remove(&lfs2, "cactus") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "burito") => 0; - info.type => LFS_TYPE_REG; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_REG; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Multi-block directory with files ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "prickly-pear") => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "prickly-pear") => 0; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "prickly-pear/test%03d", i); - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = 6; memcpy(wbuffer, "Hello", size); - lfs_file_write(&lfs, &file[0], wbuffer, size) => size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "prickly-pear") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "prickly-pear") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; + info.type => LFS2_TYPE_DIR; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "test%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 6; } - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Multi-block rename with files ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "prickly-pear/test%03d", i); sprintf((char*)wbuffer, "prickly-pear/tedd%03d", i); - lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0; + lfs2_rename(&lfs2, (char*)buffer, (char*)wbuffer) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "prickly-pear") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "prickly-pear") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; + info.type => LFS2_TYPE_DIR; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "tedd%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 6; } - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Multi-block remove with files ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_remove(&lfs2, "prickly-pear") => LFS2_ERR_NOTEMPTY; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "prickly-pear/tedd%03d", i); - lfs_remove(&lfs, (char*)buffer) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; } - lfs_remove(&lfs, "prickly-pear") => 0; - lfs_unmount(&lfs) => 0; + lfs2_remove(&lfs2, "prickly-pear") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "burito") => 0; - info.type => LFS_TYPE_REG; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + info.type => LFS2_TYPE_REG; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_entries.sh b/tests/test_entries.sh index 4728b7f..d9616ba 100755 --- a/tests/test_entries.sh +++ b/tests/test_entries.sh @@ -10,10 +10,10 @@ function read_file { cat << TEST size = $2; - lfs_file_open(&lfs, &file[0], "$1", LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], rbuffer, size) => size; + lfs2_file_open(&lfs2, &file[0], "$1", LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size; memcmp(rbuffer, wbuffer, size) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; TEST } @@ -21,19 +21,19 @@ function write_file { cat << TEST size = $2; - lfs_file_open(&lfs, &file[0], "$1", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + lfs2_file_open(&lfs2, &file[0], "$1", + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; memset(wbuffer, 'c', size); - lfs_file_write(&lfs, &file[0], wbuffer, size) => size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size; + lfs2_file_close(&lfs2, &file[0]) => 0; TEST } echo "--- Entry grow test ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; $(write_file "hi0" 20) $(write_file "hi1" 20) $(write_file "hi2" 20) @@ -46,14 +46,14 @@ tests/test.py << TEST $(read_file "hi1" 200) $(read_file "hi2" 20) $(read_file "hi3" 20) - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Entry shrink test ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; $(write_file "hi0" 20) $(write_file "hi1" 200) $(write_file "hi2" 20) @@ -66,14 +66,14 @@ tests/test.py << TEST $(read_file "hi1" 20) $(read_file "hi2" 20) $(read_file "hi3" 20) - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Entry spill test ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; $(write_file "hi0" 200) $(write_file "hi1" 200) $(write_file "hi2" 200) @@ -83,14 +83,14 @@ tests/test.py << TEST $(read_file "hi1" 200) $(read_file "hi2" 200) $(read_file "hi3" 200) - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Entry push spill test ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; $(write_file "hi0" 200) $(write_file "hi1" 20) $(write_file "hi2" 200) @@ -103,14 +103,14 @@ tests/test.py << TEST $(read_file "hi1" 200) $(read_file "hi2" 200) $(read_file "hi3" 200) - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Entry push spill two test ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; $(write_file "hi0" 200) $(write_file "hi1" 20) $(write_file "hi2" 200) @@ -125,96 +125,96 @@ tests/test.py << TEST $(read_file "hi2" 200) $(read_file "hi3" 200) $(read_file "hi4" 200) - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Entry drop test ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; $(write_file "hi0" 200) $(write_file "hi1" 200) $(write_file "hi2" 200) $(write_file "hi3" 200) - lfs_remove(&lfs, "hi1") => 0; - lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT; + lfs2_remove(&lfs2, "hi1") => 0; + lfs2_stat(&lfs2, "hi1", &info) => LFS2_ERR_NOENT; $(read_file "hi0" 200) $(read_file "hi2" 200) $(read_file "hi3" 200) - lfs_remove(&lfs, "hi2") => 0; - lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT; + lfs2_remove(&lfs2, "hi2") => 0; + lfs2_stat(&lfs2, "hi2", &info) => LFS2_ERR_NOENT; $(read_file "hi0" 200) $(read_file "hi3" 200) - lfs_remove(&lfs, "hi3") => 0; - lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT; + lfs2_remove(&lfs2, "hi3") => 0; + lfs2_stat(&lfs2, "hi3", &info) => LFS2_ERR_NOENT; $(read_file "hi0" 200) - lfs_remove(&lfs, "hi0") => 0; - lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT; - lfs_unmount(&lfs) => 0; + lfs2_remove(&lfs2, "hi0") => 0; + lfs2_stat(&lfs2, "hi0", &info) => LFS2_ERR_NOENT; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Create too big ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; memset(buffer, 'm', 200); buffer[200] = '\0'; size = 400; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; memset(wbuffer, 'c', size); - lfs_file_write(&lfs, &file[0], wbuffer, size) => size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size; + lfs2_file_close(&lfs2, &file[0]) => 0; size = 400; - lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], rbuffer, size) => size; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size; memcmp(rbuffer, wbuffer, size) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Resize too big ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; memset(buffer, 'm', 200); buffer[200] = '\0'; size = 40; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; memset(wbuffer, 'c', size); - lfs_file_write(&lfs, &file[0], wbuffer, size) => size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size; + lfs2_file_close(&lfs2, &file[0]) => 0; size = 40; - lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], rbuffer, size) => size; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size; memcmp(rbuffer, wbuffer, size) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; size = 400; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; memset(wbuffer, 'c', size); - lfs_file_write(&lfs, &file[0], wbuffer, size) => size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size; + lfs2_file_close(&lfs2, &file[0]) => 0; size = 400; - lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], rbuffer, size) => size; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size; memcmp(rbuffer, wbuffer, size) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_files.sh b/tests/test_files.sh index 5251c61..b042c3e 100755 --- a/tests/test_files.sh +++ b/tests/test_files.sh @@ -8,65 +8,65 @@ LARGESIZE=262144 echo "=== File tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST echo "--- Simple file test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = strlen("Hello World!\n"); memcpy(wbuffer, "Hello World!\n", size); - lfs_file_write(&lfs, &file[0], wbuffer, size) => size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_file_open(&lfs, &file[0], "hello", LFS_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[0], "hello", LFS2_O_RDONLY) => 0; size = strlen("Hello World!\n"); - lfs_file_read(&lfs, &file[0], rbuffer, size) => size; + lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size; memcmp(rbuffer, wbuffer, size) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST w_test() { tests/test.py ${4:-} << TEST size = $1; - lfs_size_t chunk = 31; + lfs2_size_t chunk = 31; srand(0); - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "$2", - ${3:-LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC}) => 0; - for (lfs_size_t i = 0; i < size; i += chunk) { + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "$2", + ${3:-LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC}) => 0; + for (lfs2_size_t i = 0; i < size; i += chunk) { chunk = (chunk < size - i) ? chunk : size - i; - for (lfs_size_t b = 0; b < chunk; b++) { + for (lfs2_size_t b = 0; b < chunk; b++) { buffer[b] = rand() & 0xff; } - lfs_file_write(&lfs, &file[0], buffer, chunk) => chunk; + lfs2_file_write(&lfs2, &file[0], buffer, chunk) => chunk; } - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST } r_test() { tests/test.py << TEST size = $1; - lfs_size_t chunk = 29; + lfs2_size_t chunk = 29; srand(0); - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "$2", &info) => 0; - info.type => LFS_TYPE_REG; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "$2", &info) => 0; + info.type => LFS2_TYPE_REG; info.size => size; - lfs_file_open(&lfs, &file[0], "$2", ${3:-LFS_O_RDONLY}) => 0; - for (lfs_size_t i = 0; i < size; i += chunk) { + lfs2_file_open(&lfs2, &file[0], "$2", ${3:-LFS2_O_RDONLY}) => 0; + for (lfs2_size_t i = 0; i < size; i += chunk) { chunk = (chunk < size - i) ? chunk : size - i; - lfs_file_read(&lfs, &file[0], buffer, chunk) => chunk; - for (lfs_size_t b = 0; b < chunk && i+b < size; b++) { + lfs2_file_read(&lfs2, &file[0], buffer, chunk) => chunk; + for (lfs2_size_t b = 0; b < chunk && i+b < size; b++) { buffer[b] => rand() & 0xff; } } - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST } @@ -106,52 +106,52 @@ r_test 0 noavacado echo "--- Dir check ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; - lfs_dir_read(&lfs, &dir[0], &info) => 1; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hello") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => strlen("Hello World!\n"); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "largeavacado") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => $LARGESIZE; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "mediumavacado") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => $MEDIUMSIZE; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "noavacado") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "smallavacado") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => $SMALLSIZE; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Many file test ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST tests/test.py << TEST // Create 300 files of 6 bytes - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "directory") => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "directory") => 0; for (unsigned i = 0; i < 300; i++) { snprintf((char*)buffer, sizeof(buffer), "file_%03d", i); - lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_WRONLY | LFS2_O_CREAT) => 0; size = 6; memcpy(wbuffer, "Hello", size); - lfs_file_write(&lfs, &file[0], wbuffer, size) => size; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_format.sh b/tests/test_format.sh index 5a11535..cfb9d21 100755 --- a/tests/test_format.sh +++ b/tests/test_format.sh @@ -6,44 +6,44 @@ rm -rf blocks echo "--- Basic formatting ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST echo "--- Basic mounting ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Invalid superblocks ---" ln -f -s /dev/zero blocks/0 ln -f -s /dev/zero blocks/1 tests/test.py << TEST - lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC; + lfs2_format(&lfs2, &cfg) => LFS2_ERR_NOSPC; TEST rm blocks/0 blocks/1 echo "--- Invalid mount ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; + lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; TEST echo "--- Expanding superblock ---" tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (int i = 0; i < 100; i++) { - lfs_mkdir(&lfs, "dummy") => 0; - lfs_remove(&lfs, "dummy") => 0; + lfs2_mkdir(&lfs2, "dummy") => 0; + lfs2_remove(&lfs2, "dummy") => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "dummy") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "dummy") => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_interspersed.sh b/tests/test_interspersed.sh index 52e24bc..c08c1a4 100755 --- a/tests/test_interspersed.sh +++ b/tests/test_interspersed.sh @@ -4,182 +4,182 @@ set -eu echo "=== Interspersed tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST echo "--- Interspersed file test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "a", LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_open(&lfs, &file[1], "b", LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_open(&lfs, &file[2], "c", LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_open(&lfs, &file[3], "d", LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "a", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[1], "b", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[2], "c", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[3], "d", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; for (int i = 0; i < 10; i++) { - lfs_file_write(&lfs, &file[0], (const void*)"a", 1) => 1; - lfs_file_write(&lfs, &file[1], (const void*)"b", 1) => 1; - lfs_file_write(&lfs, &file[2], (const void*)"c", 1) => 1; - lfs_file_write(&lfs, &file[3], (const void*)"d", 1) => 1; + lfs2_file_write(&lfs2, &file[0], (const void*)"a", 1) => 1; + lfs2_file_write(&lfs2, &file[1], (const void*)"b", 1) => 1; + lfs2_file_write(&lfs2, &file[2], (const void*)"c", 1) => 1; + lfs2_file_write(&lfs2, &file[3], (const void*)"d", 1) => 1; } - lfs_file_close(&lfs, &file[0]); - lfs_file_close(&lfs, &file[1]); - lfs_file_close(&lfs, &file[2]); - lfs_file_close(&lfs, &file[3]); + lfs2_file_close(&lfs2, &file[0]); + lfs2_file_close(&lfs2, &file[1]); + lfs2_file_close(&lfs2, &file[2]); + lfs2_file_close(&lfs2, &file[3]); - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "a") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 10; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "b") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 10; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "c") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 10; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "d") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 10; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; - lfs_file_open(&lfs, &file[0], "a", LFS_O_RDONLY) => 0; - lfs_file_open(&lfs, &file[1], "b", LFS_O_RDONLY) => 0; - lfs_file_open(&lfs, &file[2], "c", LFS_O_RDONLY) => 0; - lfs_file_open(&lfs, &file[3], "d", LFS_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[0], "a", LFS2_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[1], "b", LFS2_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[2], "c", LFS2_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[3], "d", LFS2_O_RDONLY) => 0; for (int i = 0; i < 10; i++) { - lfs_file_read(&lfs, &file[0], buffer, 1) => 1; + lfs2_file_read(&lfs2, &file[0], buffer, 1) => 1; buffer[0] => 'a'; - lfs_file_read(&lfs, &file[1], buffer, 1) => 1; + lfs2_file_read(&lfs2, &file[1], buffer, 1) => 1; buffer[0] => 'b'; - lfs_file_read(&lfs, &file[2], buffer, 1) => 1; + lfs2_file_read(&lfs2, &file[2], buffer, 1) => 1; buffer[0] => 'c'; - lfs_file_read(&lfs, &file[3], buffer, 1) => 1; + lfs2_file_read(&lfs2, &file[3], buffer, 1) => 1; buffer[0] => 'd'; } - lfs_file_close(&lfs, &file[0]); - lfs_file_close(&lfs, &file[1]); - lfs_file_close(&lfs, &file[2]); - lfs_file_close(&lfs, &file[3]); + lfs2_file_close(&lfs2, &file[0]); + lfs2_file_close(&lfs2, &file[1]); + lfs2_file_close(&lfs2, &file[2]); + lfs2_file_close(&lfs2, &file[3]); - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Interspersed remove file test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; for (int i = 0; i < 5; i++) { - lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; + lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1; } - lfs_remove(&lfs, "a") => 0; - lfs_remove(&lfs, "b") => 0; - lfs_remove(&lfs, "c") => 0; - lfs_remove(&lfs, "d") => 0; + lfs2_remove(&lfs2, "a") => 0; + lfs2_remove(&lfs2, "b") => 0; + lfs2_remove(&lfs2, "c") => 0; + lfs2_remove(&lfs2, "d") => 0; for (int i = 0; i < 5; i++) { - lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; + lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1; } - lfs_file_close(&lfs, &file[0]); + lfs2_file_close(&lfs2, &file[0]); - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "e") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 10; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; - lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_RDONLY) => 0; for (int i = 0; i < 10; i++) { - lfs_file_read(&lfs, &file[0], buffer, 1) => 1; + lfs2_file_read(&lfs2, &file[0], buffer, 1) => 1; buffer[0] => 'e'; } - lfs_file_close(&lfs, &file[0]); + lfs2_file_close(&lfs2, &file[0]); - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Remove inconveniently test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_TRUNC) => 0; - lfs_file_open(&lfs, &file[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_open(&lfs, &file[2], "g", LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; + lfs2_file_open(&lfs2, &file[1], "f", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_open(&lfs2, &file[2], "g", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; for (int i = 0; i < 5; i++) { - lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; - lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1; - lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1; + lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1; + lfs2_file_write(&lfs2, &file[1], (const void*)"f", 1) => 1; + lfs2_file_write(&lfs2, &file[2], (const void*)"g", 1) => 1; } - lfs_remove(&lfs, "f") => 0; + lfs2_remove(&lfs2, "f") => 0; for (int i = 0; i < 5; i++) { - lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; - lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1; - lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1; + lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1; + lfs2_file_write(&lfs2, &file[1], (const void*)"f", 1) => 1; + lfs2_file_write(&lfs2, &file[2], (const void*)"g", 1) => 1; } - lfs_file_close(&lfs, &file[0]); - lfs_file_close(&lfs, &file[1]); - lfs_file_close(&lfs, &file[2]); + lfs2_file_close(&lfs2, &file[0]); + lfs2_file_close(&lfs2, &file[1]); + lfs2_file_close(&lfs2, &file[2]); - lfs_dir_open(&lfs, &dir[0], "/") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "/") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - info.type => LFS_TYPE_DIR; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + info.type => LFS2_TYPE_DIR; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "e") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 10; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "g") => 0; - info.type => LFS_TYPE_REG; + info.type => LFS2_TYPE_REG; info.size => 10; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; - lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0; - lfs_file_open(&lfs, &file[1], "g", LFS_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_RDONLY) => 0; + lfs2_file_open(&lfs2, &file[1], "g", LFS2_O_RDONLY) => 0; for (int i = 0; i < 10; i++) { - lfs_file_read(&lfs, &file[0], buffer, 1) => 1; + lfs2_file_read(&lfs2, &file[0], buffer, 1) => 1; buffer[0] => 'e'; - lfs_file_read(&lfs, &file[1], buffer, 1) => 1; + lfs2_file_read(&lfs2, &file[1], buffer, 1) => 1; buffer[0] => 'g'; } - lfs_file_close(&lfs, &file[0]); - lfs_file_close(&lfs, &file[1]); + lfs2_file_close(&lfs2, &file[0]); + lfs2_file_close(&lfs2, &file[1]); - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_move.sh b/tests/test_move.sh index 458ca1e..9017dff 100755 --- a/tests/test_move.sh +++ b/tests/test_move.sh @@ -4,327 +4,327 @@ set -eu echo "=== Move tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "a") => 0; - lfs_mkdir(&lfs, "b") => 0; - lfs_mkdir(&lfs, "c") => 0; - lfs_mkdir(&lfs, "d") => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "a") => 0; + lfs2_mkdir(&lfs2, "b") => 0; + lfs2_mkdir(&lfs2, "c") => 0; + lfs2_mkdir(&lfs2, "d") => 0; - lfs_mkdir(&lfs, "a/hi") => 0; - lfs_mkdir(&lfs, "a/hi/hola") => 0; - lfs_mkdir(&lfs, "a/hi/bonjour") => 0; - lfs_mkdir(&lfs, "a/hi/ohayo") => 0; + lfs2_mkdir(&lfs2, "a/hi") => 0; + lfs2_mkdir(&lfs2, "a/hi/hola") => 0; + lfs2_mkdir(&lfs2, "a/hi/bonjour") => 0; + lfs2_mkdir(&lfs2, "a/hi/ohayo") => 0; - lfs_file_open(&lfs, &file[0], "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; - lfs_file_write(&lfs, &file[0], "hola\n", 5) => 5; - lfs_file_write(&lfs, &file[0], "bonjour\n", 8) => 8; - lfs_file_write(&lfs, &file[0], "ohayo\n", 6) => 6; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_open(&lfs2, &file[0], "a/hello", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; + lfs2_file_write(&lfs2, &file[0], "hola\n", 5) => 5; + lfs2_file_write(&lfs2, &file[0], "bonjour\n", 8) => 8; + lfs2_file_write(&lfs2, &file[0], "ohayo\n", 6) => 6; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move file ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "a/hello", "b/hello") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "a/hello", "b/hello") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "a") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "a") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hi") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "b") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "b") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move file corrupt source ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "b/hello", "c/hello") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "b/hello", "c/hello") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/corrupt.py -n 1 tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "b") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "b") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "c") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "c") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move file corrupt source and dest ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "c/hello", "d/hello") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "c/hello", "d/hello") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/corrupt.py -n 2 tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "c") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "c") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "d") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "d") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move file after corrupt ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "c/hello", "d/hello") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "c/hello", "d/hello") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "c") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "c") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "d") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "d") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move dir ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "a/hi", "b/hi") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "a/hi", "b/hi") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "a") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "a") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "b") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "b") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hi") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move dir corrupt source ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "b/hi", "c/hi") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "b/hi", "c/hi") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/corrupt.py -n 1 tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "b") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "b") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "c") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "c") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hi") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move dir corrupt source and dest ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "c/hi", "d/hi") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "c/hi", "d/hi") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/corrupt.py -n 2 tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "c") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "c") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hi") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "d") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "d") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move dir after corrupt ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_rename(&lfs, "c/hi", "d/hi") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_rename(&lfs2, "c/hi", "d/hi") => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "c") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "c") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "d") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_dir_open(&lfs2, &dir[0], "d") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hi") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move check ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "c/hi") => LFS_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "a/hi") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "b/hi") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "c/hi") => LFS2_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "d/hi") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "d/hi") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "bonjour") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hola") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "ohayo") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "c/hello") => LFS_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "a/hello") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "b/hello") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "c/hello") => LFS2_ERR_NOENT; - lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], buffer, 5) => 5; + lfs2_file_open(&lfs2, &file[0], "d/hello", LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, 5) => 5; memcmp(buffer, "hola\n", 5) => 0; - lfs_file_read(&lfs, &file[0], buffer, 8) => 8; + lfs2_file_read(&lfs2, &file[0], buffer, 8) => 8; memcmp(buffer, "bonjour\n", 8) => 0; - lfs_file_read(&lfs, &file[0], buffer, 6) => 6; + lfs2_file_read(&lfs2, &file[0], buffer, 6) => 6; memcmp(buffer, "ohayo\n", 6) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Move state stealing ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; - lfs_remove(&lfs, "b") => 0; - lfs_remove(&lfs, "c") => 0; + lfs2_remove(&lfs2, "b") => 0; + lfs2_remove(&lfs2, "c") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "a/hi") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "b") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "c") => LFS2_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "d/hi") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_open(&lfs2, &dir[0], "d/hi") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "bonjour") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "hola") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "ohayo") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; - lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT; - lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "a/hello") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "b") => LFS2_ERR_NOENT; + lfs2_dir_open(&lfs2, &dir[0], "c") => LFS2_ERR_NOENT; - lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0; - lfs_file_read(&lfs, &file[0], buffer, 5) => 5; + lfs2_file_open(&lfs2, &file[0], "d/hello", LFS2_O_RDONLY) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, 5) => 5; memcmp(buffer, "hola\n", 5) => 0; - lfs_file_read(&lfs, &file[0], buffer, 8) => 8; + lfs2_file_read(&lfs2, &file[0], buffer, 8) => 8; memcmp(buffer, "bonjour\n", 8) => 0; - lfs_file_read(&lfs, &file[0], buffer, 6) => 6; + lfs2_file_read(&lfs2, &file[0], buffer, 6) => 6; memcmp(buffer, "ohayo\n", 6) => 0; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST diff --git a/tests/test_orphan.sh b/tests/test_orphan.sh index 9c2cb7b..8c5b1f6 100755 --- a/tests/test_orphan.sh +++ b/tests/test_orphan.sh @@ -4,41 +4,41 @@ set -eu echo "=== Orphan tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST echo "--- Orphan test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "parent") => 0; - lfs_mkdir(&lfs, "parent/orphan") => 0; - lfs_mkdir(&lfs, "parent/child") => 0; - lfs_remove(&lfs, "parent/orphan") => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "parent") => 0; + lfs2_mkdir(&lfs2, "parent/orphan") => 0; + lfs2_mkdir(&lfs2, "parent/child") => 0; + lfs2_remove(&lfs2, "parent/orphan") => 0; TEST # corrupt most recent commit, this should be the update to the previous # linked-list entry and should orphan the child tests/corrupt.py tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; - lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; - lfs_ssize_t before = lfs_fs_size(&lfs); + lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; + lfs2_ssize_t before = lfs2_fs_size(&lfs2); before => 8; - lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs2_unmount(&lfs2) => 0; + lfs2_mount(&lfs2, &cfg) => 0; - lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; - lfs_ssize_t orphaned = lfs_fs_size(&lfs); + lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; + lfs2_ssize_t orphaned = lfs2_fs_size(&lfs2); orphaned => 8; - lfs_mkdir(&lfs, "parent/otherchild") => 0; + lfs2_mkdir(&lfs2, "parent/otherchild") => 0; - lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; - lfs_ssize_t deorphaned = lfs_fs_size(&lfs); + lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; + lfs2_ssize_t deorphaned = lfs2_fs_size(&lfs2); deorphaned => 8; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_paths.sh b/tests/test_paths.sh index 3cffcfe..c73baad 100755 --- a/tests/test_paths.sh +++ b/tests/test_paths.sh @@ -4,197 +4,197 @@ set -eu echo "=== Path tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "tea") => 0; - lfs_mkdir(&lfs, "coffee") => 0; - lfs_mkdir(&lfs, "soda") => 0; - lfs_mkdir(&lfs, "tea/hottea") => 0; - lfs_mkdir(&lfs, "tea/warmtea") => 0; - lfs_mkdir(&lfs, "tea/coldtea") => 0; - lfs_mkdir(&lfs, "coffee/hotcoffee") => 0; - lfs_mkdir(&lfs, "coffee/warmcoffee") => 0; - lfs_mkdir(&lfs, "coffee/coldcoffee") => 0; - lfs_mkdir(&lfs, "soda/hotsoda") => 0; - lfs_mkdir(&lfs, "soda/warmsoda") => 0; - lfs_mkdir(&lfs, "soda/coldsoda") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "tea") => 0; + lfs2_mkdir(&lfs2, "coffee") => 0; + lfs2_mkdir(&lfs2, "soda") => 0; + lfs2_mkdir(&lfs2, "tea/hottea") => 0; + lfs2_mkdir(&lfs2, "tea/warmtea") => 0; + lfs2_mkdir(&lfs2, "tea/coldtea") => 0; + lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0; + lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0; + lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0; + lfs2_mkdir(&lfs2, "soda/hotsoda") => 0; + lfs2_mkdir(&lfs2, "soda/warmsoda") => 0; + lfs2_mkdir(&lfs2, "soda/coldsoda") => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Root path tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "tea/hottea", &info) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "/tea/hottea", &info) => 0; + lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_mkdir(&lfs, "/milk1") => 0; - lfs_stat(&lfs, "/milk1", &info) => 0; + lfs2_mkdir(&lfs2, "/milk1") => 0; + lfs2_stat(&lfs2, "/milk1", &info) => 0; strcmp(info.name, "milk1") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Redundant slash path tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "/tea/hottea", &info) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "//tea//hottea", &info) => 0; + lfs2_stat(&lfs2, "//tea//hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "///tea///hottea", &info) => 0; + lfs2_stat(&lfs2, "///tea///hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_mkdir(&lfs, "///milk2") => 0; - lfs_stat(&lfs, "///milk2", &info) => 0; + lfs2_mkdir(&lfs2, "///milk2") => 0; + lfs2_stat(&lfs2, "///milk2", &info) => 0; strcmp(info.name, "milk2") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Dot path tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "./tea/hottea", &info) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "./tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "/./tea/hottea", &info) => 0; + lfs2_stat(&lfs2, "/./tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "/././tea/hottea", &info) => 0; + lfs2_stat(&lfs2, "/././tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "/./tea/./hottea", &info) => 0; + lfs2_stat(&lfs2, "/./tea/./hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_mkdir(&lfs, "/./milk3") => 0; - lfs_stat(&lfs, "/./milk3", &info) => 0; + lfs2_mkdir(&lfs2, "/./milk3") => 0; + lfs2_stat(&lfs2, "/./milk3", &info) => 0; strcmp(info.name, "milk3") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Dot dot path tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "coffee/../tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0; + lfs2_stat(&lfs2, "tea/coldtea/../hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "coffee/coldcoffee/../../tea/hottea", &info) => 0; + lfs2_stat(&lfs2, "coffee/coldcoffee/../../tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "coffee/../soda/../tea/hottea", &info) => 0; + lfs2_stat(&lfs2, "coffee/../soda/../tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_mkdir(&lfs, "coffee/../milk4") => 0; - lfs_stat(&lfs, "coffee/../milk4", &info) => 0; + lfs2_mkdir(&lfs2, "coffee/../milk4") => 0; + lfs2_stat(&lfs2, "coffee/../milk4", &info) => 0; strcmp(info.name, "milk4") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Trailing dot path tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "tea/hottea/", &info) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "tea/hottea/", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "tea/hottea/.", &info) => 0; + lfs2_stat(&lfs2, "tea/hottea/.", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "tea/hottea/./.", &info) => 0; + lfs2_stat(&lfs2, "tea/hottea/./.", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_stat(&lfs, "tea/hottea/..", &info) => 0; + lfs2_stat(&lfs2, "tea/hottea/..", &info) => 0; strcmp(info.name, "tea") => 0; - lfs_stat(&lfs, "tea/hottea/../.", &info) => 0; + lfs2_stat(&lfs2, "tea/hottea/../.", &info) => 0; strcmp(info.name, "tea") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Root dot dot path tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "coffee/../../../../../../tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; - lfs_mkdir(&lfs, "coffee/../../../../../../milk5") => 0; - lfs_stat(&lfs, "coffee/../../../../../../milk5", &info) => 0; + lfs2_mkdir(&lfs2, "coffee/../../../../../../milk5") => 0; + lfs2_stat(&lfs2, "coffee/../../../../../../milk5", &info) => 0; strcmp(info.name, "milk5") => 0; - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Root tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_stat(&lfs, "/", &info) => 0; - info.type => LFS_TYPE_DIR; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_stat(&lfs2, "/", &info) => 0; + info.type => LFS2_TYPE_DIR; strcmp(info.name, "/") => 0; - lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST; - lfs_file_open(&lfs, &file[0], "/", LFS_O_WRONLY | LFS_O_CREAT) - => LFS_ERR_ISDIR; + lfs2_mkdir(&lfs2, "/") => LFS2_ERR_EXIST; + lfs2_file_open(&lfs2, &file[0], "/", LFS2_O_WRONLY | LFS2_O_CREAT) + => LFS2_ERR_ISDIR; // more corner cases - lfs_remove(&lfs, "") => LFS_ERR_INVAL; - lfs_remove(&lfs, ".") => LFS_ERR_INVAL; - lfs_remove(&lfs, "..") => LFS_ERR_INVAL; - lfs_remove(&lfs, "/") => LFS_ERR_INVAL; - lfs_remove(&lfs, "//") => LFS_ERR_INVAL; - lfs_remove(&lfs, "./") => LFS_ERR_INVAL; - lfs_unmount(&lfs) => 0; + lfs2_remove(&lfs2, "") => LFS2_ERR_INVAL; + lfs2_remove(&lfs2, ".") => LFS2_ERR_INVAL; + lfs2_remove(&lfs2, "..") => LFS2_ERR_INVAL; + lfs2_remove(&lfs2, "/") => LFS2_ERR_INVAL; + lfs2_remove(&lfs2, "//") => LFS2_ERR_INVAL; + lfs2_remove(&lfs2, "./") => LFS2_ERR_INVAL; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Sketchy path tests ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "dirt/ground") => LFS_ERR_NOENT; - lfs_mkdir(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "dirt/ground") => LFS2_ERR_NOENT; + lfs2_mkdir(&lfs2, "dirt/ground/earth") => LFS2_ERR_NOENT; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Superblock conflict test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "littlefs") => 0; - lfs_remove(&lfs, "littlefs") => 0; - lfs_unmount(&lfs) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "littlefs") => 0; + lfs2_remove(&lfs2, "littlefs") => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Max path test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - memset(buffer, 'w', LFS_NAME_MAX+1); - buffer[LFS_NAME_MAX+2] = '\0'; - lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG; + lfs2_mount(&lfs2, &cfg) => 0; + memset(buffer, 'w', LFS2_NAME_MAX+1); + buffer[LFS2_NAME_MAX+2] = '\0'; + lfs2_mkdir(&lfs2, (char*)buffer) => LFS2_ERR_NAMETOOLONG; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_NAMETOOLONG; memcpy(buffer, "coffee/", strlen("coffee/")); - memset(buffer+strlen("coffee/"), 'w', LFS_NAME_MAX+1); - buffer[strlen("coffee/")+LFS_NAME_MAX+2] = '\0'; - lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG; - lfs_unmount(&lfs) => 0; + memset(buffer+strlen("coffee/"), 'w', LFS2_NAME_MAX+1); + buffer[strlen("coffee/")+LFS2_NAME_MAX+2] = '\0'; + lfs2_mkdir(&lfs2, (char*)buffer) => LFS2_ERR_NAMETOOLONG; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_NAMETOOLONG; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Really big path test ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - memset(buffer, 'w', LFS_NAME_MAX); - buffer[LFS_NAME_MAX+1] = '\0'; - lfs_mkdir(&lfs, (char*)buffer) => 0; - lfs_remove(&lfs, (char*)buffer) => 0; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_remove(&lfs, (char*)buffer) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + memset(buffer, 'w', LFS2_NAME_MAX); + buffer[LFS2_NAME_MAX+1] = '\0'; + lfs2_mkdir(&lfs2, (char*)buffer) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; memcpy(buffer, "coffee/", strlen("coffee/")); - memset(buffer+strlen("coffee/"), 'w', LFS_NAME_MAX); - buffer[strlen("coffee/")+LFS_NAME_MAX+1] = '\0'; - lfs_mkdir(&lfs, (char*)buffer) => 0; - lfs_remove(&lfs, (char*)buffer) => 0; - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_remove(&lfs, (char*)buffer) => 0; - lfs_unmount(&lfs) => 0; + memset(buffer+strlen("coffee/"), 'w', LFS2_NAME_MAX); + buffer[strlen("coffee/")+LFS2_NAME_MAX+1] = '\0'; + lfs2_mkdir(&lfs2, (char*)buffer) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_remove(&lfs2, (char*)buffer) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_seek.sh b/tests/test_seek.sh index 2cd711a..48be258 100755 --- a/tests/test_seek.sh +++ b/tests/test_seek.sh @@ -8,353 +8,353 @@ LARGESIZE=132 echo "=== Seek tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "hello") => 0; + lfs2_format(&lfs2, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_mkdir(&lfs2, "hello") => 0; for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "hello/kitty%03d", i); - lfs_file_open(&lfs, &file[0], (char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; + lfs2_file_open(&lfs2, &file[0], (char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; size = strlen("kittycatcat"); memcpy(buffer, "kittycatcat", size); for (int j = 0; j < $LARGESIZE; j++) { - lfs_file_write(&lfs, &file[0], buffer, size); + lfs2_file_write(&lfs2, &file[0], buffer, size); } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Simple dir seek ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "hello") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_soff_t pos; + lfs2_soff_t pos; int i; for (i = 0; i < $SMALLSIZE; i++) { sprintf((char*)buffer, "kitty%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - pos = lfs_dir_tell(&lfs, &dir[0]); + pos = lfs2_dir_tell(&lfs2, &dir[0]); } pos >= 0 => 1; - lfs_dir_seek(&lfs, &dir[0], pos) => 0; + lfs2_dir_seek(&lfs2, &dir[0], pos) => 0; sprintf((char*)buffer, "kitty%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - lfs_dir_rewind(&lfs, &dir[0]) => 0; + lfs2_dir_rewind(&lfs2, &dir[0]) => 0; sprintf((char*)buffer, "kitty%03d", 0); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - lfs_dir_seek(&lfs, &dir[0], pos) => 0; + lfs2_dir_seek(&lfs2, &dir[0], pos) => 0; sprintf((char*)buffer, "kitty%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Large dir seek ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_dir_open(&lfs, &dir[0], "hello") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_dir_open(&lfs2, &dir[0], "hello") => 0; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_soff_t pos; + lfs2_soff_t pos; int i; for (i = 0; i < $MEDIUMSIZE; i++) { sprintf((char*)buffer, "kitty%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - pos = lfs_dir_tell(&lfs, &dir[0]); + pos = lfs2_dir_tell(&lfs2, &dir[0]); } pos >= 0 => 1; - lfs_dir_seek(&lfs, &dir[0], pos) => 0; + lfs2_dir_seek(&lfs2, &dir[0], pos) => 0; sprintf((char*)buffer, "kitty%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - lfs_dir_rewind(&lfs, &dir[0]) => 0; + lfs2_dir_rewind(&lfs2, &dir[0]) => 0; sprintf((char*)buffer, "kitty%03d", 0); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, ".") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, "..") => 0; - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - lfs_dir_seek(&lfs, &dir[0], pos) => 0; + lfs2_dir_seek(&lfs2, &dir[0], pos) => 0; sprintf((char*)buffer, "kitty%03d", i); - lfs_dir_read(&lfs, &dir[0], &info) => 1; + lfs2_dir_read(&lfs2, &dir[0], &info) => 1; strcmp(info.name, (char*)buffer) => 0; - lfs_dir_close(&lfs, &dir[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_dir_close(&lfs2, &dir[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Simple file seek ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDONLY) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDONLY) => 0; - lfs_soff_t pos; + lfs2_soff_t pos; size = strlen("kittycatcat"); for (int i = 0; i < $SMALLSIZE; i++) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - pos = lfs_file_tell(&lfs, &file[0]); + pos = lfs2_file_tell(&lfs2, &file[0]); } pos >= 0 => 1; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_rewind(&lfs, &file[0]) => 0; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_rewind(&lfs2, &file[0]) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], size, LFS_SEEK_CUR) => 3*size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], size, LFS2_SEEK_CUR) => 3*size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_CUR) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - size = lfs_file_size(&lfs, &file[0]); - lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; + size = lfs2_file_size(&lfs2, &file[0]); + lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Large file seek ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDONLY) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDONLY) => 0; - lfs_soff_t pos; + lfs2_soff_t pos; size = strlen("kittycatcat"); for (int i = 0; i < $MEDIUMSIZE; i++) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - pos = lfs_file_tell(&lfs, &file[0]); + pos = lfs2_file_tell(&lfs2, &file[0]); } pos >= 0 => 1; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_rewind(&lfs, &file[0]) => 0; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_rewind(&lfs2, &file[0]) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], size, LFS_SEEK_CUR) => 3*size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], size, LFS2_SEEK_CUR) => 3*size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_CUR) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - size = lfs_file_size(&lfs, &file[0]); - lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; + size = lfs2_file_size(&lfs2, &file[0]); + lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Simple file seek and write ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0; - lfs_soff_t pos; + lfs2_soff_t pos; size = strlen("kittycatcat"); for (int i = 0; i < $SMALLSIZE; i++) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - pos = lfs_file_tell(&lfs, &file[0]); + pos = lfs2_file_tell(&lfs2, &file[0]); } pos >= 0 => 1; memcpy(buffer, "doggodogdog", size); - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "doggodogdog", size) => 0; - lfs_file_rewind(&lfs, &file[0]) => 0; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_rewind(&lfs2, &file[0]) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "doggodogdog", size) => 0; - lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - size = lfs_file_size(&lfs, &file[0]); - lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; + size = lfs2_file_size(&lfs2, &file[0]); + lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Large file seek and write ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0; - lfs_soff_t pos; + lfs2_soff_t pos; size = strlen("kittycatcat"); for (int i = 0; i < $MEDIUMSIZE; i++) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; if (i != $SMALLSIZE) { memcmp(buffer, "kittycatcat", size) => 0; } - pos = lfs_file_tell(&lfs, &file[0]); + pos = lfs2_file_tell(&lfs2, &file[0]); } pos >= 0 => 1; memcpy(buffer, "doggodogdog", size); - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "doggodogdog", size) => 0; - lfs_file_rewind(&lfs, &file[0]) => 0; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_rewind(&lfs2, &file[0]) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "doggodogdog", size) => 0; - lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - size = lfs_file_size(&lfs, &file[0]); - lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; + size = lfs2_file_size(&lfs2, &file[0]); + lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Boundary seek and write ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0; size = strlen("hedgehoghog"); - const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; + const lfs2_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { - lfs_soff_t off = offsets[i]; + lfs2_soff_t off = offsets[i]; memcpy(buffer, "hedgehoghog", size); - lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off; - lfs_file_write(&lfs, &file[0], buffer, size) => size; - lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], off, LFS2_SEEK_SET) => off; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], off, LFS2_SEEK_SET) => off; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "hedgehoghog", size) => 0; - lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_SET) => 0; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_SET) => 0; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "kittycatcat", size) => 0; - lfs_file_sync(&lfs, &file[0]) => 0; + lfs2_file_sync(&lfs2, &file[0]) => 0; } - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Out-of-bounds seek ---" tests/test.py << TEST - lfs_mount(&lfs, &cfg) => 0; - lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0; + lfs2_mount(&lfs2, &cfg) => 0; + lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0; size = strlen("kittycatcat"); - lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size; - lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size, - LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; - lfs_file_read(&lfs, &file[0], buffer, size) => 0; + lfs2_file_size(&lfs2, &file[0]) => $LARGESIZE*size; + lfs2_file_seek(&lfs2, &file[0], ($LARGESIZE+$SMALLSIZE)*size, + LFS2_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => 0; memcpy(buffer, "porcupineee", size); - lfs_file_write(&lfs, &file[0], buffer, size) => size; + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; - lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size, - LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], ($LARGESIZE+$SMALLSIZE)*size, + LFS2_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "porcupineee", size) => 0; - lfs_file_seek(&lfs, &file[0], $LARGESIZE*size, - LFS_SEEK_SET) => $LARGESIZE*size; - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_seek(&lfs2, &file[0], $LARGESIZE*size, + LFS2_SEEK_SET) => $LARGESIZE*size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0; - lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+$SMALLSIZE)*size), - LFS_SEEK_CUR) => LFS_ERR_INVAL; - lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size; + lfs2_file_seek(&lfs2, &file[0], -(($LARGESIZE+$SMALLSIZE)*size), + LFS2_SEEK_CUR) => LFS2_ERR_INVAL; + lfs2_file_tell(&lfs2, &file[0]) => ($LARGESIZE+1)*size; - lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+2*$SMALLSIZE)*size), - LFS_SEEK_END) => LFS_ERR_INVAL; - lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size; + lfs2_file_seek(&lfs2, &file[0], -(($LARGESIZE+2*$SMALLSIZE)*size), + LFS2_SEEK_END) => LFS2_ERR_INVAL; + lfs2_file_tell(&lfs2, &file[0]) => ($LARGESIZE+1)*size; - lfs_file_close(&lfs, &file[0]) => 0; - lfs_unmount(&lfs) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; + lfs2_unmount(&lfs2) => 0; TEST echo "--- Results ---" diff --git a/tests/test_truncate.sh b/tests/test_truncate.sh index 053b2e0..28381a3 100755 --- a/tests/test_truncate.sh +++ b/tests/test_truncate.sh @@ -8,7 +8,7 @@ LARGESIZE=8192 echo "=== Truncate tests ===" rm -rf blocks tests/test.py << TEST - lfs_format(&lfs, &cfg) => 0; + lfs2_format(&lfs2, &cfg) => 0; TEST truncate_test() { @@ -17,98 +17,98 @@ STARTSEEKS="$2" HOTSIZES="$3" COLDSIZES="$4" tests/test.py << TEST - static const lfs_off_t startsizes[] = {$STARTSIZES}; - static const lfs_off_t startseeks[] = {$STARTSEEKS}; - static const lfs_off_t hotsizes[] = {$HOTSIZES}; + static const lfs2_off_t startsizes[] = {$STARTSIZES}; + static const lfs2_off_t startseeks[] = {$STARTSEEKS}; + static const lfs2_off_t hotsizes[] = {$HOTSIZES}; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { sprintf((char*)buffer, "hairyhead%d", i); - lfs_file_open(&lfs, &file[0], (const char*)buffer, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + lfs2_file_open(&lfs2, &file[0], (const char*)buffer, + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; strcpy((char*)buffer, "hair"); size = strlen((char*)buffer); - for (lfs_off_t j = 0; j < startsizes[i]; j += size) { - lfs_file_write(&lfs, &file[0], buffer, size) => size; + for (lfs2_off_t j = 0; j < startsizes[i]; j += size) { + lfs2_file_write(&lfs2, &file[0], buffer, size) => size; } - lfs_file_size(&lfs, &file[0]) => startsizes[i]; + lfs2_file_size(&lfs2, &file[0]) => startsizes[i]; if (startseeks[i] != startsizes[i]) { - lfs_file_seek(&lfs, &file[0], - startseeks[i], LFS_SEEK_SET) => startseeks[i]; + lfs2_file_seek(&lfs2, &file[0], + startseeks[i], LFS2_SEEK_SET) => startseeks[i]; } - lfs_file_truncate(&lfs, &file[0], hotsizes[i]) => 0; - lfs_file_size(&lfs, &file[0]) => hotsizes[i]; + lfs2_file_truncate(&lfs2, &file[0], hotsizes[i]) => 0; + lfs2_file_size(&lfs2, &file[0]) => hotsizes[i]; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - static const lfs_off_t startsizes[] = {$STARTSIZES}; - static const lfs_off_t hotsizes[] = {$HOTSIZES}; - static const lfs_off_t coldsizes[] = {$COLDSIZES}; + static const lfs2_off_t startsizes[] = {$STARTSIZES}; + static const lfs2_off_t hotsizes[] = {$HOTSIZES}; + static const lfs2_off_t coldsizes[] = {$COLDSIZES}; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { sprintf((char*)buffer, "hairyhead%d", i); - lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0; - lfs_file_size(&lfs, &file[0]) => hotsizes[i]; + lfs2_file_open(&lfs2, &file[0], (const char*)buffer, LFS2_O_RDWR) => 0; + lfs2_file_size(&lfs2, &file[0]) => hotsizes[i]; size = strlen("hair"); - lfs_off_t j = 0; + lfs2_off_t j = 0; for (; j < startsizes[i] && j < hotsizes[i]; j += size) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "hair", size) => 0; } for (; j < hotsizes[i]; j += size) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "\0\0\0\0", size) => 0; } - lfs_file_truncate(&lfs, &file[0], coldsizes[i]) => 0; - lfs_file_size(&lfs, &file[0]) => coldsizes[i]; + lfs2_file_truncate(&lfs2, &file[0], coldsizes[i]) => 0; + lfs2_file_size(&lfs2, &file[0]) => coldsizes[i]; - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST tests/test.py << TEST - static const lfs_off_t startsizes[] = {$STARTSIZES}; - static const lfs_off_t hotsizes[] = {$HOTSIZES}; - static const lfs_off_t coldsizes[] = {$COLDSIZES}; + static const lfs2_off_t startsizes[] = {$STARTSIZES}; + static const lfs2_off_t hotsizes[] = {$HOTSIZES}; + static const lfs2_off_t coldsizes[] = {$COLDSIZES}; - lfs_mount(&lfs, &cfg) => 0; + lfs2_mount(&lfs2, &cfg) => 0; for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { sprintf((char*)buffer, "hairyhead%d", i); - lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0; - lfs_file_size(&lfs, &file[0]) => coldsizes[i]; + lfs2_file_open(&lfs2, &file[0], (const char*)buffer, LFS2_O_RDONLY) => 0; + lfs2_file_size(&lfs2, &file[0]) => coldsizes[i]; size = strlen("hair"); - lfs_off_t j = 0; + lfs2_off_t j = 0; for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; j += size) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "hair", size) => 0; } for (; j < coldsizes[i]; j += size) { - lfs_file_read(&lfs, &file[0], buffer, size) => size; + lfs2_file_read(&lfs2, &file[0], buffer, size) => size; memcmp(buffer, "\0\0\0\0", size) => 0; } - lfs_file_close(&lfs, &file[0]) => 0; + lfs2_file_close(&lfs2, &file[0]) => 0; } - lfs_unmount(&lfs) => 0; + lfs2_unmount(&lfs2) => 0; TEST }