mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-11-01 08:48:31 +01:00 
			
		
		
		
	Compare commits
	
		
			240 Commits
		
	
	
		
			v1.3
			...
			patch-rele
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c8be114e77 | ||
|  | db054684a6 | ||
|  | 7872918ec8 | ||
|  | e249854858 | ||
|  | 501b0240a9 | ||
|  | e1f3b90b56 | ||
|  | 74fe46de3d | ||
|  | 582b596ed1 | ||
|  | 0d4c0b105c | ||
|  | 4850e01e14 | ||
|  | 4ec4425272 | ||
|  | 31e28fddb7 | ||
|  | 3806d88285 | ||
|  | de5972699a | ||
|  | 0d8ffd6b86 | ||
|  | c0af471bc1 | ||
|  | e8c023aab0 | ||
|  | 38a2a8d2a3 | ||
|  | 51fabc672b | ||
|  | 19838371fb | ||
|  | 312326c4e4 | ||
|  | ef1c926940 | ||
|  | 72e3bb4448 | ||
|  | 649640c605 | ||
|  | eb013e6dd6 | ||
|  | 7e1bad3eee | ||
|  | 72a3758958 | ||
|  | df2e676562 | ||
|  | 53a6e04712 | ||
|  | 1aaf1cb6c0 | ||
|  | 52a90b8dcc | ||
|  | e279c8ff90 | ||
|  | 6a1ee91490 | ||
|  | 2e92f7a49b | ||
|  | 2588948d70 | ||
|  | abd90cb84c | ||
|  | b73ac594f2 | ||
|  | 614f7b1e68 | ||
|  | a9a61a3e78 | ||
|  | 36973d8fd5 | ||
|  | f06dc5737f | ||
|  | 3fb242f3ae | ||
|  | ef77195a64 | ||
|  | 12e464e9c3 | ||
|  | 9899c7fe48 | ||
|  | bc7bed740b | ||
|  | cf9afdddff | ||
|  | 2533a0f6d6 | ||
|  | 2a7f0ed11b | ||
|  | f35fb8c148 | ||
|  | 0a1f706ca2 | ||
|  | fdd239fe21 | ||
|  | 780ef2fce4 | ||
|  | 73ea008b74 | ||
|  | c849748453 | ||
|  | 25a843aab7 | ||
|  | 905727b684 | ||
|  | 0907ba7813 | ||
|  | 48bd2bff82 | ||
|  | 651e14e796 | ||
|  | 1ff6432298 | ||
|  | c2c2ce6b97 | ||
|  | 0b76635f10 | ||
|  | a32be1d875 | ||
|  | 7e110b44c0 | ||
|  | 7f7b7332e3 | ||
|  | 9568f8ee2d | ||
|  | bdff4bc59e | ||
|  | 26d25608b6 | ||
|  | 4ad09d6c4e | ||
|  | 7d8f8ced03 | ||
|  | a0644794ca | ||
|  | 512930c856 | ||
|  | 10dfc36f08 | ||
|  | 95c1a6339d | ||
|  | 173c212151 | ||
|  | d3a2cf48d4 | ||
|  | 22b0456623 | ||
|  | 8cca1b6a86 | ||
|  | 5fb8fa9f06 | ||
|  | 916b308558 | ||
|  | e1f9d2bc09 | ||
|  | 51b2c7e4b6 | ||
|  | 66d751544d | ||
|  | b989b4a89f | ||
|  | a548ce68c1 | ||
|  | dc507a7b5f | ||
|  | 5b26c68ae2 | ||
|  | 4a1b8ae222 | ||
|  | c8a39c4b23 | ||
|  | ec4d8b68ad | ||
|  | c7894a61e1 | ||
|  | 195075819e | ||
|  | 97d8d5e96a | ||
|  | 795dd8c7ab | ||
|  | 97a7191814 | ||
|  | aeca7667b3 | ||
|  | 7af8b81b81 | ||
|  | ad96fca18f | ||
|  | f010d2add1 | ||
|  | d7e4abad0b | ||
|  | cafe6ab466 | ||
|  | 29b881017d | ||
|  | cf87ba5375 | ||
|  | 7bacf9b1e0 | ||
|  | 5eeeb9d6ac | ||
|  | 617dd87621 | ||
|  | c67a41af7a | ||
|  | 6046d85e6e | ||
|  | 6db5202bdc | ||
|  | a43f9b3cd5 | ||
|  | 478dcdddef | ||
|  | 4db96d4d44 | ||
|  | a2532a34cd | ||
|  | d5e800575d | ||
|  | 21217d75ad | ||
|  | 38011f4cd0 | ||
|  | 126ef8b07f | ||
|  | e4a0d586d5 | ||
|  | 20b669a23d | ||
|  | 10f45ac02f | ||
|  | 3b3981eb74 | ||
|  | d8f930eeab | ||
|  | 7c70068b89 | ||
|  | c3e36bd2a7 | ||
|  | 6d0a6fc462 | ||
|  | 3186e89b14 | ||
|  | dbcbe4e088 | ||
|  | 213530c376 | ||
|  | a88230ae6a | ||
|  | f369f80540 | ||
|  | 1941bbda76 | ||
|  | 3cfa08602a | ||
|  | 97f35c3e05 | ||
|  | 35f68d28cc | ||
|  | bd1e0c4059 | ||
|  | 01d837e08d | ||
|  | 112fefc068 | ||
|  | 64df0a5e20 | ||
|  | 1a58ba799c | ||
|  | 105907ba66 | ||
|  | df1b607351 | ||
|  | 225706044e | ||
|  | 3e246da52c | ||
|  | 15d156082c | ||
|  | 3914cdf39f | ||
|  | 392b2ac79f | ||
|  | d9a24d0a2b | ||
|  | 5d24e656f1 | ||
|  | d3f3711560 | ||
|  | 5fc53bd726 | ||
|  | 2b35c36b67 | ||
|  | 7c88bc96b6 | ||
|  | e3b867897a | ||
|  | 67d9f88b0e | ||
|  | 7ad9700d9e | ||
|  | fe31f79b5f | ||
|  | fd121dc2e2 | ||
|  | b7bd34f461 | ||
|  | c1103efb53 | ||
|  | d7b0652936 | ||
|  | 2ff32d2dfb | ||
|  | b46fcac585 | ||
|  | cebf7aa0fe | ||
|  | 3ffcedb95b | ||
|  | e39f7e99d1 | ||
|  | 116c1e76de | ||
|  | f458da4b7c | ||
|  | eaa9220aad | ||
|  | 9278b17537 | ||
|  | 85a9638d9f | ||
|  | 483d41c545 | ||
|  | 11a3c8d062 | ||
|  | 0bdaeb7f8b | ||
|  | 0405ceb171 | ||
|  | a3c67d9697 | ||
|  | 0695862b38 | ||
|  | fe553e8af4 | ||
|  | 87f3e01a17 | ||
|  | 8070abec34 | ||
|  | 61f454b008 | ||
|  | ea4ded420c | ||
|  | 2a8277bd4d | ||
|  | 746b90965c | ||
|  | 93244a3734 | ||
|  | 636c0ed3d1 | ||
|  | 6c754c8023 | ||
|  | 6ffc8d3480 | ||
|  | 65ea6b3d0f | ||
|  | 6774276124 | ||
|  | 6362afa8d0 | ||
|  | 955545839b | ||
|  | ad74825bcf | ||
|  | d0e0453651 | ||
|  | 701e4fa438 | ||
|  | d8cadecba6 | ||
|  | 836e23895a | ||
|  | fb23044872 | ||
|  | 9273ac708b | ||
|  | 03b262b1e8 | ||
|  | 362b0bbe45 | ||
|  | e4a0cd942d | ||
|  | f30ab677a4 | ||
|  | ca3d6a52d2 | ||
|  | 692f0c542e | ||
|  | e3daee2621 | ||
|  | 73d29f05b2 | ||
|  | 4c35c8655a | ||
|  | 49698e431f | ||
|  | 0bb1f7af17 | ||
|  | 447d89cbd8 | ||
|  | 28d2d96a83 | ||
|  | cb62bf2188 | ||
|  | 646b1b5a6c | ||
|  | 1b7a15599e | ||
|  | e5a6938faf | ||
|  | 6ad544f3f3 | ||
|  | 3419284689 | ||
|  | 510cd13df9 | ||
|  | f5e0539951 | ||
|  | 066448055c | ||
|  | d66723ccfd | ||
|  | 0234c77102 | ||
|  | 84adead98b | ||
|  | 0422c55b81 | ||
|  | 11ad3a2414 | ||
|  | 16318d003f | ||
|  | 961fab70c3 | ||
|  | 041e90a1ca | ||
|  | f94d233deb | ||
|  | 577d777c20 | ||
|  | c72d25203c | ||
|  | 7e67f9324e | ||
|  | 5a17fa42e4 | ||
|  | eed1eec5fd | ||
|  | 4a86370327 | ||
|  | ba4f17173f | ||
|  | 51346b8bf4 | ||
|  | 93a2e0bbe5 | ||
|  | 6beff502e9 | 
							
								
								
									
										217
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -18,16 +18,29 @@ script: | ||||
|   - make test QUIET=1 | ||||
|  | ||||
|   # run tests with a few different configurations | ||||
|   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1      -DLFS_PROG_SIZE=1" | ||||
|   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512    -DLFS_PROG_SIZE=512" | ||||
|   - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048" | ||||
|   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1      -DLFS_CACHE_SIZE=4" | ||||
|   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512    -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16" | ||||
|   - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256" | ||||
|  | ||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0" | ||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS_EMUBD_ERASE_VALUE=0xff" | ||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" | ||||
|  | ||||
|   # additional configurations that don't support all tests (this should be | ||||
|   # fixed but at the moment it is what it is) | ||||
|   - make test_files QUIET=1 | ||||
|         CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096" | ||||
|   - make test_files QUIET=1 | ||||
|         CFLAGS+="-DLFS_READ_SIZE=\(2*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)" | ||||
|   - make test_files QUIET=1 | ||||
|         CFLAGS+="-DLFS_READ_SIZE=\(8*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)" | ||||
|   - make test_files QUIET=1 | ||||
|         CFLAGS+="-DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704" | ||||
|  | ||||
|   # compile and find the code size with the smallest configuration | ||||
|   - make clean size | ||||
|         OBJ="$(ls lfs*.o | tr '\n' ' ')" | ||||
|         CFLAGS+="-DLFS_NO{ASSERT,DEBUG,WARN,ERROR}" | ||||
|         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 | ||||
| @@ -35,7 +48,7 @@ script: | ||||
|     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 \ | ||||
|         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 == \"$STAGE/$NAME\").description | ||||
|                 | capture(\"code size is (?<size>[0-9]+)\").size" \ | ||||
| @@ -65,7 +78,10 @@ jobs: | ||||
|         - CC="arm-linux-gnueabi-gcc --static -mthumb" | ||||
|         - EXEC="qemu-arm" | ||||
|       install: | ||||
|         - sudo apt-get install gcc-arm-linux-gnueabi qemu-user | ||||
|         - sudo apt-get install | ||||
|               gcc-arm-linux-gnueabi | ||||
|               libc6-dev-armel-cross | ||||
|               qemu-user | ||||
|         - arm-linux-gnueabi-gcc --version | ||||
|         - qemu-arm -version | ||||
|  | ||||
| @@ -77,7 +93,10 @@ jobs: | ||||
|         - CC="powerpc-linux-gnu-gcc --static" | ||||
|         - EXEC="qemu-ppc" | ||||
|       install: | ||||
|         - sudo apt-get install gcc-powerpc-linux-gnu qemu-user | ||||
|         - sudo apt-get install | ||||
|               gcc-powerpc-linux-gnu | ||||
|               libc6-dev-powerpc-cross | ||||
|               qemu-user | ||||
|         - powerpc-linux-gnu-gcc --version | ||||
|         - qemu-ppc -version | ||||
|  | ||||
| @@ -89,9 +108,10 @@ jobs: | ||||
|         - CC="mips-linux-gnu-gcc --static" | ||||
|         - EXEC="qemu-mips" | ||||
|       install: | ||||
|         - sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main universe" | ||||
|         - sudo apt-get -qq update | ||||
|         - sudo apt-get install gcc-mips-linux-gnu qemu-user | ||||
|         - sudo apt-get install | ||||
|               gcc-mips-linux-gnu | ||||
|               libc6-dev-mips-cross | ||||
|               qemu-user | ||||
|         - mips-linux-gnu-gcc --version | ||||
|         - qemu-mips -version | ||||
|  | ||||
| @@ -100,9 +120,10 @@ jobs: | ||||
|       env: | ||||
|         - STAGE=test | ||||
|         - NAME=littlefs-fuse | ||||
|       if: branch !~ -prefix$ | ||||
|       install: | ||||
|         - sudo apt-get install libfuse-dev | ||||
|         - git clone --depth 1 https://github.com/geky/littlefs-fuse | ||||
|         - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 | ||||
|         - fusermount -V | ||||
|         - gcc --version | ||||
|       before_script: | ||||
| @@ -112,7 +133,7 @@ jobs: | ||||
|  | ||||
|         - mkdir mount | ||||
|         - sudo chmod a+rw /dev/loop0 | ||||
|         - dd if=/dev/zero bs=512 count=2048 of=disk | ||||
|         - dd if=/dev/zero bs=512 count=4096 of=disk | ||||
|         - losetup /dev/loop0 disk | ||||
|       script: | ||||
|         # self-host test | ||||
| @@ -125,68 +146,140 @@ jobs: | ||||
|         - mkdir mount/littlefs | ||||
|         - cp -r $(git ls-tree --name-only HEAD) mount/littlefs | ||||
|         - cd mount/littlefs | ||||
|         - ls | ||||
|         - stat . | ||||
|         - ls -flh | ||||
|         - make -B test_dirs test_files QUIET=1 | ||||
|  | ||||
|       # Automatically update releases | ||||
|     # self-host with littlefs-fuse for fuzz test | ||||
|     - stage: test | ||||
|       env: | ||||
|         - STAGE=test | ||||
|         - NAME=littlefs-migration | ||||
|       if: branch !~ -prefix$ | ||||
|       install: | ||||
|         - sudo apt-get install libfuse-dev | ||||
|         - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2 | ||||
|         - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1 | ||||
|         - fusermount -V | ||||
|         - gcc --version | ||||
|       before_script: | ||||
|         # setup disk for littlefs-fuse | ||||
|         - rm -rf v2/littlefs/* | ||||
|         - cp -r $(git ls-tree --name-only HEAD) v2/littlefs | ||||
|  | ||||
|         - mkdir mount | ||||
|         - sudo chmod a+rw /dev/loop0 | ||||
|         - dd if=/dev/zero bs=512 count=4096 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_dirs test_files QUIET=1 | ||||
|  | ||||
|         # 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_dirs test_files QUIET=1 | ||||
|  | ||||
|     # Automatically create releases | ||||
|     - stage: deploy | ||||
|       env: | ||||
|         - STAGE=deploy | ||||
|         - NAME=deploy | ||||
|       script: | ||||
|         # Update tag for 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))) | ||||
|         - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR" | ||||
|         - echo "littlefs version $LFS_VERSION" | ||||
|         - | | ||||
|           curl -u $GEKY_BOT_RELEASES -X POST \ | ||||
|             https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \ | ||||
|             -d "{ | ||||
|               \"ref\": \"refs/tags/$LFS_VERSION\", | ||||
|               \"sha\": \"$TRAVIS_COMMIT\" | ||||
|             }" | ||||
|         - | | ||||
|           curl -f -u $GEKY_BOT_RELEASES -X PATCH \ | ||||
|             https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/$LFS_VERSION \ | ||||
|             -d "{ | ||||
|               \"sha\": \"$TRAVIS_COMMIT\" | ||||
|             }" | ||||
|         # Create release notes from commits | ||||
|         - LFS_PREV_VERSION="v$LFS_VERSION_MAJOR.$(($LFS_VERSION_MINOR-1))" | ||||
|         - | | ||||
|           if [ $(git tag -l "$LFS_PREV_VERSION") ] | ||||
|           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 | ||||
|             curl -u $GEKY_BOT_RELEASES -X POST \ | ||||
|                 https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \ | ||||
|                 -d "{ | ||||
|                     \"tag_name\": \"$LFS_VERSION\", | ||||
|                     \"name\": \"$LFS_VERSION\" | ||||
|                 }" | ||||
|             RELEASE=$( | ||||
|                 curl -f -u $GEKY_BOT_RELEASES \ | ||||
|                     https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/tags/$LFS_VERSION | ||||
|             ) | ||||
|             CHANGES=$( | ||||
|                 git log --oneline $LFS_PREV_VERSION.. --grep='^Merge' --invert-grep | ||||
|             ) | ||||
|             curl -f -u $GEKY_BOT_RELEASES -X PATCH \ | ||||
|                 https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/$( | ||||
|                     jq -r '.id' <<< "$RELEASE" | ||||
|                 ) \ | ||||
|                 -d "$( | ||||
|                     jq -s '{ | ||||
|                         "body": ((.[0] // "" | sub("(?<=\n)#+ Changes.*"; ""; "mi")) | ||||
|                             + "### Changes\n\n" + .[1]) | ||||
|                     }' <(jq '.body' <<< "$RELEASE") <(jq -sR '.' <<< "$CHANGES") | ||||
|                 )" | ||||
|               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: | ||||
|   - | | ||||
|     curl -u $GEKY_BOT_STATUSES -X POST \ | ||||
|     curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||
|         https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||
|         -d "{ | ||||
|             \"context\": \"$STAGE/$NAME\", | ||||
| @@ -197,7 +290,7 @@ before_install: | ||||
|  | ||||
| after_failure: | ||||
|   - | | ||||
|     curl -u $GEKY_BOT_STATUSES -X POST \ | ||||
|     curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||
|         https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||
|         -d "{ | ||||
|             \"context\": \"$STAGE/$NAME\", | ||||
| @@ -208,7 +301,7 @@ after_failure: | ||||
|  | ||||
| after_success: | ||||
|   - | | ||||
|     curl -u $GEKY_BOT_STATUSES -X POST \ | ||||
|     curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||
|         https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||
|         -d "{ | ||||
|             \"context\": \"$STAGE/$NAME\", | ||||
|   | ||||
							
								
								
									
										183
									
								
								LICENSE.md
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								LICENSE.md
									
									
									
									
									
								
							| @@ -1,165 +1,24 @@ | ||||
| Apache License | ||||
| Version 2.0, January 2004 | ||||
| http://www.apache.org/licenses/ | ||||
| Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  | ||||
| TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
| Redistribution and use in source and binary forms, with or without modification, | ||||
| are permitted provided that the following conditions are met: | ||||
|  | ||||
| 1. Definitions. | ||||
| -  Redistributions of source code must retain the above copyright notice, this | ||||
|    list of conditions and the following disclaimer. | ||||
| -  Redistributions in binary form must reproduce the above copyright notice, this | ||||
|    list of conditions and the following disclaimer in the documentation and/or | ||||
|    other materials provided with the distribution. | ||||
| -  Neither the name of ARM nor the names of its contributors may be used to | ||||
|    endorse or promote products derived from this software without specific prior | ||||
|    written permission. | ||||
|  | ||||
| "License" shall mean the terms and conditions for use, reproduction, and | ||||
| distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
| "Licensor" shall mean the copyright owner or entity authorized by the copyright | ||||
| owner that is granting the License. | ||||
|  | ||||
| "Legal Entity" shall mean the union of the acting entity and all other entities | ||||
| that control, are controlled by, or are under common control with that entity. | ||||
| For the purposes of this definition, "control" means (i) the power, direct or | ||||
| indirect, to cause the direction or management of such entity, whether by | ||||
| contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
| outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
| "You" (or "Your") shall mean an individual or Legal Entity exercising | ||||
| permissions granted by this License. | ||||
|  | ||||
| "Source" form shall mean the preferred form for making modifications, including | ||||
| but not limited to software source code, documentation source, and configuration | ||||
| files. | ||||
|  | ||||
| "Object" form shall mean any form resulting from mechanical transformation or | ||||
| translation of a Source form, including but not limited to compiled object code, | ||||
| generated documentation, and conversions to other media types. | ||||
|  | ||||
| "Work" shall mean the work of authorship, whether in Source or Object form, made | ||||
| available under the License, as indicated by a copyright notice that is included | ||||
| in or attached to the work (an example is provided in the Appendix below). | ||||
|  | ||||
| "Derivative Works" shall mean any work, whether in Source or Object form, that | ||||
| is based on (or derived from) the Work and for which the editorial revisions, | ||||
| annotations, elaborations, or other modifications represent, as a whole, an | ||||
| original work of authorship. For the purposes of this License, Derivative Works | ||||
| shall not include works that remain separable from, or merely link (or bind by | ||||
| name) to the interfaces of, the Work and Derivative Works thereof. | ||||
|  | ||||
| "Contribution" shall mean any work of authorship, including the original version | ||||
| of the Work and any modifications or additions to that Work or Derivative Works | ||||
| thereof, that is intentionally submitted to Licensor for inclusion in the Work | ||||
| by the copyright owner or by an individual or Legal Entity authorized to submit | ||||
| on behalf of the copyright owner. For the purposes of this definition, | ||||
| "submitted" means any form of electronic, verbal, or written communication sent | ||||
| to the Licensor or its representatives, including but not limited to | ||||
| communication on electronic mailing lists, source code control systems, and | ||||
| issue tracking systems that are managed by, or on behalf of, the Licensor for | ||||
| the purpose of discussing and improving the Work, but excluding communication | ||||
| that is conspicuously marked or otherwise designated in writing by the copyright | ||||
| owner as "Not a Contribution." | ||||
|  | ||||
| "Contributor" shall mean Licensor and any individual or Legal Entity on behalf | ||||
| of whom a Contribution has been received by Licensor and subsequently | ||||
| incorporated within the Work. | ||||
|  | ||||
| 2. Grant of Copyright License. | ||||
|  | ||||
| Subject to the terms and conditions of this License, each Contributor hereby | ||||
| grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||||
| irrevocable copyright license to reproduce, prepare Derivative Works of, | ||||
| publicly display, publicly perform, sublicense, and distribute the Work and such | ||||
| Derivative Works in Source or Object form. | ||||
|  | ||||
| 3. Grant of Patent License. | ||||
|  | ||||
| Subject to the terms and conditions of this License, each Contributor hereby | ||||
| grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||||
| irrevocable (except as stated in this section) patent license to make, have | ||||
| made, use, offer to sell, sell, import, and otherwise transfer the Work, where | ||||
| such license applies only to those patent claims licensable by such Contributor | ||||
| that are necessarily infringed by their Contribution(s) alone or by combination | ||||
| of their Contribution(s) with the Work to which such Contribution(s) was | ||||
| submitted. If You institute patent litigation against any entity (including a | ||||
| cross-claim or counterclaim in a lawsuit) alleging that the Work or a | ||||
| Contribution incorporated within the Work constitutes direct or contributory | ||||
| patent infringement, then any patent licenses granted to You under this License | ||||
| for that Work shall terminate as of the date such litigation is filed. | ||||
|  | ||||
| 4. Redistribution. | ||||
|  | ||||
| You may reproduce and distribute copies of the Work or Derivative Works thereof | ||||
| in any medium, with or without modifications, and in Source or Object form, | ||||
| provided that You meet the following conditions: | ||||
|  | ||||
| You must give any other recipients of the Work or Derivative Works a copy of | ||||
| this License; and | ||||
| You must cause any modified files to carry prominent notices stating that You | ||||
| changed the files; and | ||||
| You must retain, in the Source form of any Derivative Works that You distribute, | ||||
| all copyright, patent, trademark, and attribution notices from the Source form | ||||
| of the Work, excluding those notices that do not pertain to any part of the | ||||
| Derivative Works; and | ||||
| If the Work includes a "NOTICE" text file as part of its distribution, then any | ||||
| Derivative Works that You distribute must include a readable copy of the | ||||
| attribution notices contained within such NOTICE file, excluding those notices | ||||
| that do not pertain to any part of the Derivative Works, in at least one of the | ||||
| following places: within a NOTICE text file distributed as part of the | ||||
| Derivative Works; within the Source form or documentation, if provided along | ||||
| with the Derivative Works; or, within a display generated by the Derivative | ||||
| Works, if and wherever such third-party notices normally appear. The contents of | ||||
| the NOTICE file are for informational purposes only and do not modify the | ||||
| License. You may add Your own attribution notices within Derivative Works that | ||||
| You distribute, alongside or as an addendum to the NOTICE text from the Work, | ||||
| provided that such additional attribution notices cannot be construed as | ||||
| modifying the License. | ||||
| You may add Your own copyright statement to Your modifications and may provide | ||||
| additional or different license terms and conditions for use, reproduction, or | ||||
| distribution of Your modifications, or for any such Derivative Works as a whole, | ||||
| provided Your use, reproduction, and distribution of the Work otherwise complies | ||||
| with the conditions stated in this License. | ||||
|  | ||||
| 5. Submission of Contributions. | ||||
|  | ||||
| Unless You explicitly state otherwise, any Contribution intentionally submitted | ||||
| for inclusion in the Work by You to the Licensor shall be under the terms and | ||||
| conditions of this License, without any additional terms or conditions. | ||||
| Notwithstanding the above, nothing herein shall supersede or modify the terms of | ||||
| any separate license agreement you may have executed with Licensor regarding | ||||
| such Contributions. | ||||
|  | ||||
| 6. Trademarks. | ||||
|  | ||||
| This License does not grant permission to use the trade names, trademarks, | ||||
| service marks, or product names of the Licensor, except as required for | ||||
| reasonable and customary use in describing the origin of the Work and | ||||
| reproducing the content of the NOTICE file. | ||||
|  | ||||
| 7. Disclaimer of Warranty. | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, Licensor provides the | ||||
| Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, | ||||
| including, without limitation, any warranties or conditions of TITLE, | ||||
| NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are | ||||
| solely responsible for determining the appropriateness of using or | ||||
| redistributing the Work and assume any risks associated with Your exercise of | ||||
| permissions under this License. | ||||
|  | ||||
| 8. Limitation of Liability. | ||||
|  | ||||
| In no event and under no legal theory, whether in tort (including negligence), | ||||
| contract, or otherwise, unless required by applicable law (such as deliberate | ||||
| and grossly negligent acts) or agreed to in writing, shall any Contributor be | ||||
| liable to You for damages, including any direct, indirect, special, incidental, | ||||
| or consequential damages of any character arising as a result of this License or | ||||
| out of the use or inability to use the Work (including but not limited to | ||||
| damages for loss of goodwill, work stoppage, computer failure or malfunction, or | ||||
| any and all other commercial damages or losses), even if such Contributor has | ||||
| been advised of the possibility of such damages. | ||||
|  | ||||
| 9. Accepting Warranty or Additional Liability. | ||||
|  | ||||
| While redistributing the Work or Derivative Works thereof, You may choose to | ||||
| offer, and charge a fee for, acceptance of support, warranty, indemnity, or | ||||
| other liability obligations and/or rights consistent with this License. However, | ||||
| in accepting such obligations, You may act only on Your own behalf and on Your | ||||
| sole responsibility, not on behalf of any other Contributor, and only if You | ||||
| agree to indemnify, defend, and hold each Contributor harmless for any liability | ||||
| incurred by, or claims asserted against, such Contributor by reason of your | ||||
| accepting any such warranty or additional liability. | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||||
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|   | ||||
							
								
								
									
										33
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,4 +1,7 @@ | ||||
| TARGET = lfs | ||||
| TARGET = lfs.a | ||||
| ifneq ($(wildcard test.c main.c),) | ||||
| override TARGET = lfs | ||||
| endif | ||||
|  | ||||
| CC ?= gcc | ||||
| AR ?= ar | ||||
| @@ -21,8 +24,14 @@ endif | ||||
| ifdef WORD | ||||
| override CFLAGS += -m$(WORD) | ||||
| endif | ||||
| ifdef TRACE | ||||
| override CFLAGS += -DLFS_YES_TRACE | ||||
| endif | ||||
| override CFLAGS += -I. | ||||
| override CFLAGS += -std=c99 -Wall -pedantic | ||||
| override CFLAGS += -Wextra -Wshadow -Wjump-misses-init | ||||
| # Remove missing-field-initializers because of GCC bug | ||||
| override CFLAGS += -Wno-missing-field-initializers | ||||
|  | ||||
|  | ||||
| all: $(TARGET) | ||||
| @@ -33,18 +42,32 @@ size: $(OBJ) | ||||
| 	$(SIZE) -t $^ | ||||
|  | ||||
| .SUFFIXES: | ||||
| test: test_format test_dirs test_files test_seek test_truncate \ | ||||
| 	test_interspersed test_alloc test_paths test_orphan test_move test_corrupt | ||||
| test: \ | ||||
| 	test_format \ | ||||
| 	test_dirs \ | ||||
| 	test_files \ | ||||
| 	test_seek \ | ||||
| 	test_truncate \ | ||||
| 	test_entries \ | ||||
| 	test_interspersed \ | ||||
| 	test_alloc \ | ||||
| 	test_paths \ | ||||
| 	test_attrs \ | ||||
| 	test_move \ | ||||
| 	test_orphan \ | ||||
| 	test_corrupt | ||||
| 	@rm test.c | ||||
| test_%: tests/test_%.sh | ||||
|  | ||||
| ifdef QUIET | ||||
| 	@./$< | sed -n '/^[-=]/p' | ||||
| 	@./$< | sed -nu '/^[-=]/p' | ||||
| else | ||||
| 	./$< | ||||
| endif | ||||
|  | ||||
| -include $(DEP) | ||||
|  | ||||
| $(TARGET): $(OBJ) | ||||
| lfs: $(OBJ) | ||||
| 	$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ | ||||
|  | ||||
| %.a: $(OBJ) | ||||
|   | ||||
							
								
								
									
										164
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| ## The little filesystem | ||||
| ## littlefs | ||||
|  | ||||
| A little fail-safe filesystem designed for embedded systems. | ||||
| A little fail-safe filesystem designed for microcontrollers. | ||||
|  | ||||
| ``` | ||||
|    | | |     .---._____ | ||||
| @@ -11,17 +11,19 @@ A little fail-safe filesystem designed for embedded systems. | ||||
|    | | | | ||||
| ``` | ||||
|  | ||||
| **Bounded RAM/ROM** - The littlefs is designed to work with a limited amount | ||||
| of memory. Recursion is avoided and dynamic memory is limited to configurable | ||||
| buffers that can be provided statically. | ||||
| **Power-loss resilience** - littlefs is designed to handle random power | ||||
| failures. All file operations have strong copy-on-write guarantees and if | ||||
| power is lost the filesystem will fall back to the last known good state. | ||||
|  | ||||
| **Power-loss resilient** - The littlefs is designed for systems that may have | ||||
| random power failures. The littlefs has strong copy-on-write guarantees and | ||||
| storage on disk is always kept in a valid state. | ||||
| **Dynamic wear leveling** - littlefs is designed with flash in mind, and | ||||
| provides wear leveling over dynamic blocks. Additionally, littlefs can | ||||
| detect bad blocks and work around them. | ||||
|  | ||||
| **Wear leveling** - Since the most common form of embedded storage is erodible | ||||
| flash memories, littlefs provides a form of dynamic wear leveling for systems | ||||
| that can not fit a full flash translation layer. | ||||
| **Bounded RAM/ROM** - littlefs is designed to work with a small amount of | ||||
| memory. RAM usage is strictly bounded, which means RAM consumption does not | ||||
| change as the filesystem grows. The filesystem contains no unbounded | ||||
| recursion and dynamic memory is limited to configurable buffers that can be | ||||
| provided statically. | ||||
|  | ||||
| ## Example | ||||
|  | ||||
| @@ -49,7 +51,8 @@ const struct lfs_config cfg = { | ||||
|     .prog_size = 16, | ||||
|     .block_size = 4096, | ||||
|     .block_count = 128, | ||||
|     .lookahead = 128, | ||||
|     .cache_size = 16, | ||||
|     .lookahead_size = 16, | ||||
| }; | ||||
|  | ||||
| // entry point | ||||
| @@ -90,11 +93,11 @@ int main(void) { | ||||
| Detailed documentation (or at least as much detail as is currently available) | ||||
| can be found in the comments in [lfs.h](lfs.h). | ||||
|  | ||||
| As you may have noticed, littlefs takes in a configuration structure that | ||||
| defines how the filesystem operates. The configuration struct provides the | ||||
| filesystem with the block device operations and dimensions, tweakable | ||||
| parameters that tradeoff memory usage for performance, and optional | ||||
| static buffers if the user wants to avoid dynamic memory. | ||||
| littlefs takes in a configuration structure that defines how the filesystem | ||||
| operates. The configuration struct provides the filesystem with the block | ||||
| 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 | ||||
| to the user to allocate, allowing multiple filesystems to be in use | ||||
| @@ -106,14 +109,14 @@ directory functions, with the deviation that the allocation of filesystem | ||||
| structures must be provided by the user. | ||||
|  | ||||
| All POSIX operations, such as remove and rename, are atomic, even in event | ||||
| of power-loss. Additionally, no file updates are actually committed to the | ||||
| filesystem until sync or close is called on the file. | ||||
| of power-loss. Additionally, no file updates are not actually committed to | ||||
| the filesystem until sync or close is called on the file. | ||||
|  | ||||
| ## Other notes | ||||
|  | ||||
| All littlefs 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. | ||||
| 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. | ||||
|  | ||||
| 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 | ||||
| @@ -127,14 +130,60 @@ from memory, otherwise data integrity can not be guaranteed. If the `write` | ||||
| function does not perform caching, and therefore each `read` or `write` call | ||||
| hits the memory, the `sync` function can simply return 0. | ||||
|  | ||||
| ## Reference material | ||||
| ## Design | ||||
|  | ||||
| [DESIGN.md](DESIGN.md) - DESIGN.md contains a fully detailed dive into how | ||||
| littlefs actually works. I would encourage you to read it since the | ||||
| solutions and tradeoffs at work here are quite interesting. | ||||
| At a high level, littlefs is a block based filesystem that uses small logs to | ||||
| store metadata and larger copy-on-write (COW) structures to store file data. | ||||
|  | ||||
| [SPEC.md](SPEC.md) - SPEC.md contains the on-disk specification of littlefs | ||||
| with all the nitty-gritty details. Can be useful for developing tooling. | ||||
| In littlefs, these ingredients form a sort of two-layered cake, with the small | ||||
| logs (called metadata pairs) providing fast updates to metadata anywhere on | ||||
| storage, while the COW structures store file data compactly and without any | ||||
| wear amplification cost. | ||||
|  | ||||
| Both of these data structures are built out of blocks, which are fed by a | ||||
| common block allocator. By limiting the number of erases allowed on a block | ||||
| per allocation, the allocator provides dynamic wear leveling over the entire | ||||
| filesystem. | ||||
|  | ||||
| ``` | ||||
|                     root | ||||
|                    .--------.--------. | ||||
|                    | A'| B'|         | | ||||
|                    |   |   |->       | | ||||
|                    |   |   |         | | ||||
|                    '--------'--------' | ||||
|                 .----'   '--------------. | ||||
|        A       v                 B       v | ||||
|       .--------.--------.       .--------.--------. | ||||
|       | C'| D'|         |       | E'|new|         | | ||||
|       |   |   |->       |       |   | E'|->       | | ||||
|       |   |   |         |       |   |   |         | | ||||
|       '--------'--------'       '--------'--------' | ||||
|       .-'   '--.                  |   '------------------. | ||||
|      v          v              .-'                        v | ||||
| .--------.  .--------.        v                       .--------. | ||||
| |   C    |  |   D    |   .--------.       write       | new E  | | ||||
| |        |  |        |   |   E    |        ==>        |        | | ||||
| |        |  |        |   |        |                   |        | | ||||
| '--------'  '--------'   |        |                   '--------' | ||||
|                          '--------'                   .-'    | | ||||
|                          .-'    '-.    .-------------|------' | ||||
|                         v          v  v              v | ||||
|                    .--------.  .--------.       .--------. | ||||
|                    |   F    |  |   G    |       | new F  | | ||||
|                    |        |  |        |       |        | | ||||
|                    |        |  |        |       |        | | ||||
|                    '--------'  '--------'       '--------' | ||||
| ``` | ||||
|  | ||||
| More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and | ||||
| [SPEC.md](SPEC.md). | ||||
|  | ||||
| - [DESIGN.md](DESIGN.md) - A fully detailed dive into how littlefs works. | ||||
|   I would suggest reading it as the tradeoffs at work are quite interesting. | ||||
|  | ||||
| - [SPEC.md](SPEC.md) - The on-disk specification of littlefs with all the | ||||
|   nitty-gritty details. May be useful for tooling development. | ||||
|  | ||||
| ## Testing | ||||
|  | ||||
| @@ -146,19 +195,54 @@ The tests assume a Linux environment and can be started with make: | ||||
| make test | ||||
| ``` | ||||
|  | ||||
| ## License | ||||
|  | ||||
| The littlefs is provided under the [BSD-3-Clause] license. See | ||||
| [LICENSE.md](LICENSE.md) for more information. Contributions to this project | ||||
| are accepted under the same license. | ||||
|  | ||||
| Individual files contain the following tag instead of the full license text. | ||||
|  | ||||
|     SPDX-License-Identifier:    BSD-3-Clause | ||||
|  | ||||
| This enables machine processing of license information based on the SPDX | ||||
| License Identifiers that are here available: http://spdx.org/licenses/ | ||||
|  | ||||
| ## Related projects | ||||
|  | ||||
| [Mbed OS](https://github.com/ARMmbed/mbed-os/tree/master/features/filesystem/littlefs) - | ||||
| The easiest way to get started with littlefs is to jump into [Mbed](https://os.mbed.com/), | ||||
| which already has block device drivers for most forms of embedded storage. The | ||||
| littlefs is available in Mbed OS as the [LittleFileSystem](https://os.mbed.com/docs/latest/reference/littlefilesystem.html) | ||||
| class. | ||||
| - [littlefs-fuse] - A [FUSE] wrapper for littlefs. The project allows you to | ||||
|   mount littlefs directly on a Linux machine. Can be useful for debugging | ||||
|   littlefs if you have an SD card handy. | ||||
|  | ||||
| [littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse) | ||||
| wrapper for littlefs. The project allows you to mount littlefs directly on a | ||||
| Linux machine. Can be useful for debugging littlefs if you have an SD card | ||||
| handy. | ||||
| - [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-js](https://github.com/geky/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](http://littlefs.geky.net/demo.html). | ||||
| - [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. | ||||
|  | ||||
| - [Mbed OS] - The easiest way to get started with littlefs is to jump into Mbed | ||||
|   which already has block device drivers for most forms of embedded storage. | ||||
|   littlefs is available in Mbed OS as the [LittleFileSystem] class. | ||||
|  | ||||
| - [SPIFFS] - Another excellent embedded filesystem for NOR flash. As a more | ||||
|   traditional logging filesystem with full static wear-leveling, SPIFFS will | ||||
|   likely outperform littlefs on small memories such as the internal flash on | ||||
|   microcontrollers. | ||||
|  | ||||
| - [Dhara] - An interesting NAND flash translation layer designed for small | ||||
|   MCUs. It offers static wear-leveling and power-resilience with only a fixed | ||||
|   _O(|address|)_ pointer structure stored on each block and in RAM. | ||||
|  | ||||
|  | ||||
| [BSD-3-Clause]: https://spdx.org/licenses/BSD-3-Clause.html | ||||
| [littlefs-fuse]: https://github.com/geky/littlefs-fuse | ||||
| [FUSE]: https://github.com/libfuse/libfuse | ||||
| [littlefs-js]: https://github.com/geky/littlefs-js | ||||
| [littlefs-js-demo]:http://littlefs.geky.net/demo.html | ||||
| [mklfs]: https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src | ||||
| [Lua RTOS]: https://github.com/whitecatboard/Lua-RTOS-ESP32 | ||||
| [Mbed OS]: https://github.com/armmbed/mbed-os | ||||
| [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 | ||||
|   | ||||
| @@ -1,19 +1,8 @@ | ||||
| /* | ||||
|  * Block device emulated on standard files | ||||
|  * | ||||
|  * Copyright (c) 2017 ARM Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #include "emubd/lfs_emubd.h" | ||||
|  | ||||
| @@ -22,15 +11,58 @@ | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <limits.h> | ||||
| #include <dirent.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
| #include <assert.h> | ||||
| #include <stdbool.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
|  | ||||
| // Emulated block device utils | ||||
| static inline void lfs_emubd_tole32(lfs_emubd_t *emu) { | ||||
|     emu->cfg.read_size     = lfs_tole32(emu->cfg.read_size); | ||||
|     emu->cfg.prog_size     = lfs_tole32(emu->cfg.prog_size); | ||||
|     emu->cfg.block_size    = lfs_tole32(emu->cfg.block_size); | ||||
|     emu->cfg.block_count   = lfs_tole32(emu->cfg.block_count); | ||||
|  | ||||
|     emu->stats.read_count  = lfs_tole32(emu->stats.read_count); | ||||
|     emu->stats.prog_count  = lfs_tole32(emu->stats.prog_count); | ||||
|     emu->stats.erase_count = lfs_tole32(emu->stats.erase_count); | ||||
|  | ||||
|     for (unsigned i = 0; i < sizeof(emu->history.blocks) / | ||||
|             sizeof(emu->history.blocks[0]); i++) { | ||||
|         emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) { | ||||
|     emu->cfg.read_size     = lfs_fromle32(emu->cfg.read_size); | ||||
|     emu->cfg.prog_size     = lfs_fromle32(emu->cfg.prog_size); | ||||
|     emu->cfg.block_size    = lfs_fromle32(emu->cfg.block_size); | ||||
|     emu->cfg.block_count   = lfs_fromle32(emu->cfg.block_count); | ||||
|  | ||||
|     emu->stats.read_count  = lfs_fromle32(emu->stats.read_count); | ||||
|     emu->stats.prog_count  = lfs_fromle32(emu->stats.prog_count); | ||||
|     emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count); | ||||
|  | ||||
|     for (unsigned i = 0; i < sizeof(emu->history.blocks) / | ||||
|             sizeof(emu->history.blocks[0]); i++) { | ||||
|         emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| // Block device emulated on existing filesystem | ||||
| int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     LFS_TRACE("lfs_emubd_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); | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|     emu->cfg.read_size   = cfg->read_size; | ||||
|     emu->cfg.prog_size   = cfg->prog_size; | ||||
| @@ -41,7 +73,9 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     size_t pathlen = strlen(path); | ||||
|     emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1); | ||||
|     if (!emu->path) { | ||||
|         return -ENOMEM; | ||||
|         int err = -ENOMEM; | ||||
|         LFS_TRACE("lfs_emubd_create -> %"PRId32, err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     strcpy(emu->path, path); | ||||
| @@ -52,38 +86,72 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     // Create directory if it doesn't exist | ||||
|     int err = mkdir(path, 0777); | ||||
|     if (err && errno != EEXIST) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_create -> %"PRId32, err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // Load stats to continue incrementing | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".stats"); | ||||
|     FILE *f = fopen(emu->path, "r"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|         memset(&emu->stats, LFS_EMUBD_ERASE_VALUE, sizeof(emu->stats)); | ||||
|     } else { | ||||
|         size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|         lfs_emubd_fromle32(emu); | ||||
|         if (res < 1) { | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_create -> %"PRId32, err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_create -> %"PRId32, err); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|     // Load history | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".history"); | ||||
|     f = fopen(emu->path, "r"); | ||||
|     if (!f) { | ||||
|         memset(&emu->history, 0, sizeof(emu->history)); | ||||
|     } else { | ||||
|         size_t res = fread(&emu->history, sizeof(emu->history), 1, f); | ||||
|         lfs_emubd_fromle32(emu); | ||||
|         if (res < 1) { | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_create -> %"PRId32, err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_create -> %"PRId32, err); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_emubd_create -> %"PRId32, 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void lfs_emubd_destroy(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_emubd_destroy(%p)", (void*)cfg); | ||||
|     lfs_emubd_sync(cfg); | ||||
|  | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|     free(emu->path); | ||||
|     LFS_TRACE("lfs_emubd_destroy -> %s", "void"); | ||||
| } | ||||
|  | ||||
| int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, void *buffer, lfs_size_t size) { | ||||
|     LFS_TRACE("lfs_emubd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|     uint8_t *data = buffer; | ||||
|  | ||||
| @@ -96,36 +164,47 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     memset(data, 0, size); | ||||
|  | ||||
|     // Read data | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "%x", block); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); | ||||
|  | ||||
|     FILE *f = fopen(emu->path, "rb"); | ||||
|     if (!f && errno != ENOENT) { | ||||
|         return -errno; | ||||
|         int err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_read -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     if (f) { | ||||
|         int err = fseek(f, off, SEEK_SET); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_read -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         size_t res = fread(data, 1, size, f); | ||||
|         if (res < size && !feof(f)) { | ||||
|             return -errno; | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_read -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_read -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     emu->stats.read_count += 1; | ||||
|     emu->stats.read_count += size; | ||||
|     LFS_TRACE("lfs_emubd_read -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     LFS_TRACE("lfs_emubd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||
|             (void*)cfg, block, off, buffer, size); | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|     const uint8_t *data = buffer; | ||||
|  | ||||
| @@ -135,11 +214,13 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|     assert(block < cfg->block_count); | ||||
|  | ||||
|     // Program data | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "%x", block); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); | ||||
|  | ||||
|     FILE *f = fopen(emu->path, "r+b"); | ||||
|     if (!f) { | ||||
|         return (errno == EACCES) ? 0 : -errno; | ||||
|         int err = (errno == EACCES) ? 0 : -errno; | ||||
|         LFS_TRACE("lfs_emubd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // Check that file was erased | ||||
| @@ -147,107 +228,176 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|  | ||||
|     int err = fseek(f, off, SEEK_SET); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     size_t res = fwrite(data, 1, size, f); | ||||
|     if (res < size) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = fseek(f, off, SEEK_SET); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     uint8_t dat; | ||||
|     res = fread(&dat, 1, 1, f); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_prog -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     emu->stats.prog_count += 1; | ||||
|     // update history and stats | ||||
|     if (block != emu->history.blocks[0]) { | ||||
|         memmove(&emu->history.blocks[1], &emu->history.blocks[0], | ||||
|                 sizeof(emu->history) - sizeof(emu->history.blocks[0])); | ||||
|         emu->history.blocks[0] = block; | ||||
|     } | ||||
|  | ||||
|     emu->stats.prog_count += size; | ||||
|     LFS_TRACE("lfs_emubd_prog -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { | ||||
|     LFS_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|  | ||||
|     // Check if erase is valid | ||||
|     assert(block < cfg->block_count); | ||||
|  | ||||
|     // Erase the block | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "%x", block); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block); | ||||
|     struct stat st; | ||||
|     int err = stat(emu->path, &st); | ||||
|     if (err && errno != ENOENT) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_erase -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) { | ||||
|         err = unlink(emu->path); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_erase -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (err || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) { | ||||
|         FILE *f = fopen(emu->path, "w"); | ||||
|         if (!f) { | ||||
|             return -errno; | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_erase -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|             err = -errno; | ||||
|             LFS_TRACE("lfs_emubd_erase -> %d", err); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     emu->stats.erase_count += 1; | ||||
|     emu->stats.erase_count += cfg->block_size; | ||||
|     LFS_TRACE("lfs_emubd_erase -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_emubd_sync(const struct lfs_config *cfg) { | ||||
|     LFS_TRACE("lfs_emubd_sync(%p)", (void*)cfg); | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|  | ||||
|     // Just write out info/stats for later lookup | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "config"); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".config"); | ||||
|     FILE *f = fopen(emu->path, "w"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|         int err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); | ||||
|     lfs_emubd_fromle32(emu); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|         int err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     int err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".stats"); | ||||
|     f = fopen(emu->path, "w"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|     lfs_emubd_fromle32(emu); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".history"); | ||||
|     f = fopen(emu->path, "w"); | ||||
|     if (!f) { | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     res = fwrite(&emu->history, sizeof(emu->history), 1, f); | ||||
|     lfs_emubd_fromle32(emu); | ||||
|     if (res < 1) { | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         err = -errno; | ||||
|         LFS_TRACE("lfs_emubd_sync -> %d", err); | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     LFS_TRACE("lfs_emubd_sync -> %d", 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,8 @@ | ||||
| /* | ||||
|  * Block device emulated on standard files | ||||
|  * | ||||
|  * Copyright (c) 2017 ARM Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #ifndef LFS_EMUBD_H | ||||
| #define LFS_EMUBD_H | ||||
| @@ -21,22 +10,15 @@ | ||||
| #include "lfs.h" | ||||
| #include "lfs_util.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // Config options | ||||
| #ifndef LFS_EMUBD_READ_SIZE | ||||
| #define LFS_EMUBD_READ_SIZE 1 | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_EMUBD_PROG_SIZE | ||||
| #define LFS_EMUBD_PROG_SIZE 1 | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_EMUBD_ERASE_SIZE | ||||
| #define LFS_EMUBD_ERASE_SIZE 512 | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_EMUBD_TOTAL_SIZE | ||||
| #define LFS_EMUBD_TOTAL_SIZE 524288 | ||||
| #ifndef LFS_EMUBD_ERASE_VALUE | ||||
| #define LFS_EMUBD_ERASE_VALUE 0x00 | ||||
| #endif | ||||
|  | ||||
|  | ||||
| @@ -51,6 +33,10 @@ typedef struct lfs_emubd { | ||||
|         uint64_t erase_count; | ||||
|     } stats; | ||||
|  | ||||
|     struct { | ||||
|         lfs_block_t blocks[4]; | ||||
|     } history; | ||||
|  | ||||
|     struct { | ||||
|         uint32_t read_size; | ||||
|         uint32_t prog_size; | ||||
| @@ -86,4 +72,8 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block); | ||||
| int lfs_emubd_sync(const struct lfs_config *cfg); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										473
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										473
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -1,19 +1,8 @@ | ||||
| /* | ||||
|  * The little filesystem | ||||
|  * | ||||
|  * Copyright (c) 2017 ARM Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #ifndef LFS_H | ||||
| #define LFS_H | ||||
| @@ -21,20 +10,25 @@ | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| /// Version info /// | ||||
|  | ||||
| // Software library version | ||||
| // Major (top-nibble), incremented on backwards incompatible changes | ||||
| // Minor (bottom-nibble), incremented on feature additions | ||||
| #define LFS_VERSION 0x00010003 | ||||
| #define LFS_VERSION 0x00020001 | ||||
| #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) | ||||
| #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >>  0)) | ||||
|  | ||||
| // Version of On-disk data structures | ||||
| // Major (top-nibble), incremented on backwards incompatible changes | ||||
| // Minor (bottom-nibble), incremented on feature additions | ||||
| #define LFS_DISK_VERSION 0x00010001 | ||||
| #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)) | ||||
|  | ||||
| @@ -50,51 +44,99 @@ typedef int32_t  lfs_soff_t; | ||||
|  | ||||
| typedef uint32_t lfs_block_t; | ||||
|  | ||||
| // Max name size in bytes | ||||
| // Maximum name size in bytes, may be redefined to reduce the size of the | ||||
| // info struct. Limited to <= 1022. Stored in superblock and must be | ||||
| // respected by other littlefs drivers. | ||||
| #ifndef LFS_NAME_MAX | ||||
| #define LFS_NAME_MAX 255 | ||||
| #endif | ||||
|  | ||||
| // Maximum size of a file in bytes, may be redefined to limit to support other | ||||
| // drivers. Limited on disk to <= 4294967296. However, above 2147483647 the | ||||
| // functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return | ||||
| // incorrect values due to using signed integers. Stored in superblock and | ||||
| // must be respected by other littlefs drivers. | ||||
| #ifndef LFS_FILE_MAX | ||||
| #define LFS_FILE_MAX 2147483647 | ||||
| #endif | ||||
|  | ||||
| // Maximum size of custom attributes in bytes, may be redefined, but there is | ||||
| // no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. | ||||
| #ifndef LFS_ATTR_MAX | ||||
| #define LFS_ATTR_MAX 1022 | ||||
| #endif | ||||
|  | ||||
| // 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  = -52,  // 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_INVAL    = -22,  // Invalid parameter | ||||
|     LFS_ERR_NOSPC    = -28,  // No space left on device | ||||
|     LFS_ERR_NOMEM    = -12,  // No more memory available | ||||
|     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 | ||||
| }; | ||||
|  | ||||
| // File types | ||||
| enum lfs_type { | ||||
|     LFS_TYPE_REG        = 0x11, | ||||
|     LFS_TYPE_DIR        = 0x22, | ||||
|     LFS_TYPE_SUPERBLOCK = 0x2e, | ||||
|     // file types | ||||
|     LFS_TYPE_REG            = 0x001, | ||||
|     LFS_TYPE_DIR            = 0x002, | ||||
|  | ||||
|     // internally used types | ||||
|     LFS_TYPE_SPLICE         = 0x400, | ||||
|     LFS_TYPE_NAME           = 0x000, | ||||
|     LFS_TYPE_STRUCT         = 0x200, | ||||
|     LFS_TYPE_USERATTR       = 0x300, | ||||
|     LFS_TYPE_FROM           = 0x100, | ||||
|     LFS_TYPE_TAIL           = 0x600, | ||||
|     LFS_TYPE_GLOBALS        = 0x700, | ||||
|     LFS_TYPE_CRC            = 0x500, | ||||
|  | ||||
|     // internally used type specializations | ||||
|     LFS_TYPE_CREATE         = 0x401, | ||||
|     LFS_TYPE_DELETE         = 0x4ff, | ||||
|     LFS_TYPE_SUPERBLOCK     = 0x0ff, | ||||
|     LFS_TYPE_DIRSTRUCT      = 0x200, | ||||
|     LFS_TYPE_CTZSTRUCT      = 0x202, | ||||
|     LFS_TYPE_INLINESTRUCT   = 0x201, | ||||
|     LFS_TYPE_SOFTTAIL       = 0x600, | ||||
|     LFS_TYPE_HARDTAIL       = 0x601, | ||||
|     LFS_TYPE_MOVESTATE      = 0x7ff, | ||||
|  | ||||
|     // internal chip sources | ||||
|     LFS_FROM_NOOP           = 0x000, | ||||
|     LFS_FROM_MOVE           = 0x101, | ||||
|     LFS_FROM_USERATTRS      = 0x102, | ||||
| }; | ||||
|  | ||||
| // File open flags | ||||
| enum lfs_open_flags { | ||||
|     // open flags | ||||
|     LFS_O_RDONLY = 1,        // Open a file as read only | ||||
|     LFS_O_WRONLY = 2,        // Open a file as write only | ||||
|     LFS_O_RDWR   = 3,        // Open a file as read and write | ||||
|     LFS_O_CREAT  = 0x0100,   // Create a file if it does not exist | ||||
|     LFS_O_EXCL   = 0x0200,   // Fail if a file already exists | ||||
|     LFS_O_TRUNC  = 0x0400,   // Truncate the existing file to zero size | ||||
|     LFS_O_APPEND = 0x0800,   // Move to end of file on every write | ||||
|     LFS_O_RDONLY = 1,         // Open a file as read only | ||||
|     LFS_O_WRONLY = 2,         // Open a file as write only | ||||
|     LFS_O_RDWR   = 3,         // Open a file as read and write | ||||
|     LFS_O_CREAT  = 0x0100,    // Create a file if it does not exist | ||||
|     LFS_O_EXCL   = 0x0200,    // Fail if a file already exists | ||||
|     LFS_O_TRUNC  = 0x0400,    // Truncate the existing file to zero size | ||||
|     LFS_O_APPEND = 0x0800,    // Move to end of file on every write | ||||
|  | ||||
|     // internally used flags | ||||
|     LFS_F_DIRTY   = 0x10000, // File does not match storage | ||||
|     LFS_F_WRITING = 0x20000, // File has been written since last flush | ||||
|     LFS_F_READING = 0x40000, // File has been read since last flush | ||||
|     LFS_F_ERRED   = 0x80000, // An error occured during write | ||||
|     LFS_F_DIRTY   = 0x010000, // File does not match storage | ||||
|     LFS_F_WRITING = 0x020000, // File has been written since last flush | ||||
|     LFS_F_READING = 0x040000, // File has been read since last flush | ||||
|     LFS_F_ERRED   = 0x080000, // An error occured during write | ||||
|     LFS_F_INLINE  = 0x100000, // Currently inlined in directory entry | ||||
|     LFS_F_OPENED  = 0x200000, // File has been opened | ||||
| }; | ||||
|  | ||||
| // File seek flags | ||||
| @@ -132,153 +174,222 @@ struct lfs_config { | ||||
|     // are propogated to the user. | ||||
|     int (*sync)(const struct lfs_config *c); | ||||
|  | ||||
|     // Minimum size of a block read. This determines the size of read buffers. | ||||
|     // This may be larger than the physical read size to improve performance | ||||
|     // by caching more of the block device. | ||||
|     // Minimum size of a block read. All read operations will be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t read_size; | ||||
|  | ||||
|     // Minimum size of a block program. This determines the size of program | ||||
|     // buffers. This may be larger than the physical program size to improve | ||||
|     // performance by caching more of the block device. | ||||
|     // Must be a multiple of the read size. | ||||
|     // Minimum size of a block program. All program operations will be a | ||||
|     // multiple of this value. | ||||
|     lfs_size_t prog_size; | ||||
|  | ||||
|     // Size of an erasable block. This does not impact ram consumption and | ||||
|     // may be larger than the physical erase size. However, this should be | ||||
|     // kept small as each file currently takes up an entire block. | ||||
|     // Must be a multiple of the program size. | ||||
|     // may be larger than the physical erase size. However, non-inlined files | ||||
|     // take up at minimum one block. Must be a multiple of the read | ||||
|     // and program sizes. | ||||
|     lfs_size_t block_size; | ||||
|  | ||||
|     // Number of erasable blocks on the device. | ||||
|     lfs_size_t block_count; | ||||
|  | ||||
|     // Number of blocks to lookahead during block allocation. A larger | ||||
|     // lookahead reduces the number of passes required to allocate a block. | ||||
|     // The lookahead buffer requires only 1 bit per block so it can be quite | ||||
|     // large with little ram impact. Should be a multiple of 32. | ||||
|     lfs_size_t lookahead; | ||||
|     // Number of erase cycles before littlefs evicts metadata logs and moves  | ||||
|     // the metadata to another block. Suggested values are in the | ||||
|     // range 100-1000, with large values having better performance at the cost | ||||
|     // of less consistent wear distribution. | ||||
|     // | ||||
|     // Set to -1 to disable block-level wear-leveling. | ||||
|     int32_t block_cycles; | ||||
|  | ||||
|     // Optional, statically allocated read buffer. Must be read sized. | ||||
|     // Size of block caches. Each cache buffers a portion of a block in RAM. | ||||
|     // The littlefs needs a read cache, a program cache, and one additional | ||||
|     // cache per file. Larger caches can improve performance by storing more | ||||
|     // data and reducing the number of disk accesses. Must be a multiple of | ||||
|     // the read and program sizes, and a factor of the block size. | ||||
|     lfs_size_t cache_size; | ||||
|  | ||||
|     // Size of the lookahead buffer in bytes. A larger lookahead buffer | ||||
|     // increases the number of blocks found during an allocation pass. The | ||||
|     // lookahead buffer is stored as a compact bitmap, so each byte of RAM | ||||
|     // can track 8 blocks. Must be a multiple of 8. | ||||
|     lfs_size_t lookahead_size; | ||||
|  | ||||
|     // Optional statically allocated read buffer. Must be cache_size. | ||||
|     // By default lfs_malloc is used to allocate this buffer. | ||||
|     void *read_buffer; | ||||
|  | ||||
|     // Optional, statically allocated program buffer. Must be program sized. | ||||
|     // Optional statically allocated program buffer. Must be cache_size. | ||||
|     // By default lfs_malloc is used to allocate this buffer. | ||||
|     void *prog_buffer; | ||||
|  | ||||
|     // Optional, statically allocated lookahead buffer. Must be 1 bit per | ||||
|     // lookahead block. | ||||
|     // Optional statically allocated lookahead buffer. Must be lookahead_size | ||||
|     // and aligned to a 32-bit boundary. By default lfs_malloc is used to | ||||
|     // allocate this buffer. | ||||
|     void *lookahead_buffer; | ||||
|  | ||||
|     // Optional, statically allocated buffer for files. Must be program sized. | ||||
|     // If enabled, only one file may be opened at a time. | ||||
|     void *file_buffer; | ||||
| }; | ||||
|     // Optional upper limit on length of file names in bytes. No downside for | ||||
|     // larger names except the size of the info struct which is controlled by | ||||
|     // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in | ||||
|     // superblock and must be respected by other littlefs drivers. | ||||
|     lfs_size_t name_max; | ||||
|  | ||||
|     // Optional upper limit on files in bytes. No downside for larger files | ||||
|     // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored | ||||
|     // in superblock and must be respected by other littlefs drivers. | ||||
|     lfs_size_t file_max; | ||||
|  | ||||
|     // Optional upper limit on custom attributes in bytes. No downside for | ||||
|     // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to | ||||
|     // LFS_ATTR_MAX when zero. | ||||
|     lfs_size_t attr_max; | ||||
| }; | ||||
|  | ||||
| // File info structure | ||||
| struct lfs_info { | ||||
|     // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR | ||||
|     uint8_t type; | ||||
|  | ||||
|     // Size of the file, only valid for REG files | ||||
|     // Size of the file, only valid for REG files. Limited to 32-bits. | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     // Name of the file stored as a null-terminated string | ||||
|     // Name of the file stored as a null-terminated string. Limited to | ||||
|     // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to | ||||
|     // reduce RAM. LFS_NAME_MAX is stored in superblock and must be | ||||
|     // respected by other littlefs drivers. | ||||
|     char name[LFS_NAME_MAX+1]; | ||||
| }; | ||||
|  | ||||
| // Custom attribute structure, used to describe custom attributes | ||||
| // committed atomically during file writes. | ||||
| struct lfs_attr { | ||||
|     // 8-bit type of attribute, provided by user and used to | ||||
|     // identify the attribute | ||||
|     uint8_t type; | ||||
|  | ||||
| /// littlefs data structures /// | ||||
| typedef struct lfs_entry { | ||||
|     lfs_off_t off; | ||||
|     // Pointer to buffer containing the attribute | ||||
|     void *buffer; | ||||
|  | ||||
|     struct lfs_disk_entry { | ||||
|         uint8_t type; | ||||
|         uint8_t elen; | ||||
|         uint8_t alen; | ||||
|         uint8_t nlen; | ||||
|         union { | ||||
|             struct { | ||||
|                 lfs_block_t head; | ||||
|                 lfs_size_t size; | ||||
|             } file; | ||||
|             lfs_block_t dir[2]; | ||||
|         } u; | ||||
|     } d; | ||||
| } lfs_entry_t; | ||||
|     // Size of attribute in bytes, limited to LFS_ATTR_MAX | ||||
|     lfs_size_t size; | ||||
| }; | ||||
|  | ||||
| // Optional configuration provided during lfs_file_opencfg | ||||
| struct lfs_file_config { | ||||
|     // Optional statically allocated file buffer. Must be cache_size. | ||||
|     // By default lfs_malloc is used to allocate this buffer. | ||||
|     void *buffer; | ||||
|  | ||||
|     // Optional list of custom attributes related to the file. If the file | ||||
|     // is opened with read access, these attributes will be read from disk | ||||
|     // during the open call. If the file is opened with write access, the | ||||
|     // attributes will be written to disk every file sync or close. This | ||||
|     // write occurs atomically with update to the file's contents. | ||||
|     // | ||||
|     // Custom attributes are uniquely identified by an 8-bit type and limited | ||||
|     // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller | ||||
|     // than the buffer, it will be padded with zeros. If the stored attribute | ||||
|     // is larger, then it will be silently truncated. If the attribute is not | ||||
|     // found, it will be created implicitly. | ||||
|     struct lfs_attr *attrs; | ||||
|  | ||||
|     // Number of custom attributes in the list | ||||
|     lfs_size_t attr_count; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /// internal littlefs data structures /// | ||||
| typedef struct lfs_cache { | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|     lfs_size_t size; | ||||
|     uint8_t *buffer; | ||||
| } lfs_cache_t; | ||||
|  | ||||
| typedef struct lfs_mdir { | ||||
|     lfs_block_t pair[2]; | ||||
|     uint32_t rev; | ||||
|     lfs_off_t off; | ||||
|     uint32_t etag; | ||||
|     uint16_t count; | ||||
|     bool erased; | ||||
|     bool split; | ||||
|     lfs_block_t tail[2]; | ||||
| } lfs_mdir_t; | ||||
|  | ||||
| // littlefs directory type | ||||
| typedef struct lfs_dir { | ||||
|     struct lfs_dir *next; | ||||
|     uint16_t id; | ||||
|     uint8_t type; | ||||
|     lfs_mdir_t m; | ||||
|  | ||||
|     lfs_off_t pos; | ||||
|     lfs_block_t head[2]; | ||||
| } lfs_dir_t; | ||||
|  | ||||
| // littlefs file type | ||||
| typedef struct lfs_file { | ||||
|     struct lfs_file *next; | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_off_t poff; | ||||
|     uint16_t id; | ||||
|     uint8_t type; | ||||
|     lfs_mdir_t m; | ||||
|  | ||||
|     lfs_block_t head; | ||||
|     lfs_size_t size; | ||||
|     struct lfs_ctz { | ||||
|         lfs_block_t head; | ||||
|         lfs_size_t size; | ||||
|     } ctz; | ||||
|  | ||||
|     uint32_t flags; | ||||
|     lfs_off_t pos; | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|     lfs_cache_t cache; | ||||
|  | ||||
|     const struct lfs_file_config *cfg; | ||||
| } lfs_file_t; | ||||
|  | ||||
| typedef struct lfs_dir { | ||||
|     struct lfs_dir *next; | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_off_t off; | ||||
|  | ||||
|     lfs_block_t head[2]; | ||||
|     lfs_off_t pos; | ||||
|  | ||||
|     struct lfs_disk_dir { | ||||
|         uint32_t rev; | ||||
|         lfs_size_t size; | ||||
|         lfs_block_t tail[2]; | ||||
|     } d; | ||||
| } lfs_dir_t; | ||||
|  | ||||
| typedef struct lfs_superblock { | ||||
|     lfs_off_t off; | ||||
|  | ||||
|     struct lfs_disk_superblock { | ||||
|         uint8_t type; | ||||
|         uint8_t elen; | ||||
|         uint8_t alen; | ||||
|         uint8_t nlen; | ||||
|         lfs_block_t root[2]; | ||||
|         uint32_t block_size; | ||||
|         uint32_t block_count; | ||||
|         uint32_t version; | ||||
|         char magic[8]; | ||||
|     } d; | ||||
|     uint32_t version; | ||||
|     lfs_size_t block_size; | ||||
|     lfs_size_t block_count; | ||||
|     lfs_size_t name_max; | ||||
|     lfs_size_t file_max; | ||||
|     lfs_size_t attr_max; | ||||
| } lfs_superblock_t; | ||||
|  | ||||
| typedef struct lfs_free { | ||||
|     lfs_block_t off; | ||||
|     lfs_block_t size; | ||||
|     lfs_block_t i; | ||||
|     lfs_block_t ack; | ||||
|     uint32_t *buffer; | ||||
| } lfs_free_t; | ||||
|  | ||||
| // The littlefs type | ||||
| // The littlefs filesystem type | ||||
| typedef struct lfs { | ||||
|     const struct lfs_config *cfg; | ||||
|  | ||||
|     lfs_block_t root[2]; | ||||
|     lfs_file_t *files; | ||||
|     lfs_dir_t *dirs; | ||||
|  | ||||
|     lfs_cache_t rcache; | ||||
|     lfs_cache_t pcache; | ||||
|  | ||||
|     lfs_free_t free; | ||||
|     bool deorphaned; | ||||
|     lfs_block_t root[2]; | ||||
|     struct lfs_mlist { | ||||
|         struct lfs_mlist *next; | ||||
|         uint16_t id; | ||||
|         uint8_t type; | ||||
|         lfs_mdir_t m; | ||||
|     } *mlist; | ||||
|     uint32_t seed; | ||||
|  | ||||
|     struct lfs_gstate { | ||||
|         uint32_t tag; | ||||
|         lfs_block_t pair[2]; | ||||
|     } gstate, gpending, gdelta; | ||||
|  | ||||
|     struct lfs_free { | ||||
|         lfs_block_t off; | ||||
|         lfs_block_t size; | ||||
|         lfs_block_t i; | ||||
|         lfs_block_t ack; | ||||
|         uint32_t *buffer; | ||||
|     } free; | ||||
|  | ||||
|     const struct lfs_config *cfg; | ||||
|     lfs_size_t name_max; | ||||
|     lfs_size_t file_max; | ||||
|     lfs_size_t attr_max; | ||||
|  | ||||
| #ifdef LFS_MIGRATE | ||||
|     struct lfs1 *lfs1; | ||||
| #endif | ||||
| } lfs_t; | ||||
|  | ||||
|  | ||||
| @@ -287,7 +398,8 @@ typedef struct lfs { | ||||
| // Format a block device with the littlefs | ||||
| // | ||||
| // Requires a littlefs object and config struct. This clobbers the littlefs | ||||
| // object, and does not leave the filesystem mounted. | ||||
| // object, and does not leave the filesystem mounted. The config struct must | ||||
| // be zeroed for defaults and backwards compatibility. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *config); | ||||
| @@ -296,7 +408,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config); | ||||
| // | ||||
| // 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. | ||||
| // lfs and config must be allocated while mounted. The config struct must | ||||
| // be zeroed for defaults and backwards compatibility. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *config); | ||||
| @@ -329,19 +442,64 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | ||||
|  | ||||
| // Get a custom attribute | ||||
| // | ||||
| // Custom attributes are uniquely identified by an 8-bit type and limited | ||||
| // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than | ||||
| // the buffer, it will be padded with zeros. If the stored attribute is larger, | ||||
| // then it will be silently truncated. If no attribute is found, the error | ||||
| // LFS_ERR_NOATTR is returned and the buffer is filled with zeros. | ||||
| // | ||||
| // Returns the size of the attribute, or a negative error code on failure. | ||||
| // Note, the returned size is the size of the attribute on disk, irrespective | ||||
| // of the size of the buffer. This can be used to dynamically allocate a buffer | ||||
| // or check for existance. | ||||
| lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, void *buffer, lfs_size_t size); | ||||
|  | ||||
| // Set custom attributes | ||||
| // | ||||
| // Custom attributes are uniquely identified by an 8-bit type and limited | ||||
| // to LFS_ATTR_MAX bytes. If an attribute is not found, it will be | ||||
| // implicitly created. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_setattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, const void *buffer, lfs_size_t size); | ||||
|  | ||||
| // Removes a custom attribute | ||||
| // | ||||
| // If an attribute is not found, nothing happens. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); | ||||
|  | ||||
|  | ||||
| /// File operations /// | ||||
|  | ||||
| // Open a file | ||||
| // | ||||
| // The mode that the file is opened in is determined | ||||
| // by the flags, which are values from the enum lfs_open_flags | ||||
| // that are bitwise-ored together. | ||||
| // The mode that the file is opened in is determined by the flags, which | ||||
| // are values from the enum lfs_open_flags that are bitwise-ored together. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags); | ||||
|  | ||||
| // 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. | ||||
| // | ||||
| // The config struct provides additional config options per file as described | ||||
| // above. The config struct must be allocated while the file is open, and the | ||||
| // config struct must be zeroed for defaults and backwards compatibility. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags, | ||||
|         const struct lfs_file_config *config); | ||||
|  | ||||
| // Close a file | ||||
| // | ||||
| // Any pending writes are written out to storage as though | ||||
| @@ -375,7 +533,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
| // Change the position of the file | ||||
| // | ||||
| // The change in position is determined by the offset and whence flag. | ||||
| // Returns the old position of the file, or a negative error code on failure. | ||||
| // 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); | ||||
|  | ||||
| @@ -392,7 +550,7 @@ lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); | ||||
|  | ||||
| // Change the position of the file to the beginning of the file | ||||
| // | ||||
| // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) | ||||
| // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET) | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); | ||||
|  | ||||
| @@ -425,7 +583,8 @@ int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); | ||||
| // Read an entry in the directory | ||||
| // | ||||
| // Fills out the info structure, based on the specified file or directory. | ||||
| // Returns a negative error code on failure. | ||||
| // Returns a positive value on success, 0 at the end of directory, | ||||
| // or a negative error code on failure. | ||||
| int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); | ||||
|  | ||||
| // Change the position of the directory | ||||
| @@ -450,7 +609,15 @@ lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); | ||||
| int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||
|  | ||||
|  | ||||
| /// Miscellaneous littlefs specific operations /// | ||||
| /// Filesystem-level filesystem operations | ||||
|  | ||||
| // Finds the current size of the filesystem | ||||
| // | ||||
| // Note: Result is best effort. If files share COW structures, the returned | ||||
| // size may be larger than the filesystem actually is. | ||||
| // | ||||
| // Returns the number of allocated blocks, or a negative error code on failure. | ||||
| lfs_ssize_t lfs_fs_size(lfs_t *lfs); | ||||
|  | ||||
| // Traverse through all blocks in use by the filesystem | ||||
| // | ||||
| @@ -459,16 +626,26 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||
| // blocks are in use or how much of the storage is available. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
| int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
|  | ||||
| // Prunes any recoverable errors that may have occured in the filesystem | ||||
| #ifdef LFS_MIGRATE | ||||
| // Attempts to migrate a previous version of littlefs | ||||
| // | ||||
| // Not needed to be called by user unless an operation is interrupted | ||||
| // but the filesystem is still mounted. This is already called on first | ||||
| // allocation. | ||||
| // Behaves similarly to the lfs_format function. Attempts to mount | ||||
| // the previous version of littlefs and update the filesystem so it can be | ||||
| // mounted with the current version of littlefs. | ||||
| // | ||||
| // Requires a littlefs object and config struct. This clobbers the littlefs | ||||
| // object, and does not leave the filesystem mounted. The config struct must | ||||
| // be zeroed for defaults and backwards compatibility. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_deorphan(lfs_t *lfs); | ||||
| int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										23
									
								
								lfs_util.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								lfs_util.c
									
									
									
									
									
								
							| @@ -1,19 +1,8 @@ | ||||
| /* | ||||
|  * lfs util functions | ||||
|  * | ||||
|  * Copyright (c) 2017 ARM Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #include "lfs_util.h" | ||||
|  | ||||
| @@ -22,7 +11,7 @@ | ||||
|  | ||||
|  | ||||
| // Software CRC implementation with small lookup table | ||||
| void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) { | ||||
| uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { | ||||
|     static const uint32_t rtable[16] = { | ||||
|         0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, | ||||
|         0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, | ||||
| @@ -33,9 +22,11 @@ void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) { | ||||
|     const uint8_t *data = buffer; | ||||
|  | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf]; | ||||
|         *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf]; | ||||
|         crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; | ||||
|         crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; | ||||
|     } | ||||
|  | ||||
|     return crc; | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										101
									
								
								lfs_util.h
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								lfs_util.h
									
									
									
									
									
								
							| @@ -1,19 +1,8 @@ | ||||
| /* | ||||
|  * lfs utility functions | ||||
|  * | ||||
|  * Copyright (c) 2017 ARM Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||
|  * SPDX-License-Identifier: BSD-3-Clause | ||||
|  */ | ||||
| #ifndef LFS_UTIL_H | ||||
| #define LFS_UTIL_H | ||||
| @@ -22,8 +11,8 @@ | ||||
| // LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_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 and | ||||
| // modifying as needed. | ||||
| // provided by the config file. To start, I would suggest copying lfs_util.h | ||||
| // and modifying as needed. | ||||
| #ifdef LFS_CONFIG | ||||
| #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) | ||||
| #define LFS_STRINGIZE2(x) #x | ||||
| @@ -34,6 +23,7 @@ | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #ifndef LFS_NO_MALLOC | ||||
| #include <stdlib.h> | ||||
| @@ -41,33 +31,48 @@ | ||||
| #ifndef LFS_NO_ASSERT | ||||
| #include <assert.h> | ||||
| #endif | ||||
| #if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) | ||||
| #if !defined(LFS_NO_DEBUG) || \ | ||||
|         !defined(LFS_NO_WARN) || \ | ||||
|         !defined(LFS_NO_ERROR) || \ | ||||
|         defined(LFS_YES_TRACE) | ||||
| #include <stdio.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // Macros, may be replaced by system specific wrappers. Arguments to these | ||||
| // macros must not have side-effects as the macros can be removed for a smaller | ||||
| // code footprint | ||||
|  | ||||
| // Logging functions | ||||
| #ifdef LFS_YES_TRACE | ||||
| #define LFS_TRACE(fmt, ...) \ | ||||
|     printf("lfs_trace:%d: " fmt "\n", __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_TRACE(fmt, ...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_DEBUG | ||||
| #define LFS_DEBUG(fmt, ...) \ | ||||
|     printf("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) | ||||
|     printf("lfs_debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_DEBUG(fmt, ...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_WARN | ||||
| #define LFS_WARN(fmt, ...) \ | ||||
|     printf("lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) | ||||
|     printf("lfs_warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_WARN(fmt, ...) | ||||
| #endif | ||||
|  | ||||
| #ifndef LFS_NO_ERROR | ||||
| #define LFS_ERROR(fmt, ...) \ | ||||
|     printf("lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__) | ||||
|     printf("lfs_error:%d: " fmt "\n", __LINE__, __VA_ARGS__) | ||||
| #else | ||||
| #define LFS_ERROR(fmt, ...) | ||||
| #endif | ||||
| @@ -93,6 +98,15 @@ static inline uint32_t lfs_min(uint32_t a, uint32_t b) { | ||||
|     return (a < b) ? a : b; | ||||
| } | ||||
|  | ||||
| // Align to nearest multiple of a size | ||||
| static inline uint32_t lfs_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); | ||||
| } | ||||
|  | ||||
| // Find the next smallest power of 2 less than or equal to a | ||||
| static inline uint32_t lfs_npw2(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||
| @@ -136,17 +150,17 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) { | ||||
|     return (int)(unsigned)(a - b); | ||||
| } | ||||
|  | ||||
| // Convert from 32-bit little-endian to native order | ||||
| // Convert between 32-bit little-endian and native order | ||||
| static inline uint32_t lfs_fromle32(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||
|     (defined(  BYTE_ORDER  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER  ) && __BYTE_ORDER   == __ORDER_LITTLE_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) | ||||
|     (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) && ( \ | ||||
|     (defined(  BYTE_ORDER  ) &&   BYTE_ORDER   ==   ORDER_BIG_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER  ) && __BYTE_ORDER   == __ORDER_BIG_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER__) && __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  ) || \ | ||||
|     (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) | ||||
|     return __builtin_bswap32(a); | ||||
| #else | ||||
|     return (((uint8_t*)&a)[0] <<  0) | | ||||
| @@ -156,19 +170,44 @@ static inline uint32_t lfs_fromle32(uint32_t a) { | ||||
| #endif | ||||
| } | ||||
|  | ||||
| // Convert to 32-bit little-endian from native order | ||||
| static inline uint32_t lfs_tole32(uint32_t a) { | ||||
|     return lfs_fromle32(a); | ||||
| } | ||||
|  | ||||
| // Convert between 32-bit big-endian and native order | ||||
| static inline uint32_t lfs_frombe32(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||
|     (defined(  BYTE_ORDER  ) && defined(  ORDER_LITTLE_ENDIAN  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER  ) && defined(__ORDER_LITTLE_ENDIAN  ) && __BYTE_ORDER   == __ORDER_LITTLE_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) | ||||
|     return __builtin_bswap32(a); | ||||
| #elif !defined(LFS_NO_INTRINSICS) && ( \ | ||||
|     (defined(  BYTE_ORDER  ) && defined(  ORDER_BIG_ENDIAN  ) &&   BYTE_ORDER   ==   ORDER_BIG_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER  ) && defined(__ORDER_BIG_ENDIAN  ) && __BYTE_ORDER   == __ORDER_BIG_ENDIAN  ) || \ | ||||
|     (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) | ||||
|     return a; | ||||
| #else | ||||
|     return (((uint8_t*)&a)[0] << 24) | | ||||
|            (((uint8_t*)&a)[1] << 16) | | ||||
|            (((uint8_t*)&a)[2] <<  8) | | ||||
|            (((uint8_t*)&a)[3] <<  0); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static inline uint32_t lfs_tobe32(uint32_t a) { | ||||
|     return lfs_frombe32(a); | ||||
| } | ||||
|  | ||||
| // Calculate CRC-32 with polynomial = 0x04c11db7 | ||||
| void lfs_crc(uint32_t *crc, const void *buffer, size_t size); | ||||
| uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); | ||||
|  | ||||
| // Allocate memory, only used if buffers are not provided to littlefs | ||||
| // Note, memory must be 64-bit aligned | ||||
| static inline void *lfs_malloc(size_t size) { | ||||
| #ifndef LFS_NO_MALLOC | ||||
|     return malloc(size); | ||||
| #else | ||||
|     (void)size; | ||||
|     return NULL; | ||||
| #endif | ||||
| } | ||||
| @@ -177,9 +216,15 @@ static inline void *lfs_malloc(size_t size) { | ||||
| static inline void lfs_free(void *p) { | ||||
| #ifndef LFS_NO_MALLOC | ||||
|     free(p); | ||||
| #else | ||||
|     (void)p; | ||||
| #endif | ||||
| } | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										44
									
								
								scripts/corrupt.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										44
									
								
								scripts/corrupt.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| #!/usr/bin/env python2 | ||||
|  | ||||
| import struct | ||||
| import sys | ||||
| import os | ||||
| import argparse | ||||
|  | ||||
| def corrupt(block): | ||||
|     with open(block, 'r+b') as file: | ||||
|         # skip rev | ||||
|         file.read(4) | ||||
|  | ||||
|         # go to last commit | ||||
|         tag = 0xffffffff | ||||
|         while True: | ||||
|             try: | ||||
|                 ntag, = struct.unpack('>I', file.read(4)) | ||||
|             except struct.error: | ||||
|                 break | ||||
|  | ||||
|             tag ^= ntag | ||||
|             size = (tag & 0x3ff) if (tag & 0x3ff) != 0x3ff else 0 | ||||
|             file.seek(size, os.SEEK_CUR) | ||||
|  | ||||
|         # lob off last 3 bytes | ||||
|         file.seek(-(size + 3), os.SEEK_CUR) | ||||
|         file.truncate() | ||||
|  | ||||
| def main(args): | ||||
|     if args.n or not args.blocks: | ||||
|         with open('blocks/.history', 'rb') as file: | ||||
|             for i in range(int(args.n or 1)): | ||||
|                 last, = struct.unpack('<I', file.read(4)) | ||||
|                 args.blocks.append('blocks/%x' % last) | ||||
|  | ||||
|     for block in args.blocks: | ||||
|         print 'corrupting %s' % block | ||||
|         corrupt(block) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument('-n') | ||||
|     parser.add_argument('blocks', nargs='*') | ||||
|     main(parser.parse_args()) | ||||
							
								
								
									
										112
									
								
								scripts/debug.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										112
									
								
								scripts/debug.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| #!/usr/bin/env python2 | ||||
|  | ||||
| import struct | ||||
| import binascii | ||||
|  | ||||
| TYPES = { | ||||
|     (0x700, 0x400): 'splice', | ||||
|     (0x7ff, 0x401): 'create', | ||||
|     (0x7ff, 0x4ff): 'delete', | ||||
|     (0x700, 0x000): 'name', | ||||
|     (0x7ff, 0x001): 'name reg', | ||||
|     (0x7ff, 0x002): 'name dir', | ||||
|     (0x7ff, 0x0ff): 'name superblock', | ||||
|     (0x700, 0x200): 'struct', | ||||
|     (0x7ff, 0x200): 'struct dir', | ||||
|     (0x7ff, 0x202): 'struct ctz', | ||||
|     (0x7ff, 0x201): 'struct inline', | ||||
|     (0x700, 0x300): 'userattr', | ||||
|     (0x700, 0x600): 'tail', | ||||
|     (0x7ff, 0x600): 'tail soft', | ||||
|     (0x7ff, 0x601): 'tail hard', | ||||
|     (0x700, 0x700): 'gstate', | ||||
|     (0x7ff, 0x7ff): 'gstate move', | ||||
|     (0x700, 0x500): 'crc', | ||||
| } | ||||
|  | ||||
| def typeof(type): | ||||
|     for prefix in range(12): | ||||
|         mask = 0x7ff & ~((1 << prefix)-1) | ||||
|         if (mask, type & mask) in TYPES: | ||||
|             return TYPES[mask, type & mask] + ( | ||||
|                 ' %0*x' % (prefix/4, type & ((1 << prefix)-1)) | ||||
|                 if prefix else '') | ||||
|     else: | ||||
|         return '%02x' % type | ||||
|  | ||||
| def main(*blocks): | ||||
|     # find most recent block | ||||
|     file = None | ||||
|     rev = None | ||||
|     crc = None | ||||
|     versions = [] | ||||
|  | ||||
|     for block in blocks: | ||||
|         try: | ||||
|             nfile = open(block, 'rb') | ||||
|             ndata = nfile.read(4) | ||||
|             ncrc = binascii.crc32(ndata) | ||||
|             nrev, = struct.unpack('<I', ndata) | ||||
|  | ||||
|             assert rev != nrev | ||||
|             if not file or ((rev - nrev) & 0x80000000): | ||||
|                 file = nfile | ||||
|                 rev = nrev | ||||
|                 crc = ncrc | ||||
|  | ||||
|             versions.append((nrev, '%s (rev %d)' % (block, nrev))) | ||||
|         except (IOError, struct.error): | ||||
|             pass | ||||
|  | ||||
|     if not file: | ||||
|         print 'Bad metadata pair {%s}' % ', '.join(blocks) | ||||
|         return 1 | ||||
|  | ||||
|     print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True)) | ||||
|  | ||||
|     # go through each tag, print useful information | ||||
|     print "%-4s  %-8s  %-14s  %3s %4s  %s" % ( | ||||
|         'off', 'tag', 'type', 'id', 'len', 'dump') | ||||
|  | ||||
|     tag = 0xffffffff | ||||
|     off = 4 | ||||
|     while True: | ||||
|         try: | ||||
|             data = file.read(4) | ||||
|             crc = binascii.crc32(data, crc) | ||||
|             ntag, = struct.unpack('>I', data) | ||||
|         except struct.error: | ||||
|             break | ||||
|  | ||||
|         tag ^= ntag | ||||
|         off += 4 | ||||
|  | ||||
|         type = (tag & 0x7ff00000) >> 20 | ||||
|         id   = (tag & 0x000ffc00) >> 10 | ||||
|         size = (tag & 0x000003ff) >> 0 | ||||
|         iscrc = (type & 0x700) == 0x500 | ||||
|  | ||||
|         data = file.read(size if size != 0x3ff else 0) | ||||
|         if iscrc: | ||||
|             crc = binascii.crc32(data[:4], crc) | ||||
|         else: | ||||
|             crc = binascii.crc32(data, crc) | ||||
|  | ||||
|         print '%04x: %08x  %-15s %3s %4s  %-23s  %-8s' % ( | ||||
|             off, tag, | ||||
|             typeof(type) + (' bad!' if iscrc and ~crc else ''), | ||||
|             hex(id)[2:] if id != 0x3ff else '.', | ||||
|             size if size != 0x3ff else 'x', | ||||
|             ' '.join('%02x' % ord(c) for c in data[:8]), | ||||
|             ''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8])) | ||||
|  | ||||
|         off += size if size != 0x3ff else 0 | ||||
|         if iscrc: | ||||
|             crc = 0 | ||||
|             tag ^= (type & 1) << 31 | ||||
|  | ||||
|     return 0 | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     sys.exit(main(*sys.argv[1:])) | ||||
							
								
								
									
										61
									
								
								scripts/prefix.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										61
									
								
								scripts/prefix.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| #!/usr/bin/env python2 | ||||
|  | ||||
| # This script replaces prefixes of files, and symbols in that file. | ||||
| # Useful for creating different versions of the codebase that don't | ||||
| # conflict at compile time. | ||||
| # | ||||
| # example: | ||||
| # $ ./scripts/prefix.py lfs2 | ||||
|  | ||||
| import os | ||||
| import os.path | ||||
| import re | ||||
| import glob | ||||
| import itertools | ||||
| import tempfile | ||||
| import shutil | ||||
| import subprocess | ||||
|  | ||||
| DEFAULT_PREFIX = "lfs" | ||||
|  | ||||
| def subn(from_prefix, to_prefix, name): | ||||
|     name, count1 = re.subn('\\b'+from_prefix, to_prefix, name) | ||||
|     name, count2 = re.subn('\\b'+from_prefix.upper(), to_prefix.upper(), name) | ||||
|     name, count3 = re.subn('\\B-D'+from_prefix.upper(), | ||||
|             '-D'+to_prefix.upper(), name) | ||||
|     return name, count1+count2+count3 | ||||
|  | ||||
| def main(from_prefix, to_prefix=None, files=None): | ||||
|     if not to_prefix: | ||||
|         from_prefix, to_prefix = DEFAULT_PREFIX, from_prefix | ||||
|  | ||||
|     if not files: | ||||
|         files = subprocess.check_output([ | ||||
|                 'git', 'ls-tree', '-r', '--name-only', 'HEAD']).split() | ||||
|  | ||||
|     for oldname in files: | ||||
|         # Rename any matching file names | ||||
|         newname, namecount = subn(from_prefix, to_prefix, oldname) | ||||
|         if namecount: | ||||
|             subprocess.check_call(['git', 'mv', oldname, newname]) | ||||
|  | ||||
|         # Rename any prefixes in file | ||||
|         count = 0 | ||||
|         with open(newname+'~', 'w') as tempf: | ||||
|             with open(newname) as newf: | ||||
|                 for line in newf: | ||||
|                     line, n = subn(from_prefix, to_prefix, line) | ||||
|                     count += n | ||||
|                     tempf.write(line) | ||||
|         shutil.copystat(newname, newname+'~') | ||||
|         os.rename(newname+'~', newname) | ||||
|         subprocess.check_call(['git', 'add', newname]) | ||||
|  | ||||
|         # Summary | ||||
|         print '%s: %d replacements' % ( | ||||
|                 '%s -> %s' % (oldname, newname) if namecount else oldname, | ||||
|                 count) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     sys.exit(main(*sys.argv[1:])) | ||||
							
								
								
									
										28
									
								
								scripts/results.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								scripts/results.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #!/usr/bin/env python2 | ||||
|  | ||||
| import struct | ||||
| import sys | ||||
| import time | ||||
| import os | ||||
| import re | ||||
|  | ||||
| def main(): | ||||
|     with open('blocks/.config') as file: | ||||
|         read_size, prog_size, block_size, block_count = ( | ||||
|             struct.unpack('<LLLL', file.read())) | ||||
|  | ||||
|     real_size = sum( | ||||
|         os.path.getsize(os.path.join('blocks', f)) | ||||
|         for f in os.listdir('blocks') if re.match('\d+', f)) | ||||
|  | ||||
|     with open('blocks/.stats') as file: | ||||
|         read_count, prog_count, erase_count = ( | ||||
|             struct.unpack('<QQQ', file.read())) | ||||
|  | ||||
|     runtime = time.time() - os.stat('blocks').st_ctime | ||||
|  | ||||
|     print 'results: %dB %dB %dB %.3fs' % ( | ||||
|         read_count, prog_count, erase_count, runtime) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main(*sys.argv[1:]) | ||||
| @@ -7,34 +7,21 @@ | ||||
| 
 | ||||
| 
 | ||||
| // test stuff | ||||
| static void test_log(const char *s, uintmax_t v) {{ | ||||
|     printf("%s: %jd\n", s, v); | ||||
| }} | ||||
| 
 | ||||
| static void test_assert(const char *file, unsigned line, | ||||
|         const char *s, uintmax_t v, uintmax_t e) {{ | ||||
|     static const char *last[6] = {{0, 0}}; | ||||
|     if (v != e || !(last[0] == s || last[1] == s || | ||||
|             last[2] == s || last[3] == s || | ||||
|             last[4] == s || last[5] == s)) {{ | ||||
|         test_log(s, v); | ||||
|         last[0] = last[1]; | ||||
|         last[1] = last[2]; | ||||
|         last[2] = last[3]; | ||||
|         last[3] = last[4]; | ||||
|         last[4] = last[5]; | ||||
|         last[5] = s; | ||||
|     }} | ||||
| 
 | ||||
|     if (v != e) {{ | ||||
|         fprintf(stderr, "\033[31m%s:%u: assert %s failed with %jd, " | ||||
|                 "expected %jd\033[0m\n", file, line, s, v, e); | ||||
|         fprintf(stderr, "\033[97m%s:%u: \033[91m" | ||||
|                 "assert failed with %jd, expected %jd\033[0m\n" | ||||
|                 "    %s\n\n", file, line, v, e, s); | ||||
|         exit(-2); | ||||
|     }} | ||||
| }} | ||||
| 
 | ||||
| #define test_assert(s, v, e) test_assert(__FILE__, __LINE__, s, v, e) | ||||
| #define test_assert(v, e) \ | ||||
|         test_assert(__FILE__, __LINE__, #v " => " #e, v, e) | ||||
| 
 | ||||
| // implicit variable for asserts | ||||
| uintmax_t test; | ||||
| 
 | ||||
| // utility functions for traversals | ||||
| static int __attribute__((used)) test_count(void *p, lfs_block_t b) {{ | ||||
| @@ -44,29 +31,23 @@ static int __attribute__((used)) test_count(void *p, lfs_block_t b) {{ | ||||
|     return 0; | ||||
| }} | ||||
| 
 | ||||
| 
 | ||||
| // lfs declarations | ||||
| lfs_t lfs; | ||||
| lfs_emubd_t bd; | ||||
| lfs_file_t file[4]; | ||||
| lfs_dir_t dir[4]; | ||||
| // other declarations for convenience | ||||
| lfs_file_t file; | ||||
| lfs_dir_t dir; | ||||
| struct lfs_info info; | ||||
| 
 | ||||
| uint8_t buffer[1024]; | ||||
| uint8_t wbuffer[1024]; | ||||
| uint8_t rbuffer[1024]; | ||||
| lfs_size_t size; | ||||
| lfs_size_t wsize; | ||||
| lfs_size_t rsize; | ||||
| 
 | ||||
| uintmax_t test; | ||||
| char path[1024]; | ||||
| 
 | ||||
| // test configuration options | ||||
| #ifndef LFS_READ_SIZE | ||||
| #define LFS_READ_SIZE 16 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_PROG_SIZE | ||||
| #define LFS_PROG_SIZE 16 | ||||
| #define LFS_PROG_SIZE LFS_READ_SIZE | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_BLOCK_SIZE | ||||
| @@ -77,8 +58,16 @@ uintmax_t test; | ||||
| #define LFS_BLOCK_COUNT 1024 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_LOOKAHEAD | ||||
| #define LFS_LOOKAHEAD 128 | ||||
| #ifndef LFS_BLOCK_CYCLES | ||||
| #define LFS_BLOCK_CYCLES 1024 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_CACHE_SIZE | ||||
| #define LFS_CACHE_SIZE (64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE) | ||||
| #endif | ||||
| 
 | ||||
| #ifndef LFS_LOOKAHEAD_SIZE | ||||
| #define LFS_LOOKAHEAD_SIZE 16 | ||||
| #endif | ||||
| 
 | ||||
| const struct lfs_config cfg = {{ | ||||
| @@ -88,11 +77,13 @@ const struct lfs_config cfg = {{ | ||||
|     .erase = &lfs_emubd_erase, | ||||
|     .sync  = &lfs_emubd_sync, | ||||
| 
 | ||||
|     .read_size   = LFS_READ_SIZE, | ||||
|     .prog_size   = LFS_PROG_SIZE, | ||||
|     .block_size  = LFS_BLOCK_SIZE, | ||||
|     .block_count = LFS_BLOCK_COUNT, | ||||
|     .lookahead   = LFS_LOOKAHEAD, | ||||
|     .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, | ||||
| }}; | ||||
| 
 | ||||
| 
 | ||||
| @@ -101,6 +92,5 @@ int main(void) {{ | ||||
|     lfs_emubd_create(&cfg, "blocks"); | ||||
| 
 | ||||
| {tests} | ||||
| 
 | ||||
|     lfs_emubd_destroy(&cfg); | ||||
| }} | ||||
							
								
								
									
										81
									
								
								scripts/test.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										81
									
								
								scripts/test.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| #!/usr/bin/env python2 | ||||
|  | ||||
| import re | ||||
| import sys | ||||
| import subprocess | ||||
| import os | ||||
|  | ||||
|  | ||||
| def generate(test): | ||||
|     with open("scripts/template.fmt") as file: | ||||
|         template = file.read() | ||||
|  | ||||
|     haslines = 'TEST_LINE' in os.environ and 'TEST_FILE' in os.environ | ||||
|  | ||||
|     lines = [] | ||||
|     for offset, line in enumerate( | ||||
|             re.split('(?<=(?:.;| [{}]))\n', test.read())): | ||||
|         match = re.match('((?: *\n)*)( *)(.*)=>(.*);', | ||||
|                 line, re.DOTALL | re.MULTILINE) | ||||
|         if match: | ||||
|             preface, tab, test, expect = match.groups() | ||||
|             lines.extend(['']*preface.count('\n')) | ||||
|             lines.append(tab+'test_assert({test}, {expect});'.format( | ||||
|                 test=test.strip(), expect=expect.strip())) | ||||
|         else: | ||||
|             lines.append(line) | ||||
|  | ||||
|     # Create test file | ||||
|     with open('test.c', 'w') as file: | ||||
|         if 'TEST_LINE' in os.environ and 'TEST_FILE' in os.environ: | ||||
|             lines.insert(0, '#line %d "%s"' % ( | ||||
|                     int(os.environ['TEST_LINE']) + 1, | ||||
|                     os.environ['TEST_FILE'])) | ||||
|             lines.append('#line %d "test.c"' % ( | ||||
|                     template[:template.find('{tests}')].count('\n') | ||||
|                     + len(lines) + 2)) | ||||
|  | ||||
|         file.write(template.format(tests='\n'.join(lines))) | ||||
|  | ||||
|     # Remove build artifacts to force rebuild | ||||
|     try: | ||||
|         os.remove('test.o') | ||||
|         os.remove('lfs') | ||||
|     except OSError: | ||||
|         pass | ||||
|  | ||||
| def compile(): | ||||
|     subprocess.check_call([ | ||||
|             os.environ.get('MAKE', 'make'), | ||||
|             '--no-print-directory', '-s']) | ||||
|  | ||||
| def execute(): | ||||
|     if 'EXEC' in os.environ: | ||||
|         subprocess.check_call([os.environ['EXEC'], "./lfs"]) | ||||
|     else: | ||||
|         subprocess.check_call(["./lfs"]) | ||||
|  | ||||
| def main(test=None): | ||||
|     try: | ||||
|         if test and not test.startswith('-'): | ||||
|             with open(test) as file: | ||||
|                 generate(file) | ||||
|         else: | ||||
|             generate(sys.stdin) | ||||
|  | ||||
|         compile() | ||||
|  | ||||
|         if test == '-s': | ||||
|             sys.exit(1) | ||||
|  | ||||
|         execute() | ||||
|  | ||||
|     except subprocess.CalledProcessError: | ||||
|         # Python stack trace is counterproductive, just exit | ||||
|         sys.exit(2) | ||||
|     except KeyboardInterrupt: | ||||
|         # Python stack trace is counterproductive, just exit | ||||
|         sys.exit(3) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main(*sys.argv[1:]) | ||||
| @@ -1,30 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
|  | ||||
| import struct | ||||
| import sys | ||||
| import time | ||||
| import os | ||||
| import re | ||||
|  | ||||
| def main(): | ||||
|     with open('blocks/config') as file: | ||||
|         s = struct.unpack('<LLLL', file.read()) | ||||
|         print 'read_size: %d' % s[0] | ||||
|         print 'prog_size: %d' % s[1] | ||||
|         print 'block_size: %d' % s[2] | ||||
|         print 'block_size: %d' % s[3] | ||||
|  | ||||
|     print 'real_size: %d' % sum( | ||||
|         os.path.getsize(os.path.join('blocks', f)) | ||||
|         for f in os.listdir('blocks') if re.match('\d+', f)) | ||||
|  | ||||
|     with open('blocks/stats') as file: | ||||
|         s = struct.unpack('<QQQ', file.read()) | ||||
|         print 'read_count: %d' % s[0] | ||||
|         print 'prog_count: %d' % s[1] | ||||
|         print 'erase_count: %d' % s[2] | ||||
|  | ||||
|     print 'runtime: %.3f' % (time.time() - os.stat('blocks').st_ctime) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main(*sys.argv[1:]) | ||||
| @@ -1,61 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
|  | ||||
| import re | ||||
| import sys | ||||
| import subprocess | ||||
| import os | ||||
|  | ||||
| def generate(test): | ||||
|     with open("tests/template.fmt") as file: | ||||
|         template = file.read() | ||||
|  | ||||
|     lines = [] | ||||
|     for line in re.split('(?<=[;{}])\n', test.read()): | ||||
|         match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE) | ||||
|         if match: | ||||
|             tab, test, expect = match.groups() | ||||
|             lines.append(tab+'test = {test};'.format(test=test.strip())) | ||||
|             lines.append(tab+'test_assert("{name}", test, {expect});'.format( | ||||
|                     name = re.match('\w*', test.strip()).group(), | ||||
|                     expect = expect.strip())) | ||||
|         else: | ||||
|             lines.append(line) | ||||
|  | ||||
|     # Create test file | ||||
|     with open('test.c', 'w') as file: | ||||
|         file.write(template.format(tests='\n'.join(lines))) | ||||
|  | ||||
|     # Remove build artifacts to force rebuild | ||||
|     try: | ||||
|         os.remove('test.o') | ||||
|         os.remove('lfs') | ||||
|     except OSError: | ||||
|         pass | ||||
|  | ||||
| def compile(): | ||||
|     subprocess.check_call([ | ||||
|             os.environ.get('MAKE', 'make'), | ||||
|             '--no-print-directory', '-s']) | ||||
|  | ||||
| def execute(): | ||||
|     if 'EXEC' in os.environ: | ||||
|         subprocess.check_call([os.environ['EXEC'], "./lfs"]) | ||||
|     else: | ||||
|         subprocess.check_call(["./lfs"]) | ||||
|  | ||||
| def main(test=None): | ||||
|     if test and not test.startswith('-'): | ||||
|         with open(test) as file: | ||||
|             generate(file) | ||||
|     else: | ||||
|         generate(sys.stdin) | ||||
|  | ||||
|     compile() | ||||
|  | ||||
|     if test == '-s': | ||||
|         sys.exit(1) | ||||
|  | ||||
|     execute() | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main(*sys.argv[1:]) | ||||
| @@ -1,16 +1,18 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| set -euE | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Allocator tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| SIZE=15000 | ||||
|  | ||||
| lfs_mkdir() { | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "$1") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| @@ -18,7 +20,7 @@ TEST | ||||
| } | ||||
|  | ||||
| lfs_remove() { | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "$1/eggs") => 0; | ||||
|     lfs_remove(&lfs, "$1/bacon") => 0; | ||||
| @@ -29,22 +31,23 @@ TEST | ||||
| } | ||||
|  | ||||
| lfs_alloc_singleproc() { | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     const char *names[] = {"bacon", "eggs", "pancakes"}; | ||||
|     lfs_file_t files[sizeof(names)/sizeof(names[0])]; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         sprintf((char*)buffer, "$1/%s", names[n]); | ||||
|         lfs_file_open(&lfs, &file[n], (char*)buffer, | ||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         sprintf(path, "$1/%s", names[n]); | ||||
|         lfs_file_open(&lfs, &files[n], path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|     } | ||||
|     for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         size = strlen(names[n]); | ||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         lfs_size_t size = strlen(names[n]); | ||||
|         for (int i = 0; i < $SIZE; i++) { | ||||
|             lfs_file_write(&lfs, &file[n], names[n], size) => size; | ||||
|             lfs_file_write(&lfs, &files[n], names[n], size) => size; | ||||
|         } | ||||
|     } | ||||
|     for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         lfs_file_close(&lfs, &file[n]) => 0; | ||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { | ||||
|         lfs_file_close(&lfs, &files[n]) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| @@ -53,16 +56,16 @@ TEST | ||||
| lfs_alloc_multiproc() { | ||||
| for name in bacon eggs pancakes | ||||
| do | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "$1/$name", | ||||
|     lfs_file_open(&lfs, &file, "$1/$name", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|     size = strlen("$name"); | ||||
|     lfs_size_t size = strlen("$name"); | ||||
|     memcpy(buffer, "$name", size); | ||||
|     for (int i = 0; i < $SIZE; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| done | ||||
| @@ -71,15 +74,15 @@ done | ||||
| lfs_verify() { | ||||
| for name in bacon eggs pancakes | ||||
| do | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "$1/$name", LFS_O_RDONLY) => 0; | ||||
|     size = strlen("$name"); | ||||
|     lfs_file_open(&lfs, &file, "$1/$name", LFS_O_RDONLY) => 0; | ||||
|     lfs_size_t size = strlen("$name"); | ||||
|     for (int i = 0; i < $SIZE; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "$name", size) => 0; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| done | ||||
| @@ -115,19 +118,19 @@ lfs_remove multiprocreuse | ||||
| lfs_remove singleprocreuse | ||||
|  | ||||
| echo "--- Exhaustion test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("exhaustion"); | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs_size_t size = strlen("exhaustion"); | ||||
|     memcpy(buffer, "exhaustion", size); | ||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_sync(&lfs, &file[0]) => 0; | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|  | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     lfs_ssize_t res; | ||||
|     while (true) { | ||||
|         res = lfs_file_write(&lfs, &file[0], buffer, size); | ||||
|         res = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         if (res < 0) { | ||||
|             break; | ||||
|         } | ||||
| @@ -136,45 +139,45 @@ tests/test.py << TEST | ||||
|     } | ||||
|     res => LFS_ERR_NOSPC; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); | ||||
|     size = strlen("exhaustion"); | ||||
|     lfs_file_size(&lfs, &file[0]) => size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); | ||||
|     lfs_size_t size = strlen("exhaustion"); | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "exhaustion", size) => 0; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Exhaustion wraparound test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "padding", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("buffering"); | ||||
|     lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs_size_t size = strlen("buffering"); | ||||
|     memcpy(buffer, "buffering", size); | ||||
|     for (int i = 0; i < $SIZE; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_remove(&lfs, "padding") => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("exhaustion"); | ||||
|     memcpy(buffer, "exhaustion", size); | ||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_sync(&lfs, &file[0]) => 0; | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|  | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     lfs_ssize_t res; | ||||
|     while (true) { | ||||
|         res = lfs_file_write(&lfs, &file[0], buffer, size); | ||||
|         res = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         if (res < 0) { | ||||
|             break; | ||||
|         } | ||||
| @@ -183,246 +186,299 @@ tests/test.py << TEST | ||||
|     } | ||||
|     res => LFS_ERR_NOSPC; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY); | ||||
|     size = strlen("exhaustion"); | ||||
|     lfs_file_size(&lfs, &file[0]) => size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); | ||||
|     lfs_size_t size = strlen("exhaustion"); | ||||
|     lfs_file_size(&lfs, &file) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "exhaustion", size) => 0; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Dir exhaustion test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     // find out max file size | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||
|     lfs_size_t size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_count-6)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     int count = 0; | ||||
|     int err; | ||||
|     while (true) { | ||||
|         err = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         if (err < 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         count += 1; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     err => LFS_ERR_NOSPC; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||
|  | ||||
|     // see if dir fits with max file size | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     for (int i = 0; i < count; i++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||
|     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     // see if dir fits with > max file size | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     for (int i = 0; i < count+1; i++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Chained dir exhaustion test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_count-24)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     // find out max file size | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_mkdir(&lfs, path) => 0; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_size_t size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     int count = 0; | ||||
|     int err; | ||||
|     while (true) { | ||||
|         err = lfs_file_write(&lfs, &file, buffer, size); | ||||
|         if (err < 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|     for (int i = 0; i < 9; i++) { | ||||
|         sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||
|         count += 1; | ||||
|     } | ||||
|     err => LFS_ERR_NOSPC; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_remove(&lfs, "exhaustiondir") => 0; | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_remove(&lfs, path) => 0; | ||||
|     } | ||||
|  | ||||
|     // see that chained dir fails | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     for (int i = 0; i < count+1; i++) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||
|         lfs_mkdir(&lfs, path) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; | ||||
|  | ||||
|     lfs_remove(&lfs, "exhaustion") => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_count-26)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     // shorten file to try a second chained dir | ||||
|     while (true) { | ||||
|         err = lfs_mkdir(&lfs, "exhaustiondir"); | ||||
|         if (err != LFS_ERR_NOSPC) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         lfs_ssize_t filesize = lfs_file_size(&lfs, &file); | ||||
|         filesize > 0 => true; | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file, filesize - size) => 0; | ||||
|         lfs_file_sync(&lfs, &file) => 0; | ||||
|     } | ||||
|     err => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "exhaustiondir") => 0; | ||||
|     lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Split dir test ---" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // create one block whole for half a directory | ||||
|     lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_write(&lfs, &file[0], (void*)"hi", 2) => 2; | ||||
|     lfs_file_close(&lfs, &file[0]) => 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) { | ||||
|         memcpy(&buffer[i], "hi", 2); | ||||
|     } | ||||
|     lfs_file_write(&lfs, &file, buffer, cfg.block_size) => cfg.block_size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); | ||||
|     lfs_size_t size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < (cfg.block_count-6)*(cfg.block_size-8); | ||||
|             i < (cfg.block_count-4)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // open hole | ||||
|     lfs_remove(&lfs, "bump") => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "splitdir") => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "splitdir/bump", | ||||
|     lfs_file_open(&lfs, &file, "splitdir/bump", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => LFS_ERR_NOSPC; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     for (lfs_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; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Outdated lookahead test ---" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // fill completely with two files | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion1", | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     lfs_size_t size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); | ||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion2", | ||||
|     lfs_file_open(&lfs, &file, "exhaustion2", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); | ||||
|             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // rewrite one file | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion1", | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_sync(&lfs, &file[0]) => 0; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); | ||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     // rewrite second file, this requires lookahead does not | ||||
|     // use old population | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion2", | ||||
|     lfs_file_open(&lfs, &file, "exhaustion2", | ||||
|             LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_sync(&lfs, &file[0]) => 0; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); | ||||
|             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Outdated lookahead and split dir test ---" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // fill completely with two files | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion1", | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     lfs_size_t size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < ((cfg.block_count-4)/2)*(cfg.block_size-8); | ||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion2", | ||||
|     lfs_file_open(&lfs, &file, "exhaustion2", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8); | ||||
|             i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     // remount to force reset of lookahead | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // rewrite one file with a hole of one block | ||||
|     lfs_file_open(&lfs, &file[0], "exhaustion1", | ||||
|     lfs_file_open(&lfs, &file, "exhaustion1", | ||||
|             LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_sync(&lfs, &file[0]) => 0; | ||||
|     lfs_file_sync(&lfs, &file) => 0; | ||||
|     size = strlen("blahblahblahblah"); | ||||
|     memcpy(buffer, "blahblahblahblah", size); | ||||
|     for (lfs_size_t i = 0; | ||||
|             i < ((cfg.block_count-4)/2 - 1)*(cfg.block_size-8); | ||||
|             i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8); | ||||
|             i += size) { | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     // try to allocate a directory, should fail! | ||||
|     lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC; | ||||
|  | ||||
|     // file should not fail | ||||
|     lfs_file_open(&lfs, &file[0], "notasplit", | ||||
|     lfs_file_open(&lfs, &file, "notasplit", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_write(&lfs, &file[0], "hi", 2) => 2; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_write(&lfs, &file, "hi", 2) => 2; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
							
								
								
									
										294
									
								
								tests/test_attrs.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										294
									
								
								tests/test_attrs.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,294 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Attr tests ===" | ||||
| rm -rf blocks | ||||
| scripts/test.py << TEST | ||||
|     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; | ||||
| TEST | ||||
|  | ||||
| echo "--- Set/get attribute ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &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; | ||||
|     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"); | ||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Set/get root attribute ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &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; | ||||
|     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"); | ||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Set/get file attribute ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     struct lfs_attr attrs1[] = { | ||||
|         {'A', buffer,    4}, | ||||
|         {'B', buffer+4,  6}, | ||||
|         {'C', buffer+10, 5}, | ||||
|     }; | ||||
|     struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||
|  | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     memcpy(buffer,    "aaaa",   4); | ||||
|     memcpy(buffer+4,  "bbbbbb", 6); | ||||
|     memcpy(buffer+10, "ccccc",  5); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     memset(buffer, 0, 15); | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     memcmp(buffer,    "aaaa",   4) => 0; | ||||
|     memcmp(buffer+4,  "bbbbbb", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",  5) => 0; | ||||
|  | ||||
|     attrs1[1].size = 0; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     memset(buffer, 0, 15); | ||||
|     attrs1[1].size = 6; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     memcmp(buffer,    "aaaa",         4) => 0; | ||||
|     memcmp(buffer+4,  "\0\0\0\0\0\0", 6) => 0; | ||||
|     memcmp(buffer+10, "ccccc",        5) => 0; | ||||
|  | ||||
|     attrs1[1].size = 6; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; | ||||
|     memcpy(buffer+4,  "dddddd", 6); | ||||
|     lfs_file_close(&lfs, &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; | ||||
|     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; | ||||
|     memcpy(buffer+4,  "eee", 3); | ||||
|     lfs_file_close(&lfs, &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; | ||||
|     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; | ||||
|  | ||||
|     struct lfs_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; | ||||
|     memcpy(buffer+4,  "fffffffff", 9); | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     attrs1[0].size = 4; | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     struct lfs_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_RDONLY, &cfg2) => 0; | ||||
|     lfs_file_close(&lfs, &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"); | ||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||
|     lfs_file_close(&lfs, &file); | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Deferred file attributes ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     memset(buffer, 0, sizeof(buffer)); | ||||
|     struct lfs_attr attrs1[] = { | ||||
|         {'B', "gggg", 4}, | ||||
|         {'C', "",     0}, | ||||
|         {'D', "hhhh", 4}, | ||||
|     }; | ||||
|     struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; | ||||
|  | ||||
|     lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_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; | ||||
|     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; | ||||
|     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; | ||||
| TEST | ||||
|  | ||||
| scripts/results.py | ||||
| @@ -1,5 +1,7 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Corrupt tests ===" | ||||
|  | ||||
| @@ -7,7 +9,7 @@ NAMEMULT=64 | ||||
| FILEMULT=1 | ||||
|  | ||||
| lfs_mktree() { | ||||
| tests/test.py ${1:-} << TEST | ||||
| scripts/test.py ${1:-} << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
| @@ -23,22 +25,22 @@ tests/test.py ${1:-} << TEST | ||||
|             buffer[j+$NAMEMULT+1] = '0'+i; | ||||
|         } | ||||
|         buffer[2*$NAMEMULT+1] = '\0'; | ||||
|         lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||
|         lfs_file_open(&lfs, &file, (char*)buffer, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|          | ||||
|         size = $NAMEMULT; | ||||
|         lfs_size_t size = $NAMEMULT; | ||||
|         for (int j = 0; j < i*$FILEMULT; j++) { | ||||
|             lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|             lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| } | ||||
|  | ||||
| lfs_chktree() { | ||||
| tests/test.py ${1:-} << TEST | ||||
| scripts/test.py ${1:-} << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int i = 1; i < 10; i++) { | ||||
|         for (int j = 0; j < $NAMEMULT; j++) { | ||||
| @@ -53,15 +55,16 @@ tests/test.py ${1:-} << TEST | ||||
|             buffer[j+$NAMEMULT+1] = '0'+i; | ||||
|         } | ||||
|         buffer[2*$NAMEMULT+1] = '\0'; | ||||
|         lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; | ||||
|          | ||||
|         size = $NAMEMULT; | ||||
|         lfs_size_t size = $NAMEMULT; | ||||
|         for (int j = 0; j < i*$FILEMULT; j++) { | ||||
|             lfs_file_read(&lfs, &file[0], rbuffer, size) => size; | ||||
|             uint8_t rbuffer[1024]; | ||||
|             lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|             memcmp(buffer, rbuffer, size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| @@ -71,24 +74,25 @@ echo "--- Sanity check ---" | ||||
| rm -rf blocks | ||||
| lfs_mktree | ||||
| lfs_chktree | ||||
| BLOCKS="$(ls blocks | grep -vw '[01]')" | ||||
|  | ||||
| echo "--- Block corruption ---" | ||||
| for i in {0..33} | ||||
| for b in $BLOCKS | ||||
| do  | ||||
|     rm -rf blocks | ||||
|     mkdir blocks | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
|     ln -s /dev/zero blocks/$b | ||||
|     lfs_mktree | ||||
|     lfs_chktree | ||||
| done | ||||
|  | ||||
| echo "--- Block persistance ---" | ||||
| for i in {0..33} | ||||
| for b in $BLOCKS | ||||
| do  | ||||
|     rm -rf blocks | ||||
|     mkdir blocks | ||||
|     lfs_mktree | ||||
|     chmod a-w blocks/$(printf '%x' $i) | ||||
|     chmod a-w blocks/$b || true | ||||
|     lfs_mktree | ||||
|     lfs_chktree | ||||
| done | ||||
| @@ -96,7 +100,7 @@ done | ||||
| echo "--- Big region corruption ---" | ||||
| rm -rf blocks | ||||
| mkdir blocks | ||||
| for i in {2..255} | ||||
| for i in {2..512} | ||||
| do | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
| done | ||||
| @@ -106,12 +110,11 @@ lfs_chktree | ||||
| echo "--- Alternating corruption ---" | ||||
| rm -rf blocks | ||||
| mkdir blocks | ||||
| for i in {2..511..2} | ||||
| for i in {2..1024..2} | ||||
| do | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
| done | ||||
| lfs_mktree | ||||
| lfs_chktree | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,188 +1,191 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Directory tests ===" | ||||
|  | ||||
| LARGESIZE=128 | ||||
|  | ||||
| echo "=== Directory tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Root directory ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Directory creation ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "potato") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- File creation ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_open(&lfs, &file, "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Directory iteration ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "potato") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "burito") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "potato") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Directory failures ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST; | ||||
|     lfs_dir_open(&lfs, &dir[0], "tomato") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir[0], "burito") => LFS_ERR_NOTDIR; | ||||
|     lfs_file_open(&lfs, &file[0], "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT; | ||||
|     lfs_file_open(&lfs, &file[0], "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR; | ||||
|     lfs_dir_open(&lfs, &dir, "tomato") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "burito") => LFS_ERR_NOTDIR; | ||||
|     lfs_file_open(&lfs, &file, "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT; | ||||
|     lfs_file_open(&lfs, &file, "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Nested directories ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "potato/baked") => 0; | ||||
|     lfs_mkdir(&lfs, "potato/sweet") => 0; | ||||
|     lfs_mkdir(&lfs, "potato/fried") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "potato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "potato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "baked") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "fried") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block directory ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "cactus") => 0; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "cactus/test%d", i); | ||||
|         lfs_mkdir(&lfs, (char*)buffer) => 0; | ||||
|         sprintf(path, "cactus/test%03d", i); | ||||
|         lfs_mkdir(&lfs, path) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "cactus") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "cactus") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "test%d", i); | ||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|         strcmp(info.name, (char*)buffer) => 0; | ||||
|         sprintf(path, "test%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|         info.type => LFS_TYPE_DIR; | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Directory remove ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "potato") => LFS_ERR_NOTEMPTY; | ||||
|     lfs_remove(&lfs, "potato/sweet") => 0; | ||||
|     lfs_remove(&lfs, "potato/baked") => 0; | ||||
|     lfs_remove(&lfs, "potato/fried") => 0; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "potato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "potato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "potato") => 0; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "burito") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "cactus") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "burito") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "cactus") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Directory rename ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "coldpotato") => 0; | ||||
|     lfs_mkdir(&lfs, "coldpotato/baked") => 0; | ||||
| @@ -190,34 +193,34 @@ tests/test.py << TEST | ||||
|     lfs_mkdir(&lfs, "coldpotato/fried") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "coldpotato", "hotpotato") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "hotpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "baked") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "fried") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "warmpotato") => 0; | ||||
|     lfs_mkdir(&lfs, "warmpotato/mushy") => 0; | ||||
| @@ -228,29 +231,29 @@ tests/test.py << TEST | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "warmpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "baked") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "fried") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "coldpotato") => 0; | ||||
|     lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0; | ||||
| @@ -260,166 +263,227 @@ tests/test.py << TEST | ||||
|     lfs_remove(&lfs, "warmpotato") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "coldpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "baked") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "fried") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "sweet") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Recursive remove ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "coldpotato") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|  | ||||
|     while (true) { | ||||
|         int err = lfs_dir_read(&lfs, &dir[0], &info); | ||||
|         int err = lfs_dir_read(&lfs, &dir, &info); | ||||
|         err >= 0 => 1; | ||||
|         if (err == 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         strcpy((char*)buffer, "coldpotato/"); | ||||
|         strcat((char*)buffer, info.name); | ||||
|         lfs_remove(&lfs, (char*)buffer) => 0; | ||||
|         strcpy(path, "coldpotato/"); | ||||
|         strcat(path, info.name); | ||||
|         lfs_remove(&lfs, path) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_remove(&lfs, "coldpotato") => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "burito") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "cactus") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block rename ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         char oldpath[1024]; | ||||
|         char newpath[1024]; | ||||
|         sprintf(oldpath, "cactus/test%03d", i); | ||||
|         sprintf(newpath, "cactus/tedd%03d", i); | ||||
|         lfs_rename(&lfs, oldpath, newpath) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "cactus") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf(path, "tedd%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|         info.type => LFS_TYPE_DIR; | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block remove ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY; | ||||
|  | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "cactus/test%d", i); | ||||
|         lfs_remove(&lfs, (char*)buffer) => 0; | ||||
|         sprintf(path, "cactus/tedd%03d", i); | ||||
|         lfs_remove(&lfs, path) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_remove(&lfs, "cactus") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "burito") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block directory with files ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "prickly-pear") => 0; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "prickly-pear/test%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|         size = 6; | ||||
|         memcpy(wbuffer, "Hello", size); | ||||
|         lfs_file_write(&lfs, &file[0], wbuffer, size) => size; | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         sprintf(path, "prickly-pear/test%03d", i); | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|         lfs_size_t size = 6; | ||||
|         memcpy(buffer, "Hello", size); | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "prickly-pear") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "prickly-pear") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "test%d", i); | ||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|         strcmp(info.name, (char*)buffer) => 0; | ||||
|         sprintf(path, "test%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|         info.type => LFS_TYPE_REG; | ||||
|         info.size => 6; | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block rename with files ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         char oldpath[1024]; | ||||
|         char newpath[1024]; | ||||
|         sprintf(oldpath, "prickly-pear/test%03d", i); | ||||
|         sprintf(newpath, "prickly-pear/tedd%03d", i); | ||||
|         lfs_rename(&lfs, oldpath, newpath) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "prickly-pear") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf(path, "tedd%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|         info.type => LFS_TYPE_REG; | ||||
|         info.size => 6; | ||||
|     } | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Multi-block remove with files ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; | ||||
|  | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "prickly-pear/test%d", i); | ||||
|         lfs_remove(&lfs, (char*)buffer) => 0; | ||||
|         sprintf(path, "prickly-pear/tedd%03d", i); | ||||
|         lfs_remove(&lfs, path) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_remove(&lfs, "prickly-pear") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "burito") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
							
								
								
									
										251
									
								
								tests/test_entries.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										251
									
								
								tests/test_entries.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Entry tests ===" | ||||
