mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Compare commits
	
		
			107 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 7127786d39 | ||
|  | 9c7e232086 | ||
|  | c676bcee4c | ||
|  | 03f088b92c | ||
|  | e955b9f65d | ||
|  | 99f58139cb | ||
|  | 5801169348 | ||
|  | 2d6f4ead13 | ||
|  | 3d1b89b41a | ||
|  | 45cefb825d | ||
|  | bbb9e3873e | ||
|  | c6d3c48939 | ||
|  | 1363c9f9d4 | ||
|  | 5bc682a0d4 | ||
|  | 1877c40aac | ||
|  | e29e7aeefa | ||
|  | 4977fa0c0e | ||
|  | fdda3b4aa2 | ||
|  | fb2c311bb4 | ||
|  | 4dcad66e16 | ||
|  | ead50807f1 | ||
|  | 2f7596811d | ||
|  | 1e423bae58 | ||
|  | 3bee4d9a19 | ||
|  | b816d7f898 | ||
|  | 1863dc7883 | ||
|  | 3d4e4f2085 | ||
|  | a2c744c8f8 | ||
|  | c0cc0a417e | ||
|  | bca64d76cf | ||
|  | cab1d6cca6 | ||
|  | c9eed1f181 | ||
|  | e7e4b352bd | ||
|  | 9449ef4be4 | ||
|  | cfe779fc08 | ||
|  | 0db6466984 | ||
|  | 21488d9e06 | ||
|  | 10a08833c6 | ||
|  | 47d6b2fcf3 | ||
|  | 745d98cde0 | ||
|  | 3216b07c3b | ||
|  | 6592719d28 | ||
|  | c9110617b3 | ||
|  | 104d65113d | ||
|  | 6d3e4ac33e | ||
|  | 9d6546071b | ||
|  | b84fb6bcc5 | ||
|  | 887f3660ed | ||
|  | eeeceb9e30 | ||
|  | b2235e956d | ||
|  | 6bb4043154 | ||
|  | 2b804537b0 | ||
|  | d804c2d3b7 | ||
|  | 37f4de2976 | ||
|  | 6b16dafb4d | ||
|  | c40271bcf3 | ||
|  | 1a59954ec6 | ||
|  | 6a7012774d | ||
|  | 288a5cbc8d | ||
|  | 5783eea0de | ||
|  | 2bb523421e | ||
|  | 7388b2938a | ||
|  | ce425a56c3 | ||
|  | a99a93fb27 | ||
|  | 45afded784 | ||
|  | 00a9ba7826 | ||
|  | fc6988c7c3 | ||
|  | d0f055d321 | ||
|  | b9fa33f9bc | ||
|  | 2efebf8e9b | ||
|  | 754b4c3cda | ||
|  | 584eb26efc | ||
|  | 008ebc37df | ||
|  | 66272067ab | ||
|  | e273a82679 | ||
|  | 1dc6ae94b9 | ||
|  | 817ef02d24 | ||
|  | b8dcf10974 | ||
|  | 0aba71d0d6 | ||
|  | 0ea2871e24 | ||
|  | d04c1392c0 | ||
|  | f215027fd4 | ||
|  | 1ae4b36f2a | ||
|  | 480cdd9f81 | ||
|  | 6303558aee | ||
|  | 4bd653dd00 | ||
|  | 8e6826c4e2 | ||
|  | 10ac6b9cf0 | ||
|  | 87a2cb0e41 | ||
|  | 6d0ec5e851 | ||
|  | 197ad15e47 | ||
|  | 4c9146ea53 | ||
|  | 5a9f38df01 | ||
|  | 1b033e9ab6 | ||
|  | 3e7b9da578 | ||
|  | 64f70f51b0 | ||
|  | 1f204e6d84 | ||
|  | 893325aeb9 | ||
|  | c39b1de658 | ||
|  | f3608e84c7 | ||
|  | c08e977799 | ||
|  | bc7be77625 | ||
|  | 96c8b6dcb3 | ||
|  | 895767cc9d | ||
|  | 87d3abba61 | ||
|  | 19f4eae52c | ||
|  | 8d4fd46a4c | 
							
								
								
									
										26
									
								
								.github/workflows/post-release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.github/workflows/post-release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| 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)" | ||||
