Compare commits

..

8 Commits

Author SHA1 Message Date
Christopher Haster
b9e403d55c WIP Cleanup 2020-02-09 10:02:41 -06:00
Christopher Haster
d58aaf88dc WIP fixed lfs_fs_size doubling metadata-pairs 2020-02-09 09:05:37 -06:00
Christopher Haster
71c844be53 WIP fixed broken wear-leveling when block_cycles = 2n-1 2020-02-09 08:52:20 -06:00
Christopher Haster
75cd51b39e Modified readmdir/readtree to make reading non-truncated data easier 2020-01-30 16:05:42 -06:00
Christopher Haster
fc354801fa WIP: Removed outlining in file sync 2020-01-29 22:05:58 -06:00
Christopher Haster
557ec332fe WIP fixed .gitignore tests_/* 2020-01-29 17:56:58 -06:00
Christopher Haster
5e839df234 WIP Added/fixed tests for noop writes (where bd error can't be trusted) 2020-01-29 17:51:25 -06:00
Christopher Haster
47ab0426b1 WIP fixed some more things 2020-01-29 01:45:19 -06:00
29 changed files with 622 additions and 1212 deletions

2
.gitignore vendored
View File

@@ -8,5 +8,3 @@ blocks/
lfs lfs
test.c test.c
tests/*.toml.* tests/*.toml.*
scripts/__pycache__
.gdb_history

View File

@@ -1,70 +1,49 @@
# environment variables # Environment variables
env: env:
global: global:
- CFLAGS=-Werror - CFLAGS=-Werror
- MAKEFLAGS=-j
# cache installation dirs # Common test script
cache: script:
pip: true
directories:
- $HOME/.cache/apt
# common installation
_: &install-common
# need toml, also pip3 isn't installed by default?
- sudo apt-get install python3 python3-pip
- sudo pip3 install toml
# setup a ram-backed disk to speed up reentrant tests
- mkdir disks
- sudo mount -t tmpfs -o size=100m tmpfs disks
- export TFLAGS="$TFLAGS --disk=disks/disk"
# test cases
_: &test-example
# make sure example can at least compile # make sure example can at least compile
- sed -n '/``` c/,/```/{/```/d; p}' README.md > test.c && - sed -n '/``` c/,/```/{/```/d; p;}' README.md > test.c &&
make all CFLAGS+=" make all CFLAGS+="
-Duser_provided_block_device_read=NULL -Duser_provided_block_device_read=NULL
-Duser_provided_block_device_prog=NULL -Duser_provided_block_device_prog=NULL
-Duser_provided_block_device_erase=NULL -Duser_provided_block_device_erase=NULL
-Duser_provided_block_device_sync=NULL -Duser_provided_block_device_sync=NULL
-include stdio.h" -include stdio.h"
# default tests
_: &test-default
# normal+reentrant tests
- make test TFLAGS+="-nrk"
# common real-life geometries
_: &test-nor
# NOR flash: read/prog = 1 block = 4KiB
- make test TFLAGS+="-nrk -DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096"
_: &test-emmc
# eMMC: read/prog = 512 block = 512
- make test TFLAGS+="-nrk -DLFS_READ_SIZE=512 -DLFS_BLOCK_SIZE=512"
_: &test-nand
# NAND flash: read/prog = 4KiB block = 32KiB
- make test TFLAGS+="-nrk -DLFS_READ_SIZE=4096 -DLFS_BLOCK_SIZE=\(32*1024\)"
# other extreme geometries that are useful for testing various corner cases
_: &test-no-intrinsics
- make test TFLAGS+="-nrk -DLFS_NO_INTRINSICS"
_: &test-no-inline
- make test TFLAGS+="-nrk -DLFS_INLINE_MAX=0"
_: &test-byte-writes
- make test TFLAGS+="-nrk -DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=1"
_: &test-block-cycles
- make test TFLAGS+="-nrk -DLFS_BLOCK_CYCLES=1"
_: &test-odd-block-count
- make test TFLAGS+="-nrk -DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
_: &test-odd-block-size
- make test TFLAGS+="-nrk -DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704"
# report size # run tests
_: &report-size - 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_READ_SIZE=8 -DLFS_CACHE_SIZE=16 -DLFS_BLOCK_CYCLES=2"
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
- make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
- make clean test QUIET=1 CFLAGS+="-DLFS_EMUBD_ERASE_VALUE=0xff"
- make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS"
# additional configurations that don't support all tests (this should be
# fixed but at the moment it is what it is)
- make test_files QUIET=1
CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096"
- make test_files QUIET=1
CFLAGS+="-DLFS_READ_SIZE=\(2*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)"
- make test_files QUIET=1
CFLAGS+="-DLFS_READ_SIZE=\(8*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)"
- make test_files QUIET=1
CFLAGS+="-DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704"
# compile and find the code size with the smallest configuration # compile and find the code size with the smallest configuration
- make -j1 clean size - make clean size
OBJ="$(ls lfs*.c | sed 's/\.c/\.o/' | tr '\n' ' ')" OBJ="$(ls lfs*.o | tr '\n' ' ')"
CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR" CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
| tee sizes | tee sizes
# update status if we succeeded, compare with master if possible # update status if we succeeded, compare with master if possible
- | - |
if [ "$TRAVIS_TEST_RESULT" -eq 0 ] if [ "$TRAVIS_TEST_RESULT" -eq 0 ]
@@ -72,10 +51,10 @@ _: &report-size
CURR=$(tail -n1 sizes | awk '{print $1}') CURR=$(tail -n1 sizes | awk '{print $1}')
PREV=$(curl -u "$GEKY_BOT_STATUSES" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ PREV=$(curl -u "$GEKY_BOT_STATUSES" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\") | jq -re "select(.sha != \"$TRAVIS_COMMIT\")
| .statuses[] | select(.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\").description | .statuses[] | select(.context == \"$STAGE/$NAME\").description
| capture(\"code size is (?<size>[0-9]+)\").size" \ | capture(\"code size is (?<size>[0-9]+)\").size" \
|| echo 0) || echo 0)
STATUS="Passed, code size is ${CURR}B" STATUS="Passed, code size is ${CURR}B"
if [ "$PREV" -ne 0 ] if [ "$PREV" -ne 0 ]
then then
@@ -83,347 +62,257 @@ _: &report-size
fi fi
fi fi
# stage control # CI matrix
stages:
- name: test
- name: deploy
if: branch = master AND type = push
# job control
jobs: jobs:
# native testing include:
- &x86 # native testing
stage: test - stage: test
env: env:
- NAME=littlefs-x86 - STAGE=test
install: *install-common - NAME=littlefs-x86
script: [*test-example, *report-size]
- {<<: *x86, script: [*test-default, *report-size]}
- {<<: *x86, script: [*test-nor, *report-size]}
- {<<: *x86, script: [*test-emmc, *report-size]}
- {<<: *x86, script: [*test-nand, *report-size]}
- {<<: *x86, script: [*test-no-intrinsics, *report-size]}
- {<<: *x86, script: [*test-no-inline, *report-size]}
- {<<: *x86, script: [*test-byte-writes, *report-size]}
- {<<: *x86, script: [*test-block-cycles, *report-size]}
- {<<: *x86, script: [*test-odd-block-count, *report-size]}
- {<<: *x86, script: [*test-odd-block-size, *report-size]}
# cross-compile with ARM (thumb mode) # cross-compile with ARM (thumb mode)
- &arm - stage: test
stage: test env:
env: - STAGE=test
- NAME=littlefs-arm - NAME=littlefs-arm
- CC="arm-linux-gnueabi-gcc --static -mthumb" - CC="arm-linux-gnueabi-gcc --static -mthumb"
- TFLAGS="$TFLAGS --exec=qemu-arm" - EXEC="qemu-arm"
install: install:
- *install-common - sudo apt-get install
- sudo apt-get install gcc-arm-linux-gnueabi
gcc-arm-linux-gnueabi libc6-dev-armel-cross
libc6-dev-armel-cross qemu-user
qemu-user - arm-linux-gnueabi-gcc --version
- arm-linux-gnueabi-gcc --version - qemu-arm -version
- qemu-arm -version
script: [*test-example, *report-size]
- {<<: *arm, script: [*test-default, *report-size]}
- {<<: *arm, script: [*test-nor, *report-size]}
- {<<: *arm, script: [*test-emmc, *report-size]}
- {<<: *arm, script: [*test-nand, *report-size]}
- {<<: *arm, script: [*test-no-intrinsics, *report-size]}
- {<<: *arm, script: [*test-no-inline, *report-size]}
# it just takes way to long to run byte-level writes in qemu,
# note this is still tested in the native tests
#- {<<: *arm, script: [*test-byte-writes, *report-size]}
- {<<: *arm, script: [*test-block-cycles, *report-size]}
- {<<: *arm, script: [*test-odd-block-count, *report-size]}
- {<<: *arm, script: [*test-odd-block-size, *report-size]}
# cross-compile with MIPS # cross-compile with PowerPC
- &mips - stage: test
stage: test env:
env: - STAGE=test
- NAME=littlefs-mips - NAME=littlefs-powerpc
- CC="mips-linux-gnu-gcc --static" - CC="powerpc-linux-gnu-gcc --static"
- TFLAGS="$TFLAGS --exec=qemu-mips" - EXEC="qemu-ppc"
install: install:
- *install-common - sudo apt-get install
- sudo apt-get install gcc-powerpc-linux-gnu
gcc-mips-linux-gnu libc6-dev-powerpc-cross
libc6-dev-mips-cross qemu-user
qemu-user - powerpc-linux-gnu-gcc --version
- mips-linux-gnu-gcc --version - qemu-ppc -version
- qemu-mips -version
script: [*test-example, *report-size]
- {<<: *mips, script: [*test-default, *report-size]}
- {<<: *mips, script: [*test-nor, *report-size]}
- {<<: *mips, script: [*test-emmc, *report-size]}
- {<<: *mips, script: [*test-nand, *report-size]}
- {<<: *mips, script: [*test-no-intrinsics, *report-size]}
- {<<: *mips, script: [*test-no-inline, *report-size]}
# it just takes way to long to run byte-level writes in qemu,
# note this is still tested in the native tests
#- {<<: *mips, script: [*test-byte-writes, *report-size]}
- {<<: *mips, script: [*test-block-cycles, *report-size]}
- {<<: *mips, script: [*test-odd-block-count, *report-size]}
- {<<: *mips, script: [*test-odd-block-size, *report-size]}
# cross-compile with PowerPC # cross-compile with MIPS
- &powerpc - stage: test
stage: test env:
env: - STAGE=test
- NAME=littlefs-powerpc - NAME=littlefs-mips
- CC="powerpc-linux-gnu-gcc --static" - CC="mips-linux-gnu-gcc --static"
- TFLAGS="$TFLAGS --exec=qemu-ppc" - EXEC="qemu-mips"
install: install:
- *install-common - sudo apt-get install
- sudo apt-get install gcc-mips-linux-gnu
gcc-powerpc-linux-gnu libc6-dev-mips-cross
libc6-dev-powerpc-cross qemu-user
qemu-user - mips-linux-gnu-gcc --version
- powerpc-linux-gnu-gcc --version - qemu-mips -version
- qemu-ppc -version
script: [*test-example, *report-size]
- {<<: *powerpc, script: [*test-default, *report-size]}
- {<<: *powerpc, script: [*test-nor, *report-size]}
- {<<: *powerpc, script: [*test-emmc, *report-size]}
- {<<: *powerpc, script: [*test-nand, *report-size]}
- {<<: *powerpc, script: [*test-no-intrinsics, *report-size]}
- {<<: *powerpc, script: [*test-no-inline, *report-size]}
# it just takes way to long to run byte-level writes in qemu,
# note this is still tested in the native tests
#- {<<: *powerpc, script: [*test-byte-writes, *report-size]}
- {<<: *powerpc, script: [*test-block-cycles, *report-size]}
- {<<: *powerpc, script: [*test-odd-block-count, *report-size]}
- {<<: *powerpc, script: [*test-odd-block-size, *report-size]}
# test under valgrind, checking for memory errors # self-host with littlefs-fuse for fuzz test
- &valgrind - stage: test
stage: test env:
env: - STAGE=test
- NAME=littlefs-valgrind - NAME=littlefs-fuse
install: if: branch !~ -prefix$
- *install-common install:
- sudo apt-get install valgrind - sudo apt-get install libfuse-dev
- valgrind --version - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2
script: - fusermount -V
- make test TFLAGS+="-k --valgrind" - gcc --version
before_script:
# setup disk for littlefs-fuse
- rm -rf littlefs-fuse/littlefs/*
- cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
# self-host with littlefs-fuse for fuzz test - mkdir mount
- stage: test - sudo chmod a+rw /dev/loop0
env: - dd if=/dev/zero bs=512 count=4096 of=disk
- NAME=littlefs-fuse - losetup /dev/loop0 disk
if: branch !~ -prefix$ script:
install: # self-host test
- *install-common - make -C littlefs-fuse
- sudo apt-get install libfuse-dev
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2
- fusermount -V
- gcc --version
# setup disk for littlefs-fuse - littlefs-fuse/lfs --format /dev/loop0
- rm -rf littlefs-fuse/littlefs/* - littlefs-fuse/lfs /dev/loop0 mount
- cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
- mkdir mount - ls mount
- sudo chmod a+rw /dev/loop0 - mkdir mount/littlefs
- dd if=/dev/zero bs=512 count=128K of=disk - cp -r $(git ls-tree --name-only HEAD) mount/littlefs
- losetup /dev/loop0 disk - cd mount/littlefs
script: - stat .
# self-host test - ls -flh
- make -C littlefs-fuse - make -B test_dirs test_files QUIET=1
- littlefs-fuse/lfs --format /dev/loop0 # self-host with littlefs-fuse for fuzz test
- littlefs-fuse/lfs /dev/loop0 mount - stage: test
env:
- STAGE=test
- NAME=littlefs-migration
if: branch !~ -prefix$
install:
- sudo apt-get install libfuse-dev
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1
- fusermount -V
- gcc --version
before_script:
# setup disk for littlefs-fuse
- rm -rf v2/littlefs/*
- cp -r $(git ls-tree --name-only HEAD) v2/littlefs
- ls mount - mkdir mount
- mkdir mount/littlefs - sudo chmod a+rw /dev/loop0
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs - dd if=/dev/zero bs=512 count=4096 of=disk
- cd mount/littlefs - losetup /dev/loop0 disk
- stat . script:
- ls -flh # compile v1 and v2
- make -B test - make -C v1
- make -C v2
# test migration using littlefs-fuse # run self-host test with v1
- stage: test - v1/lfs --format /dev/loop0
env: - v1/lfs /dev/loop0 mount
- NAME=littlefs-migration
if: branch !~ -prefix$
install:
- *install-common
- sudo apt-get install libfuse-dev
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1
- fusermount -V
- gcc --version
# setup disk for littlefs-fuse - ls mount
- rm -rf v2/littlefs/* - mkdir mount/littlefs
- cp -r $(git ls-tree --name-only HEAD) v2/littlefs - cp -r $(git ls-tree --name-only HEAD) mount/littlefs
- cd mount/littlefs
- stat .
- ls -flh
- make -B test_dirs test_files QUIET=1
- mkdir mount # attempt to migrate
- sudo chmod a+rw /dev/loop0 - cd ../..
- dd if=/dev/zero bs=512 count=128K of=disk - fusermount -u mount
- losetup /dev/loop0 disk
script:
# compile v1 and v2
- make -C v1
- make -C v2
# run self-host test with v1 - v2/lfs --migrate /dev/loop0
- v1/lfs --format /dev/loop0 - v2/lfs /dev/loop0 mount
- v1/lfs /dev/loop0 mount
- ls mount # run self-host test with v2 right where we left off
- mkdir mount/littlefs - ls mount
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs - cd mount/littlefs
- cd mount/littlefs - stat .
- stat . - ls -flh
- ls -flh - make -B test_dirs test_files QUIET=1
- make -B test
# attempt to migrate # Automatically create releases
- cd ../.. - stage: deploy
- fusermount -u mount env:
- STAGE=deploy
- NAME=deploy
script:
- |
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)))
# 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=$(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" \
| 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"
# 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
# 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 $( \
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")
git reset --hard
# Update major version branches (vN and vN-prefix)
git push --atomic https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
v$LFS_VERSION_MAJOR \
v$LFS_VERSION_MAJOR-prefix
# Build release notes
PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
if [ ! -z "$PREV" ]
then
echo "PREV $PREV"
CHANGES=$(git log --oneline $PREV.. --grep='^Merge' --invert-grep)
printf "CHANGES\n%s\n\n" "$CHANGES"
fi
case ${GEKY_BOT_DRAFT:-minor} in
true) DRAFT=true ;;
minor) DRAFT=$(jq -R 'endswith(".0")' <<< "$LFS_VERSION") ;;
false) DRAFT=false ;;
esac
# Create the release and patch version tag (vN.N.N)
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}\",
\"target_commitish\": \"$TRAVIS_COMMIT\",
\"draft\": $DRAFT,
\"body\": $(jq -sR '.' <<< "$CHANGES")
}" #"
SCRIPT
- v2/lfs --migrate /dev/loop0 # Manage statuses
- v2/lfs /dev/loop0 mount
# run self-host test with v2 right where we left off
- ls mount
- cd mount/littlefs
- stat .
- ls -flh
- make -B test
# automatically create releases
- stage: deploy
env:
- NAME=deploy
script:
- |
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)))
# 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=$(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" \
| 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"
# 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
# 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 $( \
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")
git reset --hard
# Update major version branches (vN and vN-prefix)
git push --atomic https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
v$LFS_VERSION_MAJOR \
v$LFS_VERSION_MAJOR-prefix
# Build release notes
PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
if [ ! -z "$PREV" ]
then
echo "PREV $PREV"
CHANGES=$(git log --oneline $PREV.. --grep='^Merge' --invert-grep)
printf "CHANGES\n%s\n\n" "$CHANGES"
fi
case ${GEKY_BOT_DRAFT:-minor} in
true) DRAFT=true ;;
minor) DRAFT=$(jq -R 'endswith(".0")' <<< "$LFS_VERSION") ;;
false) DRAFT=false ;;
esac
# Create the release and patch version tag (vN.N.N)
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}\",
\"target_commitish\": \"$TRAVIS_COMMIT\",
\"draft\": $DRAFT,
\"body\": $(jq -sR '.' <<< "$CHANGES")
}" #"
SCRIPT
# manage statuses
before_install: before_install:
- | - |
# don't clobber other (not us) failures curl -u "$GEKY_BOT_STATUSES" -X POST \
if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
| jq -e ".statuses[] | select( -d "{
.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and \"context\": \"$STAGE/$NAME\",
.state == \"failure\" and \"state\": \"pending\",
(.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))" \"description\": \"${STATUS:-In progress}\",
then \"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
curl -u "$GEKY_BOT_STATUSES" -X POST \ }"
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
-d "{
\"context\": \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\",
\"state\": \"pending\",
\"description\": \"${STATUS:-In progress}\",
\"target_url\": \"$TRAVIS_JOB_WEB_URL#$TRAVIS_JOB_NUMBER\"
}"
fi
after_failure: after_failure:
- | - |
# don't clobber other (not us) failures curl -u "$GEKY_BOT_STATUSES" -X POST \
if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
| jq -e ".statuses[] | select( -d "{
.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and \"context\": \"$STAGE/$NAME\",
.state == \"failure\" and \"state\": \"failure\",
(.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))" \"description\": \"${STATUS:-Failed}\",
then \"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
curl -u "$GEKY_BOT_STATUSES" -X POST \ }"
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
-d "{
\"context\": \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\",
\"state\": \"failure\",
\"description\": \"${STATUS:-Failed}\",
\"target_url\": \"$TRAVIS_JOB_WEB_URL#$TRAVIS_JOB_NUMBER\"
}"
fi
after_success: after_success:
- | - |
# don't clobber other (not us) failures curl -u "$GEKY_BOT_STATUSES" -X POST \
# only update if we were last job to mark in progress, https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
# this isn't perfect but is probably good enough -d "{
if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ \"context\": \"$STAGE/$NAME\",
| jq -e ".statuses[] | select( \"state\": \"success\",
.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and \"description\": \"${STATUS:-Passed}\",
(.state == \"failure\" or .state == \"pending\") and \"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
(.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))" }"
then
curl -u "$GEKY_BOT_STATUSES" -X POST \ # Job control
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ stages:
-d "{ - name: test
\"context\": \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\", - name: deploy
\"state\": \"success\", if: branch = master AND type = push
\"description\": \"${STATUS:-Passed}\",
\"target_url\": \"$TRAVIS_JOB_WEB_URL#$TRAVIS_JOB_NUMBER\"
}"
fi

View File

@@ -26,6 +26,8 @@ endif
override CFLAGS += -I. override CFLAGS += -I.
override CFLAGS += -std=c99 -Wall -pedantic override CFLAGS += -std=c99 -Wall -pedantic
override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef
# Remove missing-field-initializers because of GCC bug
override CFLAGS += -Wno-missing-field-initializers
ifdef VERBOSE ifdef VERBOSE
override TFLAGS += -v override TFLAGS += -v

View File

@@ -115,9 +115,6 @@ the filesystem until sync or close is called on the file.
## Other notes ## Other notes
Littlefs is written in C, and specifically should compile with any compiler
that conforms to the `C99` standard.
All littlefs calls have the potential to return a negative error code. The 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 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. [lfs.h](lfs.h), or an error returned by the user's block device operations.

18
SPEC.md
View File

@@ -289,8 +289,8 @@ Layout of the name tag:
``` ```
tag data tag data
[-- 32 --][--- variable length ---] [-- 32 --][--- variable length ---]
[1| 3| 8 | 10 | 10 ][--- (size * 8) ---] [1| 3| 8 | 10 | 10 ][--- (size) ---]
^ ^ ^ ^ ^- size ^- file name ^ ^ ^ ^ ^- size ^- file name
| | | '------ id | | | '------ id
| | '----------- file type | | '----------- file type
| '-------------- type1 (0x0) | '-------------- type1 (0x0)
@@ -470,8 +470,8 @@ Layout of the inline-struct tag:
``` ```
tag data tag data
[-- 32 --][--- variable length ---] [-- 32 --][--- variable length ---]
[1|- 11 -| 10 | 10 ][--- (size * 8) ---] [1|- 11 -| 10 | 10 ][--- (size) ---]
^ ^ ^ ^- size ^- inline data ^ ^ ^ ^- size ^- inline data
| | '------ id | | '------ id
| '------------ type (0x201) | '------------ type (0x201)
'----------------- valid bit '----------------- valid bit
@@ -556,8 +556,8 @@ Layout of the user-attr tag:
``` ```
tag data tag data
[-- 32 --][--- variable length ---] [-- 32 --][--- variable length ---]
[1| 3| 8 | 10 | 10 ][--- (size * 8) ---] [1| 3| 8 | 10 | 10 ][--- (size) ---]
^ ^ ^ ^ ^- size ^- attr data ^ ^ ^ ^ ^- size ^- attr data
| | | '------ id | | | '------ id
| | '----------- attr type | | '----------- attr type
| '-------------- type1 (0x3) | '-------------- type1 (0x3)
@@ -764,9 +764,9 @@ Layout of the CRC tag:
``` ```
tag data tag data
[-- 32 --][-- 32 --|--- variable length ---] [-- 32 --][-- 32 --|--- variable length ---]
[1| 3| 8 | 10 | 10 ][-- 32 --|--- (size * 8 - 32) ---] [1| 3| 8 | 10 | 10 ][-- 32 --|--- (size) ---]
^ ^ ^ ^ ^ ^- crc ^- padding ^ ^ ^ ^ ^ ^- crc ^- padding
| | | | '- size | | | | '- size (12)
| | | '------ id (0x3ff) | | | '------ id (0x3ff)
| | '----------- valid state | | '----------- valid state
| '-------------- type1 (0x5) | '-------------- type1 (0x5)

View File

@@ -12,7 +12,7 @@
int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path,
const struct lfs_filebd_config *bdcfg) { const struct lfs_filebd_config *bdcfg) {
LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p {.context=%p, " LFS_TRACE("lfs_filebd_createcfg(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
@@ -30,16 +30,16 @@ int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path,
bd->fd = open(path, O_RDWR | O_CREAT, 0666); bd->fd = open(path, O_RDWR | O_CREAT, 0666);
if (bd->fd < 0) { if (bd->fd < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", err); LFS_TRACE("lfs_filebd_createcfg -> %d", err);
return err; return err;
} }
LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", 0); LFS_TRACE("lfs_filebd_createcfg -> %d", 0);
return 0; return 0;
} }
int lfs_filebd_create(const struct lfs_config *cfg, const char *path) { int lfs_filebd_create(const struct lfs_config *cfg, const char *path) {
LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, " LFS_TRACE("lfs_filebd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
@@ -51,27 +51,26 @@ int lfs_filebd_create(const struct lfs_config *cfg, const char *path) {
path); path);
static const struct lfs_filebd_config defaults = {.erase_value=-1}; static const struct lfs_filebd_config defaults = {.erase_value=-1};
int err = lfs_filebd_createcfg(cfg, path, &defaults); int err = lfs_filebd_createcfg(cfg, path, &defaults);
LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err); LFS_TRACE("lfs_filebd_create -> %d", err);
return err; return err;
} }
int lfs_filebd_destroy(const struct lfs_config *cfg) { int lfs_filebd_destroy(const struct lfs_config *cfg) {
LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); LFS_TRACE("lfs_filebd_destroy(%p)", (void*)cfg);
lfs_filebd_t *bd = cfg->context; lfs_filebd_t *bd = cfg->context;
int err = close(bd->fd); int err = close(bd->fd);
if (err < 0) { if (err < 0) {
err = -errno; err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err); LFS_TRACE("lfs_filebd_destroy -> %d", err);
return err; return err;
} }
LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0); LFS_TRACE("lfs_filebd_destroy -> %d", 0);
return 0; return 0;
} }
int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block, int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) { lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_read(%p, " LFS_TRACE("lfs_filebd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)cfg, block, off, buffer, size);
lfs_filebd_t *bd = cfg->context; lfs_filebd_t *bd = cfg->context;
@@ -90,24 +89,24 @@ int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET); (off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); LFS_TRACE("lfs_filebd_read -> %d", err);
return err; return err;
} }
ssize_t res2 = read(bd->fd, buffer, size); ssize_t res2 = read(bd->fd, buffer, size);
if (res2 < 0) { if (res2 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); LFS_TRACE("lfs_filebd_read -> %d", err);
return err; return err;
} }
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0); LFS_TRACE("lfs_filebd_read -> %d", 0);
return 0; return 0;
} }
int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", LFS_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)cfg, block, off, buffer, size);
lfs_filebd_t *bd = cfg->context; lfs_filebd_t *bd = cfg->context;
@@ -122,7 +121,7 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET); (off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_TRACE("lfs_filebd_prog -> %d", err);
return err; return err;
} }
@@ -131,7 +130,7 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
ssize_t res2 = read(bd->fd, &c, 1); ssize_t res2 = read(bd->fd, &c, 1);
if (res2 < 0) { if (res2 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_TRACE("lfs_filebd_prog -> %d", err);
return err; return err;
} }
@@ -144,23 +143,23 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET); (off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_TRACE("lfs_filebd_prog -> %d", err);
return err; return err;
} }
ssize_t res2 = write(bd->fd, buffer, size); ssize_t res2 = write(bd->fd, buffer, size);
if (res2 < 0) { if (res2 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_TRACE("lfs_filebd_prog -> %d", err);
return err; return err;
} }
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0); LFS_TRACE("lfs_filebd_prog -> %d", 0);
return 0; return 0;
} }
int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) { int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); LFS_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs_filebd_t *bd = cfg->context; lfs_filebd_t *bd = cfg->context;
// check if erase is valid // check if erase is valid
@@ -171,7 +170,7 @@ int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET); off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); LFS_TRACE("lfs_filebd_erase -> %d", err);
return err; return err;
} }
@@ -179,27 +178,27 @@ int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1); ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1);
if (res2 < 0) { if (res2 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); LFS_TRACE("lfs_filebd_erase -> %d", err);
return err; return err;
} }
} }
} }
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0); LFS_TRACE("lfs_filebd_erase -> %d", 0);
return 0; return 0;
} }
int lfs_filebd_sync(const struct lfs_config *cfg) { int lfs_filebd_sync(const struct lfs_config *cfg) {
LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg); LFS_TRACE("lfs_filebd_sync(%p)", (void*)cfg);
// file sync // file sync
lfs_filebd_t *bd = cfg->context; lfs_filebd_t *bd = cfg->context;
int err = fsync(bd->fd); int err = fsync(bd->fd);
if (err) { if (err) {
err = -errno; err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); LFS_TRACE("lfs_filebd_sync -> %d", 0);
return err; return err;
} }
LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); LFS_TRACE("lfs_filebd_sync -> %d", 0);
return 0; return 0;
} }

View File

@@ -15,14 +15,6 @@ extern "C"
{ {
#endif #endif
// Block device specific tracing
#ifdef LFS_FILEBD_YES_TRACE
#define LFS_FILEBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#else
#define LFS_FILEBD_TRACE(...)
#endif
// filebd config (optional) // filebd config (optional)
struct lfs_filebd_config { struct lfs_filebd_config {
// 8-bit erase value to use for simulating erases. -1 does not simulate // 8-bit erase value to use for simulating erases. -1 does not simulate

View File

@@ -8,7 +8,7 @@
int lfs_rambd_createcfg(const struct lfs_config *cfg, int lfs_rambd_createcfg(const struct lfs_config *cfg,
const struct lfs_rambd_config *bdcfg) { const struct lfs_rambd_config *bdcfg) {
LFS_RAMBD_TRACE("lfs_rambd_createcfg(%p {.context=%p, " LFS_TRACE("lfs_rambd_createcfg(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
@@ -27,7 +27,7 @@ int lfs_rambd_createcfg(const struct lfs_config *cfg,
} else { } else {
bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count); bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count);
if (!bd->buffer) { if (!bd->buffer) {
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); LFS_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
} }
} }
@@ -38,12 +38,12 @@ int lfs_rambd_createcfg(const struct lfs_config *cfg,
cfg->block_size * cfg->block_count); cfg->block_size * cfg->block_count);
} }
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0); LFS_TRACE("lfs_rambd_createcfg -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_create(const struct lfs_config *cfg) { int lfs_rambd_create(const struct lfs_config *cfg) {
LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, " LFS_TRACE("lfs_rambd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"})", ".block_size=%"PRIu32", .block_count=%"PRIu32"})",
@@ -53,25 +53,24 @@ int lfs_rambd_create(const struct lfs_config *cfg) {
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count); cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count);
static const struct lfs_rambd_config defaults = {.erase_value=-1}; static const struct lfs_rambd_config defaults = {.erase_value=-1};
int err = lfs_rambd_createcfg(cfg, &defaults); int err = lfs_rambd_createcfg(cfg, &defaults);
LFS_RAMBD_TRACE("lfs_rambd_create -> %d", err); LFS_TRACE("lfs_rambd_create -> %d", err);
return err; return err;
} }
int lfs_rambd_destroy(const struct lfs_config *cfg) { int lfs_rambd_destroy(const struct lfs_config *cfg) {
LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)cfg); LFS_TRACE("lfs_rambd_destroy(%p)", (void*)cfg);
// clean up memory // clean up memory
lfs_rambd_t *bd = cfg->context; lfs_rambd_t *bd = cfg->context;
if (!bd->cfg->buffer) { if (!bd->cfg->buffer) {
lfs_free(bd->buffer); lfs_free(bd->buffer);
} }
LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0); LFS_TRACE("lfs_rambd_destroy -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block, int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) { lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_read(%p, " LFS_TRACE("lfs_rambd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)cfg, block, off, buffer, size);
lfs_rambd_t *bd = cfg->context; lfs_rambd_t *bd = cfg->context;
@@ -83,14 +82,13 @@ int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block,
// read data // read data
memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size); memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size);
LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0); LFS_TRACE("lfs_rambd_read -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block, int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_prog(%p, " LFS_TRACE("lfs_rambd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)cfg, block, off, buffer, size);
lfs_rambd_t *bd = cfg->context; lfs_rambd_t *bd = cfg->context;
@@ -110,12 +108,12 @@ int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block,
// program data // program data
memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size); memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size);
LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0); LFS_TRACE("lfs_rambd_prog -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); LFS_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs_rambd_t *bd = cfg->context; lfs_rambd_t *bd = cfg->context;
// check if erase is valid // check if erase is valid
@@ -127,14 +125,14 @@ int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) {
bd->cfg->erase_value, cfg->block_size); bd->cfg->erase_value, cfg->block_size);
} }
LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0); LFS_TRACE("lfs_rambd_erase -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_sync(const struct lfs_config *cfg) { int lfs_rambd_sync(const struct lfs_config *cfg) {
LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)cfg); LFS_TRACE("lfs_rambd_sync(%p)", (void*)cfg);
// sync does nothing because we aren't backed by anything real // sync does nothing because we aren't backed by anything real
(void)cfg; (void)cfg;
LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0); LFS_TRACE("lfs_rambd_sync -> %d", 0);
return 0; return 0;
} }

View File

@@ -15,14 +15,6 @@ extern "C"
{ {
#endif #endif
// Block device specific tracing
#ifdef LFS_RAMBD_YES_TRACE
#define LFS_RAMBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#else
#define LFS_RAMBD_TRACE(...)
#endif
// rambd config (optional) // rambd config (optional)
struct lfs_rambd_config { struct lfs_rambd_config {
// 8-bit erase value to simulate erasing with. -1 indicates no erase // 8-bit erase value to simulate erasing with. -1 indicates no erase

View File

@@ -12,7 +12,7 @@
int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
const struct lfs_testbd_config *bdcfg) { const struct lfs_testbd_config *bdcfg) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, " LFS_TRACE("lfs_testbd_createcfg(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
@@ -38,9 +38,9 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
if (bd->cfg->wear_buffer) { if (bd->cfg->wear_buffer) {
bd->wear = bd->cfg->wear_buffer; bd->wear = bd->cfg->wear_buffer;
} else { } else {
bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->block_count); bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t) * cfg->block_count);
if (!bd->wear) { if (!bd->wear) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM); LFS_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
} }
} }
@@ -54,7 +54,7 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
.erase_value = bd->cfg->erase_value, .erase_value = bd->cfg->erase_value,
}; };
int err = lfs_filebd_createcfg(cfg, path, &bd->u.file.cfg); int err = lfs_filebd_createcfg(cfg, path, &bd->u.file.cfg);
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); LFS_TRACE("lfs_testbd_createcfg -> %d", err);
return err; return err;
} else { } else {
bd->u.ram.cfg = (struct lfs_rambd_config){ bd->u.ram.cfg = (struct lfs_rambd_config){
@@ -62,13 +62,13 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
.buffer = bd->cfg->buffer, .buffer = bd->cfg->buffer,
}; };
int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg); int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg);
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); LFS_TRACE("lfs_testbd_createcfg -> %d", err);
return err; return err;
} }
} }
int lfs_testbd_create(const struct lfs_config *cfg, const char *path) { int lfs_testbd_create(const struct lfs_config *cfg, const char *path) {
LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, " LFS_TRACE("lfs_testbd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
@@ -80,12 +80,12 @@ int lfs_testbd_create(const struct lfs_config *cfg, const char *path) {
path); path);
static const struct lfs_testbd_config defaults = {.erase_value=-1}; static const struct lfs_testbd_config defaults = {.erase_value=-1};
int err = lfs_testbd_createcfg(cfg, path, &defaults); int err = lfs_testbd_createcfg(cfg, path, &defaults);
LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err); LFS_TRACE("lfs_testbd_create -> %d", err);
return err; return err;
} }
int lfs_testbd_destroy(const struct lfs_config *cfg) { int lfs_testbd_destroy(const struct lfs_config *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg); LFS_TRACE("lfs_testbd_destroy(%p)", (void*)cfg);
lfs_testbd_t *bd = cfg->context; lfs_testbd_t *bd = cfg->context;
if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) { if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) {
lfs_free(bd->wear); lfs_free(bd->wear);
@@ -93,11 +93,11 @@ int lfs_testbd_destroy(const struct lfs_config *cfg) {
if (bd->persist) { if (bd->persist) {
int err = lfs_filebd_destroy(cfg); int err = lfs_filebd_destroy(cfg);
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); LFS_TRACE("lfs_testbd_destroy -> %d", err);
return err; return err;
} else { } else {
int err = lfs_rambd_destroy(cfg); int err = lfs_rambd_destroy(cfg);
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); LFS_TRACE("lfs_testbd_destroy -> %d", err);
return err; return err;
} }
} }
@@ -145,8 +145,7 @@ static int lfs_testbd_rawsync(const struct lfs_config *cfg) {
/// block device API /// /// block device API ///
int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block, int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) { lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_read(%p, " LFS_TRACE("lfs_testbd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)cfg, block, off, buffer, size);
lfs_testbd_t *bd = cfg->context; lfs_testbd_t *bd = cfg->context;
@@ -158,20 +157,19 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
// block bad? // block bad?
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles && if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles &&
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) { bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); LFS_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} }
// read // read
int err = lfs_testbd_rawread(cfg, block, off, buffer, size); int err = lfs_testbd_rawread(cfg, block, off, buffer, size);
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err); LFS_TRACE("lfs_testbd_read -> %d", err);
return err; return err;
} }
int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_prog(%p, " LFS_TRACE("lfs_testbd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)cfg, block, off, buffer, size);
lfs_testbd_t *bd = cfg->context; lfs_testbd_t *bd = cfg->context;
@@ -184,13 +182,13 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) { if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior == if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGERROR) { LFS_TESTBD_BADBLOCK_PROGERROR) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); LFS_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior == } else if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGNOOP || LFS_TESTBD_BADBLOCK_PROGNOOP ||
bd->cfg->badblock_behavior == bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) { LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); LFS_TRACE("lfs_testbd_prog -> %d", 0);
return 0; return 0;
} }
} }
@@ -198,7 +196,7 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
// prog // prog
int err = lfs_testbd_rawprog(cfg, block, off, buffer, size); int err = lfs_testbd_rawprog(cfg, block, off, buffer, size);
if (err) { if (err) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err); LFS_TRACE("lfs_testbd_prog -> %d", err);
return err; return err;
} }
@@ -213,12 +211,12 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
} }
} }
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); LFS_TRACE("lfs_testbd_prog -> %d", 0);
return 0; return 0;
} }
int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); LFS_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs_testbd_t *bd = cfg->context; lfs_testbd_t *bd = cfg->context;
// check if erase is valid // check if erase is valid
@@ -229,11 +227,11 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
if (bd->wear[block] >= bd->cfg->erase_cycles) { if (bd->wear[block] >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior == if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASEERROR) { LFS_TESTBD_BADBLOCK_ERASEERROR) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); LFS_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior == } else if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) { LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0); LFS_TRACE("lfs_testbd_erase -> %d", 0);
return 0; return 0;
} }
} else { } else {
@@ -245,7 +243,7 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
// erase // erase
int err = lfs_testbd_rawerase(cfg, block); int err = lfs_testbd_rawerase(cfg, block);
if (err) { if (err) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err); LFS_TRACE("lfs_testbd_erase -> %d", err);
return err; return err;
} }
@@ -260,14 +258,14 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
} }
} }
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); LFS_TRACE("lfs_testbd_prog -> %d", 0);
return 0; return 0;
} }
int lfs_testbd_sync(const struct lfs_config *cfg) { int lfs_testbd_sync(const struct lfs_config *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg); LFS_TRACE("lfs_testbd_sync(%p)", (void*)cfg);
int err = lfs_testbd_rawsync(cfg); int err = lfs_testbd_rawsync(cfg);
LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err); LFS_TRACE("lfs_testbd_sync -> %d", err);
return err; return err;
} }
@@ -275,20 +273,20 @@ int lfs_testbd_sync(const struct lfs_config *cfg) {
/// simulated wear operations /// /// simulated wear operations ///
lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg, lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg,
lfs_block_t block) { lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); LFS_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block);
lfs_testbd_t *bd = cfg->context; lfs_testbd_t *bd = cfg->context;
// check if block is valid // check if block is valid
LFS_ASSERT(bd->cfg->erase_cycles); LFS_ASSERT(bd->cfg->erase_cycles);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < cfg->block_count);
LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); LFS_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]);
return bd->wear[block]; return bd->wear[block];
} }
int lfs_testbd_setwear(const struct lfs_config *cfg, int lfs_testbd_setwear(const struct lfs_config *cfg,
lfs_block_t block, lfs_testbd_wear_t wear) { lfs_block_t block, lfs_testbd_wear_t wear) {
LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); LFS_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block);
lfs_testbd_t *bd = cfg->context; lfs_testbd_t *bd = cfg->context;
// check if block is valid // check if block is valid
@@ -297,6 +295,6 @@ int lfs_testbd_setwear(const struct lfs_config *cfg,
bd->wear[block] = wear; bd->wear[block] = wear;
LFS_TESTBD_TRACE("lfs_testbd_setwear -> %d", 0); LFS_TRACE("lfs_testbd_setwear -> %d", 0);
return 0; return 0;
} }

View File

@@ -19,13 +19,6 @@ extern "C"
#endif #endif
// Block device specific tracing
#ifdef LFS_TESTBD_YES_TRACE
#define LFS_TESTBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#else
#define LFS_TESTBD_TRACE(...)
#endif
// Mode determining how "bad blocks" behave during testing. This simulates // Mode determining how "bad blocks" behave during testing. This simulates
// some real-world circumstances such as progs not sticking (prog-noop), // some real-world circumstances such as progs not sticking (prog-noop),
// a readonly disk (erase-noop), and ECC failures (read-error). // a readonly disk (erase-noop), and ECC failures (read-error).

233
lfs.c
View File

@@ -29,8 +29,8 @@ static int lfs_bd_read(lfs_t *lfs,
lfs_block_t block, lfs_off_t off, lfs_block_t block, lfs_off_t off,
void *buffer, lfs_size_t size) { void *buffer, lfs_size_t size) {
uint8_t *data = buffer; uint8_t *data = buffer;
if (block >= lfs->cfg->block_count || LFS_ASSERT(block != LFS_BLOCK_NULL);
off+size > lfs->cfg->block_size) { if (off+size > lfs->cfg->block_size) {
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} }
@@ -71,21 +71,6 @@ static int lfs_bd_read(lfs_t *lfs,
diff = lfs_min(diff, rcache->off-off); 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 // load to cache, first condition can no longer fail
LFS_ASSERT(block < lfs->cfg->block_count); LFS_ASSERT(block < lfs->cfg->block_count);
rcache->block = block; rcache->block = block;
@@ -188,7 +173,7 @@ static int lfs_bd_prog(lfs_t *lfs,
lfs_block_t block, lfs_off_t off, lfs_block_t block, lfs_off_t off,
const void *buffer, lfs_size_t size) { const void *buffer, lfs_size_t size) {
const uint8_t *data = buffer; const uint8_t *data = buffer;
LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->cfg->block_count); LFS_ASSERT(block != LFS_BLOCK_NULL);
LFS_ASSERT(off + size <= lfs->cfg->block_size); LFS_ASSERT(off + size <= lfs->cfg->block_size);
while (size > 0) { while (size > 0) {
@@ -284,7 +269,7 @@ typedef int32_t lfs_stag_t;
((cond) ? LFS_MKTAG(type, id, size) : LFS_MKTAG(LFS_FROM_NOOP, 0, 0)) ((cond) ? LFS_MKTAG(type, id, size) : LFS_MKTAG(LFS_FROM_NOOP, 0, 0))
#define LFS_MKTAG_IF_ELSE(cond, type1, id1, size1, type2, id2, size2) \ #define LFS_MKTAG_IF_ELSE(cond, type1, id1, size1, type2, id2, size2) \
((cond) ? LFS_MKTAG(type1, id1, size1) : LFS_MKTAG(type2, id2, size2)) ((cond) ? LFS_MKTAG(type, id, size) : LFS_MKTAG(type2, id2, size2))
static inline bool lfs_tag_isvalid(lfs_tag_t tag) { static inline bool lfs_tag_isvalid(lfs_tag_t tag) {
return !(tag & 0x80000000); return !(tag & 0x80000000);
@@ -452,21 +437,6 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
return 0; return 0;
} }
// indicate allocated blocks have been committed into the filesystem, this
// is to prevent blocks from being garbage collected in the middle of a
// commit operation
static void lfs_alloc_ack(lfs_t *lfs) {
lfs->free.ack = lfs->cfg->block_count;
}
// drop the lookahead buffer, this is done during mounting and failed
// traversals in order to avoid invalid lookahead state
static void lfs_alloc_drop(lfs_t *lfs) {
lfs->free.size = 0;
lfs->free.i = 0;
lfs_alloc_ack(lfs);
}
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
while (true) { while (true) {
while (lfs->free.i != lfs->free.size) { while (lfs->free.i != lfs->free.size) {
@@ -507,12 +477,16 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true); int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true);
if (err) { if (err) {
lfs_alloc_drop(lfs);
return err; return err;
} }
} }
} }
static void lfs_alloc_ack(lfs_t *lfs) {
lfs->free.ack = lfs->cfg->block_count;
}
/// Metadata pair and directory operations /// /// Metadata pair and directory operations ///
static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir,
lfs_tag_t gmask, lfs_tag_t gtag, lfs_tag_t gmask, lfs_tag_t gtag,
@@ -740,7 +714,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
uint16_t fromid = lfs_tag_size(tag); uint16_t fromid = lfs_tag_size(tag);
uint16_t toid = lfs_tag_id(tag); uint16_t toid = lfs_tag_id(tag);
int err = lfs_dir_traverse(lfs, int err = lfs_dir_traverse(lfs,
buffer, 0, 0xffffffff, NULL, 0, buffer, 0, LFS_BLOCK_NULL, NULL, 0,
LFS_MKTAG(0x600, 0x3ff, 0), LFS_MKTAG(0x600, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0),
fromid, fromid+1, toid-fromid+diff, fromid, fromid+1, toid-fromid+diff,
@@ -774,12 +748,6 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// scanning the entire directory // scanning the entire directory
lfs_stag_t besttag = -1; lfs_stag_t besttag = -1;
// if either block address is invalid we return LFS_ERR_CORRUPT here,
// otherwise later writes to the pair could fail
if (pair[0] >= lfs->cfg->block_count || pair[1] >= lfs->cfg->block_count) {
return LFS_ERR_CORRUPT;
}
// find the block with the most recent revision // find the block with the most recent revision
uint32_t revs[2] = {0, 0}; uint32_t revs[2] = {0, 0};
int r = 0; int r = 0;
@@ -806,7 +774,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// now scan tags to fetch the actual dir and find possible match // now scan tags to fetch the actual dir and find possible match
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
lfs_off_t off = 0; lfs_off_t off = 0;
lfs_tag_t ptag = 0xffffffff; lfs_tag_t ptag = LFS_BLOCK_NULL;
uint16_t tempcount = 0; uint16_t tempcount = 0;
lfs_block_t temptail[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; lfs_block_t temptail[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
@@ -814,7 +782,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
lfs_stag_t tempbesttag = besttag; lfs_stag_t tempbesttag = besttag;
dir->rev = lfs_tole32(dir->rev); dir->rev = lfs_tole32(dir->rev);
uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); uint32_t crc = lfs_crc(LFS_BLOCK_NULL, &dir->rev, sizeof(dir->rev));
dir->rev = lfs_fromle32(dir->rev); dir->rev = lfs_fromle32(dir->rev);
while (true) { while (true) {
@@ -872,10 +840,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31; ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31;
// toss our crc into the filesystem seed for // toss our crc into the filesystem seed for
// pseudorandom numbers, note we use another crc here // pseudorandom numbers
// as a collection function because it is sufficiently lfs->seed ^= crc;
// random and convenient
lfs->seed = lfs_crc(lfs->seed, &crc, sizeof(crc));
// update with what's found so far // update with what's found so far
besttag = tempbesttag; besttag = tempbesttag;
@@ -887,7 +853,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
dir->split = tempsplit; dir->split = tempsplit;
// reset crc // reset crc
crc = 0xffffffff; crc = LFS_BLOCK_NULL;
continue; continue;
} }
@@ -998,7 +964,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
dir->rev = revs[(r+1)%2]; dir->rev = revs[(r+1)%2];
} }
LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", LFS_ERROR("Corrupted dir pair at %"PRIx32" %"PRIx32,
dir->pair[0], dir->pair[1]); dir->pair[0], dir->pair[1]);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} }
@@ -1266,14 +1232,13 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
// align to program units // align to program units
const lfs_off_t end = lfs_alignup(commit->off + 2*sizeof(uint32_t), const lfs_off_t off1 = commit->off + sizeof(lfs_tag_t);
const lfs_off_t end = lfs_alignup(off1 + sizeof(uint32_t),
lfs->cfg->prog_size); lfs->cfg->prog_size);
uint32_t ncrc = commit->crc;
lfs_off_t off1 = 0;
uint32_t crc1 = 0;
// create crc tags to fill up remainder of commit, note that // create crc tags to fill up remainder of commit, note that
// padding is not crced, which lets fetches skip padding but // padding is not crcd, which lets fetches skip padding but
// makes committing a bit more complicated // makes committing a bit more complicated
while (commit->off < end) { while (commit->off < end) {
lfs_off_t off = commit->off + sizeof(lfs_tag_t); lfs_off_t off = commit->off + sizeof(lfs_tag_t);
@@ -1283,7 +1248,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
} }
// read erased state from next program unit // read erased state from next program unit
lfs_tag_t tag = 0xffffffff; lfs_tag_t tag = LFS_BLOCK_NULL;
int err = lfs_bd_read(lfs, int err = lfs_bd_read(lfs,
NULL, &lfs->rcache, sizeof(tag), NULL, &lfs->rcache, sizeof(tag),
commit->block, noff, &tag, sizeof(tag)); commit->block, noff, &tag, sizeof(tag));
@@ -1307,15 +1272,10 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
return err; return err;
} }
// keep track of non-padding checksum to verify ncrc = commit->crc;
if (off1 == 0) {
off1 = commit->off + sizeof(uint32_t);
crc1 = commit->crc;
}
commit->off += sizeof(tag)+lfs_tag_size(tag); commit->off += sizeof(tag)+lfs_tag_size(tag);
commit->ptag = tag ^ ((lfs_tag_t)reset << 31); commit->ptag = tag ^ ((lfs_tag_t)reset << 31);
commit->crc = 0xffffffff; // reset crc for next "commit" commit->crc = LFS_BLOCK_NULL; // reset crc for next "commit"
} }
// flush buffers // flush buffers
@@ -1328,14 +1288,8 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
lfs_off_t off = commit->begin; lfs_off_t off = commit->begin;
lfs_off_t noff = off1; lfs_off_t noff = off1;
while (off < end) { while (off < end) {
uint32_t crc = 0xffffffff; uint32_t crc = LFS_BLOCK_NULL;
for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) { for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) {
// check against written crc, may catch blocks that
// become readonly and match our commit size exactly
if (i == off1 && crc != crc1) {
return LFS_ERR_CORRUPT;
}
// leave it up to caching to make this efficient // leave it up to caching to make this efficient
uint8_t dat; uint8_t dat;
err = lfs_bd_read(lfs, err = lfs_bd_read(lfs,
@@ -1345,6 +1299,12 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
return err; return err;
} }
// check against written crc to detect if block is readonly
// (we may pick up old commits)
if (i == noff && crc != ncrc) {
return LFS_ERR_CORRUPT;
}
crc = lfs_crc(crc, &dat, 1); crc = lfs_crc(crc, &dat, 1);
} }
@@ -1391,7 +1351,7 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
// set defaults // set defaults
dir->off = sizeof(dir->rev); dir->off = sizeof(dir->rev);
dir->etag = 0xffffffff; dir->etag = LFS_BLOCK_NULL;
dir->count = 0; dir->count = 0;
dir->tail[0] = LFS_BLOCK_NULL; dir->tail[0] = LFS_BLOCK_NULL;
dir->tail[1] = LFS_BLOCK_NULL; dir->tail[1] = LFS_BLOCK_NULL;
@@ -1485,7 +1445,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// find size // find size
lfs_size_t size = 0; lfs_size_t size = 0;
int err = lfs_dir_traverse(lfs, int err = lfs_dir_traverse(lfs,
source, 0, 0xffffffff, attrs, attrcount, source, 0, LFS_BLOCK_NULL, attrs, attrcount,
LFS_MKTAG(0x400, 0x3ff, 0), LFS_MKTAG(0x400, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 0, 0), LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
begin, end, -begin, begin, end, -begin,
@@ -1580,8 +1540,8 @@ static int lfs_dir_compact(lfs_t *lfs,
struct lfs_commit commit = { struct lfs_commit commit = {
.block = dir->pair[1], .block = dir->pair[1],
.off = 0, .off = 0,
.ptag = 0xffffffff, .ptag = LFS_BLOCK_NULL,
.crc = 0xffffffff, .crc = LFS_BLOCK_NULL,
.begin = 0, .begin = 0,
.end = lfs->cfg->block_size - 8, .end = lfs->cfg->block_size - 8,
@@ -1610,7 +1570,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// traverse the directory, this time writing out all unique tags // traverse the directory, this time writing out all unique tags
err = lfs_dir_traverse(lfs, err = lfs_dir_traverse(lfs,
source, 0, 0xffffffff, attrs, attrcount, source, 0, LFS_BLOCK_NULL, attrs, attrcount,
LFS_MKTAG(0x400, 0x3ff, 0), LFS_MKTAG(0x400, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 0, 0), LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
begin, end, -begin, begin, end, -begin,
@@ -1693,13 +1653,12 @@ relocate:
relocated = true; relocated = true;
lfs_cache_drop(lfs, &lfs->pcache); lfs_cache_drop(lfs, &lfs->pcache);
if (!tired) { if (!tired) {
LFS_DEBUG("Bad block at 0x%"PRIx32, dir->pair[1]); LFS_DEBUG("Bad block at %"PRIx32, dir->pair[1]);
} }
// can't relocate superblock, filesystem is now frozen // can't relocate superblock, filesystem is now frozen
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
LFS_WARN("Superblock 0x%"PRIx32" has become unwritable", LFS_WARN("Superblock %"PRIx32" has become unwritable", dir->pair[1]);
dir->pair[1]);
return LFS_ERR_NOSPC; return LFS_ERR_NOSPC;
} }
@@ -1715,8 +1674,7 @@ relocate:
if (relocated) { if (relocated) {
// update references if we relocated // update references if we relocated
LFS_DEBUG("Relocating {0x%"PRIx32", 0x%"PRIx32"} " LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32,
"-> {0x%"PRIx32", 0x%"PRIx32"}",
oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
int err = lfs_fs_relocate(lfs, oldpair, dir->pair); int err = lfs_fs_relocate(lfs, oldpair, dir->pair);
if (err) { if (err) {
@@ -1789,7 +1747,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
.block = dir->pair[0], .block = dir->pair[0],
.off = dir->off, .off = dir->off,
.ptag = dir->etag, .ptag = dir->etag,
.crc = 0xffffffff, .crc = LFS_BLOCK_NULL,
.begin = dir->off, .begin = dir->off,
.end = lfs->cfg->block_size - 8, .end = lfs->cfg->block_size - 8,
@@ -2240,6 +2198,7 @@ static int lfs_ctz_find(lfs_t *lfs,
return err; return err;
} }
LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
current -= 1 << skip; current -= 1 << skip;
} }
@@ -2259,6 +2218,7 @@ static int lfs_ctz_extend(lfs_t *lfs,
if (err) { if (err) {
return err; return err;
} }
LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count);
{ {
err = lfs_bd_erase(lfs, nblock); err = lfs_bd_erase(lfs, nblock);
@@ -2331,6 +2291,8 @@ static int lfs_ctz_extend(lfs_t *lfs,
return err; return err;
} }
} }
LFS_ASSERT(nhead >= 2 && nhead <= lfs->cfg->block_count);
} }
*block = nblock; *block = nblock;
@@ -2339,7 +2301,7 @@ static int lfs_ctz_extend(lfs_t *lfs,
} }
relocate: relocate:
LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); LFS_DEBUG("Bad block at %"PRIx32, nblock);
// just clear cache and try a new block // just clear cache and try a new block
lfs_cache_drop(lfs, pcache); lfs_cache_drop(lfs, pcache);
@@ -2443,9 +2405,9 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
// get next slot and create entry to remember name // get next slot and create entry to remember name
err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0), NULL}, {LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0)},
{LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path}, {LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path},
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL})); {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0)}));
if (err) { if (err) {
err = LFS_ERR_NAMETOOLONG; err = LFS_ERR_NAMETOOLONG;
goto cleanup; goto cleanup;
@@ -2643,7 +2605,7 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
return 0; return 0;
relocate: relocate:
LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); LFS_DEBUG("Bad block at %"PRIx32, nblock);
// just clear cache and try a new block // just clear cache and try a new block
lfs_cache_drop(lfs, &lfs->pcache); lfs_cache_drop(lfs, &lfs->pcache);
@@ -2720,7 +2682,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
break; break;
relocate: relocate:
LFS_DEBUG("Bad block at 0x%"PRIx32, file->block); LFS_DEBUG("Bad block at %"PRIx32, file->block);
err = lfs_file_relocate(lfs, file); err = lfs_file_relocate(lfs, file);
if (err) { if (err) {
return err; return err;
@@ -2746,12 +2708,6 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file); LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(file->flags & LFS_F_OPENED); LFS_ASSERT(file->flags & LFS_F_OPENED);
if (file->flags & LFS_F_ERRED) {
// it's not safe to do anything if our file errored
LFS_TRACE("lfs_file_sync -> %d", 0);
return 0;
}
int err = lfs_file_flush(lfs, file); int err = lfs_file_flush(lfs, file);
if (err) { if (err) {
file->flags |= LFS_F_ERRED; file->flags |= LFS_F_ERRED;
@@ -2760,6 +2716,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
} }
if ((file->flags & LFS_F_DIRTY) && if ((file->flags & LFS_F_DIRTY) &&
!(file->flags & LFS_F_ERRED) &&
!lfs_pair_isnull(file->m.pair)) { !lfs_pair_isnull(file->m.pair)) {
// update dir entry // update dir entry
uint16_t type; uint16_t type;
@@ -3211,7 +3168,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
// delete the entry // delete the entry
err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0), NULL})); {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0)}));
if (err) { if (err) {
lfs->mlist = dir.next; lfs->mlist = dir.next;
LFS_TRACE("lfs_remove -> %d", err); LFS_TRACE("lfs_remove -> %d", err);
@@ -3337,12 +3294,12 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// move over all attributes // move over all attributes
err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS( err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS(
{LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT, {LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT,
LFS_TYPE_DELETE, newid, 0), NULL}, LFS_TYPE_DELETE, newid, 0)},
{LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL}, {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0)},
{LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath}, {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath},
{LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd}, {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd},
{LFS_MKTAG_IF(samepair, {LFS_MKTAG_IF(samepair,
LFS_TYPE_DELETE, newoldid, 0), NULL})); LFS_TYPE_DELETE, newoldid, 0)}));
if (err) { if (err) {
lfs->mlist = prevdir.next; lfs->mlist = prevdir.next;
LFS_TRACE("lfs_rename -> %d", err); LFS_TRACE("lfs_rename -> %d", err);
@@ -3355,7 +3312,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// prep gstate and delete move id // prep gstate and delete move id
lfs_fs_prepmove(lfs, 0x3ff, NULL); lfs_fs_prepmove(lfs, 0x3ff, NULL);
err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS( err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(oldtag), 0), NULL})); {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(oldtag), 0)}));
if (err) { if (err) {
lfs->mlist = prevdir.next; lfs->mlist = prevdir.next;
LFS_TRACE("lfs_rename -> %d", err); LFS_TRACE("lfs_rename -> %d", err);
@@ -3488,7 +3445,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_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 // 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_ASSERT(4*lfs_npw2(LFS_BLOCK_NULL / (lfs->cfg->block_size-2*4))
<= lfs->cfg->block_size); <= lfs->cfg->block_size);
// block_cycles = 0 is no longer supported. // block_cycles = 0 is no longer supported.
@@ -3647,7 +3604,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_superblock_tole32(&superblock); lfs_superblock_tole32(&superblock);
err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0)},
{LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})); &superblock}));
@@ -3701,15 +3658,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
// scan directory blocks for superblock and any global updates // scan directory blocks for superblock and any global updates
lfs_mdir_t dir = {.tail = {0, 1}}; lfs_mdir_t dir = {.tail = {0, 1}};
lfs_block_t cycle = 0;
while (!lfs_pair_isnull(dir.tail)) { while (!lfs_pair_isnull(dir.tail)) {
if (cycle >= lfs->cfg->block_count/2) {
// loop detected
err = LFS_ERR_CORRUPT;
goto cleanup;
}
cycle += 1;
// fetch next block in tail list // fetch next block in tail list
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail,
LFS_MKTAG(0x7ff, 0x3ff, 0), LFS_MKTAG(0x7ff, 0x3ff, 0),
@@ -3744,7 +3693,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
uint16_t minor_version = (0xffff & (superblock.version >> 0)); uint16_t minor_version = (0xffff & (superblock.version >> 0));
if ((major_version != LFS_DISK_VERSION_MAJOR || if ((major_version != LFS_DISK_VERSION_MAJOR ||
minor_version > LFS_DISK_VERSION_MINOR)) { minor_version > LFS_DISK_VERSION_MINOR)) {
LFS_ERROR("Invalid version v%"PRIu16".%"PRIu16, LFS_ERROR("Invalid version %"PRIu16".%"PRIu16,
major_version, minor_version); major_version, minor_version);
err = LFS_ERR_INVAL; err = LFS_ERR_INVAL;
goto cleanup; goto cleanup;
@@ -3800,7 +3749,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
// update littlefs with gstate // update littlefs with gstate
if (!lfs_gstate_iszero(&lfs->gstate)) { if (!lfs_gstate_iszero(&lfs->gstate)) {
LFS_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32, LFS_DEBUG("Found pending gstate %08"PRIx32" %08"PRIx32" %08"PRIx32,
lfs->gstate.tag, lfs->gstate.tag,
lfs->gstate.pair[0], lfs->gstate.pair[0],
lfs->gstate.pair[1]); lfs->gstate.pair[1]);
@@ -3808,10 +3757,11 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->gstate.tag += !lfs_tag_isvalid(lfs->gstate.tag); lfs->gstate.tag += !lfs_tag_isvalid(lfs->gstate.tag);
lfs->gdisk = lfs->gstate; lfs->gdisk = lfs->gstate;
// setup free lookahead, to distribute allocations uniformly across // setup free lookahead
// boots, we start the allocator at a random location lfs->free.off = lfs->seed % lfs->cfg->block_size;
lfs->free.off = lfs->seed % lfs->cfg->block_count; lfs->free.size = 0;
lfs_alloc_drop(lfs); lfs->free.i = 0;
lfs_alloc_ack(lfs);
LFS_TRACE("lfs_mount -> %d", 0); LFS_TRACE("lfs_mount -> %d", 0);
return 0; return 0;
@@ -3850,14 +3800,7 @@ int lfs_fs_traverseraw(lfs_t *lfs,
} }
#endif #endif
lfs_block_t cycle = 0;
while (!lfs_pair_isnull(dir.tail)) { while (!lfs_pair_isnull(dir.tail)) {
if (cycle >= lfs->cfg->block_count/2) {
// loop detected
return LFS_ERR_CORRUPT;
}
cycle += 1;
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
int err = cb(data, dir.tail[i]); int err = cb(data, dir.tail[i]);
if (err) { if (err) {
@@ -3941,14 +3884,7 @@ static int lfs_fs_pred(lfs_t *lfs,
// iterate over all directory directory entries // iterate over all directory directory entries
pdir->tail[0] = 0; pdir->tail[0] = 0;
pdir->tail[1] = 1; pdir->tail[1] = 1;
lfs_block_t cycle = 0;
while (!lfs_pair_isnull(pdir->tail)) { while (!lfs_pair_isnull(pdir->tail)) {
if (cycle >= lfs->cfg->block_count/2) {
// loop detected
return LFS_ERR_CORRUPT;
}
cycle += 1;
if (lfs_pair_cmp(pdir->tail, pair) == 0) { if (lfs_pair_cmp(pdir->tail, pair) == 0) {
return 0; return 0;
} }
@@ -3991,14 +3927,9 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
// use fetchmatch with callback to find pairs // use fetchmatch with callback to find pairs
parent->tail[0] = 0; parent->tail[0] = 0;
parent->tail[1] = 1; parent->tail[1] = 1;
lfs_block_t cycle = 0; int i = 0;
while (!lfs_pair_isnull(parent->tail)) { while (!lfs_pair_isnull(parent->tail)) {
if (cycle >= lfs->cfg->block_count/2) { i += 1;
// loop detected
return LFS_ERR_CORRUPT;
}
cycle += 1;
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail,
LFS_MKTAG(0x7ff, 0, 0x3ff), LFS_MKTAG(0x7ff, 0, 0x3ff),
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8),
@@ -4017,6 +3948,8 @@ static int lfs_fs_relocate(lfs_t *lfs,
const lfs_block_t oldpair[2], lfs_block_t newpair[2]) { const lfs_block_t oldpair[2], lfs_block_t newpair[2]) {
// update internal root // update internal root
if (lfs_pair_cmp(oldpair, lfs->root) == 0) { if (lfs_pair_cmp(oldpair, lfs->root) == 0) {
LFS_DEBUG("Relocating root %"PRIx32" %"PRIx32,
newpair[0], newpair[1]);
lfs->root[0] = newpair[0]; lfs->root[0] = newpair[0];
lfs->root[1] = newpair[1]; lfs->root[1] = newpair[1];
} }
@@ -4052,7 +3985,7 @@ static int lfs_fs_relocate(lfs_t *lfs,
if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) {
moveid = lfs_tag_id(lfs->gstate.tag); moveid = lfs_tag_id(lfs->gstate.tag);
LFS_DEBUG("Fixing move while relocating " LFS_DEBUG("Fixing move while relocating "
"{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", "%"PRIx32" %"PRIx32" %"PRIx16"\n",
parent.pair[0], parent.pair[1], moveid); parent.pair[0], parent.pair[1], moveid);
lfs_fs_prepmove(lfs, 0x3ff, NULL); lfs_fs_prepmove(lfs, 0x3ff, NULL);
if (moveid < lfs_tag_id(tag)) { if (moveid < lfs_tag_id(tag)) {
@@ -4063,7 +3996,7 @@ static int lfs_fs_relocate(lfs_t *lfs,
lfs_pair_tole32(newpair); lfs_pair_tole32(newpair);
int err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( int err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS(
{LFS_MKTAG_IF(moveid != 0x3ff, {LFS_MKTAG_IF(moveid != 0x3ff,
LFS_TYPE_DELETE, moveid, 0), NULL}, LFS_TYPE_DELETE, moveid, 0)},
{tag, newpair})); {tag, newpair}));
lfs_pair_fromle32(newpair); lfs_pair_fromle32(newpair);
if (err) { if (err) {
@@ -4088,7 +4021,7 @@ static int lfs_fs_relocate(lfs_t *lfs,
if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) {
moveid = lfs_tag_id(lfs->gstate.tag); moveid = lfs_tag_id(lfs->gstate.tag);
LFS_DEBUG("Fixing move while relocating " LFS_DEBUG("Fixing move while relocating "
"{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", "%"PRIx32" %"PRIx32" %"PRIx16"\n",
parent.pair[0], parent.pair[1], moveid); parent.pair[0], parent.pair[1], moveid);
lfs_fs_prepmove(lfs, 0x3ff, NULL); lfs_fs_prepmove(lfs, 0x3ff, NULL);
} }
@@ -4097,7 +4030,7 @@ static int lfs_fs_relocate(lfs_t *lfs,
lfs_pair_tole32(newpair); lfs_pair_tole32(newpair);
err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS(
{LFS_MKTAG_IF(moveid != 0x3ff, {LFS_MKTAG_IF(moveid != 0x3ff,
LFS_TYPE_DELETE, moveid, 0), NULL}, LFS_TYPE_DELETE, moveid, 0)},
{LFS_MKTAG(LFS_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); {LFS_MKTAG(LFS_TYPE_TAIL + parent.split, 0x3ff, 8), newpair}));
lfs_pair_fromle32(newpair); lfs_pair_fromle32(newpair);
if (err) { if (err) {
@@ -4129,7 +4062,7 @@ static int lfs_fs_demove(lfs_t *lfs) {
} }
// Fix bad moves // Fix bad moves
LFS_DEBUG("Fixing move {0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16, LFS_DEBUG("Fixing move %"PRIx32" %"PRIx32" %"PRIx16,
lfs->gdisk.pair[0], lfs->gdisk.pair[0],
lfs->gdisk.pair[1], lfs->gdisk.pair[1],
lfs_tag_id(lfs->gdisk.tag)); lfs_tag_id(lfs->gdisk.tag));
@@ -4145,7 +4078,7 @@ static int lfs_fs_demove(lfs_t *lfs) {
uint16_t moveid = lfs_tag_id(lfs->gdisk.tag); uint16_t moveid = lfs_tag_id(lfs->gdisk.tag);
lfs_fs_prepmove(lfs, 0x3ff, NULL); lfs_fs_prepmove(lfs, 0x3ff, NULL);
err = lfs_dir_commit(lfs, &movedir, LFS_MKATTRS( err = lfs_dir_commit(lfs, &movedir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_DELETE, moveid, 0), NULL})); {LFS_MKTAG(LFS_TYPE_DELETE, moveid, 0)}));
if (err) { if (err) {
return err; return err;
} }
@@ -4180,7 +4113,7 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
if (tag == LFS_ERR_NOENT) { if (tag == LFS_ERR_NOENT) {
// we are an orphan // we are an orphan
LFS_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}", LFS_DEBUG("Fixing orphan %"PRIx32" %"PRIx32,
pdir.tail[0], pdir.tail[1]); pdir.tail[0], pdir.tail[1]);
err = lfs_dir_drop(lfs, &pdir, &dir); err = lfs_dir_drop(lfs, &pdir, &dir);
@@ -4202,8 +4135,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
if (!lfs_pair_sync(pair, pdir.tail)) { if (!lfs_pair_sync(pair, pdir.tail)) {
// we have desynced // we have desynced
LFS_DEBUG("Fixing half-orphan {0x%"PRIx32", 0x%"PRIx32"} " LFS_DEBUG("Fixing half-orphan "
"-> {0x%"PRIx32", 0x%"PRIx32"}", "%"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32,
pdir.tail[0], pdir.tail[1], pair[0], pair[1]); pdir.tail[0], pdir.tail[1], pair[0], pair[1]);
lfs_pair_tole32(pair); lfs_pair_tole32(pair);
@@ -4439,7 +4372,7 @@ static int lfs1_dir_fetch(lfs_t *lfs,
continue; continue;
} }
uint32_t crc = 0xffffffff; uint32_t crc = LFS_BLOCK_NULL;
lfs1_dir_tole32(&test); lfs1_dir_tole32(&test);
lfs1_crc(&crc, &test, sizeof(test)); lfs1_crc(&crc, &test, sizeof(test));
lfs1_dir_fromle32(&test); lfs1_dir_fromle32(&test);
@@ -4466,7 +4399,7 @@ static int lfs1_dir_fetch(lfs_t *lfs,
} }
if (!valid) { if (!valid) {
LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", LFS_ERROR("Corrupted dir pair at %" PRIx32 " %" PRIx32 ,
tpair[0], tpair[1]); tpair[0], tpair[1]);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} }
@@ -4654,8 +4587,7 @@ static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1,
} }
if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) {
LFS_ERROR("Invalid superblock at {0x%"PRIx32", 0x%"PRIx32"}", LFS_ERROR("Invalid superblock at %d %d", 0, 1);
0, 1);
err = LFS_ERR_CORRUPT; err = LFS_ERR_CORRUPT;
goto cleanup; goto cleanup;
} }
@@ -4664,7 +4596,7 @@ static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1,
uint16_t minor_version = (0xffff & (superblock.d.version >> 0)); uint16_t minor_version = (0xffff & (superblock.d.version >> 0));
if ((major_version != LFS1_DISK_VERSION_MAJOR || if ((major_version != LFS1_DISK_VERSION_MAJOR ||
minor_version > LFS1_DISK_VERSION_MINOR)) { minor_version > LFS1_DISK_VERSION_MINOR)) {
LFS_ERROR("Invalid version v%d.%d", major_version, minor_version); LFS_ERROR("Invalid version %d.%d", major_version, minor_version);
err = LFS_ERR_INVAL; err = LFS_ERR_INVAL;
goto cleanup; goto cleanup;
} }
@@ -4830,8 +4762,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
// Copy over first block to thread into fs. Unfortunately // Copy over first block to thread into fs. Unfortunately
// if this fails there is not much we can do. // if this fails there is not much we can do.
LFS_DEBUG("Migrating {0x%"PRIx32", 0x%"PRIx32"} " LFS_DEBUG("Migrating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32,
"-> {0x%"PRIx32", 0x%"PRIx32"}",
lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]); lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]);
err = lfs_bd_erase(lfs, dir1.head[1]); err = lfs_bd_erase(lfs, dir1.head[1]);
@@ -4877,7 +4808,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
dir2.pair[1] = dir1.pair[1]; dir2.pair[1] = dir1.pair[1];
dir2.rev = dir1.d.rev; dir2.rev = dir1.d.rev;
dir2.off = sizeof(dir2.rev); dir2.off = sizeof(dir2.rev);
dir2.etag = 0xffffffff; dir2.etag = LFS_BLOCK_NULL;
dir2.count = 0; dir2.count = 0;
dir2.tail[0] = lfs->lfs1->root[0]; dir2.tail[0] = lfs->lfs1->root[0];
dir2.tail[1] = lfs->lfs1->root[1]; dir2.tail[1] = lfs->lfs1->root[1];

2
lfs.h
View File

@@ -21,7 +21,7 @@ extern "C"
// Software library version // Software library version
// Major (top-nibble), incremented on backwards incompatible changes // Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions // Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00020002 #define LFS_VERSION 0x00020001
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))

View File

@@ -50,35 +50,31 @@ extern "C"
// Logging functions // Logging functions
#ifdef LFS_YES_TRACE #ifdef LFS_YES_TRACE
#define LFS_TRACE_(fmt, ...) \ #define LFS_TRACE(fmt, ...) \
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) printf("%s:%d:trace: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#else #else
#define LFS_TRACE(...) #define LFS_TRACE(fmt, ...)
#endif #endif
#ifndef LFS_NO_DEBUG #ifndef LFS_NO_DEBUG
#define LFS_DEBUG_(fmt, ...) \ #define LFS_DEBUG(fmt, ...) \
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) printf("%s:%d:debug: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "")
#else #else
#define LFS_DEBUG(...) #define LFS_DEBUG(fmt, ...)
#endif #endif
#ifndef LFS_NO_WARN #ifndef LFS_NO_WARN
#define LFS_WARN_(fmt, ...) \ #define LFS_WARN(fmt, ...) \
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) printf("%s:%d:warn: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "")
#else #else
#define LFS_WARN(...) #define LFS_WARN(fmt, ...)
#endif #endif
#ifndef LFS_NO_ERROR #ifndef LFS_NO_ERROR
#define LFS_ERROR_(fmt, ...) \ #define LFS_ERROR(fmt, ...) \
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) printf("%s:%d:error: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "")
#else #else
#define LFS_ERROR(...) #define LFS_ERROR(fmt, ...)
#endif #endif
// Runtime assertions // Runtime assertions
@@ -111,7 +107,7 @@ static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return lfs_aligndown(a + alignment-1, alignment); return lfs_aligndown(a + alignment-1, alignment);
} }
// Find the smallest power of 2 greater than or equal to a // Find the next smallest power of 2 less than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) { static inline uint32_t lfs_npw2(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a-1); return 32 - __builtin_clz(a-1);

View File

@@ -166,8 +166,8 @@ def mkassert(type, comp, lh, rh, size=None):
'type': type.lower(), 'TYPE': type.upper(), 'type': type.lower(), 'TYPE': type.upper(),
'comp': comp.lower(), 'COMP': comp.upper(), 'comp': comp.lower(), 'COMP': comp.upper(),
'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(), 'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(),
'lh': lh.strip(' '), 'lh': lh.strip(),
'rh': rh.strip(' '), 'rh': rh.strip(),
'size': size, 'size': size,
} }
if size: if size:

View File

@@ -233,8 +233,8 @@ class MetadataPair:
def __lt__(self, other): def __lt__(self, other):
# corrupt blocks don't count # corrupt blocks don't count
if not self or not other: if not self and other:
return bool(other) return True
# use sequence arithmetic to avoid overflow # use sequence arithmetic to avoid overflow
return not ((other.rev - self.rev) & 0x80000000) return not ((other.rev - self.rev) & 0x80000000)
@@ -318,24 +318,6 @@ def main(args):
# find most recent pair # find most recent pair
mdir = MetadataPair(blocks) mdir = MetadataPair(blocks)
try:
mdir.tail = mdir[Tag('tail', 0, 0)]
if mdir.tail.size != 8 or mdir.tail.data == 8*b'\xff':
mdir.tail = None
except KeyError:
mdir.tail = None
print("mdir {%s} rev %d%s%s%s" % (
', '.join('%#x' % b
for b in [args.block1, args.block2]
if b is not None),
mdir.rev,
' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:])
if len(mdir.pair) > 1 else '',
' (corrupted!)' if not mdir else '',
' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data)
if mdir.tail else ''))
if args.all: if args.all:
mdir.dump_all(truncate=not args.no_truncate) mdir.dump_all(truncate=not args.no_truncate)
elif args.log: elif args.log:

View File

@@ -7,14 +7,79 @@ import io
import itertools as it import itertools as it
from readmdir import Tag, MetadataPair from readmdir import Tag, MetadataPair
def popc(x):
return bin(x).count('1')
def ctz(x):
return len(bin(x)) - len(bin(x).rstrip('0'))
def dumpentries(args, mdir, f):
for k, id_ in enumerate(mdir.ids):
name = mdir[Tag('name', id_, 0)]
struct_ = mdir[Tag('struct', id_, 0)]
f.write("id %d %s %s" % (
id_, name.typerepr(),
json.dumps(name.data.decode('utf8'))))
if struct_.is_('dirstruct'):
f.write(" dir {%#x, %#x}" % struct.unpack(
'<II', struct_.data[:8].ljust(8, b'\xff')))
if struct_.is_('ctzstruct'):
f.write(" ctz {%#x} size %d" % struct.unpack(
'<II', struct_.data[:8].ljust(8, b'\xff')))
if struct_.is_('inlinestruct'):
f.write(" inline size %d" % struct_.size)
f.write("\n")
if args.data and struct_.is_('inlinestruct'):
for i in range(0, len(struct_.data), 16):
f.write(" %08x: %-47s %-16s\n" % (
i, ' '.join('%02x' % c for c in struct_.data[i:i+16]),
''.join(c if c >= ' ' and c <= '~' else '.'
for c in map(chr, struct_.data[i:i+16]))))
elif args.data and struct_.is_('ctzstruct'):
block, size = struct.unpack(
'<II', struct_.data[:8].ljust(8, b'\xff'))
data = []
i = 0 if size == 0 else (size-1) // (args.block_size - 8)
if i != 0:
i = ((size-1) - 4*popc(i-1)+2) // (args.block_size - 8)
with open(args.disk, 'rb') as f2:
while i >= 0:
f2.seek(block * args.block_size)
dat = f2.read(args.block_size)
data.append(dat[4*(ctz(i)+1) if i != 0 else 0:])
block, = struct.unpack('<I', dat[:4].ljust(4, b'\xff'))
i -= 1
data = bytes(it.islice(
it.chain.from_iterable(reversed(data)), size))
for i in range(0, min(len(data), 256)
if not args.no_truncate else len(data), 16):
f.write(" %08x: %-47s %-16s\n" % (
i, ' '.join('%02x' % c for c in data[i:i+16]),
''.join(c if c >= ' ' and c <= '~' else '.'
for c in map(chr, data[i:i+16]))))
for tag in mdir.tags:
if tag.id==id_ and tag.is_('userattr'):
f.write("id %d %s size %d\n" % (
id_, tag.typerepr(), tag.size))
if args.data:
for i in range(0, len(tag.data), 16):
f.write(" %-47s %-16s\n" % (
' '.join('%02x' % c for c in tag.data[i:i+16]),
''.join(c if c >= ' ' and c <= '~' else '.'
for c in map(chr, tag.data[i:i+16]))))
def main(args): def main(args):
superblock = None
gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0'
dirs = []
mdirs = []
corrupted = []
cycle = False
with open(args.disk, 'rb') as f: with open(args.disk, 'rb') as f:
dirs = []
superblock = None
gstate = b''
mdirs = []
cycle = False
tail = (args.block1, args.block2) tail = (args.block1, args.block2)
hard = False hard = False
while True: while True:
@@ -61,10 +126,6 @@ def main(args):
except KeyError: except KeyError:
pass pass
# corrupted?
if not mdir:
corrupted.append(mdir)
# add to directories # add to directories
mdirs.append(mdir) mdirs.append(mdir)
if mdir.tail is None or not mdir.tail.is_('hardtail'): if mdir.tail is None or not mdir.tail.is_('hardtail'):
@@ -99,65 +160,72 @@ def main(args):
dir[0].path = path.replace('//', '/') dir[0].path = path.replace('//', '/')
# print littlefs + version info # dump tree
version = ('?', '?') if not args.superblock and not args.gstate and not args.mdirs:
if superblock: args.superblock = True
version = tuple(reversed( args.gstate = True
struct.unpack('<HH', superblock[1].data[0:4].ljust(4, b'\xff')))) args.mdirs = True
print("%-47s%s" % ("littlefs v%s.%s" % version,
"data (truncated, if it fits)"
if not any([args.no_truncate, args.log, args.all]) else ""))
# print gstate if args.superblock and superblock:
print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) print("superblock %s v%d.%d" % (
tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) json.dumps(superblock[0].data.decode('utf8')),
blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) struct.unpack('<H', superblock[1].data[2:2+2])[0],
if tag.size or not tag.isvalid: struct.unpack('<H', superblock[1].data[0:0+2])[0]))
print(" orphans >=%d" % max(tag.size, 1)) print(
if tag.type: " block_size %d\n"
print(" move dir {%#x, %#x} id %d" % ( " block_count %d\n"
blocks[0], blocks[1], tag.id)) " name_max %d\n"
" file_max %d\n"
" attr_max %d" % struct.unpack(
'<IIIII', superblock[1].data[4:4+20].ljust(20, b'\xff')))
# print mdir info if args.gstate and gstate:
for i, dir in enumerate(dirs): print("gstate 0x%s" % ''.join('%02x' % c for c in gstate))
print("dir %s" % (json.dumps(dir[0].path) tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0])
if hasattr(dir[0], 'path') else '(orphan)')) blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff'))
if tag.size:
print(" orphans %d" % tag.size)
if tag.type:
print(" move dir {%#x, %#x} id %d" % (
blocks[0], blocks[1], tag.id))
for j, mdir in enumerate(dir): if args.mdirs:
print("mdir {%#x, %#x} rev %d (was %d)%s%s" % ( for i, dir in enumerate(dirs):
mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev, print("dir %s" % (json.dumps(dir[0].path)
' (corrupted!)' if not mdir else '', if hasattr(dir[0], 'path') else '(orphan)'))
' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data)
if mdir.tail else ''))
f = io.StringIO() for j, mdir in enumerate(dir):
if args.log: print("mdir {%#x, %#x} rev %d%s" % (
mdir.dump_log(f, truncate=not args.no_truncate) mdir.blocks[0], mdir.blocks[1], mdir.rev,
elif args.all: ' (corrupted)' if not mdir else ''))
mdir.dump_all(f, truncate=not args.no_truncate)
else:
mdir.dump_tags(f, truncate=not args.no_truncate)
lines = list(filter(None, f.getvalue().split('\n'))) f = io.StringIO()
for k, line in enumerate(lines): if args.tags:
print("%s %s" % ( mdir.dump_tags(f, truncate=not args.no_truncate)
' ' if j == len(dir)-1 else elif args.log:
'v' if k == len(lines)-1 else mdir.dump_log(f, truncate=not args.no_truncate)
'|', elif args.all:
line)) mdir.dump_all(f, truncate=not args.no_truncate)
else:
dumpentries(args, mdir, f)
errcode = 0 lines = list(filter(None, f.getvalue().split('\n')))
for mdir in corrupted: for k, line in enumerate(lines):
errcode = errcode or 1 print("%s %s" % (
print("*** corrupted mdir {%#x, %#x}! ***" % ( ' ' if j == len(dir)-1 else
mdir.blocks[0], mdir.blocks[1])) 'v' if k == len(lines)-1 else
'|',
line))
if cycle: if cycle:
errcode = errcode or 2 print("*** cycle detected! -> {%#x, %#x} ***" % (cycle[0], cycle[1]))
print("*** cycle detected {%#x, %#x}! ***" % (
cycle[0], cycle[1]))
return errcode if cycle:
return 2
elif not all(mdir for dir in dirs for mdir in dir):
return 1
else:
return 0;
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse
@@ -170,14 +238,24 @@ if __name__ == "__main__":
help="Size of a block in bytes.") help="Size of a block in bytes.")
parser.add_argument('block1', nargs='?', default=0, parser.add_argument('block1', nargs='?', default=0,
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
help="Optional first block address for finding the superblock.") help="Optional first block address for finding the root.")
parser.add_argument('block2', nargs='?', default=1, parser.add_argument('block2', nargs='?', default=1,
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
help="Optional second block address for finding the superblock.") help="Optional second block address for finding the root.")
parser.add_argument('-s', '--superblock', action='store_true',
help="Show contents of the superblock.")
parser.add_argument('-g', '--gstate', action='store_true',
help="Show contents of global-state.")
parser.add_argument('-m', '--mdirs', action='store_true',
help="Show contents of metadata-pairs/directories.")
parser.add_argument('-t', '--tags', action='store_true',
help="Show metadata tags instead of reconstructing entries.")
parser.add_argument('-l', '--log', action='store_true', parser.add_argument('-l', '--log', action='store_true',
help="Show tags in log.") help="Show tags in log.")
parser.add_argument('-a', '--all', action='store_true', parser.add_argument('-a', '--all', action='store_true',
help="Show all tags in log, included tags in corrupted commits.") help="Show all tags in log, included tags in corrupted commits.")
parser.add_argument('-d', '--data', action='store_true',
help="Also show the raw contents of files/attrs/tags.")
parser.add_argument('-T', '--no-truncate', action='store_true', parser.add_argument('-T', '--no-truncate', action='store_true',
help="Show the full contents of files/attrs/tags.") help="Don't truncate large amounts of data.")
sys.exit(main(parser.parse_args())) sys.exit(main(parser.parse_args()))

View File

@@ -184,8 +184,7 @@ class TestCase:
elif self.if_ is not None: elif self.if_ is not None:
if_ = self.if_ if_ = self.if_
while True: while True:
for k, v in sorted(self.defines.items(), for k, v in self.defines.items():
key=lambda x: len(x[0]), reverse=True):
if k in if_: if k in if_:
if_ = if_.replace(k, '(%s)' % v) if_ = if_.replace(k, '(%s)' % v)
break break
@@ -200,25 +199,22 @@ class TestCase:
return True return True
def test(self, exec=[], persist=False, cycles=None, def test(self, exec=[], persist=False, cycles=None,
gdb=False, failure=None, disk=None, **args): gdb=False, failure=None, **args):
# build command # build command
cmd = exec + ['./%s.test' % self.suite.path, cmd = exec + ['./%s.test' % self.suite.path,
repr(self.caseno), repr(self.permno)] repr(self.caseno), repr(self.permno)]
# persist disk or keep in RAM for speed? # persist disk or keep in RAM for speed?
if persist: if persist:
if not disk:
disk = self.suite.path + '.disk'
if persist != 'noerase': if persist != 'noerase':
try: try:
with open(disk, 'w') as f: os.remove(self.suite.path + '.disk')
f.truncate(0)
if args.get('verbose', False): if args.get('verbose', False):
print('truncate --size=0', disk) print('rm', self.suite.path + '.disk')
except FileNotFoundError: except FileNotFoundError:
pass pass
cmd.append(disk) cmd.append(self.suite.path + '.disk')
# simulate power-loss after n cycles? # simulate power-loss after n cycles?
if cycles: if cycles:
@@ -231,7 +227,7 @@ class TestCase:
ncmd.extend(['-ex', 'r']) ncmd.extend(['-ex', 'r'])
if failure.assert_: if failure.assert_:
ncmd.extend(['-ex', 'up 2']) ncmd.extend(['-ex', 'up 2'])
elif gdb == 'main': elif gdb == 'start':
ncmd.extend([ ncmd.extend([
'-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno),
'-ex', 'r']) '-ex', 'r'])
@@ -299,17 +295,11 @@ class ValgrindTestCase(TestCase):
return not self.leaky and super().shouldtest(**args) return not self.leaky and super().shouldtest(**args)
def test(self, exec=[], **args): def test(self, exec=[], **args):
verbose = args.get('verbose', False) exec = exec + [
uninit = (self.defines.get('LFS_ERASE_VALUE', None) == -1)
exec = [
'valgrind', 'valgrind',
'--leak-check=full', '--leak-check=full',
] + (['--undef-value-errors=no'] if uninit else []) + [
] + (['--track-origins=yes'] if not uninit else []) + [
'--error-exitcode=4', '--error-exitcode=4',
'--error-limit=no', '-q']
] + (['--num-callers=1'] if not verbose else []) + [
'-q'] + exec
return super().test(exec=exec, **args) return super().test(exec=exec, **args)
class ReentrantTestCase(TestCase): class ReentrantTestCase(TestCase):
@@ -320,7 +310,7 @@ class ReentrantTestCase(TestCase):
def shouldtest(self, **args): def shouldtest(self, **args):
return self.reentrant and super().shouldtest(**args) return self.reentrant and super().shouldtest(**args)
def test(self, persist=False, gdb=False, failure=None, **args): def test(self, exec=[], persist=False, gdb=False, failure=None, **args):
for cycles in it.count(1): for cycles in it.count(1):
# clear disk first? # clear disk first?
if cycles == 1 and persist != 'noerase': if cycles == 1 and persist != 'noerase':
@@ -386,11 +376,10 @@ class TestSuite:
# code lineno? # code lineno?
if 'code' in case: if 'code' in case:
case['code_lineno'] = code_linenos.pop() case['code_lineno'] = code_linenos.pop()
# merge conditions if necessary # give our case's config a copy of our "global" config
if 'if' in config and 'if' in case: for k, v in config.items():
case['if'] = '(%s) && (%s)' % (config['if'], case['if']) if k not in case:
elif 'if' in config: case[k] = v
case['if'] = config['if']
# initialize test case # initialize test case
self.cases.append(TestCase(case, filter=filter, self.cases.append(TestCase(case, filter=filter,
suite=self, caseno=i+1, lineno=lineno, **args)) suite=self, caseno=i+1, lineno=lineno, **args))
@@ -713,6 +702,8 @@ def main(**args):
stdout = perm.result.stdout[:-1] stdout = perm.result.stdout[:-1]
else: else:
stdout = perm.result.stdout stdout = perm.result.stdout
if (not args.get('verbose', False) and len(stdout) > 5):
sys.stdout.write('...\n')
for line in stdout[-5:]: for line in stdout[-5:]:
sys.stdout.write(line) sys.stdout.write(line)
if perm.result.assert_: if perm.result.assert_:
@@ -760,7 +751,7 @@ if __name__ == "__main__":
help="Store disk image in a file.") help="Store disk image in a file.")
parser.add_argument('-b', '--build', action='store_true', parser.add_argument('-b', '--build', action='store_true',
help="Only build the tests, do not execute.") help="Only build the tests, do not execute.")
parser.add_argument('-g', '--gdb', choices=['init', 'main', 'assert'], parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'],
nargs='?', const='assert', nargs='?', const='assert',
help="Drop into gdb on test failure.") help="Drop into gdb on test failure.")
parser.add_argument('--no-internal', action='store_true', parser.add_argument('--no-internal', action='store_true',
@@ -773,6 +764,4 @@ if __name__ == "__main__":
help="Run non-leaky tests under valgrind to check for memory leaks.") help="Run non-leaky tests under valgrind to check for memory leaks.")
parser.add_argument('-e', '--exec', default=[], type=lambda e: e.split(' '), parser.add_argument('-e', '--exec', default=[], type=lambda e: e.split(' '),
help="Run tests with another executable prefixed on the command line.") help="Run tests with another executable prefixed on the command line.")
parser.add_argument('-d', '--disk',
help="Specify a file to use for persistent/reentrant tests.")
sys.exit(main(**vars(parser.parse_args()))) sys.exit(main(**vars(parser.parse_args())))

View File

@@ -1,10 +1,9 @@
# allocator tests # allocator tests
# note for these to work there are a number constraints on the device geometry # note for these to work there are many constraints on the device geometry
if = 'LFS_BLOCK_CYCLES == -1'
[[case]] # parallel allocation test [[case]] # parallel allocation test
define.FILES = 3 define.FILES = 3
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)'
code = ''' code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"}; const char *names[FILES] = {"bacon", "eggs", "pancakes"};
lfs_file_t files[FILES]; lfs_file_t files[FILES];
@@ -47,7 +46,7 @@ code = '''
[[case]] # serial allocation test [[case]] # serial allocation test
define.FILES = 3 define.FILES = 3
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)'
code = ''' code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"}; const char *names[FILES] = {"bacon", "eggs", "pancakes"};
@@ -86,7 +85,7 @@ code = '''
[[case]] # parallel allocation reuse test [[case]] # parallel allocation reuse test
define.FILES = 3 define.FILES = 3
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)'
define.CYCLES = [1, 10] define.CYCLES = [1, 10]
code = ''' code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"}; const char *names[FILES] = {"bacon", "eggs", "pancakes"};
@@ -141,7 +140,7 @@ code = '''
[[case]] # serial allocation reuse test [[case]] # serial allocation reuse test
define.FILES = 3 define.FILES = 3
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)'
define.CYCLES = [1, 10] define.CYCLES = [1, 10]
code = ''' code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"}; const char *names[FILES] = {"bacon", "eggs", "pancakes"};
@@ -323,90 +322,6 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''
[[case]] # what if we have a bad block during an allocation scan?
in = "lfs.c"
define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR'
code = '''
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
// first fill to exhaustion to find available space
lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "waka");
size = strlen("waka");
lfs_size_t filesize = 0;
while (true) {
lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) {
break;
}
filesize += size;
}
lfs_file_close(&lfs, &file) => 0;
// now fill all but a couple of blocks of the filesystem with data
filesize -= 3*LFS_BLOCK_SIZE;
lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "waka");
size = strlen("waka");
for (lfs_size_t i = 0; i < filesize/size; i++) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs_file_close(&lfs, &file) => 0;
// also save head of file so we can error during lookahead scan
lfs_block_t fileblock = file.ctz.head;
lfs_unmount(&lfs) => 0;
// remount to force an alloc scan
lfs_mount(&lfs, &cfg) => 0;
// but mark the head of our file as a "bad block", this is force our
// scan to bail early
lfs_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0;
lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "chomp");
size = strlen("chomp");
while (true) {
lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
assert(res == (lfs_ssize_t)size || res == LFS_ERR_CORRUPT);
if (res == LFS_ERR_CORRUPT) {
break;
}
}
lfs_file_close(&lfs, &file) => 0;
// now reverse the "bad block" and try to write the file again until we
// run out of space
lfs_testbd_setwear(&cfg, fileblock, 0) => 0;
lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "chomp");
size = strlen("chomp");
while (true) {
lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) {
break;
}
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// check that the disk isn't hurt
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0;
strcpy((char*)buffer, "waka");
size = strlen("waka");
for (lfs_size_t i = 0; i < filesize/size; i++) {
uint8_t rbuffer[4];
lfs_file_read(&lfs, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
# Below, I don't like these tests. They're fragile and depend _heavily_ # Below, I don't like these tests. They're fragile and depend _heavily_
# on the geometry of the block device. But they are valuable. Eventually they # on the geometry of the block device. But they are valuable. Eventually they
# should be removed and replaced with generalized tests. # should be removed and replaced with generalized tests.

View File

@@ -1,6 +1,3 @@
# bad blocks with block cycles should be tested in test_relocations
if = 'LFS_BLOCK_CYCLES == -1'
[[case]] # single bad blocks [[case]] # single bad blocks
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_ERASE_CYCLES = 0xffffffff define.LFS_ERASE_CYCLES = 0xffffffff

View File

@@ -155,7 +155,7 @@ code = '''
''' '''
[[case]] # reentrant many directory creation/rename/removal [[case]] # reentrant many directory creation/rename/removal
define.N = [5, 11] define.N = [5, 10] # TODO changed from 20, should we be able to do more?
reentrant = true reentrant = true
code = ''' code = '''
err = lfs_mount(&lfs, &cfg); err = lfs_mount(&lfs, &cfg);

View File

@@ -3,7 +3,7 @@
# still pass with other inline sizes but wouldn't be testing anything. # still pass with other inline sizes but wouldn't be testing anything.
define.LFS_CACHE_SIZE = 512 define.LFS_CACHE_SIZE = 512
if = 'LFS_CACHE_SIZE % LFS_PROG_SIZE == 0 && LFS_CACHE_SIZE == 512' if = 'LFS_CACHE_SIZE == 512'
[[case]] # entry grow test [[case]] # entry grow test
code = ''' code = '''

View File

@@ -1,288 +0,0 @@
# Tests for recovering from conditions which shouldn't normally
# happen during normal operation of littlefs
# invalid pointer tests (outside of block_count)
[[case]] # invalid tail-pointer test
define.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL']
define.INVALSET = [0x3, 0x1, 0x2]
in = "lfs.c"
code = '''
// create littlefs
lfs_format(&lfs, &cfg) => 0;
// change tail-pointer to invalid pointers
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
(lfs_block_t[2]){
(INVALSET & 0x1) ? 0xcccccccc : 0,
(INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0;
lfs_deinit(&lfs) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''
[[case]] # invalid dir pointer test
define.INVALSET = [0x3, 0x1, 0x2]
in = "lfs.c"
code = '''
// create littlefs
lfs_format(&lfs, &cfg) => 0;
// make a dir
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "dir_here") => 0;
lfs_unmount(&lfs) => 0;
// change the dir pointer to be invalid
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
// make sure id 1 == our directory
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer)
=> LFS_MKTAG(LFS_TYPE_DIR, 1, strlen("dir_here"));
assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0);
// change dir pointer
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, 8),
(lfs_block_t[2]){
(INVALSET & 0x1) ? 0xcccccccc : 0,
(INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0;
lfs_deinit(&lfs) => 0;
// test that accessing our bad dir fails, note there's a number
// of ways to access the dir, some can fail, but some don't
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "dir_here", &info) => 0;
assert(strcmp(info.name, "dir_here") == 0);
assert(info.type == LFS_TYPE_DIR);
lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT;
lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT;
lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT;
lfs_file_open(&lfs, &file, "dir_here/file_here",
LFS_O_RDONLY) => LFS_ERR_CORRUPT;
lfs_file_open(&lfs, &file, "dir_here/file_here",
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_CORRUPT;
lfs_unmount(&lfs) => 0;
'''
[[case]] # invalid file pointer test
in = "lfs.c"
define.SIZE = [10, 1000, 100000] # faked file size
code = '''
// create littlefs
lfs_format(&lfs, &cfg) => 0;
// make a file
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "file_here",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// change the file pointer to be invalid
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
// make sure id 1 == our file
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here"));
assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0);
// change file pointer
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)),
&(struct lfs_ctz){0xcccccccc, lfs_tole32(SIZE)}})) => 0;
lfs_deinit(&lfs) => 0;
// test that accessing our bad file fails, note there's a number
// of ways to access the dir, some can fail, but some don't
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "file_here", &info) => 0;
assert(strcmp(info.name, "file_here") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT;
lfs_file_close(&lfs, &file) => 0;
// any allocs that traverse CTZ must unfortunately must fail
if (SIZE > 2*LFS_BLOCK_SIZE) {
lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
}
lfs_unmount(&lfs) => 0;
'''
[[case]] # invalid pointer in CTZ skip-list test
define.SIZE = ['2*LFS_BLOCK_SIZE', '3*LFS_BLOCK_SIZE', '4*LFS_BLOCK_SIZE']
in = "lfs.c"
code = '''
// create littlefs
lfs_format(&lfs, &cfg) => 0;
// make a file
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "file_here",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (int i = 0; i < SIZE; i++) {
char c = 'c';
lfs_file_write(&lfs, &file, &c, 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// change pointer in CTZ skip-list to be invalid
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
// make sure id 1 == our file and get our CTZ structure
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here"));
assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0);
struct lfs_ctz ctz;
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, 1, sizeof(struct lfs_ctz)), &ctz)
=> LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz));
lfs_ctz_fromle32(&ctz);
// rewrite block to contain bad pointer
uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
uint32_t bad = lfs_tole32(0xcccccccc);
memcpy(&bbuffer[0], &bad, sizeof(bad));
memcpy(&bbuffer[4], &bad, sizeof(bad));
cfg.erase(&cfg, ctz.head) => 0;
cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
lfs_deinit(&lfs) => 0;
// test that accessing our bad file fails, note there's a number
// of ways to access the dir, some can fail, but some don't
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "file_here", &info) => 0;
assert(strcmp(info.name, "file_here") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT;
lfs_file_close(&lfs, &file) => 0;
// any allocs that traverse CTZ must unfortunately must fail
if (SIZE > 2*LFS_BLOCK_SIZE) {
lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
}
lfs_unmount(&lfs) => 0;
'''
[[case]] # invalid gstate pointer
define.INVALSET = [0x3, 0x1, 0x2]
in = "lfs.c"
code = '''
// create littlefs
lfs_format(&lfs, &cfg) => 0;
// create an invalid gstate
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){
(INVALSET & 0x1) ? 0xcccccccc : 0,
(INVALSET & 0x2) ? 0xcccccccc : 0});
lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0;
lfs_deinit(&lfs) => 0;
// test that mount fails gracefully
// mount may not fail, but our first alloc should fail when
// we try to fix the gstate
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT;
lfs_unmount(&lfs) => 0;
'''
# cycle detection/recovery tests
[[case]] # metadata-pair threaded-list loop test
in = "lfs.c"
code = '''
// create littlefs
lfs_format(&lfs, &cfg) => 0;
// change tail-pointer to point to ourself
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
(lfs_block_t[2]){0, 1}})) => 0;
lfs_deinit(&lfs) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''
[[case]] # metadata-pair threaded-list 2-length loop test
in = "lfs.c"
code = '''
// create littlefs with child dir
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
// find child
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_block_t pair[2];
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs_pair_fromle32(pair);
// change tail-pointer to point to root
lfs_dir_fetch(&lfs, &mdir, pair) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
(lfs_block_t[2]){0, 1}})) => 0;
lfs_deinit(&lfs) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''
[[case]] # metadata-pair threaded-list 1-length child loop test
in = "lfs.c"
code = '''
// create littlefs with child dir
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
// find child
lfs_init(&lfs, &cfg) => 0;
lfs_mdir_t mdir;
lfs_block_t pair[2];
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs_pair_fromle32(pair);
// change tail-pointer to point to ourself
lfs_dir_fetch(&lfs, &mdir, pair) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0;
lfs_deinit(&lfs) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''

View File

@@ -33,9 +33,6 @@ code = '''
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC); assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) { if (res == LFS_ERR_NOSPC) {
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -43,7 +40,6 @@ code = '''
err = lfs_file_close(&lfs, &file); err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC); assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) { if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -115,9 +111,6 @@ code = '''
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC); assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) { if (res == LFS_ERR_NOSPC) {
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -125,7 +118,6 @@ code = '''
err = lfs_file_close(&lfs, &file); err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC); assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) { if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -206,9 +198,6 @@ code = '''
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC); assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) { if (res == LFS_ERR_NOSPC) {
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -216,7 +205,6 @@ code = '''
err = lfs_file_close(&lfs, &file); err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC); assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) { if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -295,9 +283,6 @@ code = '''
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC); assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) { if (res == LFS_ERR_NOSPC) {
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -305,7 +290,6 @@ code = '''
err = lfs_file_close(&lfs, &file); err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC); assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) { if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -354,9 +338,9 @@ exhausted:
define.LFS_ERASE_CYCLES = 0xffffffff define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1] define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1]
#define.LFS_BLOCK_CYCLES = [4, 2]
define.CYCLES = 100 define.CYCLES = 100
define.FILES = 10 define.FILES = 10
if = 'LFS_BLOCK_CYCLES < CYCLES/10'
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
@@ -380,9 +364,6 @@ code = '''
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC); assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) { if (res == LFS_ERR_NOSPC) {
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }
@@ -390,7 +371,6 @@ code = '''
err = lfs_file_close(&lfs, &file); err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC); assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) { if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted; goto exhausted;
} }
} }

View File

@@ -27,55 +27,41 @@ code = '''
''' '''
[[case]] # expanding superblock [[case]] # expanding superblock
define.LFS_BLOCK_CYCLES = [32, 33, 1] define.BLOCK_CYCLES = [32, 33, 1]
define.N = [10, 100, 1000] define.N = [10, 100, 1000]
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
lfs_file_open(&lfs, &file, "dummy", lfs_mkdir(&lfs, "dummy") => 0;
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_stat(&lfs, "dummy", &info) => 0; lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0); assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0; lfs_remove(&lfs, "dummy") => 0;
} }
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
// one last check after power-cycle // one last check after power-cycle
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "dummy", lfs_mkdir(&lfs, "dummy") => 0;
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_stat(&lfs, "dummy", &info) => 0; lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0); assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''
[[case]] # expanding superblock with power cycle [[case]] # expanding superblock with power cycle
define.LFS_BLOCK_CYCLES = [32, 33, 1] define.BLOCK_CYCLES = [32, 33, 1]
define.N = [10, 100, 1000] define.N = [10, 100, 1000]
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
// remove lingering dummy? // remove lingering dummy?
err = lfs_stat(&lfs, "dummy", &info); err = lfs_remove(&lfs, "dummy");
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
if (!err) {
assert(strcmp(info.name, "dummy") == 0); lfs_mkdir(&lfs, "dummy") => 0;
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
}
lfs_file_open(&lfs, &file, "dummy",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_stat(&lfs, "dummy", &info) => 0; lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0); assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
} }
@@ -83,12 +69,11 @@ code = '''
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "dummy", &info) => 0; lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0); assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''
[[case]] # reentrant expanding superblock [[case]] # reentrant expanding superblock
define.LFS_BLOCK_CYCLES = [2, 1] define.BLOCK_CYCLES = [2, 1]
define.N = 24 define.N = 24
reentrant = true reentrant = true
code = ''' code = '''
@@ -100,20 +85,12 @@ code = '''
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
// remove lingering dummy? // remove lingering dummy?
err = lfs_stat(&lfs, "dummy", &info); err = lfs_remove(&lfs, "dummy");
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
if (!err) {
assert(strcmp(info.name, "dummy") == 0); lfs_mkdir(&lfs, "dummy") => 0;
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
}
lfs_file_open(&lfs, &file, "dummy",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_stat(&lfs, "dummy", &info) => 0; lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0); assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
} }
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -122,6 +99,5 @@ code = '''
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "dummy", &info) => 0; lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0); assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''

View File

@@ -148,7 +148,6 @@ code = '''
[[case]] # move file corrupt source and dest [[case]] # move file corrupt source and dest
in = "lfs.c" in = "lfs.c"
if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
@@ -240,7 +239,6 @@ code = '''
[[case]] # move file after corrupt [[case]] # move file after corrupt
in = "lfs.c" in = "lfs.c"
if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
@@ -595,7 +593,6 @@ code = '''
[[case]] # move dir corrupt source and dest [[case]] # move dir corrupt source and dest
in = "lfs.c" in = "lfs.c"
if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
@@ -695,7 +692,6 @@ code = '''
[[case]] # move dir after corrupt [[case]] # move dir after corrupt
in = "lfs.c" in = "lfs.c"
if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;

View File

@@ -1,6 +1,5 @@
[[case]] # orphan test [[case]] # orphan test
in = "lfs.c" in = "lfs.c"
if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit
code = ''' code = '''
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;

View File

@@ -247,14 +247,14 @@ code = '''
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0; lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
memset(path, 'w', LFS_NAME_MAX+1); memset(path, 'w', LFS_NAME_MAX+1);
path[LFS_NAME_MAX+1] = '\0'; path[LFS_NAME_MAX+2] = '\0';
lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG; lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT)
=> LFS_ERR_NAMETOOLONG; => LFS_ERR_NAMETOOLONG;
memcpy(path, "coffee/", strlen("coffee/")); memcpy(path, "coffee/", strlen("coffee/"));
memset(path+strlen("coffee/"), 'w', LFS_NAME_MAX+1); memset(path+strlen("coffee/"), 'w', LFS_NAME_MAX+1);
path[strlen("coffee/")+LFS_NAME_MAX+1] = '\0'; path[strlen("coffee/")+LFS_NAME_MAX+2] = '\0';
lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG; lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT)
=> LFS_ERR_NAMETOOLONG; => LFS_ERR_NAMETOOLONG;
@@ -270,6 +270,7 @@ code = '''
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0; lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0; lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
lfs_mount(&lfs, &cfg) => 0;
memset(path, 'w', LFS_NAME_MAX); memset(path, 'w', LFS_NAME_MAX);
path[LFS_NAME_MAX] = '\0'; path[LFS_NAME_MAX] = '\0';
lfs_mkdir(&lfs, path) => 0; lfs_mkdir(&lfs, path) => 0;

View File

@@ -100,7 +100,7 @@ code = '''
lfs_file_open(&lfs, &file, "sequence", lfs_file_open(&lfs, &file, "sequence",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0; LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
size = lfs_min(lfs.cfg->cache_size, sizeof(buffer)/2); size = lfs.cfg->cache_size;
lfs_size_t qsize = size / 4; lfs_size_t qsize = size / 4;
uint8_t *wb = buffer; uint8_t *wb = buffer;
uint8_t *rb = buffer + size; uint8_t *rb = buffer + size;