Compare commits

..

6 Commits

Author SHA1 Message Date
Christopher Haster
f9324d1443 Exploring inlined mutable configuration
Currently littlefs uses a separate mutable state struct and immutable
config struct. This lets users place the config struct in ROM where
possible.

However the recent addition of LFS_STATICCFG raises the question of if
this split is still valuable.

If the config is copied into the mutable struct at runtime, this allows
a couple things:
1. Easier user interface, config can be stack allocated, no need to store
   the config struct for the lifetime of littlefs in OSs.
2. Avoids duplication when littlefs would need to change config based on
   observed superblocks, such as LFS_NAME_MAX limits
3. In theory, access to a single struct is faster/smaller as it avoids
   an additional load instruction.

Unfortunately, inlining the dynamic config runs into several issues:

1. The code size actually increases with this change! From digging into
   this it's for a couple reasons:

   - Copying the config over takes code.

   - C has notorious problems with pointer aliasing, accessing
     constants from a const struct actually allows C to assume the
     values aren't going to change in more situations.

     This suggests it may be possible to reduce the code size more by
     loading the config pointer into a variable, but I haven't explored
     this probably not-worth-it optimization.

   - Even assuming deduplication of superblock-dependent configuration,
     the config struct is significantly larger than the mutable struct,
     and it turns out combining these two exceeds the limits of
     immediate-relative-loads, discarding the possible code size
     improvement from avoiding a second dereference.

2. The implementation of dynamic configuration differs significantly from
   the static configuration. This adds mess into the compile-time #ifdefs
   needed to support both options.
2020-11-28 20:13:32 -06:00
Christopher Haster
a7cdd563f6 Changed callbacks to take user-provided context directly
This is a style change to make littlefs's callbacks consistent with most
callback declarations found in C. That is, taking in a user-provided
`void*`.

Previously, these callbacks took a pointer to the config struct itself,
which indirectly contained a user provided context, and this gets the
job done, but taking in a callback with a `void*` is arguably more
expected, has a better chance of integrating with C++/OS-specific code,
and is more likely to be optimized out by a clever compiler.

---

As a part of these changes, the geometry for the test bds needed to be
moved into bd specific configuration objects. This is a good change as
it also allows for testing situations where littlefs's geometry does not
match the underlying bd.
2020-11-28 20:02:18 -06:00
Christopher Haster
a549413077 Rename config structs to cfg structs
Since this is already going to be a breaking API change, this renames
structs/variables named _config -> _cfg. This is in order to be
consistent with functions such as lfs_file_opencfg.
2020-11-28 19:52:21 -06:00
Christopher Haster
3f6f88778a Added minimal build+size reporting to CI based on static config 2020-11-28 19:51:08 -06:00
Christopher Haster
c44427f9ec Exploring ideas for static configuration
As an embedded library, littlefs's configuration straddles two worlds.
In most cases the configuration is usually constant at build time, but
when integrated into OSs, the configuration needs to be dynamically
configurable.

To help with this, littlefs has a separate lfs_config struct that can be
placed into ROM when possible.

But you know what's better than ROM configuration? Truely inlinable
static configuration known at compile-time. In addition to avoiding the
RAM cost, compile-time configuration allows for additional compiler
optimizations, such as constexpr-elimination and removal of unused
code-paths.

So how to enable static configuration?

1. define LFS_STATICCFG
2. implement callbacks as global functions:
   - lfs_read
   - lfs_prog
   - lfs_erase
   - lfs_sync
2. define the now-required constants that configure littlefs:
   - LFS_READ_SIZE
   - LFS_PROG_SIZE
   - LFS_BLOCK_SIZE
   - LFS_BLOCK_COUNT
   - LFS_BLOCK_CYCLES
   - LFS_CACHE_SIZE
   - LFS_LOOKAHEAD_SIZE
   - LFS_READ_BUFFER (optional)
   - LFS_PROG_BUFFER (optional)
   - LFS_LOOKAHEAD_BUFFER (optional)
   - LFS_NAME_MAX (optional)
   - LFS_FILE_MAX (optional)
   - LFS_ATTR_MAX (optional)

Note, there is a separate configuration for the file configuration, this
can be enabled/disabled independently of LFS_STATICCFG. You will likely
want to define this as well if you are looking for the smallest code
size.

In order to avoid a mess of #ifdefs, the internals of littlefs use a
simple macro that redirects to either the dynamic or static config at
compile time:

    #ifdef LFS_STATICCFG
    #define LFS_CFG_READ_SIZE(lfs) ((void)lfs, LFS_READ_SIZE)
    #else
    #define LFS_CFG_READ_SIZE(lfs) lfs->cfg->read_size
    #endif

Unfortunately it does look like there still may be a lot of issues
related to warnings of comparisons against constants... If only C had
a way to ignore warnings on individual statements...

Original idea by apmorton
2020-11-28 19:15:24 -06:00
Christopher Haster
ef9ba2d912 A number of small lfs_util.h QOL changes
- Changed the name of the LFS_CONFIG macro to LFS_UTIL to avoid
  confusion with the lfs_config struct. This also hints that LFS_UTIL
  is related to lfs_util.h.

  LFS_UTIL allows the user to override lfs_util.h so they can provide
  their own system-level dependencies such as malloc, tracing, builtins,
  stdint definitions, string.h, and others.

- Removed stdlib includes from lfs.h, these should all go through
  lfs_util.h to let users override these definitions if stdlib is
  unavailable on their system.

- Moved error code definitions to lfs_util.h. This lets users override
  the error codes to replace them with their own error codes and avoid
  a translation layer in some situations. Note the error codes must
  still be in the range of a negative int.

- Used proper stdint definitions in lfs_scmp.
2020-11-28 18:58:10 -06:00
48 changed files with 11382 additions and 12641 deletions

View File

@@ -1,26 +0,0 @@
name: post-release
on:
release:
branches: [master]
types: [released]
jobs:
post-release:
runs-on: ubuntu-18.04
steps:
# trigger post-release in dependency repo, this indirection allows the
# dependency repo to be updated often without affecting this repo. At
# the time of this comment, the dependency repo is responsible for
# creating PRs for other dependent repos post-release.
- name: trigger-post-release
continue-on-error: true
run: |
curl -sS -X POST -H "authorization: token ${{secrets.BOT_TOKEN}}" \
"$GITHUB_API_URL/repos/${{secrets.POST_RELEASE_REPO}}/dispatches" \
-d "$(jq -n '{
event_type: "post-release",
client_payload: {
repo: env.GITHUB_REPOSITORY,
version: "${{github.event.release.tag_name}}"}}' \
| tee /dev/stderr)"

View File