|  | ||||
							
								
								
									
										215
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| 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)" | ||||
|  | ||||
							
								
								
									
										55
									
								
								.github/workflows/status.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								.github/workflows/status.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| 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 | ||||
							
								
								
									
										446
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,446 @@ | ||||
| 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
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,7 +5,7 @@ | ||||
|  | ||||
| # Testing things | ||||
| blocks/ | ||||
| lfs | ||||
| lfs2 | ||||
| test.c | ||||
| tests/*.toml.* | ||||
| scripts/__pycache__ | ||||
|   | ||||
							
								
								
									
										429
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										429
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,429 +0,0 @@ | ||||
| # 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" | ||||
|  | ||||
|   # 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 | ||||
							
								
								
									
										89
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,69 +1,114 @@ | ||||
| TARGET = lfs.a | ||||
| ifneq ($(wildcard test.c main.c),) | ||||
| override TARGET = lfs | ||||
| 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 | ||||
| ifneq ($(wildcard test.c main.c),) | ||||
| TARGET ?= $(BUILDDIR)lfs2 | ||||
| else | ||||
| TARGET ?= $(BUILDDIR)lfs2.a | ||||
| endif | ||||
|  | ||||
|  | ||||
| CC ?= gcc | ||||
| AR ?= ar | ||||
| SIZE ?= size | ||||
| CTAGS ?= ctags | ||||
| NM ?= nm | ||||
| LCOV ?= lcov | ||||
|  | ||||
| SRC += $(wildcard *.c bd/*.c) | ||||
| OBJ := $(SRC:.c=.o) | ||||
| DEP := $(SRC:.c=.d) | ||||
| ASM := $(SRC:.c=.s) | ||||
| SRC ?= $(wildcard *.c) | ||||
| OBJ := $(SRC:%.c=$(BUILDDIR)%.o) | ||||
| DEP := $(SRC:%.c=$(BUILDDIR)%.d) | ||||
| ASM := $(SRC:%.c=$(BUILDDIR)%.s) | ||||
|  | ||||
| ifdef DEBUG | ||||
| override CFLAGS += -O0 -g3 | ||||
| else | ||||
| override CFLAGS += -Os | ||||
| endif | ||||
| ifdef WORD | ||||
| override CFLAGS += -m$(WORD) | ||||
| endif | ||||
| ifdef TRACE | ||||
| override CFLAGS += -DLFS_YES_TRACE | ||||
| override CFLAGS += -DLFS2_YES_TRACE | ||||
| endif | ||||
| override CFLAGS += -I. | ||||
| override CFLAGS += -std=c99 -Wall -pedantic | ||||
| override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef | ||||
| # Remove missing-field-initializers because of GCC bug | ||||
| override CFLAGS += -Wno-missing-field-initializers | ||||
|  | ||||
| ifdef VERBOSE | ||||
| override TFLAGS += -v | ||||
| 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)" | ||||
| endif | ||||
|  | ||||
|  | ||||
| all: $(TARGET) | ||||
| # commands | ||||
| .PHONY: all build | ||||
| all build: $(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 $(TFLAGS) | ||||
| 	./scripts/test.py $(TESTFLAGS) | ||||
| .SECONDEXPANSION: | ||||
| test%: tests/test$$(firstword $$(subst \#, ,%)).toml | ||||
| 	./scripts/test.py $@ $(TFLAGS) | ||||
| 	./scripts/test.py $@ $(TESTFLAGS) | ||||
|  | ||||
| .PHONY: coverage | ||||
| coverage: | ||||
| 	./scripts/coverage.py $(BUILDDIR)tests/*.toml.info $(COVERAGEFLAGS) | ||||
|  | ||||
| # rules | ||||
| -include $(DEP) | ||||
| .SUFFIXES: | ||||
|  | ||||
| lfs: $(OBJ) | ||||
| $(BUILDDIR)lfs2: $(OBJ) | ||||
| 	$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ | ||||
|  | ||||
| %.a: $(OBJ) | ||||
| $(BUILDDIR)%.a: $(OBJ) | ||||
| 	$(AR) rcs $@ $^ | ||||
|  | ||||
| %.o: %.c | ||||
| $(BUILDDIR)%.o: %.c | ||||
| 	$(CC) -c -MMD $(CFLAGS) $< -o $@ | ||||
|  | ||||
| %.s: %.c | ||||
| $(BUILDDIR)%.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 tests/*.toml.* | ||||
| 	rm -f $(BUILDDIR)tests/*.toml.* | ||||
|   | ||||
							
								
								
									
										46
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								README.md
									
									
									
									
									
								
							| @@ -32,14 +32,14 @@ main runs. The program can be interrupted at any time without losing track | ||||
| of how many times it has been booted and without corrupting the filesystem: | ||||
|  | ||||
| ``` c | ||||
| #include "lfs.h" | ||||
| #include "lfs2.h" | ||||
|  | ||||
| // variables used by the filesystem | ||||
| lfs_t lfs; | ||||
| lfs_file_t file; | ||||
| lfs2_t lfs2; | ||||
| lfs2_file_t file; | ||||
|  | ||||
| // configuration of the filesystem is provided by this struct | ||||
| const struct lfs_config cfg = { | ||||
| const struct lfs2_config cfg = { | ||||
|     // block device operations | ||||
|     .read  = user_provided_block_device_read, | ||||
|     .prog  = user_provided_block_device_prog, | ||||
| @@ -59,30 +59,30 @@ const struct lfs_config cfg = { | ||||
| // entry point | ||||
| int main(void) { | ||||
|     // mount the filesystem | ||||
|     int err = lfs_mount(&lfs, &cfg); | ||||
|     int err = lfs2_mount(&lfs2, &cfg); | ||||
|  | ||||
|     // reformat if we can't mount the filesystem | ||||
|     // this should only happen on the first boot | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg); | ||||
|         lfs_mount(&lfs, &cfg); | ||||
|         lfs2_format(&lfs2, &cfg); | ||||
|         lfs2_mount(&lfs2, &cfg); | ||||
|     } | ||||
|  | ||||
|     // read current count | ||||
|     uint32_t boot_count = 0; | ||||
|     lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); | ||||
|     lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count)); | ||||
|     lfs2_file_open(&lfs2, &file, "boot_count", LFS2_O_RDWR | LFS2_O_CREAT); | ||||
|     lfs2_file_read(&lfs2, &file, &boot_count, sizeof(boot_count)); | ||||
|  | ||||
|     // update boot count | ||||
|     boot_count += 1; | ||||
|     lfs_file_rewind(&lfs, &file); | ||||
|     lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count)); | ||||
|     lfs2_file_rewind(&lfs2, &file); | ||||
|     lfs2_file_write(&lfs2, &file, &boot_count, sizeof(boot_count)); | ||||
|  | ||||
|     // remember the storage is not updated until the file is closed successfully | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs2_file_close(&lfs2, &file); | ||||
|  | ||||
|     // release any resources we were using | ||||
|     lfs_unmount(&lfs); | ||||
|     lfs2_unmount(&lfs2); | ||||
|  | ||||
|     // print the boot count | ||||
|     printf("boot_count: %d\n", boot_count); | ||||
| @@ -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 [lfs.h](lfs.h). | ||||
| can be found in the comments in [lfs2.h](lfs2.h). | ||||
|  | ||||
| littlefs takes in a configuration structure that defines how the filesystem | ||||
| operates. The configuration struct provides the filesystem with the block | ||||
| @@ -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 `lfs_t` type which is left up | ||||
| The state of the littlefs is stored in the `lfs2_t` type which is left up | ||||
| to the user to allocate, allowing multiple filesystems to be in use | ||||
| simultaneously. With the `lfs_t` and configuration struct, a user can | ||||
| simultaneously. With the `lfs2_t` and configuration struct, a user can | ||||
| format a block device or mount the filesystem. | ||||
|  | ||||
| Once mounted, the littlefs provides a full set of POSIX-like file and | ||||
| @@ -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 lfs_error` in | ||||
| [lfs.h](lfs.h), or an error returned by the user's block device operations. | ||||
| errors can be either one of those found in the `enum lfs2_error` in | ||||
| [lfs2.h](lfs2.h), or an error returned by the user's block device operations. | ||||
|  | ||||
| In the configuration struct, the `prog` and `erase` function provided by the | ||||
| user may return a `LFS_ERR_CORRUPT` error if the implementation already can | ||||
| user may return a `LFS2_ERR_CORRUPT` error if the implementation already can | ||||
| detect corrupt blocks. However, the wear leveling does not depend on the return | ||||
| code of these functions, instead all data is read back and checked for | ||||
| integrity. | ||||
| @@ -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](emubd/lfs_emubd.h) found in the emubd directory. | ||||
| [emulated block device](bd/lfs2_testbd.h) found in the `bd` directory. | ||||
| The tests assume a Linux environment and can be started with make: | ||||
|  | ||||
| ``` bash | ||||
| @@ -221,6 +221,11 @@ License Identifiers that are here available: http://spdx.org/licenses/ | ||||
| - [littlefs-js] - A javascript wrapper for littlefs. I'm not sure why you would | ||||
|   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. | ||||
| @@ -250,3 +255,4 @@ 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
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								SPEC.md
									
									
									
									
									
								
							| @@ -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. | ||||
|  | ||||
| 3. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into | ||||
|    8 categories that facilitate bitmasked lookups. | ||||
|     1. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into | ||||
|        8 categories that facilitate bitmasked lookups. | ||||
|  | ||||
| 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. | ||||
|     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. | ||||
|  | ||||
| 5. **Id (10-bits)** - File id associated with the tag. Each file in a metadata | ||||
| 3. **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. | ||||
|  | ||||
| 6. **Length (10-bits)** - Length of the data in bytes. The special value | ||||
| 4. **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` LFS_TYPE_CREATE | ||||
| #### `0x401` LFS2_TYPE_CREATE | ||||
|  | ||||
| Creates a new file with this id. Note that files in a metadata block | ||||
| don't necessarily need a create tag. All a create does is move over any | ||||
| @@ -264,14 +264,14 @@ The create and delete tags allow littlefs to keep files in a directory | ||||
| ordered alphabetically by filename. | ||||
|  | ||||
| --- | ||||
| #### `0x4ff` LFS_TYPE_DELETE | ||||
| #### `0x4ff` LFS2_TYPE_DELETE | ||||
|  | ||||
| Deletes the file with this id. An inverse to create, this tag moves over | ||||
| any files neighboring this id similar to a deletion from an imaginary | ||||
| array of files. | ||||
|  | ||||
| --- | ||||
| #### `0x0xx` LFS_TYPE_NAME | ||||
| #### `0x0xx` LFS2_TYPE_NAME | ||||
|  | ||||
| Associates the id with a file name and file type. | ||||
|  | ||||
| @@ -304,14 +304,14 @@ Name fields: | ||||
| 2. **file name** - File name stored as an ASCII string. | ||||
|  | ||||
| --- | ||||
| #### `0x001` LFS_TYPE_REG | ||||
| #### `0x001` LFS2_TYPE_REG | ||||
|  | ||||
| Initializes the id + name as a regular file. | ||||
|  | ||||
| How each file is stored depends on its struct tag, which is described below. | ||||
|  | ||||
| --- | ||||
| #### `0x002` LFS_TYPE_DIR | ||||
| #### `0x002` LFS2_TYPE_DIR | ||||
|  | ||||
| Initializes the id + name as a directory. | ||||
|  | ||||
| @@ -320,7 +320,7 @@ each pair containing any number of files in alphabetical order. A pointer to | ||||
| the directory is stored in the struct tag, which is described below. | ||||
|  | ||||
| --- | ||||
| #### `0x0ff` LFS_TYPE_SUPERBLOCK | ||||
| #### `0x0ff` LFS2_TYPE_SUPERBLOCK | ||||
|  | ||||
| Initializes the id as a superblock entry. | ||||
|  | ||||
| @@ -405,7 +405,7 @@ as be the first entry written to the block. This means that the superblock | ||||
| entry can be read from a device using offsets alone. | ||||
|  | ||||
| --- | ||||
| #### `0x2xx` LFS_TYPE_STRUCT | ||||
| #### `0x2xx` LFS2_TYPE_STRUCT | ||||
|  | ||||
| Associates the id with an on-disk data structure. | ||||
|  | ||||
| @@ -416,7 +416,7 @@ Any type of struct supersedes all other structs associated with the id. For | ||||
| example, appending a ctz-struct replaces an inline-struct on the same file. | ||||
|  | ||||
| --- | ||||
| #### `0x200` LFS_TYPE_DIRSTRUCT | ||||
| #### `0x200` LFS2_TYPE_DIRSTRUCT | ||||
|  | ||||
| Gives the id a directory data structure. | ||||
|  | ||||
| @@ -458,7 +458,7 @@ Dir-struct fields: | ||||
|    in the directory. | ||||
|  | ||||
| --- | ||||
| #### `0x201` LFS_TYPE_INLINESTRUCT | ||||
| #### `0x201` LFS2_TYPE_INLINESTRUCT | ||||
|  | ||||
| Gives the id an inline data structure. | ||||
|  | ||||
| @@ -482,7 +482,7 @@ Inline-struct fields: | ||||
| 1. **Inline data** - File data stored directly in the metadata-pair. | ||||
|  | ||||
| --- | ||||
| #### `0x202` LFS_TYPE_CTZSTRUCT | ||||
| #### `0x202` LFS2_TYPE_CTZSTRUCT | ||||
|  | ||||
| Gives the id a CTZ skip-list data structure. | ||||
|  | ||||
| @@ -537,7 +537,7 @@ CTZ-struct fields: | ||||
| 2. **File size (32-bits)** - Size of the file in bytes. | ||||
|  | ||||
| --- | ||||
| #### `0x3xx` LFS_TYPE_USERATTR | ||||
| #### `0x3xx` LFS2_TYPE_USERATTR | ||||
|  | ||||
| Attaches a user attribute to an id. | ||||
|  | ||||
| @@ -571,7 +571,7 @@ User-attr fields: | ||||
| 2. **Attr data** - The data associated with the user attribute. | ||||
|  | ||||
| --- | ||||
| #### `0x6xx` LFS_TYPE_TAIL | ||||
| #### `0x6xx` LFS2_TYPE_TAIL | ||||
|  | ||||
| Provides the tail pointer for the metadata pair itself. | ||||
|  | ||||
| @@ -637,7 +637,7 @@ Tail fields: | ||||
| 2. **Metadata pair (8-bytes)** - Pointer to the next metadata-pair. | ||||
|  | ||||
| --- | ||||
| #### `0x600` LFS_TYPE_SOFTTAIL | ||||
| #### `0x600` LFS2_TYPE_SOFTTAIL | ||||
|  | ||||
| Provides a tail pointer that points to the next metadata pair in the | ||||
| filesystem. | ||||
| @@ -646,7 +646,7 @@ In this case, the next metadata pair is not a part of our current directory | ||||
| and should only be followed when traversing the entire filesystem. | ||||
|  | ||||
| --- | ||||
| #### `0x601` LFS_TYPE_HARDTAIL | ||||
| #### `0x601` LFS2_TYPE_HARDTAIL | ||||
|  | ||||
| Provides a tail pointer that points to the next metadata pair in the | ||||
| directory. | ||||
| @@ -657,7 +657,7 @@ metadata pair should only contain filenames greater than any filename in the | ||||
| current pair. | ||||
|  | ||||
| --- | ||||
| #### `0x7xx` LFS_TYPE_GSTATE | ||||
| #### `0x7xx` LFS2_TYPE_GSTATE | ||||
|  | ||||
| Provides delta bits for global state entries. | ||||
|  | ||||
| @@ -687,7 +687,7 @@ is stored in the chunk field. Currently, the only global state is move state, | ||||
| which is outlined below. | ||||
|  | ||||
| --- | ||||
| #### `0x7ff` LFS_TYPE_MOVESTATE | ||||
| #### `0x7ff` LFS2_TYPE_MOVESTATE | ||||
|  | ||||
| Provides delta bits for the global move state. | ||||
|  | ||||
| @@ -740,7 +740,7 @@ Move state fields: | ||||
|    the move. | ||||
|  | ||||
| --- | ||||
| #### `0x5xx` LFS_TYPE_CRC | ||||
| #### `0x5xx` LFS2_TYPE_CRC | ||||
|  | ||||
| Last but not least, the CRC tag marks the end of a commit and provides a | ||||
| checksum for any commits to the metadata block. | ||||
|   | ||||
| @@ -4,15 +4,15 @@ | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #include "bd/lfs_filebd.h" | ||||
| #include "bd/lfs2_filebd.h" | ||||
| 
 | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|         const struct lfs_filebd_config *bdcfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p {.context=%p, " | ||||
| 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"}, " | ||||
| @@ -23,23 +23,23 @@ int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|             (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); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     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; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", err); | ||||
|         LFS2_FILEBD_TRACE("lfs2_filebd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| 
 | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", 0); | ||||
|     LFS2_FILEBD_TRACE("lfs2_filebd_createcfg -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_filebd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, " | ||||
| 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"}, " | ||||
| @@ -49,38 +49,38 @@ int lfs_filebd_create(const struct lfs_config *cfg, const char *path) { | ||||
|             (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 lfs_filebd_config defaults = {.erase_value=-1}; | ||||
|     int err = lfs_filebd_createcfg(cfg, path, &defaults); | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err); | ||||
|     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 lfs_filebd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
| 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; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err); | ||||
|         LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0); | ||||
|     LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_read(%p, " | ||||
| 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); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     lfs2_filebd_t *bd = cfg->context; | ||||
| 
 | ||||
|     // check if read is valid
 | ||||
|     LFS_ASSERT(off  % cfg->read_size == 0); | ||||
|     LFS_ASSERT(size % cfg->read_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS2_ASSERT(off  % cfg->read_size == 0); | ||||
|     LFS2_ASSERT(size % cfg->read_size == 0); | ||||
|     LFS2_ASSERT(block < cfg->block_count); | ||||
| 
 | ||||
|     // zero for reproducability (in case file is truncated)
 | ||||
|     // zero for reproducibility (in case file is truncated)
 | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
|         memset(buffer, bd->cfg->erase_value, size); | ||||
|     } | ||||
| @@ -90,31 +90,31 @@ int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|     if (res1 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); | ||||
|         LFS2_FILEBD_TRACE("lfs2_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); | ||||
|         LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| 
 | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0); | ||||
|     LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
| 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); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     lfs2_filebd_t *bd = cfg->context; | ||||
| 
 | ||||
|     // check if write is valid
 | ||||
|     LFS_ASSERT(off  % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(size % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     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) { | ||||
| @@ -122,20 +122,20 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|                 (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|         if (res1 < 0) { | ||||
|             int err = -errno; | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|             LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
| 
 | ||||
|         for (lfs_off_t i = 0; i < size; i++) { | ||||
|         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; | ||||
|                 LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|                 LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err); | ||||
|                 return err; | ||||
|             } | ||||
| 
 | ||||
|             LFS_ASSERT(c == bd->cfg->erase_value); | ||||
|             LFS2_ASSERT(c == bd->cfg->erase_value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -144,62 +144,62 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||
|     if (res1 < 0) { | ||||
|         int err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); | ||||
|         LFS2_FILEBD_TRACE("lfs2_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); | ||||
|         LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| 
 | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0); | ||||
|     LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
| 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
 | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     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; | ||||
|             LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
|             LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
| 
 | ||||
|         for (lfs_off_t i = 0; i < cfg->block_size; i++) { | ||||
|         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; | ||||
|                 LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); | ||||
|                 LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", err); | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0); | ||||
|     LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_filebd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg); | ||||
| int lfs2_filebd_sync(const struct lfs2_config *cfg) { | ||||
|     LFS2_FILEBD_TRACE("lfs2_filebd_sync(%p)", (void*)cfg); | ||||
|     // file sync
 | ||||
|     lfs_filebd_t *bd = cfg->context; | ||||
|     lfs2_filebd_t *bd = cfg->context; | ||||
|     int err = fsync(bd->fd); | ||||
|     if (err) { | ||||
|         err = -errno; | ||||
|         LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|         LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0); | ||||
|         return err; | ||||
|     } | ||||
| 
 | ||||
|     LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0); | ||||
|     LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										73
									
								
								bd/lfs2_filebd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								bd/lfs2_filebd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| /* | ||||
|  * 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 | ||||
| @@ -4,11 +4,11 @@ | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #include "bd/lfs_rambd.h" | ||||
| #include "bd/lfs2_rambd.h" | ||||
| 
 | ||||
| int lfs_rambd_createcfg(const struct lfs_config *cfg, | ||||
|         const struct lfs_rambd_config *bdcfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_createcfg(%p {.context=%p, " | ||||
| 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"}, " | ||||
| @@ -18,32 +18,34 @@ int lfs_rambd_createcfg(const struct lfs_config *cfg, | ||||
|             (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); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|     lfs2_rambd_t *bd = cfg->context; | ||||
|     bd->cfg = bdcfg; | ||||
| 
 | ||||
|     // allocate buffer?
 | ||||
|     if (bd->cfg->buffer) { | ||||
|         bd->buffer = bd->cfg->buffer; | ||||
|     } else { | ||||
|         bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count); | ||||
|         bd->buffer = lfs2_malloc(cfg->block_size * cfg->block_count); | ||||
|         if (!bd->buffer) { | ||||
|             LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); | ||||
|             return LFS_ERR_NOMEM; | ||||
|             LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", LFS2_ERR_NOMEM); | ||||
|             return LFS2_ERR_NOMEM; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // zero for reproducability?
 | ||||
|     // 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); | ||||
|     } | ||||
| 
 | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0); | ||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_rambd_create(const struct lfs_config *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, " | ||||
| 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"})", | ||||
| @@ -51,58 +53,58 @@ int lfs_rambd_create(const struct lfs_config *cfg) { | ||||
|             (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 lfs_rambd_config defaults = {.erase_value=-1}; | ||||
|     int err = lfs_rambd_createcfg(cfg, &defaults); | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_create -> %d", err); | ||||
|     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 lfs_rambd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)cfg); | ||||
| int lfs2_rambd_destroy(const struct lfs2_config *cfg) { | ||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_destroy(%p)", (void*)cfg); | ||||
|     // clean up memory
 | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|     lfs2_rambd_t *bd = cfg->context; | ||||
|     if (!bd->cfg->buffer) { | ||||
|         lfs_free(bd->buffer); | ||||
|         lfs2_free(bd->buffer); | ||||
|     } | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0); | ||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_destroy -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_read(%p, " | ||||
| 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); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|     lfs2_rambd_t *bd = cfg->context; | ||||
| 
 | ||||
|     // check if read is valid
 | ||||
|     LFS_ASSERT(off  % cfg->read_size == 0); | ||||
|     LFS_ASSERT(size % cfg->read_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     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); | ||||
| 
 | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0); | ||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_read -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog(%p, " | ||||
| 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); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
|     lfs2_rambd_t *bd = cfg->context; | ||||
| 
 | ||||
|     // check if write is valid
 | ||||
|     LFS_ASSERT(off  % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(size % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     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 (lfs_off_t i = 0; i < size; i++) { | ||||
|             LFS_ASSERT(bd->buffer[block*cfg->block_size + off + i] == | ||||
|         for (lfs2_off_t i = 0; i < size; i++) { | ||||
|             LFS2_ASSERT(bd->buffer[block*cfg->block_size + off + i] == | ||||
|                     bd->cfg->erase_value); | ||||
|         } | ||||
|     } | ||||
| @@ -110,16 +112,16 @@ int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     // program data
 | ||||
|     memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size); | ||||
| 
 | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0); | ||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_rambd_t *bd = cfg->context; | ||||
| 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
 | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|     LFS2_ASSERT(block < cfg->block_count); | ||||
| 
 | ||||
|     // erase, only needed for testing
 | ||||
|     if (bd->cfg->erase_value != -1) { | ||||
| @@ -127,14 +129,14 @@ int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|                 bd->cfg->erase_value, cfg->block_size); | ||||
|     } | ||||
| 
 | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0); | ||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int lfs_rambd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)cfg); | ||||
| 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; | ||||
|     LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0); | ||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										75
									
								
								bd/lfs2_rambd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								bd/lfs2_rambd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										302
									
								
								bd/lfs2_testbd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								bd/lfs2_testbd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,302 @@ | ||||
| /* | ||||
|  * 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; | ||||
| } | ||||
| @@ -5,13 +5,13 @@ | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #ifndef LFS_TESTBD_H | ||||
| #define LFS_TESTBD_H | ||||
| #ifndef LFS2_TESTBD_H | ||||
| #define LFS2_TESTBD_H | ||||
| 
 | ||||
| #include "lfs.h" | ||||
| #include "lfs_util.h" | ||||
| #include "bd/lfs_rambd.h" | ||||
| #include "bd/lfs_filebd.h" | ||||
| #include "lfs2.h" | ||||
| #include "lfs2_util.h" | ||||
| #include "bd/lfs2_rambd.h" | ||||
| #include "bd/lfs2_filebd.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| @@ -20,10 +20,10 @@ extern "C" | ||||
| 
 | ||||
| 
 | ||||
| // Block device specific tracing
 | ||||
| #ifdef LFS_TESTBD_YES_TRACE | ||||
| #define LFS_TESTBD_TRACE(...) LFS_TRACE(__VA_ARGS__) | ||||
| #ifdef LFS2_TESTBD_YES_TRACE | ||||
| #define LFS2_TESTBD_TRACE(...) LFS2_TRACE(__VA_ARGS__) | ||||
| #else | ||||
| #define LFS_TESTBD_TRACE(...) | ||||
| #define LFS2_TESTBD_TRACE(...) | ||||
| #endif | ||||
| 
 | ||||
| // Mode determining how "bad blocks" behave during testing. This simulates
 | ||||
| @@ -32,20 +32,20 @@ extern "C" | ||||
| //
 | ||||
| // Not that read-noop is not allowed. Read _must_ return a consistent (but
 | ||||
| // may be arbitrary) value on every read.
 | ||||
| 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, | ||||
| 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, | ||||
| }; | ||||
| 
 | ||||
| // Type for measuring wear
 | ||||
| typedef uint32_t lfs_testbd_wear_t; | ||||
| typedef int32_t  lfs_testbd_swear_t; | ||||
| typedef uint32_t lfs2_testbd_wear_t; | ||||
| typedef int32_t  lfs2_testbd_swear_t; | ||||
| 
 | ||||
| // testbd config, this is required for testing
 | ||||
| struct lfs_testbd_config { | ||||
| struct lfs2_testbd_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.
 | ||||
| @@ -70,68 +70,68 @@ struct lfs_testbd_config { | ||||
| }; | ||||
| 
 | ||||
| // testbd state
 | ||||
| typedef struct lfs_testbd { | ||||
| typedef struct lfs2_testbd { | ||||
|     union { | ||||
|         struct { | ||||
|             lfs_filebd_t bd; | ||||
|             struct lfs_filebd_config cfg; | ||||
|             lfs2_filebd_t bd; | ||||
|             struct lfs2_filebd_config cfg; | ||||
|         } file; | ||||
|         struct { | ||||
|             lfs_rambd_t bd; | ||||
|             struct lfs_rambd_config cfg; | ||||
|             lfs2_rambd_t bd; | ||||
|             struct lfs2_rambd_config cfg; | ||||
|         } ram; | ||||
|     } u; | ||||
| 
 | ||||
|     bool persist; | ||||
|     uint32_t power_cycles; | ||||
|     lfs_testbd_wear_t *wear; | ||||
|     lfs2_testbd_wear_t *wear; | ||||
| 
 | ||||
|     const struct lfs_testbd_config *cfg; | ||||
| } lfs_testbd_t; | ||||
|     const struct lfs2_testbd_config *cfg; | ||||
| } lfs2_testbd_t; | ||||
| 
 | ||||
| 
 | ||||
| /// Block device API ///
 | ||||
| 
 | ||||
| // Create a test block device using the geometry in lfs_config
 | ||||
| // Create a test block device using the geometry in lfs2_config
 | ||||
| //
 | ||||
| // Note that filebd is used if a path is provided, if path is NULL
 | ||||
| // testbd will use rambd which can be much faster.
 | ||||
| int lfs_testbd_create(const struct lfs_config *cfg, const char *path); | ||||
| int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|         const struct lfs_testbd_config *bdcfg); | ||||
| 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); | ||||
| 
 | ||||
| // Clean up memory associated with block device
 | ||||
| int lfs_testbd_destroy(const struct lfs_config *cfg); | ||||
| int lfs2_testbd_destroy(const struct lfs2_config *cfg); | ||||
| 
 | ||||
| // Read a block
 | ||||
| int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size); | ||||
| int lfs2_testbd_read(const struct lfs2_config *cfg, lfs2_block_t block, | ||||
|         lfs2_off_t off, void *buffer, lfs2_size_t size); | ||||
| 
 | ||||
| // Program a block
 | ||||
| //
 | ||||
| // The block must have previously been erased.
 | ||||
| int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size); | ||||
| int lfs2_testbd_prog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||
|         lfs2_off_t off, const void *buffer, lfs2_size_t size); | ||||
| 
 | ||||
| // Erase a block
 | ||||
| //
 | ||||
| // A block must be erased before being programmed. The
 | ||||
| // state of an erased block is undefined.
 | ||||
| int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block); | ||||
| int lfs2_testbd_erase(const struct lfs2_config *cfg, lfs2_block_t block); | ||||
| 
 | ||||
| // Sync the block device
 | ||||
| int lfs_testbd_sync(const struct lfs_config *cfg); | ||||
| int lfs2_testbd_sync(const struct lfs2_config *cfg); | ||||
| 
 | ||||
| 
 | ||||
| /// Additional extended API for driving test features ///
 | ||||
| 
 | ||||
| // Get simulated wear on a given block
 | ||||
| lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg, | ||||
|         lfs_block_t block); | ||||
| lfs2_testbd_swear_t lfs2_testbd_getwear(const struct lfs2_config *cfg, | ||||
|         lfs2_block_t block); | ||||
| 
 | ||||
| // Manually set simulated wear on a given block
 | ||||
| int lfs_testbd_setwear(const struct lfs_config *cfg, | ||||
|         lfs_block_t block, lfs_testbd_wear_t wear); | ||||
| int lfs2_testbd_setwear(const struct lfs2_config *cfg, | ||||
|         lfs2_block_t block, lfs2_testbd_wear_t wear); | ||||
| 
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| @@ -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 LFS_FILEBD_H | ||||
| #define LFS_FILEBD_H | ||||
|  | ||||
| #include "lfs.h" | ||||
| #include "lfs_util.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_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 lfs_filebd { | ||||
|     int fd; | ||||
|     const struct lfs_filebd_config *cfg; | ||||
| } lfs_filebd_t; | ||||
|  | ||||
|  | ||||
| // Create a file block device using the geometry in lfs_config | ||||
| int lfs_filebd_create(const struct lfs_config *cfg, const char *path); | ||||
| int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|         const struct lfs_filebd_config *bdcfg); | ||||
|  | ||||
| // Clean up memory associated with block device | ||||
| int lfs_filebd_destroy(const struct lfs_config *cfg); | ||||
|  | ||||
| // Read a block | ||||
| int lfs_filebd_read(const struct lfs_config *cfg, 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(const struct lfs_config *cfg, 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(const struct lfs_config *cfg, lfs_block_t block); | ||||
|  | ||||
| // Sync the block device | ||||
| int lfs_filebd_sync(const struct lfs_config *cfg); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
| @@ -1,75 +0,0 @@ | ||||
| /* | ||||
|  * 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" | ||||
| #include "lfs_util.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_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 lfs_rambd { | ||||
|     uint8_t *buffer; | ||||
|     const struct lfs_rambd_config *cfg; | ||||
| } lfs_rambd_t; | ||||
|  | ||||
|  | ||||
| // Create a RAM block device using the geometry in lfs_config | ||||
| int lfs_rambd_create(const struct lfs_config *cfg); | ||||
| int lfs_rambd_createcfg(const struct lfs_config *cfg, | ||||
|         const struct lfs_rambd_config *bdcfg); | ||||
|  | ||||
| // Clean up memory associated with block device | ||||
| int lfs_rambd_destroy(const struct lfs_config *cfg); | ||||
|  | ||||
| // Read a block | ||||
| int lfs_rambd_read(const struct lfs_config *cfg, 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(const struct lfs_config *cfg, 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(const struct lfs_config *cfg, lfs_block_t block); | ||||
|  | ||||
| // Sync the block device | ||||
| int lfs_rambd_sync(const struct lfs_config *cfg); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										302
									
								
								bd/lfs_testbd.c
									
									
									
									
									
								
							
							
						
						
									
										302
									
								
								bd/lfs_testbd.c
									
									
									
									
									
								
							| @@ -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/lfs_testbd.h" | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
|  | ||||
| int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path, | ||||
|         const struct lfs_testbd_config *bdcfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_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); | ||||
|     lfs_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 = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->block_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) * cfg->block_count); | ||||
|     } | ||||
|  | ||||
|     // create underlying block device | ||||
|     if (bd->persist) { | ||||
|         bd->u.file.cfg = (struct lfs_filebd_config){ | ||||
|             .erase_value = bd->cfg->erase_value, | ||||
|         }; | ||||
|         int err = lfs_filebd_createcfg(cfg, path, &bd->u.file.cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } else { | ||||
|         bd->u.ram.cfg = (struct lfs_rambd_config){ | ||||
|             .erase_value = bd->cfg->erase_value, | ||||
|             .buffer = bd->cfg->buffer, | ||||
|         }; | ||||
|         int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int lfs_testbd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     LFS_TESTBD_TRACE("lfs_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 lfs_testbd_config defaults = {.erase_value=-1}; | ||||
|     int err = lfs_testbd_createcfg(cfg, path, &defaults); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) { | ||||
|         lfs_free(bd->wear); | ||||
|     } | ||||
|  | ||||
|     if (bd->persist) { | ||||
|         int err = lfs_filebd_destroy(cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } else { | ||||
|         int err = lfs_rambd_destroy(cfg); | ||||
|         LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Internal mapping to block devices /// | ||||
| static int lfs_testbd_rawread(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_read(cfg, block, off, buffer, size); | ||||
|     } else { | ||||
|         return lfs_rambd_read(cfg, block, off, buffer, size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_testbd_rawprog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_prog(cfg, block, off, buffer, size); | ||||
|     } else { | ||||
|         return lfs_rambd_prog(cfg, block, off, buffer, size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_testbd_rawerase(const struct lfs_config *cfg, | ||||
|         lfs_block_t block) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_erase(cfg, block); | ||||
|     } else { | ||||
|         return lfs_rambd_erase(cfg, block); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_testbd_rawsync(const struct lfs_config *cfg) { | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|     if (bd->persist) { | ||||
|         return lfs_filebd_sync(cfg); | ||||
|     } else { | ||||
|         return lfs_rambd_sync(cfg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// block device API /// | ||||
| int lfs_testbd_read(const struct lfs_config *cfg, 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*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if read is valid | ||||
|     LFS_ASSERT(off  % cfg->read_size == 0); | ||||
|     LFS_ASSERT(size % cfg->read_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_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(cfg, block, off, buffer, size); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog(%p, " | ||||
|                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if write is valid | ||||
|     LFS_ASSERT(off  % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(size % cfg->prog_size == 0); | ||||
|     LFS_ASSERT(block < cfg->block_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(cfg, 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(cfg) == 0); | ||||
|             // simulate power loss | ||||
|             exit(33); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if erase is valid | ||||
|     LFS_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 == | ||||
|                     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(cfg, 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(cfg) == 0); | ||||
|             // simulate power loss | ||||
|             exit(33); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg); | ||||
|     int err = lfs_testbd_rawsync(cfg); | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
|  | ||||
| /// simulated wear operations /// | ||||
| lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg, | ||||
|         lfs_block_t block) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if block is valid | ||||
|     LFS_ASSERT(bd->cfg->erase_cycles); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); | ||||
|     return bd->wear[block]; | ||||
| } | ||||
|  | ||||
| int lfs_testbd_setwear(const struct lfs_config *cfg, | ||||
|         lfs_block_t block, lfs_testbd_wear_t wear) { | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); | ||||
|     lfs_testbd_t *bd = cfg->context; | ||||
|  | ||||
|     // check if block is valid | ||||
|     LFS_ASSERT(bd->cfg->erase_cycles); | ||||
|     LFS_ASSERT(block < cfg->block_count); | ||||
|  | ||||
|     bd->wear[block] = wear; | ||||
|  | ||||
|     LFS_TESTBD_TRACE("lfs_testbd_setwear -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										524
									
								
								lfs.h → lfs2.h
									
									
									
									
									
								
							
							
						
						
									
										524
									
								
								lfs.h → lfs2.h
									
									
									
									
									
								
							| @@ -4,11 +4,12 @@ | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #ifndef LFS_H | ||||
| #define LFS_H | ||||
| #ifndef LFS2_H | ||||
| #define LFS2_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include "lfs2_util.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| @@ -21,177 +22,192 @@ extern "C" | ||||
| // 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)) | ||||
| #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 LFS_DISK_VERSION 0x00020000 | ||||
| #define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) | ||||
| #define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >>  0)) | ||||
| #define LFS2_DISK_VERSION 0x00020000 | ||||
| #define LFS2_DISK_VERSION_MAJOR (0xffff & (LFS2_DISK_VERSION >> 16)) | ||||
| #define LFS2_DISK_VERSION_MINOR (0xffff & (LFS2_DISK_VERSION >>  0)) | ||||
| 
 | ||||
| 
 | ||||
| /// Definitions ///
 | ||||
| 
 | ||||
| // Type definitions
 | ||||
| typedef uint32_t lfs_size_t; | ||||
| typedef uint32_t lfs_off_t; | ||||
| typedef uint32_t lfs2_size_t; | ||||
| typedef uint32_t lfs2_off_t; | ||||
| 
 | ||||
| typedef int32_t  lfs_ssize_t; | ||||
| typedef int32_t  lfs_soff_t; | ||||
| typedef int32_t  lfs2_ssize_t; | ||||
| typedef int32_t  lfs2_soff_t; | ||||
| 
 | ||||
| typedef uint32_t lfs_block_t; | ||||
| typedef uint32_t lfs2_block_t; | ||||
| 
 | ||||
| // Maximum name size in bytes, may be redefined to reduce the size of the
 | ||||
| // info struct. Limited to <= 1022. Stored in superblock and must be
 | ||||
| // respected by other littlefs drivers.
 | ||||
| #ifndef LFS_NAME_MAX | ||||
| #define LFS_NAME_MAX 255 | ||||
| #ifndef LFS2_NAME_MAX | ||||
| #define LFS2_NAME_MAX 255 | ||||
| #endif | ||||
| 
 | ||||
| // Maximum size of a file in bytes, may be redefined to limit to support other
 | ||||
| // drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
 | ||||
| // functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return
 | ||||
| // functions lfs2_file_seek, lfs2_file_size, and lfs2_file_tell will return
 | ||||
| // incorrect values due to using signed integers. Stored in superblock and
 | ||||
| // must be respected by other littlefs drivers.
 | ||||
| #ifndef LFS_FILE_MAX | ||||
| #define LFS_FILE_MAX 2147483647 | ||||
| #ifndef LFS2_FILE_MAX | ||||
| #define LFS2_FILE_MAX 2147483647 | ||||
| #endif | ||||
| 
 | ||||
| // Maximum size of custom attributes in bytes, may be redefined, but there is
 | ||||
| // no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022.
 | ||||
| #ifndef LFS_ATTR_MAX | ||||
| #define LFS_ATTR_MAX 1022 | ||||
| // no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022.
 | ||||
| #ifndef LFS2_ATTR_MAX | ||||
| #define LFS2_ATTR_MAX 1022 | ||||
| #endif | ||||
| 
 | ||||
| // Possible error codes, these are negative to allow
 | ||||
| // valid positive return values
 | ||||
| enum lfs_error { | ||||
|     LFS_ERR_OK          = 0,    // No error
 | ||||
|     LFS_ERR_IO          = -5,   // Error during device operation
 | ||||
|     LFS_ERR_CORRUPT     = -84,  // Corrupted
 | ||||
|     LFS_ERR_NOENT       = -2,   // No directory entry
 | ||||
|     LFS_ERR_EXIST       = -17,  // Entry already exists
 | ||||
|     LFS_ERR_NOTDIR      = -20,  // Entry is not a dir
 | ||||
|     LFS_ERR_ISDIR       = -21,  // Entry is a dir
 | ||||
|     LFS_ERR_NOTEMPTY    = -39,  // Dir is not empty
 | ||||
|     LFS_ERR_BADF        = -9,   // Bad file number
 | ||||
|     LFS_ERR_FBIG        = -27,  // File too large
 | ||||
|     LFS_ERR_INVAL       = -22,  // Invalid parameter
 | ||||
|     LFS_ERR_NOSPC       = -28,  // No space left on device
 | ||||
|     LFS_ERR_NOMEM       = -12,  // No more memory available
 | ||||
|     LFS_ERR_NOATTR      = -61,  // No data/attr available
 | ||||
|     LFS_ERR_NAMETOOLONG = -36,  // File name too long
 | ||||
| enum lfs2_error { | ||||
|     LFS2_ERR_OK          = 0,    // No error
 | ||||
|     LFS2_ERR_IO          = -5,   // Error during device operation
 | ||||
|     LFS2_ERR_CORRUPT     = -84,  // Corrupted
 | ||||
|     LFS2_ERR_NOENT       = -2,   // No directory entry
 | ||||
|     LFS2_ERR_EXIST       = -17,  // Entry already exists
 | ||||
|     LFS2_ERR_NOTDIR      = -20,  // Entry is not a dir
 | ||||
|     LFS2_ERR_ISDIR       = -21,  // Entry is a dir
 | ||||
|     LFS2_ERR_NOTEMPTY    = -39,  // Dir is not empty
 | ||||
|     LFS2_ERR_BADF        = -9,   // Bad file number
 | ||||
|     LFS2_ERR_FBIG        = -27,  // File too large
 | ||||
|     LFS2_ERR_INVAL       = -22,  // Invalid parameter
 | ||||
|     LFS2_ERR_NOSPC       = -28,  // No space left on device
 | ||||
|     LFS2_ERR_NOMEM       = -12,  // No more memory available
 | ||||
|     LFS2_ERR_NOATTR      = -61,  // No data/attr available
 | ||||
|     LFS2_ERR_NAMETOOLONG = -36,  // File name too long
 | ||||
| }; | ||||
| 
 | ||||
| // File types
 | ||||
| enum lfs_type { | ||||
| enum lfs2_type { | ||||
|     // file types
 | ||||
|     LFS_TYPE_REG            = 0x001, | ||||
|     LFS_TYPE_DIR            = 0x002, | ||||
|     LFS2_TYPE_REG            = 0x001, | ||||
|     LFS2_TYPE_DIR            = 0x002, | ||||
| 
 | ||||
|     // internally used types
 | ||||
|     LFS_TYPE_SPLICE         = 0x400, | ||||
|     LFS_TYPE_NAME           = 0x000, | ||||
|     LFS_TYPE_STRUCT         = 0x200, | ||||
|     LFS_TYPE_USERATTR       = 0x300, | ||||
|     LFS_TYPE_FROM           = 0x100, | ||||
|     LFS_TYPE_TAIL           = 0x600, | ||||
|     LFS_TYPE_GLOBALS        = 0x700, | ||||
|     LFS_TYPE_CRC            = 0x500, | ||||
|     LFS2_TYPE_SPLICE         = 0x400, | ||||
|     LFS2_TYPE_NAME           = 0x000, | ||||
|     LFS2_TYPE_STRUCT         = 0x200, | ||||
|     LFS2_TYPE_USERATTR       = 0x300, | ||||
|     LFS2_TYPE_FROM           = 0x100, | ||||
|     LFS2_TYPE_TAIL           = 0x600, | ||||
|     LFS2_TYPE_GLOBALS        = 0x700, | ||||
|     LFS2_TYPE_CRC            = 0x500, | ||||
| 
 | ||||
|     // internally used type specializations
 | ||||
|     LFS_TYPE_CREATE         = 0x401, | ||||
|     LFS_TYPE_DELETE         = 0x4ff, | ||||
|     LFS_TYPE_SUPERBLOCK     = 0x0ff, | ||||
|     LFS_TYPE_DIRSTRUCT      = 0x200, | ||||
|     LFS_TYPE_CTZSTRUCT      = 0x202, | ||||
|     LFS_TYPE_INLINESTRUCT   = 0x201, | ||||
|     LFS_TYPE_SOFTTAIL       = 0x600, | ||||
|     LFS_TYPE_HARDTAIL       = 0x601, | ||||
|     LFS_TYPE_MOVESTATE      = 0x7ff, | ||||
|     LFS2_TYPE_CREATE         = 0x401, | ||||
|     LFS2_TYPE_DELETE         = 0x4ff, | ||||
|     LFS2_TYPE_SUPERBLOCK     = 0x0ff, | ||||
|     LFS2_TYPE_DIRSTRUCT      = 0x200, | ||||
|     LFS2_TYPE_CTZSTRUCT      = 0x202, | ||||
|     LFS2_TYPE_INLINESTRUCT   = 0x201, | ||||
|     LFS2_TYPE_SOFTTAIL       = 0x600, | ||||
|     LFS2_TYPE_HARDTAIL       = 0x601, | ||||
|     LFS2_TYPE_MOVESTATE      = 0x7ff, | ||||
| 
 | ||||
|     // internal chip sources
 | ||||
|     LFS_FROM_NOOP           = 0x000, | ||||
|     LFS_FROM_MOVE           = 0x101, | ||||
|     LFS_FROM_USERATTRS      = 0x102, | ||||
|     LFS2_FROM_NOOP           = 0x000, | ||||
|     LFS2_FROM_MOVE           = 0x101, | ||||
|     LFS2_FROM_USERATTRS      = 0x102, | ||||
| }; | ||||
| 
 | ||||
| // File open flags
 | ||||
| enum lfs_open_flags { | ||||
| enum lfs2_open_flags { | ||||
|     // open flags
 | ||||
|     LFS_O_RDONLY = 1,         // Open a file as read only
 | ||||
|     LFS_O_WRONLY = 2,         // Open a file as write only
 | ||||
|     LFS_O_RDWR   = 3,         // Open a file as read and write
 | ||||
|     LFS_O_CREAT  = 0x0100,    // Create a file if it does not exist
 | ||||
|     LFS_O_EXCL   = 0x0200,    // Fail if a file already exists
 | ||||
|     LFS_O_TRUNC  = 0x0400,    // Truncate the existing file to zero size
 | ||||
|     LFS_O_APPEND = 0x0800,    // Move to end of file on every write
 | ||||
|     LFS2_O_RDONLY = 1,         // Open a file as read only
 | ||||
| #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
 | ||||
|     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
 | ||||
| #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 lfs_whence_flags { | ||||
|     LFS_SEEK_SET = 0,   // Seek relative to an absolute position
 | ||||
|     LFS_SEEK_CUR = 1,   // Seek relative to the current file position
 | ||||
|     LFS_SEEK_END = 2,   // Seek relative to the end of the file
 | ||||
| enum lfs2_whence_flags { | ||||
|     LFS2_SEEK_SET = 0,   // Seek relative to an absolute position
 | ||||
|     LFS2_SEEK_CUR = 1,   // Seek relative to the current file position
 | ||||
|     LFS2_SEEK_END = 2,   // Seek relative to the end of the file
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Configuration provided during initialization of the littlefs
 | ||||
| struct lfs_config { | ||||
| struct lfs2_config { | ||||
|     // Opaque user provided context that can be used to pass
 | ||||
|     // information to the block device operations
 | ||||
|     void *context; | ||||
| 
 | ||||
|     // Read a region in a block. Negative error codes are propogated
 | ||||
|     // Read a region in a block. Negative error codes are propagated
 | ||||
|     // to the user.
 | ||||
|     int (*read)(const struct lfs_config *c, lfs_block_t block, | ||||
|             lfs_off_t off, void *buffer, lfs_size_t size); | ||||
|     int (*read)(const struct lfs2_config *c, lfs2_block_t block, | ||||
|             lfs2_off_t off, void *buffer, lfs2_size_t size); | ||||
| 
 | ||||
|     // Program a region in a block. The block must have previously
 | ||||
|     // been erased. Negative error codes are propogated to the user.
 | ||||
|     // May return LFS_ERR_CORRUPT if the block should be considered bad.
 | ||||
|     int (*prog)(const struct lfs_config *c, lfs_block_t block, | ||||
|             lfs_off_t off, const void *buffer, lfs_size_t size); | ||||
|     // 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 propogated to the user.
 | ||||
|     // May return LFS_ERR_CORRUPT if the block should be considered bad.
 | ||||
|     int (*erase)(const struct lfs_config *c, lfs_block_t block); | ||||
|     // 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 propogated to the user.
 | ||||
|     int (*sync)(const struct lfs_config *c); | ||||
|     // are propagated to the user.
 | ||||
|     int (*sync)(const struct lfs2_config *c); | ||||
| 
 | ||||
|     // Minimum size of a block read. All read operations will be a
 | ||||
| #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.
 | ||||
|     lfs_size_t read_size; | ||||
|     lfs2_size_t read_size; | ||||
| 
 | ||||
|     // Minimum size of a block program. All program operations will be a
 | ||||
|     // multiple of this value.
 | ||||
|     lfs_size_t prog_size; | ||||
|     // 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. 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; | ||||
|     // 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.
 | ||||
|     lfs_size_t block_count; | ||||
|     lfs2_size_t block_count; | ||||
| 
 | ||||
|     // Number of erase cycles before littlefs evicts metadata logs and moves 
 | ||||
|     // 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.
 | ||||
| @@ -199,67 +215,73 @@ struct lfs_config { | ||||
|     // 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
 | ||||
|     // 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.
 | ||||
|     lfs_size_t cache_size; | ||||
|     // 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.
 | ||||
|     lfs_size_t lookahead_size; | ||||
|     lfs2_size_t lookahead_size; | ||||
| 
 | ||||
|     // Optional statically allocated read buffer. Must be cache_size.
 | ||||
|     // By default lfs_malloc is used to allocate this buffer.
 | ||||
|     // By default lfs2_malloc is used to allocate this buffer.
 | ||||
|     void *read_buffer; | ||||
| 
 | ||||
|     // Optional statically allocated program buffer. Must be cache_size.
 | ||||
|     // By default lfs_malloc is used to allocate this buffer.
 | ||||
|     // By default lfs2_malloc is used to allocate this buffer.
 | ||||
|     void *prog_buffer; | ||||
| 
 | ||||
|     // Optional statically allocated lookahead buffer. Must be lookahead_size
 | ||||
|     // and aligned to a 32-bit boundary. By default lfs_malloc is used to
 | ||||
|     // 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 LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
 | ||||
|     // the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX when zero. Stored in
 | ||||
|     // superblock and must be respected by other littlefs drivers.
 | ||||
|     lfs_size_t name_max; | ||||
|     lfs2_size_t name_max; | ||||
| 
 | ||||
|     // Optional upper limit on files in bytes. No downside for larger files
 | ||||
|     // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
 | ||||
|     // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX when zero. Stored
 | ||||
|     // in superblock and must be respected by other littlefs drivers.
 | ||||
|     lfs_size_t file_max; | ||||
|     lfs2_size_t file_max; | ||||
| 
 | ||||
|     // Optional upper limit on custom attributes in bytes. No downside for
 | ||||
|     // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
 | ||||
|     // LFS_ATTR_MAX when zero.
 | ||||
|     lfs_size_t attr_max; | ||||
|     // larger attributes size but must be <= LFS2_ATTR_MAX. Defaults to
 | ||||
|     // LFS2_ATTR_MAX when zero.
 | ||||
|     lfs2_size_t attr_max; | ||||
| 
 | ||||
|     // 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 lfs_info { | ||||
|     // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
 | ||||
| struct lfs2_info { | ||||
|     // Type of the file, either LFS2_TYPE_REG or LFS2_TYPE_DIR
 | ||||
|     uint8_t type; | ||||
| 
 | ||||
|     // Size of the file, only valid for REG files. Limited to 32-bits.
 | ||||
|     lfs_size_t size; | ||||
|     lfs2_size_t size; | ||||
| 
 | ||||
|     // Name of the file stored as a null-terminated string. Limited to
 | ||||
|     // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to
 | ||||
|     // reduce RAM. LFS_NAME_MAX is stored in superblock and must be
 | ||||
|     // LFS2_NAME_MAX+1, which can be changed by redefining LFS2_NAME_MAX to
 | ||||
|     // reduce RAM. LFS2_NAME_MAX is stored in superblock and must be
 | ||||
|     // respected by other littlefs drivers.
 | ||||
|     char name[LFS_NAME_MAX+1]; | ||||
|     char name[LFS2_NAME_MAX+1]; | ||||
| }; | ||||
| 
 | ||||
| // Custom attribute structure, used to describe custom attributes
 | ||||
| // committed atomically during file writes.
 | ||||
| struct lfs_attr { | ||||
| struct lfs2_attr { | ||||
|     // 8-bit type of attribute, provided by user and used to
 | ||||
|     // identify the attribute
 | ||||
|     uint8_t type; | ||||
| @@ -267,14 +289,14 @@ struct lfs_attr { | ||||
|     // Pointer to buffer containing the attribute
 | ||||
|     void *buffer; | ||||
| 
 | ||||
|     // Size of attribute in bytes, limited to LFS_ATTR_MAX
 | ||||
|     lfs_size_t size; | ||||
|     // Size of attribute in bytes, limited to LFS2_ATTR_MAX
 | ||||
|     lfs2_size_t size; | ||||
| }; | ||||
| 
 | ||||
| // Optional configuration provided during lfs_file_opencfg
 | ||||
| struct lfs_file_config { | ||||
| // Optional configuration provided during lfs2_file_opencfg
 | ||||
| struct lfs2_file_config { | ||||
|     // Optional statically allocated file buffer. Must be cache_size.
 | ||||
|     // By default lfs_malloc is used to allocate this buffer.
 | ||||
|     // By default lfs2_malloc is used to allocate this buffer.
 | ||||
|     void *buffer; | ||||
| 
 | ||||
|     // Optional list of custom attributes related to the file. If the file
 | ||||
| @@ -284,121 +306,122 @@ struct lfs_file_config { | ||||
|     // write occurs atomically with update to the file's contents.
 | ||||
|     //
 | ||||
|     // Custom attributes are uniquely identified by an 8-bit type and limited
 | ||||
|     // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller
 | ||||
|     // to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller
 | ||||
|     // than the buffer, it will be padded with zeros. If the stored attribute
 | ||||
|     // is larger, then it will be silently truncated. If the attribute is not
 | ||||
|     // found, it will be created implicitly.
 | ||||
|     struct lfs_attr *attrs; | ||||
|     struct lfs2_attr *attrs; | ||||
| 
 | ||||
|     // Number of custom attributes in the list
 | ||||
|     lfs_size_t attr_count; | ||||
|     lfs2_size_t attr_count; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /// internal littlefs data structures ///
 | ||||
| typedef struct lfs_cache { | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|     lfs_size_t size; | ||||
| typedef struct lfs2_cache { | ||||
|     lfs2_block_t block; | ||||
|     lfs2_off_t off; | ||||
|     lfs2_size_t size; | ||||
|     uint8_t *buffer; | ||||
| } lfs_cache_t; | ||||
| } lfs2_cache_t; | ||||
| 
 | ||||
| typedef struct lfs_mdir { | ||||
|     lfs_block_t pair[2]; | ||||
| typedef struct lfs2_mdir { | ||||
|     lfs2_block_t pair[2]; | ||||
|     uint32_t rev; | ||||
|     lfs_off_t off; | ||||
|     lfs2_off_t off; | ||||
|     uint32_t etag; | ||||
|     uint16_t count; | ||||
|     bool erased; | ||||
|     bool split; | ||||
|     lfs_block_t tail[2]; | ||||
| } lfs_mdir_t; | ||||
|     lfs2_block_t tail[2]; | ||||
| } lfs2_mdir_t; | ||||
| 
 | ||||
| // littlefs directory type
 | ||||
| typedef struct lfs_dir { | ||||
|     struct lfs_dir *next; | ||||
| typedef struct lfs2_dir { | ||||
|     struct lfs2_dir *next; | ||||
|     uint16_t id; | ||||
|     uint8_t type; | ||||
|     lfs_mdir_t m; | ||||
|     lfs2_mdir_t m; | ||||
| 
 | ||||
|     lfs_off_t pos; | ||||
|     lfs_block_t head[2]; | ||||
| } lfs_dir_t; | ||||
|     lfs2_off_t pos; | ||||
|     lfs2_block_t head[2]; | ||||
| } lfs2_dir_t; | ||||
| 
 | ||||
| // littlefs file type
 | ||||
| typedef struct lfs_file { | ||||
|     struct lfs_file *next; | ||||
| typedef struct lfs2_file { | ||||
|     struct lfs2_file *next; | ||||
|     uint16_t id; | ||||
|     uint8_t type; | ||||
|     lfs_mdir_t m; | ||||
|     lfs2_mdir_t m; | ||||
| 
 | ||||
|     struct lfs_ctz { | ||||
|         lfs_block_t head; | ||||
|         lfs_size_t size; | ||||
|     struct lfs2_ctz { | ||||
|         lfs2_block_t head; | ||||
|         lfs2_size_t size; | ||||
|     } ctz; | ||||
| 
 | ||||
|     uint32_t flags; | ||||
|     lfs_off_t pos; | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|     lfs_cache_t cache; | ||||
|     lfs2_off_t pos; | ||||
|     lfs2_block_t block; | ||||
|     lfs2_off_t off; | ||||
|     lfs2_cache_t cache; | ||||
| 
 | ||||
|     const struct lfs_file_config *cfg; | ||||
| } lfs_file_t; | ||||
|     const struct lfs2_file_config *cfg; | ||||
| } lfs2_file_t; | ||||
| 
 | ||||
| typedef struct lfs_superblock { | ||||
| typedef struct lfs2_superblock { | ||||
|     uint32_t version; | ||||
|     lfs_size_t block_size; | ||||
|     lfs_size_t block_count; | ||||
|     lfs_size_t name_max; | ||||
|     lfs_size_t file_max; | ||||
|     lfs_size_t attr_max; | ||||
| } lfs_superblock_t; | ||||
|     lfs2_size_t block_size; | ||||
|     lfs2_size_t block_count; | ||||
|     lfs2_size_t name_max; | ||||
|     lfs2_size_t file_max; | ||||
|     lfs2_size_t attr_max; | ||||
| } lfs2_superblock_t; | ||||
| 
 | ||||
| typedef struct lfs_gstate { | ||||
| typedef struct lfs2_gstate { | ||||
|     uint32_t tag; | ||||
|     lfs_block_t pair[2]; | ||||
| } lfs_gstate_t; | ||||
|     lfs2_block_t pair[2]; | ||||
| } lfs2_gstate_t; | ||||
| 
 | ||||
| // The littlefs filesystem type
 | ||||
| typedef struct lfs { | ||||
|     lfs_cache_t rcache; | ||||
|     lfs_cache_t pcache; | ||||
| typedef struct lfs2 { | ||||
|     lfs2_cache_t rcache; | ||||
|     lfs2_cache_t pcache; | ||||
| 
 | ||||
|     lfs_block_t root[2]; | ||||
|     struct lfs_mlist { | ||||
|         struct lfs_mlist *next; | ||||
|     lfs2_block_t root[2]; | ||||
|     struct lfs2_mlist { | ||||
|         struct lfs2_mlist *next; | ||||
|         uint16_t id; | ||||
|         uint8_t type; | ||||
|         lfs_mdir_t m; | ||||
|         lfs2_mdir_t m; | ||||
|     } *mlist; | ||||
|     uint32_t seed; | ||||
| 
 | ||||
|     lfs_gstate_t gstate; | ||||
|     lfs_gstate_t gdisk; | ||||
|     lfs_gstate_t gdelta; | ||||
|     lfs2_gstate_t gstate; | ||||
|     lfs2_gstate_t gdisk; | ||||
|     lfs2_gstate_t gdelta; | ||||
| 
 | ||||
|     struct lfs_free { | ||||
|         lfs_block_t off; | ||||
|         lfs_block_t size; | ||||
|         lfs_block_t i; | ||||
|         lfs_block_t ack; | ||||
|     struct lfs2_free { | ||||
|         lfs2_block_t off; | ||||
|         lfs2_block_t size; | ||||
|         lfs2_block_t i; | ||||
|         lfs2_block_t ack; | ||||
|         uint32_t *buffer; | ||||
|     } free; | ||||
| 
 | ||||
|     const struct lfs_config *cfg; | ||||
|     lfs_size_t name_max; | ||||
|     lfs_size_t file_max; | ||||
|     lfs_size_t attr_max; | ||||
|     const struct lfs2_config *cfg; | ||||
|     lfs2_size_t name_max; | ||||
|     lfs2_size_t file_max; | ||||
|     lfs2_size_t attr_max; | ||||
| 
 | ||||
| #ifdef LFS_MIGRATE | ||||
|     struct lfs1 *lfs1; | ||||
| #ifdef LFS2_MIGRATE | ||||
|     struct lfs21 *lfs21; | ||||
| #endif | ||||
| } lfs_t; | ||||
| } lfs2_t; | ||||
| 
 | ||||
| 
 | ||||
| /// Filesystem functions ///
 | ||||
| 
 | ||||
| #ifndef LFS2_READONLY | ||||
| // Format a block device with the littlefs
 | ||||
| //
 | ||||
| // Requires a littlefs object and config struct. This clobbers the littlefs
 | ||||
| @@ -406,77 +429,86 @@ typedef struct lfs { | ||||
| // be zeroed for defaults and backwards compatibility.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *config); | ||||
| int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *config); | ||||
| #endif | ||||
| 
 | ||||
| // Mounts a littlefs
 | ||||
| //
 | ||||
| // Requires a littlefs object and config struct. Multiple filesystems
 | ||||
| // may be mounted simultaneously with multiple littlefs objects. Both
 | ||||
| // lfs and config must be allocated while mounted. The config struct must
 | ||||
| // lfs2 and config must be allocated while mounted. The config struct must
 | ||||
| // be zeroed for defaults and backwards compatibility.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *config); | ||||
| int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *config); | ||||
| 
 | ||||
| // Unmounts a littlefs
 | ||||
| //
 | ||||
| // Does nothing besides releasing any allocated resources.
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_unmount(lfs_t *lfs); | ||||
| int lfs2_unmount(lfs2_t *lfs2); | ||||
| 
 | ||||
| /// General operations ///
 | ||||
| 
 | ||||
| #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 lfs_remove(lfs_t *lfs, const char *path); | ||||
| 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 lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); | ||||
| 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 lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | ||||
| int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info); | ||||
| 
 | ||||
| // Get a custom attribute
 | ||||
| //
 | ||||
| // Custom attributes are uniquely identified by an 8-bit type and limited
 | ||||
| // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
 | ||||
| // to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller than
 | ||||
| // the buffer, it will be padded with zeros. If the stored attribute is larger,
 | ||||
| // then it will be silently truncated. If no attribute is found, the error
 | ||||
| // LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
 | ||||
| // LFS2_ERR_NOATTR is returned and the buffer is filled with zeros.
 | ||||
| //
 | ||||
| // Returns the size of the attribute, or a negative error code on failure.
 | ||||
| // Note, the returned size is the size of the attribute on disk, irrespective
 | ||||
| // of the size of the buffer. This can be used to dynamically allocate a buffer
 | ||||
| // or check for existance.
 | ||||
| lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, void *buffer, lfs_size_t size); | ||||
| // 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 LFS_ATTR_MAX bytes. If an attribute is not found, it will be
 | ||||
| // to LFS2_ATTR_MAX bytes. If an attribute is not found, it will be
 | ||||
| // implicitly created.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_setattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, const void *buffer, lfs_size_t size); | ||||
| int lfs2_setattr(lfs2_t *lfs2, const char *path, | ||||
|         uint8_t type, const void *buffer, lfs2_size_t size); | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS2_READONLY | ||||
| // Removes a custom attribute
 | ||||
| //
 | ||||
| // If an attribute is not found, nothing happens.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); | ||||
| int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type); | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /// File operations ///
 | ||||
| @@ -484,25 +516,25 @@ int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); | ||||
| // Open a file
 | ||||
| //
 | ||||
| // The mode that the file is opened in is determined by the flags, which
 | ||||
| // are values from the enum lfs_open_flags that are bitwise-ored together.
 | ||||
| // are values from the enum lfs2_open_flags that are bitwise-ored together.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
| int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, | ||||
|         const char *path, int flags); | ||||
| 
 | ||||
| // Open a file with extra configuration
 | ||||
| //
 | ||||
| // The mode that the file is opened in is determined by the flags, which
 | ||||
| // are values from the enum lfs_open_flags that are bitwise-ored together.
 | ||||
| // are values from the enum lfs2_open_flags that are bitwise-ored together.
 | ||||
| //
 | ||||
| // The config struct provides additional config options per file as described
 | ||||
| // above. The config struct must be allocated while the file is open, and the
 | ||||
| // config struct must be zeroed for defaults and backwards compatibility.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, | ||||
| int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, | ||||
|         const char *path, int flags, | ||||
|         const struct lfs_file_config *config); | ||||
|         const struct lfs2_file_config *config); | ||||
| 
 | ||||
| // Close a file
 | ||||
| //
 | ||||
| @@ -510,86 +542,92 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, | ||||
| // sync had been called and releases any allocated resources.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_file_close(lfs_t *lfs, lfs_file_t *file); | ||||
| int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file); | ||||
| 
 | ||||
| // Synchronize a file on storage
 | ||||
| //
 | ||||
| // Any pending writes are written out to storage.
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); | ||||
| int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file); | ||||
| 
 | ||||
| // Read data from file
 | ||||
| //
 | ||||
| // Takes a buffer and size indicating where to store the read data.
 | ||||
| // Returns the number of bytes read, or a negative error code on failure.
 | ||||
| lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | ||||
|         void *buffer, lfs_size_t size); | ||||
| lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, | ||||
|         void *buffer, lfs2_size_t size); | ||||
| 
 | ||||
| #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.
 | ||||
| lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
|         const void *buffer, lfs_size_t size); | ||||
| lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, | ||||
|         const void *buffer, lfs2_size_t size); | ||||
| #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.
 | ||||
| lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|         lfs_soff_t off, int whence); | ||||
| lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, | ||||
|         lfs2_soff_t off, int whence); | ||||
| 
 | ||||
| #ifndef LFS2_READONLY | ||||
| // Truncates the size of the file to the specified size
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); | ||||
| int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size); | ||||
| #endif | ||||
| 
 | ||||
| // Return the position of the file
 | ||||
| //
 | ||||
| // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
 | ||||
| // Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_CUR)
 | ||||
| // Returns the position of the file, or a negative error code on failure.
 | ||||
| lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); | ||||
| lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file); | ||||
| 
 | ||||
| // Change the position of the file to the beginning of the file
 | ||||
| //
 | ||||
| // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET)
 | ||||
| // Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_SET)
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); | ||||
| int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file); | ||||
| 
 | ||||
| // Return the size of the file
 | ||||
| //
 | ||||
| // Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
 | ||||
| // Similar to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_END)
 | ||||
| // Returns the size of the file, or a negative error code on failure.
 | ||||
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); | ||||
| lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file); | ||||
| 
 | ||||
| 
 | ||||
| /// Directory operations ///
 | ||||
| 
 | ||||
| #ifndef LFS2_READONLY | ||||
| // Create a directory
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_mkdir(lfs_t *lfs, const char *path); | ||||
| int lfs2_mkdir(lfs2_t *lfs2, const char *path); | ||||
| #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 lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path); | ||||
| int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path); | ||||
| 
 | ||||
| // Close a directory
 | ||||
| //
 | ||||
| // Releases any allocated resources.
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); | ||||
| int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir); | ||||
| 
 | ||||
| // Read an entry in the directory
 | ||||
| //
 | ||||
| // Fills out the info structure, based on the specified file or directory.
 | ||||
| // Returns a 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); | ||||
| int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info); | ||||
| 
 | ||||
| // Change the position of the directory
 | ||||
| //
 | ||||
| @@ -597,7 +635,7 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); | ||||
| // an absolute offset in the directory seek.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); | ||||
| int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off); | ||||
| 
 | ||||
| // Return the position of the directory
 | ||||
| //
 | ||||
| @@ -605,12 +643,12 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); | ||||
| // sense, but does indicate the current position in the directory iteration.
 | ||||
| //
 | ||||
| // Returns the position of the directory, or a negative error code on failure.
 | ||||
| lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); | ||||
| lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir); | ||||
| 
 | ||||
| // Change the position of the directory to the beginning of the directory
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||
| int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir); | ||||
| 
 | ||||
| 
 | ||||
| /// Filesystem-level filesystem operations
 | ||||
| @@ -621,7 +659,7 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||
| // size may be larger than the filesystem actually is.
 | ||||
| //
 | ||||
| // Returns the number of allocated blocks, or a negative error code on failure.
 | ||||
| lfs_ssize_t lfs_fs_size(lfs_t *lfs); | ||||
| lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2); | ||||
| 
 | ||||
| // Traverse through all blocks in use by the filesystem
 | ||||
| //
 | ||||
| @@ -630,12 +668,13 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs); | ||||
| // blocks are in use or how much of the storage is available.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
| int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); | ||||
| 
 | ||||
| #ifdef LFS_MIGRATE | ||||
| #ifndef LFS2_READONLY | ||||
| #ifdef LFS2_MIGRATE | ||||
| // Attempts to migrate a previous version of littlefs
 | ||||
| //
 | ||||
| // Behaves similarly to the lfs_format function. Attempts to mount
 | ||||
| // Behaves similarly to the lfs2_format function. Attempts to mount
 | ||||
| // the previous version of littlefs and update the filesystem so it can be
 | ||||
| // mounted with the current version of littlefs.
 | ||||
| //
 | ||||
| @@ -644,7 +683,8 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
| // be zeroed for defaults and backwards compatibility.
 | ||||
| //
 | ||||
| // Returns a negative error code on failure.
 | ||||
| int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); | ||||
| int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg); | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| @@ -1,17 +1,17 @@ | ||||
| /*
 | ||||
|  * lfs util functions | ||||
|  * lfs2 util functions | ||||
|  * | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #include "lfs_util.h" | ||||
| #include "lfs2_util.h" | ||||
| 
 | ||||
| // Only compile if user does not provide custom config
 | ||||
| #ifndef LFS_CONFIG | ||||
| #ifndef LFS2_CONFIG | ||||
| 
 | ||||
| 
 | ||||
| // Software CRC implementation with small lookup table
 | ||||
| uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { | ||||
| uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { | ||||
|     static const uint32_t rtable[16] = { | ||||
|         0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, | ||||
|         0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, | ||||
| @@ -1,22 +1,22 @@ | ||||
| /*
 | ||||
|  * lfs utility functions | ||||
|  * lfs2 utility functions | ||||
|  * | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #ifndef LFS_UTIL_H | ||||
| #define LFS_UTIL_H | ||||
| #ifndef LFS2_UTIL_H | ||||
| #define LFS2_UTIL_H | ||||
| 
 | ||||
| // Users can override lfs_util.h with their own configuration by defining
 | ||||
| // LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
 | ||||
| // Users can override lfs2_util.h with their own configuration by defining
 | ||||
| // LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h).
 | ||||
| //
 | ||||
| // If LFS_CONFIG is used, none of the default utils will be emitted and must be
 | ||||
| // provided by the config file. To start, I would suggest copying lfs_util.h
 | ||||
| // If LFS2_CONFIG is used, none of the default utils will be emitted and must be
 | ||||
| // provided by the config file. To start, I would suggest copying lfs2_util.h
 | ||||
| // and modifying as needed.
 | ||||
| #ifdef LFS_CONFIG | ||||
| #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) | ||||
| #define LFS_STRINGIZE2(x) #x | ||||
| #include LFS_STRINGIZE(LFS_CONFIG) | ||||
| #ifdef LFS2_CONFIG | ||||
| #define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x) | ||||
| #define LFS2_STRINGIZE2(x) #x | ||||
| #include LFS2_STRINGIZE(LFS2_CONFIG) | ||||
| #else | ||||
| 
 | ||||
| // System includes
 | ||||
| @@ -25,16 +25,16 @@ | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
| 
 | ||||
| #ifndef LFS_NO_MALLOC | ||||
| #ifndef LFS2_NO_MALLOC | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #ifndef LFS_NO_ASSERT | ||||
| #ifndef LFS2_NO_ASSERT | ||||
| #include <assert.h> | ||||
| #endif | ||||
| #if !defined(LFS_NO_DEBUG) || \ | ||||
|         !defined(LFS_NO_WARN) || \ | ||||
|         !defined(LFS_NO_ERROR) || \ | ||||
|         defined(LFS_YES_TRACE) | ||||
| #if !defined(LFS2_NO_DEBUG) || \ | ||||
|         !defined(LFS2_NO_WARN) || \ | ||||
|         !defined(LFS2_NO_ERROR) || \ | ||||
|         defined(LFS2_YES_TRACE) | ||||
| #include <stdio.h> | ||||
| #endif | ||||
| 
 | ||||
| @@ -49,71 +49,81 @@ extern "C" | ||||
| // code footprint
 | ||||
| 
 | ||||
| // Logging functions
 | ||||
| #ifdef LFS_YES_TRACE | ||||
| #define LFS_TRACE_(fmt, ...) \ | ||||
| #ifndef LFS2_TRACE | ||||
| #ifdef LFS2_YES_TRACE | ||||
| #define LFS2_TRACE_(fmt, ...) \ | ||||
|     printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "") | ||||
| #define LFS2_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_TRACE(...) | ||||
| #define LFS2_TRACE(...) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_NO_DEBUG | ||||
| #define LFS_DEBUG_(fmt, ...) \ | ||||
| #ifndef LFS2_DEBUG | ||||
| #ifndef LFS2_NO_DEBUG | ||||
| #define LFS2_DEBUG_(fmt, ...) \ | ||||
|     printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "") | ||||
| #define LFS2_DEBUG(...) LFS2_DEBUG_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_DEBUG(...) | ||||
| #define LFS2_DEBUG(...) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_NO_WARN | ||||
| #define LFS_WARN_(fmt, ...) \ | ||||
| #ifndef LFS2_WARN | ||||
| #ifndef LFS2_NO_WARN | ||||
| #define LFS2_WARN_(fmt, ...) \ | ||||
|     printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "") | ||||
| #define LFS2_WARN(...) LFS2_WARN_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_WARN(...) | ||||
| #define LFS2_WARN(...) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_NO_ERROR | ||||
| #define LFS_ERROR_(fmt, ...) \ | ||||
| #ifndef LFS2_ERROR | ||||
| #ifndef LFS2_NO_ERROR | ||||
| #define LFS2_ERROR_(fmt, ...) \ | ||||
|     printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||
| #define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "") | ||||
| #define LFS2_ERROR(...) LFS2_ERROR_(__VA_ARGS__, "") | ||||
| #else | ||||
| #define LFS_ERROR(...) | ||||
| #define LFS2_ERROR(...) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| // Runtime assertions
 | ||||
| #ifndef LFS_NO_ASSERT | ||||
| #define LFS_ASSERT(test) assert(test) | ||||
| #ifndef LFS2_ASSERT | ||||
| #ifndef LFS2_NO_ASSERT | ||||
| #define LFS2_ASSERT(test) assert(test) | ||||
| #else | ||||
| #define LFS_ASSERT(test) | ||||
| #define LFS2_ASSERT(test) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| // Builtin functions, these may be replaced by more efficient
 | ||||
| // toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
 | ||||
| // toolchain-specific implementations. LFS2_NO_INTRINSICS falls back to a more
 | ||||
| // expensive basic C implementation for debugging purposes
 | ||||
| 
 | ||||
| // Min/max functions for unsigned 32-bit numbers
 | ||||
| static inline uint32_t lfs_max(uint32_t a, uint32_t b) { | ||||
| static inline uint32_t lfs2_max(uint32_t a, uint32_t b) { | ||||
|     return (a > b) ? a : b; | ||||
| } | ||||
| 
 | ||||
| static inline uint32_t lfs_min(uint32_t a, uint32_t b) { | ||||
| static inline uint32_t lfs2_min(uint32_t a, uint32_t b) { | ||||
|     return (a < b) ? a : b; | ||||
| } | ||||
| 
 | ||||
| // Align to nearest multiple of a size
 | ||||
| static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { | ||||
| static inline uint32_t lfs2_aligndown(uint32_t a, uint32_t alignment) { | ||||
|     return a - (a % alignment); | ||||
| } | ||||
| 
 | ||||
| static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { | ||||
|     return lfs_aligndown(a + alignment-1, alignment); | ||||
| static inline uint32_t lfs2_alignup(uint32_t a, uint32_t alignment) { | ||||
|     return lfs2_aligndown(a + alignment-1, alignment); | ||||
| } | ||||
| 
 | ||||
| // Find the smallest power of 2 greater than or equal to a
 | ||||
| static inline uint32_t lfs_npw2(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||
| static inline uint32_t lfs2_npw2(uint32_t a) { | ||||
| #if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||
|     return 32 - __builtin_clz(a-1); | ||||
| #else | ||||
|     uint32_t r = 0; | ||||
| @@ -128,18 +138,18 @@ static inline uint32_t lfs_npw2(uint32_t a) { | ||||
| } | ||||
| 
 | ||||
| // Count the number of trailing binary zeros in a
 | ||||
| // lfs_ctz(0) may be undefined
 | ||||
| static inline uint32_t lfs_ctz(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) | ||||
| // lfs2_ctz(0) may be undefined
 | ||||
| static inline uint32_t lfs2_ctz(uint32_t a) { | ||||
| #if !defined(LFS2_NO_INTRINSICS) && defined(__GNUC__) | ||||
|     return __builtin_ctz(a); | ||||
| #else | ||||
|     return lfs_npw2((a & -a) + 1) - 1; | ||||
|     return lfs2_npw2((a & -a) + 1) - 1; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| // Count the number of binary ones in a
 | ||||
| static inline uint32_t lfs_popc(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||
| static inline uint32_t lfs2_popc(uint32_t a) { | ||||
| #if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||
|     return __builtin_popcount(a); | ||||
| #else | ||||
|     a = a - ((a >> 1) & 0x55555555); | ||||
| @@ -150,18 +160,18 @@ static inline uint32_t lfs_popc(uint32_t a) { | ||||
| 
 | ||||
| // Find the sequence comparison of a and b, this is the distance
 | ||||
| // between a and b ignoring overflow
 | ||||
| static inline int lfs_scmp(uint32_t a, uint32_t b) { | ||||
| static inline int lfs2_scmp(uint32_t a, uint32_t b) { | ||||
|     return (int)(unsigned)(a - b); | ||||
| } | ||||
| 
 | ||||
| // Convert between 32-bit little-endian and native order
 | ||||
| static inline uint32_t lfs_fromle32(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||
| static inline uint32_t lfs2_fromle32(uint32_t a) { | ||||
| #if !defined(LFS2_NO_INTRINSICS) && ( \ | ||||
|     (defined(  BYTE_ORDER  ) && 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(LFS_NO_INTRINSICS) && ( \ | ||||
| #elif !defined(LFS2_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__)) | ||||
| @@ -174,18 +184,18 @@ static inline uint32_t lfs_fromle32(uint32_t a) { | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static inline uint32_t lfs_tole32(uint32_t a) { | ||||
|     return lfs_fromle32(a); | ||||
| static inline uint32_t lfs2_tole32(uint32_t a) { | ||||
|     return lfs2_fromle32(a); | ||||
| } | ||||
| 
 | ||||
| // Convert between 32-bit big-endian and native order
 | ||||
| static inline uint32_t lfs_frombe32(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||
| static inline uint32_t lfs2_frombe32(uint32_t a) { | ||||
| #if !defined(LFS2_NO_INTRINSICS) && ( \ | ||||
|     (defined(  BYTE_ORDER  ) && 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(LFS_NO_INTRINSICS) && ( \ | ||||
| #elif !defined(LFS2_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__)) | ||||
| @@ -198,17 +208,17 @@ static inline uint32_t lfs_frombe32(uint32_t a) { | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static inline uint32_t lfs_tobe32(uint32_t a) { | ||||
|     return lfs_frombe32(a); | ||||
| static inline uint32_t lfs2_tobe32(uint32_t a) { | ||||
|     return lfs2_frombe32(a); | ||||
| } | ||||
| 
 | ||||
| // Calculate CRC-32 with polynomial = 0x04c11db7
 | ||||
| uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); | ||||
| uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size); | ||||
| 
 | ||||
| // Allocate memory, only used if buffers are not provided to littlefs
 | ||||
| // Note, memory must be 64-bit aligned
 | ||||
| static inline void *lfs_malloc(size_t size) { | ||||
| #ifndef LFS_NO_MALLOC | ||||
| static inline void *lfs2_malloc(size_t size) { | ||||
| #ifndef LFS2_NO_MALLOC | ||||
|     return malloc(size); | ||||
| #else | ||||
|     (void)size; | ||||
| @@ -217,8 +227,8 @@ static inline void *lfs_malloc(size_t size) { | ||||
| } | ||||
| 
 | ||||
| // Deallocate memory, only used if buffers are not provided to littlefs
 | ||||
| static inline void lfs_free(void *p) { | ||||
| #ifndef LFS_NO_MALLOC | ||||
| static inline void lfs2_free(void *p) { | ||||
| #ifndef LFS2_NO_MALLOC | ||||
|     free(p); | ||||
| #else | ||||
|     (void)p; | ||||
							
								
								
									
										214
									
								
								scripts/code.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										214
									
								
								scripts/code.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,214 @@ | ||||
| #!/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()))) | ||||
							
								
								
									
										254
									
								
								scripts/coverage.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										254
									
								
								scripts/coverage.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,254 @@ | ||||
| #!/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()))) | ||||
| @@ -3,8 +3,8 @@ | ||||
| import re | ||||
| import sys | ||||
|  | ||||
| PATTERN = ['LFS_ASSERT', 'assert'] | ||||
| PREFIX = 'LFS' | ||||
| PATTERN = ['LFS2_ASSERT', 'assert'] | ||||
| PREFIX = 'LFS2' | ||||
| MAXWIDTH = 16 | ||||
|  | ||||
| ASSERT = "__{PREFIX}_ASSERT_{TYPE}_{COMP}" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| # conflict at compile time. | ||||
| # | ||||
| # example: | ||||
| # $ ./scripts/prefix.py lfs2 | ||||
| # $ ./scripts/prefix.py lfs22 | ||||
|  | ||||
| import os | ||||
| import os.path | ||||
| @@ -16,7 +16,7 @@ import tempfile | ||||
| import shutil | ||||
| import subprocess | ||||
|  | ||||
| DEFAULT_PREFIX = "lfs" | ||||
| DEFAULT_PREFIX = "lfs2" | ||||
|  | ||||
| def subn(from_prefix, to_prefix, name): | ||||
|     name, count1 = re.subn('\\b'+from_prefix, to_prefix, name) | ||||
|   | ||||
| @@ -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.tags, args.log, args.all]) else "")) | ||||
|         if not any([args.no_truncate, args.log, args.all]) else "")) | ||||
|  | ||||
|     # print gstate | ||||
|     print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) | ||||
|   | ||||
							
								
								
									
										287
									
								
								scripts/test.py
									
									
									
									
									
								
							
							
						
						
									
										287
									
								
								scripts/test.py
									
									
									
									
									
								
							| @@ -20,79 +20,110 @@ import pty | ||||
| import errno | ||||
| import signal | ||||
|  | ||||
| TESTDIR = 'tests' | ||||
| TEST_PATHS = 'tests' | ||||
| RULES = """ | ||||
| # add block devices to sources | ||||
| TESTSRC ?= $(SRC) $(wildcard bd/*.c) | ||||
|  | ||||
| define FLATTEN | ||||
| tests/%$(subst /,.,$(target)): $(target) | ||||
| %(path)s%%$(subst /,.,$(target)): $(target) | ||||
|     ./scripts/explode_asserts.py $$< -o $$@ | ||||
| endef | ||||
| $(foreach target,$(SRC),$(eval $(FLATTEN))) | ||||
|  | ||||
| -include tests/*.d | ||||
| $(foreach target,$(TESTSRC),$(eval $(FLATTEN))) | ||||
|  | ||||
| -include %(path)s*.d | ||||
| .SECONDARY: | ||||
| %.test: %.test.o $(foreach f,$(subst /,.,$(SRC:.c=.o)),%.$f) | ||||
|  | ||||
| %(path)s.test: %(path)s.test.o \\ | ||||
|         $(foreach t,$(subst /,.,$(TESTSRC:.c=.o)),%(path)s.$t) | ||||
|     $(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 | ||||
|  | ||||
| # delete lingering coverage | ||||
| %(path)s.test: | %(path)s.info.clean | ||||
| .PHONY: %(path)s.info.clean | ||||
| %(path)s.info.clean: | ||||
|     rm -f %(path)s*.gcda | ||||
|  | ||||
| # 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 | ||||
| """ | ||||
| GLOBALS = """ | ||||
| //////////////// AUTOGENERATED TEST //////////////// | ||||
| #include "lfs.h" | ||||
| #include "bd/lfs_testbd.h" | ||||
| #include "lfs2.h" | ||||
| #include "bd/lfs2_testbd.h" | ||||
| #include <stdio.h> | ||||
| extern const char *lfs_testbd_path; | ||||
| extern uint32_t lfs_testbd_cycles; | ||||
| extern const char *lfs2_testbd_path; | ||||
| extern uint32_t lfs2_testbd_cycles; | ||||
| """ | ||||
| DEFINES = { | ||||
|     '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', | ||||
|     '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', | ||||
| } | ||||
| PROLOGUE = """ | ||||
|     // prologue | ||||
|     __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)) 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)) char path[1024]; | ||||
|     __attribute__((unused)) uint8_t buffer[1024]; | ||||
|     __attribute__((unused)) lfs_size_t size; | ||||
|     __attribute__((unused)) lfs2_size_t size; | ||||
|     __attribute__((unused)) int err; | ||||
|      | ||||
|     __attribute__((unused)) const struct lfs_config cfg = { | ||||
|     __attribute__((unused)) const struct lfs2_config cfg = { | ||||
|         .context        = &bd, | ||||
|         .read           = lfs_testbd_read, | ||||
|         .prog           = lfs_testbd_prog, | ||||
|         .erase          = lfs_testbd_erase, | ||||
|         .sync           = lfs_testbd_sync, | ||||
|         .read_size      = LFS_READ_SIZE, | ||||
|         .prog_size      = LFS_PROG_SIZE, | ||||
|         .block_size     = LFS_BLOCK_SIZE, | ||||
|         .block_count    = LFS_BLOCK_COUNT, | ||||
|         .block_cycles   = LFS_BLOCK_CYCLES, | ||||
|         .cache_size     = LFS_CACHE_SIZE, | ||||
|         .lookahead_size = LFS_LOOKAHEAD_SIZE, | ||||
|         .read           = 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_testbd_config bdcfg = { | ||||
|         .erase_value        = LFS_ERASE_VALUE, | ||||
|         .erase_cycles       = LFS_ERASE_CYCLES, | ||||
|         .badblock_behavior  = LFS_BADBLOCK_BEHAVIOR, | ||||
|         .power_cycles       = lfs_testbd_cycles, | ||||
|     __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, | ||||
|     }; | ||||
|  | ||||
|     lfs_testbd_createcfg(&cfg, lfs_testbd_path, &bdcfg) => 0; | ||||
|     lfs2_testbd_createcfg(&cfg, lfs2_testbd_path, &bdcfg) => 0; | ||||
| """ | ||||
| EPILOGUE = """ | ||||
|     // epilogue | ||||
|     lfs_testbd_destroy(&cfg) => 0; | ||||
|     lfs2_testbd_destroy(&cfg) => 0; | ||||
| """ | ||||
| PASS = '\033[32m✓\033[0m' | ||||
| FAIL = '\033[31m✗\033[0m' | ||||
| @@ -119,6 +150,8 @@ 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): | ||||
| @@ -179,7 +212,7 @@ class TestCase: | ||||
|                 len(self.filter) >= 2 and | ||||
|                 self.filter[1] != self.permno): | ||||
|             return False | ||||
|         elif args.get('no_internal', False) and self.in_ is not None: | ||||
|         elif args.get('no_internal') and self.in_ is not None: | ||||
|             return False | ||||
|         elif self.if_ is not None: | ||||
|             if_ = self.if_ | ||||
| @@ -213,7 +246,7 @@ class TestCase: | ||||
|                 try: | ||||
|                     with open(disk, 'w') as f: | ||||
|                         f.truncate(0) | ||||
|                     if args.get('verbose', False): | ||||
|                     if args.get('verbose'): | ||||
|                         print('truncate --size=0', disk) | ||||
|                 except FileNotFoundError: | ||||
|                     pass | ||||
| @@ -237,14 +270,14 @@ class TestCase: | ||||
|                     '-ex', 'r']) | ||||
|             ncmd.extend(['--args'] + cmd) | ||||
|  | ||||
|             if args.get('verbose', False): | ||||
|             if args.get('verbose'): | ||||
|                 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', False): | ||||
|         if args.get('verbose'): | ||||
|             print(' '.join(shlex.quote(c) for c in cmd)) | ||||
|         proc = sp.Popen(cmd, stdout=spty, stderr=spty) | ||||
|         os.close(spty) | ||||
| @@ -259,8 +292,10 @@ class TestCase: | ||||
|                     if e.errno == errno.EIO: | ||||
|                         break | ||||
|                     raise | ||||
|                 if not line: | ||||
|                     break; | ||||
|                 stdout.append(line) | ||||
|                 if args.get('verbose', False): | ||||
|                 if args.get('verbose'): | ||||
|                     sys.stdout.write(line) | ||||
|                 # intercept asserts | ||||
|                 m = re.match( | ||||
| @@ -299,8 +334,8 @@ class ValgrindTestCase(TestCase): | ||||
|         return not self.leaky and super().shouldtest(**args) | ||||
|  | ||||
|     def test(self, exec=[], **args): | ||||
|         verbose = args.get('verbose', False) | ||||
|         uninit = (self.defines.get('LFS_ERASE_VALUE', None) == -1) | ||||
|         verbose = args.get('verbose') | ||||
|         uninit = (self.defines.get('LFS2_ERASE_VALUE', None) == -1) | ||||
|         exec = [ | ||||
|             'valgrind', | ||||
|             '--leak-check=full', | ||||
| @@ -351,12 +386,17 @@ class TestSuite: | ||||
|         self.name = os.path.basename(path) | ||||
|         if self.name.endswith('.toml'): | ||||
|             self.name = self.name[:-len('.toml')] | ||||
|         self.path = path | ||||
|         if args.get('build_dir'): | ||||
|             self.toml = path | ||||
|             self.path = args['build_dir'] + '/' + path | ||||
|         else: | ||||
|             self.toml = path | ||||
|             self.path = path | ||||
|         self.classes = classes | ||||
|         self.defines = defines.copy() | ||||
|         self.filter = filter | ||||
|  | ||||
|         with open(path) as f: | ||||
|         with open(self.toml) as f: | ||||
|             # load tests | ||||
|             config = toml.load(f) | ||||
|  | ||||
| @@ -467,7 +507,7 @@ class TestSuite: | ||||
|  | ||||
|     def build(self, **args): | ||||
|         # build test files | ||||
|         tf = open(self.path + '.test.c.t', 'w') | ||||
|         tf = open(self.path + '.test.tc', 'w') | ||||
|         tf.write(GLOBALS) | ||||
|         if self.code is not None: | ||||
|             tf.write('#line %d "%s"\n' % (self.code_lineno, self.path)) | ||||
| @@ -477,7 +517,7 @@ class TestSuite: | ||||
|         for case in self.cases: | ||||
|             if case.in_ not in tfs: | ||||
|                 tfs[case.in_] = open(self.path+'.'+ | ||||
|                     case.in_.replace('/', '.')+'.t', 'w') | ||||
|                     re.sub('(\.c)?$', '.tc', case.in_.replace('/', '.')), 'w') | ||||
|                 tfs[case.in_].write('#line 1 "%s"\n' % case.in_) | ||||
|                 with open(case.in_) as f: | ||||
|                     for line in f: | ||||
| @@ -489,13 +529,13 @@ class TestSuite: | ||||
|             case.build(tfs[case.in_], **args) | ||||
|  | ||||
|         tf.write('\n') | ||||
|         tf.write('const char *lfs_testbd_path;\n') | ||||
|         tf.write('uint32_t lfs_testbd_cycles;\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*' '+'lfs_testbd_path   = (argc > 3) ? argv[3] : NULL;\n') | ||||
|         tf.write(4*' '+'lfs_testbd_cycles = (argc > 4) ? atoi(argv[4]) : 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') | ||||
|         for perm in self.perms: | ||||
|             # test declaration | ||||
|             tf.write(4*' '+'extern void test_case%d(%s);\n' % ( | ||||
| @@ -516,25 +556,33 @@ class TestSuite: | ||||
|  | ||||
|         # write makefiles | ||||
|         with open(self.path + '.mk', 'w') as mk: | ||||
|             mk.write(RULES.replace(4*' ', '\t')) | ||||
|             mk.write(RULES.replace(4*' ', '\t') % dict(path=self.path)) | ||||
|             mk.write('\n') | ||||
|  | ||||
|             # add truely global defines globally | ||||
|             # 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 | ||||
|             for k, v in sorted(self.defines.items()): | ||||
|                 mk.write('%s: override CFLAGS += -D%s=%r\n' % ( | ||||
|                     self.path+'.test', k, v)) | ||||
|                 mk.write('%s.test: override CFLAGS += -D%s=%r\n' | ||||
|                     % (self.path, k, v)) | ||||
|  | ||||
|             for path in tfs: | ||||
|                 if path is None: | ||||
|                     mk.write('%s: %s | %s\n' % ( | ||||
|                         self.path+'.test.c', | ||||
|                         self.path, | ||||
|                         self.path+'.test.c.t')) | ||||
|                         self.toml, | ||||
|                         self.path+'.test.tc')) | ||||
|                 else: | ||||
|                     mk.write('%s: %s %s | %s\n' % ( | ||||
|                         self.path+'.'+path.replace('/', '.'), | ||||
|                         self.path, path, | ||||
|                         self.path+'.'+path.replace('/', '.')+'.t')) | ||||
|                         self.toml, | ||||
|                         path, | ||||
|                         self.path+'.'+re.sub('(\.c)?$', '.tc', | ||||
|                             path.replace('/', '.')))) | ||||
|                 mk.write('\t./scripts/explode_asserts.py $| -o $@\n') | ||||
|  | ||||
|         self.makefile = self.path + '.mk' | ||||
| @@ -557,7 +605,7 @@ class TestSuite: | ||||
|                 if not args.get('verbose', True): | ||||
|                     sys.stdout.write(FAIL) | ||||
|                     sys.stdout.flush() | ||||
|                 if not args.get('keep_going', False): | ||||
|                 if not args.get('keep_going'): | ||||
|                     if not args.get('verbose', True): | ||||
|                         sys.stdout.write('\n') | ||||
|                     raise | ||||
| @@ -579,36 +627,36 @@ def main(**args): | ||||
|  | ||||
|     # and what class of TestCase to run | ||||
|     classes = [] | ||||
|     if args.get('normal', False): | ||||
|     if args.get('normal'): | ||||
|         classes.append(TestCase) | ||||
|     if args.get('reentrant', False): | ||||
|     if args.get('reentrant'): | ||||
|         classes.append(ReentrantTestCase) | ||||
|     if args.get('valgrind', False): | ||||
|     if args.get('valgrind'): | ||||
|         classes.append(ValgrindTestCase) | ||||
|     if not classes: | ||||
|         classes = [TestCase] | ||||
|  | ||||
|     suites = [] | ||||
|     for testpath in args['testpaths']: | ||||
|     for testpath in args['test_paths']: | ||||
|         # 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 + '/test_*.toml' | ||||
|             testpath = testpath + '/*.toml' | ||||
|         elif os.path.isfile(testpath): | ||||
|             testpath = testpath | ||||
|         elif testpath.endswith('.toml'): | ||||
|             testpath = TESTDIR + '/' + testpath | ||||
|             testpath = TEST_PATHS + '/' + testpath | ||||
|         else: | ||||
|             testpath = TESTDIR + '/' + testpath + '.toml' | ||||
|             testpath = TEST_PATHS + '/' + testpath + '.toml' | ||||
|  | ||||
|         # find tests | ||||
|         for path in glob.glob(testpath): | ||||
|             suites.append(TestSuite(path, classes, defines, filter, **args)) | ||||
|  | ||||
|     # sort for reproducability | ||||
|     # sort for reproducibility | ||||
|     suites = sorted(suites) | ||||
|  | ||||
|     # generate permutations | ||||
| @@ -628,7 +676,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', False): | ||||
|     if args.get('verbose'): | ||||
|         print(' '.join(shlex.quote(c) for c in cmd)) | ||||
|     proc = sp.Popen(cmd, stdout=spty, stderr=spty) | ||||
|     os.close(spty) | ||||
| @@ -641,15 +689,17 @@ def main(**args): | ||||
|             if e.errno == errno.EIO: | ||||
|                 break | ||||
|             raise | ||||
|         if not line: | ||||
|             break; | ||||
|         stdout.append(line) | ||||
|         if args.get('verbose', False): | ||||
|         if args.get('verbose'): | ||||
|             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', False): | ||||
|         if m and not args.get('verbose'): | ||||
|             try: | ||||
|                 with open(m.group(1)) as f: | ||||
|                     lineno = int(m.group(2)) | ||||
| @@ -662,27 +712,26 @@ def main(**args): | ||||
|             except: | ||||
|                 pass | ||||
|     proc.wait() | ||||
|  | ||||
|     if proc.returncode != 0: | ||||
|         if not args.get('verbose', False): | ||||
|         if not args.get('verbose'): | ||||
|             for line in stdout: | ||||
|                 sys.stdout.write(line) | ||||
|         sys.exit(-3) | ||||
|         sys.exit(-1) | ||||
|  | ||||
|     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))) | ||||
|  | ||||
|     filtered = 0 | ||||
|     total = 0 | ||||
|     for suite in suites: | ||||
|         for perm in suite.perms: | ||||
|             filtered += perm.shouldtest(**args) | ||||
|     if filtered != sum(len(suite.perms) for suite in suites): | ||||
|         print('filtered down to %d permutations' % filtered) | ||||
|             total += perm.shouldtest(**args) | ||||
|     if total != sum(len(suite.perms) for suite in suites): | ||||
|         print('filtered down to %d permutations' % total) | ||||
|  | ||||
|     # only requested to build? | ||||
|     if args.get('build', False): | ||||
|     if args.get('build'): | ||||
|         return 0 | ||||
|  | ||||
|     print('====== testing ======') | ||||
| @@ -697,15 +746,12 @@ 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 | ||||
|             else: | ||||
|             elif isinstance(perm.result, TestFailure): | ||||
|                 sys.stdout.write( | ||||
|                     "\033[01m{path}:{lineno}:\033[01;31mfailure:\033[m " | ||||
|                     "{perm} failed with {returncode}\n".format( | ||||
|                     "{perm} failed\n".format( | ||||
|                         perm=perm, path=perm.suite.path, lineno=perm.lineno, | ||||
|                         returncode=perm.result.returncode or 0)) | ||||
|                 if perm.result.stdout: | ||||
| @@ -723,11 +769,33 @@ def main(**args): | ||||
|                 sys.stdout.write('\n') | ||||
|                 failed += 1 | ||||
|  | ||||
|     if args.get('gdb', False): | ||||
|     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'): | ||||
|         failure = None | ||||
|         for suite in suites: | ||||
|             for perm in suite.perms: | ||||
|                 if getattr(perm, 'result', PASS) != PASS: | ||||
|                 if isinstance(perm.result, TestFailure): | ||||
|                     failure = perm.result | ||||
|         if failure is not None: | ||||
|             print('======= gdb ======') | ||||
| @@ -735,20 +803,22 @@ def main(**args): | ||||
|             failure.case.test(failure=failure, **args) | ||||
|             sys.exit(0) | ||||
|  | ||||
|     print('tests passed: %d' % passed) | ||||
|     print('tests failed: %d' % failed) | ||||
|     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))) | ||||
|     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('testpaths', nargs='*', default=[TESTDIR], | ||||
|     parser.add_argument('test_paths', nargs='*', default=[TEST_PATHS], | ||||
|         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 a \ | ||||
|             specific test case by adding brackets. For example \ | ||||
|             \"test_dirs[0]\" or \"{0}/test_dirs.toml[0]\".".format(TESTDIR)) | ||||
|             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)) | ||||
|     parser.add_argument('-D', action='append', default=[], | ||||
|         help="Overriding parameter definitions.") | ||||
|     parser.add_argument('-v', '--verbose', action='store_true', | ||||
| @@ -769,10 +839,19 @@ 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('-V', '--valgrind', action='store_true', | ||||
|     parser.add_argument('--valgrind', action='store_true', | ||||
|         help="Run non-leaky tests under valgrind to check for memory leaks.") | ||||
|     parser.add_argument('-e', '--exec', default=[], type=lambda e: e.split(' '), | ||||
|     parser.add_argument('--exec', default=[], type=lambda e: e.split(), | ||||
|         help="Run tests with another executable prefixed on the command line.") | ||||
|     parser.add_argument('-d', '--disk', | ||||
|     parser.add_argument('--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()))) | ||||
|   | ||||
| @@ -1,409 +1,409 @@ | ||||
| # allocator tests | ||||
| # note for these to work there are a number constraints on the device geometry | ||||
| if = 'LFS_BLOCK_CYCLES == -1' | ||||
| if = 'LFS2_BLOCK_CYCLES == -1' | ||||
|  | ||||
| [[case]] # parallel allocation test | ||||
| define.FILES = 3 | ||||
| define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' | ||||
| define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||
| code = ''' | ||||
|     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||
|     lfs_file_t files[FILES]; | ||||
|     lfs2_file_t files[FILES]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "breakfast") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int n = 0; n < FILES; n++) { | ||||
|         sprintf(path, "breakfast/%s", names[n]); | ||||
|         lfs_file_open(&lfs, &files[n], path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|         lfs2_file_open(&lfs2, &files[n], path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||
|     } | ||||
|     for (int n = 0; n < FILES; n++) { | ||||
|         size = strlen(names[n]); | ||||
|         for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||
|             lfs_file_write(&lfs, &files[n], names[n], size) => size; | ||||
|         for (lfs2_size_t i = 0; i < SIZE; i += size) { | ||||
|             lfs2_file_write(&lfs2, &files[n], names[n], size) => size; | ||||
|         } | ||||
|     } | ||||
|     for (int n = 0; n < FILES; n++) { | ||||
|         lfs_file_close(&lfs, &files[n]) => 0; | ||||
|         lfs2_file_close(&lfs2, &files[n]) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int n = 0; n < FILES; n++) { | ||||
|         sprintf(path, "breakfast/%s", names[n]); | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|         size = strlen(names[n]); | ||||
|         for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         for (lfs2_size_t i = 0; i < SIZE; i += size) { | ||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|             assert(memcmp(buffer, names[n], size) == 0); | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # serial allocation test | ||||
| define.FILES = 3 | ||||
| define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' | ||||
| define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||
| code = ''' | ||||
|     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "breakfast") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     for (int n = 0; n < FILES; n++) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         sprintf(path, "breakfast/%s", names[n]); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||
|         size = strlen(names[n]); | ||||
|         memcpy(buffer, names[n], size); | ||||
|         for (int i = 0; i < SIZE; i += size) { | ||||
|             lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int n = 0; n < FILES; n++) { | ||||
|         sprintf(path, "breakfast/%s", names[n]); | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|         size = strlen(names[n]); | ||||
|         for (int i = 0; i < SIZE; i += size) { | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|             assert(memcmp(buffer, names[n], size) == 0); | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # parallel allocation reuse test | ||||
| define.FILES = 3 | ||||
| define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' | ||||
| define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||
| define.CYCLES = [1, 10] | ||||
| code = ''' | ||||
|     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||
|     lfs_file_t files[FILES]; | ||||
|     lfs2_file_t files[FILES]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     for (int c = 0; c < CYCLES; c++) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs_mkdir(&lfs, "breakfast") => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             sprintf(path, "breakfast/%s", names[n]); | ||||
|             lfs_file_open(&lfs, &files[n], path, | ||||
|                     LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|             lfs2_file_open(&lfs2, &files[n], path, | ||||
|                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||
|         } | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             size = strlen(names[n]); | ||||
|             for (int i = 0; i < SIZE; i += size) { | ||||
|                 lfs_file_write(&lfs, &files[n], names[n], size) => size; | ||||
|                 lfs2_file_write(&lfs2, &files[n], names[n], size) => size; | ||||
|             } | ||||
|         } | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             lfs_file_close(&lfs, &files[n]) => 0; | ||||
|             lfs2_file_close(&lfs2, &files[n]) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             sprintf(path, "breakfast/%s", names[n]); | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|             size = strlen(names[n]); | ||||
|             for (int i = 0; i < SIZE; i += size) { | ||||
|                 lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|                 lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|                 assert(memcmp(buffer, names[n], size) == 0); | ||||
|             } | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             sprintf(path, "breakfast/%s", names[n]); | ||||
|             lfs_remove(&lfs, path) => 0; | ||||
|             lfs2_remove(&lfs2, path) => 0; | ||||
|         } | ||||
|         lfs_remove(&lfs, "breakfast") => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_remove(&lfs2, "breakfast") => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|     } | ||||
| ''' | ||||
|  | ||||
| [[case]] # serial allocation reuse test | ||||
| define.FILES = 3 | ||||
| define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' | ||||
| define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||
| define.CYCLES = [1, 10] | ||||
| code = ''' | ||||
|     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     for (int c = 0; c < CYCLES; c++) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs_mkdir(&lfs, "breakfast") => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             lfs_mount(&lfs, &cfg) => 0; | ||||
|             lfs2_mount(&lfs2, &cfg) => 0; | ||||
|             sprintf(path, "breakfast/%s", names[n]); | ||||
|             lfs_file_open(&lfs, &file, path, | ||||
|                     LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, | ||||
|                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||
|             size = strlen(names[n]); | ||||
|             memcpy(buffer, names[n], size); | ||||
|             for (int i = 0; i < SIZE; i += size) { | ||||
|                 lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|                 lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|             } | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs_unmount(&lfs) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|             lfs2_unmount(&lfs2) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             sprintf(path, "breakfast/%s", names[n]); | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|             size = strlen(names[n]); | ||||
|             for (int i = 0; i < SIZE; i += size) { | ||||
|                 lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|                 lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|                 assert(memcmp(buffer, names[n], size) == 0); | ||||
|             } | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (int n = 0; n < FILES; n++) { | ||||
|             sprintf(path, "breakfast/%s", names[n]); | ||||
|             lfs_remove(&lfs, path) => 0; | ||||
|             lfs2_remove(&lfs2, path) => 0; | ||||
|         } | ||||
|         lfs_remove(&lfs, "breakfast") => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_remove(&lfs2, "breakfast") => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|     } | ||||
| ''' | ||||
|  | ||||
| [[case]] # exhaustion test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     size = strlen("exhaustion"); | ||||
|     memcpy(buffer, "exhaustion", size); | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|  | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     lfs_ssize_t res; | ||||
|     lfs2_ssize_t res; | ||||
|     while (true) { | ||||
|         res = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         res = lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|         if (res < 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         res => size; | ||||
|     } | ||||
|     res => LFS_ERR_NOSPC; | ||||
|     res => LFS2_ERR_NOSPC; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY); | ||||
|     size = strlen("exhaustion"); | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "exhaustion", size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # exhaustion wraparound test | ||||
| define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / 3)' | ||||
| define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-4)) / 3)' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     size = strlen("buffering"); | ||||
|     memcpy(buffer, "buffering", size); | ||||
|     for (int i = 0; i < SIZE; i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_remove(&lfs, "padding") => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_remove(&lfs2, "padding") => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     size = strlen("exhaustion"); | ||||
|     memcpy(buffer, "exhaustion", size); | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|  | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     lfs_ssize_t res; | ||||
|     lfs2_ssize_t res; | ||||
|     while (true) { | ||||
|         res = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         res = lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|         if (res < 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         res => size; | ||||
|     } | ||||
|     res => LFS_ERR_NOSPC; | ||||
|     res => LFS2_ERR_NOSPC; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY); | ||||
|     size = strlen("exhaustion"); | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "exhaustion", size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # dir exhaustion test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // find out max file size | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||
|     lfs2_mkdir(&lfs2, "exhaustiondir") => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     int count = 0; | ||||
|     while (true) { | ||||
|         err = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         err = lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|         if (err < 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         count += 1; | ||||
|     } | ||||
|     err => LFS_ERR_NOSPC; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     err => LFS2_ERR_NOSPC; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustiondir") => 0; | ||||
|  | ||||
|     // see if dir fits with max file size | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     for (int i = 0; i < count; i++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||
|     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs2_mkdir(&lfs2, "exhaustiondir") => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustiondir") => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; | ||||
|  | ||||
|     // see if dir fits with > max file size | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     for (int i = 0; i < count+1; i++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||
|     lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # what if we have a bad block during an allocation scan? | ||||
| in = "lfs.c" | ||||
| define.LFS_ERASE_CYCLES = 0xffffffff | ||||
| define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR' | ||||
| in = "lfs2.c" | ||||
| define.LFS2_ERASE_CYCLES = 0xffffffff | ||||
| define.LFS2_BADBLOCK_BEHAVIOR = 'LFS2_TESTBD_BADBLOCK_READERROR' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     // first fill to exhaustion to find available space | ||||
|     lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     strcpy((char*)buffer, "waka"); | ||||
|     size = strlen("waka"); | ||||
|     lfs_size_t filesize = 0; | ||||
|     lfs2_size_t filesize = 0; | ||||
|     while (true) { | ||||
|         lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC); | ||||
|         if (res == LFS_ERR_NOSPC) { | ||||
|         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) { | ||||
|             break; | ||||
|         } | ||||
|         filesize += size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // now fill all but a couple of blocks of the filesystem with data | ||||
|     filesize -= 3*LFS_BLOCK_SIZE; | ||||
|     lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     filesize -= 3*LFS2_BLOCK_SIZE; | ||||
|     lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     strcpy((char*)buffer, "waka"); | ||||
|     size = strlen("waka"); | ||||
|     for (lfs_size_t i = 0; i < filesize/size; i++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_size_t i = 0; i < filesize/size; i++) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // also save head of file so we can error during lookahead scan | ||||
|     lfs_block_t fileblock = file.ctz.head; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_block_t fileblock = file.ctz.head; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // remount to force an alloc scan | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // but mark the head of our file as a "bad block", this is force our | ||||
|     // scan to bail early | ||||
|     lfs_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0; | ||||
|     lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "ghost", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     strcpy((char*)buffer, "chomp"); | ||||
|     size = strlen("chomp"); | ||||
|     while (true) { | ||||
|         lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         assert(res == (lfs_ssize_t)size || res == LFS_ERR_CORRUPT); | ||||
|         if (res == LFS_ERR_CORRUPT) { | ||||
|         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) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // now reverse the "bad block" and try to write the file again until we | ||||
|     // run out of space | ||||
|     lfs_testbd_setwear(&cfg, fileblock, 0) => 0; | ||||
|     lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_testbd_setwear(&cfg, fileblock, 0) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "ghost", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     strcpy((char*)buffer, "chomp"); | ||||
|     size = strlen("chomp"); | ||||
|     while (true) { | ||||
|         lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC); | ||||
|         if (res == LFS_ERR_NOSPC) { | ||||
|         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) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // check that the disk isn't hurt | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_RDONLY) => 0; | ||||
|     strcpy((char*)buffer, "waka"); | ||||
|     size = strlen("waka"); | ||||
|     for (lfs_size_t i = 0; i < filesize/size; i++) { | ||||
|     for (lfs2_size_t i = 0; i < filesize/size; i++) { | ||||
|         uint8_t rbuffer[4]; | ||||
|         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|         assert(memcmp(rbuffer, buffer, size) == 0); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
|  | ||||
| @@ -412,242 +412,242 @@ code = ''' | ||||
| # should be removed and replaced with generalized tests. | ||||
|  | ||||
| [[case]] # chained dir exhaustion test | ||||
| define.LFS_BLOCK_SIZE = 512 | ||||
| define.LFS_BLOCK_COUNT = 1024 | ||||
| if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' | ||||
| define.LFS2_BLOCK_SIZE = 512 | ||||
| define.LFS2_BLOCK_COUNT = 1024 | ||||
| if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // find out max file size | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||
|     lfs2_mkdir(&lfs2, "exhaustiondir") => 0; | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_mkdir(&lfs, path) => 0; | ||||
|         lfs2_mkdir(&lfs2, path) => 0; | ||||
|     } | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     int count = 0; | ||||
|     while (true) { | ||||
|         err = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         err = lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|         if (err < 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         count += 1; | ||||
|     } | ||||
|     err => LFS_ERR_NOSPC; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     err => LFS2_ERR_NOSPC; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; | ||||
|     lfs2_remove(&lfs2, "exhaustiondir") => 0; | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_remove(&lfs, path) => 0; | ||||
|         lfs2_remove(&lfs2, path) => 0; | ||||
|     } | ||||
|  | ||||
|     // see that chained dir fails | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     for (int i = 0; i < count+1; i++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_mkdir(&lfs, path) => 0; | ||||
|         lfs2_mkdir(&lfs2, path) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||
|     lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC; | ||||
|  | ||||
|     // shorten file to try a second chained dir | ||||
|     while (true) { | ||||
|         err = lfs_mkdir(&lfs, "exhaustiondir"); | ||||
|         if (err != LFS_ERR_NOSPC) { | ||||
|         err = lfs2_mkdir(&lfs2, "exhaustiondir"); | ||||
|         if (err != LFS2_ERR_NOSPC) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         lfs_ssize_t filesize = lfs_file_size(&lfs, &file); | ||||
|         lfs2_ssize_t filesize = lfs2_file_size(&lfs2, &file); | ||||
|         filesize > 0 => true; | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file, filesize - size) => 0; | ||||
|         lfs_file_sync(&lfs, &file) => 0; | ||||
|         lfs2_file_truncate(&lfs2, &file, filesize - size) => 0; | ||||
|         lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     } | ||||
|     err => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC; | ||||
|     lfs2_mkdir(&lfs2, "exhaustiondir2") => LFS2_ERR_NOSPC; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # split dir test | ||||
| define.LFS_BLOCK_SIZE = 512 | ||||
| define.LFS_BLOCK_COUNT = 1024 | ||||
| if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' | ||||
| define.LFS2_BLOCK_SIZE = 512 | ||||
| define.LFS2_BLOCK_COUNT = 1024 | ||||
| if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // create one block hole for half a directory | ||||
|     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) { | ||||
|     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) { | ||||
|         memcpy(&buffer[i], "hi", 2); | ||||
|     } | ||||
|     lfs_file_write(&lfs, &file, buffer, cfg.block_size) => cfg.block_size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, buffer, cfg.block_size) => cfg.block_size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < (cfg.block_count-4)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // open hole | ||||
|     lfs_remove(&lfs, "bump") => 0; | ||||
|     lfs2_remove(&lfs2, "bump") => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "splitdir") => 0; | ||||
|     lfs_file_open(&lfs, &file, "splitdir/bump", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { | ||||
|     lfs2_mkdir(&lfs2, "splitdir") => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "splitdir/bump", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     for (lfs2_size_t i = 0; i < cfg.block_size; i += 2) { | ||||
|         memcpy(&buffer[i], "hi", 2); | ||||
|     } | ||||
|     lfs_file_write(&lfs, &file, buffer, 2*cfg.block_size) => LFS_ERR_NOSPC; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, buffer, 2*cfg.block_size) => LFS2_ERR_NOSPC; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # outdated lookahead test | ||||
| define.LFS_BLOCK_SIZE = 512 | ||||
| define.LFS_BLOCK_COUNT = 1024 | ||||
| if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' | ||||
| define.LFS2_BLOCK_SIZE = 512 | ||||
| define.LFS2_BLOCK_COUNT = 1024 | ||||
| if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // fill completely with two files | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion1", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "exhaustion2", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion2", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // rewrite one file | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion1", | ||||
|             LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // rewrite second file, this requires lookahead does not | ||||
|     // use old population | ||||
|     lfs_file_open(&lfs, &file, "exhaustion2", | ||||
|             LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion2", | ||||
|             LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # outdated lookahead and split dir test | ||||
| define.LFS_BLOCK_SIZE = 512 | ||||
| define.LFS_BLOCK_COUNT = 1024 | ||||
| if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' | ||||
| define.LFS2_BLOCK_SIZE = 512 | ||||
| define.LFS2_BLOCK_COUNT = 1024 | ||||
| if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // fill completely with two files | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion1", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "exhaustion2", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion2", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // rewrite one file with a hole of one block | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "exhaustion1", | ||||
|             LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|     for (lfs2_size_t i = 0; | ||||
|             i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // try to allocate a directory, should fail! | ||||
|     lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC; | ||||
|     lfs2_mkdir(&lfs2, "split") => LFS2_ERR_NOSPC; | ||||
|  | ||||
|     // file should not fail | ||||
|     lfs_file_open(&lfs, &file, "notasplit", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_write(&lfs, &file, "hi", 2) => 2; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     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_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
| @@ -1,304 +1,304 @@ | ||||
| [[case]] # set/get attribute | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     lfs_setattr(&lfs, "hello", 'A', "aaaa",   4) => 0; | ||||
|     lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0; | ||||
|     lfs_setattr(&lfs, "hello", 'C', "ccccc",  5) => 0; | ||||
|     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 6; | ||||
|     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "hello", 'A', "aaaa",   4) => 0; | ||||
|     lfs2_setattr(&lfs2, "hello", 'B', "bbbbbb", 6) => 0; | ||||
|     lfs2_setattr(&lfs2, "hello", 'C', "ccccc",  5) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello", 'B', buffer+4,  6) => 6; | ||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",   4) => 0; | ||||
|     memcmp(buffer+4,  "bbbbbb", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",  5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "hello", 'B', "", 0) => 0; | ||||
|     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 0; | ||||
|     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "hello", 'B', "", 0) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello", 'B', buffer+4,  6) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",         4) => 0; | ||||
|     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",        5) => 0; | ||||
|  | ||||
|     lfs_removeattr(&lfs, "hello", 'B') => 0; | ||||
|     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => LFS_ERR_NOATTR; | ||||
|     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_removeattr(&lfs2, "hello", 'B') => 0; | ||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello", 'B', buffer+4,  6) => LFS2_ERR_NOATTR; | ||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",         4) => 0; | ||||
|     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",        5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0; | ||||
|     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 6; | ||||
|     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "hello", 'B', "dddddd", 6) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello", 'B', buffer+4,  6) => 6; | ||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",   4) => 0; | ||||
|     memcmp(buffer+4,  "dddddd", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",  5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0; | ||||
|     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 3; | ||||
|     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "hello", 'B', "eee", 3) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello", 'B', buffer+4,  6) => 3; | ||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",      4) => 0; | ||||
|     memcmp(buffer+4,  "eee\0\0\0", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",     5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; | ||||
|     lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0; | ||||
|     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "hello", 'B', buffer+4,  6) => 9; | ||||
|     lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "hello", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC; | ||||
|     lfs2_setattr(&lfs2, "hello", 'B', "fffffffff", 9) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello", 'B', buffer+4,  6) => 9; | ||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     lfs_getattr(&lfs, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "hello", 'B', buffer+4,  9) => 9; | ||||
|     lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5; | ||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello", 'B', buffer+4,  9) => 9; | ||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+13, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",      4) => 0; | ||||
|     memcmp(buffer+4,  "fffffffff", 9) => 0; | ||||
|     memcmp(buffer+13, "ccccc",     5) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello"); | ||||
|     lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello"); | ||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file); | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # set/get root attribute | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     lfs_setattr(&lfs, "/", 'A', "aaaa",   4) => 0; | ||||
|     lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0; | ||||
|     lfs_setattr(&lfs, "/", 'C', "ccccc",  5) => 0; | ||||
|     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 6; | ||||
|     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "/", 'A', "aaaa",   4) => 0; | ||||
|     lfs2_setattr(&lfs2, "/", 'B', "bbbbbb", 6) => 0; | ||||
|     lfs2_setattr(&lfs2, "/", 'C', "ccccc",  5) => 0; | ||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => 6; | ||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",   4) => 0; | ||||
|     memcmp(buffer+4,  "bbbbbb", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",  5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "/", 'B', "", 0) => 0; | ||||
|     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 0; | ||||
|     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "/", 'B', "", 0) => 0; | ||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => 0; | ||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",         4) => 0; | ||||
|     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",        5) => 0; | ||||
|  | ||||
|     lfs_removeattr(&lfs, "/", 'B') => 0; | ||||
|     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => LFS_ERR_NOATTR; | ||||
|     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_removeattr(&lfs2, "/", 'B') => 0; | ||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => LFS2_ERR_NOATTR; | ||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",         4) => 0; | ||||
|     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",        5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0; | ||||
|     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 6; | ||||
|     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "/", 'B', "dddddd", 6) => 0; | ||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => 6; | ||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",   4) => 0; | ||||
|     memcmp(buffer+4,  "dddddd", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",  5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0; | ||||
|     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 3; | ||||
|     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_setattr(&lfs2, "/", 'B', "eee", 3) => 0; | ||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => 3; | ||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",      4) => 0; | ||||
|     memcmp(buffer+4,  "eee\0\0\0", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",     5) => 0; | ||||
|  | ||||
|     lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; | ||||
|     lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0; | ||||
|     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "/", 'B', buffer+4,  6) => 9; | ||||
|     lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_setattr(&lfs2, "/", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC; | ||||
|     lfs2_setattr(&lfs2, "/", 'B', "fffffffff", 9) => 0; | ||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => 9; | ||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     lfs_getattr(&lfs, "/", 'A', buffer,    4) => 4; | ||||
|     lfs_getattr(&lfs, "/", 'B', buffer+4,  9) => 9; | ||||
|     lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5; | ||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  9) => 9; | ||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+13, 5) => 5; | ||||
|     memcmp(buffer,    "aaaa",      4) => 0; | ||||
|     memcmp(buffer+4,  "fffffffff", 9) => 0; | ||||
|     memcmp(buffer+13, "ccccc",     5) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello"); | ||||
|     lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello"); | ||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file); | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # set/get file attribute | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     struct lfs_attr attrs1[] = { | ||||
|     struct lfs2_attr attrs1[] = { | ||||
|         {'A', buffer,    4}, | ||||
|         {'B', buffer+4,  6}, | ||||
|         {'C', buffer+10, 5}, | ||||
|     }; | ||||
|     struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||
|     struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||
|  | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; | ||||
|     memcpy(buffer,    "aaaa",   4); | ||||
|     memcpy(buffer+4,  "bbbbbb", 6); | ||||
|     memcpy(buffer+10, "ccccc",  5); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     memset(buffer, 0, 15); | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     memcmp(buffer,    "aaaa",   4) => 0; | ||||
|     memcmp(buffer+4,  "bbbbbb", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",  5) => 0; | ||||
|  | ||||
|     attrs1[1].size = 0; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     memset(buffer, 0, 15); | ||||
|     attrs1[1].size = 6; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; | ||||
|     lfs2_file_close(&lfs2, &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; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; | ||||
|     memcpy(buffer+4,  "dddddd", 6); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     memset(buffer, 0, 15); | ||||
|     attrs1[1].size = 6; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     memcmp(buffer,    "aaaa",   4) => 0; | ||||
|     memcmp(buffer+4,  "dddddd", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",  5) => 0; | ||||
|  | ||||
|     attrs1[1].size = 3; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; | ||||
|     memcpy(buffer+4,  "eee", 3); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     memset(buffer, 0, 15); | ||||
|     attrs1[1].size = 6; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; | ||||
|     lfs2_file_close(&lfs2, &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 = LFS_ATTR_MAX+1; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) | ||||
|         => LFS_ERR_NOSPC; | ||||
|     attrs1[0].size = LFS2_ATTR_MAX+1; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) | ||||
|         => LFS2_ERR_NOSPC; | ||||
|  | ||||
|     struct lfs_attr attrs2[] = { | ||||
|     struct lfs2_attr attrs2[] = { | ||||
|         {'A', buffer,    4}, | ||||
|         {'B', buffer+4,  9}, | ||||
|         {'C', buffer+13, 5}, | ||||
|     }; | ||||
|     struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDWR, &cfg2) => 0; | ||||
|     struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDWR, &cfg2) => 0; | ||||
|     memcpy(buffer+4,  "fffffffff", 9); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     attrs1[0].size = 4; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     struct lfs_attr attrs3[] = { | ||||
|     struct lfs2_attr attrs3[] = { | ||||
|         {'A', buffer,    4}, | ||||
|         {'B', buffer+4,  9}, | ||||
|         {'C', buffer+13, 5}, | ||||
|     }; | ||||
|     struct lfs_file_config cfg3 = {.attrs=attrs3, .attr_count=3}; | ||||
|     struct lfs2_file_config cfg3 = {.attrs=attrs3, .attr_count=3}; | ||||
|  | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg3) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg3) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     memcmp(buffer,    "aaaa",      4) => 0; | ||||
|     memcmp(buffer+4,  "fffffffff", 9) => 0; | ||||
|     memcmp(buffer+13, "ccccc",     5) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello"); | ||||
|     lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello"); | ||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file); | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # deferred file attributes | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_mount(&lfs, &cfg) => 0; | ||||
|     lfs_setattr(&lfs, "hello/hello", 'B', "fffffffff",  9) => 0; | ||||
|     lfs_setattr(&lfs, "hello/hello", 'C', "ccccc",      5) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_setattr(&lfs2, "hello/hello", 'B', "fffffffff",  9) => 0; | ||||
|     lfs2_setattr(&lfs2, "hello/hello", 'C', "ccccc",      5) => 0; | ||||
|  | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     struct lfs_attr attrs1[] = { | ||||
|     struct lfs2_attr attrs1[] = { | ||||
|         {'B', "gggg", 4}, | ||||
|         {'C', "",     0}, | ||||
|         {'D', "hhhh", 4}, | ||||
|     }; | ||||
|     struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||
|     struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||
|  | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0; | ||||
|  | ||||
|     lfs_getattr(&lfs, "hello/hello", 'B', buffer,    9) => 9; | ||||
|     lfs_getattr(&lfs, "hello/hello", 'C', buffer+9,  9) => 5; | ||||
|     lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => LFS_ERR_NOATTR; | ||||
|     lfs2_getattr(&lfs2, "hello/hello", 'B', buffer,    9) => 9; | ||||
|     lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9,  9) => 5; | ||||
|     lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => LFS2_ERR_NOATTR; | ||||
|     memcmp(buffer,    "fffffffff",          9) => 0; | ||||
|     memcmp(buffer+9,  "ccccc\0\0\0\0",      9) => 0; | ||||
|     memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0; | ||||
|  | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs_getattr(&lfs, "hello/hello", 'B', buffer,    9) => 4; | ||||
|     lfs_getattr(&lfs, "hello/hello", 'C', buffer+9,  9) => 0; | ||||
|     lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello/hello", 'B', buffer,    9) => 4; | ||||
|     lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9,  9) => 0; | ||||
|     lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => 4; | ||||
|     memcmp(buffer,    "gggg\0\0\0\0\0",     9) => 0; | ||||
|     memcmp(buffer+9,  "\0\0\0\0\0\0\0\0\0", 9) => 0; | ||||
|     memcmp(buffer+18, "hhhh\0\0\0\0\0",     9) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
| @@ -1,241 +1,241 @@ | ||||
| # bad blocks with block cycles should be tested in test_relocations | ||||
| if = 'LFS_BLOCK_CYCLES == -1' | ||||
| if = 'LFS2_BLOCK_CYCLES == -1' | ||||
|  | ||||
| [[case]] # single bad blocks | ||||
| 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.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.NAMEMULT = 64 | ||||
| define.FILEMULT = 1 | ||||
| code = ''' | ||||
|     for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) { | ||||
|         lfs_testbd_setwear(&cfg, badblock-1, 0) => 0; | ||||
|         lfs_testbd_setwear(&cfg, badblock, 0xffffffff) => 0; | ||||
|     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; | ||||
|          | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (int i = 1; i < 10; i++) { | ||||
|             for (int j = 0; j < NAMEMULT; j++) { | ||||
|                 buffer[j] = '0'+i; | ||||
|             } | ||||
|             buffer[NAMEMULT] = '\0'; | ||||
|             lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||
|             lfs2_mkdir(&lfs2, (char*)buffer) => 0; | ||||
|  | ||||
|             buffer[NAMEMULT] = '/'; | ||||
|             for (int j = 0; j < NAMEMULT; j++) { | ||||
|                 buffer[j+NAMEMULT+1] = '0'+i; | ||||
|             } | ||||
|             buffer[2*NAMEMULT+1] = '\0'; | ||||
|             lfs_file_open(&lfs, &file, (char*)buffer, | ||||
|                     LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, (char*)buffer, | ||||
|                     LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|              | ||||
|             size = NAMEMULT; | ||||
|             for (int j = 0; j < i*FILEMULT; j++) { | ||||
|                 lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|                 lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|             } | ||||
|  | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (int i = 1; i < 10; i++) { | ||||
|             for (int j = 0; j < NAMEMULT; j++) { | ||||
|                 buffer[j] = '0'+i; | ||||
|             } | ||||
|             buffer[NAMEMULT] = '\0'; | ||||
|             lfs_stat(&lfs, (char*)buffer, &info) => 0; | ||||
|             info.type => LFS_TYPE_DIR; | ||||
|             lfs2_stat(&lfs2, (char*)buffer, &info) => 0; | ||||
|             info.type => LFS2_TYPE_DIR; | ||||
|  | ||||
|             buffer[NAMEMULT] = '/'; | ||||
|             for (int j = 0; j < NAMEMULT; j++) { | ||||
|                 buffer[j+NAMEMULT+1] = '0'+i; | ||||
|             } | ||||
|             buffer[2*NAMEMULT+1] = '\0'; | ||||
|             lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0; | ||||
|              | ||||
|             size = NAMEMULT; | ||||
|             for (int j = 0; j < i*FILEMULT; j++) { | ||||
|                 uint8_t rbuffer[1024]; | ||||
|                 lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|                 lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|                 memcmp(buffer, rbuffer, size) => 0; | ||||
|             } | ||||
|  | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|     } | ||||
| ''' | ||||
|  | ||||
| [[case]] # region corruption (causes cascading failures) | ||||
| 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.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.NAMEMULT = 64 | ||||
| define.FILEMULT = 1 | ||||
| code = ''' | ||||
|     for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) { | ||||
|         lfs_testbd_setwear(&cfg, i+2, 0xffffffff) => 0; | ||||
|     for (lfs2_block_t i = 0; i < (LFS2_BLOCK_COUNT-2)/2; i++) { | ||||
|         lfs2_testbd_setwear(&cfg, i+2, 0xffffffff) => 0; | ||||
|     } | ||||
|      | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int i = 1; i < 10; i++) { | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j] = '0'+i; | ||||
|         } | ||||
|         buffer[NAMEMULT] = '\0'; | ||||
|         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||
|         lfs2_mkdir(&lfs2, (char*)buffer) => 0; | ||||
|  | ||||
|         buffer[NAMEMULT] = '/'; | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j+NAMEMULT+1] = '0'+i; | ||||
|         } | ||||
|         buffer[2*NAMEMULT+1] = '\0'; | ||||
|         lfs_file_open(&lfs, &file, (char*)buffer, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, (char*)buffer, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|          | ||||
|         size = NAMEMULT; | ||||
|         for (int j = 0; j < i*FILEMULT; j++) { | ||||
|             lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int i = 1; i < 10; i++) { | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j] = '0'+i; | ||||
|         } | ||||
|         buffer[NAMEMULT] = '\0'; | ||||
|         lfs_stat(&lfs, (char*)buffer, &info) => 0; | ||||
|         info.type => LFS_TYPE_DIR; | ||||
|         lfs2_stat(&lfs2, (char*)buffer, &info) => 0; | ||||
|         info.type => LFS2_TYPE_DIR; | ||||
|  | ||||
|         buffer[NAMEMULT] = '/'; | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j+NAMEMULT+1] = '0'+i; | ||||
|         } | ||||
|         buffer[2*NAMEMULT+1] = '\0'; | ||||
|         lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0; | ||||
|          | ||||
|         size = NAMEMULT; | ||||
|         for (int j = 0; j < i*FILEMULT; j++) { | ||||
|             uint8_t rbuffer[1024]; | ||||
|             lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|             lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|             memcmp(buffer, rbuffer, size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # alternating corruption (causes cascading failures) | ||||
| 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.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.NAMEMULT = 64 | ||||
| define.FILEMULT = 1 | ||||
| code = ''' | ||||
|     for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) { | ||||
|         lfs_testbd_setwear(&cfg, (2*i) + 2, 0xffffffff) => 0; | ||||
|     for (lfs2_block_t i = 0; i < (LFS2_BLOCK_COUNT-2)/2; i++) { | ||||
|         lfs2_testbd_setwear(&cfg, (2*i) + 2, 0xffffffff) => 0; | ||||
|     } | ||||
|      | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int i = 1; i < 10; i++) { | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j] = '0'+i; | ||||
|         } | ||||
|         buffer[NAMEMULT] = '\0'; | ||||
|         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||
|         lfs2_mkdir(&lfs2, (char*)buffer) => 0; | ||||
|  | ||||
|         buffer[NAMEMULT] = '/'; | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j+NAMEMULT+1] = '0'+i; | ||||
|         } | ||||
|         buffer[2*NAMEMULT+1] = '\0'; | ||||
|         lfs_file_open(&lfs, &file, (char*)buffer, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, (char*)buffer, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|          | ||||
|         size = NAMEMULT; | ||||
|         for (int j = 0; j < i*FILEMULT; j++) { | ||||
|             lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int i = 1; i < 10; i++) { | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j] = '0'+i; | ||||
|         } | ||||
|         buffer[NAMEMULT] = '\0'; | ||||
|         lfs_stat(&lfs, (char*)buffer, &info) => 0; | ||||
|         info.type => LFS_TYPE_DIR; | ||||
|         lfs2_stat(&lfs2, (char*)buffer, &info) => 0; | ||||
|         info.type => LFS2_TYPE_DIR; | ||||
|  | ||||
|         buffer[NAMEMULT] = '/'; | ||||
|         for (int j = 0; j < NAMEMULT; j++) { | ||||
|             buffer[j+NAMEMULT+1] = '0'+i; | ||||
|         } | ||||
|         buffer[2*NAMEMULT+1] = '\0'; | ||||
|         lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0; | ||||
|          | ||||
|         size = NAMEMULT; | ||||
|         for (int j = 0; j < i*FILEMULT; j++) { | ||||
|             uint8_t rbuffer[1024]; | ||||
|             lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|             lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|             memcmp(buffer, rbuffer, size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| # other corner cases | ||||
| [[case]] # bad superblocks (corrupt 1 or 0) | ||||
| 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.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', | ||||
| ] | ||||
| code = ''' | ||||
|     lfs_testbd_setwear(&cfg, 0, 0xffffffff) => 0; | ||||
|     lfs_testbd_setwear(&cfg, 1, 0xffffffff) => 0; | ||||
|     lfs2_testbd_setwear(&cfg, 0, 0xffffffff) => 0; | ||||
|     lfs2_testbd_setwear(&cfg, 1, 0xffffffff) => 0; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC; | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
|     lfs2_format(&lfs2, &cfg) => LFS2_ERR_NOSPC; | ||||
|     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||
| ''' | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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.LFS_CACHE_SIZE = 512 | ||||
| if = 'LFS_CACHE_SIZE % LFS_PROG_SIZE == 0 && LFS_CACHE_SIZE == 512' | ||||
| define.LFS2_CACHE_SIZE = 512 | ||||
| if = 'LFS2_CACHE_SIZE % LFS2_PROG_SIZE == 0 && LFS2_CACHE_SIZE == 512' | ||||
|  | ||||
| [[case]] # entry grow test | ||||
| code = ''' | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write hi0 20 | ||||
|     sprintf(path, "hi0"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi2 20 | ||||
|     sprintf(path, "hi2"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi3 20 | ||||
|     sprintf(path, "hi3"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi0 20 | ||||
|     sprintf(path, "hi0"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi2 20 | ||||
|     sprintf(path, "hi2"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi3 20 | ||||
|     sprintf(path, "hi3"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # entry shrink test | ||||
| @@ -99,88 +99,88 @@ code = ''' | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write hi0 20 | ||||
|     sprintf(path, "hi0"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi2 20 | ||||
|     sprintf(path, "hi2"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi3 20 | ||||
|     sprintf(path, "hi3"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi0 20 | ||||
|     sprintf(path, "hi0"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi2 20 | ||||
|     sprintf(path, "hi2"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi3 20 | ||||
|     sprintf(path, "hi3"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # entry spill test | ||||
| @@ -188,72 +188,72 @@ code = ''' | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # entry push spill test | ||||
| @@ -261,88 +261,88 @@ code = ''' | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # entry push spill two test | ||||
| @@ -350,103 +350,103 @@ code = ''' | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi4 200 | ||||
|     sprintf(path, "hi4"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi1 20 | ||||
|     sprintf(path, "hi1"); size = 20; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi4 200 | ||||
|     sprintf(path, "hi4"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # entry drop test | ||||
| @@ -454,158 +454,158 @@ code = ''' | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi1 200 | ||||
|     sprintf(path, "hi1"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // write hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "hi1") => 0; | ||||
|     lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT; | ||||
|     lfs2_remove(&lfs2, "hi1") => 0; | ||||
|     lfs2_stat(&lfs2, "hi1", &info) => LFS2_ERR_NOENT; | ||||
|     // read hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi2 200 | ||||
|     sprintf(path, "hi2"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "hi2") => 0; | ||||
|     lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT; | ||||
|     lfs2_remove(&lfs2, "hi2") => 0; | ||||
|     lfs2_stat(&lfs2, "hi2", &info) => LFS2_ERR_NOENT; | ||||
|     // read hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // read hi3 200 | ||||
|     sprintf(path, "hi3"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "hi3") => 0; | ||||
|     lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT; | ||||
|     lfs2_remove(&lfs2, "hi3") => 0; | ||||
|     lfs2_stat(&lfs2, "hi3", &info) => LFS2_ERR_NOENT; | ||||
|     // read hi0 200 | ||||
|     sprintf(path, "hi0"); size = 200; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "hi0") => 0; | ||||
|     lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT; | ||||
|     lfs2_remove(&lfs2, "hi0") => 0; | ||||
|     lfs2_stat(&lfs2, "hi0", &info) => LFS2_ERR_NOENT; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # create too big | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(path, 'm', 200); | ||||
|     path[200] = '\0'; | ||||
|  | ||||
|     size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     uint8_t wbuffer[1024]; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # resize too big | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     memset(path, 'm', 200); | ||||
|     path[200] = '\0'; | ||||
|  | ||||
|     size = 40; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     uint8_t wbuffer[1024]; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     size = 40; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, path, | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
| @@ -4,285 +4,285 @@ | ||||
| # invalid pointer tests (outside of block_count) | ||||
|  | ||||
| [[case]] # invalid tail-pointer test | ||||
| define.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL'] | ||||
| define.TAIL_TYPE = ['LFS2_TYPE_HARDTAIL', 'LFS2_TYPE_SOFTTAIL'] | ||||
| define.INVALSET = [0x3, 0x1, 0x2] | ||||
| in = "lfs.c" | ||||
| in = "lfs2.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // change tail-pointer to invalid pointers | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), | ||||
|                 (lfs_block_t[2]){ | ||||
|     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]){ | ||||
|                     (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||
|                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     lfs2_deinit(&lfs2) => 0; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
|     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid dir pointer test | ||||
| define.INVALSET = [0x3, 0x1, 0x2] | ||||
| in = "lfs.c" | ||||
| in = "lfs2.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     // make a dir | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "dir_here") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "dir_here") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // change the dir pointer to be invalid | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs2_init(&lfs2, &cfg) => 0; | ||||
|     lfs2_mdir_t mdir; | ||||
|     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||
|     // make sure id 1 == our directory | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer) | ||||
|                 => LFS_MKTAG(LFS_TYPE_DIR, 1, strlen("dir_here")); | ||||
|     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")); | ||||
|     assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0); | ||||
|     // change dir pointer | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, 8), | ||||
|                 (lfs_block_t[2]){ | ||||
|     lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( | ||||
|             {LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, 8), | ||||
|                 (lfs2_block_t[2]){ | ||||
|                     (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||
|                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     lfs2_deinit(&lfs2) => 0; | ||||
|  | ||||
|     // test that accessing our bad dir fails, note there's a number | ||||
|     // of ways to access the dir, some can fail, but some don't | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "dir_here", &info) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_stat(&lfs2, "dir_here", &info) => 0; | ||||
|     assert(strcmp(info.name, "dir_here") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT; | ||||
|     lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT; | ||||
|     lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT; | ||||
|     lfs_file_open(&lfs, &file, "dir_here/file_here", | ||||
|             LFS_O_RDONLY) => LFS_ERR_CORRUPT; | ||||
|     lfs_file_open(&lfs, &file, "dir_here/file_here", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_CORRUPT; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     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; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid file pointer test | ||||
| in = "lfs.c" | ||||
| in = "lfs2.c" | ||||
| define.SIZE = [10, 1000, 100000] # faked file size | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     // make a file | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "file_here", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     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; | ||||
|  | ||||
|     // change the file pointer to be invalid | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs2_init(&lfs2, &cfg) => 0; | ||||
|     lfs2_mdir_t mdir; | ||||
|     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||
|     // make sure id 1 == our file | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) | ||||
|                 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); | ||||
|     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")); | ||||
|     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); | ||||
|     // change file pointer | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)), | ||||
|                 &(struct lfs_ctz){0xcccccccc, lfs_tole32(SIZE)}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     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; | ||||
|  | ||||
|     // test that accessing our bad file fails, note there's a number | ||||
|     // of ways to access the dir, some can fail, but some don't | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "file_here", &info) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_stat(&lfs2, "file_here", &info) => 0; | ||||
|     assert(strcmp(info.name, "file_here") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     assert(info.size == SIZE); | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     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; | ||||
|  | ||||
|     // any allocs that traverse CTZ must unfortunately must fail | ||||
|     if (SIZE > 2*LFS_BLOCK_SIZE) { | ||||
|         lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; | ||||
|     if (SIZE > 2*LFS2_BLOCK_SIZE) { | ||||
|         lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid pointer in CTZ skip-list test | ||||
| define.SIZE = ['2*LFS_BLOCK_SIZE', '3*LFS_BLOCK_SIZE', '4*LFS_BLOCK_SIZE'] | ||||
| in = "lfs.c" | ||||
| define.SIZE = ['2*LFS2_BLOCK_SIZE', '3*LFS2_BLOCK_SIZE', '4*LFS2_BLOCK_SIZE'] | ||||
| in = "lfs2.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     // make a file | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "file_here", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "file_here", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     for (int i = 0; i < SIZE; i++) { | ||||
|         char c = 'c'; | ||||
|         lfs_file_write(&lfs, &file, &c, 1) => 1; | ||||
|         lfs2_file_write(&lfs2, &file, &c, 1) => 1; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|     // change pointer in CTZ skip-list to be invalid | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs2_init(&lfs2, &cfg) => 0; | ||||
|     lfs2_mdir_t mdir; | ||||
|     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||
|     // make sure id 1 == our file and get our CTZ structure | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) | ||||
|                 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); | ||||
|     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")); | ||||
|     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); | ||||
|     struct lfs_ctz ctz; | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_STRUCT, 1, sizeof(struct lfs_ctz)), &ctz) | ||||
|                 => LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)); | ||||
|     lfs_ctz_fromle32(&ctz); | ||||
|     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); | ||||
|     // rewrite block to contain bad pointer | ||||
|     uint8_t bbuffer[LFS_BLOCK_SIZE]; | ||||
|     cfg.read(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; | ||||
|     uint32_t bad = lfs_tole32(0xcccccccc); | ||||
|     uint8_t bbuffer[LFS2_BLOCK_SIZE]; | ||||
|     cfg.read(&cfg, ctz.head, 0, bbuffer, LFS2_BLOCK_SIZE) => 0; | ||||
|     uint32_t bad = lfs2_tole32(0xcccccccc); | ||||
|     memcpy(&bbuffer[0], &bad, sizeof(bad)); | ||||
|     memcpy(&bbuffer[4], &bad, sizeof(bad)); | ||||
|     cfg.erase(&cfg, ctz.head) => 0; | ||||
|     cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS2_BLOCK_SIZE) => 0; | ||||
|     lfs2_deinit(&lfs2) => 0; | ||||
|  | ||||
|     // test that accessing our bad file fails, note there's a number | ||||
|     // of ways to access the dir, some can fail, but some don't | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "file_here", &info) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_stat(&lfs2, "file_here", &info) => 0; | ||||
|     assert(strcmp(info.name, "file_here") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     assert(info.size == SIZE); | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     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; | ||||
|  | ||||
|     // any allocs that traverse CTZ must unfortunately must fail | ||||
|     if (SIZE > 2*LFS_BLOCK_SIZE) { | ||||
|         lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; | ||||
|     if (SIZE > 2*LFS2_BLOCK_SIZE) { | ||||
|         lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
|  | ||||
| [[case]] # invalid gstate pointer | ||||
| define.INVALSET = [0x3, 0x1, 0x2] | ||||
| in = "lfs.c" | ||||
| in = "lfs2.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // create an invalid gstate | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){ | ||||
|     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]){ | ||||
|             (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||
|             (INVALSET & 0x2) ? 0xcccccccc : 0}); | ||||
|     lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     lfs2_dir_commit(&lfs2, &mdir, NULL, 0) => 0; | ||||
|     lfs2_deinit(&lfs2) => 0; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     // mount may not fail, but our first alloc should fail when | ||||
|     // we try to fix the gstate | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "should_fail") => LFS2_ERR_CORRUPT; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| # cycle detection/recovery tests | ||||
|  | ||||
| [[case]] # metadata-pair threaded-list loop test | ||||
| in = "lfs.c" | ||||
| in = "lfs2.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // change tail-pointer to point to ourself | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), | ||||
|                 (lfs_block_t[2]){0, 1}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     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; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
|     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # metadata-pair threaded-list 2-length loop test | ||||
| in = "lfs.c" | ||||
| in = "lfs2.c" | ||||
| code = ''' | ||||
|     // create littlefs with child dir | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "child") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // find child | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x7ff, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) | ||||
|                 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); | ||||
|     lfs_pair_fromle32(pair); | ||||
|     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); | ||||
|     // change tail-pointer to point to root | ||||
|     lfs_dir_fetch(&lfs, &mdir, pair) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), | ||||
|                 (lfs_block_t[2]){0, 1}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     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; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
|     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # metadata-pair threaded-list 1-length child loop test | ||||
| in = "lfs.c" | ||||
| in = "lfs2.c" | ||||
| code = ''' | ||||
|     // create littlefs with child dir | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "child") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // find child | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x7ff, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) | ||||
|                 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); | ||||
|     lfs_pair_fromle32(pair); | ||||
|     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); | ||||
|     // change tail-pointer to point to ourself | ||||
|     lfs_dir_fetch(&lfs, &mdir, pair) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|     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; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
|     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||
| ''' | ||||
|   | ||||
| @@ -1,49 +1,49 @@ | ||||
| [[case]] # test running a filesystem to exhaustion | ||||
| 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.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.FILES = 10 | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "roadrunner") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "roadrunner") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     uint32_t cycle = 0; | ||||
|     while (true) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &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); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, | ||||
|                     LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, | ||||
|                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|  | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|             for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 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; | ||||
|                 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; | ||||
|                     goto exhausted; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             err = lfs_file_close(&lfs, &file); | ||||
|             assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
|             if (err == LFS_ERR_NOSPC) { | ||||
|                 lfs_unmount(&lfs) => 0; | ||||
|             err = lfs2_file_close(&lfs2, &file); | ||||
|             assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||
|             if (err == LFS2_ERR_NOSPC) { | ||||
|                 lfs2_unmount(&lfs2) => 0; | ||||
|                 goto exhausted; | ||||
|             } | ||||
|         } | ||||
| @@ -54,78 +54,78 @@ code = ''' | ||||
|             srand(cycle * i); | ||||
|             size = 1 << ((rand() % 10)+2); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|             for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 char r; | ||||
|                 lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||
|                 lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||
|                 assert(r == c); | ||||
|             } | ||||
|  | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         cycle += 1; | ||||
|     } | ||||
|  | ||||
| exhausted: | ||||
|     // should still be readable | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (uint32_t i = 0; i < FILES; i++) { | ||||
|         // check for errors | ||||
|         sprintf(path, "roadrunner/test%d", i); | ||||
|         lfs_stat(&lfs, path, &info) => 0; | ||||
|         lfs2_stat(&lfs2, path, &info) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     LFS_WARN("completed %d cycles", cycle); | ||||
|     LFS2_WARN("completed %d cycles", cycle); | ||||
| ''' | ||||
|  | ||||
| [[case]] # test running a filesystem to exhaustion | ||||
|          # which also requires expanding superblocks | ||||
| 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.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.FILES = 10 | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     uint32_t cycle = 0; | ||||
|     while (true) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &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); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, | ||||
|                     LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, | ||||
|                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|  | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|             for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 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; | ||||
|                 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; | ||||
|                     goto exhausted; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             err = lfs_file_close(&lfs, &file); | ||||
|             assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
|             if (err == LFS_ERR_NOSPC) { | ||||
|                 lfs_unmount(&lfs) => 0; | ||||
|             err = lfs2_file_close(&lfs2, &file); | ||||
|             assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||
|             if (err == LFS2_ERR_NOSPC) { | ||||
|                 lfs2_unmount(&lfs2) => 0; | ||||
|                 goto exhausted; | ||||
|             } | ||||
|         } | ||||
| @@ -136,32 +136,32 @@ code = ''' | ||||
|             srand(cycle * i); | ||||
|             size = 1 << ((rand() % 10)+2); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|             for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 char r; | ||||
|                 lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||
|                 lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||
|                 assert(r == c); | ||||
|             } | ||||
|  | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         cycle += 1; | ||||
|     } | ||||
|  | ||||
| exhausted: | ||||
|     // should still be readable | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (uint32_t i = 0; i < FILES; i++) { | ||||
|         // check for errors | ||||
|         sprintf(path, "test%d", i); | ||||
|         lfs_stat(&lfs, path, &info) => 0; | ||||
|         lfs2_stat(&lfs2, path, &info) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     LFS_WARN("completed %d cycles", cycle); | ||||
|     LFS2_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.LFS_ERASE_CYCLES = 20 | ||||
| define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster | ||||
| define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' | ||||
| 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.FILES = 10 | ||||
| code = ''' | ||||
|     uint32_t run_cycles[2]; | ||||
|     const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; | ||||
|     const uint32_t run_block_count[2] = {LFS2_BLOCK_COUNT/2, LFS2_BLOCK_COUNT}; | ||||
|  | ||||
|     for (int run = 0; run < 2; run++) { | ||||
|         for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { | ||||
|             lfs_testbd_setwear(&cfg, b, | ||||
|                     (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; | ||||
|         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; | ||||
|         } | ||||
|  | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs_mkdir(&lfs, "roadrunner") => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         lfs2_mkdir(&lfs2, "roadrunner") => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         uint32_t cycle = 0; | ||||
|         while (true) { | ||||
|             lfs_mount(&lfs, &cfg) => 0; | ||||
|             lfs2_mount(&lfs2, &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); | ||||
|  | ||||
|                 lfs_file_open(&lfs, &file, path, | ||||
|                         LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|                 lfs2_file_open(&lfs2, &file, path, | ||||
|                         LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|  | ||||
|                 for (lfs_size_t j = 0; j < size; j++) { | ||||
|                 for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                     char c = 'a' + (rand() % 26); | ||||
|                     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; | ||||
|                     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; | ||||
|                         goto exhausted; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 err = lfs_file_close(&lfs, &file); | ||||
|                 assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
|                 if (err == LFS_ERR_NOSPC) { | ||||
|                     lfs_unmount(&lfs) => 0; | ||||
|                 err = lfs2_file_close(&lfs2, &file); | ||||
|                 assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||
|                 if (err == LFS2_ERR_NOSPC) { | ||||
|                     lfs2_unmount(&lfs2) => 0; | ||||
|                     goto exhausted; | ||||
|                 } | ||||
|             } | ||||
| @@ -227,85 +227,85 @@ code = ''' | ||||
|                 srand(cycle * i); | ||||
|                 size = 1 << ((rand() % 10)+2); | ||||
|  | ||||
|                 lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|                 for (lfs_size_t j = 0; j < size; j++) { | ||||
|                 lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|                 for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                     char c = 'a' + (rand() % 26); | ||||
|                     char r; | ||||
|                     lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||
|                     lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||
|                     assert(r == c); | ||||
|                 } | ||||
|  | ||||
|                 lfs_file_close(&lfs, &file) => 0; | ||||
|                 lfs2_file_close(&lfs2, &file) => 0; | ||||
|             } | ||||
|             lfs_unmount(&lfs) => 0; | ||||
|             lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|             cycle += 1; | ||||
|         } | ||||
|  | ||||
| exhausted: | ||||
|         // should still be readable | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (uint32_t i = 0; i < FILES; i++) { | ||||
|             // check for errors | ||||
|             sprintf(path, "roadrunner/test%d", i); | ||||
|             lfs_stat(&lfs, path, &info) => 0; | ||||
|             lfs2_stat(&lfs2, path, &info) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         run_cycles[run] = cycle; | ||||
|         LFS_WARN("completed %d blocks %d cycles", | ||||
|         LFS2_WARN("completed %d blocks %d cycles", | ||||
|                 run_block_count[run], run_cycles[run]); | ||||
|     } | ||||
|  | ||||
|     // check we increased the lifetime by 2x with ~10% error | ||||
|     LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||
|     LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||
| ''' | ||||
|  | ||||
| [[case]] # wear-level test + expanding superblock | ||||
| 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.LFS2_ERASE_CYCLES = 20 | ||||
| define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||
| define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2' | ||||
| define.FILES = 10 | ||||
| code = ''' | ||||
|     uint32_t run_cycles[2]; | ||||
|     const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; | ||||
|     const uint32_t run_block_count[2] = {LFS2_BLOCK_COUNT/2, LFS2_BLOCK_COUNT}; | ||||
|  | ||||
|     for (int run = 0; run < 2; run++) { | ||||
|         for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { | ||||
|             lfs_testbd_setwear(&cfg, b, | ||||
|                     (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; | ||||
|         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; | ||||
|         } | ||||
|  | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|         uint32_t cycle = 0; | ||||
|         while (true) { | ||||
|             lfs_mount(&lfs, &cfg) => 0; | ||||
|             lfs2_mount(&lfs2, &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); | ||||
|  | ||||
|                 lfs_file_open(&lfs, &file, path, | ||||
|                         LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|                 lfs2_file_open(&lfs2, &file, path, | ||||
|                         LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|  | ||||
|                 for (lfs_size_t j = 0; j < size; j++) { | ||||
|                 for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                     char c = 'a' + (rand() % 26); | ||||
|                     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; | ||||
|                     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; | ||||
|                         goto exhausted; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 err = lfs_file_close(&lfs, &file); | ||||
|                 assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
|                 if (err == LFS_ERR_NOSPC) { | ||||
|                     lfs_unmount(&lfs) => 0; | ||||
|                 err = lfs2_file_close(&lfs2, &file); | ||||
|                 assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||
|                 if (err == LFS2_ERR_NOSPC) { | ||||
|                     lfs2_unmount(&lfs2) => 0; | ||||
|                     goto exhausted; | ||||
|                 } | ||||
|             } | ||||
| @@ -316,81 +316,81 @@ code = ''' | ||||
|                 srand(cycle * i); | ||||
|                 size = 1 << ((rand() % 10)+2); | ||||
|  | ||||
|                 lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|                 for (lfs_size_t j = 0; j < size; j++) { | ||||
|                 lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|                 for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                     char c = 'a' + (rand() % 26); | ||||
|                     char r; | ||||
|                     lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||
|                     lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||
|                     assert(r == c); | ||||
|                 } | ||||
|  | ||||
|                 lfs_file_close(&lfs, &file) => 0; | ||||
|                 lfs2_file_close(&lfs2, &file) => 0; | ||||
|             } | ||||
|             lfs_unmount(&lfs) => 0; | ||||
|             lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|             cycle += 1; | ||||
|         } | ||||
|  | ||||
| exhausted: | ||||
|         // should still be readable | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         for (uint32_t i = 0; i < FILES; i++) { | ||||
|             // check for errors | ||||
|             sprintf(path, "test%d", i); | ||||
|             lfs_stat(&lfs, path, &info) => 0; | ||||
|             lfs2_stat(&lfs2, path, &info) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         run_cycles[run] = cycle; | ||||
|         LFS_WARN("completed %d blocks %d cycles", | ||||
|         LFS2_WARN("completed %d blocks %d cycles", | ||||
|                 run_block_count[run], run_cycles[run]); | ||||
|     } | ||||
|  | ||||
|     // check we increased the lifetime by 2x with ~10% error | ||||
|     LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||
|     LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||
| ''' | ||||
|  | ||||
| [[case]] # test that we wear blocks roughly evenly | ||||
| 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.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.CYCLES = 100 | ||||
| define.FILES = 10 | ||||
| if = 'LFS_BLOCK_CYCLES < CYCLES/10' | ||||
| if = 'LFS2_BLOCK_CYCLES < CYCLES/10' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "roadrunner") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, "roadrunner") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     uint32_t cycle = 0; | ||||
|     while (cycle < CYCLES) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &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); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, | ||||
|                     LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, | ||||
|                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|  | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|             for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 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; | ||||
|                 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; | ||||
|                     goto exhausted; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             err = lfs_file_close(&lfs, &file); | ||||
|             assert(err == 0 || err == LFS_ERR_NOSPC); | ||||
|             if (err == LFS_ERR_NOSPC) { | ||||
|                 lfs_unmount(&lfs) => 0; | ||||
|             err = lfs2_file_close(&lfs2, &file); | ||||
|             assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||
|             if (err == LFS2_ERR_NOSPC) { | ||||
|                 lfs2_unmount(&lfs2) => 0; | ||||
|                 goto exhausted; | ||||
|             } | ||||
|         } | ||||
| @@ -401,40 +401,40 @@ code = ''' | ||||
|             srand(cycle * i); | ||||
|             size = 1 << 4; //((rand() % 10)+2); | ||||
|  | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|             for (lfs_size_t j = 0; j < size; j++) { | ||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|             for (lfs2_size_t j = 0; j < size; j++) { | ||||
|                 char c = 'a' + (rand() % 26); | ||||
|                 char r; | ||||
|                 lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||
|                 lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||
|                 assert(r == c); | ||||
|             } | ||||
|  | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         cycle += 1; | ||||
|     } | ||||
|  | ||||
| exhausted: | ||||
|     // should still be readable | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (uint32_t i = 0; i < FILES; i++) { | ||||
|         // check for errors | ||||
|         sprintf(path, "roadrunner/test%d", i); | ||||
|         lfs_stat(&lfs, path, &info) => 0; | ||||
|         lfs2_stat(&lfs2, path, &info) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     LFS_WARN("completed %d cycles", cycle); | ||||
|     LFS2_WARN("completed %d cycles", cycle); | ||||
|  | ||||
|     // check the wear on our block device | ||||
|     lfs_testbd_wear_t minwear = -1; | ||||
|     lfs_testbd_wear_t totalwear = 0; | ||||
|     lfs_testbd_wear_t maxwear = 0; | ||||
|     lfs2_testbd_wear_t minwear = -1; | ||||
|     lfs2_testbd_wear_t totalwear = 0; | ||||
|     lfs2_testbd_wear_t maxwear = 0; | ||||
|     // skip 0 and 1 as superblock movement is intentionally avoided | ||||
|     for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { | ||||
|         lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); | ||||
|     for (lfs2_block_t b = 2; b < LFS2_BLOCK_COUNT; b++) { | ||||
|         lfs2_testbd_wear_t wear = lfs2_testbd_getwear(&cfg, b); | ||||
|         printf("%08x: wear %d\n", b, wear); | ||||
|         assert(wear >= 0); | ||||
|         if (wear < minwear) { | ||||
| @@ -445,21 +445,21 @@ exhausted: | ||||
|         } | ||||
|         totalwear += wear; | ||||
|     } | ||||
|     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); | ||||
|     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); | ||||
|  | ||||
|     // find standard deviation^2 | ||||
|     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(&cfg, b); | ||||
|     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); | ||||
|         assert(wear >= 0); | ||||
|         lfs_testbd_swear_t diff = wear - avgwear; | ||||
|         lfs2_testbd_swear_t diff = wear - avgwear; | ||||
|         dev2 += diff*diff; | ||||
|     } | ||||
|     dev2 /= totalwear; | ||||
|     LFS_WARN("std dev^2: %d", dev2); | ||||
|     LFS2_WARN("std dev^2: %d", dev2); | ||||
|     assert(dev2 < 8); | ||||
| ''' | ||||
|  | ||||
|   | ||||
| @@ -1,60 +1,60 @@ | ||||
|  | ||||
| [[case]] # simple file test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|     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; | ||||
|     size = strlen("Hello World!")+1; | ||||
|     strcpy((char*)buffer, "Hello World!"); | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "hello", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     assert(strcmp((char*)buffer, "Hello World!") == 0); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # larger files | ||||
| define.SIZE = [32, 8192, 262144, 0, 7, 8193] | ||||
| define.CHUNKSIZE = [31, 16, 33, 1, 1023] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 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 = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE1; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE1; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // rewrite | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY) => 0; | ||||
|     srand(2); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => lfs_max(SIZE1, SIZE2); | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => lfs2_max(SIZE1, SIZE2); | ||||
|     srand(2); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     if (SIZE1 > SIZE2) { | ||||
|         srand(1); | ||||
|         for (lfs_size_t b = 0; b < SIZE2; b++) { | ||||
|         for (lfs2_size_t b = 0; b < SIZE2; b++) { | ||||
|             rand(); | ||||
|         } | ||||
|         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++) { | ||||
|         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++) { | ||||
|                 assert(buffer[b] == (rand() & 0xff)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 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 = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE1; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE1; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // append | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_APPEND) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_APPEND) => 0; | ||||
|     srand(2); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE1 + SIZE2; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE1 + SIZE2; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     srand(2); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 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 = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     // write | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE1; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE1; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // truncate | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; | ||||
|     srand(2); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE2; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE2; | ||||
|     srand(2); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant file writing | ||||
| @@ -290,197 +290,197 @@ define.SIZE = [32, 0, 7, 2049] | ||||
| define.CHUNKSIZE = [31, 16, 65] | ||||
| reentrant = true | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|  | ||||
|     err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY); | ||||
|     assert(err == LFS_ERR_NOENT || err == 0); | ||||
|     err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY); | ||||
|     assert(err == LFS2_ERR_NOENT || err == 0); | ||||
|     if (err == 0) { | ||||
|         // can only be 0 (new file) or full size | ||||
|         size = lfs_file_size(&lfs, &file); | ||||
|         size = lfs2_file_size(&lfs2, &file); | ||||
|         assert(size == 0 || size == SIZE); | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     // write | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant file writing with syncs | ||||
| define = [ | ||||
|     # append (O(n)) | ||||
|     {MODE='LFS_O_APPEND',   SIZE=[32, 0, 7, 2049],  CHUNKSIZE=[31, 16, 65]}, | ||||
|     {MODE='LFS2_O_APPEND',   SIZE=[32, 0, 7, 2049],  CHUNKSIZE=[31, 16, 65]}, | ||||
|     # truncate (O(n^2)) | ||||
|     {MODE='LFS_O_TRUNC',    SIZE=[32, 0, 7, 200],   CHUNKSIZE=[31, 16, 65]}, | ||||
|     {MODE='LFS2_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 = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|  | ||||
|     err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY); | ||||
|     assert(err == LFS_ERR_NOENT || err == 0); | ||||
|     err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY); | ||||
|     assert(err == LFS2_ERR_NOENT || err == 0); | ||||
|     if (err == 0) { | ||||
|         // with syncs we could be any size, but it at least must be valid data | ||||
|         size = lfs_file_size(&lfs, &file); | ||||
|         size = lfs2_file_size(&lfs2, &file); | ||||
|         assert(size <= SIZE); | ||||
|         srand(1); | ||||
|         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++) { | ||||
|         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++) { | ||||
|                 assert(buffer[b] == (rand() & 0xff)); | ||||
|             } | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     // write | ||||
|     lfs_file_open(&lfs, &file, "avacado", | ||||
|         LFS_O_WRONLY | LFS_O_CREAT | MODE) => 0; | ||||
|     size = lfs_file_size(&lfs, &file); | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", | ||||
|         LFS2_O_WRONLY | LFS2_O_CREAT | MODE) => 0; | ||||
|     size = lfs2_file_size(&lfs2, &file); | ||||
|     assert(size <= SIZE); | ||||
|     srand(1); | ||||
|     lfs_size_t skip = (MODE == LFS_O_APPEND) ? size : 0; | ||||
|     for (lfs_size_t b = 0; b < skip; b++) { | ||||
|     lfs2_size_t skip = (MODE == LFS2_O_APPEND) ? size : 0; | ||||
|     for (lfs2_size_t b = 0; b < skip; b++) { | ||||
|         rand(); | ||||
|     } | ||||
|     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++) { | ||||
|     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++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|         lfs_file_sync(&lfs, &file) => 0; | ||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||
|         lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     // read | ||||
|     lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => SIZE; | ||||
|     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE; | ||||
|     srand(1); | ||||
|     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++) { | ||||
|     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++) { | ||||
|             assert(buffer[b] == (rand() & 0xff)); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # many files | ||||
| define.N = 300 | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     // create N files of 7 bytes | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         sprintf(path, "file_%03d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|         char wbuffer[1024]; | ||||
|         size = 7; | ||||
|         snprintf(wbuffer, size, "Hi %03d", i); | ||||
|         lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|         char rbuffer[1024]; | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|         assert(strcmp(rbuffer, wbuffer) == 0); | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # many files with power cycle | ||||
| define.N = 300 | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     // create N files of 7 bytes | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         sprintf(path, "file_%03d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|         char wbuffer[1024]; | ||||
|         size = 7; | ||||
|         snprintf(wbuffer, size, "Hi %03d", i); | ||||
|         lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|         char rbuffer[1024]; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|         assert(strcmp(rbuffer, wbuffer) == 0); | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # many files with power loss | ||||
| define.N = 300 | ||||
| reentrant = true | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|     // create N files of 7 bytes | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         sprintf(path, "file_%03d", i); | ||||
|         err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT); | ||||
|         err = lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT); | ||||
|         char wbuffer[1024]; | ||||
|         size = 7; | ||||
|         snprintf(wbuffer, size, "Hi %03d", i); | ||||
|         if ((lfs_size_t)lfs_file_size(&lfs, &file) != size) { | ||||
|             lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|         if ((lfs2_size_t)lfs2_file_size(&lfs2, &file) != size) { | ||||
|             lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|         char rbuffer[1024]; | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||
|         assert(strcmp(rbuffer, wbuffer) == 0); | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
| @@ -3,60 +3,60 @@ | ||||
| define.SIZE = [10, 100] | ||||
| define.FILES = [4, 10, 26]  | ||||
| code = ''' | ||||
|     lfs_file_t files[FILES]; | ||||
|     lfs2_file_t files[FILES]; | ||||
|     const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_file_open(&lfs, &files[j], path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs2_file_open(&lfs2, &files[j], path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < SIZE; i++) { | ||||
|         for (int j = 0; j < FILES; j++) { | ||||
|             lfs_file_write(&lfs, &files[j], &alphas[j], 1) => 1; | ||||
|             lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         lfs_file_close(&lfs, &files[j]); | ||||
|         lfs2_file_close(&lfs2, &files[j]); | ||||
|     } | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, ".") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         assert(strcmp(info.name, path) == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|         assert(info.type == LFS2_TYPE_REG); | ||||
|         assert(info.size == SIZE); | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|     lfs2_dir_close(&lfs2, &dir) => 0; | ||||
|  | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0; | ||||
|         lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         for (int j = 0; j < FILES; j++) { | ||||
|             lfs_file_read(&lfs, &files[j], buffer, 1) => 1; | ||||
|             lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1; | ||||
|             assert(buffer[0] == alphas[j]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         lfs_file_close(&lfs, &files[j]); | ||||
|         lfs2_file_close(&lfs2, &files[j]); | ||||
|     } | ||||
|      | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # interspersed remove file test | ||||
| @@ -64,112 +64,112 @@ define.SIZE = [10, 100] | ||||
| define.FILES = [4, 10, 26] | ||||
| code = ''' | ||||
|     const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||
|         for (int i = 0; i < SIZE; i++) { | ||||
|             lfs_file_write(&lfs, &file, &alphas[j], 1) => 1; | ||||
|             lfs2_file_write(&lfs2, &file, &alphas[j], 1) => 1; | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file); | ||||
|         lfs2_file_close(&lfs2, &file); | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "zzz", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         lfs_file_write(&lfs, &file, (const void*)"~", 1) => 1; | ||||
|         lfs_file_sync(&lfs, &file) => 0; | ||||
|         lfs2_file_write(&lfs2, &file, (const void*)"~", 1) => 1; | ||||
|         lfs2_file_sync(&lfs2, &file) => 0; | ||||
|  | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_remove(&lfs, path) => 0; | ||||
|         lfs2_remove(&lfs2, path) => 0; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs2_file_close(&lfs2, &file); | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, ".") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, "zzz") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     assert(info.size == FILES); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|     lfs2_dir_close(&lfs2, &dir) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "zzz", LFS_O_RDONLY) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_RDONLY) => 0; | ||||
|     for (int i = 0; i < FILES; i++) { | ||||
|         lfs_file_read(&lfs, &file, buffer, 1) => 1; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, 1) => 1; | ||||
|         assert(buffer[0] == '~'); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs2_file_close(&lfs2, &file); | ||||
|      | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # remove inconveniently test | ||||
| define.SIZE = [10, 100] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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; | ||||
|     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; | ||||
|  | ||||
|     for (int i = 0; i < SIZE/2; i++) { | ||||
|         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_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_remove(&lfs, "f") => 0; | ||||
|     lfs2_remove(&lfs2, "f") => 0; | ||||
|  | ||||
|     for (int i = 0; i < SIZE/2; i++) { | ||||
|         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_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_close(&lfs, &files[0]); | ||||
|     lfs_file_close(&lfs, &files[1]); | ||||
|     lfs_file_close(&lfs, &files[2]); | ||||
|     lfs2_file_close(&lfs2, &files[0]); | ||||
|     lfs2_file_close(&lfs2, &files[1]); | ||||
|     lfs2_file_close(&lfs2, &files[2]); | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, ".") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, "e") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     assert(info.size == SIZE); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, "g") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     assert(info.size == SIZE); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|     lfs2_dir_close(&lfs2, &dir) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &files[0], "e", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[1], "g", LFS_O_RDONLY) => 0; | ||||
|     lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_open(&lfs2, &files[1], "g", LFS2_O_RDONLY) => 0; | ||||
|     for (int i = 0; i < SIZE; i++) { | ||||
|         lfs_file_read(&lfs, &files[0], buffer, 1) => 1; | ||||
|         lfs2_file_read(&lfs2, &files[0], buffer, 1) => 1; | ||||
|         assert(buffer[0] == 'e'); | ||||
|         lfs_file_read(&lfs, &files[1], buffer, 1) => 1; | ||||
|         lfs2_file_read(&lfs2, &files[1], buffer, 1) => 1; | ||||
|         assert(buffer[0] == 'g'); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &files[0]); | ||||
|     lfs_file_close(&lfs, &files[1]); | ||||
|     lfs2_file_close(&lfs2, &files[0]); | ||||
|     lfs2_file_close(&lfs2, &files[1]); | ||||
|      | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant interspersed file test | ||||
| @@ -177,68 +177,68 @@ define.SIZE = [10, 100] | ||||
| define.FILES = [4, 10, 26]  | ||||
| reentrant = true | ||||
| code = ''' | ||||
|     lfs_file_t files[FILES]; | ||||
|     lfs2_file_t files[FILES]; | ||||
|     const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; | ||||
|  | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|  | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_file_open(&lfs, &files[j], path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|         lfs2_file_open(&lfs2, &files[j], path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < SIZE; i++) { | ||||
|         for (int j = 0; j < FILES; j++) { | ||||
|             size = lfs_file_size(&lfs, &files[j]); | ||||
|             size = lfs2_file_size(&lfs2, &files[j]); | ||||
|             assert((int)size >= 0); | ||||
|             if ((int)size <= i) { | ||||
|                 lfs_file_write(&lfs, &files[j], &alphas[j], 1) => 1; | ||||
|                 lfs_file_sync(&lfs, &files[j]) => 0; | ||||
|                 lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1; | ||||
|                 lfs2_file_sync(&lfs2, &files[j]) => 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         lfs_file_close(&lfs, &files[j]); | ||||
|         lfs2_file_close(&lfs2, &files[j]); | ||||
|     } | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, ".") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         assert(strcmp(info.name, path) == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|         assert(info.type == LFS2_TYPE_REG); | ||||
|         assert(info.size == SIZE); | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|     lfs2_dir_close(&lfs2, &dir) => 0; | ||||
|  | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         sprintf(path, "%c", alphas[j]); | ||||
|         lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0; | ||||
|         lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         for (int j = 0; j < FILES; j++) { | ||||
|             lfs_file_read(&lfs, &files[j], buffer, 1) => 1; | ||||
|             lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1; | ||||
|             assert(buffer[0] == alphas[j]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (int j = 0; j < FILES; j++) { | ||||
|         lfs_file_close(&lfs, &files[j]); | ||||
|         lfs2_file_close(&lfs2, &files[j]); | ||||
|     } | ||||
|      | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
							
								
								
									
										2448
									
								
								tests/test_move.toml
									
									
									
									
									
								
							
							
						
						
									
										2448
									
								
								tests/test_move.toml
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,75 +1,75 @@ | ||||
| [[case]] # orphan test | ||||
| in = "lfs.c" | ||||
| if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit | ||||
| in = "lfs2.c" | ||||
| if = 'LFS2_PROG_SIZE <= 0x3fe' # only works with one crc per commit | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "parent") => 0; | ||||
|     lfs_mkdir(&lfs, "parent/orphan") => 0; | ||||
|     lfs_mkdir(&lfs, "parent/child") => 0; | ||||
|     lfs_remove(&lfs, "parent/orphan") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     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; | ||||
|  | ||||
|     // 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. | ||||
|     lfs_mount(&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]; | ||||
|     cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; | ||||
|     int off = LFS_BLOCK_SIZE-1; | ||||
|     while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { | ||||
|     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) { | ||||
|         off -= 1; | ||||
|     } | ||||
|     memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); | ||||
|     memset(&bbuffer[off-3], LFS2_BLOCK_SIZE, 3); | ||||
|     cfg.erase(&cfg, block) => 0; | ||||
|     cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; | ||||
|     cfg.prog(&cfg, block, 0, bbuffer, LFS2_BLOCK_SIZE) => 0; | ||||
|     cfg.sync(&cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&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; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||
|     lfs_stat(&lfs, "parent/child", &info) => 0; | ||||
|     lfs_fs_size(&lfs) => 8; | ||||
|     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; | ||||
|     // this mkdir should both create a dir and deorphan, so size | ||||
|     // should be unchanged | ||||
|     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_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_mount(&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; | ||||
|     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; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant testing for orphans, basically just spam mkdir/remove | ||||
| reentrant = true | ||||
| # TODO fix this case, caused by non-DAG trees | ||||
| if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {FILES=6,  DEPTH=1, CYCLES=20}, | ||||
|     {FILES=26, DEPTH=1, CYCLES=20}, | ||||
|     {FILES=3,  DEPTH=3, CYCLES=20}, | ||||
| ] | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|  | ||||
|     srand(1); | ||||
| @@ -82,39 +82,39 @@ code = ''' | ||||
|         } | ||||
|  | ||||
|         // if it does not exist, we create it, else we destroy | ||||
|         int res = lfs_stat(&lfs, full_path, &info); | ||||
|         if (res == LFS_ERR_NOENT) { | ||||
|         int res = lfs2_stat(&lfs2, full_path, &info); | ||||
|         if (res == LFS2_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 = lfs_mkdir(&lfs, path); | ||||
|                 assert(!err || err == LFS_ERR_EXIST); | ||||
|                 err = lfs2_mkdir(&lfs2, path); | ||||
|                 assert(!err || err == LFS2_ERR_EXIST); | ||||
|             } | ||||
|  | ||||
|             for (int d = 0; d < DEPTH; d++) { | ||||
|                 strcpy(path, full_path); | ||||
|                 path[2*d+2] = '\0'; | ||||
|                 lfs_stat(&lfs, path, &info) => 0; | ||||
|                 lfs2_stat(&lfs2, path, &info) => 0; | ||||
|                 assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||
|                 assert(info.type == LFS_TYPE_DIR); | ||||
|                 assert(info.type == LFS2_TYPE_DIR); | ||||
|             } | ||||
|         } else { | ||||
|             // is valid dir? | ||||
|             assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); | ||||
|             assert(info.type == LFS_TYPE_DIR); | ||||
|             assert(info.type == LFS2_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 = lfs_remove(&lfs, path); | ||||
|                 assert(!err || err == LFS_ERR_NOTEMPTY); | ||||
|                 err = lfs2_remove(&lfs2, path); | ||||
|                 assert(!err || err == LFS2_ERR_NOTEMPTY); | ||||
|             } | ||||
|  | ||||
|             lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; | ||||
|             lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT; | ||||
|         } | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
|   | ||||
| @@ -1,293 +1,293 @@ | ||||
|  | ||||
| [[case]] # simple path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_stat(&lfs, "tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "/tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|  | ||||
|     lfs_mkdir(&lfs, "/milk") => 0; | ||||
|     lfs_stat(&lfs, "/milk", &info) => 0; | ||||
|     lfs2_mkdir(&lfs2, "/milk") => 0; | ||||
|     lfs2_stat(&lfs2, "/milk", &info) => 0; | ||||
|     assert(strcmp(info.name, "milk") == 0); | ||||
|     lfs_stat(&lfs, "milk", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||
|     assert(strcmp(info.name, "milk") == 0); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # redundant slashes | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_stat(&lfs, "/tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "//tea//hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "//tea//hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "///tea///hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "///tea///hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|  | ||||
|     lfs_mkdir(&lfs, "////milk") => 0; | ||||
|     lfs_stat(&lfs, "////milk", &info) => 0; | ||||
|     lfs2_mkdir(&lfs2, "////milk") => 0; | ||||
|     lfs2_stat(&lfs2, "////milk", &info) => 0; | ||||
|     assert(strcmp(info.name, "milk") == 0); | ||||
|     lfs_stat(&lfs, "milk", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||
|     assert(strcmp(info.name, "milk") == 0); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # dot path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_stat(&lfs, "./tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "./tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "/./tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "/./tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "/././tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "/././tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "/./tea/./hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "/./tea/./hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|  | ||||
|     lfs_mkdir(&lfs, "/./milk") => 0; | ||||
|     lfs_stat(&lfs, "/./milk", &info) => 0; | ||||
|     lfs2_mkdir(&lfs2, "/./milk") => 0; | ||||
|     lfs2_stat(&lfs2, "/./milk", &info) => 0; | ||||
|     assert(strcmp(info.name, "milk") == 0); | ||||
|     lfs_stat(&lfs, "milk", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||
|     assert(strcmp(info.name, "milk") == 0); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # dot dot path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_stat(&lfs, "coffee/../tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "coffee/../tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/coldtea/../hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "coffee/coldcoffee/../../tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "coffee/coldcoffee/../../tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "coffee/../coffee/../tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "coffee/../coffee/../tea/hottea", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|  | ||||
|     lfs_mkdir(&lfs, "coffee/../milk") => 0; | ||||
|     lfs_stat(&lfs, "coffee/../milk", &info) => 0; | ||||
|     lfs2_mkdir(&lfs2, "coffee/../milk") => 0; | ||||
|     lfs2_stat(&lfs2, "coffee/../milk", &info) => 0; | ||||
|     strcmp(info.name, "milk") => 0; | ||||
|     lfs_stat(&lfs, "milk", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||
|     strcmp(info.name, "milk") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # trailing dot path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_stat(&lfs, "tea/hottea/", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/hottea/", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "tea/hottea/.", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/hottea/.", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "tea/hottea/./.", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/hottea/./.", &info) => 0; | ||||
|     assert(strcmp(info.name, "hottea") == 0); | ||||
|     lfs_stat(&lfs, "tea/hottea/..", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/hottea/..", &info) => 0; | ||||
|     assert(strcmp(info.name, "tea") == 0); | ||||
|     lfs_stat(&lfs, "tea/hottea/../.", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/hottea/../.", &info) => 0; | ||||
|     assert(strcmp(info.name, "tea") == 0); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # leading dot path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, ".milk") => 0; | ||||
|     lfs_stat(&lfs, ".milk", &info) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_mkdir(&lfs2, ".milk") => 0; | ||||
|     lfs2_stat(&lfs2, ".milk", &info) => 0; | ||||
|     strcmp(info.name, ".milk") => 0; | ||||
|     lfs_stat(&lfs, "tea/.././.milk", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "tea/.././.milk", &info) => 0; | ||||
|     strcmp(info.name, ".milk") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # root dot dot path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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_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_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "coffee/../../../../../../tea/hottea", &info) => 0; | ||||
|     strcmp(info.name, "hottea") => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "coffee/../../../../../../milk") => 0; | ||||
|     lfs_stat(&lfs, "coffee/../../../../../../milk", &info) => 0; | ||||
|     lfs2_mkdir(&lfs2, "coffee/../../../../../../milk") => 0; | ||||
|     lfs2_stat(&lfs2, "coffee/../../../../../../milk", &info) => 0; | ||||
|     strcmp(info.name, "milk") => 0; | ||||
|     lfs_stat(&lfs, "milk", &info) => 0; | ||||
|     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||
|     strcmp(info.name, "milk") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid path tests | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg); | ||||
|     lfs_mount(&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_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_remove(&lfs, "dirt") => LFS_ERR_NOENT; | ||||
|     lfs_remove(&lfs, "dirt/ground") => LFS_ERR_NOENT; | ||||
|     lfs_remove(&lfs, "dirt/ground/earth") => 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_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; | ||||
|     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; | ||||
| ''' | ||||
|  | ||||
| [[case]] # root operations | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "/", &info) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_stat(&lfs2, "/", &info) => 0; | ||||
|     assert(strcmp(info.name, "/") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|  | ||||
|     lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST; | ||||
|     lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY | LFS_O_CREAT) | ||||
|             => LFS_ERR_ISDIR; | ||||
|     lfs2_mkdir(&lfs2, "/") => LFS2_ERR_EXIST; | ||||
|     lfs2_file_open(&lfs2, &file, "/", LFS2_O_WRONLY | LFS2_O_CREAT) | ||||
|             => LFS2_ERR_ISDIR; | ||||
|  | ||||
|     lfs_remove(&lfs, "/") => LFS_ERR_INVAL; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_remove(&lfs2, "/") => LFS2_ERR_INVAL; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # root representations | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "/", &info) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_stat(&lfs2, "/", &info) => 0; | ||||
|     assert(strcmp(info.name, "/") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_stat(&lfs, "", &info) => 0; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_stat(&lfs2, "", &info) => 0; | ||||
|     assert(strcmp(info.name, "/") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_stat(&lfs, ".", &info) => 0; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_stat(&lfs2, ".", &info) => 0; | ||||
|     assert(strcmp(info.name, "/") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_stat(&lfs, "..", &info) => 0; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_stat(&lfs2, "..", &info) => 0; | ||||
|     assert(strcmp(info.name, "/") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_stat(&lfs, "//", &info) => 0; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_stat(&lfs2, "//", &info) => 0; | ||||
|     assert(strcmp(info.name, "/") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_stat(&lfs, "./", &info) => 0; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_stat(&lfs2, "./", &info) => 0; | ||||
|     assert(strcmp(info.name, "/") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # superblock conflict test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "littlefs", &info) => LFS_ERR_NOENT; | ||||
|     lfs_remove(&lfs, "littlefs") => LFS_ERR_NOENT; | ||||
|     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_mkdir(&lfs, "littlefs") => 0; | ||||
|     lfs_stat(&lfs, "littlefs", &info) => 0; | ||||
|     lfs2_mkdir(&lfs2, "littlefs") => 0; | ||||
|     lfs2_stat(&lfs2, "littlefs", &info) => 0; | ||||
|     assert(strcmp(info.name, "littlefs") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     lfs_remove(&lfs, "littlefs") => 0; | ||||
|     lfs_stat(&lfs, "littlefs", &info) => LFS_ERR_NOENT; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     assert(info.type == LFS2_TYPE_DIR); | ||||
|     lfs2_remove(&lfs2, "littlefs") => 0; | ||||
|     lfs2_stat(&lfs2, "littlefs", &info) => LFS2_ERR_NOENT; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # max path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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; | ||||
|     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; | ||||
|  | ||||
|     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; | ||||
|     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; | ||||
|  | ||||
|     memcpy(path, "coffee/", strlen("coffee/")); | ||||
|     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; | ||||
|     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; | ||||
| ''' | ||||
|  | ||||
| [[case]] # really big path test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&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; | ||||
|     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; | ||||
|  | ||||
|     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; | ||||
|     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; | ||||
|  | ||||
|     memcpy(path, "coffee/", strlen("coffee/")); | ||||
|     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; | ||||
|     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; | ||||
| ''' | ||||
|  | ||||
|   | ||||
| @@ -2,39 +2,39 @@ | ||||
| [[case]] # dangling split dir test | ||||
| define.ITERATIONS = 20 | ||||
| define.COUNT = 10 | ||||
| define.LFS_BLOCK_CYCLES = [8, 1] | ||||
| define.LFS2_BLOCK_CYCLES = [8, 1] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     // fill up filesystem so only ~16 blocks are left | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||
|     memset(buffer, 0, 512); | ||||
|     while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { | ||||
|         lfs_file_write(&lfs, &file, buffer, 512) => 512; | ||||
|     while (LFS2_BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, 512) => 512; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // make a child dir to use in bounded space | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_mkdir(&lfs2, "child") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int j = 0; j < ITERATIONS; j++) { | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_dir_open(&lfs, &dir, "child") => 0; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs2_dir_open(&lfs2, &dir, "child") => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|         } | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|         lfs_dir_close(&lfs, &dir) => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|         lfs2_dir_close(&lfs2, &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); | ||||
|             lfs_remove(&lfs, path) => 0; | ||||
|             lfs2_remove(&lfs2, path) => 0; | ||||
|         } | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "child") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     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; | ||||
|     for (int i = 0; i < COUNT; i++) { | ||||
|         sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|     lfs2_dir_close(&lfs2, &dir) => 0; | ||||
|     for (int i = 0; i < COUNT; i++) { | ||||
|         sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
|         lfs_remove(&lfs, path) => 0; | ||||
|         lfs2_remove(&lfs2, path) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # outdated head test | ||||
| define.ITERATIONS = 20 | ||||
| define.COUNT = 10 | ||||
| define.LFS_BLOCK_CYCLES = [8, 1] | ||||
| define.LFS2_BLOCK_CYCLES = [8, 1] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     // fill up filesystem so only ~16 blocks are left | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||
|     memset(buffer, 0, 512); | ||||
|     while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { | ||||
|         lfs_file_write(&lfs, &file, buffer, 512) => 512; | ||||
|     while (LFS2_BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, 512) => 512; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     // make a child dir to use in bounded space | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_mkdir(&lfs2, "child") => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int j = 0; j < ITERATIONS; j++) { | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|             lfs_file_close(&lfs, &file) => 0; | ||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_dir_open(&lfs, &dir, "child") => 0; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs2_dir_open(&lfs2, &dir, "child") => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|             info.size => 0; | ||||
|  | ||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
|             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_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0; | ||||
|             lfs2_file_write(&lfs2, &file, "hi", 2) => 2; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|  | ||||
|         lfs_dir_rewind(&lfs, &dir) => 0; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs2_dir_rewind(&lfs2, &dir) => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|             info.size => 2; | ||||
|  | ||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
|             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_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0; | ||||
|             lfs2_file_write(&lfs2, &file, "hi", 2) => 2; | ||||
|             lfs2_file_close(&lfs2, &file) => 0; | ||||
|         } | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|  | ||||
|         lfs_dir_rewind(&lfs, &dir) => 0; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         lfs2_dir_rewind(&lfs2, &dir) => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||
|             strcmp(info.name, path) => 0; | ||||
|             info.size => 2; | ||||
|         } | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|         lfs_dir_close(&lfs, &dir) => 0; | ||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||
|         lfs2_dir_close(&lfs2, &dir) => 0; | ||||
|  | ||||
|         for (int i = 0; i < COUNT; i++) { | ||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||
|             lfs_remove(&lfs, path) => 0; | ||||
|             lfs2_remove(&lfs2, path) => 0; | ||||
|         } | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 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 && LFS_CACHE_SIZE != 64)' | ||||
| if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {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}, | ||||
|     {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}, | ||||
| ] | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|  | ||||
|     srand(1); | ||||
| @@ -171,56 +171,56 @@ code = ''' | ||||
|         } | ||||
|  | ||||
|         // if it does not exist, we create it, else we destroy | ||||
|         int res = lfs_stat(&lfs, full_path, &info); | ||||
|         if (res == LFS_ERR_NOENT) { | ||||
|         int res = lfs2_stat(&lfs2, full_path, &info); | ||||
|         if (res == LFS2_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 = lfs_mkdir(&lfs, path); | ||||
|                 assert(!err || err == LFS_ERR_EXIST); | ||||
|                 err = lfs2_mkdir(&lfs2, path); | ||||
|                 assert(!err || err == LFS2_ERR_EXIST); | ||||
|             } | ||||
|  | ||||
|             for (int d = 0; d < DEPTH; d++) { | ||||
|                 strcpy(path, full_path); | ||||
|                 path[2*d+2] = '\0'; | ||||
|                 lfs_stat(&lfs, path, &info) => 0; | ||||
|                 lfs2_stat(&lfs2, path, &info) => 0; | ||||
|                 assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||
|                 assert(info.type == LFS_TYPE_DIR); | ||||
|                 assert(info.type == LFS2_TYPE_DIR); | ||||
|             } | ||||
|         } else { | ||||
|             // is valid dir? | ||||
|             assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); | ||||
|             assert(info.type == LFS_TYPE_DIR); | ||||
|             assert(info.type == LFS2_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 = lfs_remove(&lfs, path); | ||||
|                 assert(!err || err == LFS_ERR_NOTEMPTY); | ||||
|                 err = lfs2_remove(&lfs2, path); | ||||
|                 assert(!err || err == LFS2_ERR_NOTEMPTY); | ||||
|             } | ||||
|  | ||||
|             lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; | ||||
|             lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT; | ||||
|         } | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 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 && LFS_CACHE_SIZE != 64)' | ||||
| if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {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}, | ||||
|     {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}, | ||||
| ] | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|  | ||||
|     srand(1); | ||||
| @@ -233,27 +233,27 @@ code = ''' | ||||
|         } | ||||
|  | ||||
|         // if it does not exist, we create it, else we destroy | ||||
|         int res = lfs_stat(&lfs, full_path, &info); | ||||
|         assert(!res || res == LFS_ERR_NOENT); | ||||
|         if (res == LFS_ERR_NOENT) { | ||||
|         int res = lfs2_stat(&lfs2, full_path, &info); | ||||
|         assert(!res || res == LFS2_ERR_NOENT); | ||||
|         if (res == LFS2_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 = lfs_mkdir(&lfs, path); | ||||
|                 assert(!err || err == LFS_ERR_EXIST); | ||||
|                 err = lfs2_mkdir(&lfs2, path); | ||||
|                 assert(!err || err == LFS2_ERR_EXIST); | ||||
|             } | ||||
|  | ||||
|             for (int d = 0; d < DEPTH; d++) { | ||||
|                 strcpy(path, full_path); | ||||
|                 path[2*d+2] = '\0'; | ||||
|                 lfs_stat(&lfs, path, &info) => 0; | ||||
|                 lfs2_stat(&lfs2, path, &info) => 0; | ||||
|                 assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||
|                 assert(info.type == LFS_TYPE_DIR); | ||||
|                 assert(info.type == LFS2_TYPE_DIR); | ||||
|             } | ||||
|         } else { | ||||
|             assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); | ||||
|             assert(info.type == LFS_TYPE_DIR); | ||||
|             assert(info.type == LFS2_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 = lfs_stat(&lfs, new_path, &info); | ||||
|             assert(!res || res == LFS_ERR_NOENT); | ||||
|             if (res == LFS_ERR_NOENT) { | ||||
|             res = lfs2_stat(&lfs2, new_path, &info); | ||||
|             assert(!res || res == LFS2_ERR_NOENT); | ||||
|             if (res == LFS2_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 = lfs_rename(&lfs, path, path+128); | ||||
|                     assert(!err || err == LFS_ERR_NOTEMPTY); | ||||
|                     err = lfs2_rename(&lfs2, path, path+128); | ||||
|                     assert(!err || err == LFS2_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'; | ||||
|                     lfs_stat(&lfs, path, &info) => 0; | ||||
|                     lfs2_stat(&lfs2, path, &info) => 0; | ||||
|                     assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||
|                     assert(info.type == LFS_TYPE_DIR); | ||||
|                     assert(info.type == LFS2_TYPE_DIR); | ||||
|                 } | ||||
|                  | ||||
|                 lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; | ||||
|                 lfs2_stat(&lfs2, full_path, &info) => LFS2_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 = lfs_remove(&lfs, path); | ||||
|                     assert(!err || err == LFS_ERR_NOTEMPTY); | ||||
|                     err = lfs2_remove(&lfs2, path); | ||||
|                     assert(!err || err == LFS2_ERR_NOTEMPTY); | ||||
|                 } | ||||
|  | ||||
|                 lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; | ||||
|                 lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
| @@ -9,63 +9,63 @@ define = [ | ||||
|     {COUNT=4,   SKIP=2}, | ||||
| ] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|     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; | ||||
|     size = strlen("kittycatcat"); | ||||
|     memcpy(buffer, "kittycatcat", size); | ||||
|     for (int j = 0; j < COUNT; j++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size); | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY) => 0; | ||||
|  | ||||
|     lfs_soff_t pos = -1; | ||||
|     lfs2_soff_t pos = -1; | ||||
|     size = strlen("kittycatcat"); | ||||
|     for (int i = 0; i < SKIP; i++) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|         pos = lfs_file_tell(&lfs, &file); | ||||
|         pos = lfs2_file_tell(&lfs2, &file); | ||||
|     } | ||||
|     assert(pos >= 0); | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_rewind(&lfs, &file) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_rewind(&lfs2, &file) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, size, LFS_SEEK_CUR) => 3*size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, size, LFS2_SEEK_CUR) => 3*size; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_CUR) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_CUR) => pos; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     size = lfs_file_size(&lfs, &file); | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|     size = lfs2_file_size(&lfs2, &file); | ||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # simple file seek and write | ||||
| @@ -78,109 +78,109 @@ define = [ | ||||
|     {COUNT=4,   SKIP=2}, | ||||
| ] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|     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; | ||||
|     size = strlen("kittycatcat"); | ||||
|     memcpy(buffer, "kittycatcat", size); | ||||
|     for (int j = 0; j < COUNT; j++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size); | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||
|  | ||||
|     lfs_soff_t pos = -1; | ||||
|     lfs2_soff_t pos = -1; | ||||
|     size = strlen("kittycatcat"); | ||||
|     for (int i = 0; i < SKIP; i++) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|         pos = lfs_file_tell(&lfs, &file); | ||||
|         pos = lfs2_file_tell(&lfs2, &file); | ||||
|     } | ||||
|     assert(pos >= 0); | ||||
|  | ||||
|     memcpy(buffer, "doggodogdog", 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_write(&lfs2, &file, buffer, size) => size; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "doggodogdog", size) => 0; | ||||
|  | ||||
|     lfs_file_rewind(&lfs, &file) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_rewind(&lfs2, &file) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "doggodogdog", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     size = lfs_file_size(&lfs, &file); | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|     size = lfs2_file_size(&lfs2, &file); | ||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # boundary seek and writes | ||||
| define.COUNT = 132 | ||||
| define.OFFSETS = '"{512, 1020, 513, 1021, 511, 1019, 1441}"' | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|     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; | ||||
|     size = strlen("kittycatcat"); | ||||
|     memcpy(buffer, "kittycatcat", size); | ||||
|     for (int j = 0; j < COUNT; j++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size); | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||
|  | ||||
|     size = strlen("hedgehoghog"); | ||||
|     const lfs_soff_t offsets[] = OFFSETS; | ||||
|     const lfs2_soff_t offsets[] = OFFSETS; | ||||
|  | ||||
|     for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { | ||||
|         lfs_soff_t off = offsets[i]; | ||||
|         lfs2_soff_t off = offsets[i]; | ||||
|         memcpy(buffer, "hedgehoghog", size); | ||||
|         lfs_file_seek(&lfs, &file, 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; | ||||
|         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; | ||||
|         memcmp(buffer, "hedgehoghog", size) => 0; | ||||
|  | ||||
|         lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|         lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hedgehoghog", size) => 0; | ||||
|  | ||||
|         lfs_file_sync(&lfs, &file) => 0; | ||||
|         lfs2_file_sync(&lfs2, &file) => 0; | ||||
|  | ||||
|         lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|         lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hedgehoghog", size) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # out of bounds seek | ||||
| @@ -193,116 +193,116 @@ define = [ | ||||
|     {COUNT=4,   SKIP=3}, | ||||
| ] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|     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; | ||||
|     size = strlen("kittycatcat"); | ||||
|     memcpy(buffer, "kittycatcat", size); | ||||
|     for (int j = 0; j < COUNT; j++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size); | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; | ||||
|     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; | ||||
|  | ||||
|     size = strlen("kittycatcat"); | ||||
|     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; | ||||
|     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; | ||||
|  | ||||
|     memcpy(buffer, "porcupineee", size); | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_write(&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; | ||||
|     lfs2_file_seek(&lfs2, &file, (COUNT+SKIP)*size, | ||||
|             LFS2_SEEK_SET) => (COUNT+SKIP)*size; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "porcupineee", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, COUNT*size, | ||||
|             LFS_SEEK_SET) => COUNT*size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     lfs2_file_seek(&lfs2, &file, COUNT*size, | ||||
|             LFS2_SEEK_SET) => COUNT*size; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0; | ||||
|  | ||||
|     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+SKIP)*size), | ||||
|             LFS2_SEEK_CUR) => 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_seek(&lfs2, &file, -((COUNT+2*SKIP)*size), | ||||
|             LFS2_SEEK_END) => LFS2_ERR_INVAL; | ||||
|     lfs2_file_tell(&lfs2, &file) => (COUNT+1)*size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # inline write and seek | ||||
| define.SIZE = [2, 4, 128, 132] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "tinykitty", | ||||
|             LFS_O_RDWR | LFS_O_CREAT) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "tinykitty", | ||||
|             LFS2_O_RDWR | LFS2_O_CREAT) => 0; | ||||
|     int j = 0; | ||||
|     int k = 0; | ||||
|  | ||||
|     memcpy(buffer, "abcdefghijklmnopqrstuvwxyz", 26); | ||||
|     for (unsigned i = 0; i < SIZE; i++) { | ||||
|         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_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1; | ||||
|         lfs2_file_tell(&lfs2, &file) => i+1; | ||||
|         lfs2_file_size(&lfs2, &file) => i+1; | ||||
|     } | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => 0; | ||||
|     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; | ||||
|     for (unsigned i = 0; i < SIZE; i++) { | ||||
|         uint8_t c; | ||||
|         lfs_file_read(&lfs, &file, &c, 1) => 1; | ||||
|         lfs2_file_read(&lfs2, &file, &c, 1) => 1; | ||||
|         c => buffer[k++ % 26]; | ||||
|     } | ||||
|  | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => SIZE; | ||||
|     lfs_file_size(&lfs, &file) => SIZE; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     lfs2_file_tell(&lfs2, &file) => SIZE; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||
|     for (unsigned i = 0; i < SIZE; i++) { | ||||
|         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; | ||||
|         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; | ||||
|         if (i < SIZE-2) { | ||||
|             uint8_t c[3]; | ||||
|             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, -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, 0, LFS_SEEK_SET) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => 0; | ||||
|     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; | ||||
|     for (unsigned i = 0; i < SIZE; i++) { | ||||
|         uint8_t c; | ||||
|         lfs_file_read(&lfs, &file, &c, 1) => 1; | ||||
|         lfs2_file_read(&lfs2, &file, &c, 1) => 1; | ||||
|         c => buffer[k++ % 26]; | ||||
|     } | ||||
|  | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => SIZE; | ||||
|     lfs_file_size(&lfs, &file) => SIZE; | ||||
|     lfs2_file_sync(&lfs2, &file) => 0; | ||||
|     lfs2_file_tell(&lfs2, &file) => SIZE; | ||||
|     lfs2_file_size(&lfs2, &file) => SIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # file seek and write with power-loss | ||||
| @@ -310,71 +310,71 @@ code = ''' | ||||
| define.COUNT = [4, 64, 128] | ||||
| reentrant = true | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|     err = lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY); | ||||
|     assert(!err || err == LFS_ERR_NOENT); | ||||
|     err = lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY); | ||||
|     assert(!err || err == LFS2_ERR_NOENT); | ||||
|     if (!err) { | ||||
|         if (lfs_file_size(&lfs, &file) != 0) { | ||||
|             lfs_file_size(&lfs, &file) => 11*COUNT; | ||||
|         if (lfs2_file_size(&lfs2, &file) != 0) { | ||||
|             lfs2_file_size(&lfs2, &file) => 11*COUNT; | ||||
|             for (int j = 0; j < COUNT; j++) { | ||||
|                 memset(buffer, 0, 11+1); | ||||
|                 lfs_file_read(&lfs, &file, buffer, 11) => 11; | ||||
|                 lfs2_file_read(&lfs2, &file, buffer, 11) => 11; | ||||
|                 assert(memcmp(buffer, "kittycatcat", 11) == 0 || | ||||
|                        memcmp(buffer, "doggodogdog", 11) == 0); | ||||
|             } | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "kitty", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     if (lfs_file_size(&lfs, &file) == 0) { | ||||
|     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|     if (lfs2_file_size(&lfs2, &file) == 0) { | ||||
|         for (int j = 0; j < COUNT; j++) { | ||||
|             strcpy((char*)buffer, "kittycatcat"); | ||||
|             size = strlen((char*)buffer); | ||||
|             lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|         } | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     strcpy((char*)buffer, "doggodogdog"); | ||||
|     size = strlen((char*)buffer); | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => COUNT*size; | ||||
|     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => COUNT*size; | ||||
|     // seek and write using quadratic probing to touch all | ||||
|     // 11-byte words in the file | ||||
|     lfs_off_t off = 0; | ||||
|     lfs2_off_t off = 0; | ||||
|     for (int j = 0; j < COUNT; j++) { | ||||
|         off = (5*off + 1) % COUNT; | ||||
|         lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         assert(memcmp(buffer, "kittycatcat", size) == 0 || | ||||
|                memcmp(buffer, "doggodogdog", size) == 0); | ||||
|         if (memcmp(buffer, "doggodogdog", size) != 0) { | ||||
|             lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size; | ||||
|             lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size; | ||||
|             strcpy((char*)buffer, "doggodogdog"); | ||||
|             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; | ||||
|             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; | ||||
|             assert(memcmp(buffer, "doggodogdog", size) == 0); | ||||
|             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; | ||||
|             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; | ||||
|             assert(memcmp(buffer, "doggodogdog", size) == 0); | ||||
|         } | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => COUNT*size; | ||||
|     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => COUNT*size; | ||||
|     for (int j = 0; j < COUNT; j++) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         assert(memcmp(buffer, "doggodogdog", size) == 0); | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
| @@ -1,127 +1,127 @@ | ||||
| [[case]] # simple formatting test | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # mount/unmount | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant format | ||||
| reentrant = true | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid mount | ||||
| code = ''' | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
|     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # expanding superblock | ||||
| define.LFS_BLOCK_CYCLES = [32, 33, 1] | ||||
| define.LFS2_BLOCK_CYCLES = [32, 33, 1] | ||||
| define.N = [10, 100, 1000] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         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; | ||||
|         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; | ||||
|         assert(strcmp(info.name, "dummy") == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|         lfs_remove(&lfs, "dummy") => 0; | ||||
|         assert(info.type == LFS2_TYPE_REG); | ||||
|         lfs2_remove(&lfs2, "dummy") => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // one last check after power-cycle | ||||
|     lfs_mount(&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; | ||||
|     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; | ||||
|     assert(strcmp(info.name, "dummy") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # expanding superblock with power cycle | ||||
| define.LFS_BLOCK_CYCLES = [32, 33, 1] | ||||
| define.LFS2_BLOCK_CYCLES = [32, 33, 1] | ||||
| define.N = [10, 100, 1000] | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|         // remove lingering dummy? | ||||
|         err = lfs_stat(&lfs, "dummy", &info); | ||||
|         assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); | ||||
|         err = lfs2_stat(&lfs2, "dummy", &info); | ||||
|         assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0)); | ||||
|         if (!err) { | ||||
|             assert(strcmp(info.name, "dummy") == 0); | ||||
|             assert(info.type == LFS_TYPE_REG); | ||||
|             lfs_remove(&lfs, "dummy") => 0; | ||||
|             assert(info.type == LFS2_TYPE_REG); | ||||
|             lfs2_remove(&lfs2, "dummy") => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_open(&lfs, &file, "dummy", | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|         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; | ||||
|         assert(strcmp(info.name, "dummy") == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|         assert(info.type == LFS2_TYPE_REG); | ||||
|         lfs2_unmount(&lfs2) => 0; | ||||
|     } | ||||
|  | ||||
|     // one last check after power-cycle | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||
|     assert(strcmp(info.name, "dummy") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # reentrant expanding superblock | ||||
| define.LFS_BLOCK_CYCLES = [2, 1] | ||||
| define.LFS2_BLOCK_CYCLES = [2, 1] | ||||
| define.N = 24 | ||||
| reentrant = true | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         // remove lingering dummy? | ||||
|         err = lfs_stat(&lfs, "dummy", &info); | ||||
|         assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); | ||||
|         err = lfs2_stat(&lfs2, "dummy", &info); | ||||
|         assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0)); | ||||
|         if (!err) { | ||||
|             assert(strcmp(info.name, "dummy") == 0); | ||||
|             assert(info.type == LFS_TYPE_REG); | ||||
|             lfs_remove(&lfs, "dummy") => 0; | ||||
|             assert(info.type == LFS2_TYPE_REG); | ||||
|             lfs2_remove(&lfs2, "dummy") => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_open(&lfs, &file, "dummy", | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|         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; | ||||
|         assert(strcmp(info.name, "dummy") == 0); | ||||
|         assert(info.type == LFS_TYPE_REG); | ||||
|         assert(info.type == LFS2_TYPE_REG); | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     // one last check after power-cycle | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "dummy", &info) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||
|     assert(strcmp(info.name, "dummy") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     assert(info.type == LFS2_TYPE_REG); | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|   | ||||
| @@ -2,198 +2,198 @@ | ||||
| define.MEDIUMSIZE = [32, 2048] | ||||
| define.LARGESIZE = 8192 | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldynoop", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldynoop", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|  | ||||
|     strcpy((char*)buffer, "hair"); | ||||
|     size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => LARGESIZE; | ||||
|     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|      | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => LARGESIZE; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDWR) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||
|  | ||||
|     lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0; | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|  | ||||
|     size = strlen("hair"); | ||||
|     for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hair", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # truncate and read | ||||
| define.MEDIUMSIZE = [32, 2048] | ||||
| define.LARGESIZE = 8192 | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldyread", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldyread", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|  | ||||
|     strcpy((char*)buffer, "hair"); | ||||
|     size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => LARGESIZE; | ||||
|     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => LARGESIZE; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDWR) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||
|  | ||||
|     lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0; | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|  | ||||
|     size = strlen("hair"); | ||||
|     for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hair", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|  | ||||
|     size = strlen("hair"); | ||||
|     for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hair", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # write, truncate, and read | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "sequence", | ||||
|             LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     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; | ||||
|  | ||||
|     size = lfs_min(lfs.cfg->cache_size, sizeof(buffer)/2); | ||||
|     lfs_size_t qsize = size / 4; | ||||
|     size = lfs2_min(lfs2.cfg->cache_size, sizeof(buffer)/2); | ||||
|     lfs2_size_t qsize = size / 4; | ||||
|     uint8_t *wb = buffer; | ||||
|     uint8_t *rb = buffer + size; | ||||
|     for (lfs_off_t j = 0; j < size; ++j) { | ||||
|     for (lfs2_off_t j = 0; j < size; ++j) { | ||||
|         wb[j] = j; | ||||
|     } | ||||
|  | ||||
|     /* Spread sequence over size */ | ||||
|     lfs_file_write(&lfs, &file, wb, size) => size; | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_tell(&lfs, &file) => size; | ||||
|     lfs2_file_write(&lfs2, &file, wb, size) => size; | ||||
|     lfs2_file_size(&lfs2, &file) => size; | ||||
|     lfs2_file_tell(&lfs2, &file) => size; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => 0; | ||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||
|     lfs2_file_tell(&lfs2, &file) => 0; | ||||
|  | ||||
|     /* Chop off the last quarter */ | ||||
|     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; | ||||
|     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; | ||||
|  | ||||
|     /* Read should produce first 3/4 */ | ||||
|     lfs_file_read(&lfs, &file, rb, size) => trunc; | ||||
|     lfs2_file_read(&lfs2, &file, rb, size) => trunc; | ||||
|     memcmp(rb, wb, trunc) => 0; | ||||
|  | ||||
|     /* Move to 1/4 */ | ||||
|     lfs_file_size(&lfs, &file) => trunc; | ||||
|     lfs_file_seek(&lfs, &file, qsize, LFS_SEEK_SET) => qsize; | ||||
|     lfs_file_tell(&lfs, &file) => qsize; | ||||
|     lfs2_file_size(&lfs2, &file) => trunc; | ||||
|     lfs2_file_seek(&lfs2, &file, qsize, LFS2_SEEK_SET) => qsize; | ||||
|     lfs2_file_tell(&lfs2, &file) => qsize; | ||||
|  | ||||
|     /* Chop to 1/2 */ | ||||
|     trunc -= qsize; | ||||
|     lfs_file_truncate(&lfs, &file, trunc) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => qsize; | ||||
|     lfs_file_size(&lfs, &file) => trunc; | ||||
|     lfs2_file_truncate(&lfs2, &file, trunc) => 0; | ||||
|     lfs2_file_tell(&lfs2, &file) => qsize; | ||||
|     lfs2_file_size(&lfs2, &file) => trunc; | ||||
|      | ||||
|     /* Read should produce second quarter */ | ||||
|     lfs_file_read(&lfs, &file, rb, size) => trunc - qsize; | ||||
|     lfs2_file_read(&lfs2, &file, rb, size) => trunc - qsize; | ||||
|     memcmp(rb, wb + qsize, trunc - qsize) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # truncate and write | ||||
| define.MEDIUMSIZE = [32, 2048] | ||||
| define.LARGESIZE = 8192 | ||||
| code = ''' | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldywrite", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldywrite", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||
|  | ||||
|     strcpy((char*)buffer, "hair"); | ||||
|     size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => LARGESIZE; | ||||
|     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => LARGESIZE; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDWR) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||
|  | ||||
|     lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0; | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|  | ||||
|     strcpy((char*)buffer, "bald"); | ||||
|     size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDONLY) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|  | ||||
|     size = strlen("bald"); | ||||
|     for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "bald", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # truncate write under powerloss | ||||
| @@ -202,64 +202,64 @@ define.MEDIUMSIZE = [32, 1024] | ||||
| define.LARGESIZE = 2048 | ||||
| reentrant = true | ||||
| code = ''' | ||||
|     err = lfs_mount(&lfs, &cfg); | ||||
|     err = lfs2_mount(&lfs2, &cfg); | ||||
|     if (err) { | ||||
|         lfs_format(&lfs, &cfg) => 0; | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs2_format(&lfs2, &cfg) => 0; | ||||
|         lfs2_mount(&lfs2, &cfg) => 0; | ||||
|     } | ||||
|     err = lfs_file_open(&lfs, &file, "baldy", LFS_O_RDONLY); | ||||
|     assert(!err || err == LFS_ERR_NOENT); | ||||
|     err = lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDONLY); | ||||
|     assert(!err || err == LFS2_ERR_NOENT); | ||||
|     if (!err) { | ||||
|         size = lfs_file_size(&lfs, &file); | ||||
|         size = lfs2_file_size(&lfs2, &file); | ||||
|         assert(size == 0 || | ||||
|                 size == LARGESIZE || | ||||
|                 size == MEDIUMSIZE || | ||||
|                 size == SMALLSIZE); | ||||
|         for (lfs_off_t j = 0; j < size; j += 4) { | ||||
|             lfs_file_read(&lfs, &file, buffer, 4) => 4; | ||||
|         for (lfs2_off_t j = 0; j < size; j += 4) { | ||||
|             lfs2_file_read(&lfs2, &file, buffer, 4) => 4; | ||||
|             assert(memcmp(buffer, "hair", 4) == 0 || | ||||
|                    memcmp(buffer, "bald", 4) == 0 || | ||||
|                    memcmp(buffer, "comb", 4) == 0); | ||||
|         } | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&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; | ||||
|     lfs2_file_open(&lfs2, &file, "baldy", | ||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => 0; | ||||
|     strcpy((char*)buffer, "hair"); | ||||
|     size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => LARGESIZE; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     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; | ||||
|     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; | ||||
|     strcpy((char*)buffer, "bald"); | ||||
|     size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => MEDIUMSIZE; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     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; | ||||
|     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; | ||||
|     strcpy((char*)buffer, "comb"); | ||||
|     size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < SMALLSIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     for (lfs2_off_t j = 0; j < SMALLSIZE; j += size) { | ||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => SMALLSIZE; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs2_file_size(&lfs2, &file) => SMALLSIZE; | ||||
|     lfs2_file_close(&lfs2, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # more aggressive general truncation tests | ||||
| @@ -270,10 +270,10 @@ define.LARGESIZE = 8192 | ||||
| code = ''' | ||||
|     #define COUNT 5 | ||||
|     const struct { | ||||
|         lfs_off_t startsizes[COUNT]; | ||||
|         lfs_off_t startseeks[COUNT]; | ||||
|         lfs_off_t hotsizes[COUNT]; | ||||
|         lfs_off_t coldsizes[COUNT]; | ||||
|         lfs2_off_t startsizes[COUNT]; | ||||
|         lfs2_off_t startseeks[COUNT]; | ||||
|         lfs2_off_t hotsizes[COUNT]; | ||||
|         lfs2_off_t coldsizes[COUNT]; | ||||
|     } configs[] = { | ||||
|         // cold shrinking | ||||
|         {{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||
| @@ -307,88 +307,133 @@ code = ''' | ||||
|          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}}, | ||||
|     }; | ||||
|  | ||||
|     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; | ||||
|     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; | ||||
|  | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_format(&lfs2, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     for (unsigned i = 0; i < COUNT; i++) { | ||||
|         sprintf(path, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|         lfs2_file_open(&lfs2, &file, path, | ||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||
|  | ||||
|         strcpy((char*)buffer, "hair"); | ||||
|         size = strlen((char*)buffer); | ||||
|         for (lfs_off_t j = 0; j < startsizes[i]; j += size) { | ||||
|             lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         for (lfs2_off_t j = 0; j < startsizes[i]; j += size) { | ||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||
|         } | ||||
|         lfs_file_size(&lfs, &file) => startsizes[i]; | ||||
|         lfs2_file_size(&lfs2, &file) => startsizes[i]; | ||||
|  | ||||
|         if (startseeks[i] != startsizes[i]) { | ||||
|             lfs_file_seek(&lfs, &file, | ||||
|                     startseeks[i], LFS_SEEK_SET) => startseeks[i]; | ||||
|             lfs2_file_seek(&lfs2, &file, | ||||
|                     startseeks[i], LFS2_SEEK_SET) => startseeks[i]; | ||||
|         } | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file, hotsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file) => hotsizes[i]; | ||||
|         lfs2_file_truncate(&lfs2, &file, hotsizes[i]) => 0; | ||||
|         lfs2_file_size(&lfs2, &file) => hotsizes[i]; | ||||
|  | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     for (unsigned i = 0; i < COUNT; i++) { | ||||
|         sprintf(path, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDWR) => 0; | ||||
|         lfs_file_size(&lfs, &file) => hotsizes[i]; | ||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDWR) => 0; | ||||
|         lfs2_file_size(&lfs2, &file) => hotsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         lfs_off_t j = 0; | ||||
|         lfs2_off_t j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "hair", size) => 0; | ||||
|         } | ||||
|  | ||||
|         for (; j < hotsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file, coldsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file) => coldsizes[i]; | ||||
|         lfs2_file_truncate(&lfs2, &file, coldsizes[i]) => 0; | ||||
|         lfs2_file_size(&lfs2, &file) => coldsizes[i]; | ||||
|  | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs2_unmount(&lfs2) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs2_mount(&lfs2, &cfg) => 0; | ||||
|  | ||||
|     for (unsigned i = 0; i < COUNT; i++) { | ||||
|         sprintf(path, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_size(&lfs, &file) => coldsizes[i]; | ||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||
|         lfs2_file_size(&lfs2, &file) => coldsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         lfs_off_t j = 0; | ||||
|         lfs2_off_t j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; | ||||
|                 j += size) { | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "hair", size) => 0; | ||||
|         } | ||||
|  | ||||
|         for (; j < coldsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs2_file_close(&lfs2, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 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; | ||||
| ''' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user