|  | ||||
| # Note: These tests are intended for 512 byte inline size at different | ||||
| # inline sizes they should still pass, but won't be testing anything | ||||
|  | ||||
| rm -rf blocks | ||||
| function read_file { | ||||
| cat << TEST | ||||
|  | ||||
|     size = $2; | ||||
|     lfs_file_open(&lfs, &file, "$1", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
| TEST | ||||
| } | ||||
|  | ||||
| function write_file { | ||||
| cat << TEST | ||||
|  | ||||
|     size = $2; | ||||
|     lfs_file_open(&lfs, &file, "$1", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
| TEST | ||||
| } | ||||
|  | ||||
| echo "--- Entry grow test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     $(write_file "hi0" 20) | ||||
|     $(write_file "hi1" 20) | ||||
|     $(write_file "hi2" 20) | ||||
|     $(write_file "hi3" 20) | ||||
|  | ||||
|     $(read_file "hi1" 20) | ||||
|     $(write_file "hi1" 200) | ||||
|  | ||||
|     $(read_file "hi0" 20) | ||||
|     $(read_file "hi1" 200) | ||||
|     $(read_file "hi2" 20) | ||||
|     $(read_file "hi3" 20) | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Entry shrink test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     $(write_file "hi0" 20) | ||||
|     $(write_file "hi1" 200) | ||||
|     $(write_file "hi2" 20) | ||||
|     $(write_file "hi3" 20) | ||||
|  | ||||
|     $(read_file "hi1" 200) | ||||
|     $(write_file "hi1" 20) | ||||
|  | ||||
|     $(read_file "hi0" 20) | ||||
|     $(read_file "hi1" 20) | ||||
|     $(read_file "hi2" 20) | ||||
|     $(read_file "hi3" 20) | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Entry spill test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     $(write_file "hi0" 200) | ||||
|     $(write_file "hi1" 200) | ||||
|     $(write_file "hi2" 200) | ||||
|     $(write_file "hi3" 200) | ||||
|  | ||||
|     $(read_file "hi0" 200) | ||||
|     $(read_file "hi1" 200) | ||||
|     $(read_file "hi2" 200) | ||||
|     $(read_file "hi3" 200) | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Entry push spill test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     $(write_file "hi0" 200) | ||||
|     $(write_file "hi1" 20) | ||||
|     $(write_file "hi2" 200) | ||||
|     $(write_file "hi3" 200) | ||||
|  | ||||
|     $(read_file "hi1" 20) | ||||
|     $(write_file "hi1" 200) | ||||
|  | ||||
|     $(read_file "hi0" 200) | ||||
|     $(read_file "hi1" 200) | ||||
|     $(read_file "hi2" 200) | ||||
|     $(read_file "hi3" 200) | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Entry push spill two test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     $(write_file "hi0" 200) | ||||
|     $(write_file "hi1" 20) | ||||
|     $(write_file "hi2" 200) | ||||
|     $(write_file "hi3" 200) | ||||
|     $(write_file "hi4" 200) | ||||
|  | ||||
|     $(read_file "hi1" 20) | ||||
|     $(write_file "hi1" 200) | ||||
|  | ||||
|     $(read_file "hi0" 200) | ||||
|     $(read_file "hi1" 200) | ||||
|     $(read_file "hi2" 200) | ||||
|     $(read_file "hi3" 200) | ||||
|     $(read_file "hi4" 200) | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Entry drop test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     uint8_t wbuffer[1024]; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     $(write_file "hi0" 200) | ||||
|     $(write_file "hi1" 200) | ||||
|     $(write_file "hi2" 200) | ||||
|     $(write_file "hi3" 200) | ||||
|  | ||||
|     lfs_remove(&lfs, "hi1") => 0; | ||||
|     lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT; | ||||
|     $(read_file "hi0" 200) | ||||
|     $(read_file "hi2" 200) | ||||
|     $(read_file "hi3" 200) | ||||
|  | ||||
|     lfs_remove(&lfs, "hi2") => 0; | ||||
|     lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT; | ||||
|     $(read_file "hi0" 200) | ||||
|     $(read_file "hi3" 200) | ||||
|  | ||||
|     lfs_remove(&lfs, "hi3") => 0; | ||||
|     lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT; | ||||
|     $(read_file "hi0" 200) | ||||
|  | ||||
|     lfs_remove(&lfs, "hi0") => 0; | ||||
|     lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Create too big ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     memset(path, 'm', 200); | ||||
|     path[200] = '\0'; | ||||
|  | ||||
|     lfs_size_t size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_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; | ||||
|  | ||||
|     size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Resize too big ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     memset(path, 'm', 200); | ||||
|     path[200] = '\0'; | ||||
|  | ||||
|     lfs_size_t size = 40; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_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; | ||||
|  | ||||
|     size = 40; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|     memset(wbuffer, 'c', size); | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     size = 400; | ||||
|     lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| scripts/results.py | ||||
| @@ -1,55 +1,60 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== File tests ===" | ||||
|  | ||||
| SMALLSIZE=32 | ||||
| MEDIUMSIZE=8192 | ||||
| LARGESIZE=262144 | ||||
|  | ||||
| echo "=== File tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Simple file test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     size = strlen("Hello World!\n"); | ||||
|     lfs_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_size_t size = strlen("Hello World!\n"); | ||||
|     uint8_t wbuffer[1024]; | ||||
|     memcpy(wbuffer, "Hello World!\n", size); | ||||
|     lfs_file_write(&lfs, &file[0], wbuffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; | ||||
|     size = strlen("Hello World!\n"); | ||||
|     lfs_file_read(&lfs, &file[0], rbuffer, size) => size; | ||||
|     uint8_t rbuffer[1024]; | ||||
|     lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|     memcmp(rbuffer, wbuffer, size) => 0; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| w_test() { | ||||
| tests/test.py << TEST | ||||
| scripts/test.py ${4:-} << TEST | ||||
|     lfs_size_t size = $1; | ||||
|     lfs_size_t chunk = 31; | ||||
|     srand(0); | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "$2", | ||||
|     lfs_file_open(&lfs, &file, "$2", | ||||
|         ${3:-LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC}) => 0; | ||||
|     for (lfs_size_t i = 0; i < size; i += chunk) { | ||||
|         chunk = (chunk < size - i) ? chunk : size - i; | ||||
|         for (lfs_size_t b = 0; b < chunk; b++) { | ||||
|             buffer[b] = rand() & 0xff; | ||||
|         } | ||||
|         lfs_file_write(&lfs, &file[0], buffer, chunk) => chunk; | ||||
|         lfs_file_write(&lfs, &file, buffer, chunk) => chunk; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| } | ||||
|  | ||||
| r_test() { | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_size_t size = $1; | ||||
|     lfs_size_t chunk = 29; | ||||
|     srand(0); | ||||
| @@ -57,15 +62,15 @@ tests/test.py << TEST | ||||
|     lfs_stat(&lfs, "$2", &info) => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => size; | ||||
|     lfs_file_open(&lfs, &file[0], "$2", ${3:-LFS_O_RDONLY}) => 0; | ||||
|     lfs_file_open(&lfs, &file, "$2", ${3:-LFS_O_RDONLY}) => 0; | ||||
|     for (lfs_size_t i = 0; i < size; i += chunk) { | ||||
|         chunk = (chunk < size - i) ? chunk : size - i; | ||||
|         lfs_file_read(&lfs, &file[0], buffer, chunk) => chunk; | ||||
|         lfs_file_read(&lfs, &file, buffer, chunk) => chunk; | ||||
|         for (lfs_size_t b = 0; b < chunk && i+b < size; b++) { | ||||
|             buffer[b] => rand() & 0xff; | ||||
|         } | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| } | ||||
| @@ -105,54 +110,112 @@ r_test $LARGESIZE largeavacado | ||||
| r_test 0 noavacado | ||||
|  | ||||
| echo "--- Dir check ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => strlen("Hello World!\n"); | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "smallavacado") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => $SMALLSIZE; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "mediumavacado") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => $MEDIUMSIZE; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "largeavacado") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => $LARGESIZE; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "mediumavacado") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => $MEDIUMSIZE; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "noavacado") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "smallavacado") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => $SMALLSIZE; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Many file test ---" | ||||
| tests/test.py << TEST | ||||
| echo "--- Many files test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
|     // Create 300 files of 6 bytes | ||||
| scripts/test.py << TEST | ||||
|     // Create 300 files of 7 bytes | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "directory") => 0; | ||||
|     for (unsigned i = 0; i < 300; i++) { | ||||
|         snprintf((char*)buffer, sizeof(buffer), "file_%03d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|         size = 6; | ||||
|         memcpy(wbuffer, "Hello", size); | ||||
|         lfs_file_write(&lfs, &file[0], wbuffer, size) => size; | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         sprintf(path, "file_%03d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_size_t size = 7; | ||||
|         uint8_t wbuffer[1024]; | ||||
|         uint8_t rbuffer[1024]; | ||||
|         snprintf((char*)wbuffer, size, "Hi %03d", i); | ||||
|         lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|         lfs_file_rewind(&lfs, &file) => 0; | ||||
|         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|         memcmp(wbuffer, rbuffer, size) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| echo "--- Many files with flush test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     // Create 300 files of 7 bytes | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (unsigned i = 0; i < 300; i++) { | ||||
|         sprintf(path, "file_%03d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_size_t size = 7; | ||||
|         uint8_t wbuffer[1024]; | ||||
|         uint8_t rbuffer[1024]; | ||||
|         snprintf((char*)wbuffer, size, "Hi %03d", i); | ||||
|         lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|         memcmp(wbuffer, rbuffer, size) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Many files with power cycle test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     // Create 300 files of 7 bytes | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (unsigned i = 0; i < 300; i++) { | ||||
|         sprintf(path, "file_%03d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_size_t size = 7; | ||||
|         uint8_t wbuffer[1024]; | ||||
|         uint8_t rbuffer[1024]; | ||||
|         snprintf((char*)wbuffer, size, "Hi %03d", i); | ||||
|         lfs_file_write(&lfs, &file, wbuffer, size) => size; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|         lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|         lfs_mount(&lfs, &cfg) => 0; | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_read(&lfs, &file, rbuffer, size) => size; | ||||
|         memcmp(wbuffer, rbuffer, size) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,49 +1,51 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Formatting tests ===" | ||||
| rm -rf blocks | ||||
|  | ||||
| echo "--- Basic formatting ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Basic mounting ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Invalid superblocks ---" | ||||
| ln -f -s /dev/zero blocks/0 | ||||
| ln -f -s /dev/zero blocks/1 | ||||
| tests/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC; | ||||
| TEST | ||||
| rm blocks/0 blocks/1 | ||||
|  | ||||
| echo "--- Basic mounting ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Invalid mount ---" | ||||
| tests/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
| rm blocks/0 blocks/1 | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| TEST | ||||
|  | ||||
| echo "--- Valid corrupt mount ---" | ||||
| tests/test.py << TEST | ||||
| echo "--- Expanding superblock ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
| rm blocks/0 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     for (int i = 0; i < 100; i++) { | ||||
|         lfs_mkdir(&lfs, "dummy") => 0; | ||||
|         lfs_remove(&lfs, "dummy") => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "dummy") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,89 +1,93 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Interspersed tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Interspersed file test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "a", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &file[1], "b", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &file[2], "c", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &file[3], "d", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_t files[4]; | ||||
|     lfs_file_open(&lfs, &files[0], "a", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &files[1], "b", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &files[2], "c", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &files[3], "d", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], (const void*)"a", 1) => 1; | ||||
|         lfs_file_write(&lfs, &file[1], (const void*)"b", 1) => 1; | ||||
|         lfs_file_write(&lfs, &file[2], (const void*)"c", 1) => 1; | ||||
|         lfs_file_write(&lfs, &file[3], (const void*)"d", 1) => 1; | ||||
|         lfs_file_write(&lfs, &files[0], (const void*)"a", 1) => 1; | ||||
|         lfs_file_write(&lfs, &files[1], (const void*)"b", 1) => 1; | ||||
|         lfs_file_write(&lfs, &files[2], (const void*)"c", 1) => 1; | ||||
|         lfs_file_write(&lfs, &files[3], (const void*)"d", 1) => 1; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]); | ||||
|     lfs_file_close(&lfs, &file[1]); | ||||
|     lfs_file_close(&lfs, &file[2]); | ||||
|     lfs_file_close(&lfs, &file[3]); | ||||
|     lfs_file_close(&lfs, &files[0]); | ||||
|     lfs_file_close(&lfs, &files[1]); | ||||
|     lfs_file_close(&lfs, &files[2]); | ||||
|     lfs_file_close(&lfs, &files[3]); | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "a") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 10; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "b") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 10; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "c") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 10; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "d") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 10; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "a", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &file[1], "b", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &file[2], "c", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &file[3], "d", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[0], "a", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[1], "b", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[2], "c", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[3], "d", LFS_O_RDONLY) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, 1) => 1; | ||||
|         lfs_file_read(&lfs, &files[0], buffer, 1) => 1; | ||||
|         buffer[0] => 'a'; | ||||
|         lfs_file_read(&lfs, &file[1], buffer, 1) => 1; | ||||
|         lfs_file_read(&lfs, &files[1], buffer, 1) => 1; | ||||
|         buffer[0] => 'b'; | ||||
|         lfs_file_read(&lfs, &file[2], buffer, 1) => 1; | ||||
|         lfs_file_read(&lfs, &files[2], buffer, 1) => 1; | ||||
|         buffer[0] => 'c'; | ||||
|         lfs_file_read(&lfs, &file[3], buffer, 1) => 1; | ||||
|         lfs_file_read(&lfs, &files[3], buffer, 1) => 1; | ||||
|         buffer[0] => 'd'; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]); | ||||
|     lfs_file_close(&lfs, &file[1]); | ||||
|     lfs_file_close(&lfs, &file[2]); | ||||
|     lfs_file_close(&lfs, &file[3]); | ||||
|     lfs_file_close(&lfs, &files[0]); | ||||
|     lfs_file_close(&lfs, &files[1]); | ||||
|     lfs_file_close(&lfs, &files[2]); | ||||
|     lfs_file_close(&lfs, &files[3]); | ||||
|      | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Interspersed remove file test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_t files[4]; | ||||
|     lfs_file_open(&lfs, &files[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 5; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; | ||||
|         lfs_file_write(&lfs, &files[0], (const void*)"e", 1) => 1; | ||||
|     } | ||||
|  | ||||
|     lfs_remove(&lfs, "a") => 0; | ||||
| @@ -92,95 +96,95 @@ tests/test.py << TEST | ||||
|     lfs_remove(&lfs, "d") => 0; | ||||
|  | ||||
|     for (int i = 0; i < 5; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; | ||||
|         lfs_file_write(&lfs, &files[0], (const void*)"e", 1) => 1; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]); | ||||
|     lfs_file_close(&lfs, &files[0]); | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "e") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 10; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[0], "e", LFS_O_RDONLY) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, 1) => 1; | ||||
|         lfs_file_read(&lfs, &files[0], buffer, 1) => 1; | ||||
|         buffer[0] => 'e'; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]); | ||||
|     lfs_file_close(&lfs, &files[0]); | ||||
|      | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Remove inconveniently test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_open(&lfs, &file[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &file[2], "g", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_t files[4]; | ||||
|     lfs_file_open(&lfs, &files[0], "e", LFS_O_WRONLY | LFS_O_TRUNC) => 0; | ||||
|     lfs_file_open(&lfs, &files[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_open(&lfs, &files[2], "g", LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 5; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; | ||||
|         lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1; | ||||
|         lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1; | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     lfs_remove(&lfs, "f") => 0; | ||||
|  | ||||
|     for (int i = 0; i < 5; i++) { | ||||
|         lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1; | ||||
|         lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1; | ||||
|         lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1; | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]); | ||||
|     lfs_file_close(&lfs, &file[1]); | ||||
|     lfs_file_close(&lfs, &file[2]); | ||||
|     lfs_file_close(&lfs, &files[0]); | ||||
|     lfs_file_close(&lfs, &files[1]); | ||||
|     lfs_file_close(&lfs, &files[2]); | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "/") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "e") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 10; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "g") => 0; | ||||
|     info.type => LFS_TYPE_REG; | ||||
|     info.size => 10; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &file[1], "g", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[0], "e", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &files[1], "g", LFS_O_RDONLY) => 0; | ||||
|  | ||||
|     for (int i = 0; i < 10; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, 1) => 1; | ||||
|         lfs_file_read(&lfs, &files[0], buffer, 1) => 1; | ||||
|         buffer[0] => 'e'; | ||||
|         lfs_file_read(&lfs, &file[1], buffer, 1) => 1; | ||||
|         lfs_file_read(&lfs, &files[1], buffer, 1) => 1; | ||||
|         buffer[0] => 'g'; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]); | ||||
|     lfs_file_close(&lfs, &file[1]); | ||||
|     lfs_file_close(&lfs, &files[0]); | ||||
|     lfs_file_close(&lfs, &files[1]); | ||||
|      | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Move tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
| @@ -17,220 +19,315 @@ tests/test.py << TEST | ||||
|     lfs_mkdir(&lfs, "a/hi/bonjour") => 0; | ||||
|     lfs_mkdir(&lfs, "a/hi/ohayo") => 0; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|     lfs_file_write(&lfs, &file[0], "hola\n", 5) => 5; | ||||
|     lfs_file_write(&lfs, &file[0], "bonjour\n", 8) => 8; | ||||
|     lfs_file_write(&lfs, &file[0], "ohayo\n", 6) => 6; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; | ||||
|     lfs_file_write(&lfs, &file, "hola\n", 5) => 5; | ||||
|     lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8; | ||||
|     lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move file ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "a/hello", "b/hello") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "a") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "a") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move file corrupt source ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "b/hello", "c/hello") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/7 | ||||
| tests/test.py << TEST | ||||
| scripts/corrupt.py -n 1 | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move file corrupt source and dest ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/8 | ||||
| rm -v blocks/a | ||||
| tests/test.py << TEST | ||||
| scripts/corrupt.py -n 2 | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "d") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "d") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move file after corrupt ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "d") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move dir ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "a/hi", "b/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "a") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "a") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move dir corrupt source ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "b/hi", "c/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/7 | ||||
| tests/test.py << TEST | ||||
| scripts/corrupt.py -n 1 | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "b") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move dir corrupt source and dest ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| rm -v blocks/9 | ||||
| rm -v blocks/a | ||||
| tests/test.py << TEST | ||||
| scripts/corrupt.py -n 2 | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "d") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "d") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move dir after corrupt ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_dir_open(&lfs, &dir, "d") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move check ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir[0], "d/hi") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "c/hi") => LFS_ERR_NOENT; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "c/hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "d/hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, "hola") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "bonjour") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hola") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "ohayo") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir[0], "d/hello") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "a/hello") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "b/hello") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "c/hello") => LFS_ERR_NOENT; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file[0], "c/hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, 5) => 5; | ||||
|     lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, 5) => 5; | ||||
|     memcmp(buffer, "hola\n", 5) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, 8) => 8; | ||||
|     lfs_file_read(&lfs, &file, buffer, 8) => 8; | ||||
|     memcmp(buffer, "bonjour\n", 8) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, 6) => 6; | ||||
|     lfs_file_read(&lfs, &file, buffer, 6) => 6; | ||||
|     memcmp(buffer, "ohayo\n", 6) => 0; | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Move state stealing ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_remove(&lfs, "b") => 0; | ||||
|     lfs_remove(&lfs, "c") => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "b") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => LFS_ERR_NOENT; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "d/hi") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "bonjour") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "hola") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "ohayo") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "a/hello") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "b") => LFS_ERR_NOENT; | ||||
|     lfs_dir_open(&lfs, &dir, "c") => LFS_ERR_NOENT; | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, 5) => 5; | ||||
|     memcmp(buffer, "hola\n", 5) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, 8) => 8; | ||||
|     memcmp(buffer, "bonjour\n", 8) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, 6) => 6; | ||||
|     memcmp(buffer, "ohayo\n", 6) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,41 +1,46 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Orphan tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Orphan test ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "parent") => 0; | ||||
|     lfs_mkdir(&lfs, "parent/orphan") => 0; | ||||
|     lfs_mkdir(&lfs, "parent/child") => 0; | ||||
|     lfs_remove(&lfs, "parent/orphan") => 0; | ||||
| TEST | ||||
| # remove most recent file, this should be the update to the previous | ||||
| # corrupt most recent commit, this should be the update to the previous | ||||
| # linked-list entry and should orphan the child | ||||
| rm -v blocks/8 | ||||
| tests/test.py << TEST | ||||
| scripts/corrupt.py | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||
|     unsigned before = 0; | ||||
|     lfs_traverse(&lfs, test_count, &before) => 0; | ||||
|     test_log("before", before); | ||||
|  | ||||
|     lfs_deorphan(&lfs) => 0; | ||||
|  | ||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||
|     unsigned after = 0; | ||||
|     lfs_traverse(&lfs, test_count, &after) => 0; | ||||
|     test_log("after", after); | ||||
|     lfs_ssize_t before = lfs_fs_size(&lfs); | ||||
|     before => 8; | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||
|     lfs_ssize_t orphaned = lfs_fs_size(&lfs); | ||||
|     orphaned => 8; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "parent/otherchild") => 0; | ||||
|  | ||||
|     lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; | ||||
|     lfs_ssize_t deorphaned = lfs_fs_size(&lfs); | ||||
|     deorphaned => 8; | ||||
|  | ||||
|     int diff = before - after; | ||||
|     diff => 2; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,13 +1,15 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Path tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "tea") => 0; | ||||
|     lfs_mkdir(&lfs, "coffee") => 0; | ||||
| @@ -25,7 +27,7 @@ tests/test.py << TEST | ||||
| TEST | ||||
|  | ||||
| echo "--- Root path tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "tea/hottea", &info) => 0; | ||||
|     strcmp(info.name, "hottea") => 0; | ||||
| @@ -39,7 +41,7 @@ tests/test.py << TEST | ||||
| TEST | ||||
|  | ||||
| echo "--- Redundant slash path tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "/tea/hottea", &info) => 0; | ||||
|     strcmp(info.name, "hottea") => 0; | ||||
| @@ -55,7 +57,7 @@ tests/test.py << TEST | ||||
| TEST | ||||
|  | ||||
| echo "--- Dot path tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "./tea/hottea", &info) => 0; | ||||
|     strcmp(info.name, "hottea") => 0; | ||||
| @@ -73,7 +75,7 @@ tests/test.py << TEST | ||||
| TEST | ||||
|  | ||||
| echo "--- Dot dot path tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0; | ||||
|     strcmp(info.name, "hottea") => 0; | ||||
| @@ -91,7 +93,7 @@ tests/test.py << TEST | ||||
| TEST | ||||
|  | ||||
| echo "--- Trailing dot path tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "tea/hottea/", &info) => 0; | ||||
|     strcmp(info.name, "hottea") => 0; | ||||
| @@ -107,7 +109,7 @@ tests/test.py << TEST | ||||
| TEST | ||||
|  | ||||
| echo "--- Root dot dot path tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0; | ||||
|     strcmp(info.name, "hottea") => 0; | ||||
| @@ -119,25 +121,82 @@ tests/test.py << TEST | ||||
| TEST | ||||
|  | ||||
| echo "--- Root tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "/", &info) => 0; | ||||
|     info.type => LFS_TYPE_DIR; | ||||
|     strcmp(info.name, "/") => 0; | ||||
|  | ||||
|     lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST; | ||||
|     lfs_file_open(&lfs, &file[0], "/", LFS_O_WRONLY | LFS_O_CREAT) | ||||
|     lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY | LFS_O_CREAT) | ||||
|         => LFS_ERR_ISDIR; | ||||
|  | ||||
|     // more corner cases | ||||
|     lfs_remove(&lfs, "") => LFS_ERR_INVAL; | ||||
|     lfs_remove(&lfs, ".") => LFS_ERR_INVAL; | ||||
|     lfs_remove(&lfs, "..") => LFS_ERR_INVAL; | ||||
|     lfs_remove(&lfs, "/") => LFS_ERR_INVAL; | ||||
|     lfs_remove(&lfs, "//") => LFS_ERR_INVAL; | ||||
|     lfs_remove(&lfs, "./") => LFS_ERR_INVAL; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Sketchy path tests ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "dirt/ground") => LFS_ERR_NOENT; | ||||
|     lfs_mkdir(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| echo "--- Superblock conflict test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "littlefs") => 0; | ||||
|     lfs_remove(&lfs, "littlefs") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Max path test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     memset(path, 'w', LFS_NAME_MAX+1); | ||||
|     path[LFS_NAME_MAX+2] = '\0'; | ||||
|     lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG; | ||||
|  | ||||
|     memcpy(path, "coffee/", strlen("coffee/")); | ||||
|     memset(path+strlen("coffee/"), 'w', LFS_NAME_MAX+1); | ||||
|     path[strlen("coffee/")+LFS_NAME_MAX+2] = '\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; | ||||
| TEST | ||||
|  | ||||
| echo "--- Really big path test ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     memset(path, 'w', LFS_NAME_MAX); | ||||
|     path[LFS_NAME_MAX+1] = '\0'; | ||||
|     lfs_mkdir(&lfs, path) => 0; | ||||
|     lfs_remove(&lfs, path) => 0; | ||||
|     lfs_file_open(&lfs, &file, path, | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_remove(&lfs, path) => 0; | ||||
|  | ||||
|     memcpy(path, "coffee/", strlen("coffee/")); | ||||
|     memset(path+strlen("coffee/"), 'w', LFS_NAME_MAX); | ||||
|     path[strlen("coffee/")+LFS_NAME_MAX+1] = '\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; | ||||
| TEST | ||||
|  | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,361 +1,431 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Seek tests ===" | ||||
|  | ||||
| SMALLSIZE=4 | ||||
| MEDIUMSIZE=128 | ||||
| LARGESIZE=132 | ||||
|  | ||||
| echo "=== Seek tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "hello") => 0; | ||||
|     for (int i = 0; i < $LARGESIZE; i++) { | ||||
|         sprintf((char*)buffer, "hello/kitty%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (char*)buffer, | ||||
|         sprintf(path, "hello/kitty%03d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; | ||||
|  | ||||
|         size = strlen("kittycatcat"); | ||||
|         lfs_size_t size = strlen("kittycatcat"); | ||||
|         memcpy(buffer, "kittycatcat", size); | ||||
|         for (int j = 0; j < $LARGESIZE; j++) { | ||||
|             lfs_file_write(&lfs, &file[0], buffer, size); | ||||
|             lfs_file_write(&lfs, &file, buffer, size); | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Simple dir seek ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|  | ||||
|     lfs_soff_t pos; | ||||
|     int i; | ||||
|     for (i = 0; i < $SMALLSIZE; i++) { | ||||
|         sprintf((char*)buffer, "kitty%d", i); | ||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|         strcmp(info.name, (char*)buffer) => 0; | ||||
|         pos = lfs_dir_tell(&lfs, &dir[0]); | ||||
|         sprintf(path, "kitty%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|         pos = lfs_dir_tell(&lfs, &dir); | ||||
|     } | ||||
|     pos >= 0 => 1; | ||||
|  | ||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||
|     sprintf((char*)buffer, "kitty%d", i); | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, (char*)buffer) => 0; | ||||
|     lfs_dir_seek(&lfs, &dir, pos) => 0; | ||||
|     sprintf(path, "kitty%03d", i); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, path) => 0; | ||||
|  | ||||
|     lfs_dir_rewind(&lfs, &dir[0]) => 0; | ||||
|     sprintf((char*)buffer, "kitty%d", 0); | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_rewind(&lfs, &dir) => 0; | ||||
|     sprintf(path, "kitty%03d", 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, (char*)buffer) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, path) => 0; | ||||
|  | ||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||
|     sprintf((char*)buffer, "kitty%d", i); | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, (char*)buffer) => 0; | ||||
|     lfs_dir_seek(&lfs, &dir, pos) => 0; | ||||
|     sprintf(path, "kitty%03d", i); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, path) => 0; | ||||
|  | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Large dir seek ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_open(&lfs, &dir, "hello") => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|  | ||||
|     lfs_soff_t pos; | ||||
|     int i; | ||||
|     for (i = 0; i < $MEDIUMSIZE; i++) { | ||||
|         sprintf((char*)buffer, "kitty%d", i); | ||||
|         lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|         strcmp(info.name, (char*)buffer) => 0; | ||||
|         pos = lfs_dir_tell(&lfs, &dir[0]); | ||||
|         sprintf(path, "kitty%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|         strcmp(info.name, path) => 0; | ||||
|         pos = lfs_dir_tell(&lfs, &dir); | ||||
|     } | ||||
|     pos >= 0 => 1; | ||||
|  | ||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||
|     sprintf((char*)buffer, "kitty%d", i); | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, (char*)buffer) => 0; | ||||
|     lfs_dir_seek(&lfs, &dir, pos) => 0; | ||||
|     sprintf(path, "kitty%03d", i); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, path) => 0; | ||||
|  | ||||
|     lfs_dir_rewind(&lfs, &dir[0]) => 0; | ||||
|     sprintf((char*)buffer, "kitty%d", 0); | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_rewind(&lfs, &dir) => 0; | ||||
|     sprintf(path, "kitty%03d", 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, ".") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, "..") => 0; | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, (char*)buffer) => 0; | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, path) => 0; | ||||
|  | ||||
|     lfs_dir_seek(&lfs, &dir[0], pos) => 0; | ||||
|     sprintf((char*)buffer, "kitty%d", i); | ||||
|     lfs_dir_read(&lfs, &dir[0], &info) => 1; | ||||
|     strcmp(info.name, (char*)buffer) => 0; | ||||
|     lfs_dir_seek(&lfs, &dir, pos) => 0; | ||||
|     sprintf(path, "kitty%03d", i); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     strcmp(info.name, path) => 0; | ||||
|  | ||||
|     lfs_dir_close(&lfs, &dir[0]) => 0; | ||||
|     lfs_dir_close(&lfs, &dir) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Simple file seek ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDONLY) => 0; | ||||
|  | ||||
|     lfs_soff_t pos; | ||||
|     size = strlen("kittycatcat"); | ||||
|     lfs_size_t size = strlen("kittycatcat"); | ||||
|     for (int i = 0; i < $SMALLSIZE; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|         pos = lfs_file_tell(&lfs, &file[0]); | ||||
|         pos = lfs_file_tell(&lfs, &file); | ||||
|     } | ||||
|     pos >= 0 => 1; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_rewind(&lfs, &file[0]) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_rewind(&lfs, &file) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], size, LFS_SEEK_CUR) => 3*size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, size, LFS_SEEK_CUR) => 3*size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_CUR) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_size_t size = lfs_file_size(&lfs, &file[0]); | ||||
|     lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; | ||||
|     size = lfs_file_size(&lfs, &file); | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Large file seek ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDONLY) => 0; | ||||
|  | ||||
|     lfs_soff_t pos; | ||||
|     size = strlen("kittycatcat"); | ||||
|     lfs_size_t size = strlen("kittycatcat"); | ||||
|     for (int i = 0; i < $MEDIUMSIZE; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|         pos = lfs_file_tell(&lfs, &file[0]); | ||||
|         pos = lfs_file_tell(&lfs, &file); | ||||
|     } | ||||
|     pos >= 0 => 1; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_rewind(&lfs, &file[0]) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_rewind(&lfs, &file) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], size, LFS_SEEK_CUR) => 3*size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, size, LFS_SEEK_CUR) => 3*size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_CUR) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_size_t size = lfs_file_size(&lfs, &file[0]); | ||||
|     lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; | ||||
|     size = lfs_file_size(&lfs, &file); | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Simple file seek and write ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0; | ||||
|  | ||||
|     lfs_soff_t pos; | ||||
|     size = strlen("kittycatcat"); | ||||
|     lfs_size_t size = strlen("kittycatcat"); | ||||
|     for (int i = 0; i < $SMALLSIZE; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|         pos = lfs_file_tell(&lfs, &file[0]); | ||||
|         pos = lfs_file_tell(&lfs, &file); | ||||
|     } | ||||
|     pos >= 0 => 1; | ||||
|  | ||||
|     memcpy(buffer, "doggodogdog", size); | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "doggodogdog", size) => 0; | ||||
|  | ||||
|     lfs_file_rewind(&lfs, &file[0]) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_rewind(&lfs, &file) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "doggodogdog", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_size_t size = lfs_file_size(&lfs, &file[0]); | ||||
|     lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; | ||||
|     size = lfs_file_size(&lfs, &file); | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Large file seek and write ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0; | ||||
|  | ||||
|     lfs_soff_t pos; | ||||
|     size = strlen("kittycatcat"); | ||||
|     lfs_size_t size = strlen("kittycatcat"); | ||||
|     for (int i = 0; i < $MEDIUMSIZE; i++) { | ||||
|         lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         if (i != $SMALLSIZE) { | ||||
|             memcmp(buffer, "kittycatcat", size) => 0; | ||||
|         } | ||||
|         pos = lfs_file_tell(&lfs, &file[0]); | ||||
|         pos = lfs_file_tell(&lfs, &file); | ||||
|     } | ||||
|     pos >= 0 => 1; | ||||
|  | ||||
|     memcpy(buffer, "doggodogdog", size); | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "doggodogdog", size) => 0; | ||||
|  | ||||
|     lfs_file_rewind(&lfs, &file[0]) => 0; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_rewind(&lfs, &file) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "doggodogdog", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|     lfs_size_t size = lfs_file_size(&lfs, &file[0]); | ||||
|     lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size; | ||||
|     size = lfs_file_size(&lfs, &file); | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Boundary seek and write ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0; | ||||
|  | ||||
|     size = strlen("hedgehoghog"); | ||||
|     lfs_size_t size = strlen("hedgehoghog"); | ||||
|     const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { | ||||
|     for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { | ||||
|         lfs_soff_t off = offsets[i]; | ||||
|         memcpy(buffer, "hedgehoghog", size); | ||||
|         lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off; | ||||
|         lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off; | ||||
|         lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hedgehoghog", size) => 0; | ||||
|  | ||||
|         lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_SET) => 0; | ||||
|         lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "kittycatcat", size) => 0; | ||||
|  | ||||
|         lfs_file_sync(&lfs, &file[0]) => 0; | ||||
|         lfs_file_sync(&lfs, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Out-of-bounds seek ---" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0; | ||||
|  | ||||
|     size = strlen("kittycatcat"); | ||||
|     lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size; | ||||
|     lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size, | ||||
|     lfs_size_t size = strlen("kittycatcat"); | ||||
|     lfs_file_size(&lfs, &file) => $LARGESIZE*size; | ||||
|     lfs_file_seek(&lfs, &file, ($LARGESIZE+$SMALLSIZE)*size, | ||||
|             LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|  | ||||
|     memcpy(buffer, "porcupineee", size); | ||||
|     lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size, | ||||
|     lfs_file_seek(&lfs, &file, ($LARGESIZE+$SMALLSIZE)*size, | ||||
|             LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "porcupineee", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], $LARGESIZE*size, | ||||
|     lfs_file_seek(&lfs, &file, $LARGESIZE*size, | ||||
|             LFS_SEEK_SET) => $LARGESIZE*size; | ||||
|     lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|     memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+$SMALLSIZE)*size), | ||||
|     lfs_file_seek(&lfs, &file, -(($LARGESIZE+$SMALLSIZE)*size), | ||||
|             LFS_SEEK_CUR) => LFS_ERR_INVAL; | ||||
|     lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size; | ||||
|     lfs_file_tell(&lfs, &file) => ($LARGESIZE+1)*size; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+2*$SMALLSIZE)*size), | ||||
|     lfs_file_seek(&lfs, &file, -(($LARGESIZE+2*$SMALLSIZE)*size), | ||||
|             LFS_SEEK_END) => LFS_ERR_INVAL; | ||||
|     lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size; | ||||
|     lfs_file_tell(&lfs, &file) => ($LARGESIZE+1)*size; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| echo "--- Inline write and seek ---" | ||||
| for SIZE in $SMALLSIZE $MEDIUMSIZE $LARGESIZE | ||||
| do | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "hello/tinykitty$SIZE", | ||||
|             LFS_O_RDWR | LFS_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; | ||||
|     } | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $SIZE; | ||||
|     for (unsigned i = 0; i < $SIZE; i++) { | ||||
|         uint8_t c; | ||||
|         lfs_file_read(&lfs, &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; | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_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; | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; | ||||
|     lfs_file_tell(&lfs, &file) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $SIZE; | ||||
|     for (unsigned i = 0; i < $SIZE; i++) { | ||||
|         uint8_t c; | ||||
|         lfs_file_read(&lfs, &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; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| done | ||||
|  | ||||
| scripts/results.py | ||||
|   | ||||
| @@ -1,111 +1,258 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
| export TEST_FILE=$0 | ||||
| trap 'export TEST_LINE=$LINENO' DEBUG | ||||
|  | ||||
| echo "=== Truncate tests ===" | ||||
|  | ||||
| SMALLSIZE=32 | ||||
| MEDIUMSIZE=2048 | ||||
| LARGESIZE=8192 | ||||
|  | ||||
| echo "=== Truncate tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Simple truncate ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldynoop", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|  | ||||
|     strcpy((char*)buffer, "hair"); | ||||
|     lfs_size_t size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < $LARGESIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => $LARGESIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $LARGESIZE; | ||||
|  | ||||
|     lfs_file_truncate(&lfs, &file, $MEDIUMSIZE) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $MEDIUMSIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $MEDIUMSIZE; | ||||
|  | ||||
|     lfs_size_t size = strlen("hair"); | ||||
|     for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hair", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Truncate and read ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldyread", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|  | ||||
|     strcpy((char*)buffer, "hair"); | ||||
|     lfs_size_t size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < $LARGESIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => $LARGESIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $LARGESIZE; | ||||
|  | ||||
|     lfs_file_truncate(&lfs, &file, $MEDIUMSIZE) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $MEDIUMSIZE; | ||||
|  | ||||
|     lfs_size_t size = strlen("hair"); | ||||
|     for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hair", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $MEDIUMSIZE; | ||||
|  | ||||
|     lfs_size_t size = strlen("hair"); | ||||
|     for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "hair", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| echo "--- Truncate and write ---" | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldywrite", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|  | ||||
|     strcpy((char*)buffer, "hair"); | ||||
|     lfs_size_t size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < $LARGESIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => $LARGESIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDWR) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $LARGESIZE; | ||||
|  | ||||
|     lfs_file_truncate(&lfs, &file, $MEDIUMSIZE) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $MEDIUMSIZE; | ||||
|  | ||||
|     strcpy((char*)buffer, "bald"); | ||||
|     lfs_size_t size = strlen((char*)buffer); | ||||
|     for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) { | ||||
|         lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|     } | ||||
|     lfs_file_size(&lfs, &file) => $MEDIUMSIZE; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| scripts/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_size(&lfs, &file) => $MEDIUMSIZE; | ||||
|  | ||||
|     lfs_size_t size = strlen("bald"); | ||||
|     for (lfs_off_t j = 0; j < $MEDIUMSIZE; j += size) { | ||||
|         lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|         memcmp(buffer, "bald", size) => 0; | ||||
|     } | ||||
|     lfs_file_read(&lfs, &file, buffer, size) => 0; | ||||
|  | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
|  | ||||
| # More aggressive general truncation tests | ||||
| truncate_test() { | ||||
| STARTSIZES="$1" | ||||
| STARTSEEKS="$2" | ||||
| HOTSIZES="$3" | ||||
| COLDSIZES="$4" | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     static const lfs_off_t startsizes[] = {$STARTSIZES}; | ||||
|     static const lfs_off_t startseeks[] = {$STARTSEEKS}; | ||||
|     static const lfs_off_t hotsizes[]   = {$HOTSIZES}; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, | ||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf(path, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file, path, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|  | ||||
|         strcpy((char*)buffer, "hair"); | ||||
|         size = strlen((char*)buffer); | ||||
|         for (int j = 0; j < startsizes[i]; j += size) { | ||||
|             lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         lfs_size_t size = strlen((char*)buffer); | ||||
|         for (lfs_off_t j = 0; j < startsizes[i]; j += size) { | ||||
|             lfs_file_write(&lfs, &file, buffer, size) => size; | ||||
|         } | ||||
|         lfs_file_size(&lfs, &file[0]) => startsizes[i]; | ||||
|         lfs_file_size(&lfs, &file) => startsizes[i]; | ||||
|  | ||||
|         if (startseeks[i] != startsizes[i]) { | ||||
|             lfs_file_seek(&lfs, &file[0], | ||||
|             lfs_file_seek(&lfs, &file, | ||||
|                     startseeks[i], LFS_SEEK_SET) => startseeks[i]; | ||||
|         } | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file[0], hotsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => hotsizes[i]; | ||||
|         lfs_file_truncate(&lfs, &file, hotsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file) => hotsizes[i]; | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     static const lfs_off_t startsizes[] = {$STARTSIZES}; | ||||
|     static const lfs_off_t hotsizes[]   = {$HOTSIZES}; | ||||
|     static const lfs_off_t coldsizes[]  = {$COLDSIZES}; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => hotsizes[i]; | ||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf(path, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDWR) => 0; | ||||
|         lfs_file_size(&lfs, &file) => hotsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         int j = 0; | ||||
|         lfs_size_t size = strlen("hair"); | ||||
|         lfs_off_t j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "hair", size) => 0; | ||||
|         } | ||||
|  | ||||
|         for (; j < hotsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file[0], coldsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => coldsizes[i]; | ||||
|         lfs_file_truncate(&lfs, &file, coldsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file) => coldsizes[i]; | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
| scripts/test.py << TEST | ||||
|     static const lfs_off_t startsizes[] = {$STARTSIZES}; | ||||
|     static const lfs_off_t hotsizes[]   = {$HOTSIZES}; | ||||
|     static const lfs_off_t coldsizes[]  = {$COLDSIZES}; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => coldsizes[i]; | ||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf(path, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_size(&lfs, &file) => coldsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         int j = 0; | ||||
|         lfs_size_t size = strlen("hair"); | ||||
|         lfs_off_t j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; | ||||
|                 j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "hair", size) => 0; | ||||
|         } | ||||
|  | ||||
|         for (; j < coldsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             lfs_file_read(&lfs, &file, buffer, size) => size; | ||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| @@ -154,5 +301,4 @@ truncate_test \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
| scripts/results.py | ||||
|   | ||||
		Reference in New Issue
	
	Block a user