@@ -1,215 +0,0 @@
name: release
on:
workflow_run:
workflows: [test]
branches: [master]
types: [completed]
jobs:
release:
runs-on: ubuntu-18.04
# need to manually check for a couple things
# - tests passed?
# - we are the most recent commit on master?
if: ${{github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_sha == github.sha}}
steps:
- uses: actions/checkout@v2
with:
ref: ${{github.event.workflow_run.head_sha}}
# need workflow access since we push branches
# containing workflows
token: ${{secrets.BOT_TOKEN}}
# need all tags
fetch-depth: 0
# try to get results from tests
- uses: dawidd6/action-download-artifact@v2
continue-on-error: true
with:
workflow: ${{github.event.workflow_run.name}}
run_id: ${{github.event.workflow_run.id}}
name: results
path: results
- name: find-version
run: |
# rip version from lfs2.h
LFS2_VERSION="$(grep -o '^#define LFS2_VERSION .*$' lfs2.h \
| awk '{print $3}')"
LFS2_VERSION_MAJOR="$((0xffff & ($LFS2_VERSION >> 16)))"
LFS2_VERSION_MINOR="$((0xffff & ($LFS2_VERSION >> 0)))"
# find a new patch version based on what we find in our tags
LFS2_VERSION_PATCH="$( \
( git describe --tags --abbrev=0 \
--match="v$LFS2_VERSION_MAJOR.$LFS2_VERSION_MINOR.*" \
|| echo 'v0.0.-1' ) \
| awk -F '.' '{print $3+1}')"
# found new version
LFS2_VERSION="v$LFS2_VERSION_MAJOR`
`.$LFS2_VERSION_MINOR`
`.$LFS2_VERSION_PATCH"
echo "LFS2_VERSION=$LFS2_VERSION"
echo "LFS2_VERSION=$LFS2_VERSION" >> $GITHUB_ENV
echo "LFS2_VERSION_MAJOR=$LFS2_VERSION_MAJOR" >> $GITHUB_ENV
echo "LFS2_VERSION_MINOR=$LFS2_VERSION_MINOR" >> $GITHUB_ENV
echo "LFS2_VERSION_PATCH=$LFS2_VERSION_PATCH" >> $GITHUB_ENV
# try to find previous version?
- name: find-prev-version
continue-on-error: true
run: |
LFS2_PREV_VERSION="$(git describe --tags --abbrev=0 --match 'v*')"
echo "LFS2_PREV_VERSION=$LFS2_PREV_VERSION"
echo "LFS2_PREV_VERSION=$LFS2_PREV_VERSION" >> $GITHUB_ENV
# try to find results from tests
- name: collect-results
run: |
# previous results to compare against?
[ -n "$LFS2_PREV_VERSION" ] && curl -sS \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/`
`status/$LFS2_PREV_VERSION" \
| jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[]' \
>> prev-results.json \
|| true
# unfortunately these each have their own format
[ -e results/code-thumb.csv ] && ( \
export PREV="$(jq -re '
select(.context == "results / code").description
| capture("Code size is (?<result>[0-9]+)").result' \
prev-results.json || echo 0)"
./scripts/code.py -u results/code-thumb.csv -s | awk '
NR==2 {printf "Code size,%d B",$2}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}
NR==2 {printf "\n"}' \
>> results.csv)
[ -e results/code-thumb-readonly.csv ] && ( \
export PREV="$(jq -re '
select(.context == "results / code (readonly)").description
| capture("Code size is (?<result>[0-9]+)").result' \
prev-results.json || echo 0)"
./scripts/code.py -u results/code-thumb-readonly.csv -s | awk '
NR==2 {printf "Code size<br/>(readonly),%d B",$2}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}
NR==2 {printf "\n"}' \
>> results.csv)
[ -e results/code-thumb-threadsafe.csv ] && ( \
export PREV="$(jq -re '
select(.context == "results / code (threadsafe)").description
| capture("Code size is (?<result>[0-9]+)").result' \
prev-results.json || echo 0)"
./scripts/code.py -u results/code-thumb-threadsafe.csv -s | awk '
NR==2 {printf "Code size<br/>(threadsafe),%d B",$2}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}
NR==2 {printf "\n"}' \
>> results.csv)
[ -e results/code-thumb-migrate.csv ] && ( \
export PREV="$(jq -re '
select(.context == "results / code (migrate)").description
| capture("Code size is (?<result>[0-9]+)").result' \
prev-results.json || echo 0)"
./scripts/code.py -u results/code-thumb-migrate.csv -s | awk '
NR==2 {printf "Code size<br/>(migrate),%d B",$2}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}
NR==2 {printf "\n"}' \
>> results.csv)
[ -e results/code-thumb-error-asserts.csv ] && ( \
export PREV="$(jq -re '
select(.context == "results / code (error-asserts)").description
| capture("Code size is (?<result>[0-9]+)").result' \
prev-results.json || echo 0)"
./scripts/code.py -u results/code-thumb-error-asserts.csv -s | awk '
NR==2 {printf "Code size<br/>(error-asserts),%d B",$2}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}
NR==2 {printf "\n"}' \
>> results.csv)
[ -e results/coverage.csv ] && ( \
export PREV="$(jq -re '
select(.context == "results / coverage").description
| capture("Coverage is (?<result>[0-9\\.]+)").result' \
prev-results.json || echo 0)"
./scripts/coverage.py -u results/coverage.csv -s | awk -F '[ /%]+' '
NR==2 {printf "Coverage,%.1f%% of %d lines",$4,$3}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",$4-ENVIRON["PREV"]}
NR==2 {printf "\n"}' \
>> results.csv)
# transpose to GitHub table
[ -e results.csv ] || exit 0
awk -F ',' '
{label[NR]=$1; value[NR]=$2}
END {
for (r=1; r<=NR; r++) {printf "| %s ",label[r]}; printf "|\n";
for (r=1; r<=NR; r++) {printf "|:--"}; printf "|\n";
for (r=1; r<=NR; r++) {printf "| %s ",value[r]}; printf "|\n"}' \
results.csv > results.txt
echo "RESULTS:"
cat results.txt
# find changes from history
- name: collect-changes
run: |
[ -n "$LFS2_PREV_VERSION" ] || exit 0
# use explicit link to github commit so that release notes can
# be copied elsewhere
git log "$LFS2_PREV_VERSION.." \
--grep='^Merge' --invert-grep \
--format="format:[\`%h\`](`
`https://github.com/$GITHUB_REPOSITORY/commit/%h) %s" \
> changes.txt
echo "CHANGES:"
cat changes.txt
# create and update major branches (vN and vN-prefix)
- name: create-major-branches
run: |
# create major branch
git branch "v$LFS2_VERSION_MAJOR" HEAD
# create major prefix branch
git config user.name ${{secrets.BOT_USER}}
git config user.email ${{secrets.BOT_EMAIL}}
git fetch "https://github.com/$GITHUB_REPOSITORY.git" \
"v$LFS2_VERSION_MAJOR-prefix" || true
./scripts/prefix.py "lfs2$LFS2_VERSION_MAJOR"
git branch "v$LFS2_VERSION_MAJOR-prefix" $( \
git commit-tree $(git write-tree) \
$(git rev-parse --verify -q FETCH_HEAD | sed -e 's/^/-p /') \
-p HEAD \
-m "Generated v$LFS2_VERSION_MAJOR prefixes")
git reset --hard
# push!
git push --atomic origin \
"v$LFS2_VERSION_MAJOR" \
"v$LFS2_VERSION_MAJOR-prefix"
# build release notes
- name: create-release
run: |
# create release and patch version tag (vN.N.N)
# only draft if not a patch release
[ -e results.txt ] && export RESULTS="$(cat results.txt)"
[ -e changes.txt ] && export CHANGES="$(cat changes.txt)"
curl -sS -X POST -H "authorization: token ${{secrets.BOT_TOKEN}}" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases" \
-d "$(jq -n '{
tag_name: env.LFS2_VERSION,
name: env.LFS2_VERSION | rtrimstr(".0"),
target_commitish: "${{github.event.workflow_run.head_sha}}",
draft: env.LFS2_VERSION | endswith(".0"),
body: [env.RESULTS, env.CHANGES | select(.)] | join("\n\n")}' \
| tee /dev/stderr)"

View File

@@ -1,55 +0,0 @@
name: status
on:
workflow_run:
workflows: [test]
types: [completed]
jobs:
status:
runs-on: ubuntu-18.04
steps:
# custom statuses?
- uses: dawidd6/action-download-artifact@v2
continue-on-error: true
with:
workflow: ${{github.event.workflow_run.name}}
run_id: ${{github.event.workflow_run.id}}
name: status
path: status
- name: update-status
continue-on-error: true
run: |
ls status
for s in $(shopt -s nullglob ; echo status/*.json)
do
# parse requested status
export STATE="$(jq -er '.state' $s)"
export CONTEXT="$(jq -er '.context' $s)"
export DESCRIPTION="$(jq -er '.description' $s)"
# help lookup URL for job/steps because GitHub makes
# it VERY HARD to link to specific jobs
export TARGET_URL="$(
jq -er '.target_url // empty' $s || (
export TARGET_JOB="$(jq -er '.target_job' $s)"
export TARGET_STEP="$(jq -er '.target_step // ""' $s)"
curl -sS -H "authorization: token ${{secrets.BOT_TOKEN}}" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/runs/`
`${{github.event.workflow_run.id}}/jobs" \
| jq -er '.jobs[]
| select(.name == env.TARGET_JOB)
| .html_url
+ "?check_suite_focus=true"
+ ((.steps[]
| select(.name == env.TARGET_STEP)
| "#step:\(.number):0") // "")'))"
# update status
curl -sS -X POST -H "authorization: token ${{secrets.BOT_TOKEN}}" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/statuses/`
`${{github.event.workflow_run.head_sha}}" \
-d "$(jq -n '{
state: env.STATE,
context: env.CONTEXT,
description: env.DESCRIPTION,
target_url: env.TARGET_URL}' \
| tee /dev/stderr)"
done

View File

@@ -1,446 +0,0 @@
name: test
on: [push, pull_request]
env:
CFLAGS: -Werror
MAKEFLAGS: -j
jobs:
# run tests
test:
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
arch: [x86_64, thumb, mips, powerpc]
steps:
- uses: actions/checkout@v2
- name: install
run: |
# need toml, also pip3 isn't installed by default?
sudo apt-get update -qq
sudo apt-get install -qq python3 python3-pip lcov
sudo pip3 install toml
gcc --version
# setup a ram-backed disk to speed up reentrant tests
mkdir disks
sudo mount -t tmpfs -o size=100m tmpfs disks
TESTFLAGS="$TESTFLAGS --disk=disks/disk"
# collect coverage
mkdir -p coverage
TESTFLAGS="$TESTFLAGS --coverage=`
`coverage/${{github.job}}-${{matrix.arch}}.info"
echo "TESTFLAGS=$TESTFLAGS" >> $GITHUB_ENV
# cross-compile with ARM Thumb (32-bit, little-endian)
- name: install-thumb
if: ${{matrix.arch == 'thumb'}}
run: |
sudo apt-get install -qq \
gcc-arm-linux-gnueabi \
libc6-dev-armel-cross \
qemu-user
echo "CC=arm-linux-gnueabi-gcc -mthumb --static" >> $GITHUB_ENV
echo "EXEC=qemu-arm" >> $GITHUB_ENV
arm-linux-gnueabi-gcc --version
qemu-arm -version
# cross-compile with MIPS (32-bit, big-endian)
- name: install-mips
if: ${{matrix.arch == 'mips'}}
run: |
sudo apt-get install -qq \
gcc-mips-linux-gnu \
libc6-dev-mips-cross \
qemu-user
echo "CC=mips-linux-gnu-gcc --static" >> $GITHUB_ENV
echo "EXEC=qemu-mips" >> $GITHUB_ENV
mips-linux-gnu-gcc --version
qemu-mips -version
# cross-compile with PowerPC (32-bit, big-endian)
- name: install-powerpc
if: ${{matrix.arch == 'powerpc'}}
run: |
sudo apt-get install -qq \
gcc-powerpc-linux-gnu \
libc6-dev-powerpc-cross \
qemu-user
echo "CC=powerpc-linux-gnu-gcc --static" >> $GITHUB_ENV
echo "EXEC=qemu-ppc" >> $GITHUB_ENV
powerpc-linux-gnu-gcc --version
qemu-ppc -version
# make sure example can at least compile
- name: test-example
run: |
sed -n '/``` c/,/```/{/```/d; p}' README.md > test.c
make all CFLAGS+=" \
-Duser_provided_block_device_read=NULL \
-Duser_provided_block_device_prog=NULL \
-Duser_provided_block_device_erase=NULL \
-Duser_provided_block_device_sync=NULL \
-include stdio.h"
rm test.c
# test configurations
# normal+reentrant tests
- name: test-default
run: |
make clean
make test TESTFLAGS+="-nrk"
# NOR flash: read/prog = 1 block = 4KiB
- name: test-nor
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_READ_SIZE=1 -DLFS2_BLOCK_SIZE=4096"
# SD/eMMC: read/prog = 512 block = 512
- name: test-emmc
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_READ_SIZE=512 -DLFS2_BLOCK_SIZE=512"
# NAND flash: read/prog = 4KiB block = 32KiB
- name: test-nand
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_READ_SIZE=4096 -DLFS2_BLOCK_SIZE=\(32*1024\)"
# other extreme geometries that are useful for various corner cases
- name: test-no-intrinsics
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_NO_INTRINSICS"
- name: test-byte-writes
# it just takes too long to test byte-level writes when in qemu,
# should be plenty covered by the other configurations
if: ${{matrix.arch == 'x86_64'}}
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_READ_SIZE=1 -DLFS2_CACHE_SIZE=1"
- name: test-block-cycles
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_BLOCK_CYCLES=1"
- name: test-odd-block-count
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_BLOCK_COUNT=1023 -DLFS2_LOOKAHEAD_SIZE=256"
- name: test-odd-block-size
run: |
make clean
make test TESTFLAGS+="-nrk \
-DLFS2_READ_SIZE=11 -DLFS2_BLOCK_SIZE=704"
# upload coverage for later coverage
- name: upload-coverage
uses: actions/upload-artifact@v2
with:
name: coverage
path: coverage
retention-days: 1
# update results
- name: results-code
run: |
mkdir -p results
make clean
make code \
CFLAGS+=" \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR" \
CODEFLAGS+="-o results/code-${{matrix.arch}}.csv"
- name: results-code-readonly
run: |
mkdir -p results
make clean
make code \
CFLAGS+=" \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-DLFS2_READONLY" \
CODEFLAGS+="-o results/code-${{matrix.arch}}-readonly.csv"
- name: results-code-threadsafe
run: |
mkdir -p results
make clean
make code \
CFLAGS+=" \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-DLFS2_THREADSAFE" \
CODEFLAGS+="-o results/code-${{matrix.arch}}-threadsafe.csv"
- name: results-code-migrate
run: |
mkdir -p results
make clean
make code \
CFLAGS+=" \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-DLFS2_MIGRATE" \
CODEFLAGS+="-o results/code-${{matrix.arch}}-migrate.csv"
- name: results-code-error-asserts
run: |
mkdir -p results
make clean
make code \
CFLAGS+=" \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-D'LFS2_ASSERT(test)=do {if(!(test)) {return -1;}} while(0)'" \
CODEFLAGS+="-o results/code-${{matrix.arch}}-error-asserts.csv"
- name: upload-results
uses: actions/upload-artifact@v2
with:
name: results
path: results
# limit reporting to Thumb, otherwise there would be too many numbers
# flying around for the results to be easily readable
- name: collect-status
if: ${{matrix.arch == 'thumb'}}
run: |
mkdir -p status
for f in $(shopt -s nullglob ; echo results/code*.csv)
do
export STEP="results-code$(
echo $f | sed -n 's/.*code-.*-\(.*\).csv/-\1/p')"
export CONTEXT="results / code$(
echo $f | sed -n 's/.*code-.*-\(.*\).csv/ (\1)/p')"
export PREV="$(curl -sS \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/master" \
| jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[]
| select(.context == env.CONTEXT).description
| capture("Code size is (?<result>[0-9]+)").result' \
|| echo 0)"
export DESCRIPTION="$(./scripts/code.py -u $f -s | awk '
NR==2 {printf "Code size is %d B",$2}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}')"
jq -n '{
state: "success",
context: env.CONTEXT,
description: env.DESCRIPTION,
target_job: "${{github.job}} (${{matrix.arch}})",
target_step: env.STEP}' \
| tee status/code$(
echo $f | sed -n 's/.*code-.*-\(.*\).csv/-\1/p').json
done
- name: upload-status
if: ${{matrix.arch == 'thumb'}}
uses: actions/upload-artifact@v2
with:
name: status
path: status
retention-days: 1
# run under Valgrind to check for memory errors
valgrind:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: install
run: |
# need toml, also pip3 isn't installed by default?
sudo apt-get update -qq
sudo apt-get install -qq python3 python3-pip
sudo pip3 install toml
- name: install-valgrind
run: |
sudo apt-get update -qq
sudo apt-get install -qq valgrind
valgrind --version
# normal tests, we don't need to test all geometries
- name: test-valgrind
run: make test TESTFLAGS+="-k --valgrind"
# self-host with littlefs-fuse for a fuzz-like test
fuse:
runs-on: ubuntu-18.04
if: ${{!endsWith(github.ref, '-prefix')}}
steps:
- uses: actions/checkout@v2
- name: install
run: |
# need toml, also pip3 isn't installed by default?
sudo apt-get update -qq
sudo apt-get install -qq python3 python3-pip libfuse-dev
sudo pip3 install toml
fusermount -V
gcc --version
- uses: actions/checkout@v2
with:
repository: littlefs-project/littlefs-fuse
ref: v2
path: littlefs-fuse
- name: setup
run: |
# copy our new version into littlefs-fuse
rm -rf littlefs-fuse/littlefs/*
cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
# setup disk for littlefs-fuse
mkdir mount
sudo chmod a+rw /dev/loop0
dd if=/dev/zero bs=512 count=128K of=disk
losetup /dev/loop0 disk
- name: test
run: |
# self-host test
make -C littlefs-fuse
littlefs-fuse/lfs2 --format /dev/loop0
littlefs-fuse/lfs2 /dev/loop0 mount
ls mount
mkdir mount/littlefs
cp -r $(git ls-tree --name-only HEAD) mount/littlefs
cd mount/littlefs
stat .
ls -flh
make -B test
# test migration using littlefs-fuse
migrate:
runs-on: ubuntu-18.04
if: ${{!endsWith(github.ref, '-prefix')}}
steps:
- uses: actions/checkout@v2
- name: install
run: |
# need toml, also pip3 isn't installed by default?
sudo apt-get update -qq
sudo apt-get install -qq python3 python3-pip libfuse-dev
sudo pip3 install toml
fusermount -V
gcc --version
- uses: actions/checkout@v2
with:
repository: littlefs-project/littlefs-fuse
ref: v2
path: v2
- uses: actions/checkout@v2
with:
repository: littlefs-project/littlefs-fuse
ref: v1
path: v1
- name: setup
run: |
# copy our new version into littlefs-fuse
rm -rf v2/littlefs/*
cp -r $(git ls-tree --name-only HEAD) v2/littlefs
# setup disk for littlefs-fuse
mkdir mount
sudo chmod a+rw /dev/loop0
dd if=/dev/zero bs=512 count=128K of=disk
losetup /dev/loop0 disk
- name: test
run: |
# compile v1 and v2
make -C v1
make -C v2
# run self-host test with v1
v1/lfs2 --format /dev/loop0
v1/lfs2 /dev/loop0 mount
ls mount
mkdir mount/littlefs
cp -r $(git ls-tree --name-only HEAD) mount/littlefs
cd mount/littlefs
stat .
ls -flh
make -B test
# attempt to migrate
cd ../..
fusermount -u mount
v2/lfs2 --migrate /dev/loop0
v2/lfs2 /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
# collect coverage info
coverage:
runs-on: ubuntu-18.04
needs: [test]
steps:
- uses: actions/checkout@v2
- name: install
run: |
sudo apt-get update -qq
sudo apt-get install -qq python3 python3-pip lcov
sudo pip3 install toml
# yes we continue-on-error nearly every step, continue-on-error
# at job level apparently still marks a job as failed, which isn't
# what we want
- uses: actions/download-artifact@v2
continue-on-error: true
with:
name: coverage
path: coverage
- name: results-coverage
continue-on-error: true
run: |
mkdir -p results
lcov $(for f in coverage/*.info ; do echo "-a $f" ; done) \
-o results/coverage.info
./scripts/coverage.py results/coverage.info -o results/coverage.csv
- name: upload-results
uses: actions/upload-artifact@v2
with:
name: results
path: results
- name: collect-status
run: |
mkdir -p status
[ -e results/coverage.csv ] || exit 0
export STEP="results-coverage"
export CONTEXT="results / coverage"
export PREV="$(curl -sS \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/master" \
| jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[]
| select(.context == env.CONTEXT).description
| capture("Coverage is (?<result>[0-9\\.]+)").result' \
|| echo 0)"
export DESCRIPTION="$(
./scripts/coverage.py -u results/coverage.csv -s | awk -F '[ /%]+' '
NR==2 {printf "Coverage is %.1f%% of %d lines",$4,$3}
NR==2 && ENVIRON["PREV"]+0 != 0 {
printf " (%+.1f%%)",$4-ENVIRON["PREV"]}')"
jq -n '{
state: "success",
context: env.CONTEXT,
description: env.DESCRIPTION,
target_job: "${{github.job}}",
target_step: env.STEP}' \
| tee status/coverage.json
- name: upload-status
uses: actions/upload-artifact@v2
with:
name: status
path: status
retention-days: 1

2
.gitignore vendored
View File

@@ -5,7 +5,7 @@
# Testing things
blocks/
lfs2
lfs
test.c
tests/*.toml.*
scripts/__pycache__

454
.travis.yml Normal file
View File

@@ -0,0 +1,454 @@
# environment variables
env:
global:
- CFLAGS=-Werror
- MAKEFLAGS=-j
# cache installation dirs
cache:
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
- sed -n '/``` c/,/```/{/```/d; p}' README.md > test.c &&
make all CFLAGS+="
-Duser_provided_block_device_read=NULL
-Duser_provided_block_device_prog=NULL
-Duser_provided_block_device_erase=NULL
-Duser_provided_block_device_sync=NULL
-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
_: &report-size
# compile and find the code size with the smallest configuration
- make -j1 clean size
OBJ="$(ls lfs*.c | sed 's/\.c/\.o/' | tr '\n' ' ')"
CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
| tee sizes
# update status if we succeeded, compare with master if possible
- |
if [ "$TRAVIS_TEST_RESULT" -eq 0 ]
then
CURR=$(tail -n1 sizes | awk '{print $1}')
PREV=$(curl -u "$GEKY_BOT_STATUSES" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\")
| .statuses[] | select(.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\").description
| capture(\"code size is (?<size>[0-9]+)\").size" \
|| echo 0)
STATUS="Passed, code size is ${CURR}B"
if [ "$PREV" -ne 0 ]
then
STATUS="$STATUS ($(python -c "print '%+.2f' % (100*($CURR-$PREV)/$PREV.0)")%)"
fi
fi
# stage control
stages:
- name: test
- name: deploy
if: branch = master AND type = push
# job control
jobs:
# native testing
- &x86
stage: test
env:
- NAME=littlefs-x86
install: *install-common
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)
- &arm
stage: test
env:
- NAME=littlefs-arm
- CC="arm-linux-gnueabi-gcc --static -mthumb"
- TFLAGS="$TFLAGS --exec=qemu-arm"
install:
- *install-common
- sudo apt-get install
gcc-arm-linux-gnueabi
libc6-dev-armel-cross
qemu-user
- arm-linux-gnueabi-gcc --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
- &mips
stage: test
env:
- NAME=littlefs-mips
- CC="mips-linux-gnu-gcc --static"
- TFLAGS="$TFLAGS --exec=qemu-mips"
install:
- *install-common
- sudo apt-get install
gcc-mips-linux-gnu
libc6-dev-mips-cross
qemu-user
- mips-linux-gnu-gcc --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
- &powerpc
stage: test
env:
- NAME=littlefs-powerpc
- CC="powerpc-linux-gnu-gcc --static"
- TFLAGS="$TFLAGS --exec=qemu-ppc"
install:
- *install-common
- sudo apt-get install
gcc-powerpc-linux-gnu
libc6-dev-powerpc-cross
qemu-user
- powerpc-linux-gnu-gcc --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
- &valgrind
stage: test
env:
- NAME=littlefs-valgrind
install:
- *install-common
- sudo apt-get install valgrind
- valgrind --version
script:
- make test TFLAGS+="-k --valgrind"
# test minimal compilation using static configs
- stage: test
env:
- NAME=littlefs-minimal
- CC="arm-linux-gnueabi-gcc --static -mthumb"
- CFLAGS="-Werror
-DLFS_STATICCFG -DLFS_FILE_STATICCFG
-DLFS_READ_SIZE=16
-DLFS_PROG_SIZE=16
-DLFS_BLOCK_SIZE=512
-DLFS_BLOCK_COUNT=1024
-DLFS_BLOCK_CYCLES=-1
-DLFS_CACHE_SIZE=64
-DLFS_LOOKAHEAD_SIZE=16
-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
if: branch !~ -prefix$
install:
- *install-common
- sudo apt-get install
gcc-arm-linux-gnueabi
libc6-dev-armel-cross
- arm-linux-gnueabi-gcc --version
# report-size will compile littlefs and report the size
script: [*report-size]
# self-host with littlefs-fuse for fuzz test
- stage: test
env:
- NAME=littlefs-fuse
if: branch !~ -prefix$
install:
- *install-common
- 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
- rm -rf littlefs-fuse/littlefs/*
- cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
- mkdir mount
- sudo chmod a+rw /dev/loop0
- dd if=/dev/zero bs=512 count=128K of=disk
- losetup /dev/loop0 disk
script:
# self-host test
- make -C littlefs-fuse
- littlefs-fuse/lfs --format /dev/loop0
- littlefs-fuse/lfs /dev/loop0 mount
- ls mount
- mkdir mount/littlefs
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs
- cd mount/littlefs
- stat .
- ls -flh
- make -B test
# test migration using littlefs-fuse
- stage: test
env:
- 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
- rm -rf v2/littlefs/*
- cp -r $(git ls-tree --name-only HEAD) v2/littlefs
- mkdir mount
- sudo chmod a+rw /dev/loop0
- dd if=/dev/zero bs=512 count=128K of=disk
- losetup /dev/loop0 disk
script:
# compile v1 and v2
- make -C v1
- make -C v2
# run self-host test with v1
- v1/lfs --format /dev/loop0
- v1/lfs /dev/loop0 mount
- ls mount
- mkdir mount/littlefs
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs
- cd mount/littlefs
- stat .
- ls -flh
- make -B test
# attempt to migrate
- cd ../..
- fusermount -u mount
- v2/lfs --migrate /dev/loop0
- 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:
- |
# don't clobber other (not us) failures
if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
| jq -e ".statuses[] | select(
.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and
.state == \"failure\" and
(.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))"
then
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:
- |
# don't clobber other (not us) failures
if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
| jq -e ".statuses[] | select(
.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and
.state == \"failure\" and
(.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))"
then
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:
- |
# don't clobber other (not us) failures
# only update if we were last job to mark in progress,
# this isn't perfect but is probably good enough
if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
| jq -e ".statuses[] | select(
.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and
(.state == \"failure\" or .state == \"pending\") and
(.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))"
then
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\": \"success\",
\"description\": \"${STATUS:-Passed}\",
\"target_url\": \"$TRAVIS_JOB_WEB_URL#$TRAVIS_JOB_NUMBER\"
}"
fi

View File

@@ -1,114 +1,67 @@
ifdef BUILDDIR
# make sure BUILDDIR ends with a slash
override BUILDDIR := $(BUILDDIR)/
# bit of a hack, but we want to make sure BUILDDIR directory structure
# is correct before any commands
$(if $(findstring n,$(MAKEFLAGS)),, $(shell mkdir -p \
$(BUILDDIR) \
$(BUILDDIR)bd \
$(BUILDDIR)tests))
endif
# overridable target/src/tools/flags/etc
TARGET = lfs.a
ifneq ($(wildcard test.c main.c),)
TARGET ?= $(BUILDDIR)lfs2
else
TARGET ?= $(BUILDDIR)lfs2.a
override TARGET = lfs
endif
CC ?= gcc
AR ?= ar
SIZE ?= size
CTAGS ?= ctags
NM ?= nm
LCOV ?= lcov
SRC ?= $(wildcard *.c)
OBJ := $(SRC:%.c=$(BUILDDIR)%.o)
DEP := $(SRC:%.c=$(BUILDDIR)%.d)
ASM := $(SRC:%.c=$(BUILDDIR)%.s)
SRC += $(wildcard *.c bd/*.c)
OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.d)
ASM := $(SRC:.c=.s)
ifdef DEBUG
override CFLAGS += -O0 -g3
else
override CFLAGS += -Os
endif
ifdef WORD
override CFLAGS += -m$(WORD)
endif
ifdef TRACE
override CFLAGS += -DLFS2_YES_TRACE
override CFLAGS += -DLFS_YES_TRACE
endif
override CFLAGS += -I.
override CFLAGS += -std=c99 -Wall -pedantic
override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef
ifdef VERBOSE
override TESTFLAGS += -v
override CODEFLAGS += -v
override COVERAGEFLAGS += -v
endif
ifdef EXEC
override TESTFLAGS += --exec="$(EXEC)"
endif
ifdef BUILDDIR
override TESTFLAGS += --build-dir="$(BUILDDIR:/=)"
override CODEFLAGS += --build-dir="$(BUILDDIR:/=)"
endif
ifneq ($(NM),nm)
override CODEFLAGS += --nm-tool="$(NM)"
override TFLAGS += -v
endif
# commands
.PHONY: all build
all build: $(TARGET)
all: $(TARGET)
.PHONY: asm
asm: $(ASM)
.PHONY: size
size: $(OBJ)
$(SIZE) -t $^
.PHONY: tags
tags:
$(CTAGS) --totals --c-types=+p $(shell find -H -name '*.h') $(SRC)
.PHONY: code
code: $(OBJ)
./scripts/code.py $^ $(CODEFLAGS)
.PHONY: test
test:
./scripts/test.py $(TESTFLAGS)
./scripts/test.py $(TFLAGS)
.SECONDEXPANSION:
test%: tests/test$$(firstword $$(subst \#, ,%)).toml
./scripts/test.py $@ $(TESTFLAGS)
./scripts/test.py $@ $(TFLAGS)
.PHONY: coverage
coverage:
./scripts/coverage.py $(BUILDDIR)tests/*.toml.info $(COVERAGEFLAGS)
# rules
-include $(DEP)
.SUFFIXES:
$(BUILDDIR)lfs2: $(OBJ)
lfs: $(OBJ)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
$(BUILDDIR)%.a: $(OBJ)
%.a: $(OBJ)
$(AR) rcs $@ $^
$(BUILDDIR)%.o: %.c
%.o: %.c
$(CC) -c -MMD $(CFLAGS) $< -o $@
$(BUILDDIR)%.s: %.c
%.s: %.c
$(CC) -S $(CFLAGS) $< -o $@
# clean everything
.PHONY: clean
clean:
rm -f $(TARGET)
rm -f $(OBJ)
rm -f $(DEP)
rm -f $(ASM)
rm -f $(BUILDDIR)tests/*.toml.*
rm -f tests/*.toml.*

View File

@@ -32,14 +32,14 @@ main runs. The program can be interrupted at any time without losing track
of how many times it has been booted and without corrupting the filesystem:
``` c
#include "lfs2.h"
#include "lfs.h"
// variables used by the filesystem
lfs2_t lfs2;
lfs2_file_t file;
lfs_t lfs;
lfs_file_t file;
// configuration of the filesystem is provided by this struct
const struct lfs2_config cfg = {
const struct lfs_cfg cfg = {
// block device operations
.read = user_provided_block_device_read,
.prog = user_provided_block_device_prog,
@@ -59,30 +59,30 @@ const struct lfs2_config cfg = {
// entry point
int main(void) {
// mount the filesystem
int err = lfs2_mount(&lfs2, &cfg);
int err = lfs_mount(&lfs, &cfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err) {
lfs2_format(&lfs2, &cfg);
lfs2_mount(&lfs2, &cfg);
lfs_format(&lfs, &cfg);
lfs_mount(&lfs, &cfg);
}
// read current count
uint32_t boot_count = 0;
lfs2_file_open(&lfs2, &file, "boot_count", LFS2_O_RDWR | LFS2_O_CREAT);
lfs2_file_read(&lfs2, &file, &boot_count, sizeof(boot_count));
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
// update boot count
boot_count += 1;
lfs2_file_rewind(&lfs2, &file);
lfs2_file_write(&lfs2, &file, &boot_count, sizeof(boot_count));
lfs_file_rewind(&lfs, &file);
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
// remember the storage is not updated until the file is closed successfully
lfs2_file_close(&lfs2, &file);
lfs_file_close(&lfs, &file);
// release any resources we were using
lfs2_unmount(&lfs2);
lfs_unmount(&lfs);
// print the boot count
printf("boot_count: %d\n", boot_count);
@@ -92,7 +92,7 @@ int main(void) {
## Usage
Detailed documentation (or at least as much detail as is currently available)
can be found in the comments in [lfs2.h](lfs2.h).
can be found in the comments in [lfs.h](lfs.h).
littlefs takes in a configuration structure that defines how the filesystem
operates. The configuration struct provides the filesystem with the block
@@ -100,9 +100,9 @@ device operations and dimensions, tweakable parameters that tradeoff memory
usage for performance, and optional static buffers if the user wants to avoid
dynamic memory.
The state of the littlefs is stored in the `lfs2_t` type which is left up
The state of the littlefs is stored in the `lfs_t` type which is left up
to the user to allocate, allowing multiple filesystems to be in use
simultaneously. With the `lfs2_t` and configuration struct, a user can
simultaneously. With the `lfs_t` and configuration struct, a user can
format a block device or mount the filesystem.
Once mounted, the littlefs provides a full set of POSIX-like file and
@@ -119,11 +119,11 @@ 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
errors can be either one of those found in the `enum lfs2_error` in
[lfs2.h](lfs2.h), or an error returned by the user's block device operations.
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.
In the configuration struct, the `prog` and `erase` function provided by the
user may return a `LFS2_ERR_CORRUPT` error if the implementation already can
user may return a `LFS_ERR_CORRUPT` error if the implementation already can
detect corrupt blocks. However, the wear leveling does not depend on the return
code of these functions, instead all data is read back and checked for
integrity.
@@ -192,7 +192,7 @@ More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and
## Testing
The littlefs comes with a test suite designed to run on a PC using the
[emulated block device](bd/lfs2_testbd.h) found in the `bd` directory.
[emulated block device](emubd/lfs_emubd.h) found in the emubd directory.
The tests assume a Linux environment and can be started with make:
``` bash
@@ -222,11 +222,6 @@ License Identifiers that are here available: http://spdx.org/licenses/
want this, but it is handy for demos. You can see it in action
[here][littlefs-js-demo].
- [littlefs-python] - A Python wrapper for littlefs. The project allows you
to create images of the filesystem on your PC. Check if littlefs will fit
your needs, create images for a later download to the target memory or
inspect the content of a binary image of the target memory.
- [mklfs] - A command line tool built by the [Lua RTOS] guys for making
littlefs images from a host PC. Supports Windows, Mac OS, and Linux.
@@ -255,4 +250,3 @@ License Identifiers that are here available: http://spdx.org/licenses/
[LittleFileSystem]: https://os.mbed.com/docs/mbed-os/v5.12/apis/littlefilesystem.html
[SPIFFS]: https://github.com/pellepl/spiffs
[Dhara]: https://github.com/dlbeer/dhara
[littlefs-python]: https://pypi.org/project/littlefs-python/

48
SPEC.md
View File

@@ -233,19 +233,19 @@ Metadata tag fields:
into a 3-bit abstract type and an 8-bit chunk field. Note that the value
`0x000` is invalid and not assigned a type.
1. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into
8 categories that facilitate bitmasked lookups.
3. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into
8 categories that facilitate bitmasked lookups.
2. **Chunk (8-bits)** - Chunk field used for various purposes by the different
abstract types. type1+chunk+id form a unique identifier for each tag in the
metadata block.
4. **Chunk (8-bits)** - Chunk field used for various purposes by the different
abstract types. type1+chunk+id form a unique identifier for each tag in the
metadata block.
3. **Id (10-bits)** - File id associated with the tag. Each file in a metadata
5. **Id (10-bits)** - File id associated with the tag. Each file in a metadata
block gets a unique id which is used to associate tags with that file. The
special value `0x3ff` is used for any tags that are not associated with a
file, such as directory and global metadata.
4. **Length (10-bits)** - Length of the data in bytes. The special value
6. **Length (10-bits)** - Length of the data in bytes. The special value
`0x3ff` indicates that this tag has been deleted.
## Metadata types
@@ -253,7 +253,7 @@ Metadata tag fields:
What follows is an exhaustive list of metadata in littlefs.
---
#### `0x401` LFS2_TYPE_CREATE
#### `0x401` LFS_TYPE_CREATE
Creates a new file with this id. Note that files in a metadata block
don't necessarily need a create tag. All a create does is move over any
@@ -264,14 +264,14 @@ The create and delete tags allow littlefs to keep files in a directory
ordered alphabetically by filename.
---
#### `0x4ff` LFS2_TYPE_DELETE
#### `0x4ff` LFS_TYPE_DELETE
Deletes the file with this id. An inverse to create, this tag moves over
any files neighboring this id similar to a deletion from an imaginary
array of files.
---
#### `0x0xx` LFS2_TYPE_NAME
#### `0x0xx` LFS_TYPE_NAME
Associates the id with a file name and file type.
@@ -304,14 +304,14 @@ Name fields:
2. **file name** - File name stored as an ASCII string.
---
#### `0x001` LFS2_TYPE_REG
#### `0x001` LFS_TYPE_REG
Initializes the id + name as a regular file.
How each file is stored depends on its struct tag, which is described below.
---
#### `0x002` LFS2_TYPE_DIR
#### `0x002` LFS_TYPE_DIR
Initializes the id + name as a directory.
@@ -320,7 +320,7 @@ each pair containing any number of files in alphabetical order. A pointer to
the directory is stored in the struct tag, which is described below.
---
#### `0x0ff` LFS2_TYPE_SUPERBLOCK
#### `0x0ff` LFS_TYPE_SUPERBLOCK
Initializes the id as a superblock entry.
@@ -405,7 +405,7 @@ as be the first entry written to the block. This means that the superblock
entry can be read from a device using offsets alone.
---
#### `0x2xx` LFS2_TYPE_STRUCT
#### `0x2xx` LFS_TYPE_STRUCT
Associates the id with an on-disk data structure.
@@ -416,7 +416,7 @@ Any type of struct supersedes all other structs associated with the id. For
example, appending a ctz-struct replaces an inline-struct on the same file.
---
#### `0x200` LFS2_TYPE_DIRSTRUCT
#### `0x200` LFS_TYPE_DIRSTRUCT
Gives the id a directory data structure.
@@ -458,7 +458,7 @@ Dir-struct fields:
in the directory.
---
#### `0x201` LFS2_TYPE_INLINESTRUCT
#### `0x201` LFS_TYPE_INLINESTRUCT
Gives the id an inline data structure.
@@ -482,7 +482,7 @@ Inline-struct fields:
1. **Inline data** - File data stored directly in the metadata-pair.
---
#### `0x202` LFS2_TYPE_CTZSTRUCT
#### `0x202` LFS_TYPE_CTZSTRUCT
Gives the id a CTZ skip-list data structure.
@@ -537,7 +537,7 @@ CTZ-struct fields:
2. **File size (32-bits)** - Size of the file in bytes.
---
#### `0x3xx` LFS2_TYPE_USERATTR
#### `0x3xx` LFS_TYPE_USERATTR
Attaches a user attribute to an id.
@@ -571,7 +571,7 @@ User-attr fields:
2. **Attr data** - The data associated with the user attribute.
---
#### `0x6xx` LFS2_TYPE_TAIL
#### `0x6xx` LFS_TYPE_TAIL
Provides the tail pointer for the metadata pair itself.
@@ -637,7 +637,7 @@ Tail fields:
2. **Metadata pair (8-bytes)** - Pointer to the next metadata-pair.
---
#### `0x600` LFS2_TYPE_SOFTTAIL
#### `0x600` LFS_TYPE_SOFTTAIL
Provides a tail pointer that points to the next metadata pair in the
filesystem.
@@ -646,7 +646,7 @@ In this case, the next metadata pair is not a part of our current directory
and should only be followed when traversing the entire filesystem.
---
#### `0x601` LFS2_TYPE_HARDTAIL
#### `0x601` LFS_TYPE_HARDTAIL
Provides a tail pointer that points to the next metadata pair in the
directory.
@@ -657,7 +657,7 @@ metadata pair should only contain filenames greater than any filename in the
current pair.
---
#### `0x7xx` LFS2_TYPE_GSTATE
#### `0x7xx` LFS_TYPE_GSTATE
Provides delta bits for global state entries.
@@ -687,7 +687,7 @@ is stored in the chunk field. Currently, the only global state is move state,
which is outlined below.
---
#### `0x7ff` LFS2_TYPE_MOVESTATE
#### `0x7ff` LFS_TYPE_MOVESTATE
Provides delta bits for the global move state.
@@ -740,7 +740,7 @@ Move state fields:
the move.
---
#### `0x5xx` LFS2_TYPE_CRC
#### `0x5xx` LFS_TYPE_CRC
Last but not least, the CRC tag marks the end of a commit and provides a
checksum for any commits to the metadata block.

View File

@@ -1,205 +0,0 @@
/*
* Block device emulated in a file
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs2_filebd.h"
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int lfs2_filebd_createcfg(const struct lfs2_config *cfg, const char *path,
const struct lfs2_filebd_config *bdcfg) {
LFS2_FILEBD_TRACE("lfs2_filebd_createcfg(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
"\"%s\", "
"%p {.erase_value=%"PRId32"})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
path, (void*)bdcfg, bdcfg->erase_value);
lfs2_filebd_t *bd = cfg->context;
bd->cfg = bdcfg;
// open file
bd->fd = open(path, O_RDWR | O_CREAT, 0666);
if (bd->fd < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_createcfg -> %d", err);
return err;
}
LFS2_FILEBD_TRACE("lfs2_filebd_createcfg -> %d", 0);
return 0;
}
int lfs2_filebd_create(const struct lfs2_config *cfg, const char *path) {
LFS2_FILEBD_TRACE("lfs2_filebd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
"\"%s\")",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
path);
static const struct lfs2_filebd_config defaults = {.erase_value=-1};
int err = lfs2_filebd_createcfg(cfg, path, &defaults);
LFS2_FILEBD_TRACE("lfs2_filebd_create -> %d", err);
return err;
}
int lfs2_filebd_destroy(const struct lfs2_config *cfg) {
LFS2_FILEBD_TRACE("lfs2_filebd_destroy(%p)", (void*)cfg);
lfs2_filebd_t *bd = cfg->context;
int err = close(bd->fd);
if (err < 0) {
err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", err);
return err;
}
LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", 0);
return 0;
}
int lfs2_filebd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
LFS2_FILEBD_TRACE("lfs2_filebd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_filebd_t *bd = cfg->context;
// check if read is valid
LFS2_ASSERT(off % cfg->read_size == 0);
LFS2_ASSERT(size % cfg->read_size == 0);
LFS2_ASSERT(block < cfg->block_count);
// zero for reproducibility (in case file is truncated)
if (bd->cfg->erase_value != -1) {
memset(buffer, bd->cfg->erase_value, size);
}
// read
off_t res1 = lseek(bd->fd,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", err);
return err;
}
ssize_t res2 = read(bd->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", err);
return err;
}
LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", 0);
return 0;
}
int lfs2_filebd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
LFS2_FILEBD_TRACE("lfs2_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_filebd_t *bd = cfg->context;
// check if write is valid
LFS2_ASSERT(off % cfg->prog_size == 0);
LFS2_ASSERT(size % cfg->prog_size == 0);
LFS2_ASSERT(block < cfg->block_count);
// check that data was erased? only needed for testing
if (bd->cfg->erase_value != -1) {
off_t res1 = lseek(bd->fd,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err);
return err;
}
for (lfs2_off_t i = 0; i < size; i++) {
uint8_t c;
ssize_t res2 = read(bd->fd, &c, 1);
if (res2 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err);
return err;
}
LFS2_ASSERT(c == bd->cfg->erase_value);
}
}
// program data
off_t res1 = lseek(bd->fd,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err);
return err;
}
ssize_t res2 = write(bd->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err);
return err;
}
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", 0);
return 0;
}
int lfs2_filebd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
LFS2_FILEBD_TRACE("lfs2_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs2_filebd_t *bd = cfg->context;
// check if erase is valid
LFS2_ASSERT(block < cfg->block_count);
// erase, only needed for testing
if (bd->cfg->erase_value != -1) {
off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", err);
return err;
}
for (lfs2_off_t i = 0; i < cfg->block_size; i++) {
ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1);
if (res2 < 0) {
int err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", err);
return err;
}
}
}
LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", 0);
return 0;
}
int lfs2_filebd_sync(const struct lfs2_config *cfg) {
LFS2_FILEBD_TRACE("lfs2_filebd_sync(%p)", (void*)cfg);
// file sync
lfs2_filebd_t *bd = cfg->context;
int err = fsync(bd->fd);
if (err) {
err = -errno;
LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0);
return err;
}
LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0);
return 0;
}

View File

@@ -1,73 +0,0 @@
/*
* Block device emulated in a file
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_FILEBD_H
#define LFS2_FILEBD_H
#include "lfs2.h"
#include "lfs2_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Block device specific tracing
#ifdef LFS2_FILEBD_YES_TRACE
#define LFS2_FILEBD_TRACE(...) LFS2_TRACE(__VA_ARGS__)
#else
#define LFS2_FILEBD_TRACE(...)
#endif
// filebd config (optional)
struct lfs2_filebd_config {
// 8-bit erase value to use for simulating erases. -1 does not simulate
// erases, which can speed up testing by avoiding all the extra block-device
// operations to store the erase value.
int32_t erase_value;
};
// filebd state
typedef struct lfs2_filebd {
int fd;
const struct lfs2_filebd_config *cfg;
} lfs2_filebd_t;
// Create a file block device using the geometry in lfs2_config
int lfs2_filebd_create(const struct lfs2_config *cfg, const char *path);
int lfs2_filebd_createcfg(const struct lfs2_config *cfg, const char *path,
const struct lfs2_filebd_config *bdcfg);
// Clean up memory associated with block device
int lfs2_filebd_destroy(const struct lfs2_config *cfg);
// Read a block
int lfs2_filebd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs2_filebd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs2_filebd_erase(const struct lfs2_config *cfg, lfs2_block_t block);
// Sync the block device
int lfs2_filebd_sync(const struct lfs2_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -1,142 +0,0 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs2_rambd.h"
int lfs2_rambd_createcfg(const struct lfs2_config *cfg,
const struct lfs2_rambd_config *bdcfg) {
LFS2_RAMBD_TRACE("lfs2_rambd_createcfg(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
"%p {.erase_value=%"PRId32", .buffer=%p})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
(void*)bdcfg, bdcfg->erase_value, bdcfg->buffer);
lfs2_rambd_t *bd = cfg->context;
bd->cfg = bdcfg;
// allocate buffer?
if (bd->cfg->buffer) {
bd->buffer = bd->cfg->buffer;
} else {
bd->buffer = lfs2_malloc(cfg->block_size * cfg->block_count);
if (!bd->buffer) {
LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
}
// zero for reproducibility?
if (bd->cfg->erase_value != -1) {
memset(bd->buffer, bd->cfg->erase_value,
cfg->block_size * cfg->block_count);
} else {
memset(bd->buffer, 0, cfg->block_size * cfg->block_count);
}
LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", 0);
return 0;
}
int lfs2_rambd_create(const struct lfs2_config *cfg) {
LFS2_RAMBD_TRACE("lfs2_rambd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count);
static const struct lfs2_rambd_config defaults = {.erase_value=-1};
int err = lfs2_rambd_createcfg(cfg, &defaults);
LFS2_RAMBD_TRACE("lfs2_rambd_create -> %d", err);
return err;
}
int lfs2_rambd_destroy(const struct lfs2_config *cfg) {
LFS2_RAMBD_TRACE("lfs2_rambd_destroy(%p)", (void*)cfg);
// clean up memory
lfs2_rambd_t *bd = cfg->context;
if (!bd->cfg->buffer) {
lfs2_free(bd->buffer);
}
LFS2_RAMBD_TRACE("lfs2_rambd_destroy -> %d", 0);
return 0;
}
int lfs2_rambd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
LFS2_RAMBD_TRACE("lfs2_rambd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_rambd_t *bd = cfg->context;
// check if read is valid
LFS2_ASSERT(off % cfg->read_size == 0);
LFS2_ASSERT(size % cfg->read_size == 0);
LFS2_ASSERT(block < cfg->block_count);
// read data
memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size);
LFS2_RAMBD_TRACE("lfs2_rambd_read -> %d", 0);
return 0;
}
int lfs2_rambd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
LFS2_RAMBD_TRACE("lfs2_rambd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_rambd_t *bd = cfg->context;
// check if write is valid
LFS2_ASSERT(off % cfg->prog_size == 0);
LFS2_ASSERT(size % cfg->prog_size == 0);
LFS2_ASSERT(block < cfg->block_count);
// check that data was erased? only needed for testing
if (bd->cfg->erase_value != -1) {
for (lfs2_off_t i = 0; i < size; i++) {
LFS2_ASSERT(bd->buffer[block*cfg->block_size + off + i] ==
bd->cfg->erase_value);
}
}
// program data
memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size);
LFS2_RAMBD_TRACE("lfs2_rambd_prog -> %d", 0);
return 0;
}
int lfs2_rambd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
LFS2_RAMBD_TRACE("lfs2_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs2_rambd_t *bd = cfg->context;
// check if erase is valid
LFS2_ASSERT(block < cfg->block_count);
// erase, only needed for testing
if (bd->cfg->erase_value != -1) {
memset(&bd->buffer[block*cfg->block_size],
bd->cfg->erase_value, cfg->block_size);
}
LFS2_RAMBD_TRACE("lfs2_rambd_erase -> %d", 0);
return 0;
}
int lfs2_rambd_sync(const struct lfs2_config *cfg) {
LFS2_RAMBD_TRACE("lfs2_rambd_sync(%p)", (void*)cfg);
// sync does nothing because we aren't backed by anything real
(void)cfg;
LFS2_RAMBD_TRACE("lfs2_rambd_sync -> %d", 0);
return 0;
}

View File

@@ -1,75 +0,0 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_RAMBD_H
#define LFS2_RAMBD_H
#include "lfs2.h"
#include "lfs2_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Block device specific tracing
#ifdef LFS2_RAMBD_YES_TRACE
#define LFS2_RAMBD_TRACE(...) LFS2_TRACE(__VA_ARGS__)
#else
#define LFS2_RAMBD_TRACE(...)
#endif
// rambd config (optional)
struct lfs2_rambd_config {
// 8-bit erase value to simulate erasing with. -1 indicates no erase
// occurs, which is still a valid block device
int32_t erase_value;
// Optional statically allocated buffer for the block device.
void *buffer;
};
// rambd state
typedef struct lfs2_rambd {
uint8_t *buffer;
const struct lfs2_rambd_config *cfg;
} lfs2_rambd_t;
// Create a RAM block device using the geometry in lfs2_config
int lfs2_rambd_create(const struct lfs2_config *cfg);
int lfs2_rambd_createcfg(const struct lfs2_config *cfg,
const struct lfs2_rambd_config *bdcfg);
// Clean up memory associated with block device
int lfs2_rambd_destroy(const struct lfs2_config *cfg);
// Read a block
int lfs2_rambd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs2_rambd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs2_rambd_erase(const struct lfs2_config *cfg, lfs2_block_t block);
// Sync the block device
int lfs2_rambd_sync(const struct lfs2_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -1,302 +0,0 @@
/*
* Testing block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs2_testbd.h"
#include <stdlib.h>
int lfs2_testbd_createcfg(const struct lfs2_config *cfg, const char *path,
const struct lfs2_testbd_config *bdcfg) {
LFS2_TESTBD_TRACE("lfs2_testbd_createcfg(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
"\"%s\", "
"%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
".buffer=%p, .wear_buffer=%p})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles,
bdcfg->badblock_behavior, bdcfg->power_cycles,
bdcfg->buffer, bdcfg->wear_buffer);
lfs2_testbd_t *bd = cfg->context;
bd->cfg = bdcfg;
// setup testing things
bd->persist = path;
bd->power_cycles = bd->cfg->power_cycles;
if (bd->cfg->erase_cycles) {
if (bd->cfg->wear_buffer) {
bd->wear = bd->cfg->wear_buffer;
} else {
bd->wear = lfs2_malloc(sizeof(lfs2_testbd_wear_t)*cfg->block_count);
if (!bd->wear) {
LFS2_TESTBD_TRACE("lfs2_testbd_createcfg -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
}
memset(bd->wear, 0, sizeof(lfs2_testbd_wear_t) * cfg->block_count);
}
// create underlying block device
if (bd->persist) {
bd->u.file.cfg = (struct lfs2_filebd_config){
.erase_value = bd->cfg->erase_value,
};
int err = lfs2_filebd_createcfg(cfg, path, &bd->u.file.cfg);
LFS2_TESTBD_TRACE("lfs2_testbd_createcfg -> %d", err);
return err;
} else {
bd->u.ram.cfg = (struct lfs2_rambd_config){
.erase_value = bd->cfg->erase_value,
.buffer = bd->cfg->buffer,
};
int err = lfs2_rambd_createcfg(cfg, &bd->u.ram.cfg);
LFS2_TESTBD_TRACE("lfs2_testbd_createcfg -> %d", err);
return err;
}
}
int lfs2_testbd_create(const struct lfs2_config *cfg, const char *path) {
LFS2_TESTBD_TRACE("lfs2_testbd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
"\"%s\")",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
path);
static const struct lfs2_testbd_config defaults = {.erase_value=-1};
int err = lfs2_testbd_createcfg(cfg, path, &defaults);
LFS2_TESTBD_TRACE("lfs2_testbd_create -> %d", err);
return err;
}
int lfs2_testbd_destroy(const struct lfs2_config *cfg) {
LFS2_TESTBD_TRACE("lfs2_testbd_destroy(%p)", (void*)cfg);
lfs2_testbd_t *bd = cfg->context;
if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) {
lfs2_free(bd->wear);
}
if (bd->persist) {
int err = lfs2_filebd_destroy(cfg);
LFS2_TESTBD_TRACE("lfs2_testbd_destroy -> %d", err);
return err;
} else {
int err = lfs2_rambd_destroy(cfg);
LFS2_TESTBD_TRACE("lfs2_testbd_destroy -> %d", err);
return err;
}
}
/// Internal mapping to block devices ///
static int lfs2_testbd_rawread(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
lfs2_testbd_t *bd = cfg->context;
if (bd->persist) {
return lfs2_filebd_read(cfg, block, off, buffer, size);
} else {
return lfs2_rambd_read(cfg, block, off, buffer, size);
}
}
static int lfs2_testbd_rawprog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
lfs2_testbd_t *bd = cfg->context;
if (bd->persist) {
return lfs2_filebd_prog(cfg, block, off, buffer, size);
} else {
return lfs2_rambd_prog(cfg, block, off, buffer, size);
}
}
static int lfs2_testbd_rawerase(const struct lfs2_config *cfg,
lfs2_block_t block) {
lfs2_testbd_t *bd = cfg->context;
if (bd->persist) {
return lfs2_filebd_erase(cfg, block);
} else {
return lfs2_rambd_erase(cfg, block);
}
}
static int lfs2_testbd_rawsync(const struct lfs2_config *cfg) {
lfs2_testbd_t *bd = cfg->context;
if (bd->persist) {
return lfs2_filebd_sync(cfg);
} else {
return lfs2_rambd_sync(cfg);
}
}
/// block device API ///
int lfs2_testbd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
LFS2_TESTBD_TRACE("lfs2_testbd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_testbd_t *bd = cfg->context;
// check if read is valid
LFS2_ASSERT(off % cfg->read_size == 0);
LFS2_ASSERT(size % cfg->read_size == 0);
LFS2_ASSERT(block < cfg->block_count);
// block bad?
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles &&
bd->cfg->badblock_behavior == LFS2_TESTBD_BADBLOCK_READERROR) {
LFS2_TESTBD_TRACE("lfs2_testbd_read -> %d", LFS2_ERR_CORRUPT);
return LFS2_ERR_CORRUPT;
}
// read
int err = lfs2_testbd_rawread(cfg, block, off, buffer, size);
LFS2_TESTBD_TRACE("lfs2_testbd_read -> %d", err);
return err;
}
int lfs2_testbd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
LFS2_TESTBD_TRACE("lfs2_testbd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_testbd_t *bd = cfg->context;
// check if write is valid
LFS2_ASSERT(off % cfg->prog_size == 0);
LFS2_ASSERT(size % cfg->prog_size == 0);
LFS2_ASSERT(block < cfg->block_count);
// block bad?
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS2_TESTBD_BADBLOCK_PROGERROR) {
LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", LFS2_ERR_CORRUPT);
return LFS2_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS2_TESTBD_BADBLOCK_PROGNOOP ||
bd->cfg->badblock_behavior ==
LFS2_TESTBD_BADBLOCK_ERASENOOP) {
LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", 0);
return 0;
}
}
// prog
int err = lfs2_testbd_rawprog(cfg, block, off, buffer, size);
if (err) {
LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", err);
return err;
}
// lose power?
if (bd->power_cycles > 0) {
bd->power_cycles -= 1;
if (bd->power_cycles == 0) {
// sync to make sure we persist the last changes
LFS2_ASSERT(lfs2_testbd_rawsync(cfg) == 0);
// simulate power loss
exit(33);
}
}
LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", 0);
return 0;
}
int lfs2_testbd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
LFS2_TESTBD_TRACE("lfs2_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs2_testbd_t *bd = cfg->context;
// check if erase is valid
LFS2_ASSERT(block < cfg->block_count);
// block bad?
if (bd->cfg->erase_cycles) {
if (bd->wear[block] >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS2_TESTBD_BADBLOCK_ERASEERROR) {
LFS2_TESTBD_TRACE("lfs2_testbd_erase -> %d", LFS2_ERR_CORRUPT);
return LFS2_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS2_TESTBD_BADBLOCK_ERASENOOP) {
LFS2_TESTBD_TRACE("lfs2_testbd_erase -> %d", 0);
return 0;
}
} else {
// mark wear
bd->wear[block] += 1;
}
}
// erase
int err = lfs2_testbd_rawerase(cfg, block);
if (err) {
LFS2_TESTBD_TRACE("lfs2_testbd_erase -> %d", err);
return err;
}
// lose power?
if (bd->power_cycles > 0) {
bd->power_cycles -= 1;
if (bd->power_cycles == 0) {
// sync to make sure we persist the last changes
LFS2_ASSERT(lfs2_testbd_rawsync(cfg) == 0);
// simulate power loss
exit(33);
}
}
LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", 0);
return 0;
}
int lfs2_testbd_sync(const struct lfs2_config *cfg) {
LFS2_TESTBD_TRACE("lfs2_testbd_sync(%p)", (void*)cfg);
int err = lfs2_testbd_rawsync(cfg);
LFS2_TESTBD_TRACE("lfs2_testbd_sync -> %d", err);
return err;
}
/// simulated wear operations ///
lfs2_testbd_swear_t lfs2_testbd_getwear(const struct lfs2_config *cfg,
lfs2_block_t block) {
LFS2_TESTBD_TRACE("lfs2_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block);
lfs2_testbd_t *bd = cfg->context;
// check if block is valid
LFS2_ASSERT(bd->cfg->erase_cycles);
LFS2_ASSERT(block < cfg->block_count);
LFS2_TESTBD_TRACE("lfs2_testbd_getwear -> %"PRIu32, bd->wear[block]);
return bd->wear[block];
}
int lfs2_testbd_setwear(const struct lfs2_config *cfg,
lfs2_block_t block, lfs2_testbd_wear_t wear) {
LFS2_TESTBD_TRACE("lfs2_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block);
lfs2_testbd_t *bd = cfg->context;
// check if block is valid
LFS2_ASSERT(bd->cfg->erase_cycles);
LFS2_ASSERT(block < cfg->block_count);
bd->wear[block] = wear;
LFS2_TESTBD_TRACE("lfs2_testbd_setwear -> %d", 0);
return 0;
}

182
bd/lfs_filebd.c Normal file
View File

@@ -0,0 +1,182 @@
/*
* Block device emulated in a file
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs_filebd.h"
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int lfs_filebd_createcfg(lfs_filebd_t *bd, const char *path,
const struct lfs_filebd_cfg *cfg) {
LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p, \"%s\", %p {"
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
".erase_value=%"PRId32"})",
(void*)bd, path, (void*)cfg,
cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
cfg->erase_value);
// copy over config
bd->cfg = *cfg;
// open file
bd->fd = open(path, O_RDWR | O_CREAT, 0666);
if (bd->fd < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", 0);
return 0;
}
int lfs_filebd_destroy(lfs_filebd_t *bd) {
LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)bd);
int err = close(bd->fd);
if (err < 0) {
err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0);
return 0;
}
int lfs_filebd_read(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)bd, block, off, buffer, size);
// check if read is valid
LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < bd->cfg.erase_count);
// zero for reproducability (in case file is truncated)
if (bd->cfg.erase_value != -1) {
memset(buffer, bd->cfg.erase_value, size);
}
// read
off_t res1 = lseek(bd->fd,
(off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
return err;
}
ssize_t res2 = read(bd->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0);
return 0;
}
int lfs_filebd_prog(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)bd, block, off, buffer, size);
// check if write is valid
LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < bd->cfg.erase_count);
// check that data was erased? only needed for testing
if (bd->cfg.erase_value != -1) {
off_t res1 = lseek(bd->fd,
(off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
return err;
}
for (lfs_off_t i = 0; i < size; i++) {
uint8_t c;
ssize_t res2 = read(bd->fd, &c, 1);
if (res2 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
return err;
}
LFS_ASSERT(c == bd->cfg.erase_value);
}
}
// program data
off_t res1 = lseek(bd->fd,
(off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
return err;
}
ssize_t res2 = write(bd->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0);
return 0;
}
int lfs_filebd_erase(lfs_filebd_t *bd, lfs_block_t block) {
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
// check if erase is valid
LFS_ASSERT(block < bd->cfg.erase_count);
// erase, only needed for testing
if (bd->cfg.erase_value != -1) {
off_t res1 = lseek(bd->fd, (off_t)block*bd->cfg.erase_size, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);
return err;
}
for (lfs_off_t i = 0; i < bd->cfg.erase_size; i++) {
ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg.erase_value}, 1);
if (res2 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);
return err;
}
}
}
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0);
return 0;
}
int lfs_filebd_sync(lfs_filebd_t *bd) {
LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)bd);
// file sync
int err = fsync(bd->fd);
if (err) {
err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
return 0;
}

84
bd/lfs_filebd.h Normal file
View File

@@ -0,0 +1,84 @@
/*
* Block device emulated in a file
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_FILEBD_H
#define LFS_FILEBD_H
#include "lfs.h"
#ifdef __cplusplus
extern "C" {
#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)
struct lfs_filebd_cfg {
// Minimum size of block read. All read operations must be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of block program. All program operations must be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block.
lfs_size_t erase_size;
// Number of erasable blocks on the device.
lfs_size_t erase_count;
// 8-bit erase value to use for simulating erases. -1 does not simulate
// erases, which can speed up testing by avoiding all the extra block-device
// operations to store the erase value.
int32_t erase_value;
};
// filebd state
typedef struct lfs_filebd {
int fd;
struct lfs_filebd_cfg cfg;
} lfs_filebd_t;
// Create a file block device using the geometry in lfs_filebd_cfg
int lfs_filebd_createcfg(lfs_filebd_t *bd, const char *path,
const struct lfs_filebd_cfg *cfg);
// Clean up memory associated with block device
int lfs_filebd_destroy(lfs_filebd_t *bd);
// Read a block
int lfs_filebd_read(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs_filebd_prog(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_filebd_erase(lfs_filebd_t *bd, lfs_block_t block);
// Sync the block device
int lfs_filebd_sync(lfs_filebd_t *bd);
#ifdef __cplusplus
}
#endif
#endif

119
bd/lfs_rambd.c Normal file
View File

@@ -0,0 +1,119 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs_rambd.h"
int lfs_rambd_createcfg(lfs_rambd_t *bd,
const struct lfs_rambd_cfg *cfg) {
LFS_RAMBD_TRACE("lfs_filebd_createcfg(%p, %p {"
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
".erase_value=%"PRId32", .buffer=%p})",
(void*)bd, (void*)cfg,
cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
cfg->erase_value, cfg->buffer);
// copy over config
bd->cfg = *cfg;
// allocate buffer?
if (bd->cfg.buffer) {
bd->buffer = bd->cfg.buffer;
} else {
bd->buffer = lfs_malloc(bd->cfg.erase_size * bd->cfg.erase_count);
if (!bd->buffer) {
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
}
// zero for reproducability?
if (bd->cfg.erase_value != -1) {
memset(bd->buffer, bd->cfg.erase_value,
bd->cfg.erase_size * bd->cfg.erase_count);
}
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0);
return 0;
}
int lfs_rambd_destroy(lfs_rambd_t *bd) {
LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)bd);
// clean up memory
if (!bd->cfg.buffer) {
lfs_free(bd->buffer);
}
LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0);
return 0;
}
int lfs_rambd_read(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)bd, block, off, buffer, size);
// check if read is valid
LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < bd->cfg.erase_count);
// read data
memcpy(buffer, &bd->buffer[block*bd->cfg.erase_size + off], size);
LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0);
return 0;
}
int lfs_rambd_prog(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)bd, block, off, buffer, size);
// check if write is valid
LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < bd->cfg.erase_count);
// check that data was erased? only needed for testing
if (bd->cfg.erase_value != -1) {
for (lfs_off_t i = 0; i < size; i++) {
LFS_ASSERT(bd->buffer[block*bd->cfg.erase_size + off + i] ==
bd->cfg.erase_value);
}
}
// program data
memcpy(&bd->buffer[block*bd->cfg.erase_size + off], buffer, size);
LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0);
return 0;
}
int lfs_rambd_erase(lfs_rambd_t *bd, lfs_block_t block) {
LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
// check if erase is valid
LFS_ASSERT(block < bd->cfg.erase_count);
// erase, only needed for testing
if (bd->cfg.erase_value != -1) {
memset(&bd->buffer[block*bd->cfg.erase_size],
bd->cfg.erase_value, bd->cfg.erase_size);
}
LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0);
return 0;
}
int lfs_rambd_sync(lfs_rambd_t *bd) {
LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)bd);
// sync does nothing because we aren't backed by anything real
(void)bd;
LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0);
return 0;
}

86
bd/lfs_rambd.h Normal file
View File

@@ -0,0 +1,86 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_RAMBD_H
#define LFS_RAMBD_H
#include "lfs.h"
#ifdef __cplusplus
extern "C" {
#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)
struct lfs_rambd_cfg {
// Minimum size of block read. All read operations must be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of block program. All program operations must be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block.
lfs_size_t erase_size;
// Number of erasable blocks on the device.
lfs_size_t erase_count;
// 8-bit erase value to simulate erasing with. -1 indicates no erase
// occurs, which is still a valid block device
int32_t erase_value;
// Optional statically allocated buffer for the block device.
void *buffer;
};
// rambd state
typedef struct lfs_rambd {
uint8_t *buffer;
struct lfs_rambd_cfg cfg;
} lfs_rambd_t;
// Create a RAM block device using the geometry in lfs_cfg
int lfs_rambd_createcfg(lfs_rambd_t *bd,
const struct lfs_rambd_cfg *cfg);
// Clean up memory associated with block device
int lfs_rambd_destroy(lfs_rambd_t *bd);
// Read a block
int lfs_rambd_read(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs_rambd_prog(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_rambd_erase(lfs_rambd_t *bd, lfs_block_t block);
// Sync the block device
int lfs_rambd_sync(lfs_rambd_t *bd);
#ifdef __cplusplus
}
#endif
#endif

278
bd/lfs_testbd.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* Testing block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs_testbd.h"
#include <stdlib.h>
int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path,
const struct lfs_testbd_cfg *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p, \"%s\", %p {"
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
".erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
".buffer=%p, .wear_buffer=%p})",
(void*)bd, path, (void*)cfg,
cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
cfg->erase_value, cfg->erase_cycles,
cfg->badblock_behavior, cfg->power_cycles,
cfg->buffer, cfg->wear_buffer);
// copy over config
bd->cfg = *cfg;
// setup testing things
bd->persist = path;
bd->power_cycles = bd->cfg.power_cycles;
if (bd->cfg.erase_cycles) {
if (bd->cfg.wear_buffer) {
bd->wear = bd->cfg.wear_buffer;
} else {
bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->erase_count);
if (!bd->wear) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
}
memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * bd->cfg.erase_count);
}
// create underlying block device
if (bd->persist) {
int err = lfs_filebd_createcfg(&bd->impl.filebd, path,
&(struct lfs_filebd_cfg){
.read_size=bd->cfg.read_size,
.prog_size=bd->cfg.prog_size,
.erase_size=bd->cfg.erase_size,
.erase_count=bd->cfg.erase_count,
.erase_value=bd->cfg.erase_value});
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
return err;
} else {
int err = lfs_rambd_createcfg(&bd->impl.rambd,
&(struct lfs_rambd_cfg){
.read_size=bd->cfg.read_size,
.prog_size=bd->cfg.prog_size,
.erase_size=bd->cfg.erase_size,
.erase_count=bd->cfg.erase_count,
.erase_value=bd->cfg.erase_value,
.buffer=bd->cfg.buffer});
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
return err;
}
}
int lfs_testbd_destroy(lfs_testbd_t *bd) {
LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)bd);
if (bd->cfg.erase_cycles && !bd->cfg.wear_buffer) {
lfs_free(bd->wear);
}
if (bd->persist) {
int err = lfs_filebd_destroy(&bd->impl.filebd);
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err);
return err;
} else {
int err = lfs_rambd_destroy(&bd->impl.rambd);
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err);
return err;
}
}
/// Internal mapping to block devices ///
static int lfs_testbd_rawread(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
if (bd->persist) {
return lfs_filebd_read(&bd->impl.filebd, block, off, buffer, size);
} else {
return lfs_rambd_read(&bd->impl.rambd, block, off, buffer, size);
}
}
static int lfs_testbd_rawprog(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
if (bd->persist) {
return lfs_filebd_prog(&bd->impl.filebd, block, off, buffer, size);
} else {
return lfs_rambd_prog(&bd->impl.rambd, block, off, buffer, size);
}
}
static int lfs_testbd_rawerase(lfs_testbd_t *bd,
lfs_block_t block) {
if (bd->persist) {
return lfs_filebd_erase(&bd->impl.filebd, block);
} else {
return lfs_rambd_erase(&bd->impl.rambd, block);
}
}
static int lfs_testbd_rawsync(lfs_testbd_t *bd) {
if (bd->persist) {
return lfs_filebd_sync(&bd->impl.filebd);
} else {
return lfs_rambd_sync(&bd->impl.rambd);
}
}
/// block device API ///
int lfs_testbd_read(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)bd, block, off, buffer, size);
// check if read is valid
LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < bd->cfg.erase_count);
// block bad?
if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles &&
bd->cfg.badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
}
// read
int err = lfs_testbd_rawread(bd, block, off, buffer, size);
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err);
return err;
}
int lfs_testbd_prog(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)bd, block, off, buffer, size);
// check if write is valid
LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < bd->cfg.erase_count);
// block bad?
if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles) {
if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGERROR) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
} else if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGNOOP ||
bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
return 0;
}
}
// prog
int err = lfs_testbd_rawprog(bd, block, off, buffer, size);
if (err) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
return err;
}
// lose power?
if (bd->power_cycles > 0) {
bd->power_cycles -= 1;
if (bd->power_cycles == 0) {
// sync to make sure we persist the last changes
assert(lfs_testbd_rawsync(bd) == 0);
// simulate power loss
exit(33);
}
}
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
return 0;
}
int lfs_testbd_erase(lfs_testbd_t *bd, lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
// check if erase is valid
LFS_ASSERT(block < bd->cfg.erase_count);
// block bad?
if (bd->cfg.erase_cycles) {
if (bd->wear[block] >= bd->cfg.erase_cycles) {
if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASEERROR) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
} else if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0);
return 0;
}
} else {
// mark wear
bd->wear[block] += 1;
}
}
// erase
int err = lfs_testbd_rawerase(bd, block);
if (err) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
return err;
}
// lose power?
if (bd->power_cycles > 0) {
bd->power_cycles -= 1;
if (bd->power_cycles == 0) {
// sync to make sure we persist the last changes
assert(lfs_testbd_rawsync(bd) == 0);
// simulate power loss
exit(33);
}
}
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
return 0;
}
int lfs_testbd_sync(lfs_testbd_t *bd) {
LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)bd);
int err = lfs_testbd_rawsync(bd);
LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err);
return err;
}
/// simulated wear operations ///
lfs_testbd_swear_t lfs_testbd_getwear(lfs_testbd_t *bd,
lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)bd, block);
// check if block is valid
LFS_ASSERT(bd->cfg.erase_cycles);
LFS_ASSERT(block < bd->cfg.erase_count);
LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]);
return bd->wear[block];
}
int lfs_testbd_setwear(lfs_testbd_t *bd,
lfs_block_t block, lfs_testbd_wear_t wear) {
LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)bd, block);
// check if block is valid
LFS_ASSERT(bd->cfg.erase_cycles);
LFS_ASSERT(block < bd->cfg.erase_count);
bd->wear[block] = wear;
LFS_TESTBD_TRACE("lfs_testbd_setwear -> %d", 0);
return 0;
}

View File

@@ -5,25 +5,23 @@
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_TESTBD_H
#define LFS2_TESTBD_H
#ifndef LFS_TESTBD_H
#define LFS_TESTBD_H
#include "lfs2.h"
#include "lfs2_util.h"
#include "bd/lfs2_rambd.h"
#include "bd/lfs2_filebd.h"
#include "lfs.h"
#include "bd/lfs_rambd.h"
#include "bd/lfs_filebd.h"
#ifdef __cplusplus
extern "C"
{
extern "C" {
#endif
// Block device specific tracing
#ifdef LFS2_TESTBD_YES_TRACE
#define LFS2_TESTBD_TRACE(...) LFS2_TRACE(__VA_ARGS__)
#ifdef LFS_TESTBD_YES_TRACE
#define LFS_TESTBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#else
#define LFS2_TESTBD_TRACE(...)
#define LFS_TESTBD_TRACE(...)
#endif
// Mode determining how "bad blocks" behave during testing. This simulates
@@ -32,20 +30,34 @@ extern "C"
//
// Not that read-noop is not allowed. Read _must_ return a consistent (but
// may be arbitrary) value on every read.
enum lfs2_testbd_badblock_behavior {
LFS2_TESTBD_BADBLOCK_PROGERROR,
LFS2_TESTBD_BADBLOCK_ERASEERROR,
LFS2_TESTBD_BADBLOCK_READERROR,
LFS2_TESTBD_BADBLOCK_PROGNOOP,
LFS2_TESTBD_BADBLOCK_ERASENOOP,
enum lfs_testbd_badblock_behavior {
LFS_TESTBD_BADBLOCK_PROGERROR,
LFS_TESTBD_BADBLOCK_ERASEERROR,
LFS_TESTBD_BADBLOCK_READERROR,
LFS_TESTBD_BADBLOCK_PROGNOOP,
LFS_TESTBD_BADBLOCK_ERASENOOP,
};
// Type for measuring wear
typedef uint32_t lfs2_testbd_wear_t;
typedef int32_t lfs2_testbd_swear_t;
typedef uint32_t lfs_testbd_wear_t;
typedef int32_t lfs_testbd_swear_t;
// testbd config, this is required for testing
struct lfs2_testbd_config {
struct lfs_testbd_cfg {
// Minimum size of block read. All read operations must be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of block program. All program operations must be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block.
lfs_size_t erase_size;
// Number of erasable blocks on the device.
lfs_size_t erase_count;
// 8-bit erase value to use for simulating erases. -1 does not simulate
// erases, which can speed up testing by avoiding all the extra block-device
// operations to store the erase value.
@@ -70,72 +82,65 @@ struct lfs2_testbd_config {
};
// testbd state
typedef struct lfs2_testbd {
typedef struct lfs_testbd {
union {
struct {
lfs2_filebd_t bd;
struct lfs2_filebd_config cfg;
} file;
struct {
lfs2_rambd_t bd;
struct lfs2_rambd_config cfg;
} ram;
} u;
lfs_filebd_t filebd;
lfs_rambd_t rambd;
} impl;
bool persist;
uint32_t power_cycles;
lfs2_testbd_wear_t *wear;
lfs_testbd_wear_t *wear;
const struct lfs2_testbd_config *cfg;
} lfs2_testbd_t;
struct lfs_testbd_cfg cfg;
} lfs_testbd_t;
/// Block device API ///
// Create a test block device using the geometry in lfs2_config
// Create a test block device using the geometry in lfs_cfg
//
// Note that filebd is used if a path is provided, if path is NULL
// testbd will use rambd which can be much faster.
int lfs2_testbd_create(const struct lfs2_config *cfg, const char *path);
int lfs2_testbd_createcfg(const struct lfs2_config *cfg, const char *path,
const struct lfs2_testbd_config *bdcfg);
int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path,
const struct lfs_testbd_cfg *cfg);
// Clean up memory associated with block device
int lfs2_testbd_destroy(const struct lfs2_config *cfg);
int lfs_testbd_destroy(lfs_testbd_t *bd);
// Read a block
int lfs2_testbd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
int lfs_testbd_read(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs2_testbd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
int lfs_testbd_prog(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs2_testbd_erase(const struct lfs2_config *cfg, lfs2_block_t block);
int lfs_testbd_erase(lfs_testbd_t *bd, lfs_block_t block);
// Sync the block device
int lfs2_testbd_sync(const struct lfs2_config *cfg);
int lfs_testbd_sync(lfs_testbd_t *bd);
/// Additional extended API for driving test features ///
// Get simulated wear on a given block
lfs2_testbd_swear_t lfs2_testbd_getwear(const struct lfs2_config *cfg,
lfs2_block_t block);
lfs_testbd_swear_t lfs_testbd_getwear(lfs_testbd_t *bd,
lfs_block_t block);
// Manually set simulated wear on a given block
int lfs2_testbd_setwear(const struct lfs2_config *cfg,
lfs2_block_t block, lfs2_testbd_wear_t wear);
int lfs_testbd_setwear(lfs_testbd_t *bd,
lfs_block_t block, lfs_testbd_wear_t wear);
#ifdef __cplusplus
} /* extern "C" */
}
#endif
#endif

5069
lfs.c Normal file

File diff suppressed because it is too large Load Diff

736
lfs.h Normal file
View File

@@ -0,0 +1,736 @@
/*
* The little filesystem
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_H
#define LFS_H
#include "lfs_util.h"
#ifdef __cplusplus
extern "C" {
#endif
/// Version info ///
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00020002
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
// Version of On-disk data structures
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_DISK_VERSION 0x00020000
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
/// Definitions ///
// Type definitions
typedef uint32_t lfs_size_t;
typedef uint32_t lfs_off_t;
typedef int32_t lfs_ssize_t;
typedef int32_t lfs_soff_t;
typedef uint32_t lfs_block_t;
// Maximum name size in bytes, may be redefined to reduce the size of the
// info struct. Limited to <= 1022. Stored in superblock and must be
// respected by other littlefs drivers.
#ifndef LFS_NAME_MAX
#define LFS_NAME_MAX 255
#endif
// Maximum size of a file in bytes, may be redefined to limit to support other
// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return
// incorrect values due to using signed integers. Stored in superblock and
// must be respected by other littlefs drivers.
#ifndef LFS_FILE_MAX
#define LFS_FILE_MAX 2147483647
#endif
// Maximum size of custom attributes in bytes, may be redefined, but there is
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022.
#ifndef LFS_ATTR_MAX
#define LFS_ATTR_MAX 1022
#endif
// File types
enum lfs_type {
// file types
LFS_TYPE_REG = 0x001,
LFS_TYPE_DIR = 0x002,
// internally used types
LFS_TYPE_SPLICE = 0x400,
LFS_TYPE_NAME = 0x000,
LFS_TYPE_STRUCT = 0x200,
LFS_TYPE_USERATTR = 0x300,
LFS_TYPE_FROM = 0x100,
LFS_TYPE_TAIL = 0x600,
LFS_TYPE_GLOBALS = 0x700,
LFS_TYPE_CRC = 0x500,
// internally used type specializations
LFS_TYPE_CREATE = 0x401,
LFS_TYPE_DELETE = 0x4ff,
LFS_TYPE_SUPERBLOCK = 0x0ff,
LFS_TYPE_DIRSTRUCT = 0x200,
LFS_TYPE_CTZSTRUCT = 0x202,
LFS_TYPE_INLINESTRUCT = 0x201,
LFS_TYPE_SOFTTAIL = 0x600,
LFS_TYPE_HARDTAIL = 0x601,
LFS_TYPE_MOVESTATE = 0x7ff,
// internal chip sources
LFS_FROM_NOOP = 0x000,
LFS_FROM_MOVE = 0x101,
LFS_FROM_USERATTRS = 0x102,
};
// File open flags
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
// internally used flags
LFS_F_DIRTY = 0x010000, // File does not match storage
LFS_F_WRITING = 0x020000, // File has been written since last flush
LFS_F_READING = 0x040000, // File has been read since last flush
LFS_F_ERRED = 0x080000, // An error occured during write
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
LFS_F_OPENED = 0x200000, // File has been opened
};
// File seek flags
enum lfs_whence_flags {
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
};
#if !defined(LFS_STATICCFG)
// Configuration provided during initialization of the littlefs
struct lfs_cfg {
// Opaque user provided context that can be used to pass
// information to the block device operations
void *ctx;
// Read a region in a block. Negative error codes are propogated
// to the user.
int (*read)(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*prog)(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*erase)(void *ctx, lfs_block_t block);
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.
int (*sync)(void *ctx);
// Minimum size of a block read. All read operations will be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of a block program. All program operations will be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, non-inlined files
// take up at minimum one block. Must be a multiple of the read
// and program sizes.
lfs_size_t block_size;
// Number of erasable blocks on the device.
lfs_size_t block_count;
// Number of erase cycles before littlefs evicts metadata logs and moves
// the metadata to another block. Suggested values are in the
// range 100-1000, with large values having better performance at the cost
// of less consistent wear distribution.
//
// Set to -1 to disable block-level wear-leveling.
int32_t block_cycles;
// Size of block caches. Each cache buffers a portion of a block in RAM.
// The littlefs needs a read cache, a program cache, and one additional
// cache per file. Larger caches can improve performance by storing more
// data and reducing the number of disk accesses. Must be a multiple of
// the read and program sizes, and a factor of the block size.
lfs_size_t cache_size;
// Size of the lookahead buffer in bytes. A larger lookahead buffer
// increases the number of blocks found during an allocation pass. The
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
// can track 8 blocks. Must be a multiple of 8.
lfs_size_t lookahead_size;
// Optional statically allocated read buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
void *read_buffer;
// Optional statically allocated program buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
void *prog_buffer;
// Optional statically allocated lookahead buffer. Must be lookahead_size
// and aligned to a 32-bit boundary. By default lfs_malloc is used to
// allocate this buffer.
void *lookahead_buffer;
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers.
lfs_size_t name_max;
// Optional upper limit on files in bytes. No downside for larger files
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
// in superblock and must be respected by other littlefs drivers.
lfs_size_t file_max;
// Optional upper limit on custom attributes in bytes. No downside for
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
// LFS_ATTR_MAX when zero.
lfs_size_t attr_max;
};
#else
// Static configuration if LFS_STATICCFG is defined, there are defaults
// for some of these, but some are required. For full documentation, see
// the lfs_cfg struct above.
// Block device operations
int lfs_read(lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
int lfs_prog(lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
int lfs_erase(lfs_block_t block);
int lfs_sync(void);
// Required configuration
#ifndef LFS_READ_SIZE
#error "LFS_STATICCFG requires LFS_READ_SIZE"
#endif
#ifndef LFS_PROG_SIZE
#error "LFS_STATICCFG requires LFS_PROG_SIZE"
#endif
#ifndef LFS_BLOCK_SIZE
#error "LFS_STATICCFG requires LFS_BLOCK_SIZE"
#endif
#ifndef LFS_BLOCK_COUNT
#error "LFS_STATICCFG requires LFS_BLOCK_COUNT"
#endif
#ifndef LFS_BLOCK_CYCLES
#error "LFS_STATICCFG requires LFS_BLOCK_CYCLES"
#endif
#ifndef LFS_CACHE_SIZE
#error "LFS_STATICCFG requires LFS_CACHE_SIZE"
#endif
#ifndef LFS_LOOKAHEAD_SIZE
#error "LFS_STATICCFG requires LFS_LOOKAHEAD_SIZE"
#endif
// Optional configuration
#ifndef LFS_READ_BUFFER
#define LFS_READ_BUFFER NULL
#endif
#ifndef LFS_PROG_BUFFER
#define LFS_PROG_BUFFER NULL
#endif
#ifndef LFS_LOOKAHEAD_BUFFER
#define LFS_LOOKAHEAD_BUFFER NULL
#endif
#endif
#if !defined(LFS_FILE_STATICCFG)
// Optional configuration provided during lfs_file_opencfg
struct lfs_file_cfg {
// Optional statically allocated file buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
void *buffer;
// Optional list of custom attributes related to the file. If the file
// is opened with read access, these attributes will be read from disk
// during the open call. If the file is opened with write access, the
// attributes will be written to disk every file sync or close. This
// write occurs atomically with update to the file's contents.
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller
// than the buffer, it will be padded with zeros. If the stored attribute
// is larger, then it will be silently truncated. If the attribute is not
// found, it will be created implicitly.
struct lfs_attr *attrs;
// Number of custom attributes in the list
lfs_size_t attr_count;
};
#else
// Static configuration if LFS_FILE_STATICCFG is defined. For full
// documentation, see the lfs_file_cfg struct above.
#ifndef LFS_FILE_BUFFER
#define LFS_FILE_BUFFER NULL
#endif
#ifndef LFS_FILE_ATTRS
#define LFS_FILE_ATTRS ((struct lfs_attr*)NULL)
#endif
#ifndef LFS_FILE_ATTR_COUNT
#define LFS_FILE_ATTR_COUNT 0
#endif
#endif
// File info structure
struct lfs_info {
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
uint8_t type;
// Size of the file, only valid for REG files. Limited to 32-bits.
lfs_size_t size;
// Name of the file stored as a null-terminated string. Limited to
// LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to
// reduce RAM. LFS_NAME_MAX is stored in superblock and must be
// respected by other littlefs drivers.
char name[LFS_NAME_MAX+1];
};
// Custom attribute structure, used to describe custom attributes
// committed atomically during file writes.
struct lfs_attr {
// 8-bit type of attribute, provided by user and used to
// identify the attribute
uint8_t type;
// Pointer to buffer containing the attribute
void *buffer;
// Size of attribute in bytes, limited to LFS_ATTR_MAX
lfs_size_t size;
};
/// internal littlefs data structures ///
typedef struct lfs_cache {
lfs_block_t block;
lfs_off_t off;
lfs_size_t size;
uint8_t *buffer;
} lfs_cache_t;
typedef struct lfs_mdir {
lfs_block_t pair[2];
uint32_t rev;
lfs_off_t off;
uint32_t etag;
uint16_t count;
bool erased;
bool split;
lfs_block_t tail[2];
} lfs_mdir_t;
// littlefs directory type
typedef struct lfs_dir {
struct lfs_dir *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
lfs_off_t pos;
lfs_block_t head[2];
} lfs_dir_t;
// littlefs file type
typedef struct lfs_file {
struct lfs_file *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
struct lfs_ctz {
lfs_block_t head;
lfs_size_t size;
} ctz;
uint32_t flags;
lfs_off_t pos;
lfs_block_t block;
lfs_off_t off;
lfs_cache_t cache;
#ifndef LFS_FILE_STATICCFG
struct lfs_file_cfg cfg;
#endif
} lfs_file_t;
typedef struct lfs_superblock {
uint32_t version;
lfs_size_t block_size;
lfs_size_t block_count;
lfs_size_t name_max;
lfs_size_t file_max;
lfs_size_t attr_max;
} lfs_superblock_t;
typedef struct lfs_gstate {
uint32_t tag;
lfs_block_t pair[2];
} lfs_gstate_t;
// The littlefs filesystem type
typedef struct lfs {
lfs_cache_t rcache;
lfs_cache_t pcache;
lfs_block_t root[2];
struct lfs_mlist {
struct lfs_mlist *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
} *mlist;
uint32_t seed;
lfs_gstate_t gstate;
lfs_gstate_t gdisk;
lfs_gstate_t gdelta;
struct lfs_free {
lfs_block_t off;
lfs_block_t size;
lfs_block_t i;
lfs_block_t ack;
uint32_t *buffer;
} free;
#ifndef LFS_STATICCFG
struct lfs_cfg cfg;
#endif
#ifdef LFS_MIGRATE
struct lfs1 *lfs1;
#endif
} lfs_t;
/// Filesystem functions ///
#if defined(LFS_STATICCFG)
// Format a block device with littlefs
//
// Requires a littlefs object. This clobbers the littlefs object, and does
// not leave the filesystem mounted.
//
// Returns a negative error code on failure.
int lfs_format(lfs_t *lfs);
#endif
#if !defined(LFS_STATICCFG)
// Format a block device with littlefs with per-filesystem configuration
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_formatcfg(lfs_t *lfs, const struct lfs_cfg *config);
#endif
#if defined(LFS_STATICCFG)
// Mounts littlefs
//
// Requires a littlefs object and static configuration.
//
// Returns a negative error code on failure.
int lfs_mount(lfs_t *lfs);
#endif
#if !defined(LFS_STATICCFG)
// Mounts a littlefs with per-filesystem configuration
//
// Requires a littlefs object and config struct. Multiple filesystems
// may be mounted simultaneously with multiple littlefs objects. Both
// lfs and config must be allocated while mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_mountcfg(lfs_t *lfs, const struct lfs_cfg *config);
#endif
// Unmounts a littlefs
//
// Does nothing besides releasing any allocated resources.
// Returns a negative error code on failure.
int lfs_unmount(lfs_t *lfs);
/// General operations ///
// Removes a file or directory
//
// If removing a directory, the directory must be empty.
// Returns a negative error code on failure.
int lfs_remove(lfs_t *lfs, const char *path);
// Rename or move a file or directory
//
// If the destination exists, it must match the source in type.
// If the destination is a directory, the directory must be empty.
//
// Returns a negative error code on failure.
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
// Find info about a file or directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a negative error code on failure.
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
// Get a custom attribute
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
// the buffer, it will be padded with zeros. If the stored attribute is larger,
// then it will be silently truncated. If no attribute is found, the error
// LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
//
// Returns the size of the attribute, or a negative error code on failure.
// Note, the returned size is the size of the attribute on disk, irrespective
// of the size of the buffer. This can be used to dynamically allocate a buffer
// or check for existance.
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
uint8_t type, void *buffer, lfs_size_t size);
// Set custom attributes
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
// implicitly created.
//
// Returns a negative error code on failure.
int lfs_setattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size);
// Removes a custom attribute
//
// If an attribute is not found, nothing happens.
//
// Returns a negative error code on failure.
int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);
/// File operations ///
// Open a file
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs_open_flags that are bitwise-ored together.
//
// Returns a negative error code on failure.
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags);
#if !defined(LFS_FILE_STATICCFG)
// Open a file with per-file configuration
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs_open_flags that are bitwise-ored together.
//
// The config struct provides additional config options per file as described
// above. The config struct must be allocated while the file is open, and the
// config struct must be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags,
const struct lfs_file_cfg *config);
#endif
// Close a file
//
// Any pending writes are written out to storage as though
// sync had been called and releases any allocated resources.
//
// Returns a negative error code on failure.
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
// Synchronize a file on storage
//
// Any pending writes are written out to storage.
// Returns a negative error code on failure.
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
// Read data from file
//
// Takes a buffer and size indicating where to store the read data.
// Returns the number of bytes read, or a negative error code on failure.
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
void *buffer, lfs_size_t size);
// Write data to file
//
// Takes a buffer and size indicating the data to write. The file will not
// actually be updated on the storage until either sync or close is called.
//
// Returns the number of bytes written, or a negative error code on failure.
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
const void *buffer, lfs_size_t size);
// Change the position of the file
//
// The change in position is determined by the offset and whence flag.
// Returns the new position of the file, or a negative error code on failure.
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
lfs_soff_t off, int whence);
// Truncates the size of the file to the specified size
//
// Returns a negative error code on failure.
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);
// Return the position of the file
//
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
// Returns the position of the file, or a negative error code on failure.
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
// Change the position of the file to the beginning of the file
//
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET)
// Returns a negative error code on failure.
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
// Return the size of the file
//
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
// Returns the size of the file, or a negative error code on failure.
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
/// Directory operations ///
// Create a directory
//
// Returns a negative error code on failure.
int lfs_mkdir(lfs_t *lfs, const char *path);
// Open a directory
//
// Once open a directory can be used with read to iterate over files.
// Returns a negative error code on failure.
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
// Close a directory
//
// Releases any allocated resources.
// Returns a negative error code on failure.
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
// Read an entry in the directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a positive value on success, 0 at the end of directory,
// or a negative error code on failure.
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
// Change the position of the directory
//
// The new off must be a value previous returned from tell and specifies
// an absolute offset in the directory seek.
//
// Returns a negative error code on failure.
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
// Return the position of the directory
//
// The returned offset is only meant to be consumed by seek and may not make
// sense, but does indicate the current position in the directory iteration.
//
// Returns the position of the directory, or a negative error code on failure.
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
// Change the position of the directory to the beginning of the directory
//
// Returns a negative error code on failure.
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
/// Filesystem-level filesystem operations
// Finds the current size of the filesystem
//
// Note: Result is best effort. If files share COW structures, the returned
// size may be larger than the filesystem actually is.
//
// Returns the number of allocated blocks, or a negative error code on failure.
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
// Traverse through all blocks in use by the filesystem
//
// The provided callback will be called with each block address that is
// currently in use by the filesystem. This can be used to determine which
// blocks are in use or how much of the storage is available.
//
// Returns a negative error code on failure.
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
#if defined(LFS_MIGRATE) && defined(LFS_STATICCFG)
// Attempts to migrate a previous version of littlefs
//
// Behaves similarly to the lfs_format function. Attempts to mount
// the previous version of littlefs and update the filesystem so it can be
// mounted with the current version of littlefs.
//
// Requires a littlefs object. This clobbers the littlefs object, and does
// not leave the filesystem mounted.
//
// Returns a negative error code on failure.
int lfs_migrate(lfs_t *lfs, const struct lfs_cfg *cfg);
#endif
#if defined(LFS_MIGRATE) && !defined(LFS_STATICCFG)
// Attempts to migrate a previous version of littlefs with per-filesystem
// configuration
//
// Behaves similarly to the lfs_format function. Attempts to mount
// the previous version of littlefs and update the filesystem so it can be
// mounted with the current version of littlefs.
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_migratecfg(lfs_t *lfs, const struct lfs_cfg *cfg);
#endif
#ifdef __cplusplus
}
#endif
#endif

5438
lfs2.c

File diff suppressed because it is too large Load Diff

695
lfs2.h
View File

@@ -1,695 +0,0 @@
/*
* The little filesystem
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_H
#define LFS2_H
#include <stdint.h>
#include <stdbool.h>
#include "lfs2_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
/// Version info ///
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS2_VERSION 0x00020004
#define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16))
#define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0))
// Version of On-disk data structures
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS2_DISK_VERSION 0x00020000
#define LFS2_DISK_VERSION_MAJOR (0xffff & (LFS2_DISK_VERSION >> 16))
#define LFS2_DISK_VERSION_MINOR (0xffff & (LFS2_DISK_VERSION >> 0))
/// Definitions ///
// Type definitions
typedef uint32_t lfs2_size_t;
typedef uint32_t lfs2_off_t;
typedef int32_t lfs2_ssize_t;
typedef int32_t lfs2_soff_t;
typedef uint32_t lfs2_block_t;
// Maximum name size in bytes, may be redefined to reduce the size of the
// info struct. Limited to <= 1022. Stored in superblock and must be
// respected by other littlefs drivers.
#ifndef LFS2_NAME_MAX
#define LFS2_NAME_MAX 255
#endif
// Maximum size of a file in bytes, may be redefined to limit to support other
// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
// functions lfs2_file_seek, lfs2_file_size, and lfs2_file_tell will return
// incorrect values due to using signed integers. Stored in superblock and
// must be respected by other littlefs drivers.
#ifndef LFS2_FILE_MAX
#define LFS2_FILE_MAX 2147483647
#endif
// Maximum size of custom attributes in bytes, may be redefined, but there is
// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022.
#ifndef LFS2_ATTR_MAX
#define LFS2_ATTR_MAX 1022
#endif
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs2_error {
LFS2_ERR_OK = 0, // No error
LFS2_ERR_IO = -5, // Error during device operation
LFS2_ERR_CORRUPT = -84, // Corrupted
LFS2_ERR_NOENT = -2, // No directory entry
LFS2_ERR_EXIST = -17, // Entry already exists
LFS2_ERR_NOTDIR = -20, // Entry is not a dir
LFS2_ERR_ISDIR = -21, // Entry is a dir
LFS2_ERR_NOTEMPTY = -39, // Dir is not empty
LFS2_ERR_BADF = -9, // Bad file number
LFS2_ERR_FBIG = -27, // File too large
LFS2_ERR_INVAL = -22, // Invalid parameter
LFS2_ERR_NOSPC = -28, // No space left on device
LFS2_ERR_NOMEM = -12, // No more memory available
LFS2_ERR_NOATTR = -61, // No data/attr available
LFS2_ERR_NAMETOOLONG = -36, // File name too long
};
// File types
enum lfs2_type {
// file types
LFS2_TYPE_REG = 0x001,
LFS2_TYPE_DIR = 0x002,
// internally used types
LFS2_TYPE_SPLICE = 0x400,
LFS2_TYPE_NAME = 0x000,
LFS2_TYPE_STRUCT = 0x200,
LFS2_TYPE_USERATTR = 0x300,
LFS2_TYPE_FROM = 0x100,
LFS2_TYPE_TAIL = 0x600,
LFS2_TYPE_GLOBALS = 0x700,
LFS2_TYPE_CRC = 0x500,
// internally used type specializations
LFS2_TYPE_CREATE = 0x401,
LFS2_TYPE_DELETE = 0x4ff,
LFS2_TYPE_SUPERBLOCK = 0x0ff,
LFS2_TYPE_DIRSTRUCT = 0x200,
LFS2_TYPE_CTZSTRUCT = 0x202,
LFS2_TYPE_INLINESTRUCT = 0x201,
LFS2_TYPE_SOFTTAIL = 0x600,
LFS2_TYPE_HARDTAIL = 0x601,
LFS2_TYPE_MOVESTATE = 0x7ff,
// internal chip sources
LFS2_FROM_NOOP = 0x000,
LFS2_FROM_MOVE = 0x101,
LFS2_FROM_USERATTRS = 0x102,
};
// File open flags
enum lfs2_open_flags {
// open flags
LFS2_O_RDONLY = 1, // Open a file as read only
#ifndef LFS2_READONLY
LFS2_O_WRONLY = 2, // Open a file as write only
LFS2_O_RDWR = 3, // Open a file as read and write
LFS2_O_CREAT = 0x0100, // Create a file if it does not exist
LFS2_O_EXCL = 0x0200, // Fail if a file already exists
LFS2_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS2_O_APPEND = 0x0800, // Move to end of file on every write
#endif
// internally used flags
#ifndef LFS2_READONLY
LFS2_F_DIRTY = 0x010000, // File does not match storage
LFS2_F_WRITING = 0x020000, // File has been written since last flush
#endif
LFS2_F_READING = 0x040000, // File has been read since last flush
#ifndef LFS2_READONLY
LFS2_F_ERRED = 0x080000, // An error occurred during write
#endif
LFS2_F_INLINE = 0x100000, // Currently inlined in directory entry
};
// File seek flags
enum lfs2_whence_flags {
LFS2_SEEK_SET = 0, // Seek relative to an absolute position
LFS2_SEEK_CUR = 1, // Seek relative to the current file position
LFS2_SEEK_END = 2, // Seek relative to the end of the file
};
// Configuration provided during initialization of the littlefs
struct lfs2_config {
// Opaque user provided context that can be used to pass
// information to the block device operations
void *context;
// Read a region in a block. Negative error codes are propagated
// to the user.
int (*read)(const struct lfs2_config *c, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propagated to the user.
// May return LFS2_ERR_CORRUPT if the block should be considered bad.
int (*prog)(const struct lfs2_config *c, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propagated to the user.
// May return LFS2_ERR_CORRUPT if the block should be considered bad.
int (*erase)(const struct lfs2_config *c, lfs2_block_t block);
// Sync the state of the underlying block device. Negative error codes
// are propagated to the user.
int (*sync)(const struct lfs2_config *c);
#ifdef LFS2_THREADSAFE
// Lock the underlying block device. Negative error codes
// are propagated to the user.
int (*lock)(const struct lfs2_config *c);
// Unlock the underlying block device. Negative error codes
// are propagated to the user.
int (*unlock)(const struct lfs2_config *c);
#endif
// Minimum size of a block read in bytes. All read operations will be a
// multiple of this value.
lfs2_size_t read_size;
// Minimum size of a block program in bytes. All program operations will be
// a multiple of this value.
lfs2_size_t prog_size;
// Size of an erasable block in bytes. This does not impact ram consumption
// and may be larger than the physical erase size. However, non-inlined
// files take up at minimum one block. Must be a multiple of the read and
// program sizes.
lfs2_size_t block_size;
// Number of erasable blocks on the device.
lfs2_size_t block_count;
// Number of erase cycles before littlefs evicts metadata logs and moves
// the metadata to another block. Suggested values are in the
// range 100-1000, with large values having better performance at the cost
// of less consistent wear distribution.
//
// Set to -1 to disable block-level wear-leveling.
int32_t block_cycles;
// Size of block caches in bytes. Each cache buffers a portion of a block in
// RAM. The littlefs needs a read cache, a program cache, and one additional
// cache per file. Larger caches can improve performance by storing more
// data and reducing the number of disk accesses. Must be a multiple of the
// read and program sizes, and a factor of the block size.
lfs2_size_t cache_size;
// Size of the lookahead buffer in bytes. A larger lookahead buffer
// increases the number of blocks found during an allocation pass. The
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
// can track 8 blocks. Must be a multiple of 8.
lfs2_size_t lookahead_size;
// Optional statically allocated read buffer. Must be cache_size.
// By default lfs2_malloc is used to allocate this buffer.
void *read_buffer;
// Optional statically allocated program buffer. Must be cache_size.
// By default lfs2_malloc is used to allocate this buffer.
void *prog_buffer;
// Optional statically allocated lookahead buffer. Must be lookahead_size
// and aligned to a 32-bit boundary. By default lfs2_malloc is used to
// allocate this buffer.
void *lookahead_buffer;
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers.
lfs2_size_t name_max;
// Optional upper limit on files in bytes. No downside for larger files
// but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX when zero. Stored
// in superblock and must be respected by other littlefs drivers.
lfs2_size_t file_max;
// Optional upper limit on custom attributes in bytes. No downside for
// larger attributes size but must be <= LFS2_ATTR_MAX. Defaults to
// LFS2_ATTR_MAX when zero.
lfs2_size_t attr_max;
// Optional upper limit on total space given to metadata pairs in bytes. On
// devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB)
// can help bound the metadata compaction time. Must be <= block_size.
// Defaults to block_size when zero.
lfs2_size_t metadata_max;
};
// File info structure
struct lfs2_info {
// Type of the file, either LFS2_TYPE_REG or LFS2_TYPE_DIR
uint8_t type;
// Size of the file, only valid for REG files. Limited to 32-bits.
lfs2_size_t size;
// Name of the file stored as a null-terminated string. Limited to
// LFS2_NAME_MAX+1, which can be changed by redefining LFS2_NAME_MAX to
// reduce RAM. LFS2_NAME_MAX is stored in superblock and must be
// respected by other littlefs drivers.
char name[LFS2_NAME_MAX+1];
};
// Custom attribute structure, used to describe custom attributes
// committed atomically during file writes.
struct lfs2_attr {
// 8-bit type of attribute, provided by user and used to
// identify the attribute
uint8_t type;
// Pointer to buffer containing the attribute
void *buffer;
// Size of attribute in bytes, limited to LFS2_ATTR_MAX
lfs2_size_t size;
};
// Optional configuration provided during lfs2_file_opencfg
struct lfs2_file_config {
// Optional statically allocated file buffer. Must be cache_size.
// By default lfs2_malloc is used to allocate this buffer.
void *buffer;
// Optional list of custom attributes related to the file. If the file
// is opened with read access, these attributes will be read from disk
// during the open call. If the file is opened with write access, the
// attributes will be written to disk every file sync or close. This
// write occurs atomically with update to the file's contents.
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller
// than the buffer, it will be padded with zeros. If the stored attribute
// is larger, then it will be silently truncated. If the attribute is not
// found, it will be created implicitly.
struct lfs2_attr *attrs;
// Number of custom attributes in the list
lfs2_size_t attr_count;
};
/// internal littlefs data structures ///
typedef struct lfs2_cache {
lfs2_block_t block;
lfs2_off_t off;
lfs2_size_t size;
uint8_t *buffer;
} lfs2_cache_t;
typedef struct lfs2_mdir {
lfs2_block_t pair[2];
uint32_t rev;
lfs2_off_t off;
uint32_t etag;
uint16_t count;
bool erased;
bool split;
lfs2_block_t tail[2];
} lfs2_mdir_t;
// littlefs directory type
typedef struct lfs2_dir {
struct lfs2_dir *next;
uint16_t id;
uint8_t type;
lfs2_mdir_t m;
lfs2_off_t pos;
lfs2_block_t head[2];
} lfs2_dir_t;
// littlefs file type
typedef struct lfs2_file {
struct lfs2_file *next;
uint16_t id;
uint8_t type;
lfs2_mdir_t m;
struct lfs2_ctz {
lfs2_block_t head;
lfs2_size_t size;
} ctz;
uint32_t flags;
lfs2_off_t pos;
lfs2_block_t block;
lfs2_off_t off;
lfs2_cache_t cache;
const struct lfs2_file_config *cfg;
} lfs2_file_t;
typedef struct lfs2_superblock {
uint32_t version;
lfs2_size_t block_size;
lfs2_size_t block_count;
lfs2_size_t name_max;
lfs2_size_t file_max;
lfs2_size_t attr_max;
} lfs2_superblock_t;
typedef struct lfs2_gstate {
uint32_t tag;
lfs2_block_t pair[2];
} lfs2_gstate_t;
// The littlefs filesystem type
typedef struct lfs2 {
lfs2_cache_t rcache;
lfs2_cache_t pcache;
lfs2_block_t root[2];
struct lfs2_mlist {
struct lfs2_mlist *next;
uint16_t id;
uint8_t type;
lfs2_mdir_t m;
} *mlist;
uint32_t seed;
lfs2_gstate_t gstate;
lfs2_gstate_t gdisk;
lfs2_gstate_t gdelta;
struct lfs2_free {
lfs2_block_t off;
lfs2_block_t size;
lfs2_block_t i;
lfs2_block_t ack;
uint32_t *buffer;
} free;
const struct lfs2_config *cfg;
lfs2_size_t name_max;
lfs2_size_t file_max;
lfs2_size_t attr_max;
#ifdef LFS2_MIGRATE
struct lfs21 *lfs21;
#endif
} lfs2_t;
/// Filesystem functions ///
#ifndef LFS2_READONLY
// Format a block device with the littlefs
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *config);
#endif
// Mounts a littlefs
//
// Requires a littlefs object and config struct. Multiple filesystems
// may be mounted simultaneously with multiple littlefs objects. Both
// lfs2 and config must be allocated while mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *config);
// Unmounts a littlefs
//
// Does nothing besides releasing any allocated resources.
// Returns a negative error code on failure.
int lfs2_unmount(lfs2_t *lfs2);
/// General operations ///
#ifndef LFS2_READONLY
// Removes a file or directory
//
// If removing a directory, the directory must be empty.
// Returns a negative error code on failure.
int lfs2_remove(lfs2_t *lfs2, const char *path);
#endif
#ifndef LFS2_READONLY
// Rename or move a file or directory
//
// If the destination exists, it must match the source in type.
// If the destination is a directory, the directory must be empty.
//
// Returns a negative error code on failure.
int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath);
#endif
// Find info about a file or directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a negative error code on failure.
int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info);
// Get a custom attribute
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller than
// the buffer, it will be padded with zeros. If the stored attribute is larger,
// then it will be silently truncated. If no attribute is found, the error
// LFS2_ERR_NOATTR is returned and the buffer is filled with zeros.
//
// Returns the size of the attribute, or a negative error code on failure.
// Note, the returned size is the size of the attribute on disk, irrespective
// of the size of the buffer. This can be used to dynamically allocate a buffer
// or check for existence.
lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path,
uint8_t type, void *buffer, lfs2_size_t size);
#ifndef LFS2_READONLY
// Set custom attributes
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS2_ATTR_MAX bytes. If an attribute is not found, it will be
// implicitly created.
//
// Returns a negative error code on failure.
int lfs2_setattr(lfs2_t *lfs2, const char *path,
uint8_t type, const void *buffer, lfs2_size_t size);
#endif
#ifndef LFS2_READONLY
// Removes a custom attribute
//
// If an attribute is not found, nothing happens.
//
// Returns a negative error code on failure.
int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type);
#endif
/// File operations ///
// Open a file
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs2_open_flags that are bitwise-ored together.
//
// Returns a negative error code on failure.
int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file,
const char *path, int flags);
// Open a file with extra configuration
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs2_open_flags that are bitwise-ored together.
//
// The config struct provides additional config options per file as described
// above. The config struct must be allocated while the file is open, and the
// config struct must be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file,
const char *path, int flags,
const struct lfs2_file_config *config);
// Close a file
//
// Any pending writes are written out to storage as though
// sync had been called and releases any allocated resources.
//
// Returns a negative error code on failure.
int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file);
// Synchronize a file on storage
//
// Any pending writes are written out to storage.
// Returns a negative error code on failure.
int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file);
// Read data from file
//
// Takes a buffer and size indicating where to store the read data.
// Returns the number of bytes read, or a negative error code on failure.
lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file,
void *buffer, lfs2_size_t size);
#ifndef LFS2_READONLY
// Write data to file
//
// Takes a buffer and size indicating the data to write. The file will not
// actually be updated on the storage until either sync or close is called.
//
// Returns the number of bytes written, or a negative error code on failure.
lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file,
const void *buffer, lfs2_size_t size);
#endif
// Change the position of the file
//
// The change in position is determined by the offset and whence flag.
// Returns the new position of the file, or a negative error code on failure.
lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file,
lfs2_soff_t off, int whence);
#ifndef LFS2_READONLY
// Truncates the size of the file to the specified size
//
// Returns a negative error code on failure.
int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size);
#endif
// Return the position of the file
//
// Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_CUR)
// Returns the position of the file, or a negative error code on failure.
lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file);
// Change the position of the file to the beginning of the file
//
// Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_SET)
// Returns a negative error code on failure.
int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file);
// Return the size of the file
//
// Similar to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_END)
// Returns the size of the file, or a negative error code on failure.
lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file);
/// Directory operations ///
#ifndef LFS2_READONLY
// Create a directory
//
// Returns a negative error code on failure.
int lfs2_mkdir(lfs2_t *lfs2, const char *path);
#endif
// Open a directory
//
// Once open a directory can be used with read to iterate over files.
// Returns a negative error code on failure.
int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path);
// Close a directory
//
// Releases any allocated resources.
// Returns a negative error code on failure.
int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir);
// Read an entry in the directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a positive value on success, 0 at the end of directory,
// or a negative error code on failure.
int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info);
// Change the position of the directory
//
// The new off must be a value previous returned from tell and specifies
// an absolute offset in the directory seek.
//
// Returns a negative error code on failure.
int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off);
// Return the position of the directory
//
// The returned offset is only meant to be consumed by seek and may not make
// sense, but does indicate the current position in the directory iteration.
//
// Returns the position of the directory, or a negative error code on failure.
lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir);
// Change the position of the directory to the beginning of the directory
//
// Returns a negative error code on failure.
int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir);
/// Filesystem-level filesystem operations
// Finds the current size of the filesystem
//
// Note: Result is best effort. If files share COW structures, the returned
// size may be larger than the filesystem actually is.
//
// Returns the number of allocated blocks, or a negative error code on failure.
lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2);
// Traverse through all blocks in use by the filesystem
//
// The provided callback will be called with each block address that is
// currently in use by the filesystem. This can be used to determine which
// blocks are in use or how much of the storage is available.
//
// Returns a negative error code on failure.
int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data);
#ifndef LFS2_READONLY
#ifdef LFS2_MIGRATE
// Attempts to migrate a previous version of littlefs
//
// Behaves similarly to the lfs2_format function. Attempts to mount
// the previous version of littlefs and update the filesystem so it can be
// mounted with the current version of littlefs.
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg);
#endif
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -1,17 +1,17 @@
/*
* lfs2 util functions
* lfs util functions
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "lfs2_util.h"
#include "lfs_util.h"
// Only compile if user does not provide custom config
#ifndef LFS2_CONFIG
#ifndef LFS_UTIL
// Software CRC implementation with small lookup table
uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) {
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,

View File

@@ -1,22 +1,23 @@
/*
* lfs2 utility functions
* lfs utility functions
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*
* Can be overridden by users with their own configuration by defining
* LFS_UTIL as a header file (-DLFS_UTIL=my_lfs_util.h)
*
* If LFS_UTIL is defined, none of the default definitions will be
* emitted and must be provided by the user's header file. To start, I would
* suggest copying lfs_util.h and modifying as needed.
*/
#ifndef LFS2_UTIL_H
#define LFS2_UTIL_H
#ifndef LFS_UTIL_H
#define LFS_UTIL_H
// Users can override lfs2_util.h with their own configuration by defining
// LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h).
//
// If LFS2_CONFIG is used, none of the default utils will be emitted and must be
// provided by the config file. To start, I would suggest copying lfs2_util.h
// and modifying as needed.
#ifdef LFS2_CONFIG
#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x)
#define LFS2_STRINGIZE2(x) #x
#include LFS2_STRINGIZE(LFS2_CONFIG)
#ifdef LFS_UTIL
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
#define LFS_STRINGIZE2(x) #x
#include LFS_STRINGIZE(LFS_UTIL)
#else
// System includes
@@ -25,105 +26,116 @@
#include <string.h>
#include <inttypes.h>
#ifndef LFS2_NO_MALLOC
#ifndef LFS_NO_MALLOC
#include <stdlib.h>
#endif
#ifndef LFS2_NO_ASSERT
#ifndef LFS_NO_ASSERT
#include <assert.h>
#endif
#if !defined(LFS2_NO_DEBUG) || \
!defined(LFS2_NO_WARN) || \
!defined(LFS2_NO_ERROR) || \
defined(LFS2_YES_TRACE)
#if !defined(LFS_NO_DEBUG) || \
!defined(LFS_NO_WARN) || \
!defined(LFS_NO_ERROR) || \
defined(LFS_YES_TRACE)
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C"
{
extern "C" {
#endif
// Possible error codes, these are negative to allow valid positive
// return values. May be redefined to system-specific error codes as long
// as they fit in a negative integer.
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -84, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_FBIG = -27, // File too large
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NOATTR = -61, // No data/attr available
LFS_ERR_NAMETOOLONG = -36, // File name too long
};
// Macros, may be replaced by system specific wrappers. Arguments to these
// macros must not have side-effects as the macros can be removed for a smaller
// code footprint
// Logging functions
#ifndef LFS2_TRACE
#ifdef LFS2_YES_TRACE
#define LFS2_TRACE_(fmt, ...) \
#ifdef LFS_YES_TRACE
#define LFS_TRACE_(fmt, ...) \
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS2_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "")
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#else
#define LFS2_TRACE(...)
#endif
#define LFS_TRACE(...)
#endif
#ifndef LFS2_DEBUG
#ifndef LFS2_NO_DEBUG
#define LFS2_DEBUG_(fmt, ...) \
#ifndef LFS_NO_DEBUG
#define LFS_DEBUG_(fmt, ...) \
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS2_DEBUG(...) LFS2_DEBUG_(__VA_ARGS__, "")
#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "")
#else
#define LFS2_DEBUG(...)
#endif
#define LFS_DEBUG(...)
#endif
#ifndef LFS2_WARN
#ifndef LFS2_NO_WARN
#define LFS2_WARN_(fmt, ...) \
#ifndef LFS_NO_WARN
#define LFS_WARN_(fmt, ...) \
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS2_WARN(...) LFS2_WARN_(__VA_ARGS__, "")
#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "")
#else
#define LFS2_WARN(...)
#endif
#define LFS_WARN(...)
#endif
#ifndef LFS2_ERROR
#ifndef LFS2_NO_ERROR
#define LFS2_ERROR_(fmt, ...) \
#ifndef LFS_NO_ERROR
#define LFS_ERROR_(fmt, ...) \
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS2_ERROR(...) LFS2_ERROR_(__VA_ARGS__, "")
#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "")
#else
#define LFS2_ERROR(...)
#endif
#define LFS_ERROR(...)
#endif
// Runtime assertions
#ifndef LFS2_ASSERT
#ifndef LFS2_NO_ASSERT
#define LFS2_ASSERT(test) assert(test)
#ifndef LFS_NO_ASSERT
#define LFS_ASSERT(test) assert(test)
#else
#define LFS2_ASSERT(test)
#endif
#define LFS_ASSERT(test)
#endif
// Builtin functions, these may be replaced by more efficient
// toolchain-specific implementations. LFS2_NO_INTRINSICS falls back to a more
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
// expensive basic C implementation for debugging purposes
// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs2_max(uint32_t a, uint32_t b) {
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
static inline uint32_t lfs2_min(uint32_t a, uint32_t b) {
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
// Align to nearest multiple of a size
static inline uint32_t lfs2_aligndown(uint32_t a, uint32_t alignment) {
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
return a - (a % alignment);
}
static inline uint32_t lfs2_alignup(uint32_t a, uint32_t alignment) {
return lfs2_aligndown(a + alignment-1, alignment);
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return lfs_aligndown(a + alignment-1, alignment);
}
// Find the smallest power of 2 greater than or equal to a
static inline uint32_t lfs2_npw2(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
static inline uint32_t lfs_npw2(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a-1);
#else
uint32_t r = 0;
@@ -138,18 +150,18 @@ static inline uint32_t lfs2_npw2(uint32_t a) {
}
// Count the number of trailing binary zeros in a
// lfs2_ctz(0) may be undefined
static inline uint32_t lfs2_ctz(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && defined(__GNUC__)
// lfs_ctz(0) may be undefined
static inline uint32_t lfs_ctz(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
return __builtin_ctz(a);
#else
return lfs2_npw2((a & -a) + 1) - 1;
return lfs_npw2((a & -a) + 1) - 1;
#endif
}
// Count the number of binary ones in a
static inline uint32_t lfs2_popc(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
static inline uint32_t lfs_popc(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return __builtin_popcount(a);
#else
a = a - ((a >> 1) & 0x55555555);
@@ -160,18 +172,18 @@ static inline uint32_t lfs2_popc(uint32_t a) {
// Find the sequence comparison of a and b, this is the distance
// between a and b ignoring overflow
static inline int lfs2_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b);
static inline int32_t lfs_scmp(uint32_t a, uint32_t b) {
return (int32_t)(uint32_t)(a - b);
}
// Convert between 32-bit little-endian and native order
static inline uint32_t lfs2_fromle32(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && ( \
static inline uint32_t lfs_fromle32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS2_NO_INTRINSICS) && ( \
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
@@ -184,18 +196,18 @@ static inline uint32_t lfs2_fromle32(uint32_t a) {
#endif
}
static inline uint32_t lfs2_tole32(uint32_t a) {
return lfs2_fromle32(a);
static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a);
}
// Convert between 32-bit big-endian and native order
static inline uint32_t lfs2_frombe32(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && ( \
static inline uint32_t lfs_frombe32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return __builtin_bswap32(a);
#elif !defined(LFS2_NO_INTRINSICS) && ( \
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
@@ -208,17 +220,17 @@ static inline uint32_t lfs2_frombe32(uint32_t a) {
#endif
}
static inline uint32_t lfs2_tobe32(uint32_t a) {
return lfs2_frombe32(a);
static inline uint32_t lfs_tobe32(uint32_t a) {
return lfs_frombe32(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size);
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs
// Note, memory must be 64-bit aligned
static inline void *lfs2_malloc(size_t size) {
#ifndef LFS2_NO_MALLOC
static inline void *lfs_malloc(size_t size) {
#ifndef LFS_NO_MALLOC
return malloc(size);
#else
(void)size;
@@ -227,8 +239,8 @@ static inline void *lfs2_malloc(size_t size) {
}
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs2_free(void *p) {
#ifndef LFS2_NO_MALLOC
static inline void lfs_free(void *p) {
#ifndef LFS_NO_MALLOC
free(p);
#else
(void)p;
@@ -237,7 +249,7 @@ static inline void lfs2_free(void *p) {
#ifdef __cplusplus
} /* extern "C" */
}
#endif
#endif

View File

@@ -1,214 +0,0 @@
#!/usr/bin/env python3
#
# Script to find code size at the function level. Basically just a bit wrapper
# around nm with some extra conveniences for comparing builds. Heavily inspired
# by Linux's Bloat-O-Meter.
#
import os
import glob
import itertools as it
import subprocess as sp
import shlex
import re
import csv
import collections as co
OBJ_PATHS = ['*.o', 'bd/*.o']
def collect(paths, **args):
results = co.defaultdict(lambda: 0)
pattern = re.compile(
'^(?P<size>[0-9a-fA-F]+)' +
' (?P<type>[%s])' % re.escape(args['type']) +
' (?P<func>.+?)$')
for path in paths:
# note nm-tool may contain extra args
cmd = args['nm_tool'] + ['--size-sort', path]
if args.get('verbose'):
print(' '.join(shlex.quote(c) for c in cmd))
proc = sp.Popen(cmd,
stdout=sp.PIPE,
stderr=sp.PIPE if not args.get('verbose') else None,
universal_newlines=True)
for line in proc.stdout:
m = pattern.match(line)
if m:
results[(path, m.group('func'))] += int(m.group('size'), 16)
proc.wait()
if proc.returncode != 0:
if not args.get('verbose'):
for line in proc.stderr:
sys.stdout.write(line)
sys.exit(-1)
flat_results = []
for (file, func), size in results.items():
# map to source files
if args.get('build_dir'):
file = re.sub('%s/*' % re.escape(args['build_dir']), '', file)
# discard internal functions
if func.startswith('__'):
continue
# discard .8449 suffixes created by optimizer
func = re.sub('\.[0-9]+', '', func)
flat_results.append((file, func, size))
return flat_results
def main(**args):
# find sizes
if not args.get('use', None):
# find .o files
paths = []
for path in args['obj_paths']:
if os.path.isdir(path):
path = path + '/*.o'
for path in glob.glob(path):
paths.append(path)
if not paths:
print('no .obj files found in %r?' % args['obj_paths'])
sys.exit(-1)
results = collect(paths, **args)
else:
with open(args['use']) as f:
r = csv.DictReader(f)
results = [
( result['file'],
result['function'],
int(result['size']))
for result in r]
total = 0
for _, _, size in results:
total += size
# find previous results?
if args.get('diff'):
with open(args['diff']) as f:
r = csv.DictReader(f)
prev_results = [
( result['file'],
result['function'],
int(result['size']))
for result in r]
prev_total = 0
for _, _, size in prev_results:
prev_total += size
# write results to CSV
if args.get('output'):
with open(args['output'], 'w') as f:
w = csv.writer(f)
w.writerow(['file', 'function', 'size'])
for file, func, size in sorted(results):
w.writerow((file, func, size))
# print results
def dedup_entries(results, by='function'):
entries = co.defaultdict(lambda: 0)
for file, func, size in results:
entry = (file if by == 'file' else func)
entries[entry] += size
return entries
def diff_entries(olds, news):
diff = co.defaultdict(lambda: (0, 0, 0, 0))
for name, new in news.items():
diff[name] = (0, new, new, 1.0)
for name, old in olds.items():
_, new, _, _ = diff[name]
diff[name] = (old, new, new-old, (new-old)/old if old else 1.0)
return diff
def print_header(by=''):
if not args.get('diff'):
print('%-36s %7s' % (by, 'size'))
else:
print('%-36s %7s %7s %7s' % (by, 'old', 'new', 'diff'))
def print_entries(by='function'):
entries = dedup_entries(results, by=by)
if not args.get('diff'):
print_header(by=by)
for name, size in sorted(entries.items()):
print("%-36s %7d" % (name, size))
else:
prev_entries = dedup_entries(prev_results, by=by)
diff = diff_entries(prev_entries, entries)
print_header(by='%s (%d added, %d removed)' % (by,
sum(1 for old, _, _, _ in diff.values() if not old),
sum(1 for _, new, _, _ in diff.values() if not new)))
for name, (old, new, diff, ratio) in sorted(diff.items(),
key=lambda x: (-x[1][3], x)):
if ratio or args.get('all'):
print("%-36s %7s %7s %+7d%s" % (name,
old or "-",
new or "-",
diff,
' (%+.1f%%)' % (100*ratio) if ratio else ''))
def print_totals():
if not args.get('diff'):
print("%-36s %7d" % ('TOTAL', total))
else:
ratio = (total-prev_total)/prev_total if prev_total else 1.0
print("%-36s %7s %7s %+7d%s" % (
'TOTAL',
prev_total if prev_total else '-',
total if total else '-',
total-prev_total,
' (%+.1f%%)' % (100*ratio) if ratio else ''))
if args.get('quiet'):
pass
elif args.get('summary'):
print_header()
print_totals()
elif args.get('files'):
print_entries(by='file')
print_totals()
else:
print_entries(by='function')
print_totals()
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser(
description="Find code size at the function level.")
parser.add_argument('obj_paths', nargs='*', default=OBJ_PATHS,
help="Description of where to find *.o files. May be a directory \
or a list of paths. Defaults to %r." % OBJ_PATHS)
parser.add_argument('-v', '--verbose', action='store_true',
help="Output commands that run behind the scenes.")
parser.add_argument('-o', '--output',
help="Specify CSV file to store results.")
parser.add_argument('-u', '--use',
help="Don't compile and find code sizes, instead use this CSV file.")
parser.add_argument('-d', '--diff',
help="Specify CSV file to diff code size against.")
parser.add_argument('-a', '--all', action='store_true',
help="Show all functions, not just the ones that changed.")
parser.add_argument('--files', action='store_true',
help="Show file-level code sizes. Note this does not include padding! "
"So sizes may differ from other tools.")
parser.add_argument('-s', '--summary', action='store_true',
help="Only show the total code size.")
parser.add_argument('-q', '--quiet', action='store_true',
help="Don't show anything, useful with -o.")
parser.add_argument('--type', default='tTrRdDbB',
help="Type of symbols to report, this uses the same single-character "
"type-names emitted by nm. Defaults to %(default)r.")
parser.add_argument('--nm-tool', default=['nm'], type=lambda x: x.split(),
help="Path to the nm tool to use.")
parser.add_argument('--build-dir',
help="Specify the relative build directory. Used to map object files \
to the correct source files.")
sys.exit(main(**vars(parser.parse_args())))

View File

@@ -1,254 +0,0 @@
#!/usr/bin/env python3
#
# Parse and report coverage info from .info files generated by lcov
#
import os
import glob
import csv
import re
import collections as co
import bisect as b
INFO_PATHS = ['tests/*.toml.info']
def collect(paths, **args):
file = None
funcs = []
lines = co.defaultdict(lambda: 0)
pattern = re.compile(
'^(?P<file>SF:/?(?P<file_name>.*))$'
'|^(?P<func>FN:(?P<func_lineno>[0-9]*),(?P<func_name>.*))$'
'|^(?P<line>DA:(?P<line_lineno>[0-9]*),(?P<line_hits>[0-9]*))$')
for path in paths:
with open(path) as f:
for line in f:
m = pattern.match(line)
if m and m.group('file'):
file = m.group('file_name')
elif m and file and m.group('func'):
funcs.append((file, int(m.group('func_lineno')),
m.group('func_name')))
elif m and file and m.group('line'):
lines[(file, int(m.group('line_lineno')))] += (
int(m.group('line_hits')))
# map line numbers to functions
funcs.sort()
def func_from_lineno(file, lineno):
i = b.bisect(funcs, (file, lineno))
if i and funcs[i-1][0] == file:
return funcs[i-1][2]
else:
return None
# reduce to function info
reduced_funcs = co.defaultdict(lambda: (0, 0))
for (file, line_lineno), line_hits in lines.items():
func = func_from_lineno(file, line_lineno)
if not func:
continue
hits, count = reduced_funcs[(file, func)]
reduced_funcs[(file, func)] = (hits + (line_hits > 0), count + 1)
results = []
for (file, func), (hits, count) in reduced_funcs.items():
# discard internal/testing functions (test_* injected with
# internal testing)
if func.startswith('__') or func.startswith('test_'):
continue
# discard .8449 suffixes created by optimizer
func = re.sub('\.[0-9]+', '', func)
results.append((file, func, hits, count))
return results
def main(**args):
# find coverage
if not args.get('use'):
# find *.info files
paths = []
for path in args['info_paths']:
if os.path.isdir(path):
path = path + '/*.gcov'
for path in glob.glob(path):
paths.append(path)
if not paths:
print('no .info files found in %r?' % args['info_paths'])
sys.exit(-1)
results = collect(paths, **args)
else:
with open(args['use']) as f:
r = csv.DictReader(f)
results = [
( result['file'],
result['function'],
int(result['hits']),
int(result['count']))
for result in r]
total_hits, total_count = 0, 0
for _, _, hits, count in results:
total_hits += hits
total_count += count
# find previous results?
if args.get('diff'):
with open(args['diff']) as f:
r = csv.DictReader(f)
prev_results = [
( result['file'],
result['function'],
int(result['hits']),
int(result['count']))
for result in r]
prev_total_hits, prev_total_count = 0, 0
for _, _, hits, count in prev_results:
prev_total_hits += hits
prev_total_count += count
# write results to CSV
if args.get('output'):
with open(args['output'], 'w') as f:
w = csv.writer(f)
w.writerow(['file', 'function', 'hits', 'count'])
for file, func, hits, count in sorted(results):
w.writerow((file, func, hits, count))
# print results
def dedup_entries(results, by='function'):
entries = co.defaultdict(lambda: (0, 0))
for file, func, hits, count in results:
entry = (file if by == 'file' else func)
entry_hits, entry_count = entries[entry]
entries[entry] = (entry_hits + hits, entry_count + count)
return entries
def diff_entries(olds, news):
diff = co.defaultdict(lambda: (0, 0, 0, 0, 0, 0, 0))
for name, (new_hits, new_count) in news.items():
diff[name] = (
0, 0,
new_hits, new_count,
new_hits, new_count,
(new_hits/new_count if new_count else 1.0) - 1.0)
for name, (old_hits, old_count) in olds.items():
_, _, new_hits, new_count, _, _, _ = diff[name]
diff[name] = (
old_hits, old_count,
new_hits, new_count,
new_hits-old_hits, new_count-old_count,
((new_hits/new_count if new_count else 1.0)
- (old_hits/old_count if old_count else 1.0)))
return diff
def print_header(by=''):
if not args.get('diff'):
print('%-36s %19s' % (by, 'hits/line'))
else:
print('%-36s %19s %19s %11s' % (by, 'old', 'new', 'diff'))
def print_entries(by='function'):
entries = dedup_entries(results, by=by)
if not args.get('diff'):
print_header(by=by)
for name, (hits, count) in sorted(entries.items()):
print("%-36s %11s %7s" % (name,
'%d/%d' % (hits, count)
if count else '-',
'%.1f%%' % (100*hits/count)
if count else '-'))
else:
prev_entries = dedup_entries(prev_results, by=by)
diff = diff_entries(prev_entries, entries)
print_header(by='%s (%d added, %d removed)' % (by,
sum(1 for _, old, _, _, _, _, _ in diff.values() if not old),
sum(1 for _, _, _, new, _, _, _ in diff.values() if not new)))
for name, (
old_hits, old_count,
new_hits, new_count,
diff_hits, diff_count, ratio) in sorted(diff.items(),
key=lambda x: (-x[1][6], x)):
if ratio or args.get('all'):
print("%-36s %11s %7s %11s %7s %11s%s" % (name,
'%d/%d' % (old_hits, old_count)
if old_count else '-',
'%.1f%%' % (100*old_hits/old_count)
if old_count else '-',
'%d/%d' % (new_hits, new_count)
if new_count else '-',
'%.1f%%' % (100*new_hits/new_count)
if new_count else '-',
'%+d/%+d' % (diff_hits, diff_count),
' (%+.1f%%)' % (100*ratio) if ratio else ''))
def print_totals():
if not args.get('diff'):
print("%-36s %11s %7s" % ('TOTAL',
'%d/%d' % (total_hits, total_count)
if total_count else '-',
'%.1f%%' % (100*total_hits/total_count)
if total_count else '-'))
else:
ratio = ((total_hits/total_count
if total_count else 1.0)
- (prev_total_hits/prev_total_count
if prev_total_count else 1.0))
print("%-36s %11s %7s %11s %7s %11s%s" % ('TOTAL',
'%d/%d' % (prev_total_hits, prev_total_count)
if prev_total_count else '-',
'%.1f%%' % (100*prev_total_hits/prev_total_count)
if prev_total_count else '-',
'%d/%d' % (total_hits, total_count)
if total_count else '-',
'%.1f%%' % (100*total_hits/total_count)
if total_count else '-',
'%+d/%+d' % (total_hits-prev_total_hits,
total_count-prev_total_count),
' (%+.1f%%)' % (100*ratio) if ratio else ''))
if args.get('quiet'):
pass
elif args.get('summary'):
print_header()
print_totals()
elif args.get('files'):
print_entries(by='file')
print_totals()
else:
print_entries(by='function')
print_totals()
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser(
description="Parse and report coverage info from .info files \
generated by lcov")
parser.add_argument('info_paths', nargs='*', default=INFO_PATHS,
help="Description of where to find *.info files. May be a directory \
or list of paths. *.info files will be merged to show the total \
coverage. Defaults to %r." % INFO_PATHS)
parser.add_argument('-v', '--verbose', action='store_true',
help="Output commands that run behind the scenes.")
parser.add_argument('-o', '--output',
help="Specify CSV file to store results.")
parser.add_argument('-u', '--use',
help="Don't do any work, instead use this CSV file.")
parser.add_argument('-d', '--diff',
help="Specify CSV file to diff code size against.")
parser.add_argument('-a', '--all', action='store_true',
help="Show all functions, not just the ones that changed.")
parser.add_argument('--files', action='store_true',
help="Show file-level coverage.")
parser.add_argument('-s', '--summary', action='store_true',
help="Only show the total coverage.")
parser.add_argument('-q', '--quiet', action='store_true',
help="Don't show anything, useful with -o.")
sys.exit(main(**vars(parser.parse_args())))

View File

@@ -3,8 +3,8 @@
import re
import sys
PATTERN = ['LFS2_ASSERT', 'assert']
PREFIX = 'LFS2'
PATTERN = ['LFS_ASSERT', 'assert']
PREFIX = 'LFS'
MAXWIDTH = 16
ASSERT = "__{PREFIX}_ASSERT_{TYPE}_{COMP}"

View File

@@ -5,7 +5,7 @@
# conflict at compile time.
#
# example:
# $ ./scripts/prefix.py lfs22
# $ ./scripts/prefix.py lfs2
import os
import os.path
@@ -16,7 +16,7 @@ import tempfile
import shutil
import subprocess
DEFAULT_PREFIX = "lfs2"
DEFAULT_PREFIX = "lfs"
def subn(from_prefix, to_prefix, name):
name, count1 = re.subn('\\b'+from_prefix, to_prefix, name)

View File

@@ -106,7 +106,7 @@ def main(args):
struct.unpack('<HH', superblock[1].data[0:4].ljust(4, b'\xff'))))
print("%-47s%s" % ("littlefs v%s.%s" % version,
"data (truncated, if it fits)"
if not any([args.no_truncate, args.log, args.all]) else ""))
if not any([args.no_truncate, args.tags, args.log, args.all]) else ""))
# print gstate
print("gstate 0x%s" % ''.join('%02x' % c for c in gstate))

View File

@@ -20,110 +20,113 @@ import pty
import errno
import signal
TEST_PATHS = 'tests'
TESTDIR = 'tests'
RULES = """
# add block devices to sources
TESTSRC ?= $(SRC) $(wildcard bd/*.c)
define FLATTEN
%(path)s%%$(subst /,.,$(target)): $(target)
tests/%$(subst /,.,$(target)): $(target)
./scripts/explode_asserts.py $$< -o $$@
endef
$(foreach target,$(TESTSRC),$(eval $(FLATTEN)))
$(foreach target,$(SRC),$(eval $(FLATTEN)))
-include tests/*.d
-include %(path)s*.d
.SECONDARY:
%(path)s.test: %(path)s.test.o \\
$(foreach t,$(subst /,.,$(TESTSRC:.c=.o)),%(path)s.$t)
%.test: %.test.o $(foreach f,$(subst /,.,$(SRC:.c=.o)),%.$f)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
# needed in case builddir is different
%(path)s%%.o: %(path)s%%.c
$(CC) -c -MMD $(CFLAGS) $< -o $@
"""
COVERAGE_RULES = """
%(path)s.test: override CFLAGS += -fprofile-arcs -ftest-coverage
BEFORE_MAIN = """
const char *lfs_testbd_path;
uint32_t lfs_testbd_cycles;
# delete lingering coverage
%(path)s.test: | %(path)s.info.clean
.PHONY: %(path)s.info.clean
%(path)s.info.clean:
rm -f %(path)s*.gcda
int lfs_testbd_readctx(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
return lfs_testbd_read((lfs_testbd_t*)ctx, block, off, buffer, size);
}
# accumulate coverage info
.PHONY: %(path)s.info
%(path)s.info:
$(strip $(LCOV) -c \\
$(addprefix -d ,$(wildcard %(path)s*.gcda)) \\
--rc 'geninfo_adjust_src_path=$(shell pwd)' \\
-o $@)
$(LCOV) -e $@ $(addprefix /,$(SRC)) -o $@
ifdef COVERAGETARGET
$(strip $(LCOV) -a $@ \\
$(addprefix -a ,$(wildcard $(COVERAGETARGET))) \\
-o $(COVERAGETARGET))
endif
int lfs_testbd_progctx(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
return lfs_testbd_prog((lfs_testbd_t*)ctx, block, off, buffer, size);
}
int lfs_testbd_erasectx(void *ctx, lfs_block_t block) {
return lfs_testbd_erase((lfs_testbd_t*)ctx, block);
}
int lfs_testbd_syncctx(void *ctx) {
return lfs_testbd_sync((lfs_testbd_t*)ctx);
}
"""
GLOBALS = """
BEFORE_TESTS = """
//////////////// AUTOGENERATED TEST ////////////////
#include "lfs2.h"
#include "bd/lfs2_testbd.h"
#include "lfs.h"
#include "bd/lfs_testbd.h"
#include <stdio.h>
extern const char *lfs2_testbd_path;
extern uint32_t lfs2_testbd_cycles;
extern const char *lfs_testbd_path;
extern uint32_t lfs_testbd_cycles;
extern int lfs_testbd_readctx(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
extern int lfs_testbd_progctx(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
extern int lfs_testbd_erasectx(void *ctx, lfs_block_t block);
extern int lfs_testbd_syncctx(void *ctx);
"""
DEFINES = {
'LFS2_READ_SIZE': 16,
'LFS2_PROG_SIZE': 'LFS2_READ_SIZE',
'LFS2_BLOCK_SIZE': 512,
'LFS2_BLOCK_COUNT': 1024,
'LFS2_BLOCK_CYCLES': -1,
'LFS2_CACHE_SIZE': '(64 % LFS2_PROG_SIZE == 0 ? 64 : LFS2_PROG_SIZE)',
'LFS2_LOOKAHEAD_SIZE': 16,
'LFS2_ERASE_VALUE': 0xff,
'LFS2_ERASE_CYCLES': 0,
'LFS2_BADBLOCK_BEHAVIOR': 'LFS2_TESTBD_BADBLOCK_PROGERROR',
'LFS_READ_SIZE': 16,
'LFS_PROG_SIZE': 'LFS_READ_SIZE',
'LFS_BLOCK_SIZE': 512,
'LFS_BLOCK_COUNT': 1024,
'LFS_BLOCK_CYCLES': -1,
'LFS_CACHE_SIZE': '(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)',
'LFS_LOOKAHEAD_SIZE': 16,
'LFS_ERASE_VALUE': 0xff,
'LFS_ERASE_CYCLES': 0,
'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_PROGERROR',
}
PROLOGUE = """
// prologue
__attribute__((unused)) lfs2_t lfs2;
__attribute__((unused)) lfs2_testbd_t bd;
__attribute__((unused)) lfs2_file_t file;
__attribute__((unused)) lfs2_dir_t dir;
__attribute__((unused)) struct lfs2_info info;
__attribute__((unused)) lfs_t lfs;
__attribute__((unused)) lfs_testbd_t bd;
__attribute__((unused)) lfs_file_t file;
__attribute__((unused)) lfs_dir_t dir;
__attribute__((unused)) struct lfs_info info;
__attribute__((unused)) char path[1024];
__attribute__((unused)) uint8_t buffer[1024];
__attribute__((unused)) lfs2_size_t size;
__attribute__((unused)) lfs_size_t size;
__attribute__((unused)) int err;
__attribute__((unused)) const struct lfs2_config cfg = {
.context = &bd,
.read = lfs2_testbd_read,
.prog = lfs2_testbd_prog,
.erase = lfs2_testbd_erase,
.sync = lfs2_testbd_sync,
.read_size = LFS2_READ_SIZE,
.prog_size = LFS2_PROG_SIZE,
.block_size = LFS2_BLOCK_SIZE,
.block_count = LFS2_BLOCK_COUNT,
.block_cycles = LFS2_BLOCK_CYCLES,
.cache_size = LFS2_CACHE_SIZE,
.lookahead_size = LFS2_LOOKAHEAD_SIZE,
__attribute__((unused)) const struct lfs_cfg cfg = {
.ctx = &bd,
.read = lfs_testbd_readctx,
.prog = lfs_testbd_progctx,
.erase = lfs_testbd_erasectx,
.sync = lfs_testbd_syncctx,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.block_cycles = LFS_BLOCK_CYCLES,
.cache_size = LFS_CACHE_SIZE,
.lookahead_size = LFS_LOOKAHEAD_SIZE,
};
__attribute__((unused)) const struct lfs2_testbd_config bdcfg = {
.erase_value = LFS2_ERASE_VALUE,
.erase_cycles = LFS2_ERASE_CYCLES,
.badblock_behavior = LFS2_BADBLOCK_BEHAVIOR,
.power_cycles = lfs2_testbd_cycles,
__attribute__((unused)) const struct lfs_testbd_cfg bdcfg = {
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.erase_size = LFS_BLOCK_SIZE,
.erase_count = LFS_BLOCK_COUNT,
.erase_value = LFS_ERASE_VALUE,
.erase_cycles = LFS_ERASE_CYCLES,
.badblock_behavior = LFS_BADBLOCK_BEHAVIOR,
.power_cycles = lfs_testbd_cycles,
};
lfs2_testbd_createcfg(&cfg, lfs2_testbd_path, &bdcfg) => 0;
lfs_testbd_createcfg(&bd, lfs_testbd_path, &bdcfg) => 0;
"""
EPILOGUE = """
// epilogue
lfs2_testbd_destroy(&cfg) => 0;
lfs_testbd_destroy(&bd) => 0;
"""
PASS = '\033[32m✓\033[0m'
FAIL = '\033[31m✗\033[0m'
@@ -150,8 +153,6 @@ class TestCase:
self.if_ = config.get('if', None)
self.in_ = config.get('in', None)
self.result = None
def __str__(self):
if hasattr(self, 'permno'):
if any(k not in self.case.defines for k in self.defines):
@@ -212,7 +213,7 @@ class TestCase:
len(self.filter) >= 2 and
self.filter[1] != self.permno):
return False
elif args.get('no_internal') and self.in_ is not None:
elif args.get('no_internal', False) and self.in_ is not None:
return False
elif self.if_ is not None:
if_ = self.if_
@@ -246,7 +247,7 @@ class TestCase:
try:
with open(disk, 'w') as f:
f.truncate(0)
if args.get('verbose'):
if args.get('verbose', False):
print('truncate --size=0', disk)
except FileNotFoundError:
pass
@@ -270,14 +271,14 @@ class TestCase:
'-ex', 'r'])
ncmd.extend(['--args'] + cmd)
if args.get('verbose'):
if args.get('verbose', False):
print(' '.join(shlex.quote(c) for c in ncmd))
signal.signal(signal.SIGINT, signal.SIG_IGN)
sys.exit(sp.call(ncmd))
# run test case!
mpty, spty = pty.openpty()
if args.get('verbose'):
if args.get('verbose', False):
print(' '.join(shlex.quote(c) for c in cmd))
proc = sp.Popen(cmd, stdout=spty, stderr=spty)
os.close(spty)
@@ -292,10 +293,8 @@ class TestCase:
if e.errno == errno.EIO:
break
raise
if not line:
break;
stdout.append(line)
if args.get('verbose'):
if args.get('verbose', False):
sys.stdout.write(line)
# intercept asserts
m = re.match(
@@ -334,8 +333,8 @@ class ValgrindTestCase(TestCase):
return not self.leaky and super().shouldtest(**args)
def test(self, exec=[], **args):
verbose = args.get('verbose')
uninit = (self.defines.get('LFS2_ERASE_VALUE', None) == -1)
verbose = args.get('verbose', False)
uninit = (self.defines.get('LFS_ERASE_VALUE', None) == -1)
exec = [
'valgrind',
'--leak-check=full',
@@ -386,17 +385,12 @@ class TestSuite:
self.name = os.path.basename(path)
if self.name.endswith('.toml'):
self.name = self.name[:-len('.toml')]
if args.get('build_dir'):
self.toml = path
self.path = args['build_dir'] + '/' + path
else:
self.toml = path
self.path = path
self.path = path
self.classes = classes
self.defines = defines.copy()
self.filter = filter
with open(self.toml) as f:
with open(path) as f:
# load tests
config = toml.load(f)
@@ -507,8 +501,8 @@ class TestSuite:
def build(self, **args):
# build test files
tf = open(self.path + '.test.tc', 'w')
tf.write(GLOBALS)
tf = open(self.path + '.test.c.t', 'w')
tf.write(BEFORE_TESTS)
if self.code is not None:
tf.write('#line %d "%s"\n' % (self.code_lineno, self.path))
tf.write(self.code)
@@ -517,25 +511,24 @@ class TestSuite:
for case in self.cases:
if case.in_ not in tfs:
tfs[case.in_] = open(self.path+'.'+
re.sub('(\.c)?$', '.tc', case.in_.replace('/', '.')), 'w')
case.in_.replace('/', '.')+'.t', 'w')
tfs[case.in_].write('#line 1 "%s"\n' % case.in_)
with open(case.in_) as f:
for line in f:
tfs[case.in_].write(line)
tfs[case.in_].write('\n')
tfs[case.in_].write(GLOBALS)
tfs[case.in_].write(BEFORE_TESTS)
tfs[case.in_].write('\n')
case.build(tfs[case.in_], **args)
tf.write(BEFORE_MAIN)
tf.write('\n')
tf.write('const char *lfs2_testbd_path;\n')
tf.write('uint32_t lfs2_testbd_cycles;\n')
tf.write('int main(int argc, char **argv) {\n')
tf.write(4*' '+'int case_ = (argc > 1) ? atoi(argv[1]) : 0;\n')
tf.write(4*' '+'int perm = (argc > 2) ? atoi(argv[2]) : 0;\n')
tf.write(4*' '+'lfs2_testbd_path = (argc > 3) ? argv[3] : NULL;\n')
tf.write(4*' '+'lfs2_testbd_cycles = (argc > 4) ? atoi(argv[4]) : 0;\n')
tf.write(4*' '+'lfs_testbd_path = (argc > 3) ? argv[3] : NULL;\n')
tf.write(4*' '+'lfs_testbd_cycles = (argc > 4) ? atoi(argv[4]) : 0;\n')
for perm in self.perms:
# test declaration
tf.write(4*' '+'extern void test_case%d(%s);\n' % (
@@ -556,33 +549,25 @@ class TestSuite:
# write makefiles
with open(self.path + '.mk', 'w') as mk:
mk.write(RULES.replace(4*' ', '\t') % dict(path=self.path))
mk.write(RULES.replace(4*' ', '\t'))
mk.write('\n')
# add coverage hooks?
if args.get('coverage'):
mk.write(COVERAGE_RULES.replace(4*' ', '\t') % dict(
path=self.path))
mk.write('\n')
# add truly global defines globally
# add truely global defines globally
for k, v in sorted(self.defines.items()):
mk.write('%s.test: override CFLAGS += -D%s=%r\n'
% (self.path, k, v))
mk.write('%s: override CFLAGS += -D%s=%r\n' % (
self.path+'.test', k, v))
for path in tfs:
if path is None:
mk.write('%s: %s | %s\n' % (
self.path+'.test.c',
self.toml,
self.path+'.test.tc'))
self.path,
self.path+'.test.c.t'))
else:
mk.write('%s: %s %s | %s\n' % (
self.path+'.'+path.replace('/', '.'),
self.toml,
path,
self.path+'.'+re.sub('(\.c)?$', '.tc',
path.replace('/', '.'))))
self.path, path,
self.path+'.'+path.replace('/', '.')+'.t'))
mk.write('\t./scripts/explode_asserts.py $| -o $@\n')
self.makefile = self.path + '.mk'
@@ -605,7 +590,7 @@ class TestSuite:
if not args.get('verbose', True):
sys.stdout.write(FAIL)
sys.stdout.flush()
if not args.get('keep_going'):
if not args.get('keep_going', False):
if not args.get('verbose', True):
sys.stdout.write('\n')
raise
@@ -627,36 +612,36 @@ def main(**args):
# and what class of TestCase to run
classes = []
if args.get('normal'):
if args.get('normal', False):
classes.append(TestCase)
if args.get('reentrant'):
if args.get('reentrant', False):
classes.append(ReentrantTestCase)
if args.get('valgrind'):
if args.get('valgrind', False):
classes.append(ValgrindTestCase)
if not classes:
classes = [TestCase]
suites = []
for testpath in args['test_paths']:
for testpath in args['testpaths']:
# optionally specified test case/perm
testpath, *filter = testpath.split('#')
filter = [int(f) for f in filter]
# figure out the suite's toml file
if os.path.isdir(testpath):
testpath = testpath + '/*.toml'
testpath = testpath + '/test_*.toml'
elif os.path.isfile(testpath):
testpath = testpath
elif testpath.endswith('.toml'):
testpath = TEST_PATHS + '/' + testpath
testpath = TESTDIR + '/' + testpath
else:
testpath = TEST_PATHS + '/' + testpath + '.toml'
testpath = TESTDIR + '/' + testpath + '.toml'
# find tests
for path in glob.glob(testpath):
suites.append(TestSuite(path, classes, defines, filter, **args))
# sort for reproducibility
# sort for reproducability
suites = sorted(suites)
# generate permutations
@@ -676,7 +661,7 @@ def main(**args):
list(it.chain.from_iterable(['-f', m] for m in makefiles)) +
[target for target in targets])
mpty, spty = pty.openpty()
if args.get('verbose'):
if args.get('verbose', False):
print(' '.join(shlex.quote(c) for c in cmd))
proc = sp.Popen(cmd, stdout=spty, stderr=spty)
os.close(spty)
@@ -689,17 +674,15 @@ def main(**args):
if e.errno == errno.EIO:
break
raise
if not line:
break;
stdout.append(line)
if args.get('verbose'):
if args.get('verbose', False):
sys.stdout.write(line)
# intercept warnings
m = re.match(
'^{0}([^:]+):(\d+):(?:\d+:)?{0}{1}:{0}(.*)$'
.format('(?:\033\[[\d;]*.| )*', 'warning'),
line)
if m and not args.get('verbose'):
if m and not args.get('verbose', False):
try:
with open(m.group(1)) as f:
lineno = int(m.group(2))
@@ -712,26 +695,27 @@ def main(**args):
except:
pass
proc.wait()
if proc.returncode != 0:
if not args.get('verbose'):
if not args.get('verbose', False):
for line in stdout:
sys.stdout.write(line)
sys.exit(-1)
sys.exit(-3)
print('built %d test suites, %d test cases, %d permutations' % (
len(suites),
sum(len(suite.cases) for suite in suites),
sum(len(suite.perms) for suite in suites)))
total = 0
filtered = 0
for suite in suites:
for perm in suite.perms:
total += perm.shouldtest(**args)
if total != sum(len(suite.perms) for suite in suites):
print('filtered down to %d permutations' % total)
filtered += perm.shouldtest(**args)
if filtered != sum(len(suite.perms) for suite in suites):
print('filtered down to %d permutations' % filtered)
# only requested to build?
if args.get('build'):
if args.get('build', False):
return 0
print('====== testing ======')
@@ -746,12 +730,15 @@ def main(**args):
failed = 0
for suite in suites:
for perm in suite.perms:
if not hasattr(perm, 'result'):
continue
if perm.result == PASS:
passed += 1
elif isinstance(perm.result, TestFailure):
else:
sys.stdout.write(
"\033[01m{path}:{lineno}:\033[01;31mfailure:\033[m "
"{perm} failed\n".format(
"{perm} failed with {returncode}\n".format(
perm=perm, path=perm.suite.path, lineno=perm.lineno,
returncode=perm.result.returncode or 0))
if perm.result.stdout:
@@ -769,33 +756,11 @@ def main(**args):
sys.stdout.write('\n')
failed += 1
if args.get('coverage'):
# collect coverage info
# why -j1? lcov doesn't work in parallel because of gcov limitations
cmd = (['make', '-j1', '-f', 'Makefile'] +
list(it.chain.from_iterable(['-f', m] for m in makefiles)) +
(['COVERAGETARGET=%s' % args['coverage']]
if isinstance(args['coverage'], str) else []) +
[suite.path + '.info' for suite in suites
if any(perm.result == PASS for perm in suite.perms)])
if args.get('verbose'):
print(' '.join(shlex.quote(c) for c in cmd))
proc = sp.Popen(cmd,
stdout=sp.PIPE if not args.get('verbose') else None,
stderr=sp.STDOUT if not args.get('verbose') else None,
universal_newlines=True)
proc.wait()
if proc.returncode != 0:
if not args.get('verbose'):
for line in proc.stdout:
sys.stdout.write(line)
sys.exit(-1)
if args.get('gdb'):
if args.get('gdb', False):
failure = None
for suite in suites:
for perm in suite.perms:
if isinstance(perm.result, TestFailure):
if getattr(perm, 'result', PASS) != PASS:
failure = perm.result
if failure is not None:
print('======= gdb ======')
@@ -803,22 +768,20 @@ def main(**args):
failure.case.test(failure=failure, **args)
sys.exit(0)
print('tests passed %d/%d (%.2f%%)' % (passed, total,
100*(passed/total if total else 1.0)))
print('tests failed %d/%d (%.2f%%)' % (failed, total,
100*(failed/total if total else 1.0)))
print('tests passed: %d' % passed)
print('tests failed: %d' % failed)
return 1 if failed > 0 else 0
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description="Run parameterized tests in various configurations.")
parser.add_argument('test_paths', nargs='*', default=[TEST_PATHS],
parser.add_argument('testpaths', nargs='*', default=[TESTDIR],
help="Description of test(s) to run. By default, this is all tests \
found in the \"{0}\" directory. Here, you can specify a different \
directory of tests, a specific file, a suite by name, and even \
specific test cases and permutations. For example \
\"test_dirs#1\" or \"{0}/test_dirs.toml#1#1\".".format(TEST_PATHS))
directory of tests, a specific file, a suite by name, and even a \
specific test case by adding brackets. For example \
\"test_dirs[0]\" or \"{0}/test_dirs.toml[0]\".".format(TESTDIR))
parser.add_argument('-D', action='append', default=[],
help="Overriding parameter definitions.")
parser.add_argument('-v', '--verbose', action='store_true',
@@ -839,19 +802,10 @@ if __name__ == "__main__":
help="Run tests normally.")
parser.add_argument('-r', '--reentrant', action='store_true',
help="Run reentrant tests with simulated power-loss.")
parser.add_argument('--valgrind', action='store_true',
parser.add_argument('-V', '--valgrind', action='store_true',
help="Run non-leaky tests under valgrind to check for memory leaks.")
parser.add_argument('--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.")
parser.add_argument('--disk',
parser.add_argument('-d', '--disk',
help="Specify a file to use for persistent/reentrant tests.")
parser.add_argument('--coverage', type=lambda x: x if x else True,
nargs='?', const='',
help="Collect coverage information during testing. This uses lcov/gcov \
to accumulate coverage information into *.info files. May also \
a path to a *.info file to accumulate coverage info into.")
parser.add_argument('--build-dir',
help="Build relative to the specified directory instead of the \
current directory.")
sys.exit(main(**vars(parser.parse_args())))

View File

@@ -1,409 +1,409 @@
# allocator tests
# note for these to work there are a number constraints on the device geometry
if = 'LFS2_BLOCK_CYCLES == -1'
if = 'LFS_BLOCK_CYCLES == -1'
[[case]] # parallel allocation test
define.FILES = 3
define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)'
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"};
lfs2_file_t files[FILES];
lfs_file_t files[FILES];
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "breakfast") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "breakfast") => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &files[n], path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_file_open(&lfs, &files[n], path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
}
for (int n = 0; n < FILES; n++) {
size = strlen(names[n]);
for (lfs2_size_t i = 0; i < SIZE; i += size) {
lfs2_file_write(&lfs2, &files[n], names[n], size) => size;
for (lfs_size_t i = 0; i < SIZE; i += size) {
lfs_file_write(&lfs, &files[n], names[n], size) => size;
}
}
for (int n = 0; n < FILES; n++) {
lfs2_file_close(&lfs2, &files[n]) => 0;
lfs_file_close(&lfs, &files[n]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
size = strlen(names[n]);
for (lfs2_size_t i = 0; i < SIZE; i += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
for (lfs_size_t i = 0; i < SIZE; i += size) {
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, names[n], size) == 0);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # serial allocation test
define.FILES = 3
define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)'
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"};
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "breakfast") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "breakfast") => 0;
lfs_unmount(&lfs) => 0;
for (int n = 0; n < FILES; n++) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen(names[n]);
memcpy(buffer, names[n], size);
for (int i = 0; i < SIZE; i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
}
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
size = strlen(names[n]);
for (int i = 0; i < SIZE; i += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, names[n], size) == 0);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # parallel allocation reuse test
define.FILES = 3
define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)'
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
define.CYCLES = [1, 10]
code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"};
lfs2_file_t files[FILES];
lfs_file_t files[FILES];
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
for (int c = 0; c < CYCLES; c++) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "breakfast") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "breakfast") => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &files[n], path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_file_open(&lfs, &files[n], path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
}
for (int n = 0; n < FILES; n++) {
size = strlen(names[n]);
for (int i = 0; i < SIZE; i += size) {
lfs2_file_write(&lfs2, &files[n], names[n], size) => size;
lfs_file_write(&lfs, &files[n], names[n], size) => size;
}
}
for (int n = 0; n < FILES; n++) {
lfs2_file_close(&lfs2, &files[n]) => 0;
lfs_file_close(&lfs, &files[n]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
size = strlen(names[n]);
for (int i = 0; i < SIZE; i += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, names[n], size) == 0);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_remove(&lfs2, path) => 0;
lfs_remove(&lfs, path) => 0;
}
lfs2_remove(&lfs2, "breakfast") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_remove(&lfs, "breakfast") => 0;
lfs_unmount(&lfs) => 0;
}
'''
[[case]] # serial allocation reuse test
define.FILES = 3
define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)'
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
define.CYCLES = [1, 10]
code = '''
const char *names[FILES] = {"bacon", "eggs", "pancakes"};
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
for (int c = 0; c < CYCLES; c++) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "breakfast") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "breakfast") => 0;
lfs_unmount(&lfs) => 0;
for (int n = 0; n < FILES; n++) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen(names[n]);
memcpy(buffer, names[n], size);
for (int i = 0; i < SIZE; i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
}
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
size = strlen(names[n]);
for (int i = 0; i < SIZE; i += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, names[n], size) == 0);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int n = 0; n < FILES; n++) {
sprintf(path, "breakfast/%s", names[n]);
lfs2_remove(&lfs2, path) => 0;
lfs_remove(&lfs, path) => 0;
}
lfs2_remove(&lfs2, "breakfast") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_remove(&lfs, "breakfast") => 0;
lfs_unmount(&lfs) => 0;
}
'''
[[case]] # exhaustion test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("exhaustion");
memcpy(buffer, "exhaustion", size);
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_sync(&lfs, &file) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_ssize_t res;
lfs_ssize_t res;
while (true) {
res = lfs2_file_write(&lfs2, &file, buffer, size);
res = lfs_file_write(&lfs, &file, buffer, size);
if (res < 0) {
break;
}
res => size;
}
res => LFS2_ERR_NOSPC;
res => LFS_ERR_NOSPC;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY);
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
size = strlen("exhaustion");
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "exhaustion", size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # exhaustion wraparound test
define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-4)) / 3)'
define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / 3)'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "padding", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("buffering");
memcpy(buffer, "buffering", size);
for (int i = 0; i < SIZE; i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_remove(&lfs2, "padding") => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_remove(&lfs, "padding") => 0;
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("exhaustion");
memcpy(buffer, "exhaustion", size);
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_sync(&lfs, &file) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_ssize_t res;
lfs_ssize_t res;
while (true) {
res = lfs2_file_write(&lfs2, &file, buffer, size);
res = lfs_file_write(&lfs, &file, buffer, size);
if (res < 0) {
break;
}
res => size;
}
res => LFS2_ERR_NOSPC;
res => LFS_ERR_NOSPC;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY);
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
size = strlen("exhaustion");
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "exhaustion", size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # dir exhaustion test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// find out max file size
lfs2_mkdir(&lfs2, "exhaustiondir") => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
int count = 0;
while (true) {
err = lfs2_file_write(&lfs2, &file, buffer, size);
err = lfs_file_write(&lfs, &file, buffer, size);
if (err < 0) {
break;
}
count += 1;
}
err => LFS2_ERR_NOSPC;
lfs2_file_close(&lfs2, &file) => 0;
err => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file) => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_remove(&lfs2, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
// see if dir fits with max file size
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count; i++) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_mkdir(&lfs2, "exhaustiondir") => 0;
lfs2_remove(&lfs2, "exhaustiondir") => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// see if dir fits with > max file size
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count+1; i++) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC;
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # what if we have a bad block during an allocation scan?
in = "lfs2.c"
define.LFS2_ERASE_CYCLES = 0xffffffff
define.LFS2_BADBLOCK_BEHAVIOR = 'LFS2_TESTBD_BADBLOCK_READERROR'
in = "lfs.c"
define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// first fill to exhaustion to find available space
lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "waka");
size = strlen("waka");
lfs2_size_t filesize = 0;
lfs_size_t filesize = 0;
while (true) {
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, buffer, size);
assert(res == (lfs2_ssize_t)size || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
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;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// now fill all but a couple of blocks of the filesystem with data
filesize -= 3*LFS2_BLOCK_SIZE;
lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
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 (lfs2_size_t i = 0; i < filesize/size; i++) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_size_t i = 0; i < filesize/size; i++) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// also save head of file so we can error during lookahead scan
lfs2_block_t fileblock = file.ctz.head;
lfs2_unmount(&lfs2) => 0;
lfs_block_t fileblock = file.ctz.head;
lfs_unmount(&lfs) => 0;
// remount to force an alloc scan
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// but mark the head of our file as a "bad block", this is force our
// scan to bail early
lfs2_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0;
lfs2_file_open(&lfs2, &file, "ghost", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_testbd_setwear(&bd, 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) {
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, buffer, size);
assert(res == (lfs2_ssize_t)size || res == LFS2_ERR_CORRUPT);
if (res == LFS2_ERR_CORRUPT) {
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;
}
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// now reverse the "bad block" and try to write the file again until we
// run out of space
lfs2_testbd_setwear(&cfg, fileblock, 0) => 0;
lfs2_file_open(&lfs2, &file, "ghost", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_testbd_setwear(&bd, 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) {
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, buffer, size);
assert(res == (lfs2_ssize_t)size || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
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;
}
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
// check that the disk isn't hurt
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_RDONLY) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0;
strcpy((char*)buffer, "waka");
size = strlen("waka");
for (lfs2_size_t i = 0; i < filesize/size; i++) {
for (lfs_size_t i = 0; i < filesize/size; i++) {
uint8_t rbuffer[4];
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
@@ -412,242 +412,242 @@ code = '''
# should be removed and replaced with generalized tests.
[[case]] # chained dir exhaustion test
define.LFS2_BLOCK_SIZE = 512
define.LFS2_BLOCK_COUNT = 1024
if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024'
define.LFS_BLOCK_SIZE = 512
define.LFS_BLOCK_COUNT = 1024
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// find out max file size
lfs2_mkdir(&lfs2, "exhaustiondir") => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
for (int i = 0; i < 10; i++) {
sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs2_mkdir(&lfs2, path) => 0;
lfs_mkdir(&lfs, path) => 0;
}
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
int count = 0;
while (true) {
err = lfs2_file_write(&lfs2, &file, buffer, size);
err = lfs_file_write(&lfs, &file, buffer, size);
if (err < 0) {
break;
}
count += 1;
}
err => LFS2_ERR_NOSPC;
lfs2_file_close(&lfs2, &file) => 0;
err => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file) => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_remove(&lfs2, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
for (int i = 0; i < 10; i++) {
sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs2_remove(&lfs2, path) => 0;
lfs_remove(&lfs, path) => 0;
}
// see that chained dir fails
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count+1; i++) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_sync(&lfs, &file) => 0;
for (int i = 0; i < 10; i++) {
sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs2_mkdir(&lfs2, path) => 0;
lfs_mkdir(&lfs, path) => 0;
}
lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC;
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
// shorten file to try a second chained dir
while (true) {
err = lfs2_mkdir(&lfs2, "exhaustiondir");
if (err != LFS2_ERR_NOSPC) {
err = lfs_mkdir(&lfs, "exhaustiondir");
if (err != LFS_ERR_NOSPC) {
break;
}
lfs2_ssize_t filesize = lfs2_file_size(&lfs2, &file);
lfs_ssize_t filesize = lfs_file_size(&lfs, &file);
filesize > 0 => true;
lfs2_file_truncate(&lfs2, &file, filesize - size) => 0;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_truncate(&lfs, &file, filesize - size) => 0;
lfs_file_sync(&lfs, &file) => 0;
}
err => 0;
lfs2_mkdir(&lfs2, "exhaustiondir2") => LFS2_ERR_NOSPC;
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # split dir test
define.LFS2_BLOCK_SIZE = 512
define.LFS2_BLOCK_COUNT = 1024
if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024'
define.LFS_BLOCK_SIZE = 512
define.LFS_BLOCK_COUNT = 1024
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// create one block hole for half a directory
lfs2_file_open(&lfs2, &file, "bump", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (lfs2_size_t i = 0; i < cfg.block_size; i += 2) {
lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs2_file_write(&lfs2, &file, buffer, cfg.block_size) => cfg.block_size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, buffer, cfg.block_size) => cfg.block_size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < (cfg.block_count-4)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// remount to force reset of lookahead
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_unmount(&lfs) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// open hole
lfs2_remove(&lfs2, "bump") => 0;
lfs_remove(&lfs, "bump") => 0;
lfs2_mkdir(&lfs2, "splitdir") => 0;
lfs2_file_open(&lfs2, &file, "splitdir/bump",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (lfs2_size_t i = 0; i < cfg.block_size; i += 2) {
lfs_mkdir(&lfs, "splitdir") => 0;
lfs_file_open(&lfs, &file, "splitdir/bump",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs2_file_write(&lfs2, &file, buffer, 2*cfg.block_size) => LFS2_ERR_NOSPC;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, buffer, 2*cfg.block_size) => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # outdated lookahead test
define.LFS2_BLOCK_SIZE = 512
define.LFS2_BLOCK_COUNT = 1024
if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024'
define.LFS_BLOCK_SIZE = 512
define.LFS_BLOCK_COUNT = 1024
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// fill completely with two files
lfs2_file_open(&lfs2, &file, "exhaustion1",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, "exhaustion1",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "exhaustion2",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, "exhaustion2",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// remount to force reset of lookahead
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_unmount(&lfs) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// rewrite one file
lfs2_file_open(&lfs2, &file, "exhaustion1",
LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "exhaustion1",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_sync(&lfs, &file) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// rewrite second file, this requires lookahead does not
// use old population
lfs2_file_open(&lfs2, &file, "exhaustion2",
LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "exhaustion2",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_sync(&lfs, &file) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # outdated lookahead and split dir test
define.LFS2_BLOCK_SIZE = 512
define.LFS2_BLOCK_COUNT = 1024
if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024'
define.LFS_BLOCK_SIZE = 512
define.LFS_BLOCK_COUNT = 1024
if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// fill completely with two files
lfs2_file_open(&lfs2, &file, "exhaustion1",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, "exhaustion1",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "exhaustion2",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, "exhaustion2",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// remount to force reset of lookahead
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_unmount(&lfs) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// rewrite one file with a hole of one block
lfs2_file_open(&lfs2, &file, "exhaustion1",
LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "exhaustion1",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_sync(&lfs, &file) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// try to allocate a directory, should fail!
lfs2_mkdir(&lfs2, "split") => LFS2_ERR_NOSPC;
lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC;
// file should not fail
lfs2_file_open(&lfs2, &file, "notasplit",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hi", 2) => 2;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "notasplit",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -1,304 +1,304 @@
[[case]] # set/get attribute
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(buffer, 0, sizeof(buffer));
lfs2_setattr(&lfs2, "hello", 'A', "aaaa", 4) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "bbbbbb", 6) => 0;
lfs2_setattr(&lfs2, "hello", 'C', "ccccc", 5) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "hello", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "hello", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "", 0) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 0;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "hello", 'B', "", 0) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_removeattr(&lfs2, "hello", 'B') => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => LFS2_ERR_NOATTR;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs_removeattr(&lfs, "hello", 'B') => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => LFS_ERR_NOATTR;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "dddddd", 6) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "eee", 3) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 3;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC;
lfs2_setattr(&lfs2, "hello", 'B', "fffffffff", 9) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 9;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(buffer, 0, sizeof(buffer));
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 9) => 9;
lfs2_getattr(&lfs2, "hello", 'C', buffer+13, 5) => 5;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello");
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
'''
[[case]] # set/get root attribute
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(buffer, 0, sizeof(buffer));
lfs2_setattr(&lfs2, "/", 'A', "aaaa", 4) => 0;
lfs2_setattr(&lfs2, "/", 'B', "bbbbbb", 6) => 0;
lfs2_setattr(&lfs2, "/", 'C', "ccccc", 5) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "/", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "/", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'B', "", 0) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 0;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "/", 'B', "", 0) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_removeattr(&lfs2, "/", 'B') => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => LFS2_ERR_NOATTR;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs_removeattr(&lfs, "/", 'B') => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => LFS_ERR_NOATTR;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'B', "dddddd", 6) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'B', "eee", 3) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 3;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC;
lfs2_setattr(&lfs2, "/", 'B', "fffffffff", 9) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 9;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs2_unmount(&lfs2) => 0;
lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(buffer, 0, sizeof(buffer));
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 9) => 9;
lfs2_getattr(&lfs2, "/", 'C', buffer+13, 5) => 5;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello");
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
'''
[[case]] # set/get file attribute
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(buffer, 0, sizeof(buffer));
struct lfs2_attr attrs1[] = {
struct lfs_attr attrs1[] = {
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5},
};
struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
struct lfs_file_cfg cfg1 = {.attrs=attrs1, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer, "aaaa", 4);
memcpy(buffer+4, "bbbbbb", 6);
memcpy(buffer+10, "ccccc", 5);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "dddddd", 6);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 3;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "eee", 3);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[0].size = LFS2_ATTR_MAX+1;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1)
=> LFS2_ERR_NOSPC;
attrs1[0].size = LFS_ATTR_MAX+1;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1)
=> LFS_ERR_NOSPC;
struct lfs2_attr attrs2[] = {
struct lfs_attr attrs2[] = {
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5},
};
struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDWR, &cfg2) => 0;
struct lfs_file_cfg cfg2 = {.attrs=attrs2, .attr_count=3};
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDWR, &cfg2) => 0;
memcpy(buffer+4, "fffffffff", 9);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
attrs1[0].size = 4;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(buffer, 0, sizeof(buffer));
struct lfs2_attr attrs3[] = {
struct lfs_attr attrs3[] = {
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5},
};
struct lfs2_file_config cfg3 = {.attrs=attrs3, .attr_count=3};
struct lfs_file_cfg cfg3 = {.attrs=attrs3, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg3) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg3) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello");
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
'''
[[case]] # deferred file attributes
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_setattr(&lfs2, "hello/hello", 'B', "fffffffff", 9) => 0;
lfs2_setattr(&lfs2, "hello/hello", 'C', "ccccc", 5) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_setattr(&lfs, "hello/hello", 'B', "fffffffff", 9) => 0;
lfs_setattr(&lfs, "hello/hello", 'C', "ccccc", 5) => 0;
memset(buffer, 0, sizeof(buffer));
struct lfs2_attr attrs1[] = {
struct lfs_attr attrs1[] = {
{'B', "gggg", 4},
{'C', "", 0},
{'D', "hhhh", 4},
};
struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
struct lfs_file_cfg cfg1 = {.attrs=attrs1, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 9;
lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 5;
lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => LFS2_ERR_NOATTR;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 9;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 5;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => LFS_ERR_NOATTR;
memcmp(buffer, "fffffffff", 9) => 0;
memcmp(buffer+9, "ccccc\0\0\0\0", 9) => 0;
memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0;
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 4;
lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => 4;
lfs_file_sync(&lfs, &file) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 4;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 0;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4;
memcmp(buffer, "gggg\0\0\0\0\0", 9) => 0;
memcmp(buffer+9, "\0\0\0\0\0\0\0\0\0", 9) => 0;
memcmp(buffer+18, "hhhh\0\0\0\0\0", 9) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -1,241 +1,241 @@
# bad blocks with block cycles should be tested in test_relocations
if = 'LFS2_BLOCK_CYCLES == -1'
if = 'LFS_BLOCK_CYCLES == -1'
[[case]] # single bad blocks
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_ERASE_CYCLES = 0xffffffff
define.LFS2_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS2_BADBLOCK_BEHAVIOR = [
'LFS2_TESTBD_BADBLOCK_PROGERROR',
'LFS2_TESTBD_BADBLOCK_ERASEERROR',
'LFS2_TESTBD_BADBLOCK_READERROR',
'LFS2_TESTBD_BADBLOCK_PROGNOOP',
'LFS2_TESTBD_BADBLOCK_ERASENOOP',
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS_BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
]
define.NAMEMULT = 64
define.FILEMULT = 1
code = '''
for (lfs2_block_t badblock = 2; badblock < LFS2_BLOCK_COUNT; badblock++) {
lfs2_testbd_setwear(&cfg, badblock-1, 0) => 0;
lfs2_testbd_setwear(&cfg, badblock, 0xffffffff) => 0;
for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) {
lfs_testbd_setwear(&bd, badblock-1, 0) => 0;
lfs_testbd_setwear(&bd, badblock, 0xffffffff) => 0;
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
lfs_mkdir(&lfs, (char*)buffer) => 0;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file, (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs2_stat(&lfs2, (char*)buffer, &info) => 0;
info.type => LFS2_TYPE_DIR;
lfs_stat(&lfs, (char*)buffer, &info) => 0;
info.type => LFS_TYPE_DIR;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
uint8_t rbuffer[1024];
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(buffer, rbuffer, size) => 0;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
}
'''
[[case]] # region corruption (causes cascading failures)
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_ERASE_CYCLES = 0xffffffff
define.LFS2_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS2_BADBLOCK_BEHAVIOR = [
'LFS2_TESTBD_BADBLOCK_PROGERROR',
'LFS2_TESTBD_BADBLOCK_ERASEERROR',
'LFS2_TESTBD_BADBLOCK_READERROR',
'LFS2_TESTBD_BADBLOCK_PROGNOOP',
'LFS2_TESTBD_BADBLOCK_ERASENOOP',
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS_BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
]
define.NAMEMULT = 64
define.FILEMULT = 1
code = '''
for (lfs2_block_t i = 0; i < (LFS2_BLOCK_COUNT-2)/2; i++) {
lfs2_testbd_setwear(&cfg, i+2, 0xffffffff) => 0;
for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) {
lfs_testbd_setwear(&bd, i+2, 0xffffffff) => 0;
}
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
lfs_mkdir(&lfs, (char*)buffer) => 0;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file, (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs2_stat(&lfs2, (char*)buffer, &info) => 0;
info.type => LFS2_TYPE_DIR;
lfs_stat(&lfs, (char*)buffer, &info) => 0;
info.type => LFS_TYPE_DIR;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
uint8_t rbuffer[1024];
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(buffer, rbuffer, size) => 0;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # alternating corruption (causes cascading failures)
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_ERASE_CYCLES = 0xffffffff
define.LFS2_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS2_BADBLOCK_BEHAVIOR = [
'LFS2_TESTBD_BADBLOCK_PROGERROR',
'LFS2_TESTBD_BADBLOCK_ERASEERROR',
'LFS2_TESTBD_BADBLOCK_READERROR',
'LFS2_TESTBD_BADBLOCK_PROGNOOP',
'LFS2_TESTBD_BADBLOCK_ERASENOOP',
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS_BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
]
define.NAMEMULT = 64
define.FILEMULT = 1
code = '''
for (lfs2_block_t i = 0; i < (LFS2_BLOCK_COUNT-2)/2; i++) {
lfs2_testbd_setwear(&cfg, (2*i) + 2, 0xffffffff) => 0;
for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) {
lfs_testbd_setwear(&bd, (2*i) + 2, 0xffffffff) => 0;
}
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
lfs_mkdir(&lfs, (char*)buffer) => 0;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file, (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs2_stat(&lfs2, (char*)buffer, &info) => 0;
info.type => LFS2_TYPE_DIR;
lfs_stat(&lfs, (char*)buffer, &info) => 0;
info.type => LFS_TYPE_DIR;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
uint8_t rbuffer[1024];
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(buffer, rbuffer, size) => 0;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
# other corner cases
[[case]] # bad superblocks (corrupt 1 or 0)
define.LFS2_ERASE_CYCLES = 0xffffffff
define.LFS2_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS2_BADBLOCK_BEHAVIOR = [
'LFS2_TESTBD_BADBLOCK_PROGERROR',
'LFS2_TESTBD_BADBLOCK_ERASEERROR',
'LFS2_TESTBD_BADBLOCK_READERROR',
'LFS2_TESTBD_BADBLOCK_PROGNOOP',
'LFS2_TESTBD_BADBLOCK_ERASENOOP',
define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_ERASE_VALUE = [0x00, 0xff, -1]
define.LFS_BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
]
code = '''
lfs2_testbd_setwear(&cfg, 0, 0xffffffff) => 0;
lfs2_testbd_setwear(&cfg, 1, 0xffffffff) => 0;
lfs_testbd_setwear(&bd, 0, 0xffffffff) => 0;
lfs_testbd_setwear(&bd, 1, 0xffffffff) => 0;
lfs2_format(&lfs2, &cfg) => LFS2_ERR_NOSPC;
lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT;
lfs_formatcfg(&lfs, &cfg) => LFS_ERR_NOSPC;
lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''

File diff suppressed because it is too large Load Diff

View File

@@ -2,96 +2,96 @@
# Note that these tests are intended for 512 byte inline sizes. They should
# still pass with other inline sizes but wouldn't be testing anything.
define.LFS2_CACHE_SIZE = 512
if = 'LFS2_CACHE_SIZE % LFS2_PROG_SIZE == 0 && LFS2_CACHE_SIZE == 512'
define.LFS_CACHE_SIZE = 512
if = 'LFS_CACHE_SIZE % LFS_PROG_SIZE == 0 && LFS_CACHE_SIZE == 512'
[[case]] # entry grow test
code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// write hi0 20
sprintf(path, "hi0"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi2 20
sprintf(path, "hi2"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi3 20
sprintf(path, "hi3"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi0 20
sprintf(path, "hi0"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi2 20
sprintf(path, "hi2"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi3 20
sprintf(path, "hi3"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # entry shrink test
@@ -99,88 +99,88 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// write hi0 20
sprintf(path, "hi0"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi2 20
sprintf(path, "hi2"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi3 20
sprintf(path, "hi3"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi0 20
sprintf(path, "hi0"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi2 20
sprintf(path, "hi2"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi3 20
sprintf(path, "hi3"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # entry spill test
@@ -188,72 +188,72 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// write hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # entry push spill test
@@ -261,88 +261,88 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// write hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # entry push spill two test
@@ -350,103 +350,103 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// write hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi4 200
sprintf(path, "hi4"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi4 200
sprintf(path, "hi4"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # entry drop test
@@ -454,158 +454,158 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// write hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_remove(&lfs2, "hi1") => 0;
lfs2_stat(&lfs2, "hi1", &info) => LFS2_ERR_NOENT;
lfs_remove(&lfs, "hi1") => 0;
lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_remove(&lfs2, "hi2") => 0;
lfs2_stat(&lfs2, "hi2", &info) => LFS2_ERR_NOENT;
lfs_remove(&lfs, "hi2") => 0;
lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_remove(&lfs2, "hi3") => 0;
lfs2_stat(&lfs2, "hi3", &info) => LFS2_ERR_NOENT;
lfs_remove(&lfs, "hi3") => 0;
lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_remove(&lfs2, "hi0") => 0;
lfs2_stat(&lfs2, "hi0", &info) => LFS2_ERR_NOENT;
lfs_remove(&lfs, "hi0") => 0;
lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # create too big
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(path, 'm', 200);
path[200] = '\0';
size = 400;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
uint8_t wbuffer[1024];
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
size = 400;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
uint8_t rbuffer[1024];
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # resize too big
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
memset(path, 'm', 200);
path[200] = '\0';
size = 40;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
uint8_t wbuffer[1024];
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
size = 40;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
uint8_t rbuffer[1024];
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
size = 400;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
size = 400;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -4,285 +4,285 @@
# invalid pointer tests (outside of block_count)
[[case]] # invalid tail-pointer test
define.TAIL_TYPE = ['LFS2_TYPE_HARDTAIL', 'LFS2_TYPE_SOFTTAIL']
define.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL']
define.INVALSET = [0x3, 0x1, 0x2]
in = "lfs2.c"
in = "lfs.c"
code = '''
// create littlefs
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// change tail-pointer to invalid pointers
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8),
(lfs2_block_t[2]){
lfs_initcommon(&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;
lfs2_deinit(&lfs2) => 0;
lfs_deinit(&lfs) => 0;
// test that mount fails gracefully
lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT;
lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''
[[case]] # invalid dir pointer test
define.INVALSET = [0x3, 0x1, 0x2]
in = "lfs2.c"
in = "lfs.c"
code = '''
// create littlefs
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// make a dir
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "dir_here") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "dir_here") => 0;
lfs_unmount(&lfs) => 0;
// change the dir pointer to be invalid
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs_initcommon(&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
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("dir_here")), buffer)
=> LFS2_MKTAG(LFS2_TYPE_DIR, 1, strlen("dir_here"));
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
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, 8),
(lfs2_block_t[2]){
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;
lfs2_deinit(&lfs2) => 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
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "dir_here", &info) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "dir_here", &info) => 0;
assert(strcmp(info.name, "dir_here") == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
lfs2_dir_open(&lfs2, &dir, "dir_here") => LFS2_ERR_CORRUPT;
lfs2_stat(&lfs2, "dir_here/file_here", &info) => LFS2_ERR_CORRUPT;
lfs2_dir_open(&lfs2, &dir, "dir_here/dir_here") => LFS2_ERR_CORRUPT;
lfs2_file_open(&lfs2, &file, "dir_here/file_here",
LFS2_O_RDONLY) => LFS2_ERR_CORRUPT;
lfs2_file_open(&lfs2, &file, "dir_here/file_here",
LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_CORRUPT;
lfs2_unmount(&lfs2) => 0;
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 = "lfs2.c"
in = "lfs.c"
define.SIZE = [10, 1000, 100000] # faked file size
code = '''
// create littlefs
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// make a file
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "file_here",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mountcfg(&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
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs_initcommon(&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
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS2_MKTAG(LFS2_TYPE_REG, 1, strlen("file_here"));
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
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_CTZSTRUCT, 1, sizeof(struct lfs2_ctz)),
&(struct lfs2_ctz){0xcccccccc, lfs2_tole32(SIZE)}})) => 0;
lfs2_deinit(&lfs2) => 0;
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
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "file_here", &info) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "file_here", &info) => 0;
assert(strcmp(info.name, "file_here") == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfs2_file_open(&lfs2, &file, "file_here", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, SIZE) => LFS2_ERR_CORRUPT;
lfs2_file_close(&lfs2, &file) => 0;
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*LFS2_BLOCK_SIZE) {
lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT;
if (SIZE > 2*LFS_BLOCK_SIZE) {
lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # invalid pointer in CTZ skip-list test
define.SIZE = ['2*LFS2_BLOCK_SIZE', '3*LFS2_BLOCK_SIZE', '4*LFS2_BLOCK_SIZE']
in = "lfs2.c"
define.SIZE = ['2*LFS_BLOCK_SIZE', '3*LFS_BLOCK_SIZE', '4*LFS_BLOCK_SIZE']
in = "lfs.c"
code = '''
// create littlefs
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// make a file
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "file_here",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_mountcfg(&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';
lfs2_file_write(&lfs2, &file, &c, 1) => 1;
lfs_file_write(&lfs, &file, &c, 1) => 1;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// change pointer in CTZ skip-list to be invalid
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs_initcommon(&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
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS2_MKTAG(LFS2_TYPE_REG, 1, strlen("file_here"));
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 lfs2_ctz ctz;
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_STRUCT, 1, sizeof(struct lfs2_ctz)), &ctz)
=> LFS2_MKTAG(LFS2_TYPE_CTZSTRUCT, 1, sizeof(struct lfs2_ctz));
lfs2_ctz_fromle32(&ctz);
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[LFS2_BLOCK_SIZE];
cfg.read(&cfg, ctz.head, 0, bbuffer, LFS2_BLOCK_SIZE) => 0;
uint32_t bad = lfs2_tole32(0xcccccccc);
uint8_t bbuffer[LFS_BLOCK_SIZE];
lfs_testbd_read(&bd, 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, LFS2_BLOCK_SIZE) => 0;
lfs2_deinit(&lfs2) => 0;
lfs_testbd_erase(&bd, ctz.head) => 0;
lfs_testbd_prog(&bd, 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
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "file_here", &info) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "file_here", &info) => 0;
assert(strcmp(info.name, "file_here") == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfs2_file_open(&lfs2, &file, "file_here", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, SIZE) => LFS2_ERR_CORRUPT;
lfs2_file_close(&lfs2, &file) => 0;
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*LFS2_BLOCK_SIZE) {
lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT;
if (SIZE > 2*LFS_BLOCK_SIZE) {
lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # invalid gstate pointer
define.INVALSET = [0x3, 0x1, 0x2]
in = "lfs2.c"
in = "lfs.c"
code = '''
// create littlefs
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// create an invalid gstate
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_fs_prepmove(&lfs2, 1, (lfs2_block_t [2]){
lfs_initcommon(&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});
lfs2_dir_commit(&lfs2, &mdir, NULL, 0) => 0;
lfs2_deinit(&lfs2) => 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
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "should_fail") => LFS2_ERR_CORRUPT;
lfs2_unmount(&lfs2) => 0;
lfs_mountcfg(&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 = "lfs2.c"
in = "lfs.c"
code = '''
// create littlefs
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// change tail-pointer to point to ourself
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8),
(lfs2_block_t[2]){0, 1}})) => 0;
lfs2_deinit(&lfs2) => 0;
lfs_initcommon(&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
lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT;
lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''
[[case]] # metadata-pair threaded-list 2-length loop test
in = "lfs2.c"
in = "lfs.c"
code = '''
// create littlefs with child dir
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
// find child
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_block_t pair[2];
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x7ff, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs2_pair_fromle32(pair);
lfs_initcommon(&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
lfs2_dir_fetch(&lfs2, &mdir, pair) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8),
(lfs2_block_t[2]){0, 1}})) => 0;
lfs2_deinit(&lfs2) => 0;
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
lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT;
lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''
[[case]] # metadata-pair threaded-list 1-length child loop test
in = "lfs2.c"
in = "lfs.c"
code = '''
// create littlefs with child dir
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
// find child
lfs2_init(&lfs2, &cfg) => 0;
lfs2_mdir_t mdir;
lfs2_block_t pair[2];
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x7ff, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs2_pair_fromle32(pair);
lfs_initcommon(&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
lfs2_dir_fetch(&lfs2, &mdir, pair) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0;
lfs2_deinit(&lfs2) => 0;
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
lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT;
lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''

View File

@@ -1,49 +1,49 @@
[[case]] # test running a filesystem to exhaustion
define.LFS2_ERASE_CYCLES = 10
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2'
define.LFS2_BADBLOCK_BEHAVIOR = [
'LFS2_TESTBD_BADBLOCK_PROGERROR',
'LFS2_TESTBD_BADBLOCK_ERASEERROR',
'LFS2_TESTBD_BADBLOCK_READERROR',
'LFS2_TESTBD_BADBLOCK_PROGNOOP',
'LFS2_TESTBD_BADBLOCK_ERASENOOP',
define.LFS_ERASE_CYCLES = 10
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
define.LFS_BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
]
define.FILES = 10
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "roadrunner") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "roadrunner") => 0;
lfs_unmount(&lfs) => 0;
uint32_t cycle = 0;
while (true) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
sprintf(path, "roadrunner/test%d", i);
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || 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;
}
}
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted;
}
}
@@ -54,78 +54,78 @@ code = '''
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
char r;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
lfs_file_read(&lfs, &file, &r, 1) => 1;
assert(r == c);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
sprintf(path, "roadrunner/test%d", i);
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
LFS2_WARN("completed %d cycles", cycle);
LFS_WARN("completed %d cycles", cycle);
'''
[[case]] # test running a filesystem to exhaustion
# which also requires expanding superblocks
define.LFS2_ERASE_CYCLES = 10
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2'
define.LFS2_BADBLOCK_BEHAVIOR = [
'LFS2_TESTBD_BADBLOCK_PROGERROR',
'LFS2_TESTBD_BADBLOCK_ERASEERROR',
'LFS2_TESTBD_BADBLOCK_READERROR',
'LFS2_TESTBD_BADBLOCK_PROGNOOP',
'LFS2_TESTBD_BADBLOCK_ERASENOOP',
define.LFS_ERASE_CYCLES = 10
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
define.LFS_BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
]
define.FILES = 10
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
uint32_t cycle = 0;
while (true) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
sprintf(path, "test%d", i);
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || 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;
}
}
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted;
}
}
@@ -136,32 +136,32 @@ code = '''
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
char r;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
lfs_file_read(&lfs, &file, &r, 1) => 1;
assert(r == c);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
sprintf(path, "test%d", i);
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
LFS2_WARN("completed %d cycles", cycle);
LFS_WARN("completed %d cycles", cycle);
'''
# These are a sort of high-level litmus test for wear-leveling. One definition
@@ -170,53 +170,53 @@ exhausted:
# check for.
[[case]] # wear-level test running a filesystem to exhaustion
define.LFS2_ERASE_CYCLES = 20
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2'
define.LFS_ERASE_CYCLES = 20
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
define.FILES = 10
code = '''
uint32_t run_cycles[2];
const uint32_t run_block_count[2] = {LFS2_BLOCK_COUNT/2, LFS2_BLOCK_COUNT};
const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
for (int run = 0; run < 2; run++) {
for (lfs2_block_t b = 0; b < LFS2_BLOCK_COUNT; b++) {
lfs2_testbd_setwear(&cfg, b,
(b < run_block_count[run]) ? 0 : LFS2_ERASE_CYCLES) => 0;
for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_setwear(&bd, b,
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
}
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "roadrunner") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "roadrunner") => 0;
lfs_unmount(&lfs) => 0;
uint32_t cycle = 0;
while (true) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
sprintf(path, "roadrunner/test%d", i);
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || 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;
}
}
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted;
}
}
@@ -227,85 +227,85 @@ code = '''
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
char r;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
lfs_file_read(&lfs, &file, &r, 1) => 1;
assert(r == c);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
sprintf(path, "roadrunner/test%d", i);
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
run_cycles[run] = cycle;
LFS2_WARN("completed %d blocks %d cycles",
LFS_WARN("completed %d blocks %d cycles",
run_block_count[run], run_cycles[run]);
}
// check we increased the lifetime by 2x with ~10% error
LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
'''
[[case]] # wear-level test + expanding superblock
define.LFS2_ERASE_CYCLES = 20
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2'
define.LFS_ERASE_CYCLES = 20
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
define.FILES = 10
code = '''
uint32_t run_cycles[2];
const uint32_t run_block_count[2] = {LFS2_BLOCK_COUNT/2, LFS2_BLOCK_COUNT};
const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
for (int run = 0; run < 2; run++) {
for (lfs2_block_t b = 0; b < LFS2_BLOCK_COUNT; b++) {
lfs2_testbd_setwear(&cfg, b,
(b < run_block_count[run]) ? 0 : LFS2_ERASE_CYCLES) => 0;
for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_setwear(&bd, b,
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
}
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
uint32_t cycle = 0;
while (true) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
sprintf(path, "test%d", i);
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || 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;
}
}
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted;
}
}
@@ -316,81 +316,81 @@ code = '''
srand(cycle * i);
size = 1 << ((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
char r;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
lfs_file_read(&lfs, &file, &r, 1) => 1;
assert(r == c);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
sprintf(path, "test%d", i);
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
run_cycles[run] = cycle;
LFS2_WARN("completed %d blocks %d cycles",
LFS_WARN("completed %d blocks %d cycles",
run_block_count[run], run_cycles[run]);
}
// check we increased the lifetime by 2x with ~10% error
LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
'''
[[case]] # test that we wear blocks roughly evenly
define.LFS2_ERASE_CYCLES = 0xffffffff
define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS2_BLOCK_CYCLES = [5, 4, 3, 2, 1]
define.LFS_ERASE_CYCLES = 0xffffffff
define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1]
define.CYCLES = 100
define.FILES = 10
if = 'LFS2_BLOCK_CYCLES < CYCLES/10'
if = 'LFS_BLOCK_CYCLES < CYCLES/10'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "roadrunner") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "roadrunner") => 0;
lfs_unmount(&lfs) => 0;
uint32_t cycle = 0;
while (cycle < CYCLES) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
sprintf(path, "roadrunner/test%d", i);
srand(cycle * i);
size = 1 << 4; //((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || 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;
}
}
err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
goto exhausted;
}
}
@@ -401,40 +401,40 @@ code = '''
srand(cycle * i);
size = 1 << 4; //((rand() % 10)+2);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
char c = 'a' + (rand() % 26);
char r;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
lfs_file_read(&lfs, &file, &r, 1) => 1;
assert(r == c);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
sprintf(path, "roadrunner/test%d", i);
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
LFS2_WARN("completed %d cycles", cycle);
LFS_WARN("completed %d cycles", cycle);
// check the wear on our block device
lfs2_testbd_wear_t minwear = -1;
lfs2_testbd_wear_t totalwear = 0;
lfs2_testbd_wear_t maxwear = 0;
lfs_testbd_wear_t minwear = -1;
lfs_testbd_wear_t totalwear = 0;
lfs_testbd_wear_t maxwear = 0;
// skip 0 and 1 as superblock movement is intentionally avoided
for (lfs2_block_t b = 2; b < LFS2_BLOCK_COUNT; b++) {
lfs2_testbd_wear_t wear = lfs2_testbd_getwear(&cfg, b);
for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_wear_t wear = lfs_testbd_getwear(&bd, b);
printf("%08x: wear %d\n", b, wear);
assert(wear >= 0);
if (wear < minwear) {
@@ -445,21 +445,21 @@ exhausted:
}
totalwear += wear;
}
lfs2_testbd_wear_t avgwear = totalwear / LFS2_BLOCK_COUNT;
LFS2_WARN("max wear: %d cycles", maxwear);
LFS2_WARN("avg wear: %d cycles", totalwear / LFS2_BLOCK_COUNT);
LFS2_WARN("min wear: %d cycles", minwear);
lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT;
LFS_WARN("max wear: %d cycles", maxwear);
LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT);
LFS_WARN("min wear: %d cycles", minwear);
// find standard deviation^2
lfs2_testbd_wear_t dev2 = 0;
for (lfs2_block_t b = 2; b < LFS2_BLOCK_COUNT; b++) {
lfs2_testbd_wear_t wear = lfs2_testbd_getwear(&cfg, b);
lfs_testbd_wear_t dev2 = 0;
for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_wear_t wear = lfs_testbd_getwear(&bd, b);
assert(wear >= 0);
lfs2_testbd_swear_t diff = wear - avgwear;
lfs_testbd_swear_t diff = wear - avgwear;
dev2 += diff*diff;
}
dev2 /= totalwear;
LFS2_WARN("std dev^2: %d", dev2);
LFS_WARN("std dev^2: %d", dev2);
assert(dev2 < 8);
'''

View File

@@ -1,60 +1,60 @@
[[case]] # simple file test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "hello",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
size = strlen("Hello World!")+1;
strcpy((char*)buffer, "Hello World!");
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(strcmp((char*)buffer, "Hello World!") == 0);
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # larger files
define.SIZE = [32, 8192, 262144, 0, 7, 8193]
define.CHUNKSIZE = [31, 16, 33, 1, 1023]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// write
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
srand(1);
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// read
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE;
srand(1);
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # rewriting files
@@ -62,81 +62,81 @@ define.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
define.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
define.CHUNKSIZE = [31, 16, 1]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// write
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
srand(1);
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// read
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1;
srand(1);
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// rewrite
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY) => 0;
srand(2);
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// read
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => lfs2_max(SIZE1, SIZE2);
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => lfs_max(SIZE1, SIZE2);
srand(2);
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
if (SIZE1 > SIZE2) {
srand(1);
for (lfs2_size_t b = 0; b < SIZE2; b++) {
for (lfs_size_t b = 0; b < SIZE2; b++) {
rand();
}
for (lfs2_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # appending files
@@ -144,76 +144,76 @@ define.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
define.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
define.CHUNKSIZE = [31, 16, 1]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// write
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
srand(1);
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// read
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1;
srand(1);
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// append
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_APPEND) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_APPEND) => 0;
srand(2);
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// read
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1 + SIZE2;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1 + SIZE2;
srand(1);
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
srand(2);
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # truncating files
@@ -221,68 +221,68 @@ define.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
define.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
define.CHUNKSIZE = [31, 16, 1]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// write
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
srand(1);
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// read
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1;
srand(1);
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// truncate
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
srand(2);
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// read
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE2;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE2;
srand(2);
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant file writing
@@ -290,197 +290,197 @@ define.SIZE = [32, 0, 7, 2049]
define.CHUNKSIZE = [31, 16, 65]
reentrant = true
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY);
assert(err == LFS2_ERR_NOENT || err == 0);
err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY);
assert(err == LFS_ERR_NOENT || err == 0);
if (err == 0) {
// can only be 0 (new file) or full size
size = lfs2_file_size(&lfs2, &file);
size = lfs_file_size(&lfs, &file);
assert(size == 0 || size == SIZE);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
// write
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_CREAT) => 0;
srand(1);
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE;
srand(1);
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant file writing with syncs
define = [
# append (O(n))
{MODE='LFS2_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]},
{MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]},
# truncate (O(n^2))
{MODE='LFS2_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]},
{MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]},
# rewrite (O(n^2))
{MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]},
]
reentrant = true
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY);
assert(err == LFS2_ERR_NOENT || err == 0);
err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY);
assert(err == LFS_ERR_NOENT || err == 0);
if (err == 0) {
// with syncs we could be any size, but it at least must be valid data
size = lfs2_file_size(&lfs2, &file);
size = lfs_file_size(&lfs, &file);
assert(size <= SIZE);
srand(1);
for (lfs2_size_t i = 0; i < size; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, size-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < size; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, size-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
// write
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | MODE) => 0;
size = lfs2_file_size(&lfs2, &file);
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | MODE) => 0;
size = lfs_file_size(&lfs, &file);
assert(size <= SIZE);
srand(1);
lfs2_size_t skip = (MODE == LFS2_O_APPEND) ? size : 0;
for (lfs2_size_t b = 0; b < skip; b++) {
lfs_size_t skip = (MODE == LFS_O_APPEND) ? size : 0;
for (lfs_size_t b = 0; b < skip; b++) {
rand();
}
for (lfs2_size_t i = skip; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = skip; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs_file_sync(&lfs, &file) => 0;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// read
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE;
srand(1);
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (rand() & 0xff));
}
}
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # many files
define.N = 300
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// create N files of 7 bytes
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 0; i < N; i++) {
sprintf(path, "file_%03d", i);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
char wbuffer[1024];
size = 7;
snprintf(wbuffer, size, "Hi %03d", i);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
char rbuffer[1024];
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
assert(strcmp(rbuffer, wbuffer) == 0);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # many files with power cycle
define.N = 300
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// create N files of 7 bytes
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 0; i < N; i++) {
sprintf(path, "file_%03d", i);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
char wbuffer[1024];
size = 7;
snprintf(wbuffer, size, "Hi %03d", i);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
char rbuffer[1024];
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
assert(strcmp(rbuffer, wbuffer) == 0);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # many files with power loss
define.N = 300
reentrant = true
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
// create N files of 7 bytes
for (int i = 0; i < N; i++) {
sprintf(path, "file_%03d", i);
err = lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT);
err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT);
char wbuffer[1024];
size = 7;
snprintf(wbuffer, size, "Hi %03d", i);
if ((lfs2_size_t)lfs2_file_size(&lfs2, &file) != size) {
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
if ((lfs_size_t)lfs_file_size(&lfs, &file) != size) {
lfs_file_write(&lfs, &file, wbuffer, size) => size;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
char rbuffer[1024];
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
assert(strcmp(rbuffer, wbuffer) == 0);
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -3,60 +3,60 @@
define.SIZE = [10, 100]
define.FILES = [4, 10, 26]
code = '''
lfs2_file_t files[FILES];
lfs_file_t files[FILES];
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int j = 0; j < FILES; j++) {
sprintf(path, "%c", alphas[j]);
lfs2_file_open(&lfs2, &files[j], path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_file_open(&lfs, &files[j], path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
}
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < FILES; j++) {
lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1;
lfs_file_write(&lfs, &files[j], &alphas[j], 1) => 1;
}
}
for (int j = 0; j < FILES; j++) {
lfs2_file_close(&lfs2, &files[j]);
lfs_file_close(&lfs, &files[j]);
}
lfs2_dir_open(&lfs2, &dir, "/") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
for (int j = 0; j < FILES; j++) {
sprintf(path, "%c", alphas[j]);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, path) == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int j = 0; j < FILES; j++) {
sprintf(path, "%c", alphas[j]);
lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0;
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < FILES; j++) {
lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1;
lfs_file_read(&lfs, &files[j], buffer, 1) => 1;
assert(buffer[0] == alphas[j]);
}
}
for (int j = 0; j < FILES; j++) {
lfs2_file_close(&lfs2, &files[j]);
lfs_file_close(&lfs, &files[j]);
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # interspersed remove file test
@@ -64,112 +64,112 @@ define.SIZE = [10, 100]
define.FILES = [4, 10, 26]
code = '''
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int j = 0; j < FILES; j++) {
sprintf(path, "%c", alphas[j]);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (int i = 0; i < SIZE; i++) {
lfs2_file_write(&lfs2, &file, &alphas[j], 1) => 1;
lfs_file_write(&lfs, &file, &alphas[j], 1) => 1;
}
lfs2_file_close(&lfs2, &file);
lfs_file_close(&lfs, &file);
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "zzz", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (int j = 0; j < FILES; j++) {
lfs2_file_write(&lfs2, &file, (const void*)"~", 1) => 1;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_write(&lfs, &file, (const void*)"~", 1) => 1;
lfs_file_sync(&lfs, &file) => 0;
sprintf(path, "%c", alphas[j]);
lfs2_remove(&lfs2, path) => 0;
lfs_remove(&lfs, path) => 0;
}
lfs2_file_close(&lfs2, &file);
lfs_file_close(&lfs, &file);
lfs2_dir_open(&lfs2, &dir, "/") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, "zzz") == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
assert(info.size == FILES);
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file, "zzz", LFS_O_RDONLY) => 0;
for (int i = 0; i < FILES; i++) {
lfs2_file_read(&lfs2, &file, buffer, 1) => 1;
lfs_file_read(&lfs, &file, buffer, 1) => 1;
assert(buffer[0] == '~');
}
lfs2_file_close(&lfs2, &file);
lfs_file_close(&lfs, &file);
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # remove inconveniently test
define.SIZE = [10, 100]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_t files[3];
lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &files[1], "f", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &files[2], "g", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_t files[3];
lfs_file_open(&lfs, &files[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &files[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &files[2], "g", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (int i = 0; i < SIZE/2; i++) {
lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1;
lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1;
lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1;
lfs_file_write(&lfs, &files[0], (const void*)"e", 1) => 1;
lfs_file_write(&lfs, &files[1], (const void*)"f", 1) => 1;
lfs_file_write(&lfs, &files[2], (const void*)"g", 1) => 1;
}
lfs2_remove(&lfs2, "f") => 0;
lfs_remove(&lfs, "f") => 0;
for (int i = 0; i < SIZE/2; i++) {
lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1;
lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1;
lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1;
lfs_file_write(&lfs, &files[0], (const void*)"e", 1) => 1;
lfs_file_write(&lfs, &files[1], (const void*)"f", 1) => 1;
lfs_file_write(&lfs, &files[2], (const void*)"g", 1) => 1;
}
lfs2_file_close(&lfs2, &files[0]);
lfs2_file_close(&lfs2, &files[1]);
lfs2_file_close(&lfs2, &files[2]);
lfs_file_close(&lfs, &files[0]);
lfs_file_close(&lfs, &files[1]);
lfs_file_close(&lfs, &files[2]);
lfs2_dir_open(&lfs2, &dir, "/") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, "e") == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, "g") == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &files[1], "g", LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &files[0], "e", LFS_O_RDONLY) => 0;
lfs_file_open(&lfs, &files[1], "g", LFS_O_RDONLY) => 0;
for (int i = 0; i < SIZE; i++) {
lfs2_file_read(&lfs2, &files[0], buffer, 1) => 1;
lfs_file_read(&lfs, &files[0], buffer, 1) => 1;
assert(buffer[0] == 'e');
lfs2_file_read(&lfs2, &files[1], buffer, 1) => 1;
lfs_file_read(&lfs, &files[1], buffer, 1) => 1;
assert(buffer[0] == 'g');
}
lfs2_file_close(&lfs2, &files[0]);
lfs2_file_close(&lfs2, &files[1]);
lfs_file_close(&lfs, &files[0]);
lfs_file_close(&lfs, &files[1]);
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant interspersed file test
@@ -177,68 +177,68 @@ define.SIZE = [10, 100]
define.FILES = [4, 10, 26]
reentrant = true
code = '''
lfs2_file_t files[FILES];
lfs_file_t files[FILES];
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
for (int j = 0; j < FILES; j++) {
sprintf(path, "%c", alphas[j]);
lfs2_file_open(&lfs2, &files[j], path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_file_open(&lfs, &files[j], path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
}
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < FILES; j++) {
size = lfs2_file_size(&lfs2, &files[j]);
size = lfs_file_size(&lfs, &files[j]);
assert((int)size >= 0);
if ((int)size <= i) {
lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1;
lfs2_file_sync(&lfs2, &files[j]) => 0;
lfs_file_write(&lfs, &files[j], &alphas[j], 1) => 1;
lfs_file_sync(&lfs, &files[j]) => 0;
}
}
}
for (int j = 0; j < FILES; j++) {
lfs2_file_close(&lfs2, &files[j]);
lfs_file_close(&lfs, &files[j]);
}
lfs2_dir_open(&lfs2, &dir, "/") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
for (int j = 0; j < FILES; j++) {
sprintf(path, "%c", alphas[j]);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(strcmp(info.name, path) == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
}
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int j = 0; j < FILES; j++) {
sprintf(path, "%c", alphas[j]);
lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0;
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < FILES; j++) {
lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1;
lfs_file_read(&lfs, &files[j], buffer, 1) => 1;
assert(buffer[0] == alphas[j]);
}
}
for (int j = 0; j < FILES; j++) {
lfs2_file_close(&lfs2, &files[j]);
lfs_file_close(&lfs, &files[j]);
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +1,75 @@
[[case]] # orphan test
in = "lfs2.c"
if = 'LFS2_PROG_SIZE <= 0x3fe' # only works with one crc per commit
in = "lfs.c"
if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "parent") => 0;
lfs2_mkdir(&lfs2, "parent/orphan") => 0;
lfs2_mkdir(&lfs2, "parent/child") => 0;
lfs2_remove(&lfs2, "parent/orphan") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "parent") => 0;
lfs_mkdir(&lfs, "parent/orphan") => 0;
lfs_mkdir(&lfs, "parent/child") => 0;
lfs_remove(&lfs, "parent/orphan") => 0;
lfs_unmount(&lfs) => 0;
// corrupt the child's most recent commit, this should be the update
// to the linked-list entry, which should orphan the orphan. Note this
// makes a lot of assumptions about the remove operation.
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir, "parent/child") => 0;
lfs2_block_t block = dir.m.pair[0];
lfs2_dir_close(&lfs2, &dir) => 0;
lfs2_unmount(&lfs2) => 0;
uint8_t bbuffer[LFS2_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS2_BLOCK_SIZE) => 0;
int off = LFS2_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS2_ERASE_VALUE) {
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "parent/child") => 0;
lfs_block_t block = dir.m.pair[0];
lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE];
lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1;
}
memset(&bbuffer[off-3], LFS2_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS2_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0;
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
lfs_testbd_erase(&bd, block) => 0;
lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
lfs_testbd_sync(&bd) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
lfs2_unmount(&lfs2) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_fs_size(&lfs) => 8;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_fs_size(&lfs) => 8;
// this mkdir should both create a dir and deorphan, so size
// should be unchanged
lfs2_mkdir(&lfs2, "parent/otherchild") => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_stat(&lfs2, "parent/otherchild", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
lfs2_unmount(&lfs2) => 0;
lfs_mkdir(&lfs, "parent/otherchild") => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_stat(&lfs, "parent/otherchild", &info) => 0;
lfs_fs_size(&lfs) => 8;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_stat(&lfs2, "parent/otherchild", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
lfs2_unmount(&lfs2) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_stat(&lfs, "parent/otherchild", &info) => 0;
lfs_fs_size(&lfs) => 8;
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant testing for orphans, basically just spam mkdir/remove
reentrant = true
# TODO fix this case, caused by non-DAG trees
if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)'
if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
define = [
{FILES=6, DEPTH=1, CYCLES=20},
{FILES=26, DEPTH=1, CYCLES=20},
{FILES=3, DEPTH=3, CYCLES=20},
]
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
srand(1);
@@ -82,39 +82,39 @@ code = '''
}
// if it does not exist, we create it, else we destroy
int res = lfs2_stat(&lfs2, full_path, &info);
if (res == LFS2_ERR_NOENT) {
int res = lfs_stat(&lfs, full_path, &info);
if (res == LFS_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (int d = 0; d < DEPTH; d++) {
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
err = lfs_mkdir(&lfs, path);
assert(!err || err == LFS_ERR_EXIST);
}
for (int d = 0; d < DEPTH; d++) {
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
}
} else {
// is valid dir?
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
// try to delete path in reverse order, ignore if dir is not empty
for (int d = DEPTH-1; d >= 0; d--) {
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
err = lfs_remove(&lfs, path);
assert(!err || err == LFS_ERR_NOTEMPTY);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
}
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -1,293 +1,293 @@
[[case]] # simple path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "tea") => 0;
lfs2_mkdir(&lfs2, "tea/hottea") => 0;
lfs2_mkdir(&lfs2, "tea/warmtea") => 0;
lfs2_mkdir(&lfs2, "tea/coldtea") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs2_stat(&lfs2, "tea/hottea", &info) => 0;
lfs_stat(&lfs, "tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "/tea/hottea", &info) => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_mkdir(&lfs2, "/milk") => 0;
lfs2_stat(&lfs2, "/milk", &info) => 0;
lfs_mkdir(&lfs, "/milk") => 0;
lfs_stat(&lfs, "/milk", &info) => 0;
assert(strcmp(info.name, "milk") == 0);
lfs2_stat(&lfs2, "milk", &info) => 0;
lfs_stat(&lfs, "milk", &info) => 0;
assert(strcmp(info.name, "milk") == 0);
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # redundant slashes
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "tea") => 0;
lfs2_mkdir(&lfs2, "tea/hottea") => 0;
lfs2_mkdir(&lfs2, "tea/warmtea") => 0;
lfs2_mkdir(&lfs2, "tea/coldtea") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs2_stat(&lfs2, "/tea/hottea", &info) => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "//tea//hottea", &info) => 0;
lfs_stat(&lfs, "//tea//hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "///tea///hottea", &info) => 0;
lfs_stat(&lfs, "///tea///hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_mkdir(&lfs2, "////milk") => 0;
lfs2_stat(&lfs2, "////milk", &info) => 0;
lfs_mkdir(&lfs, "////milk") => 0;
lfs_stat(&lfs, "////milk", &info) => 0;
assert(strcmp(info.name, "milk") == 0);
lfs2_stat(&lfs2, "milk", &info) => 0;
lfs_stat(&lfs, "milk", &info) => 0;
assert(strcmp(info.name, "milk") == 0);
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # dot path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "tea") => 0;
lfs2_mkdir(&lfs2, "tea/hottea") => 0;
lfs2_mkdir(&lfs2, "tea/warmtea") => 0;
lfs2_mkdir(&lfs2, "tea/coldtea") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs2_stat(&lfs2, "./tea/hottea", &info) => 0;
lfs_stat(&lfs, "./tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "/./tea/hottea", &info) => 0;
lfs_stat(&lfs, "/./tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "/././tea/hottea", &info) => 0;
lfs_stat(&lfs, "/././tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "/./tea/./hottea", &info) => 0;
lfs_stat(&lfs, "/./tea/./hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_mkdir(&lfs2, "/./milk") => 0;
lfs2_stat(&lfs2, "/./milk", &info) => 0;
lfs_mkdir(&lfs, "/./milk") => 0;
lfs_stat(&lfs, "/./milk", &info) => 0;
assert(strcmp(info.name, "milk") == 0);
lfs2_stat(&lfs2, "milk", &info) => 0;
lfs_stat(&lfs, "milk", &info) => 0;
assert(strcmp(info.name, "milk") == 0);
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # dot dot path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "tea") => 0;
lfs2_mkdir(&lfs2, "tea/hottea") => 0;
lfs2_mkdir(&lfs2, "tea/warmtea") => 0;
lfs2_mkdir(&lfs2, "tea/coldtea") => 0;
lfs2_mkdir(&lfs2, "coffee") => 0;
lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
lfs2_stat(&lfs2, "coffee/../tea/hottea", &info) => 0;
lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "tea/coldtea/../hottea", &info) => 0;
lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "coffee/coldcoffee/../../tea/hottea", &info) => 0;
lfs_stat(&lfs, "coffee/coldcoffee/../../tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "coffee/../coffee/../tea/hottea", &info) => 0;
lfs_stat(&lfs, "coffee/../coffee/../tea/hottea", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_mkdir(&lfs2, "coffee/../milk") => 0;
lfs2_stat(&lfs2, "coffee/../milk", &info) => 0;
lfs_mkdir(&lfs, "coffee/../milk") => 0;
lfs_stat(&lfs, "coffee/../milk", &info) => 0;
strcmp(info.name, "milk") => 0;
lfs2_stat(&lfs2, "milk", &info) => 0;
lfs_stat(&lfs, "milk", &info) => 0;
strcmp(info.name, "milk") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # trailing dot path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "tea") => 0;
lfs2_mkdir(&lfs2, "tea/hottea") => 0;
lfs2_mkdir(&lfs2, "tea/warmtea") => 0;
lfs2_mkdir(&lfs2, "tea/coldtea") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs2_stat(&lfs2, "tea/hottea/", &info) => 0;
lfs_stat(&lfs, "tea/hottea/", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "tea/hottea/.", &info) => 0;
lfs_stat(&lfs, "tea/hottea/.", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "tea/hottea/./.", &info) => 0;
lfs_stat(&lfs, "tea/hottea/./.", &info) => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs2_stat(&lfs2, "tea/hottea/..", &info) => 0;
lfs_stat(&lfs, "tea/hottea/..", &info) => 0;
assert(strcmp(info.name, "tea") == 0);
lfs2_stat(&lfs2, "tea/hottea/../.", &info) => 0;
lfs_stat(&lfs, "tea/hottea/../.", &info) => 0;
assert(strcmp(info.name, "tea") == 0);
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # leading dot path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, ".milk") => 0;
lfs2_stat(&lfs2, ".milk", &info) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, ".milk") => 0;
lfs_stat(&lfs, ".milk", &info) => 0;
strcmp(info.name, ".milk") => 0;
lfs2_stat(&lfs2, "tea/.././.milk", &info) => 0;
lfs_stat(&lfs, "tea/.././.milk", &info) => 0;
strcmp(info.name, ".milk") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # root dot dot path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "tea") => 0;
lfs2_mkdir(&lfs2, "tea/hottea") => 0;
lfs2_mkdir(&lfs2, "tea/warmtea") => 0;
lfs2_mkdir(&lfs2, "tea/coldtea") => 0;
lfs2_mkdir(&lfs2, "coffee") => 0;
lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
lfs2_stat(&lfs2, "coffee/../../../../../../tea/hottea", &info) => 0;
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_mkdir(&lfs2, "coffee/../../../../../../milk") => 0;
lfs2_stat(&lfs2, "coffee/../../../../../../milk", &info) => 0;
lfs_mkdir(&lfs, "coffee/../../../../../../milk") => 0;
lfs_stat(&lfs, "coffee/../../../../../../milk", &info) => 0;
strcmp(info.name, "milk") => 0;
lfs2_stat(&lfs2, "milk", &info) => 0;
lfs_stat(&lfs, "milk", &info) => 0;
strcmp(info.name, "milk") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # invalid path tests
code = '''
lfs2_format(&lfs2, &cfg);
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "dirt", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "dirt/ground", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "dirt/ground/earth", &info) => LFS2_ERR_NOENT;
lfs_formatcfg(&lfs, &cfg);
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "dirt", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "dirt/ground", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "dirt/ground/earth", &info) => LFS_ERR_NOENT;
lfs2_remove(&lfs2, "dirt") => LFS2_ERR_NOENT;
lfs2_remove(&lfs2, "dirt/ground") => LFS2_ERR_NOENT;
lfs2_remove(&lfs2, "dirt/ground/earth") => LFS2_ERR_NOENT;
lfs_remove(&lfs, "dirt") => LFS_ERR_NOENT;
lfs_remove(&lfs, "dirt/ground") => LFS_ERR_NOENT;
lfs_remove(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT;
lfs2_mkdir(&lfs2, "dirt/ground") => LFS2_ERR_NOENT;
lfs2_file_open(&lfs2, &file, "dirt/ground", LFS2_O_WRONLY | LFS2_O_CREAT)
=> LFS2_ERR_NOENT;
lfs2_mkdir(&lfs2, "dirt/ground/earth") => LFS2_ERR_NOENT;
lfs2_file_open(&lfs2, &file, "dirt/ground/earth", LFS2_O_WRONLY | LFS2_O_CREAT)
=> LFS2_ERR_NOENT;
lfs2_unmount(&lfs2) => 0;
lfs_mkdir(&lfs, "dirt/ground") => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file, "dirt/ground", LFS_O_WRONLY | LFS_O_CREAT)
=> LFS_ERR_NOENT;
lfs_mkdir(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file, "dirt/ground/earth", LFS_O_WRONLY | LFS_O_CREAT)
=> LFS_ERR_NOENT;
lfs_unmount(&lfs) => 0;
'''
[[case]] # root operations
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "/", &info) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "/", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
lfs2_mkdir(&lfs2, "/") => LFS2_ERR_EXIST;
lfs2_file_open(&lfs2, &file, "/", LFS2_O_WRONLY | LFS2_O_CREAT)
=> LFS2_ERR_ISDIR;
lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST;
lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY | LFS_O_CREAT)
=> LFS_ERR_ISDIR;
lfs2_remove(&lfs2, "/") => LFS2_ERR_INVAL;
lfs2_unmount(&lfs2) => 0;
lfs_remove(&lfs, "/") => LFS_ERR_INVAL;
lfs_unmount(&lfs) => 0;
'''
[[case]] # root representations
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "/", &info) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "/", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_stat(&lfs2, "", &info) => 0;
assert(info.type == LFS_TYPE_DIR);
lfs_stat(&lfs, "", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_stat(&lfs2, ".", &info) => 0;
assert(info.type == LFS_TYPE_DIR);
lfs_stat(&lfs, ".", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_stat(&lfs2, "..", &info) => 0;
assert(info.type == LFS_TYPE_DIR);
lfs_stat(&lfs, "..", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_stat(&lfs2, "//", &info) => 0;
assert(info.type == LFS_TYPE_DIR);
lfs_stat(&lfs, "//", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_stat(&lfs2, "./", &info) => 0;
assert(info.type == LFS_TYPE_DIR);
lfs_stat(&lfs, "./", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_unmount(&lfs2) => 0;
assert(info.type == LFS_TYPE_DIR);
lfs_unmount(&lfs) => 0;
'''
[[case]] # superblock conflict test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "littlefs", &info) => LFS2_ERR_NOENT;
lfs2_remove(&lfs2, "littlefs") => LFS2_ERR_NOENT;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "littlefs", &info) => LFS_ERR_NOENT;
lfs_remove(&lfs, "littlefs") => LFS_ERR_NOENT;
lfs2_mkdir(&lfs2, "littlefs") => 0;
lfs2_stat(&lfs2, "littlefs", &info) => 0;
lfs_mkdir(&lfs, "littlefs") => 0;
lfs_stat(&lfs, "littlefs", &info) => 0;
assert(strcmp(info.name, "littlefs") == 0);
assert(info.type == LFS2_TYPE_DIR);
lfs2_remove(&lfs2, "littlefs") => 0;
lfs2_stat(&lfs2, "littlefs", &info) => LFS2_ERR_NOENT;
lfs2_unmount(&lfs2) => 0;
assert(info.type == LFS_TYPE_DIR);
lfs_remove(&lfs, "littlefs") => 0;
lfs_stat(&lfs, "littlefs", &info) => LFS_ERR_NOENT;
lfs_unmount(&lfs) => 0;
'''
[[case]] # max path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "coffee") => 0;
lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
memset(path, 'w', LFS2_NAME_MAX+1);
path[LFS2_NAME_MAX+1] = '\0';
lfs2_mkdir(&lfs2, path) => LFS2_ERR_NAMETOOLONG;
lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT)
=> LFS2_ERR_NAMETOOLONG;
memset(path, 'w', LFS_NAME_MAX+1);
path[LFS_NAME_MAX+1] = '\0';
lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT)
=> LFS_ERR_NAMETOOLONG;
memcpy(path, "coffee/", strlen("coffee/"));
memset(path+strlen("coffee/"), 'w', LFS2_NAME_MAX+1);
path[strlen("coffee/")+LFS2_NAME_MAX+1] = '\0';
lfs2_mkdir(&lfs2, path) => LFS2_ERR_NAMETOOLONG;
lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT)
=> LFS2_ERR_NAMETOOLONG;
lfs2_unmount(&lfs2) => 0;
memset(path+strlen("coffee/"), 'w', LFS_NAME_MAX+1);
path[strlen("coffee/")+LFS_NAME_MAX+1] = '\0';
lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT)
=> LFS_ERR_NAMETOOLONG;
lfs_unmount(&lfs) => 0;
'''
[[case]] # really big path test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "coffee") => 0;
lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
memset(path, 'w', LFS2_NAME_MAX);
path[LFS2_NAME_MAX] = '\0';
lfs2_mkdir(&lfs2, path) => 0;
lfs2_remove(&lfs2, path) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_remove(&lfs2, path) => 0;
memset(path, 'w', LFS_NAME_MAX);
path[LFS_NAME_MAX] = '\0';
lfs_mkdir(&lfs, path) => 0;
lfs_remove(&lfs, path) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_remove(&lfs, path) => 0;
memcpy(path, "coffee/", strlen("coffee/"));
memset(path+strlen("coffee/"), 'w', LFS2_NAME_MAX);
path[strlen("coffee/")+LFS2_NAME_MAX] = '\0';
lfs2_mkdir(&lfs2, path) => 0;
lfs2_remove(&lfs2, path) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_remove(&lfs2, path) => 0;
lfs2_unmount(&lfs2) => 0;
memset(path+strlen("coffee/"), 'w', LFS_NAME_MAX);
path[strlen("coffee/")+LFS_NAME_MAX] = '\0';
lfs_mkdir(&lfs, path) => 0;
lfs_remove(&lfs, path) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_remove(&lfs, path) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -2,39 +2,39 @@
[[case]] # dangling split dir test
define.ITERATIONS = 20
define.COUNT = 10
define.LFS2_BLOCK_CYCLES = [8, 1]
define.LFS_BLOCK_CYCLES = [8, 1]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// fill up filesystem so only ~16 blocks are left
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
memset(buffer, 0, 512);
while (LFS2_BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) {
lfs2_file_write(&lfs2, &file, buffer, 512) => 512;
while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
lfs_file_write(&lfs, &file, buffer, 512) => 512;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// make a child dir to use in bounded space
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int j = 0; j < ITERATIONS; j++) {
for (int i = 0; i < COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_dir_open(&lfs2, &dir, "child") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
}
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
if (j == ITERATIONS-1) {
break;
@@ -42,105 +42,105 @@ code = '''
for (int i = 0; i < COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs2_remove(&lfs2, path) => 0;
lfs_remove(&lfs, path) => 0;
}
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir, "child") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
}
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int i = 0; i < COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs2_remove(&lfs2, path) => 0;
lfs_remove(&lfs, path) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # outdated head test
define.ITERATIONS = 20
define.COUNT = 10
define.LFS2_BLOCK_CYCLES = [8, 1]
define.LFS_BLOCK_CYCLES = [8, 1]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
// fill up filesystem so only ~16 blocks are left
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
memset(buffer, 0, 512);
while (LFS2_BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) {
lfs2_file_write(&lfs2, &file, buffer, 512) => 512;
while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
lfs_file_write(&lfs, &file, buffer, 512) => 512;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
// make a child dir to use in bounded space
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int j = 0; j < ITERATIONS; j++) {
for (int i = 0; i < COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_dir_open(&lfs2, &dir, "child") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 0;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hi", 2) => 2;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs2_dir_rewind(&lfs2, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hi", 2) => 2;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs2_dir_rewind(&lfs2, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
}
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int i = 0; i < COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs2_remove(&lfs2, path) => 0;
lfs_remove(&lfs, path) => 0;
}
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant testing for relocations, this is the same as the
@@ -148,17 +148,17 @@ code = '''
# almost every tree operation needs a relocation
reentrant = true
# TODO fix this case, caused by non-DAG trees
if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)'
if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
define = [
{FILES=6, DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1},
{FILES=3, DEPTH=3, CYCLES=20, LFS2_BLOCK_CYCLES=1},
{FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
{FILES=3, DEPTH=3, CYCLES=20, LFS_BLOCK_CYCLES=1},
]
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
srand(1);
@@ -171,56 +171,56 @@ code = '''
}
// if it does not exist, we create it, else we destroy
int res = lfs2_stat(&lfs2, full_path, &info);
if (res == LFS2_ERR_NOENT) {
int res = lfs_stat(&lfs, full_path, &info);
if (res == LFS_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (int d = 0; d < DEPTH; d++) {
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
err = lfs_mkdir(&lfs, path);
assert(!err || err == LFS_ERR_EXIST);
}
for (int d = 0; d < DEPTH; d++) {
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
}
} else {
// is valid dir?
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
// try to delete path in reverse order, ignore if dir is not empty
for (int d = DEPTH-1; d >= 0; d--) {
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
err = lfs_remove(&lfs, path);
assert(!err || err == LFS_ERR_NOTEMPTY);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
}
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant testing for relocations, but now with random renames!
reentrant = true
# TODO fix this case, caused by non-DAG trees
if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)'
if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
define = [
{FILES=6, DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1},
{FILES=3, DEPTH=3, CYCLES=20, LFS2_BLOCK_CYCLES=1},
{FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
{FILES=3, DEPTH=3, CYCLES=20, LFS_BLOCK_CYCLES=1},
]
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
srand(1);
@@ -233,27 +233,27 @@ code = '''
}
// if it does not exist, we create it, else we destroy
int res = lfs2_stat(&lfs2, full_path, &info);
assert(!res || res == LFS2_ERR_NOENT);
if (res == LFS2_ERR_NOENT) {
int res = lfs_stat(&lfs, full_path, &info);
assert(!res || res == LFS_ERR_NOENT);
if (res == LFS_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (int d = 0; d < DEPTH; d++) {
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
err = lfs_mkdir(&lfs, path);
assert(!err || err == LFS_ERR_EXIST);
}
for (int d = 0; d < DEPTH; d++) {
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
}
} else {
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
// create new random path
char new_path[256];
@@ -262,17 +262,17 @@ code = '''
}
// if new path does not exist, rename, otherwise destroy
res = lfs2_stat(&lfs2, new_path, &info);
assert(!res || res == LFS2_ERR_NOENT);
if (res == LFS2_ERR_NOENT) {
res = lfs_stat(&lfs, new_path, &info);
assert(!res || res == LFS_ERR_NOENT);
if (res == LFS_ERR_NOENT) {
// stop once some dir is renamed
for (int d = 0; d < DEPTH; d++) {
strcpy(&path[2*d], &full_path[2*d]);
path[2*d+2] = '\0';
strcpy(&path[128+2*d], &new_path[2*d]);
path[128+2*d+2] = '\0';
err = lfs2_rename(&lfs2, path, path+128);
assert(!err || err == LFS2_ERR_NOTEMPTY);
err = lfs_rename(&lfs, path, path+128);
assert(!err || err == LFS_ERR_NOTEMPTY);
if (!err) {
strcpy(path, path+128);
}
@@ -281,25 +281,25 @@ code = '''
for (int d = 0; d < DEPTH; d++) {
strcpy(path, new_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
lfs_stat(&lfs, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
assert(info.type == LFS_TYPE_DIR);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
} else {
// try to delete path in reverse order,
// ignore if dir is not empty
for (int d = DEPTH-1; d >= 0; d--) {
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
err = lfs_remove(&lfs, path);
assert(!err || err == LFS_ERR_NOTEMPTY);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
}
}
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -9,63 +9,63 @@ define = [
{COUNT=4, SKIP=2},
]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("kittycatcat");
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size);
lfs_file_write(&lfs, &file, buffer, size);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY) => 0;
lfs2_soff_t pos = -1;
lfs_soff_t pos = -1;
size = strlen("kittycatcat");
for (int i = 0; i < SKIP; i++) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
pos = lfs2_file_tell(&lfs2, &file);
pos = lfs_file_tell(&lfs, &file);
}
assert(pos >= 0);
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_rewind(&lfs2, &file) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_rewind(&lfs, &file) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, size, LFS2_SEEK_CUR) => 3*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, size, LFS_SEEK_CUR) => 3*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_CUR) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_CUR) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs2_file_size(&lfs2, &file);
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size;
size = lfs_file_size(&lfs, &file);
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # simple file seek and write
@@ -78,109 +78,109 @@ define = [
{COUNT=4, SKIP=2},
]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("kittycatcat");
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size);
lfs_file_write(&lfs, &file, buffer, size);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs2_soff_t pos = -1;
lfs_soff_t pos = -1;
size = strlen("kittycatcat");
for (int i = 0; i < SKIP; i++) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
pos = lfs2_file_tell(&lfs2, &file);
pos = lfs_file_tell(&lfs, &file);
}
assert(pos >= 0);
memcpy(buffer, "doggodogdog", size);
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs2_file_rewind(&lfs2, &file) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_rewind(&lfs, &file) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs2_file_size(&lfs2, &file);
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size;
size = lfs_file_size(&lfs, &file);
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # boundary seek and writes
define.COUNT = 132
define.OFFSETS = '"{512, 1020, 513, 1021, 511, 1019, 1441}"'
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("kittycatcat");
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size);
lfs_file_write(&lfs, &file, buffer, size);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
size = strlen("hedgehoghog");
const lfs2_soff_t offsets[] = OFFSETS;
const lfs_soff_t offsets[] = OFFSETS;
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
lfs2_soff_t off = offsets[i];
lfs_soff_t off = offsets[i];
memcpy(buffer, "hedgehoghog", size);
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hedgehoghog", size) => 0;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hedgehoghog", size) => 0;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_sync(&lfs, &file) => 0;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hedgehoghog", size) => 0;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # out of bounds seek
@@ -193,116 +193,116 @@ define = [
{COUNT=4, SKIP=3},
]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("kittycatcat");
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size);
lfs_file_write(&lfs, &file, buffer, size);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
size = strlen("kittycatcat");
lfs2_file_size(&lfs2, &file) => COUNT*size;
lfs2_file_seek(&lfs2, &file, (COUNT+SKIP)*size,
LFS2_SEEK_SET) => (COUNT+SKIP)*size;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_size(&lfs, &file) => COUNT*size;
lfs_file_seek(&lfs, &file, (COUNT+SKIP)*size,
LFS_SEEK_SET) => (COUNT+SKIP)*size;
lfs_file_read(&lfs, &file, buffer, size) => 0;
memcpy(buffer, "porcupineee", size);
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, (COUNT+SKIP)*size,
LFS2_SEEK_SET) => (COUNT+SKIP)*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, (COUNT+SKIP)*size,
LFS_SEEK_SET) => (COUNT+SKIP)*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "porcupineee", size) => 0;
lfs2_file_seek(&lfs2, &file, COUNT*size,
LFS2_SEEK_SET) => COUNT*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, COUNT*size,
LFS_SEEK_SET) => COUNT*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0;
lfs2_file_seek(&lfs2, &file, -((COUNT+SKIP)*size),
LFS2_SEEK_CUR) => LFS2_ERR_INVAL;
lfs2_file_tell(&lfs2, &file) => (COUNT+1)*size;
lfs_file_seek(&lfs, &file, -((COUNT+SKIP)*size),
LFS_SEEK_CUR) => LFS_ERR_INVAL;
lfs_file_tell(&lfs, &file) => (COUNT+1)*size;
lfs2_file_seek(&lfs2, &file, -((COUNT+2*SKIP)*size),
LFS2_SEEK_END) => LFS2_ERR_INVAL;
lfs2_file_tell(&lfs2, &file) => (COUNT+1)*size;
lfs_file_seek(&lfs, &file, -((COUNT+2*SKIP)*size),
LFS_SEEK_END) => LFS_ERR_INVAL;
lfs_file_tell(&lfs, &file) => (COUNT+1)*size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # inline write and seek
define.SIZE = [2, 4, 128, 132]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "tinykitty",
LFS2_O_RDWR | LFS2_O_CREAT) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "tinykitty",
LFS_O_RDWR | LFS_O_CREAT) => 0;
int j = 0;
int k = 0;
memcpy(buffer, "abcdefghijklmnopqrstuvwxyz", 26);
for (unsigned i = 0; i < SIZE; i++) {
lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => i+1;
lfs_file_write(&lfs, &file, &buffer[j++ % 26], 1) => 1;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => i+1;
}
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs_file_size(&lfs, &file) => SIZE;
for (unsigned i = 0; i < SIZE; i++) {
uint8_t c;
lfs2_file_read(&lfs2, &file, &c, 1) => 1;
lfs_file_read(&lfs, &file, &c, 1) => 1;
c => buffer[k++ % 26];
}
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_tell(&lfs2, &file) => SIZE;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_sync(&lfs, &file) => 0;
lfs_file_tell(&lfs, &file) => SIZE;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
for (unsigned i = 0; i < SIZE; i++) {
lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_write(&lfs, &file, &buffer[j++ % 26], 1) => 1;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => SIZE;
lfs_file_sync(&lfs, &file) => 0;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => SIZE;
if (i < SIZE-2) {
uint8_t c[3];
lfs2_file_seek(&lfs2, &file, -1, LFS2_SEEK_CUR) => i;
lfs2_file_read(&lfs2, &file, &c, 3) => 3;
lfs2_file_tell(&lfs2, &file) => i+3;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs2_file_seek(&lfs2, &file, i+1, LFS2_SEEK_SET) => i+1;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_seek(&lfs, &file, -1, LFS_SEEK_CUR) => i;
lfs_file_read(&lfs, &file, &c, 3) => 3;
lfs_file_tell(&lfs, &file) => i+3;
lfs_file_size(&lfs, &file) => SIZE;
lfs_file_seek(&lfs, &file, i+1, LFS_SEEK_SET) => i+1;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => SIZE;
}
}
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs_file_size(&lfs, &file) => SIZE;
for (unsigned i = 0; i < SIZE; i++) {
uint8_t c;
lfs2_file_read(&lfs2, &file, &c, 1) => 1;
lfs_file_read(&lfs, &file, &c, 1) => 1;
c => buffer[k++ % 26];
}
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_tell(&lfs2, &file) => SIZE;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_sync(&lfs, &file) => 0;
lfs_file_tell(&lfs, &file) => SIZE;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # file seek and write with power-loss
@@ -310,71 +310,71 @@ code = '''
define.COUNT = [4, 64, 128]
reentrant = true
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
err = lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY);
assert(!err || err == LFS2_ERR_NOENT);
err = lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY);
assert(!err || err == LFS_ERR_NOENT);
if (!err) {
if (lfs2_file_size(&lfs2, &file) != 0) {
lfs2_file_size(&lfs2, &file) => 11*COUNT;
if (lfs_file_size(&lfs, &file) != 0) {
lfs_file_size(&lfs, &file) => 11*COUNT;
for (int j = 0; j < COUNT; j++) {
memset(buffer, 0, 11+1);
lfs2_file_read(&lfs2, &file, buffer, 11) => 11;
lfs_file_read(&lfs, &file, buffer, 11) => 11;
assert(memcmp(buffer, "kittycatcat", 11) == 0 ||
memcmp(buffer, "doggodogdog", 11) == 0);
}
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
if (lfs2_file_size(&lfs2, &file) == 0) {
lfs_file_open(&lfs, &file, "kitty", LFS_O_WRONLY | LFS_O_CREAT) => 0;
if (lfs_file_size(&lfs, &file) == 0) {
for (int j = 0; j < COUNT; j++) {
strcpy((char*)buffer, "kittycatcat");
size = strlen((char*)buffer);
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
}
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
strcpy((char*)buffer, "doggodogdog");
size = strlen((char*)buffer);
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => COUNT*size;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => COUNT*size;
// seek and write using quadratic probing to touch all
// 11-byte words in the file
lfs2_off_t off = 0;
lfs_off_t off = 0;
for (int j = 0; j < COUNT; j++) {
off = (5*off + 1) % COUNT;
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, "kittycatcat", size) == 0 ||
memcmp(buffer, "doggodogdog", size) == 0);
if (memcmp(buffer, "doggodogdog", size) != 0) {
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
strcpy((char*)buffer, "doggodogdog");
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, "doggodogdog", size) == 0);
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_sync(&lfs, &file) => 0;
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, "doggodogdog", size) == 0);
}
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => COUNT*size;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => COUNT*size;
for (int j = 0; j < COUNT; j++) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
assert(memcmp(buffer, "doggodogdog", size) == 0);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -1,127 +1,127 @@
[[case]] # simple formatting test
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
'''
[[case]] # mount/unmount
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant format
reentrant = true
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # invalid mount
code = '''
lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT;
lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;
'''
[[case]] # expanding superblock
define.LFS2_BLOCK_CYCLES = [32, 33, 1]
define.LFS_BLOCK_CYCLES = [32, 33, 1]
define.N = [10, 100, 1000]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (int i = 0; i < N; i++) {
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 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;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_remove(&lfs2, "dummy") => 0;
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
// one last check after power-cycle
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 0;
lfs_mountcfg(&lfs, &cfg) => 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;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 0;
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
'''
[[case]] # expanding superblock with power cycle
define.LFS2_BLOCK_CYCLES = [32, 33, 1]
define.LFS_BLOCK_CYCLES = [32, 33, 1]
define.N = [10, 100, 1000]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
for (int i = 0; i < N; i++) {
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
// remove lingering dummy?
err = lfs2_stat(&lfs2, "dummy", &info);
assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0));
err = lfs_stat(&lfs, "dummy", &info);
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
if (!err) {
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_remove(&lfs2, "dummy") => 0;
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
}
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 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;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 0;
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
}
// one last check after power-cycle
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 0;
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
'''
[[case]] # reentrant expanding superblock
define.LFS2_BLOCK_CYCLES = [2, 1]
define.LFS_BLOCK_CYCLES = [2, 1]
define.N = 24
reentrant = true
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
for (int i = 0; i < N; i++) {
// remove lingering dummy?
err = lfs2_stat(&lfs2, "dummy", &info);
assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0));
err = lfs_stat(&lfs, "dummy", &info);
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
if (!err) {
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_remove(&lfs2, "dummy") => 0;
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
}
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 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;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
assert(info.type == LFS_TYPE_REG);
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
// one last check after power-cycle
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 0;
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
'''

View File

@@ -2,198 +2,198 @@
define.MEDIUMSIZE = [32, 2048]
define.LARGESIZE = 8192
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldynoop",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
size = strlen("hair");
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # truncate and read
define.MEDIUMSIZE = [32, 2048]
define.LARGESIZE = 8192
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldyread",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldyread",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
size = strlen("hair");
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
size = strlen("hair");
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # write, truncate, and read
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "sequence",
LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "sequence",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
size = lfs2_min(lfs2.cfg->cache_size, sizeof(buffer)/2);
lfs2_size_t qsize = size / 4;
size = lfs_min(lfs.cfg.cache_size, sizeof(buffer)/2);
lfs_size_t qsize = size / 4;
uint8_t *wb = buffer;
uint8_t *rb = buffer + size;
for (lfs2_off_t j = 0; j < size; ++j) {
for (lfs_off_t j = 0; j < size; ++j) {
wb[j] = j;
}
/* Spread sequence over size */
lfs2_file_write(&lfs2, &file, wb, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_tell(&lfs2, &file) => size;
lfs_file_write(&lfs, &file, wb, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_tell(&lfs, &file) => size;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_tell(&lfs, &file) => 0;
/* Chop off the last quarter */
lfs2_size_t trunc = size - qsize;
lfs2_file_truncate(&lfs2, &file, trunc) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
lfs2_file_size(&lfs2, &file) => trunc;
lfs_size_t trunc = size - qsize;
lfs_file_truncate(&lfs, &file, trunc) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs_file_size(&lfs, &file) => trunc;
/* Read should produce first 3/4 */
lfs2_file_read(&lfs2, &file, rb, size) => trunc;
lfs_file_read(&lfs, &file, rb, size) => trunc;
memcmp(rb, wb, trunc) => 0;
/* Move to 1/4 */
lfs2_file_size(&lfs2, &file) => trunc;
lfs2_file_seek(&lfs2, &file, qsize, LFS2_SEEK_SET) => qsize;
lfs2_file_tell(&lfs2, &file) => qsize;
lfs_file_size(&lfs, &file) => trunc;
lfs_file_seek(&lfs, &file, qsize, LFS_SEEK_SET) => qsize;
lfs_file_tell(&lfs, &file) => qsize;
/* Chop to 1/2 */
trunc -= qsize;
lfs2_file_truncate(&lfs2, &file, trunc) => 0;
lfs2_file_tell(&lfs2, &file) => qsize;
lfs2_file_size(&lfs2, &file) => trunc;
lfs_file_truncate(&lfs, &file, trunc) => 0;
lfs_file_tell(&lfs, &file) => qsize;
lfs_file_size(&lfs, &file) => trunc;
/* Read should produce second quarter */
lfs2_file_read(&lfs2, &file, rb, size) => trunc - qsize;
lfs_file_read(&lfs, &file, rb, size) => trunc - qsize;
memcmp(rb, wb + qsize, trunc - qsize) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # truncate and write
define.MEDIUMSIZE = [32, 2048]
define.LARGESIZE = 8192
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldywrite",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldywrite",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
strcpy((char*)buffer, "bald");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_mountcfg(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
size = strlen("bald");
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "bald", size) => 0;
}
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # truncate write under powerloss
@@ -202,64 +202,64 @@ define.MEDIUMSIZE = [32, 1024]
define.LARGESIZE = 2048
reentrant = true
code = '''
err = lfs2_mount(&lfs2, &cfg);
err = lfs_mountcfg(&lfs, &cfg);
if (err) {
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
}
err = lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDONLY);
assert(!err || err == LFS2_ERR_NOENT);
err = lfs_file_open(&lfs, &file, "baldy", LFS_O_RDONLY);
assert(!err || err == LFS_ERR_NOENT);
if (!err) {
size = lfs2_file_size(&lfs2, &file);
size = lfs_file_size(&lfs, &file);
assert(size == 0 ||
size == LARGESIZE ||
size == MEDIUMSIZE ||
size == SMALLSIZE);
for (lfs2_off_t j = 0; j < size; j += 4) {
lfs2_file_read(&lfs2, &file, buffer, 4) => 4;
for (lfs_off_t j = 0; j < size; j += 4) {
lfs_file_read(&lfs, &file, buffer, 4) => 4;
assert(memcmp(buffer, "hair", 4) == 0 ||
memcmp(buffer, "bald", 4) == 0 ||
memcmp(buffer, "comb", 4) == 0);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_file_open(&lfs2, &file, "baldy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs2_file_size(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "baldy",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs_file_size(&lfs, &file) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_open(&lfs, &file, "baldy", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
strcpy((char*)buffer, "bald");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs2_file_truncate(&lfs2, &file, SMALLSIZE) => 0;
lfs2_file_size(&lfs2, &file) => SMALLSIZE;
lfs_file_open(&lfs, &file, "baldy", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs_file_truncate(&lfs, &file, SMALLSIZE) => 0;
lfs_file_size(&lfs, &file) => SMALLSIZE;
strcpy((char*)buffer, "comb");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < SMALLSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < SMALLSIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => SMALLSIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_size(&lfs, &file) => SMALLSIZE;
lfs_file_close(&lfs, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # more aggressive general truncation tests
@@ -270,10 +270,10 @@ define.LARGESIZE = 8192
code = '''
#define COUNT 5
const struct {
lfs2_off_t startsizes[COUNT];
lfs2_off_t startseeks[COUNT];
lfs2_off_t hotsizes[COUNT];
lfs2_off_t coldsizes[COUNT];
lfs_off_t startsizes[COUNT];
lfs_off_t startseeks[COUNT];
lfs_off_t hotsizes[COUNT];
lfs_off_t coldsizes[COUNT];
} configs[] = {
// cold shrinking
{{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE},
@@ -307,133 +307,88 @@ code = '''
{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}},
};
const lfs2_off_t *startsizes = configs[CONFIG].startsizes;
const lfs2_off_t *startseeks = configs[CONFIG].startseeks;
const lfs2_off_t *hotsizes = configs[CONFIG].hotsizes;
const lfs2_off_t *coldsizes = configs[CONFIG].coldsizes;
const lfs_off_t *startsizes = configs[CONFIG].startsizes;
const lfs_off_t *startseeks = configs[CONFIG].startseeks;
const lfs_off_t *hotsizes = configs[CONFIG].hotsizes;
const lfs_off_t *coldsizes = configs[CONFIG].coldsizes;
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_formatcfg(&lfs, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (unsigned i = 0; i < COUNT; i++) {
sprintf(path, "hairyhead%d", i);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < startsizes[i]; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
for (lfs_off_t j = 0; j < startsizes[i]; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
}
lfs2_file_size(&lfs2, &file) => startsizes[i];
lfs_file_size(&lfs, &file) => startsizes[i];
if (startseeks[i] != startsizes[i]) {
lfs2_file_seek(&lfs2, &file,
startseeks[i], LFS2_SEEK_SET) => startseeks[i];
lfs_file_seek(&lfs, &file,
startseeks[i], LFS_SEEK_SET) => startseeks[i];
}
lfs2_file_truncate(&lfs2, &file, hotsizes[i]) => 0;
lfs2_file_size(&lfs2, &file) => hotsizes[i];
lfs_file_truncate(&lfs, &file, hotsizes[i]) => 0;
lfs_file_size(&lfs, &file) => hotsizes[i];
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (unsigned i = 0; i < COUNT; i++) {
sprintf(path, "hairyhead%d", i);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => hotsizes[i];
lfs_file_open(&lfs, &file, path, LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => hotsizes[i];
size = strlen("hair");
lfs2_off_t j = 0;
lfs_off_t j = 0;
for (; j < startsizes[i] && j < hotsizes[i]; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
for (; j < hotsizes[i]; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "\0\0\0\0", size) => 0;
}
lfs2_file_truncate(&lfs2, &file, coldsizes[i]) => 0;
lfs2_file_size(&lfs2, &file) => coldsizes[i];
lfs_file_truncate(&lfs, &file, coldsizes[i]) => 0;
lfs_file_size(&lfs, &file) => coldsizes[i];
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mountcfg(&lfs, &cfg) => 0;
for (unsigned i = 0; i < COUNT; i++) {
sprintf(path, "hairyhead%d", i);
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => coldsizes[i];
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => coldsizes[i];
size = strlen("hair");
lfs2_off_t j = 0;
lfs_off_t j = 0;
for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i];
j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
for (; j < coldsizes[i]; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
memcmp(buffer, "\0\0\0\0", size) => 0;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs2_unmount(&lfs2) => 0;
'''
[[case]] # noop truncate
define.MEDIUMSIZE = [32, 2048]
code = '''
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop",
LFS2_O_RDWR | LFS2_O_CREAT) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
// this truncate should do nothing
lfs2_file_truncate(&lfs2, &file, j+size) => 0;
}
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
// should do nothing again
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// still there after reboot?
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
'''