Compare commits
No commits in common. "master" and "gh-images" have entirely different histories.
38 changed files with 4210 additions and 13665 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -1,9 +0,0 @@
|
|||
# Compilation output
|
||||
*.o
|
||||
*.d
|
||||
*.a
|
||||
|
||||
# Testing things
|
||||
blocks/
|
||||
lfs
|
||||
test.c
|
||||
317
.travis.yml
317
.travis.yml
|
|
@ -1,317 +0,0 @@
|
|||
# Environment variables
|
||||
env:
|
||||
global:
|
||||
- CFLAGS=-Werror
|
||||
|
||||
# Common test script
|
||||
script:
|
||||
# make sure example can at least compile
|
||||
- sed -n '/``` c/,/```/{/```/d; p;}' README.md > test.c &&
|
||||
make all CFLAGS+="
|
||||
-Duser_provided_block_device_read=NULL
|
||||
-Duser_provided_block_device_prog=NULL
|
||||
-Duser_provided_block_device_erase=NULL
|
||||
-Duser_provided_block_device_sync=NULL
|
||||
-include stdio.h"
|
||||
|
||||
# run tests
|
||||
- make test QUIET=1
|
||||
|
||||
# run tests with a few different configurations
|
||||
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=4"
|
||||
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16"
|
||||
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
|
||||
|
||||
- make 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 -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
|
||||
| tee sizes
|
||||
|
||||
# update status if we succeeded, compare with master if possible
|
||||
- |
|
||||
if [ "$TRAVIS_TEST_RESULT" -eq 0 ]
|
||||
then
|
||||
CURR=$(tail -n1 sizes | awk '{print $1}')
|
||||
PREV=$(curl -u "$GEKY_BOT_STATUSES" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
|
||||
| jq -re "select(.sha != \"$TRAVIS_COMMIT\")
|
||||
| .statuses[] | select(.context == \"$STAGE/$NAME\").description
|
||||
| capture(\"code size is (?<size>[0-9]+)\").size" \
|
||||
|| echo 0)
|
||||
|
||||
STATUS="Passed, code size is ${CURR}B"
|
||||
if [ "$PREV" -ne 0 ]
|
||||
then
|
||||
STATUS="$STATUS ($(python -c "print '%+.2f' % (100*($CURR-$PREV)/$PREV.0)")%)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# CI matrix
|
||||
jobs:
|
||||
include:
|
||||
# native testing
|
||||
- stage: test
|
||||
env:
|
||||
- STAGE=test
|
||||
- NAME=littlefs-x86
|
||||
|
||||
# cross-compile with ARM (thumb mode)
|
||||
- stage: test
|
||||
env:
|
||||
- STAGE=test
|
||||
- NAME=littlefs-arm
|
||||
- CC="arm-linux-gnueabi-gcc --static -mthumb"
|
||||
- EXEC="qemu-arm"
|
||||
install:
|
||||
- sudo apt-get install
|
||||
gcc-arm-linux-gnueabi
|
||||
libc6-dev-armel-cross
|
||||
qemu-user
|
||||
- arm-linux-gnueabi-gcc --version
|
||||
- qemu-arm -version
|
||||
|
||||
# cross-compile with PowerPC
|
||||
- stage: test
|
||||
env:
|
||||
- STAGE=test
|
||||
- NAME=littlefs-powerpc
|
||||
- CC="powerpc-linux-gnu-gcc --static"
|
||||
- EXEC="qemu-ppc"
|
||||
install:
|
||||
- sudo apt-get install
|
||||
gcc-powerpc-linux-gnu
|
||||
libc6-dev-powerpc-cross
|
||||
qemu-user
|
||||
- powerpc-linux-gnu-gcc --version
|
||||
- qemu-ppc -version
|
||||
|
||||
# cross-compile with MIPS
|
||||
- stage: test
|
||||
env:
|
||||
- STAGE=test
|
||||
- NAME=littlefs-mips
|
||||
- CC="mips-linux-gnu-gcc --static"
|
||||
- EXEC="qemu-mips"
|
||||
install:
|
||||
- sudo apt-get install
|
||||
gcc-mips-linux-gnu
|
||||
libc6-dev-mips-cross
|
||||
qemu-user
|
||||
- mips-linux-gnu-gcc --version
|
||||
- qemu-mips -version
|
||||
|
||||
# self-host with littlefs-fuse for fuzz test
|
||||
- stage: test
|
||||
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 -b v2
|
||||
- fusermount -V
|
||||
- gcc --version
|
||||
before_script:
|
||||
# setup disk for littlefs-fuse
|
||||
- rm -rf littlefs-fuse/littlefs/*
|
||||
- cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
|
||||
|
||||
- mkdir mount
|
||||
- sudo chmod a+rw /dev/loop0
|
||||
- dd if=/dev/zero bs=512 count=4096 of=disk
|
||||
- losetup /dev/loop0 disk
|
||||
script:
|
||||
# self-host test
|
||||
- make -C littlefs-fuse
|
||||
|
||||
- littlefs-fuse/lfs --format /dev/loop0
|
||||
- littlefs-fuse/lfs /dev/loop0 mount
|
||||
|
||||
- ls mount
|
||||
- mkdir mount/littlefs
|
||||
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs
|
||||
- cd mount/littlefs
|
||||
- stat .
|
||||
- ls -flh
|
||||
- make -B test_dirs test_files QUIET=1
|
||||
|
||||
# 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:
|
||||
- |
|
||||
bash << 'SCRIPT'
|
||||
set -ev
|
||||
# Find version defined in lfs.h
|
||||
LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3)
|
||||
LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16)))
|
||||
LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >> 0)))
|
||||
# Grab latests patch from repo tags, default to 0, needs finagling
|
||||
# to get past github's pagination api
|
||||
PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.
|
||||
PREV_URL=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" -I \
|
||||
| sed -n '/^Link/{s/.*<\(.*\)>; rel="last"/\1/;p;q0};$q1' \
|
||||
|| echo $PREV_URL)
|
||||
LFS_VERSION_PATCH=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" \
|
||||
| jq 'map(.ref | match("\\bv.*\\..*\\.(.*)$";"g")
|
||||
.captures[].string | tonumber) | max + 1' \
|
||||
|| echo 0)
|
||||
# We have our new version
|
||||
LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
|
||||
echo "VERSION $LFS_VERSION"
|
||||
# Check that we're the most recent commit
|
||||
CURRENT_COMMIT=$(curl -f -u "$GEKY_BOT_RELEASES" \
|
||||
https://api.github.com/repos/$TRAVIS_REPO_SLUG/commits/master \
|
||||
| jq -re '.sha')
|
||||
[ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] || exit 0
|
||||
# Create major branch
|
||||
git branch v$LFS_VERSION_MAJOR HEAD
|
||||
# Create major prefix branch
|
||||
git config user.name "geky bot"
|
||||
git config user.email "bot@geky.net"
|
||||
git fetch https://github.com/$TRAVIS_REPO_SLUG.git \
|
||||
--depth=50 v$LFS_VERSION_MAJOR-prefix || true
|
||||
./scripts/prefix.py lfs$LFS_VERSION_MAJOR
|
||||
git branch v$LFS_VERSION_MAJOR-prefix $( \
|
||||
git commit-tree $(git write-tree) \
|
||||
$(git rev-parse --verify -q FETCH_HEAD | sed -e 's/^/-p /') \
|
||||
-p HEAD \
|
||||
-m "Generated v$LFS_VERSION_MAJOR prefixes")
|
||||
git reset --hard
|
||||
# Update major version branches (vN and vN-prefix)
|
||||
git push --atomic https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
|
||||
v$LFS_VERSION_MAJOR \
|
||||
v$LFS_VERSION_MAJOR-prefix
|
||||
# Build release notes
|
||||
PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
|
||||
if [ ! -z "$PREV" ]
|
||||
then
|
||||
echo "PREV $PREV"
|
||||
CHANGES=$(git log --oneline $PREV.. --grep='^Merge' --invert-grep)
|
||||
printf "CHANGES\n%s\n\n" "$CHANGES"
|
||||
fi
|
||||
case ${GEKY_BOT_DRAFT:-minor} in
|
||||
true) DRAFT=true ;;
|
||||
minor) DRAFT=$(jq -R 'endswith(".0")' <<< "$LFS_VERSION") ;;
|
||||
false) DRAFT=false ;;
|
||||
esac
|
||||
# Create the release and patch version tag (vN.N.N)
|
||||
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
|
||||
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
|
||||
-d "{
|
||||
\"tag_name\": \"$LFS_VERSION\",
|
||||
\"name\": \"${LFS_VERSION%.0}\",
|
||||
\"target_commitish\": \"$TRAVIS_COMMIT\",
|
||||
\"draft\": $DRAFT,
|
||||
\"body\": $(jq -sR '.' <<< "$CHANGES")
|
||||
}" #"
|
||||
SCRIPT
|
||||
|
||||
# Manage statuses
|
||||
before_install:
|
||||
- |
|
||||
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\",
|
||||
\"state\": \"pending\",
|
||||
\"description\": \"${STATUS:-In progress}\",
|
||||
\"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
|
||||
}"
|
||||
|
||||
after_failure:
|
||||
- |
|
||||
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\",
|
||||
\"state\": \"failure\",
|
||||
\"description\": \"${STATUS:-Failed}\",
|
||||
\"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
|
||||
}"
|
||||
|
||||
after_success:
|
||||
- |
|
||||
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\",
|
||||
\"state\": \"success\",
|
||||
\"description\": \"${STATUS:-Passed}\",
|
||||
\"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
|
||||
}"
|
||||
|
||||
# Job control
|
||||
stages:
|
||||
- name: test
|
||||
- name: deploy
|
||||
if: branch = master AND type = push
|
||||
24
LICENSE.md
24
LICENSE.md
|
|
@ -1,24 +0,0 @@
|
|||
Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
- 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.
|
||||
|
||||
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.
|
||||
86
Makefile
86
Makefile
|
|
@ -1,86 +0,0 @@
|
|||
TARGET = lfs.a
|
||||
ifneq ($(wildcard test.c main.c),)
|
||||
override TARGET = lfs
|
||||
endif
|
||||
|
||||
CC ?= gcc
|
||||
AR ?= ar
|
||||
SIZE ?= size
|
||||
|
||||
SRC += $(wildcard *.c emubd/*.c)
|
||||
OBJ := $(SRC:.c=.o)
|
||||
DEP := $(SRC:.c=.d)
|
||||
ASM := $(SRC:.c=.s)
|
||||
|
||||
TEST := $(patsubst tests/%.sh,%,$(wildcard tests/test_*))
|
||||
|
||||
SHELL = /bin/bash -o pipefail
|
||||
|
||||
ifdef DEBUG
|
||||
override CFLAGS += -O0 -g3
|
||||
else
|
||||
override CFLAGS += -Os
|
||||
endif
|
||||
ifdef WORD
|
||||
override CFLAGS += -m$(WORD)
|
||||
endif
|
||||
ifdef TRACE
|
||||
override CFLAGS += -DLFS_YES_TRACE
|
||||
endif
|
||||
override CFLAGS += -I.
|
||||
override CFLAGS += -std=c99 -Wall -pedantic
|
||||
override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef
|
||||
# Remove missing-field-initializers because of GCC bug
|
||||
override CFLAGS += -Wno-missing-field-initializers
|
||||
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
asm: $(ASM)
|
||||
|
||||
size: $(OBJ)
|
||||
$(SIZE) -t $^
|
||||
|
||||
.SUFFIXES:
|
||||
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 -nu '/^[-=]/p'
|
||||
else
|
||||
./$<
|
||||
endif
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
lfs: $(OBJ)
|
||||
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
|
||||
|
||||
%.a: $(OBJ)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -MMD $(CFLAGS) $< -o $@
|
||||
|
||||
%.s: %.c
|
||||
$(CC) -S $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
rm -f $(OBJ)
|
||||
rm -f $(DEP)
|
||||
rm -f $(ASM)
|
||||
248
README.md
248
README.md
|
|
@ -1,248 +0,0 @@
|
|||
## littlefs
|
||||
|
||||
A little fail-safe filesystem designed for microcontrollers.
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
.-----. | |
|
||||
--|o |---| littlefs |
|
||||
--| |---| |
|
||||
'-----' '----------'
|
||||
| | |
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
**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.
|
||||
|
||||
**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
|
||||
|
||||
Here's a simple example that updates a file named `boot_count` every time
|
||||
main runs. The program can be interrupted at any time without losing track
|
||||
of how many times it has been booted and without corrupting the filesystem:
|
||||
|
||||
``` c
|
||||
#include "lfs.h"
|
||||
|
||||
// variables used by the filesystem
|
||||
lfs_t lfs;
|
||||
lfs_file_t file;
|
||||
|
||||
// configuration of the filesystem is provided by this struct
|
||||
const struct lfs_config cfg = {
|
||||
// block device operations
|
||||
.read = user_provided_block_device_read,
|
||||
.prog = user_provided_block_device_prog,
|
||||
.erase = user_provided_block_device_erase,
|
||||
.sync = user_provided_block_device_sync,
|
||||
|
||||
// block device configuration
|
||||
.read_size = 16,
|
||||
.prog_size = 16,
|
||||
.block_size = 4096,
|
||||
.block_count = 128,
|
||||
.cache_size = 16,
|
||||
.lookahead_size = 16,
|
||||
};
|
||||
|
||||
// entry point
|
||||
int main(void) {
|
||||
// mount the filesystem
|
||||
int err = lfs_mount(&lfs, &cfg);
|
||||
|
||||
// reformat if we can't mount the filesystem
|
||||
// this should only happen on the first boot
|
||||
if (err) {
|
||||
lfs_format(&lfs, &cfg);
|
||||
lfs_mount(&lfs, &cfg);
|
||||
}
|
||||
|
||||
// read current count
|
||||
uint32_t boot_count = 0;
|
||||
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
|
||||
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
|
||||
|
||||
// update boot count
|
||||
boot_count += 1;
|
||||
lfs_file_rewind(&lfs, &file);
|
||||
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
|
||||
|
||||
// remember the storage is not updated until the file is closed successfully
|
||||
lfs_file_close(&lfs, &file);
|
||||
|
||||
// release any resources we were using
|
||||
lfs_unmount(&lfs);
|
||||
|
||||
// print the boot count
|
||||
printf("boot_count: %d\n", boot_count);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Detailed documentation (or at least as much detail as is currently available)
|
||||
can be found in the comments in [lfs.h](lfs.h).
|
||||
|
||||
littlefs takes in a configuration structure that defines how the filesystem
|
||||
operates. The configuration struct provides the filesystem with the block
|
||||
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
|
||||
simultaneously. With the `lfs_t` and configuration struct, a user can
|
||||
format a block device or mount the filesystem.
|
||||
|
||||
Once mounted, the littlefs provides a full set of POSIX-like file and
|
||||
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 not actually committed to
|
||||
the filesystem until sync or close is called on the file.
|
||||
|
||||
## Other notes
|
||||
|
||||
All littlefs calls have the potential to return a negative error code. The
|
||||
errors can be either one of those found in the `enum lfs_error` in
|
||||
[lfs.h](lfs.h), or an error returned by the user's block device operations.
|
||||
|
||||
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
|
||||
detect corrupt blocks. However, the wear leveling does not depend on the return
|
||||
code of these functions, instead all data is read back and checked for
|
||||
integrity.
|
||||
|
||||
If your storage caches writes, make sure that the provided `sync` function
|
||||
flushes all the data to memory and ensures that the next read fetches the data
|
||||
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.
|
||||
|
||||
## Design
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
The littlefs comes with a test suite designed to run on a PC using the
|
||||
[emulated block device](emubd/lfs_emubd.h) found in the emubd directory.
|
||||
The tests assume a Linux environment and can be started with make:
|
||||
|
||||
``` bash
|
||||
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
|
||||
|
||||
- [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-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].
|
||||
|
||||
- [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
|
||||
787
SPEC.md
787
SPEC.md
|
|
@ -1,787 +0,0 @@
|
|||
## littlefs technical specification
|
||||
|
||||
This is the technical specification of the little filesystem. This document
|
||||
covers the technical details of how the littlefs is stored on disk for
|
||||
introspection and tooling. This document assumes you are familiar with the
|
||||
design of the littlefs, for more info on how littlefs works check
|
||||
out [DESIGN.md](DESIGN.md).
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
.-----. | |
|
||||
--|o |---| littlefs |
|
||||
--| |---| |
|
||||
'-----' '----------'
|
||||
| | |
|
||||
```
|
||||
|
||||
## Some quick notes
|
||||
|
||||
- littlefs is a block-based filesystem. The disk is divided into an array of
|
||||
evenly sized blocks that are used as the logical unit of storage.
|
||||
|
||||
- Block pointers are stored in 32 bits, with the special value `0xffffffff`
|
||||
representing a null block address.
|
||||
|
||||
- In addition to the logical block size (which usually matches the erase
|
||||
block size), littlefs also uses a program block size and read block size.
|
||||
These determine the alignment of block device operations, but don't need
|
||||
to be consistent for portability.
|
||||
|
||||
- By default, all values in littlefs are stored in little-endian byte order.
|
||||
|
||||
## Directories / Metadata pairs
|
||||
|
||||
Metadata pairs form the backbone of littlefs and provide a system for
|
||||
distributed atomic updates. Even the superblock is stored in a metadata pair.
|
||||
|
||||
As their name suggests, a metadata pair is stored in two blocks, with one block
|
||||
providing a backup during erase cycles in case power is lost. These two blocks
|
||||
are not necessarily sequential and may be anywhere on disk, so a "pointer" to a
|
||||
metadata pair is stored as two block pointers.
|
||||
|
||||
On top of this, each metadata block behaves as an appendable log, containing a
|
||||
variable number of commits. Commits can be appended to the metadata log in
|
||||
order to update the metadata without requiring an erase cycles. Note that
|
||||
successive commits may supersede the metadata in previous commits. Only the
|
||||
most recent metadata should be considered valid.
|
||||
|
||||
The high-level layout of a metadata block is fairly simple:
|
||||
|
||||
```
|
||||
.---------------------------------------.
|
||||
.-| revision count | entries | \
|
||||
| |-------------------+ | |
|
||||
| | | |
|
||||
| | | +-- 1st commit
|
||||
| | | |
|
||||
| | +-------------------| |
|
||||
| | | CRC | /
|
||||
| |-------------------+-------------------|
|
||||
| | entries | \
|
||||
| | | |
|
||||
| | | +-- 2nd commit
|
||||
| | +-------------------+--------------| |
|
||||
| | | CRC | padding | /
|
||||
| |----+-------------------+--------------|
|
||||
| | entries | \
|
||||
| | | |
|
||||
| | | +-- 3rd commit
|
||||
| | +-------------------+---------| |
|
||||
| | | CRC | | /
|
||||
| |---------+-------------------+ |
|
||||
| | unwritten storage | more commits
|
||||
| | | |
|
||||
| | | v
|
||||
| | |
|
||||
| | |
|
||||
| '---------------------------------------'
|
||||
'---------------------------------------'
|
||||
```
|
||||
|
||||
Each metadata block contains a 32-bit revision count followed by a number of
|
||||
commits. Each commit contains a variable number of metadata entries followed
|
||||
by a 32-bit CRC.
|
||||
|
||||
Note also that entries aren't necessarily word-aligned. This allows us to
|
||||
store metadata more compactly, however we can only write to addresses that are
|
||||
aligned to our program block size. This means each commit may have padding for
|
||||
alignment.
|
||||
|
||||
Metadata block fields:
|
||||
|
||||
1. **Revision count (32-bits)** - Incremented every erase cycle. If both blocks
|
||||
contain valid commits, only the block with the most recent revision count
|
||||
should be used. Sequence comparison must be used to avoid issues with
|
||||
integer overflow.
|
||||
|
||||
2. **CRC (32-bits)** - Detects corruption from power-loss or other write
|
||||
issues. Uses a CRC-32 with a polynomial of `0x04c11db7` initialized
|
||||
with `0xffffffff`.
|
||||
|
||||
Entries themselves are stored as a 32-bit tag followed by a variable length
|
||||
blob of data. But exactly how these tags are stored is a little bit tricky.
|
||||
|
||||
Metadata blocks support both forward and backward iteration. In order to do
|
||||
this without duplicating the space for each tag, neighboring entries have their
|
||||
tags XORed together, starting with `0xffffffff`.
|
||||
|
||||
```
|
||||
Forward iteration Backward iteration
|
||||
|
||||
.-------------------. 0xffffffff .-------------------.
|
||||
| revision count | | | revision count |
|
||||
|-------------------| v |-------------------|
|
||||
| tag ~A |---> xor -> tag A | tag ~A |---> xor -> 0xffffffff
|
||||
|-------------------| | |-------------------| ^
|
||||
| data A | | | data A | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
|-------------------| v |-------------------| |
|
||||
| tag AxB |---> xor -> tag B | tag AxB |---> xor -> tag A
|
||||
|-------------------| | |-------------------| ^
|
||||
| data B | | | data B | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
|-------------------| v |-------------------| |
|
||||
| tag BxC |---> xor -> tag C | tag BxC |---> xor -> tag B
|
||||
|-------------------| |-------------------| ^
|
||||
| data C | | data C | |
|
||||
| | | | tag C
|
||||
| | | |
|
||||
| | | |
|
||||
'-------------------' '-------------------'
|
||||
```
|
||||
|
||||
One last thing to note before we get into the details around tag encoding. Each
|
||||
tag contains a valid bit used to indicate if the tag and containing commit is
|
||||
valid. This valid bit is the first bit found in the tag and the commit and can
|
||||
be used to tell if we've attempted to write to the remaining space in the
|
||||
block.
|
||||
|
||||
Here's a more complete example of metadata block containing 4 entries:
|
||||
|
||||
```
|
||||
.---------------------------------------.
|
||||
.-| revision count | tag ~A | \
|
||||
| |-------------------+-------------------| |
|
||||
| | data A | |
|
||||
| | | |
|
||||
| |-------------------+-------------------| |
|
||||
| | tag AxB | data B | <--. |
|
||||
| |-------------------+ | | |
|
||||
| | | | +-- 1st commit
|
||||
| | +-------------------+---------| | |
|
||||
| | | tag BxC | | <-.| |
|
||||
| |---------+-------------------+ | || |
|
||||
| | data C | || |
|
||||
| | | || |
|
||||
| |-------------------+-------------------| || |
|
||||
| | tag CxCRC | CRC | || /
|
||||
| |-------------------+-------------------| ||
|
||||
| | tag CRCxA' | data A' | || \
|
||||
| |-------------------+ | || |
|
||||
| | | || |
|
||||
| | +-------------------+----| || +-- 2nd commit
|
||||
| | | tag CRCxA' | | || |
|
||||
| |--------------+-------------------+----| || |
|
||||
| | CRC | padding | || /
|
||||
| |--------------+----+-------------------| ||
|
||||
| | tag CRCxA'' | data A'' | <---. \
|
||||
| |-------------------+ | ||| |
|
||||
| | | ||| |
|
||||
| | +-------------------+---------| ||| |
|
||||
| | | tag A''xD | | < ||| |
|
||||
| |---------+-------------------+ | |||| +-- 3rd commit
|
||||
| | data D | |||| |
|
||||
| | +---------| |||| |
|
||||
| | | tag Dx| |||| |
|
||||
| |---------+-------------------+---------| |||| |
|
||||
| |CRC | CRC | | |||| /
|
||||
| |---------+-------------------+ | ||||
|
||||
| | unwritten storage | |||| more commits
|
||||
| | | |||| |
|
||||
| | | |||| v
|
||||
| | | ||||
|
||||
| | | ||||
|
||||
| '---------------------------------------' ||||
|
||||
'---------------------------------------' |||'- most recent A
|
||||
||'-- most recent B
|
||||
|'--- most recent C
|
||||
'---- most recent D
|
||||
```
|
||||
|
||||
## Metadata tags
|
||||
|
||||
So in littlefs, 32-bit tags describe every type of metadata. And this means
|
||||
_every_ type of metadata, including file entries, directory fields, and
|
||||
global state. Even the CRCs used to mark the end of commits get their own tag.
|
||||
|
||||
Because of this, the tag format contains some densely packed information. Note
|
||||
that there are multiple levels of types which break down into more info:
|
||||
|
||||
```
|
||||
[---- 32 ----]
|
||||
[1|-- 11 --|-- 10 --|-- 10 --]
|
||||
^. ^ . ^ ^- length
|
||||
|. | . '------------ id
|
||||
|. '-----.------------------ type (type3)
|
||||
'.-----------.------------------ valid bit
|
||||
[-3-|-- 8 --]
|
||||
^ ^- chunk
|
||||
'------- type (type1)
|
||||
```
|
||||
|
||||
|
||||
Before we go further, there's one important thing to note. These tags are
|
||||
**not** stored in little-endian. Tags stored in commits are actually stored
|
||||
in big-endian (and is the only thing in littlefs stored in big-endian). This
|
||||
little bit of craziness comes from the fact that the valid bit must be the
|
||||
first bit in a commit, and when converted to little-endian, the valid bit finds
|
||||
itself in byte 4. We could restructure the tag to store the valid bit lower,
|
||||
but, because none of the fields are byte-aligned, this would be more
|
||||
complicated than just storing the tag in big-endian.
|
||||
|
||||
Another thing to note is that both the tags `0x00000000` and `0xffffffff` are
|
||||
invalid and can be used for null values.
|
||||
|
||||
Metadata tag fields:
|
||||
|
||||
1. **Valid bit (1-bit)** - Indicates if the tag is valid.
|
||||
|
||||
2. **Type3 (11-bits)** - Type of the tag. This field is broken down further
|
||||
into a 3-bit abstract type and an 8-bit chunk field. Note that the value
|
||||
`0x000` is invalid and not assigned a type.
|
||||
|
||||
3. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into
|
||||
8 categories that facilitate bitmasked lookups.
|
||||
|
||||
4. **Chunk (8-bits)** - Chunk field used for various purposes by the different
|
||||
abstract types. type1+chunk+id form a unique identifier for each tag in the
|
||||
metadata block.
|
||||
|
||||
5. **Id (10-bits)** - File id associated with the tag. Each file in a metadata
|
||||
block gets a unique id which is used to associate tags with that file. The
|
||||
special value `0x3ff` is used for any tags that are not associated with a
|
||||
file, such as directory and global metadata.
|
||||
|
||||
6. **Length (10-bits)** - Length of the data in bytes. The special value
|
||||
`0x3ff` indicates that this tag has been deleted.
|
||||
|
||||
## Metadata types
|
||||
|
||||
What follows is an exhaustive list of metadata in littlefs.
|
||||
|
||||
---
|
||||
#### `0x401` LFS_TYPE_CREATE
|
||||
|
||||
Creates a new file with this id. Note that files in a metadata block
|
||||
don't necessarily need a create tag. All a create does is move over any
|
||||
files using this id. In this sense a create is similar to insertion into
|
||||
an imaginary array of files.
|
||||
|
||||
The create and delete tags allow littlefs to keep files in a directory
|
||||
ordered alphabetically by filename.
|
||||
|
||||
---
|
||||
#### `0x4ff` LFS_TYPE_DELETE
|
||||
|
||||
Deletes the file with this id. An inverse to create, this tag moves over
|
||||
any files neighboring this id similar to a deletion from an imaginary
|
||||
array of files.
|
||||
|
||||
---
|
||||
#### `0x0xx` LFS_TYPE_NAME
|
||||
|
||||
Associates the id with a file name and file type.
|
||||
|
||||
The data contains the file name stored as an ASCII string (may be expanded to
|
||||
UTF8 in the future).
|
||||
|
||||
The chunk field in this tag indicates an 8-bit file type which can be one of
|
||||
the following.
|
||||
|
||||
Currently, the name tag must precede any other tags associated with the id and
|
||||
can not be reassigned without deleting the file.
|
||||
|
||||
Layout of the name tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][--- variable length ---]
|
||||
[1| 3| 8 | 10 | 10 ][--- (size) ---]
|
||||
^ ^ ^ ^ ^- size ^- file name
|
||||
| | | '------ id
|
||||
| | '----------- file type
|
||||
| '-------------- type1 (0x0)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Name fields:
|
||||
|
||||
1. **file type (8-bits)** - Type of the file.
|
||||
|
||||
2. **file name** - File name stored as an ASCII string.
|
||||
|
||||
---
|
||||
#### `0x001` LFS_TYPE_REG
|
||||
|
||||
Initializes the id + name as a regular file.
|
||||
|
||||
How each file is stored depends on its struct tag, which is described below.
|
||||
|
||||
---
|
||||
#### `0x002` LFS_TYPE_DIR
|
||||
|
||||
Initializes the id + name as a directory.
|
||||
|
||||
Directories in littlefs are stored on disk as a linked-list of metadata pairs,
|
||||
each pair containing any number of files in alphabetical order. A pointer to
|
||||
the directory is stored in the struct tag, which is described below.
|
||||
|
||||
---
|
||||
#### `0x0ff` LFS_TYPE_SUPERBLOCK
|
||||
|
||||
Initializes the id as a superblock entry.
|
||||
|
||||
The superblock entry is a special entry used to store format-time configuration
|
||||
and identify the filesystem.
|
||||
|
||||
The name is a bit of a misnomer. While the superblock entry serves the same
|
||||
purpose as a superblock found in other filesystems, in littlefs the superblock
|
||||
does not get a dedicated block. Instead, the superblock entry is duplicated
|
||||
across a linked-list of metadata pairs rooted on the blocks 0 and 1. The last
|
||||
metadata pair doubles as the root directory of the filesystem.
|
||||
|
||||
```
|
||||
.--------. .--------. .--------. .--------. .--------.
|
||||
.| super |->| super |->| super |->| super |->| file B |
|
||||
|| block | || block | || block | || block | || file C |
|
||||
|| | || | || | || file A | || file D |
|
||||
|'--------' |'--------' |'--------' |'--------' |'--------'
|
||||
'--------' '--------' '--------' '--------' '--------'
|
||||
|
||||
\----------------+----------------/ \----------+----------/
|
||||
superblock pairs root directory
|
||||
```
|
||||
|
||||
The filesystem starts with only the root directory. The superblock metadata
|
||||
pairs grow every time the root pair is compacted in order to prolong the
|
||||
life of the device exponentially.
|
||||
|
||||
The contents of the superblock entry are stored in a name tag with the
|
||||
superblock type and an inline-struct tag. The name tag contains the magic
|
||||
string "littlefs", while the inline-struct tag contains version and
|
||||
configuration information.
|
||||
|
||||
Layout of the superblock name tag and inline-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][--- 64 ---]
|
||||
^ ^ ^ ^- size (8) ^- magic string ("littlefs")
|
||||
| | '------ id (0)
|
||||
| '------------ type (0x0ff)
|
||||
'----------------- valid bit
|
||||
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][-- 32 --|-- 32 --|-- 32 --]
|
||||
^ ^ ^ ^ ^- version ^- block size ^- block count
|
||||
| | | | [-- 32 --|-- 32 --|-- 32 --]
|
||||
| | | | [-- 32 --|-- 32 --|-- 32 --]
|
||||
| | | | ^- name max ^- file max ^- attr max
|
||||
| | | '- size (24)
|
||||
| | '------ id (0)
|
||||
| '------------ type (0x201)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Superblock fields:
|
||||
|
||||
1. **Magic string (8-bytes)** - Magic string indicating the presence of
|
||||
littlefs on the device. Must be the string "littlefs".
|
||||
|
||||
2. **Version (32-bits)** - The version of littlefs at format time. The version
|
||||
is encoded in a 32-bit value with the upper 16-bits containing the major
|
||||
version, and the lower 16-bits containing the minor version.
|
||||
|
||||
This specification describes version 2.0 (`0x00020000`).
|
||||
|
||||
3. **Block size (32-bits)** - Size of the logical block size used by the
|
||||
filesystem in bytes.
|
||||
|
||||
4. **Block count (32-bits)** - Number of blocks in the filesystem.
|
||||
|
||||
5. **Name max (32-bits)** - Maximum size of file names in bytes.
|
||||
|
||||
6. **File max (32-bits)** - Maximum size of files in bytes.
|
||||
|
||||
7. **Attr max (32-bits)** - Maximum size of file attributes in bytes.
|
||||
|
||||
The superblock must always be the first entry (id 0) in a metadata pair as well
|
||||
as be the first entry written to the block. This means that the superblock
|
||||
entry can be read from a device using offsets alone.
|
||||
|
||||
---
|
||||
#### `0x2xx` LFS_TYPE_STRUCT
|
||||
|
||||
Associates the id with an on-disk data structure.
|
||||
|
||||
The exact layout of the data depends on the data structure type stored in the
|
||||
chunk field and can be one of the following.
|
||||
|
||||
Any type of struct supersedes all other structs associated with the id. For
|
||||
example, appending a ctz-struct replaces an inline-struct on the same file.
|
||||
|
||||
---
|
||||
#### `0x200` LFS_TYPE_DIRSTRUCT
|
||||
|
||||
Gives the id a directory data structure.
|
||||
|
||||
Directories in littlefs are stored on disk as a linked-list of metadata pairs,
|
||||
each pair containing any number of files in alphabetical order.
|
||||
|
||||
```
|
||||
|
|
||||
v
|
||||
.--------. .--------. .--------. .--------. .--------. .--------.
|
||||
.| file A |->| file D |->| file G |->| file I |->| file J |->| file M |
|
||||
|| file B | || file E | || file H | || | || file K | || file N |
|
||||
|| file C | || file F | || | || | || file L | || |
|
||||
|'--------' |'--------' |'--------' |'--------' |'--------' |'--------'
|
||||
'--------' '--------' '--------' '--------' '--------' '--------'
|
||||
```
|
||||
|
||||
The dir-struct tag contains only the pointer to the first metadata-pair in the
|
||||
directory. The directory size is not known without traversing the directory.
|
||||
|
||||
The pointer to the next metadata-pair in the directory is stored in a tail tag,
|
||||
which is described below.
|
||||
|
||||
Layout of the dir-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][--- 64 ---]
|
||||
^ ^ ^ ^- size (8) ^- metadata pair
|
||||
| | '------ id
|
||||
| '------------ type (0x200)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Dir-struct fields:
|
||||
|
||||
1. **Metadata pair (8-bytes)** - Pointer to the first metadata-pair
|
||||
in the directory.
|
||||
|
||||
---
|
||||
#### `0x201` LFS_TYPE_INLINESTRUCT
|
||||
|
||||
Gives the id an inline data structure.
|
||||
|
||||
Inline structs store small files that can fit in the metadata pair. In this
|
||||
case, the file data is stored directly in the tag's data area.
|
||||
|
||||
Layout of the inline-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][--- variable length ---]
|
||||
[1|- 11 -| 10 | 10 ][--- (size) ---]
|
||||
^ ^ ^ ^- size ^- inline data
|
||||
| | '------ id
|
||||
| '------------ type (0x201)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Inline-struct fields:
|
||||
|
||||
1. **Inline data** - File data stored directly in the metadata-pair.
|
||||
|
||||
---
|
||||
#### `0x202` LFS_TYPE_CTZSTRUCT
|
||||
|
||||
Gives the id a CTZ skip-list data structure.
|
||||
|
||||
CTZ skip-lists store files that can not fit in the metadata pair. These files
|
||||
are stored in a skip-list in reverse, with a pointer to the head of the
|
||||
skip-list. Note that the head of the skip-list and the file size is enough
|
||||
information to read the file.
|
||||
|
||||
How exactly CTZ skip-lists work is a bit complicated. A full explanation can be
|
||||
found in the [DESIGN.md](DESIGN.md#ctz-skip-lists).
|
||||
|
||||
A quick summary: For every _n_‍th block where _n_ is divisible by
|
||||
2‍_ˣ_, that block contains a pointer to block _n_-2‍_ˣ_.
|
||||
These pointers are stored in increasing order of _x_ in each block of the file
|
||||
before the actual data.
|
||||
|
||||
```
|
||||
|
|
||||
v
|
||||
.--------. .--------. .--------. .--------. .--------. .--------.
|
||||
| A |<-| D |<-| G |<-| J |<-| M |<-| P |
|
||||
| B |<-| E |--| H |<-| K |--| N | | Q |
|
||||
| C |<-| F |--| I |--| L |--| O | | |
|
||||
'--------' '--------' '--------' '--------' '--------' '--------'
|
||||
block 0 block 1 block 2 block 3 block 4 block 5
|
||||
1 skip 2 skips 1 skip 3 skips 1 skip
|
||||
```
|
||||
|
||||
Note that the maximum number of pointers in a block is bounded by the maximum
|
||||
file size divided by the block size. With 32 bits for file size, this results
|
||||
in a minimum block size of 104 bytes.
|
||||
|
||||
Layout of the CTZ-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][-- 32 --|-- 32 --]
|
||||
^ ^ ^ ^ ^ ^- file size
|
||||
| | | | '-------------------- file head
|
||||
| | | '- size (8)
|
||||
| | '------ id
|
||||
| '------------ type (0x202)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
CTZ-struct fields:
|
||||
|
||||
1. **File head (32-bits)** - Pointer to the block that is the head of the
|
||||
file's CTZ skip-list.
|
||||
|
||||
2. **File size (32-bits)** - Size of the file in bytes.
|
||||
|
||||
---
|
||||
#### `0x3xx` LFS_TYPE_USERATTR
|
||||
|
||||
Attaches a user attribute to an id.
|
||||
|
||||
littlefs has a concept of "user attributes". These are small user-provided
|
||||
attributes that can be used to store things like timestamps, hashes,
|
||||
permissions, etc.
|
||||
|
||||
Each user attribute is uniquely identified by an 8-bit type which is stored in
|
||||
the chunk field, and the user attribute itself can be found in the tag's data.
|
||||
|
||||
There are currently no standard user attributes and a portable littlefs
|
||||
implementation should work with any user attributes missing.
|
||||
|
||||
Layout of the user-attr tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][--- variable length ---]
|
||||
[1| 3| 8 | 10 | 10 ][--- (size) ---]
|
||||
^ ^ ^ ^ ^- size ^- attr data
|
||||
| | | '------ id
|
||||
| | '----------- attr type
|
||||
| '-------------- type1 (0x3)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
User-attr fields:
|
||||
|
||||
1. **Attr type (8-bits)** - Type of the user attributes.
|
||||
|
||||
2. **Attr data** - The data associated with the user attribute.
|
||||
|
||||
---
|
||||
#### `0x6xx` LFS_TYPE_TAIL
|
||||
|
||||
Provides the tail pointer for the metadata pair itself.
|
||||
|
||||
The metadata pair's tail pointer is used in littlefs for a linked-list
|
||||
containing all metadata pairs. The chunk field contains the type of the tail,
|
||||
which indicates if the following metadata pair is a part of the directory
|
||||
(hard-tail) or only used to traverse the filesystem (soft-tail).
|
||||
|
||||
```
|
||||
.--------.
|
||||
.| dir A |-.
|
||||
||softtail| |
|
||||
.--------| |-'
|
||||
| |'--------'
|
||||
| '---|--|-'
|
||||
| .-' '-------------.
|
||||
| v v
|
||||
| .--------. .--------. .--------.
|
||||
'->| dir B |->| dir B |->| dir C |
|
||||
||hardtail| ||softtail| || |
|
||||
|| | || | || |
|
||||
|'--------' |'--------' |'--------'
|
||||
'--------' '--------' '--------'
|
||||
```
|
||||
|
||||
Currently any type supersedes any other preceding tails in the metadata pair,
|
||||
but this may change if additional metadata pair state is added.
|
||||
|
||||
A note about the metadata pair linked-list: Normally, this linked-list contains
|
||||
every metadata pair in the filesystem. However, there are some operations that
|
||||
can cause this linked-list to become out of sync if a power-loss were to occur.
|
||||
When this happens, littlefs sets the "sync" flag in the global state. How
|
||||
exactly this flag is stored is described below.
|
||||
|
||||
When the sync flag is set:
|
||||
|
||||
1. The linked-list may contain an orphaned directory that has been removed in
|
||||
the filesystem.
|
||||
2. The linked-list may contain a metadata pair with a bad block that has been
|
||||
replaced in the filesystem.
|
||||
|
||||
If the sync flag is set, the threaded linked-list must be checked for these
|
||||
errors before it can be used reliably. Note that the threaded linked-list can
|
||||
be ignored if littlefs is mounted read-only.
|
||||
|
||||
Layout of the tail tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1| 3| 8 | 10 | 10 ][--- 64 ---]
|
||||
^ ^ ^ ^ ^- size (8) ^- metadata pair
|
||||
| | | '------ id
|
||||
| | '---------- tail type
|
||||
| '------------- type1 (0x6)
|
||||
'---------------- valid bit
|
||||
```
|
||||
|
||||
Tail fields:
|
||||
|
||||
1. **Tail type (8-bits)** - Type of the tail pointer.
|
||||
|
||||
2. **Metadata pair (8-bytes)** - Pointer to the next metadata-pair.
|
||||
|
||||
---
|
||||
#### `0x600` LFS_TYPE_SOFTTAIL
|
||||
|
||||
Provides a tail pointer that points to the next metadata pair in the
|
||||
filesystem.
|
||||
|
||||
In this case, the next metadata pair is not a part of our current directory
|
||||
and should only be followed when traversing the entire filesystem.
|
||||
|
||||
---
|
||||
#### `0x601` LFS_TYPE_HARDTAIL
|
||||
|
||||
Provides a tail pointer that points to the next metadata pair in the
|
||||
directory.
|
||||
|
||||
In this case, the next metadata pair belongs to the current directory. Note
|
||||
that because directories in littlefs are sorted alphabetically, the next
|
||||
metadata pair should only contain filenames greater than any filename in the
|
||||
current pair.
|
||||
|
||||
---
|
||||
#### `0x7xx` LFS_TYPE_GSTATE
|
||||
|
||||
Provides delta bits for global state entries.
|
||||
|
||||
littlefs has a concept of "global state". This is a small set of state that
|
||||
can be updated by a commit to _any_ metadata pair in the filesystem.
|
||||
|
||||
The way this works is that the global state is stored as a set of deltas
|
||||
distributed across the filesystem such that the global state can be found by
|
||||
the xor-sum of these deltas.
|
||||
|
||||
```
|
||||
.--------. .--------. .--------. .--------. .--------.
|
||||
.| |->| gdelta |->| |->| gdelta |->| gdelta |
|
||||
|| | || 0x23 | || | || 0xff | || 0xce |
|
||||
|| | || | || | || | || |
|
||||
|'--------' |'--------' |'--------' |'--------' |'--------'
|
||||
'--------' '----|---' '--------' '----|---' '----|---'
|
||||
v v v
|
||||
0x00 --> xor ------------------> xor ------> xor --> gstate = 0x12
|
||||
```
|
||||
|
||||
Note that storing globals this way is very expensive in terms of storage usage,
|
||||
so any global state should be kept very small.
|
||||
|
||||
The size and format of each piece of global state depends on the type, which
|
||||
is stored in the chunk field. Currently, the only global state is move state,
|
||||
which is outlined below.
|
||||
|
||||
---
|
||||
#### `0x7ff` LFS_TYPE_MOVESTATE
|
||||
|
||||
Provides delta bits for the global move state.
|
||||
|
||||
The move state in littlefs is used to store info about operations that could
|
||||
cause to filesystem to go out of sync if the power is lost. The operations
|
||||
where this could occur is moves of files between metadata pairs and any
|
||||
operation that changes metadata pairs on the threaded linked-list.
|
||||
|
||||
In the case of moves, the move state contains a tag + metadata pair describing
|
||||
the source of the ongoing move. If this tag is non-zero, that means that power
|
||||
was lost during a move, and the file exists in two different locations. If this
|
||||
happens, the source of the move should be considered deleted, and the move
|
||||
should be completed (the source should be deleted) before any other write
|
||||
operations to the filesystem.
|
||||
|
||||
In the case of operations to the threaded linked-list, a single "sync" bit is
|
||||
used to indicate that a modification is ongoing. If this sync flag is set, the
|
||||
threaded linked-list will need to be checked for errors before it can be used
|
||||
reliably. The exact cases to check for are described above in the tail tag.
|
||||
|
||||
Layout of the move state:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][1|- 11 -| 10 | 10 |--- 64 ---]
|
||||
^ ^ ^ ^ ^ ^ ^ ^- padding (0) ^- metadata pair
|
||||
| | | | | | '------ move id
|
||||
| | | | | '------------ move type
|
||||
| | | | '----------------- sync bit
|
||||
| | | |
|
||||
| | | '- size (12)
|
||||
| | '------ id (0x3ff)
|
||||
| '------------ type (0x7ff)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Move state fields:
|
||||
|
||||
1. **Sync bit (1-bit)** - Indicates if the metadata pair threaded linked-list
|
||||
is in-sync. If set, the threaded linked-list should be checked for errors.
|
||||
|
||||
2. **Move type (11-bits)** - Type of move being performed. Must be either
|
||||
`0x000`, indicating no move, or `0x4ff` indicating the source file should
|
||||
be deleted.
|
||||
|
||||
3. **Move id (10-bits)** - The file id being moved.
|
||||
|
||||
4. **Metadata pair (8-bytes)** - Pointer to the metadata-pair containing
|
||||
the move.
|
||||
|
||||
---
|
||||
#### `0x5xx` LFS_TYPE_CRC
|
||||
|
||||
Last but not least, the CRC tag marks the end of a commit and provides a
|
||||
checksum for any commits to the metadata block.
|
||||
|
||||
The first 32-bits of the data contain a CRC-32 with a polynomial of
|
||||
`0x04c11db7` initialized with `0xffffffff`. This CRC provides a checksum for
|
||||
all metadata since the previous CRC tag, including the CRC tag itself. For
|
||||
the first commit, this includes the revision count for the metadata block.
|
||||
|
||||
However, the size of the data is not limited to 32-bits. The data field may
|
||||
larger to pad the commit to the next program-aligned boundary.
|
||||
|
||||
In addition, the CRC tag's chunk field contains a set of flags which can
|
||||
change the behaviour of commits. Currently the only flag in use is the lowest
|
||||
bit, which determines the expected state of the valid bit for any following
|
||||
tags. This is used to guarantee that unwritten storage in a metadata block
|
||||
will be detected as invalid.
|
||||
|
||||
Layout of the CRC tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|--- variable length ---]
|
||||
[1| 3| 8 | 10 | 10 ][-- 32 --|--- (size) ---]
|
||||
^ ^ ^ ^ ^ ^- crc ^- padding
|
||||
| | | | '- size (12)
|
||||
| | | '------ id (0x3ff)
|
||||
| | '----------- valid state
|
||||
| '-------------- type1 (0x5)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
CRC fields:
|
||||
|
||||
1. **Valid state (1-bit)** - Indicates the expected value of the valid bit for
|
||||
any tags in the next commit.
|
||||
|
||||
2. **CRC (32-bits)** - CRC-32 with a polynomial of `0x04c11db7` initialized
|
||||
with `0xffffffff`.
|
||||
|
||||
3. **Padding** - Padding to the next program-aligned boundary. No guarantees
|
||||
are made about the contents.
|
||||
|
||||
---
|
||||
|
|
@ -1,403 +0,0 @@
|
|||
/*
|
||||
* Block device emulated on standard files
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include "emubd/lfs_emubd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.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;
|
||||
emu->cfg.block_size = cfg->block_size;
|
||||
emu->cfg.block_count = cfg->block_count;
|
||||
|
||||
// Allocate buffer for creating children files
|
||||
size_t pathlen = strlen(path);
|
||||
emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1);
|
||||
if (!emu->path) {
|
||||
int err = -ENOMEM;
|
||||
LFS_TRACE("lfs_emubd_create -> %"PRId32, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
strcpy(emu->path, path);
|
||||
emu->path[pathlen] = '/';
|
||||
emu->child = &emu->path[pathlen+1];
|
||||
memset(emu->child, '\0', LFS_NAME_MAX+1);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
int err = mkdir(path, 0777);
|
||||
if (err && errno != EEXIST) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_create -> %"PRId32, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Load stats to continue incrementing
|
||||
snprintf(emu->child, LFS_NAME_MAX, ".stats");
|
||||
FILE *f = fopen(emu->path, "r");
|
||||
if (!f) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Check if read is valid
|
||||
assert(off % cfg->read_size == 0);
|
||||
assert(size % cfg->read_size == 0);
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Zero out buffer for debugging
|
||||
memset(data, 0, size);
|
||||
|
||||
// Read data
|
||||
snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block);
|
||||
|
||||
FILE *f = fopen(emu->path, "rb");
|
||||
if (!f && errno != ENOENT) {
|
||||
int err = -errno;
|
||||
LFS_TRACE("lfs_emubd_read -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (f) {
|
||||
int err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_read -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t res = fread(data, 1, size, f);
|
||||
if (res < size && !feof(f)) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_read -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_read -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Check if write is valid
|
||||
assert(off % cfg->prog_size == 0);
|
||||
assert(size % cfg->prog_size == 0);
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Program data
|
||||
snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block);
|
||||
|
||||
FILE *f = fopen(emu->path, "r+b");
|
||||
if (!f) {
|
||||
int err = (errno == EACCES) ? 0 : -errno;
|
||||
LFS_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Check that file was erased
|
||||
assert(f);
|
||||
|
||||
int err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t res = fwrite(data, 1, size, f);
|
||||
if (res < size) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t dat;
|
||||
res = fread(&dat, 1, 1, f);
|
||||
if (res < 1) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// 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, "%" PRIx32, block);
|
||||
struct stat st;
|
||||
int err = stat(emu->path, &st);
|
||||
if (err && errno != ENOENT) {
|
||||
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) {
|
||||
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) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_erase -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_erase -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
FILE *f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
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) {
|
||||
int err = -errno;
|
||||
LFS_TRACE("lfs_emubd_sync -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int err = fclose(f);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TRACE("lfs_emubd_sync -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
snprintf(emu->child, LFS_NAME_MAX, ".stats");
|
||||
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->stats, sizeof(emu->stats), 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;
|
||||
}
|
||||
|
||||
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,79 +0,0 @@
|
|||
/*
|
||||
* Block device emulated on standard files
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_EMUBD_H
|
||||
#define LFS_EMUBD_H
|
||||
|
||||
#include "lfs.h"
|
||||
#include "lfs_util.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
// Config options
|
||||
#ifndef LFS_EMUBD_ERASE_VALUE
|
||||
#define LFS_EMUBD_ERASE_VALUE 0x00
|
||||
#endif
|
||||
|
||||
|
||||
// The emu bd state
|
||||
typedef struct lfs_emubd {
|
||||
char *path;
|
||||
char *child;
|
||||
|
||||
struct {
|
||||
uint64_t read_count;
|
||||
uint64_t prog_count;
|
||||
uint64_t erase_count;
|
||||
} stats;
|
||||
|
||||
struct {
|
||||
lfs_block_t blocks[4];
|
||||
} history;
|
||||
|
||||
struct {
|
||||
uint32_t read_size;
|
||||
uint32_t prog_size;
|
||||
uint32_t block_size;
|
||||
uint32_t block_count;
|
||||
} cfg;
|
||||
} lfs_emubd_t;
|
||||
|
||||
|
||||
// Create a block device using path for the directory to store blocks
|
||||
int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
|
||||
|
||||
// Clean up memory associated with emu block device
|
||||
void lfs_emubd_destroy(const struct lfs_config *cfg);
|
||||
|
||||
// Read a block
|
||||
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
|
||||
// Program a block
|
||||
//
|
||||
// The block must have previously been erased.
|
||||
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
|
||||
// Erase a block
|
||||
//
|
||||
// A block must be erased before being programmed. The
|
||||
// state of an erased block is undefined.
|
||||
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
|
||||
|
||||
// Sync the block device
|
||||
int lfs_emubd_sync(const struct lfs_config *cfg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
55
file-cost.py
Executable file
55
file-cost.py
Executable file
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('SVG')
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
matplotlib.rc('font', family='sans-serif', size=11)
|
||||
matplotlib.rc('axes', titlesize='medium', labelsize='medium')
|
||||
matplotlib.rc('xtick', labelsize='small')
|
||||
matplotlib.rc('ytick', labelsize='small')
|
||||
|
||||
fig, ax = plt.subplots(figsize=(6, 3.5))
|
||||
ax.set_title('File storage cost')
|
||||
|
||||
# multiplicative cost of inline files
|
||||
def inline_cost(n, B):
|
||||
#return 4+n-n
|
||||
return np.full_like(n, 4)
|
||||
|
||||
# multiplicative cost of ctz skip-list files
|
||||
def ctz_cost(n, B):
|
||||
return (np.ceil(n/B)*B) / n
|
||||
|
||||
# combined multiplicative cost
|
||||
def file_cost(n, B):
|
||||
return np.where(n < B/4, inline_cost(n, B), ctz_cost(n, B))
|
||||
|
||||
t = np.arange(0.0, 80.0, 0.25)
|
||||
ax.plot(t, file_cost(t, 16))
|
||||
t1 = np.arange(0.0, 16.0/4.0 + 0.5, 0.25)
|
||||
t2 = np.arange(16.0/4.0, 80.0, 0.25)
|
||||
ax.plot(t1, ctz_cost(t1, 16), color='C0', linestyle=(2.65, (3.65,1.65)))
|
||||
ax.plot(t2, inline_cost(t2, 16), color='C0', linestyle=(3.65, (3.65,1.65)))
|
||||
|
||||
ax.text(70.5, 4.15, 'inline file', size='small')
|
||||
ax.text(3.3, 5.85, 'ctz skip-list', rotation=-85, size='small')
|
||||
|
||||
ax.set_xlabel('file size')
|
||||
ax.set_xlim(0, 80)
|
||||
ax.set_ylabel('multiplicative cost')
|
||||
ax.set_ylim(0, 6)
|
||||
|
||||
xticks = np.arange(0, 80+1, 16)
|
||||
ax.set_xticks(xticks)
|
||||
ax.set_xticklabels(['%dKiB' % ((t*(4096/16))/1024) for t in xticks])
|
||||
yticks = np.arange(0, 6+1, 2)
|
||||
ax.set_yticks(yticks)
|
||||
ax.set_yticklabels(['%dx' % t for t in yticks])
|
||||
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['top'].set_visible(False)
|
||||
|
||||
fig.tight_layout()
|
||||
plt.savefig('file-cost.svg', bbox_inches="tight")
|
||||
995
file-cost.svg
Normal file
995
file-cost.svg
Normal file
|
|
@ -0,0 +1,995 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Created with matplotlib (http://matplotlib.org/) -->
|
||||
<svg height="241.712285pt" version="1.1" viewBox="0 0 422.509804 241.712285" width="422.509804pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
*{stroke-linecap:butt;stroke-linejoin:round;}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 241.712285
|
||||
L 422.509804 241.712285
|
||||
L 422.509804 0
|
||||
L 0 0
|
||||
z
|
||||
" style="fill:#ffffff;"/>
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="patch_2">
|
||||
<path d="M 40.099247 203.998281
|
||||
L 402.059247 203.998281
|
||||
L 402.059247 21.558281
|
||||
L 40.099247 21.558281
|
||||
z
|
||||
" style="fill:#ffffff;"/>
|
||||
</g>
|
||||
<g id="matplotlib.axis_1">
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path d="M 0 0
|
||||
L 0 3.5
|
||||
" id="mf543708c25" style="stroke:#000000;stroke-width:0.8;"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="40.099247" xlink:href="#mf543708c25" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
<!-- 0KiB -->
|
||||
<defs>
|
||||
<path d="M 31.78125 66.40625
|
||||
Q 24.171875 66.40625 20.328125 58.90625
|
||||
Q 16.5 51.421875 16.5 36.375
|
||||
Q 16.5 21.390625 20.328125 13.890625
|
||||
Q 24.171875 6.390625 31.78125 6.390625
|
||||
Q 39.453125 6.390625 43.28125 13.890625
|
||||
Q 47.125 21.390625 47.125 36.375
|
||||
Q 47.125 51.421875 43.28125 58.90625
|
||||
Q 39.453125 66.40625 31.78125 66.40625
|
||||
z
|
||||
M 31.78125 74.21875
|
||||
Q 44.046875 74.21875 50.515625 64.515625
|
||||
Q 56.984375 54.828125 56.984375 36.375
|
||||
Q 56.984375 17.96875 50.515625 8.265625
|
||||
Q 44.046875 -1.421875 31.78125 -1.421875
|
||||
Q 19.53125 -1.421875 13.0625 8.265625
|
||||
Q 6.59375 17.96875 6.59375 36.375
|
||||
Q 6.59375 54.828125 13.0625 64.515625
|
||||
Q 19.53125 74.21875 31.78125 74.21875
|
||||
z
|
||||
" id="DejaVuSans-30"/>
|
||||
<path d="M 9.8125 72.90625
|
||||
L 19.671875 72.90625
|
||||
L 19.671875 42.09375
|
||||
L 52.390625 72.90625
|
||||
L 65.09375 72.90625
|
||||
L 28.90625 38.921875
|
||||
L 67.671875 0
|
||||
L 54.6875 0
|
||||
L 19.671875 35.109375
|
||||
L 19.671875 0
|
||||
L 9.8125 0
|
||||
z
|
||||
" id="DejaVuSans-4b"/>
|
||||
<path d="M 9.421875 54.6875
|
||||
L 18.40625 54.6875
|
||||
L 18.40625 0
|
||||
L 9.421875 0
|
||||
z
|
||||
M 9.421875 75.984375
|
||||
L 18.40625 75.984375
|
||||
L 18.40625 64.59375
|
||||
L 9.421875 64.59375
|
||||
z
|
||||
" id="DejaVuSans-69"/>
|
||||
<path d="M 19.671875 34.8125
|
||||
L 19.671875 8.109375
|
||||
L 35.5 8.109375
|
||||
Q 43.453125 8.109375 47.28125 11.40625
|
||||
Q 51.125 14.703125 51.125 21.484375
|
||||
Q 51.125 28.328125 47.28125 31.5625
|
||||
Q 43.453125 34.8125 35.5 34.8125
|
||||
z
|
||||
M 19.671875 64.796875
|
||||
L 19.671875 42.828125
|
||||
L 34.28125 42.828125
|
||||
Q 41.5 42.828125 45.03125 45.53125
|
||||
Q 48.578125 48.25 48.578125 53.8125
|
||||
Q 48.578125 59.328125 45.03125 62.0625
|
||||
Q 41.5 64.796875 34.28125 64.796875
|
||||
z
|
||||
M 9.8125 72.90625
|
||||
L 35.015625 72.90625
|
||||
Q 46.296875 72.90625 52.390625 68.21875
|
||||
Q 58.5 63.53125 58.5 54.890625
|
||||
Q 58.5 48.1875 55.375 44.234375
|
||||
Q 52.25 40.28125 46.1875 39.3125
|
||||
Q 53.46875 37.75 57.5 32.78125
|
||||
Q 61.53125 27.828125 61.53125 20.40625
|
||||
Q 61.53125 10.640625 54.890625 5.3125
|
||||
Q 48.25 0 35.984375 0
|
||||
L 9.8125 0
|
||||
z
|
||||
" id="DejaVuSans-42"/>
|
||||
</defs>
|
||||
<g transform="translate(29.763669 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-30"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-4b"/>
|
||||
<use x="129.199219" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="156.982422" xlink:href="#DejaVuSans-42"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="112.491247" xlink:href="#mf543708c25" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
<!-- 4KiB -->
|
||||
<defs>
|
||||
<path d="M 37.796875 64.3125
|
||||
L 12.890625 25.390625
|
||||
L 37.796875 25.390625
|
||||
z
|
||||
M 35.203125 72.90625
|
||||
L 47.609375 72.90625
|
||||
L 47.609375 25.390625
|
||||
L 58.015625 25.390625
|
||||
L 58.015625 17.1875
|
||||
L 47.609375 17.1875
|
||||
L 47.609375 0
|
||||
L 37.796875 0
|
||||
L 37.796875 17.1875
|
||||
L 4.890625 17.1875
|
||||
L 4.890625 26.703125
|
||||
z
|
||||
" id="DejaVuSans-34"/>
|
||||
</defs>
|
||||
<g transform="translate(102.155669 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-34"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-4b"/>
|
||||
<use x="129.199219" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="156.982422" xlink:href="#DejaVuSans-42"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="184.883247" xlink:href="#mf543708c25" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
<!-- 8KiB -->
|
||||
<defs>
|
||||
<path d="M 31.78125 34.625
|
||||
Q 24.75 34.625 20.71875 30.859375
|
||||
Q 16.703125 27.09375 16.703125 20.515625
|
||||
Q 16.703125 13.921875 20.71875 10.15625
|
||||
Q 24.75 6.390625 31.78125 6.390625
|
||||
Q 38.8125 6.390625 42.859375 10.171875
|
||||
Q 46.921875 13.96875 46.921875 20.515625
|
||||
Q 46.921875 27.09375 42.890625 30.859375
|
||||
Q 38.875 34.625 31.78125 34.625
|
||||
z
|
||||
M 21.921875 38.8125
|
||||
Q 15.578125 40.375 12.03125 44.71875
|
||||
Q 8.5 49.078125 8.5 55.328125
|
||||
Q 8.5 64.0625 14.71875 69.140625
|
||||
Q 20.953125 74.21875 31.78125 74.21875
|
||||
Q 42.671875 74.21875 48.875 69.140625
|
||||
Q 55.078125 64.0625 55.078125 55.328125
|
||||
Q 55.078125 49.078125 51.53125 44.71875
|
||||
Q 48 40.375 41.703125 38.8125
|
||||
Q 48.828125 37.15625 52.796875 32.3125
|
||||
Q 56.78125 27.484375 56.78125 20.515625
|
||||
Q 56.78125 9.90625 50.3125 4.234375
|
||||
Q 43.84375 -1.421875 31.78125 -1.421875
|
||||
Q 19.734375 -1.421875 13.25 4.234375
|
||||
Q 6.78125 9.90625 6.78125 20.515625
|
||||
Q 6.78125 27.484375 10.78125 32.3125
|
||||
Q 14.796875 37.15625 21.921875 38.8125
|
||||
z
|
||||
M 18.3125 54.390625
|
||||
Q 18.3125 48.734375 21.84375 45.5625
|
||||
Q 25.390625 42.390625 31.78125 42.390625
|
||||
Q 38.140625 42.390625 41.71875 45.5625
|
||||
Q 45.3125 48.734375 45.3125 54.390625
|
||||
Q 45.3125 60.0625 41.71875 63.234375
|
||||
Q 38.140625 66.40625 31.78125 66.40625
|
||||
Q 25.390625 66.40625 21.84375 63.234375
|
||||
Q 18.3125 60.0625 18.3125 54.390625
|
||||
z
|
||||
" id="DejaVuSans-38"/>
|
||||
</defs>
|
||||
<g transform="translate(174.547669 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-38"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-4b"/>
|
||||
<use x="129.199219" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="156.982422" xlink:href="#DejaVuSans-42"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="257.275247" xlink:href="#mf543708c25" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
<!-- 12KiB -->
|
||||
<defs>
|
||||
<path d="M 12.40625 8.296875
|
||||
L 28.515625 8.296875
|
||||
L 28.515625 63.921875
|
||||
L 10.984375 60.40625
|
||||
L 10.984375 69.390625
|
||||
L 28.421875 72.90625
|
||||
L 38.28125 72.90625
|
||||
L 38.28125 8.296875
|
||||
L 54.390625 8.296875
|
||||
L 54.390625 0
|
||||
L 12.40625 0
|
||||
z
|
||||
" id="DejaVuSans-31"/>
|
||||
<path d="M 19.1875 8.296875
|
||||
L 53.609375 8.296875
|
||||
L 53.609375 0
|
||||
L 7.328125 0
|
||||
L 7.328125 8.296875
|
||||
Q 12.9375 14.109375 22.625 23.890625
|
||||
Q 32.328125 33.6875 34.8125 36.53125
|
||||
Q 39.546875 41.84375 41.421875 45.53125
|
||||
Q 43.3125 49.21875 43.3125 52.78125
|
||||
Q 43.3125 58.59375 39.234375 62.25
|
||||
Q 35.15625 65.921875 28.609375 65.921875
|
||||
Q 23.96875 65.921875 18.8125 64.3125
|
||||
Q 13.671875 62.703125 7.8125 59.421875
|
||||
L 7.8125 69.390625
|
||||
Q 13.765625 71.78125 18.9375 73
|
||||
Q 24.125 74.21875 28.421875 74.21875
|
||||
Q 39.75 74.21875 46.484375 68.546875
|
||||
Q 53.21875 62.890625 53.21875 53.421875
|
||||
Q 53.21875 48.921875 51.53125 44.890625
|
||||
Q 49.859375 40.875 45.40625 35.40625
|
||||
Q 44.1875 33.984375 37.640625 27.21875
|
||||
Q 31.109375 20.453125 19.1875 8.296875
|
||||
z
|
||||
" id="DejaVuSans-32"/>
|
||||
</defs>
|
||||
<g transform="translate(244.02469 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-32"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-4b"/>
|
||||
<use x="192.822266" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="220.605469" xlink:href="#DejaVuSans-42"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="329.667247" xlink:href="#mf543708c25" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
<!-- 16KiB -->
|
||||
<defs>
|
||||
<path d="M 33.015625 40.375
|
||||
Q 26.375 40.375 22.484375 35.828125
|
||||
Q 18.609375 31.296875 18.609375 23.390625
|
||||
Q 18.609375 15.53125 22.484375 10.953125
|
||||
Q 26.375 6.390625 33.015625 6.390625
|
||||
Q 39.65625 6.390625 43.53125 10.953125
|
||||
Q 47.40625 15.53125 47.40625 23.390625
|
||||
Q 47.40625 31.296875 43.53125 35.828125
|
||||
Q 39.65625 40.375 33.015625 40.375
|
||||
z
|
||||
M 52.59375 71.296875
|
||||
L 52.59375 62.3125
|
||||
Q 48.875 64.0625 45.09375 64.984375
|
||||
Q 41.3125 65.921875 37.59375 65.921875
|
||||
Q 27.828125 65.921875 22.671875 59.328125
|
||||
Q 17.53125 52.734375 16.796875 39.40625
|
||||
Q 19.671875 43.65625 24.015625 45.921875
|
||||
Q 28.375 48.1875 33.59375 48.1875
|
||||
Q 44.578125 48.1875 50.953125 41.515625
|
||||
Q 57.328125 34.859375 57.328125 23.390625
|
||||
Q 57.328125 12.15625 50.6875 5.359375
|
||||
Q 44.046875 -1.421875 33.015625 -1.421875
|
||||
Q 20.359375 -1.421875 13.671875 8.265625
|
||||
Q 6.984375 17.96875 6.984375 36.375
|
||||
Q 6.984375 53.65625 15.1875 63.9375
|
||||
Q 23.390625 74.21875 37.203125 74.21875
|
||||
Q 40.921875 74.21875 44.703125 73.484375
|
||||
Q 48.484375 72.75 52.59375 71.296875
|
||||
z
|
||||
" id="DejaVuSans-36"/>
|
||||
</defs>
|
||||
<g transform="translate(316.41669 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-36"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-4b"/>
|
||||
<use x="192.822266" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="220.605469" xlink:href="#DejaVuSans-42"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="402.059247" xlink:href="#mf543708c25" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
<!-- 20KiB -->
|
||||
<g transform="translate(388.80869 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-32"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-4b"/>
|
||||
<use x="192.822266" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="220.605469" xlink:href="#DejaVuSans-42"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
<!-- file size -->
|
||||
<defs>
|
||||
<path d="M 37.109375 75.984375
|
||||
L 37.109375 68.5
|
||||
L 28.515625 68.5
|
||||
Q 23.6875 68.5 21.796875 66.546875
|
||||
Q 19.921875 64.59375 19.921875 59.515625
|
||||
L 19.921875 54.6875
|
||||
L 34.71875 54.6875
|
||||
L 34.71875 47.703125
|
||||
L 19.921875 47.703125
|
||||
L 19.921875 0
|
||||
L 10.890625 0
|
||||
L 10.890625 47.703125
|
||||
L 2.296875 47.703125
|
||||
L 2.296875 54.6875
|
||||
L 10.890625 54.6875
|
||||
L 10.890625 58.5
|
||||
Q 10.890625 67.625 15.140625 71.796875
|
||||
Q 19.390625 75.984375 28.609375 75.984375
|
||||
z
|
||||
" id="DejaVuSans-66"/>
|
||||
<path d="M 9.421875 75.984375
|
||||
L 18.40625 75.984375
|
||||
L 18.40625 0
|
||||
L 9.421875 0
|
||||
z
|
||||
" id="DejaVuSans-6c"/>
|
||||
<path d="M 56.203125 29.59375
|
||||
L 56.203125 25.203125
|
||||
L 14.890625 25.203125
|
||||
Q 15.484375 15.921875 20.484375 11.0625
|
||||
Q 25.484375 6.203125 34.421875 6.203125
|
||||
Q 39.59375 6.203125 44.453125 7.46875
|
||||
Q 49.3125 8.734375 54.109375 11.28125
|
||||
L 54.109375 2.78125
|
||||
Q 49.265625 0.734375 44.1875 -0.34375
|
||||
Q 39.109375 -1.421875 33.890625 -1.421875
|
||||
Q 20.796875 -1.421875 13.15625 6.1875
|
||||
Q 5.515625 13.8125 5.515625 26.8125
|
||||
Q 5.515625 40.234375 12.765625 48.109375
|
||||
Q 20.015625 56 32.328125 56
|
||||
Q 43.359375 56 49.78125 48.890625
|
||||
Q 56.203125 41.796875 56.203125 29.59375
|
||||
z
|
||||
M 47.21875 32.234375
|
||||
Q 47.125 39.59375 43.09375 43.984375
|
||||
Q 39.0625 48.390625 32.421875 48.390625
|
||||
Q 24.90625 48.390625 20.390625 44.140625
|
||||
Q 15.875 39.890625 15.1875 32.171875
|
||||
z
|
||||
" id="DejaVuSans-65"/>
|
||||
<path id="DejaVuSans-20"/>
|
||||
<path d="M 44.28125 53.078125
|
||||
L 44.28125 44.578125
|
||||
Q 40.484375 46.53125 36.375 47.5
|
||||
Q 32.28125 48.484375 27.875 48.484375
|
||||
Q 21.1875 48.484375 17.84375 46.4375
|
||||
Q 14.5 44.390625 14.5 40.28125
|
||||
Q 14.5 37.15625 16.890625 35.375
|
||||
Q 19.28125 33.59375 26.515625 31.984375
|
||||
L 29.59375 31.296875
|
||||
Q 39.15625 29.25 43.1875 25.515625
|
||||
Q 47.21875 21.78125 47.21875 15.09375
|
||||
Q 47.21875 7.46875 41.1875 3.015625
|
||||
Q 35.15625 -1.421875 24.609375 -1.421875
|
||||
Q 20.21875 -1.421875 15.453125 -0.5625
|
||||
Q 10.6875 0.296875 5.421875 2
|
||||
L 5.421875 11.28125
|
||||
Q 10.40625 8.6875 15.234375 7.390625
|
||||
Q 20.0625 6.109375 24.8125 6.109375
|
||||
Q 31.15625 6.109375 34.5625 8.28125
|
||||
Q 37.984375 10.453125 37.984375 14.40625
|
||||
Q 37.984375 18.0625 35.515625 20.015625
|
||||
Q 33.0625 21.96875 24.703125 23.78125
|
||||
L 21.578125 24.515625
|
||||
Q 13.234375 26.265625 9.515625 29.90625
|
||||
Q 5.8125 33.546875 5.8125 39.890625
|
||||
Q 5.8125 47.609375 11.28125 51.796875
|
||||
Q 16.75 56 26.8125 56
|
||||
Q 31.78125 56 36.171875 55.265625
|
||||
Q 40.578125 54.546875 44.28125 53.078125
|
||||
z
|
||||
" id="DejaVuSans-73"/>
|
||||
<path d="M 5.515625 54.6875
|
||||
L 48.1875 54.6875
|
||||
L 48.1875 46.484375
|
||||
L 14.40625 7.171875
|
||||
L 48.1875 7.171875
|
||||
L 48.1875 0
|
||||
L 4.296875 0
|
||||
L 4.296875 8.203125
|
||||
L 38.09375 47.515625
|
||||
L 5.515625 47.515625
|
||||
z
|
||||
" id="DejaVuSans-7a"/>
|
||||
</defs>
|
||||
<g transform="translate(200.290966 232.224628)scale(0.11 -0.11)">
|
||||
<use xlink:href="#DejaVuSans-66"/>
|
||||
<use x="35.205078" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="62.988281" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="90.771484" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="152.294922" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="184.082031" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="236.181641" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="263.964844" xlink:href="#DejaVuSans-7a"/>
|
||||
<use x="316.455078" xlink:href="#DejaVuSans-65"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="matplotlib.axis_2">
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_7">
|
||||
<defs>
|
||||
<path d="M 0 0
|
||||
L -3.5 0
|
||||
" id="medd6d3c8aa" style="stroke:#000000;stroke-width:0.8;"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="40.099247" xlink:href="#medd6d3c8aa" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
<!-- 0x -->
|
||||
<defs>
|
||||
<path d="M 54.890625 54.6875
|
||||
L 35.109375 28.078125
|
||||
L 55.90625 0
|
||||
L 45.3125 0
|
||||
L 29.390625 21.484375
|
||||
L 13.484375 0
|
||||
L 2.875 0
|
||||
L 24.125 28.609375
|
||||
L 4.6875 54.6875
|
||||
L 15.28125 54.6875
|
||||
L 29.78125 35.203125
|
||||
L 44.28125 54.6875
|
||||
z
|
||||
" id="DejaVuSans-78"/>
|
||||
</defs>
|
||||
<g transform="translate(21.845938 207.479505)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-30"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="40.099247" xlink:href="#medd6d3c8aa" y="143.184948"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
<!-- 2x -->
|
||||
<g transform="translate(21.845938 146.666172)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-32"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_9">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="40.099247" xlink:href="#medd6d3c8aa" y="82.371615"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
<!-- 4x -->
|
||||
<g transform="translate(21.845938 85.852839)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-34"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_10">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="40.099247" xlink:href="#medd6d3c8aa" y="21.558281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
<!-- 6x -->
|
||||
<g transform="translate(21.845938 25.039505)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-36"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
<!-- multiplicative cost -->
|
||||
<defs>
|
||||
<path d="M 52 44.1875
|
||||
Q 55.375 50.25 60.0625 53.125
|
||||
Q 64.75 56 71.09375 56
|
||||
Q 79.640625 56 84.28125 50.015625
|
||||
Q 88.921875 44.046875 88.921875 33.015625
|
||||
L 88.921875 0
|
||||
L 79.890625 0
|
||||
L 79.890625 32.71875
|
||||
Q 79.890625 40.578125 77.09375 44.375
|
||||
Q 74.3125 48.1875 68.609375 48.1875
|
||||
Q 61.625 48.1875 57.5625 43.546875
|
||||
Q 53.515625 38.921875 53.515625 30.90625
|
||||
L 53.515625 0
|
||||
L 44.484375 0
|
||||
L 44.484375 32.71875
|
||||
Q 44.484375 40.625 41.703125 44.40625
|
||||
Q 38.921875 48.1875 33.109375 48.1875
|
||||
Q 26.21875 48.1875 22.15625 43.53125
|
||||
Q 18.109375 38.875 18.109375 30.90625
|
||||
L 18.109375 0
|
||||
L 9.078125 0
|
||||
L 9.078125 54.6875
|
||||
L 18.109375 54.6875
|
||||
L 18.109375 46.1875
|
||||
Q 21.1875 51.21875 25.484375 53.609375
|
||||
Q 29.78125 56 35.6875 56
|
||||
Q 41.65625 56 45.828125 52.96875
|
||||
Q 50 49.953125 52 44.1875
|
||||
z
|
||||
" id="DejaVuSans-6d"/>
|
||||
<path d="M 8.5 21.578125
|
||||
L 8.5 54.6875
|
||||
L 17.484375 54.6875
|
||||
L 17.484375 21.921875
|
||||
Q 17.484375 14.15625 20.5 10.265625
|
||||
Q 23.53125 6.390625 29.59375 6.390625
|
||||
Q 36.859375 6.390625 41.078125 11.03125
|
||||
Q 45.3125 15.671875 45.3125 23.6875
|
||||
L 45.3125 54.6875
|
||||
L 54.296875 54.6875
|
||||
L 54.296875 0
|
||||
L 45.3125 0
|
||||
L 45.3125 8.40625
|
||||
Q 42.046875 3.421875 37.71875 1
|
||||
Q 33.40625 -1.421875 27.6875 -1.421875
|
||||
Q 18.265625 -1.421875 13.375 4.4375
|
||||
Q 8.5 10.296875 8.5 21.578125
|
||||
z
|
||||
M 31.109375 56
|
||||
z
|
||||
" id="DejaVuSans-75"/>
|
||||
<path d="M 18.3125 70.21875
|
||||
L 18.3125 54.6875
|
||||
L 36.8125 54.6875
|
||||
L 36.8125 47.703125
|
||||
L 18.3125 47.703125
|
||||
L 18.3125 18.015625
|
||||
Q 18.3125 11.328125 20.140625 9.421875
|
||||
Q 21.96875 7.515625 27.59375 7.515625
|
||||
L 36.8125 7.515625
|
||||
L 36.8125 0
|
||||
L 27.59375 0
|
||||
Q 17.1875 0 13.234375 3.875
|
||||
Q 9.28125 7.765625 9.28125 18.015625
|
||||
L 9.28125 47.703125
|
||||
L 2.6875 47.703125
|
||||
L 2.6875 54.6875
|
||||
L 9.28125 54.6875
|
||||
L 9.28125 70.21875
|
||||
z
|
||||
" id="DejaVuSans-74"/>
|
||||
<path d="M 18.109375 8.203125
|
||||
L 18.109375 -20.796875
|
||||
L 9.078125 -20.796875
|
||||
L 9.078125 54.6875
|
||||
L 18.109375 54.6875
|
||||
L 18.109375 46.390625
|
||||
Q 20.953125 51.265625 25.265625 53.625
|
||||
Q 29.59375 56 35.59375 56
|
||||
Q 45.5625 56 51.78125 48.09375
|
||||
Q 58.015625 40.1875 58.015625 27.296875
|
||||
Q 58.015625 14.40625 51.78125 6.484375
|
||||
Q 45.5625 -1.421875 35.59375 -1.421875
|
||||
Q 29.59375 -1.421875 25.265625 0.953125
|
||||
Q 20.953125 3.328125 18.109375 8.203125
|
||||
z
|
||||
M 48.6875 27.296875
|
||||
Q 48.6875 37.203125 44.609375 42.84375
|
||||
Q 40.53125 48.484375 33.40625 48.484375
|
||||
Q 26.265625 48.484375 22.1875 42.84375
|
||||
Q 18.109375 37.203125 18.109375 27.296875
|
||||
Q 18.109375 17.390625 22.1875 11.75
|
||||
Q 26.265625 6.109375 33.40625 6.109375
|
||||
Q 40.53125 6.109375 44.609375 11.75
|
||||
Q 48.6875 17.390625 48.6875 27.296875
|
||||
z
|
||||
" id="DejaVuSans-70"/>
|
||||
<path d="M 48.78125 52.59375
|
||||
L 48.78125 44.1875
|
||||
Q 44.96875 46.296875 41.140625 47.34375
|
||||
Q 37.3125 48.390625 33.40625 48.390625
|
||||
Q 24.65625 48.390625 19.8125 42.84375
|
||||
Q 14.984375 37.3125 14.984375 27.296875
|
||||
Q 14.984375 17.28125 19.8125 11.734375
|
||||
Q 24.65625 6.203125 33.40625 6.203125
|
||||
Q 37.3125 6.203125 41.140625 7.25
|
||||
Q 44.96875 8.296875 48.78125 10.40625
|
||||
L 48.78125 2.09375
|
||||
Q 45.015625 0.34375 40.984375 -0.53125
|
||||
Q 36.96875 -1.421875 32.421875 -1.421875
|
||||
Q 20.0625 -1.421875 12.78125 6.34375
|
||||
Q 5.515625 14.109375 5.515625 27.296875
|
||||
Q 5.515625 40.671875 12.859375 48.328125
|
||||
Q 20.21875 56 33.015625 56
|
||||
Q 37.15625 56 41.109375 55.140625
|
||||
Q 45.0625 54.296875 48.78125 52.59375
|
||||
z
|
||||
" id="DejaVuSans-63"/>
|
||||
<path d="M 34.28125 27.484375
|
||||
Q 23.390625 27.484375 19.1875 25
|
||||
Q 14.984375 22.515625 14.984375 16.5
|
||||
Q 14.984375 11.71875 18.140625 8.90625
|
||||
Q 21.296875 6.109375 26.703125 6.109375
|
||||
Q 34.1875 6.109375 38.703125 11.40625
|
||||
Q 43.21875 16.703125 43.21875 25.484375
|
||||
L 43.21875 27.484375
|
||||
z
|
||||
M 52.203125 31.203125
|
||||
L 52.203125 0
|
||||
L 43.21875 0
|
||||
L 43.21875 8.296875
|
||||
Q 40.140625 3.328125 35.546875 0.953125
|
||||
Q 30.953125 -1.421875 24.3125 -1.421875
|
||||
Q 15.921875 -1.421875 10.953125 3.296875
|
||||
Q 6 8.015625 6 15.921875
|
||||
Q 6 25.140625 12.171875 29.828125
|
||||
Q 18.359375 34.515625 30.609375 34.515625
|
||||
L 43.21875 34.515625
|
||||
L 43.21875 35.40625
|
||||
Q 43.21875 41.609375 39.140625 45
|
||||
Q 35.0625 48.390625 27.6875 48.390625
|
||||
Q 23 48.390625 18.546875 47.265625
|
||||
Q 14.109375 46.140625 10.015625 43.890625
|
||||
L 10.015625 52.203125
|
||||
Q 14.9375 54.109375 19.578125 55.046875
|
||||
Q 24.21875 56 28.609375 56
|
||||
Q 40.484375 56 46.34375 49.84375
|
||||
Q 52.203125 43.703125 52.203125 31.203125
|
||||
z
|
||||
" id="DejaVuSans-61"/>
|
||||
<path d="M 2.984375 54.6875
|
||||
L 12.5 54.6875
|
||||
L 29.59375 8.796875
|
||||
L 46.6875 54.6875
|
||||
L 56.203125 54.6875
|
||||
L 35.6875 0
|
||||
L 23.484375 0
|
||||
z
|
||||
" id="DejaVuSans-76"/>
|
||||
<path d="M 30.609375 48.390625
|
||||
Q 23.390625 48.390625 19.1875 42.75
|
||||
Q 14.984375 37.109375 14.984375 27.296875
|
||||
Q 14.984375 17.484375 19.15625 11.84375
|
||||
Q 23.34375 6.203125 30.609375 6.203125
|
||||
Q 37.796875 6.203125 41.984375 11.859375
|
||||
Q 46.1875 17.53125 46.1875 27.296875
|
||||
Q 46.1875 37.015625 41.984375 42.703125
|
||||
Q 37.796875 48.390625 30.609375 48.390625
|
||||
z
|
||||
M 30.609375 56
|
||||
Q 42.328125 56 49.015625 48.375
|
||||
Q 55.71875 40.765625 55.71875 27.296875
|
||||
Q 55.71875 13.875 49.015625 6.21875
|
||||
Q 42.328125 -1.421875 30.609375 -1.421875
|
||||
Q 18.84375 -1.421875 12.171875 6.21875
|
||||
Q 5.515625 13.875 5.515625 27.296875
|
||||
Q 5.515625 40.765625 12.171875 48.375
|
||||
Q 18.84375 56 30.609375 56
|
||||
z
|
||||
" id="DejaVuSans-6f"/>
|
||||
</defs>
|
||||
<g transform="translate(15.558281 163.257969)rotate(-90)scale(0.11 -0.11)">
|
||||
<use xlink:href="#DejaVuSans-6d"/>
|
||||
<use x="97.412109" xlink:href="#DejaVuSans-75"/>
|
||||
<use x="160.791016" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="188.574219" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="227.783203" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="255.566406" xlink:href="#DejaVuSans-70"/>
|
||||
<use x="319.042969" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="346.826172" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="374.609375" xlink:href="#DejaVuSans-63"/>
|
||||
<use x="429.589844" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="490.869141" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="530.078125" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="557.861328" xlink:href="#DejaVuSans-76"/>
|
||||
<use x="617.041016" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="678.564453" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="710.351562" xlink:href="#DejaVuSans-63"/>
|
||||
<use x="765.332031" xlink:href="#DejaVuSans-6f"/>
|
||||
<use x="826.513672" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="878.613281" xlink:href="#DejaVuSans-74"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="line2d_11">
|
||||
<path clip-path="url(#pdf50752646)" d="M 40.099247 82.371615
|
||||
L 58.197247 82.371615
|
||||
L 59.328372 89.526124
|
||||
L 60.459497 95.885689
|
||||
L 62.721747 106.696948
|
||||
L 64.983997 115.542524
|
||||
L 67.246247 122.913837
|
||||
L 69.508497 129.151102
|
||||
L 71.770747 134.497329
|
||||
L 74.032997 139.130726
|
||||
L 76.295247 143.184948
|
||||
L 78.557497 146.762203
|
||||
L 80.819747 149.941985
|
||||
L 83.081997 152.787053
|
||||
L 85.344247 155.347615
|
||||
L 87.606497 157.664313
|
||||
L 90.999872 160.753244
|
||||
L 94.393247 163.456059
|
||||
L 97.786622 165.840896
|
||||
L 101.179997 167.96075
|
||||
L 104.573372 169.857463
|
||||
L 109.097872 172.096205
|
||||
L 112.491247 173.591615
|
||||
L 113.622372 144.120538
|
||||
L 117.015747 146.762203
|
||||
L 120.409122 149.180629
|
||||
L 124.933622 152.104237
|
||||
L 129.458122 154.731783
|
||||
L 133.982622 157.106072
|
||||
L 138.507122 159.262036
|
||||
L 144.162747 161.693354
|
||||
L 149.818372 163.87402
|
||||
L 155.473997 165.840896
|
||||
L 162.260747 167.96075
|
||||
L 169.047497 169.857463
|
||||
L 176.965372 171.832551
|
||||
L 184.883247 173.591615
|
||||
L 186.014372 158.741847
|
||||
L 193.932247 161.071222
|
||||
L 201.850122 163.172547
|
||||
L 210.899122 165.3355
|
||||
L 219.948122 167.280797
|
||||
L 230.128247 169.247805
|
||||
L 241.439497 171.200079
|
||||
L 253.881872 173.108969
|
||||
L 257.275247 173.591615
|
||||
L 258.406372 163.666122
|
||||
L 270.848747 165.840896
|
||||
L 283.291122 167.793134
|
||||
L 296.864622 169.707062
|
||||
L 311.569247 171.564503
|
||||
L 328.536122 173.472373
|
||||
L 329.667247 173.591615
|
||||
L 330.798372 166.13784
|
||||
L 346.634122 168.09373
|
||||
L 364.732122 170.095378
|
||||
L 383.961247 171.991264
|
||||
L 400.928122 173.496296
|
||||
L 400.928122 173.496296
|
||||
" style="fill:none;stroke:#1f77b4;stroke-linecap:square;stroke-width:1.5;"/>
|
||||
</g>
|
||||
<g id="line2d_12">
|
||||
<path clip-path="url(#pdf50752646)" d="M 50.866678 -1
|
||||
L 51.410497 9.395615
|
||||
L 52.541622 27.086766
|
||||
L 53.672747 41.829392
|
||||
L 54.803872 54.303922
|
||||
L 55.934997 64.996376
|
||||
L 57.066122 74.26317
|
||||
L 58.197247 82.371615
|
||||
L 59.328372 89.526124
|
||||
" style="fill:none;stroke:#1f77b4;stroke-dasharray:5.475,2.475;stroke-dashoffset:3.975;stroke-width:1.5;"/>
|
||||
</g>
|
||||
<g id="line2d_13">
|
||||
<path clip-path="url(#pdf50752646)" d="M 58.197247 82.371615
|
||||
L 400.928122 82.371615
|
||||
L 400.928122 82.371615
|
||||
" style="fill:none;stroke:#1f77b4;stroke-dasharray:5.475,2.475;stroke-dashoffset:5.475;stroke-width:1.5;"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 40.099247 203.998281
|
||||
L 40.099247 21.558281
|
||||
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
|
||||
</g>
|
||||
<g id="patch_4">
|
||||
<path d="M 40.099247 203.998281
|
||||
L 402.059247 203.998281
|
||||
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
<!-- inline file -->
|
||||
<defs>
|
||||
<path d="M 54.890625 33.015625
|
||||
L 54.890625 0
|
||||
L 45.90625 0
|
||||
L 45.90625 32.71875
|
||||
Q 45.90625 40.484375 42.875 44.328125
|
||||
Q 39.84375 48.1875 33.796875 48.1875
|
||||
Q 26.515625 48.1875 22.3125 43.546875
|
||||
Q 18.109375 38.921875 18.109375 30.90625
|
||||
L 18.109375 0
|
||||
L 9.078125 0
|
||||
L 9.078125 54.6875
|
||||
L 18.109375 54.6875
|
||||
L 18.109375 46.1875
|
||||
Q 21.34375 51.125 25.703125 53.5625
|
||||
Q 30.078125 56 35.796875 56
|
||||
Q 45.21875 56 50.046875 50.171875
|
||||
Q 54.890625 44.34375 54.890625 33.015625
|
||||
z
|
||||
" id="DejaVuSans-6e"/>
|
||||
</defs>
|
||||
<g transform="translate(359.076497 77.810615)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-69"/>
|
||||
<use x="27.783203" xlink:href="#DejaVuSans-6e"/>
|
||||
<use x="91.162109" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="118.945312" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="146.728516" xlink:href="#DejaVuSans-6e"/>
|
||||
<use x="210.107422" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="271.630859" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="303.417969" xlink:href="#DejaVuSans-66"/>
|
||||
<use x="338.623047" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="366.40625" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="394.189453" xlink:href="#DejaVuSans-65"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
<!-- ctz skip-list -->
|
||||
<defs>
|
||||
<path d="M 9.078125 75.984375
|
||||
L 18.109375 75.984375
|
||||
L 18.109375 31.109375
|
||||
L 44.921875 54.6875
|
||||
L 56.390625 54.6875
|
||||
L 27.390625 29.109375
|
||||
L 57.625 0
|
||||
L 45.90625 0
|
||||
L 18.109375 26.703125
|
||||
L 18.109375 0
|
||||
L 9.078125 0
|
||||
z
|
||||
" id="DejaVuSans-6b"/>
|
||||
<path d="M 4.890625 31.390625
|
||||
L 31.203125 31.390625
|
||||
L 31.203125 23.390625
|
||||
L 4.890625 23.390625
|
||||
z
|
||||
" id="DejaVuSans-2d"/>
|
||||
</defs>
|
||||
<g transform="translate(56.928463 19.76365)rotate(-275)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-63"/>
|
||||
<use x="54.980469" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="94.189453" xlink:href="#DejaVuSans-7a"/>
|
||||
<use x="146.679688" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="178.466797" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="230.566406" xlink:href="#DejaVuSans-6b"/>
|
||||
<use x="288.476562" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="316.259766" xlink:href="#DejaVuSans-70"/>
|
||||
<use x="379.736328" xlink:href="#DejaVuSans-2d"/>
|
||||
<use x="415.820312" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="443.603516" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="471.386719" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="523.486328" xlink:href="#DejaVuSans-74"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
<!-- File storage cost -->
|
||||
<defs>
|
||||
<path d="M 9.8125 72.90625
|
||||
L 51.703125 72.90625
|
||||
L 51.703125 64.59375
|
||||
L 19.671875 64.59375
|
||||
L 19.671875 43.109375
|
||||
L 48.578125 43.109375
|
||||
L 48.578125 34.8125
|
||||
L 19.671875 34.8125
|
||||
L 19.671875 0
|
||||
L 9.8125 0
|
||||
z
|
||||
" id="DejaVuSans-46"/>
|
||||
<path d="M 41.109375 46.296875
|
||||
Q 39.59375 47.171875 37.8125 47.578125
|
||||
Q 36.03125 48 33.890625 48
|
||||
Q 26.265625 48 22.1875 43.046875
|
||||
Q 18.109375 38.09375 18.109375 28.8125
|
||||
L 18.109375 0
|
||||
L 9.078125 0
|
||||
L 9.078125 54.6875
|
||||
L 18.109375 54.6875
|
||||
L 18.109375 46.1875
|
||||
Q 20.953125 51.171875 25.484375 53.578125
|
||||
Q 30.03125 56 36.53125 56
|
||||
Q 37.453125 56 38.578125 55.875
|
||||
Q 39.703125 55.765625 41.0625 55.515625
|
||||
z
|
||||
" id="DejaVuSans-72"/>
|
||||
<path d="M 45.40625 27.984375
|
||||
Q 45.40625 37.75 41.375 43.109375
|
||||
Q 37.359375 48.484375 30.078125 48.484375
|
||||
Q 22.859375 48.484375 18.828125 43.109375
|
||||
Q 14.796875 37.75 14.796875 27.984375
|
||||
Q 14.796875 18.265625 18.828125 12.890625
|
||||
Q 22.859375 7.515625 30.078125 7.515625
|
||||
Q 37.359375 7.515625 41.375 12.890625
|
||||
Q 45.40625 18.265625 45.40625 27.984375
|
||||
z
|
||||
M 54.390625 6.78125
|
||||
Q 54.390625 -7.171875 48.1875 -13.984375
|
||||
Q 42 -20.796875 29.203125 -20.796875
|
||||
Q 24.46875 -20.796875 20.265625 -20.09375
|
||||
Q 16.0625 -19.390625 12.109375 -17.921875
|
||||
L 12.109375 -9.1875
|
||||
Q 16.0625 -11.328125 19.921875 -12.34375
|
||||
Q 23.78125 -13.375 27.78125 -13.375
|
||||
Q 36.625 -13.375 41.015625 -8.765625
|
||||
Q 45.40625 -4.15625 45.40625 5.171875
|
||||
L 45.40625 9.625
|
||||
Q 42.625 4.78125 38.28125 2.390625
|
||||
Q 33.9375 0 27.875 0
|
||||
Q 17.828125 0 11.671875 7.65625
|
||||
Q 5.515625 15.328125 5.515625 27.984375
|
||||
Q 5.515625 40.671875 11.671875 48.328125
|
||||
Q 17.828125 56 27.875 56
|
||||
Q 33.9375 56 38.28125 53.609375
|
||||
Q 42.625 51.21875 45.40625 46.390625
|
||||
L 45.40625 54.6875
|
||||
L 54.390625 54.6875
|
||||
z
|
||||
" id="DejaVuSans-67"/>
|
||||
</defs>
|
||||
<g transform="translate(175.681044 15.558281)scale(0.11 -0.11)">
|
||||
<use xlink:href="#DejaVuSans-46"/>
|
||||
<use x="57.410156" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="85.193359" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="112.976562" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="174.5" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="206.287109" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="258.386719" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="297.595703" xlink:href="#DejaVuSans-6f"/>
|
||||
<use x="358.777344" xlink:href="#DejaVuSans-72"/>
|
||||
<use x="399.890625" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="461.169922" xlink:href="#DejaVuSans-67"/>
|
||||
<use x="524.646484" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="586.169922" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="617.957031" xlink:href="#DejaVuSans-63"/>
|
||||
<use x="672.9375" xlink:href="#DejaVuSans-6f"/>
|
||||
<use x="734.119141" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="786.21875" xlink:href="#DejaVuSans-74"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pdf50752646">
|
||||
<rect height="182.44" width="361.96" x="40.099247" y="21.558281"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 30 KiB |
651
lfs.h
651
lfs.h
|
|
@ -1,651 +0,0 @@
|
|||
/*
|
||||
* The little filesystem
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_H
|
||||
#define LFS_H
|
||||
|
||||
#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 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 0x00020000
|
||||
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
|
||||
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
|
||||
|
||||
|
||||
/// Definitions ///
|
||||
|
||||
// Type definitions
|
||||
typedef uint32_t lfs_size_t;
|
||||
typedef uint32_t lfs_off_t;
|
||||
|
||||
typedef int32_t lfs_ssize_t;
|
||||
typedef int32_t lfs_soff_t;
|
||||
|
||||
typedef uint32_t lfs_block_t;
|
||||
|
||||
// Maximum name size in bytes, may be redefined to reduce the size of the
|
||||
// info struct. Limited to <= 1022. Stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
#ifndef LFS_NAME_MAX
|
||||
#define LFS_NAME_MAX 255
|
||||
#endif
|
||||
|
||||
// Maximum size of a file in bytes, may be redefined to limit to support other
|
||||
// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
|
||||
// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return
|
||||
// incorrect values due to using signed integers. Stored in superblock and
|
||||
// must be respected by other littlefs drivers.
|
||||
#ifndef LFS_FILE_MAX
|
||||
#define LFS_FILE_MAX 2147483647
|
||||
#endif
|
||||
|
||||
// Maximum size of custom attributes in bytes, may be redefined, but there is
|
||||
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022.
|
||||
#ifndef LFS_ATTR_MAX
|
||||
#define LFS_ATTR_MAX 1022
|
||||
#endif
|
||||
|
||||
// Possible error codes, these are negative to allow
|
||||
// valid positive return values
|
||||
enum lfs_error {
|
||||
LFS_ERR_OK = 0, // No error
|
||||
LFS_ERR_IO = -5, // Error during device operation
|
||||
LFS_ERR_CORRUPT = -84, // Corrupted
|
||||
LFS_ERR_NOENT = -2, // No directory entry
|
||||
LFS_ERR_EXIST = -17, // Entry already exists
|
||||
LFS_ERR_NOTDIR = -20, // Entry is not a dir
|
||||
LFS_ERR_ISDIR = -21, // Entry is a dir
|
||||
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
|
||||
LFS_ERR_BADF = -9, // Bad file number
|
||||
LFS_ERR_FBIG = -27, // File too large
|
||||
LFS_ERR_INVAL = -22, // Invalid parameter
|
||||
LFS_ERR_NOSPC = -28, // No space left on device
|
||||
LFS_ERR_NOMEM = -12, // No more memory available
|
||||
LFS_ERR_NOATTR = -61, // No data/attr available
|
||||
LFS_ERR_NAMETOOLONG = -36, // File name too long
|
||||
};
|
||||
|
||||
// File types
|
||||
enum lfs_type {
|
||||
// file types
|
||||
LFS_TYPE_REG = 0x001,
|
||||
LFS_TYPE_DIR = 0x002,
|
||||
|
||||
// internally used types
|
||||
LFS_TYPE_SPLICE = 0x400,
|
||||
LFS_TYPE_NAME = 0x000,
|
||||
LFS_TYPE_STRUCT = 0x200,
|
||||
LFS_TYPE_USERATTR = 0x300,
|
||||
LFS_TYPE_FROM = 0x100,
|
||||
LFS_TYPE_TAIL = 0x600,
|
||||
LFS_TYPE_GLOBALS = 0x700,
|
||||
LFS_TYPE_CRC = 0x500,
|
||||
|
||||
// internally used type specializations
|
||||
LFS_TYPE_CREATE = 0x401,
|
||||
LFS_TYPE_DELETE = 0x4ff,
|
||||
LFS_TYPE_SUPERBLOCK = 0x0ff,
|
||||
LFS_TYPE_DIRSTRUCT = 0x200,
|
||||
LFS_TYPE_CTZSTRUCT = 0x202,
|
||||
LFS_TYPE_INLINESTRUCT = 0x201,
|
||||
LFS_TYPE_SOFTTAIL = 0x600,
|
||||
LFS_TYPE_HARDTAIL = 0x601,
|
||||
LFS_TYPE_MOVESTATE = 0x7ff,
|
||||
|
||||
// internal chip sources
|
||||
LFS_FROM_NOOP = 0x000,
|
||||
LFS_FROM_MOVE = 0x101,
|
||||
LFS_FROM_USERATTRS = 0x102,
|
||||
};
|
||||
|
||||
// File open flags
|
||||
enum lfs_open_flags {
|
||||
// open flags
|
||||
LFS_O_RDONLY = 1, // Open a file as read only
|
||||
LFS_O_WRONLY = 2, // Open a file as write only
|
||||
LFS_O_RDWR = 3, // Open a file as read and write
|
||||
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
|
||||
LFS_O_EXCL = 0x0200, // Fail if a file already exists
|
||||
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
|
||||
LFS_O_APPEND = 0x0800, // Move to end of file on every write
|
||||
|
||||
// internally used flags
|
||||
LFS_F_DIRTY = 0x010000, // File does not match storage
|
||||
LFS_F_WRITING = 0x020000, // File has been written since last flush
|
||||
LFS_F_READING = 0x040000, // File has been read since last flush
|
||||
LFS_F_ERRED = 0x080000, // An error occured during write
|
||||
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
|
||||
LFS_F_OPENED = 0x200000, // File has been opened
|
||||
};
|
||||
|
||||
// File seek flags
|
||||
enum lfs_whence_flags {
|
||||
LFS_SEEK_SET = 0, // Seek relative to an absolute position
|
||||
LFS_SEEK_CUR = 1, // Seek relative to the current file position
|
||||
LFS_SEEK_END = 2, // Seek relative to the end of the file
|
||||
};
|
||||
|
||||
|
||||
// Configuration provided during initialization of the littlefs
|
||||
struct lfs_config {
|
||||
// Opaque user provided context that can be used to pass
|
||||
// information to the block device operations
|
||||
void *context;
|
||||
|
||||
// Read a region in a block. Negative error codes are propogated
|
||||
// to the user.
|
||||
int (*read)(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
|
||||
// Program a region in a block. The block must have previously
|
||||
// been erased. Negative error codes are propogated to the user.
|
||||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
||||
int (*prog)(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
|
||||
// Erase a block. A block must be erased before being programmed.
|
||||
// The state of an erased block is undefined. Negative error codes
|
||||
// are propogated to the user.
|
||||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
||||
int (*erase)(const struct lfs_config *c, lfs_block_t block);
|
||||
|
||||
// Sync the state of the underlying block device. Negative error codes
|
||||
// are propogated to the user.
|
||||
int (*sync)(const struct lfs_config *c);
|
||||
|
||||
// Minimum size of a block read. All read operations will be a
|
||||
// multiple of this value.
|
||||
lfs_size_t read_size;
|
||||
|
||||
// Minimum size of a block program. All program operations will be a
|
||||
// multiple of this value.
|
||||
lfs_size_t prog_size;
|
||||
|
||||
// Size of an erasable block. This does not impact ram consumption and
|
||||
// may be larger than the physical erase size. However, non-inlined files
|
||||
// take up at minimum one block. Must be a multiple of the read
|
||||
// and program sizes.
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of erasable blocks on the device.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Number of erase cycles before littlefs evicts metadata logs and moves
|
||||
// the metadata to another block. Suggested values are in the
|
||||
// range 100-1000, with large values having better performance at the cost
|
||||
// of less consistent wear distribution.
|
||||
//
|
||||
// Set to -1 to disable block-level wear-leveling.
|
||||
int32_t block_cycles;
|
||||
|
||||
// Size of block caches. Each cache buffers a portion of a block in RAM.
|
||||
// The littlefs needs a read cache, a program cache, and one additional
|
||||
// cache per file. Larger caches can improve performance by storing more
|
||||
// data and reducing the number of disk accesses. Must be a multiple of
|
||||
// the read and program sizes, and a factor of the block size.
|
||||
lfs_size_t cache_size;
|
||||
|
||||
// Size of the lookahead buffer in bytes. A larger lookahead buffer
|
||||
// increases the number of blocks found during an allocation pass. The
|
||||
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
|
||||
// can track 8 blocks. Must be a multiple of 8.
|
||||
lfs_size_t lookahead_size;
|
||||
|
||||
// Optional statically allocated read buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *read_buffer;
|
||||
|
||||
// Optional statically allocated program buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *prog_buffer;
|
||||
|
||||
// Optional statically allocated lookahead buffer. Must be lookahead_size
|
||||
// and aligned to a 32-bit boundary. By default lfs_malloc is used to
|
||||
// allocate this buffer.
|
||||
void *lookahead_buffer;
|
||||
|
||||
// Optional upper limit on length of file names in bytes. No downside for
|
||||
// larger names except the size of the info struct which is controlled by
|
||||
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
|
||||
// superblock and must be respected by other littlefs drivers.
|
||||
lfs_size_t name_max;
|
||||
|
||||
// Optional upper limit on files in bytes. No downside for larger files
|
||||
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
|
||||
// in superblock and must be respected by other littlefs drivers.
|
||||
lfs_size_t file_max;
|
||||
|
||||
// Optional upper limit on custom attributes in bytes. No downside for
|
||||
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
|
||||
// LFS_ATTR_MAX when zero.
|
||||
lfs_size_t attr_max;
|
||||
};
|
||||
|
||||
// File info structure
|
||||
struct lfs_info {
|
||||
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
|
||||
uint8_t type;
|
||||
|
||||
// Size of the file, only valid for REG files. Limited to 32-bits.
|
||||
lfs_size_t size;
|
||||
|
||||
// Name of the file stored as a null-terminated string. Limited to
|
||||
// LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to
|
||||
// reduce RAM. LFS_NAME_MAX is stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
char name[LFS_NAME_MAX+1];
|
||||
};
|
||||
|
||||
// Custom attribute structure, used to describe custom attributes
|
||||
// committed atomically during file writes.
|
||||
struct lfs_attr {
|
||||
// 8-bit type of attribute, provided by user and used to
|
||||
// identify the attribute
|
||||
uint8_t type;
|
||||
|
||||
// Pointer to buffer containing the attribute
|
||||
void *buffer;
|
||||
|
||||
// Size of attribute in bytes, limited to LFS_ATTR_MAX
|
||||
lfs_size_t size;
|
||||
};
|
||||
|
||||
// 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;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
|
||||
struct lfs_ctz {
|
||||
lfs_block_t head;
|
||||
lfs_size_t size;
|
||||
} ctz;
|
||||
|
||||
uint32_t flags;
|
||||
lfs_off_t pos;
|
||||
lfs_block_t block;
|
||||
lfs_off_t off;
|
||||
lfs_cache_t cache;
|
||||
|
||||
const struct lfs_file_config *cfg;
|
||||
} lfs_file_t;
|
||||
|
||||
typedef struct lfs_superblock {
|
||||
uint32_t version;
|
||||
lfs_size_t block_size;
|
||||
lfs_size_t block_count;
|
||||
lfs_size_t name_max;
|
||||
lfs_size_t file_max;
|
||||
lfs_size_t attr_max;
|
||||
} lfs_superblock_t;
|
||||
|
||||
// The littlefs filesystem type
|
||||
typedef struct lfs {
|
||||
lfs_cache_t rcache;
|
||||
lfs_cache_t pcache;
|
||||
|
||||
lfs_block_t root[2];
|
||||
struct lfs_mlist {
|
||||
struct lfs_mlist *next;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
} *mlist;
|
||||
uint32_t seed;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/// Filesystem functions ///
|
||||
|
||||
// Format a block device with the littlefs
|
||||
//
|
||||
// Requires a littlefs object and config struct. This clobbers the littlefs
|
||||
// object, and does not leave the filesystem mounted. The config struct must
|
||||
// be zeroed for defaults and backwards compatibility.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
|
||||
|
||||
// Mounts a littlefs
|
||||
//
|
||||
// Requires a littlefs object and config struct. Multiple filesystems
|
||||
// may be mounted simultaneously with multiple littlefs objects. Both
|
||||
// lfs and config must be allocated while mounted. The config struct must
|
||||
// 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);
|
||||
|
||||
// Unmounts a littlefs
|
||||
//
|
||||
// Does nothing besides releasing any allocated resources.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_unmount(lfs_t *lfs);
|
||||
|
||||
/// General operations ///
|
||||
|
||||
// Removes a file or directory
|
||||
//
|
||||
// If removing a directory, the directory must be empty.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_remove(lfs_t *lfs, const char *path);
|
||||
|
||||
// Rename or move a file or directory
|
||||
//
|
||||
// If the destination exists, it must match the source in type.
|
||||
// If the destination is a directory, the directory must be empty.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
|
||||
|
||||
// Find info about a file or directory
|
||||
//
|
||||
// Fills out the info structure, based on the specified file or directory.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
|
||||
|
||||
// Get a custom attribute
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
|
||||
// the buffer, it will be padded with zeros. If the stored attribute is larger,
|
||||
// then it will be silently truncated. If no attribute is found, the error
|
||||
// LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
|
||||
//
|
||||
// Returns the size of the attribute, or a negative error code on failure.
|
||||
// Note, the returned size is the size of the attribute on disk, irrespective
|
||||
// of the size of the buffer. This can be used to dynamically allocate a buffer
|
||||
// or check for existance.
|
||||
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
|
||||
uint8_t type, void *buffer, lfs_size_t size);
|
||||
|
||||
// Set custom attributes
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
|
||||
// implicitly created.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_setattr(lfs_t *lfs, const char *path,
|
||||
uint8_t type, const void *buffer, lfs_size_t size);
|
||||
|
||||
// Removes a custom attribute
|
||||
//
|
||||
// If an attribute is not found, nothing happens.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);
|
||||
|
||||
|
||||
/// File operations ///
|
||||
|
||||
// Open a file
|
||||
//
|
||||
// The mode that the file is opened in is determined by the flags, which
|
||||
// are values from the enum lfs_open_flags that are bitwise-ored together.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
|
||||
const char *path, int flags);
|
||||
|
||||
// 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
|
||||
// sync had been called and releases any allocated resources.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Synchronize a file on storage
|
||||
//
|
||||
// Any pending writes are written out to storage.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Read data from file
|
||||
//
|
||||
// Takes a buffer and size indicating where to store the read data.
|
||||
// Returns the number of bytes read, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
||||
void *buffer, lfs_size_t size);
|
||||
|
||||
// Write data to file
|
||||
//
|
||||
// Takes a buffer and size indicating the data to write. The file will not
|
||||
// actually be updated on the storage until either sync or close is called.
|
||||
//
|
||||
// Returns the number of bytes written, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
||||
const void *buffer, lfs_size_t size);
|
||||
|
||||
// Change the position of the file
|
||||
//
|
||||
// The change in position is determined by the offset and whence flag.
|
||||
// Returns the new position of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
|
||||
lfs_soff_t off, int whence);
|
||||
|
||||
// Truncates the size of the file to the specified size
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);
|
||||
|
||||
// Return the position of the file
|
||||
//
|
||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
|
||||
// Returns the position of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Change the position of the file to the beginning of the file
|
||||
//
|
||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET)
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Return the size of the file
|
||||
//
|
||||
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
|
||||
// Returns the size of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
|
||||
/// Directory operations ///
|
||||
|
||||
// Create a directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_mkdir(lfs_t *lfs, const char *path);
|
||||
|
||||
// Open a directory
|
||||
//
|
||||
// Once open a directory can be used with read to iterate over files.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
|
||||
|
||||
// Close a directory
|
||||
//
|
||||
// Releases any allocated resources.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
// Read an entry in the directory
|
||||
//
|
||||
// Fills out the info structure, based on the specified file or directory.
|
||||
// Returns a positive value on success, 0 at the end of directory,
|
||||
// or a negative error code on failure.
|
||||
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
|
||||
|
||||
// Change the position of the directory
|
||||
//
|
||||
// The new off must be a value previous returned from tell and specifies
|
||||
// an absolute offset in the directory seek.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
|
||||
|
||||
// Return the position of the directory
|
||||
//
|
||||
// The returned offset is only meant to be consumed by seek and may not make
|
||||
// sense, but does indicate the current position in the directory iteration.
|
||||
//
|
||||
// Returns the position of the directory, or a negative error code on failure.
|
||||
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
// Change the position of the directory to the beginning of the directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
|
||||
/// Filesystem-level filesystem operations
|
||||
|
||||
// Finds the current size of the filesystem
|
||||
//
|
||||
// Note: Result is best effort. If files share COW structures, the returned
|
||||
// size may be larger than the filesystem actually is.
|
||||
//
|
||||
// Returns the number of allocated blocks, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
|
||||
|
||||
// Traverse through all blocks in use by the filesystem
|
||||
//
|
||||
// The provided callback will be called with each block address that is
|
||||
// currently in use by the filesystem. This can be used to determine which
|
||||
// blocks are in use or how much of the storage is available.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
|
||||
|
||||
#ifdef LFS_MIGRATE
|
||||
// Attempts to migrate a previous version of littlefs
|
||||
//
|
||||
// Behaves similarly to the lfs_format function. Attempts to mount
|
||||
// the previous version of littlefs and update the filesystem so it can be
|
||||
// mounted with the current version of littlefs.
|
||||
//
|
||||
// Requires a littlefs object 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_migrate(lfs_t *lfs, const struct lfs_config *cfg);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
33
lfs_util.c
33
lfs_util.c
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* lfs util functions
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include "lfs_util.h"
|
||||
|
||||
// Only compile if user does not provide custom config
|
||||
#ifndef LFS_CONFIG
|
||||
|
||||
|
||||
// Software CRC implementation with small lookup table
|
||||
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,
|
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
|
||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
|
||||
};
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
230
lfs_util.h
230
lfs_util.h
|
|
@ -1,230 +0,0 @@
|
|||
/*
|
||||
* lfs utility functions
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_UTIL_H
|
||||
#define LFS_UTIL_H
|
||||
|
||||
// Users can override lfs_util.h with their own configuration by defining
|
||||
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
||||
//
|
||||
// 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.
|
||||
#ifdef LFS_CONFIG
|
||||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
||||
#define LFS_STRINGIZE2(x) #x
|
||||
#include LFS_STRINGIZE(LFS_CONFIG)
|
||||
#else
|
||||
|
||||
// System includes
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef LFS_NO_MALLOC
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#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__)
|
||||
#else
|
||||
#define LFS_DEBUG(fmt, ...)
|
||||
#endif
|
||||
|
||||
#ifndef LFS_NO_WARN
|
||||
#define LFS_WARN(fmt, ...) \
|
||||
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__)
|
||||
#else
|
||||
#define LFS_ERROR(fmt, ...)
|
||||
#endif
|
||||
|
||||
// Runtime assertions
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#define LFS_ASSERT(test) assert(test)
|
||||
#else
|
||||
#define LFS_ASSERT(test)
|
||||
#endif
|
||||
|
||||
|
||||
// Builtin functions, these may be replaced by more efficient
|
||||
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
|
||||
// expensive basic C implementation for debugging purposes
|
||||
|
||||
// Min/max functions for unsigned 32-bit numbers
|
||||
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
// Align to nearest multiple of a size
|
||||
static inline uint32_t 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))
|
||||
return 32 - __builtin_clz(a-1);
|
||||
#else
|
||||
uint32_t r = 0;
|
||||
uint32_t s;
|
||||
a -= 1;
|
||||
s = (a > 0xffff) << 4; a >>= s; r |= s;
|
||||
s = (a > 0xff ) << 3; a >>= s; r |= s;
|
||||
s = (a > 0xf ) << 2; a >>= s; r |= s;
|
||||
s = (a > 0x3 ) << 1; a >>= s; r |= s;
|
||||
return (r | (a >> 1)) + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of trailing binary zeros in a
|
||||
// lfs_ctz(0) may be undefined
|
||||
static inline uint32_t lfs_ctz(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
|
||||
return __builtin_ctz(a);
|
||||
#else
|
||||
return lfs_npw2((a & -a) + 1) - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of binary ones in a
|
||||
static inline uint32_t lfs_popc(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return __builtin_popcount(a);
|
||||
#else
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
||||
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Find the sequence comparison of a and b, this is the distance
|
||||
// between a and b ignoring overflow
|
||||
static inline int lfs_scmp(uint32_t a, uint32_t b) {
|
||||
return (int)(unsigned)(a - b);
|
||||
}
|
||||
|
||||
// Convert between 32-bit little-endian and native order
|
||||
static inline uint32_t lfs_fromle32(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && ( \
|
||||
(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 ) && 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) |
|
||||
(((uint8_t*)&a)[1] << 8) |
|
||||
(((uint8_t*)&a)[2] << 16) |
|
||||
(((uint8_t*)&a)[3] << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// Deallocate memory, only used if buffers are not provided to littlefs
|
||||
static inline void lfs_free(void *p) {
|
||||
#ifndef LFS_NO_MALLOC
|
||||
free(p);
|
||||
#else
|
||||
(void)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
40
metadata-cost.py
Executable file
40
metadata-cost.py
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('SVG')
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
matplotlib.rc('font', family='sans-serif', size=11)
|
||||
matplotlib.rc('axes', titlesize='medium', labelsize='medium')
|
||||
matplotlib.rc('xtick', labelsize='small')
|
||||
matplotlib.rc('ytick', labelsize='small')
|
||||
|
||||
fig, ax = plt.subplots(figsize=(6, 3.5))
|
||||
ax.set_title('Metadata pair update cost')
|
||||
|
||||
# multiplicative cost of metadata
|
||||
def cost(r, n, size):
|
||||
cost = n + n*(r*(size/n) / ((1-r)*(size/n) + 1))
|
||||
return cost / n
|
||||
|
||||
t = np.arange(0.0, 1.0, 0.01)
|
||||
ax.plot(t, cost(t, 100, 4096))
|
||||
|
||||
ax.set_xlabel('% full')
|
||||
ax.set_xlim(0, 1)
|
||||
ax.set_ylabel('multiplicative cost')
|
||||
ax.set_ylim(0, 12)
|
||||
|
||||
xticks = np.arange(0.0, 1+0.1, 0.2)
|
||||
ax.set_xticks(xticks)
|
||||
ax.set_xticklabels(['%d%%' % (100*t) for t in xticks])
|
||||
yticks = np.arange(0, 12+1, 2)
|
||||
ax.set_yticks(yticks)
|
||||
ax.set_yticklabels(['%dx' % t for t in yticks])
|
||||
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['top'].set_visible(False)
|
||||
|
||||
fig.tight_layout()
|
||||
plt.savefig('metadata-cost.svg', bbox_inches="tight")
|
||||
968
metadata-cost.svg
Normal file
968
metadata-cost.svg
Normal file
|
|
@ -0,0 +1,968 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Created with matplotlib (http://matplotlib.org/) -->
|
||||
<svg height="241.712285pt" version="1.1" viewBox="0 0 422.382285 241.712285" width="422.382285pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
*{stroke-linecap:butt;stroke-linejoin:round;}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 241.712285
|
||||
L 422.382285 241.712285
|
||||
L 422.382285 0
|
||||
L 0 0
|
||||
z
|
||||
" style="fill:#ffffff;"/>
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="patch_2">
|
||||
<path d="M 45.929206 203.998281
|
||||
L 402.084206 203.998281
|
||||
L 402.084206 21.558281
|
||||
L 45.929206 21.558281
|
||||
z
|
||||
" style="fill:#ffffff;"/>
|
||||
</g>
|
||||
<g id="matplotlib.axis_1">
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path d="M 0 0
|
||||
L 0 3.5
|
||||
" id="md69e27c4ae" style="stroke:#000000;stroke-width:0.8;"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#md69e27c4ae" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
<!-- 0% -->
|
||||
<defs>
|
||||
<path d="M 31.78125 66.40625
|
||||
Q 24.171875 66.40625 20.328125 58.90625
|
||||
Q 16.5 51.421875 16.5 36.375
|
||||
Q 16.5 21.390625 20.328125 13.890625
|
||||
Q 24.171875 6.390625 31.78125 6.390625
|
||||
Q 39.453125 6.390625 43.28125 13.890625
|
||||
Q 47.125 21.390625 47.125 36.375
|
||||
Q 47.125 51.421875 43.28125 58.90625
|
||||
Q 39.453125 66.40625 31.78125 66.40625
|
||||
z
|
||||
M 31.78125 74.21875
|
||||
Q 44.046875 74.21875 50.515625 64.515625
|
||||
Q 56.984375 54.828125 56.984375 36.375
|
||||
Q 56.984375 17.96875 50.515625 8.265625
|
||||
Q 44.046875 -1.421875 31.78125 -1.421875
|
||||
Q 19.53125 -1.421875 13.0625 8.265625
|
||||
Q 6.59375 17.96875 6.59375 36.375
|
||||
Q 6.59375 54.828125 13.0625 64.515625
|
||||
Q 19.53125 74.21875 31.78125 74.21875
|
||||
z
|
||||
" id="DejaVuSans-30"/>
|
||||
<path d="M 72.703125 32.078125
|
||||
Q 68.453125 32.078125 66.03125 28.46875
|
||||
Q 63.625 24.859375 63.625 18.40625
|
||||
Q 63.625 12.0625 66.03125 8.421875
|
||||
Q 68.453125 4.78125 72.703125 4.78125
|
||||
Q 76.859375 4.78125 79.265625 8.421875
|
||||
Q 81.6875 12.0625 81.6875 18.40625
|
||||
Q 81.6875 24.8125 79.265625 28.4375
|
||||
Q 76.859375 32.078125 72.703125 32.078125
|
||||
z
|
||||
M 72.703125 38.28125
|
||||
Q 80.421875 38.28125 84.953125 32.90625
|
||||
Q 89.5 27.546875 89.5 18.40625
|
||||
Q 89.5 9.28125 84.9375 3.921875
|
||||
Q 80.375 -1.421875 72.703125 -1.421875
|
||||
Q 64.890625 -1.421875 60.34375 3.921875
|
||||
Q 55.8125 9.28125 55.8125 18.40625
|
||||
Q 55.8125 27.59375 60.375 32.9375
|
||||
Q 64.9375 38.28125 72.703125 38.28125
|
||||
z
|
||||
M 22.3125 68.015625
|
||||
Q 18.109375 68.015625 15.6875 64.375
|
||||
Q 13.28125 60.75 13.28125 54.390625
|
||||
Q 13.28125 47.953125 15.671875 44.328125
|
||||
Q 18.0625 40.71875 22.3125 40.71875
|
||||
Q 26.5625 40.71875 28.96875 44.328125
|
||||
Q 31.390625 47.953125 31.390625 54.390625
|
||||
Q 31.390625 60.6875 28.953125 64.34375
|
||||
Q 26.515625 68.015625 22.3125 68.015625
|
||||
z
|
||||
M 66.40625 74.21875
|
||||
L 74.21875 74.21875
|
||||
L 28.609375 -1.421875
|
||||
L 20.796875 -1.421875
|
||||
z
|
||||
M 22.3125 74.21875
|
||||
Q 30.03125 74.21875 34.609375 68.875
|
||||
Q 39.203125 63.53125 39.203125 54.390625
|
||||
Q 39.203125 45.171875 34.640625 39.84375
|
||||
Q 30.078125 34.515625 22.3125 34.515625
|
||||
Q 14.546875 34.515625 10.03125 39.859375
|
||||
Q 5.515625 45.21875 5.515625 54.390625
|
||||
Q 5.515625 63.484375 10.046875 68.84375
|
||||
Q 14.59375 74.21875 22.3125 74.21875
|
||||
z
|
||||
" id="DejaVuSans-25"/>
|
||||
</defs>
|
||||
<g transform="translate(38.661085 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-30"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-25"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="117.160206" xlink:href="#md69e27c4ae" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
<!-- 20% -->
|
||||
<defs>
|
||||
<path d="M 19.1875 8.296875
|
||||
L 53.609375 8.296875
|
||||
L 53.609375 0
|
||||
L 7.328125 0
|
||||
L 7.328125 8.296875
|
||||
Q 12.9375 14.109375 22.625 23.890625
|
||||
Q 32.328125 33.6875 34.8125 36.53125
|
||||
Q 39.546875 41.84375 41.421875 45.53125
|
||||
Q 43.3125 49.21875 43.3125 52.78125
|
||||
Q 43.3125 58.59375 39.234375 62.25
|
||||
Q 35.15625 65.921875 28.609375 65.921875
|
||||
Q 23.96875 65.921875 18.8125 64.3125
|
||||
Q 13.671875 62.703125 7.8125 59.421875
|
||||
L 7.8125 69.390625
|
||||
Q 13.765625 71.78125 18.9375 73
|
||||
Q 24.125 74.21875 28.421875 74.21875
|
||||
Q 39.75 74.21875 46.484375 68.546875
|
||||
Q 53.21875 62.890625 53.21875 53.421875
|
||||
Q 53.21875 48.921875 51.53125 44.890625
|
||||
Q 49.859375 40.875 45.40625 35.40625
|
||||
Q 44.1875 33.984375 37.640625 27.21875
|
||||
Q 31.109375 20.453125 19.1875 8.296875
|
||||
z
|
||||
" id="DejaVuSans-32"/>
|
||||
</defs>
|
||||
<g transform="translate(106.977106 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-32"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-25"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="188.391206" xlink:href="#md69e27c4ae" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
<!-- 40% -->
|
||||
<defs>
|
||||
<path d="M 37.796875 64.3125
|
||||
L 12.890625 25.390625
|
||||
L 37.796875 25.390625
|
||||
z
|
||||
M 35.203125 72.90625
|
||||
L 47.609375 72.90625
|
||||
L 47.609375 25.390625
|
||||
L 58.015625 25.390625
|
||||
L 58.015625 17.1875
|
||||
L 47.609375 17.1875
|
||||
L 47.609375 0
|
||||
L 37.796875 0
|
||||
L 37.796875 17.1875
|
||||
L 4.890625 17.1875
|
||||
L 4.890625 26.703125
|
||||
z
|
||||
" id="DejaVuSans-34"/>
|
||||
</defs>
|
||||
<g transform="translate(178.208106 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-34"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-25"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="259.622206" xlink:href="#md69e27c4ae" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
<!-- 60% -->
|
||||
<defs>
|
||||
<path d="M 33.015625 40.375
|
||||
Q 26.375 40.375 22.484375 35.828125
|
||||
Q 18.609375 31.296875 18.609375 23.390625
|
||||
Q 18.609375 15.53125 22.484375 10.953125
|
||||
Q 26.375 6.390625 33.015625 6.390625
|
||||
Q 39.65625 6.390625 43.53125 10.953125
|
||||
Q 47.40625 15.53125 47.40625 23.390625
|
||||
Q 47.40625 31.296875 43.53125 35.828125
|
||||
Q 39.65625 40.375 33.015625 40.375
|
||||
z
|
||||
M 52.59375 71.296875
|
||||
L 52.59375 62.3125
|
||||
Q 48.875 64.0625 45.09375 64.984375
|
||||
Q 41.3125 65.921875 37.59375 65.921875
|
||||
Q 27.828125 65.921875 22.671875 59.328125
|
||||
Q 17.53125 52.734375 16.796875 39.40625
|
||||
Q 19.671875 43.65625 24.015625 45.921875
|
||||
Q 28.375 48.1875 33.59375 48.1875
|
||||
Q 44.578125 48.1875 50.953125 41.515625
|
||||
Q 57.328125 34.859375 57.328125 23.390625
|
||||
Q 57.328125 12.15625 50.6875 5.359375
|
||||
Q 44.046875 -1.421875 33.015625 -1.421875
|
||||
Q 20.359375 -1.421875 13.671875 8.265625
|
||||
Q 6.984375 17.96875 6.984375 36.375
|
||||
Q 6.984375 53.65625 15.1875 63.9375
|
||||
Q 23.390625 74.21875 37.203125 74.21875
|
||||
Q 40.921875 74.21875 44.703125 73.484375
|
||||
Q 48.484375 72.75 52.59375 71.296875
|
||||
z
|
||||
" id="DejaVuSans-36"/>
|
||||
</defs>
|
||||
<g transform="translate(249.439106 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-36"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-25"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="330.853206" xlink:href="#md69e27c4ae" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
<!-- 80% -->
|
||||
<defs>
|
||||
<path d="M 31.78125 34.625
|
||||
Q 24.75 34.625 20.71875 30.859375
|
||||
Q 16.703125 27.09375 16.703125 20.515625
|
||||
Q 16.703125 13.921875 20.71875 10.15625
|
||||
Q 24.75 6.390625 31.78125 6.390625
|
||||
Q 38.8125 6.390625 42.859375 10.171875
|
||||
Q 46.921875 13.96875 46.921875 20.515625
|
||||
Q 46.921875 27.09375 42.890625 30.859375
|
||||
Q 38.875 34.625 31.78125 34.625
|
||||
z
|
||||
M 21.921875 38.8125
|
||||
Q 15.578125 40.375 12.03125 44.71875
|
||||
Q 8.5 49.078125 8.5 55.328125
|
||||
Q 8.5 64.0625 14.71875 69.140625
|
||||
Q 20.953125 74.21875 31.78125 74.21875
|
||||
Q 42.671875 74.21875 48.875 69.140625
|
||||
Q 55.078125 64.0625 55.078125 55.328125
|
||||
Q 55.078125 49.078125 51.53125 44.71875
|
||||
Q 48 40.375 41.703125 38.8125
|
||||
Q 48.828125 37.15625 52.796875 32.3125
|
||||
Q 56.78125 27.484375 56.78125 20.515625
|
||||
Q 56.78125 9.90625 50.3125 4.234375
|
||||
Q 43.84375 -1.421875 31.78125 -1.421875
|
||||
Q 19.734375 -1.421875 13.25 4.234375
|
||||
Q 6.78125 9.90625 6.78125 20.515625
|
||||
Q 6.78125 27.484375 10.78125 32.3125
|
||||
Q 14.796875 37.15625 21.921875 38.8125
|
||||
z
|
||||
M 18.3125 54.390625
|
||||
Q 18.3125 48.734375 21.84375 45.5625
|
||||
Q 25.390625 42.390625 31.78125 42.390625
|
||||
Q 38.140625 42.390625 41.71875 45.5625
|
||||
Q 45.3125 48.734375 45.3125 54.390625
|
||||
Q 45.3125 60.0625 41.71875 63.234375
|
||||
Q 38.140625 66.40625 31.78125 66.40625
|
||||
Q 25.390625 66.40625 21.84375 63.234375
|
||||
Q 18.3125 60.0625 18.3125 54.390625
|
||||
z
|
||||
" id="DejaVuSans-38"/>
|
||||
</defs>
|
||||
<g transform="translate(320.670106 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-38"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-25"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="402.084206" xlink:href="#md69e27c4ae" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
<!-- 100% -->
|
||||
<defs>
|
||||
<path d="M 12.40625 8.296875
|
||||
L 28.515625 8.296875
|
||||
L 28.515625 63.921875
|
||||
L 10.984375 60.40625
|
||||
L 10.984375 69.390625
|
||||
L 28.421875 72.90625
|
||||
L 38.28125 72.90625
|
||||
L 38.28125 8.296875
|
||||
L 54.390625 8.296875
|
||||
L 54.390625 0
|
||||
L 12.40625 0
|
||||
z
|
||||
" id="DejaVuSans-31"/>
|
||||
</defs>
|
||||
<g transform="translate(388.986127 217.96073)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="190.869141" xlink:href="#DejaVuSans-25"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
<!-- % full -->
|
||||
<defs>
|
||||
<path id="DejaVuSans-20"/>
|
||||
<path d="M 37.109375 75.984375
|
||||
L 37.109375 68.5
|
||||
L 28.515625 68.5
|
||||
Q 23.6875 68.5 21.796875 66.546875
|
||||
Q 19.921875 64.59375 19.921875 59.515625
|
||||
L 19.921875 54.6875
|
||||
L 34.71875 54.6875
|
||||
L 34.71875 47.703125
|
||||
L 19.921875 47.703125
|
||||
L 19.921875 0
|
||||
L 10.890625 0
|
||||
L 10.890625 47.703125
|
||||
L 2.296875 47.703125
|
||||
L 2.296875 54.6875
|
||||
L 10.890625 54.6875
|
||||
L 10.890625 58.5
|
||||
Q 10.890625 67.625 15.140625 71.796875
|
||||
Q 19.390625 75.984375 28.609375 75.984375
|
||||
z
|
||||
" id="DejaVuSans-66"/>
|
||||
<path d="M 8.5 21.578125
|
||||
L 8.5 54.6875
|
||||
L 17.484375 54.6875
|
||||
L 17.484375 21.921875
|
||||
Q 17.484375 14.15625 20.5 10.265625
|
||||
Q 23.53125 6.390625 29.59375 6.390625
|
||||
Q 36.859375 6.390625 41.078125 11.03125
|
||||
Q 45.3125 15.671875 45.3125 23.6875
|
||||
L 45.3125 54.6875
|
||||
L 54.296875 54.6875
|
||||
L 54.296875 0
|
||||
L 45.3125 0
|
||||
L 45.3125 8.40625
|
||||
Q 42.046875 3.421875 37.71875 1
|
||||
Q 33.40625 -1.421875 27.6875 -1.421875
|
||||
Q 18.265625 -1.421875 13.375 4.4375
|
||||
Q 8.5 10.296875 8.5 21.578125
|
||||
z
|
||||
M 31.109375 56
|
||||
z
|
||||
" id="DejaVuSans-75"/>
|
||||
<path d="M 9.421875 75.984375
|
||||
L 18.40625 75.984375
|
||||
L 18.40625 0
|
||||
L 9.421875 0
|
||||
z
|
||||
" id="DejaVuSans-6c"/>
|
||||
</defs>
|
||||
<g transform="translate(208.555143 232.224628)scale(0.11 -0.11)">
|
||||
<use xlink:href="#DejaVuSans-25"/>
|
||||
<use x="95.019531" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="126.806641" xlink:href="#DejaVuSans-66"/>
|
||||
<use x="162.011719" xlink:href="#DejaVuSans-75"/>
|
||||
<use x="225.390625" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="253.173828" xlink:href="#DejaVuSans-6c"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="matplotlib.axis_2">
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_7">
|
||||
<defs>
|
||||
<path d="M 0 0
|
||||
L -3.5 0
|
||||
" id="m3661cbe52c" style="stroke:#000000;stroke-width:0.8;"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#m3661cbe52c" y="203.998281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
<!-- 0x -->
|
||||
<defs>
|
||||
<path d="M 54.890625 54.6875
|
||||
L 35.109375 28.078125
|
||||
L 55.90625 0
|
||||
L 45.3125 0
|
||||
L 29.390625 21.484375
|
||||
L 13.484375 0
|
||||
L 2.875 0
|
||||
L 24.125 28.609375
|
||||
L 4.6875 54.6875
|
||||
L 15.28125 54.6875
|
||||
L 29.78125 35.203125
|
||||
L 44.28125 54.6875
|
||||
z
|
||||
" id="DejaVuSans-78"/>
|
||||
</defs>
|
||||
<g transform="translate(27.675896 207.479505)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-30"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#m3661cbe52c" y="173.591615"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
<!-- 2x -->
|
||||
<g transform="translate(27.675896 177.072839)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-32"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_9">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#m3661cbe52c" y="143.184948"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
<!-- 4x -->
|
||||
<g transform="translate(27.675896 146.666172)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-34"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_10">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#m3661cbe52c" y="112.778281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
<!-- 6x -->
|
||||
<g transform="translate(27.675896 116.259505)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-36"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_5">
|
||||
<g id="line2d_11">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#m3661cbe52c" y="82.371615"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
<!-- 8x -->
|
||||
<g transform="translate(27.675896 85.852839)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-38"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_6">
|
||||
<g id="line2d_12">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#m3661cbe52c" y="51.964948"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
<!-- 10x -->
|
||||
<g transform="translate(21.845938 55.446172)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="ytick_7">
|
||||
<g id="line2d_13">
|
||||
<g>
|
||||
<use style="stroke:#000000;stroke-width:0.8;" x="45.929206" xlink:href="#m3661cbe52c" y="21.558281"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
<!-- 12x -->
|
||||
<g transform="translate(21.845938 25.039505)scale(0.09163 -0.09163)">
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use x="63.623047" xlink:href="#DejaVuSans-32"/>
|
||||
<use x="127.246094" xlink:href="#DejaVuSans-78"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
<!-- multiplicative cost -->
|
||||
<defs>
|
||||
<path d="M 52 44.1875
|
||||
Q 55.375 50.25 60.0625 53.125
|
||||
Q 64.75 56 71.09375 56
|
||||
Q 79.640625 56 84.28125 50.015625
|
||||
Q 88.921875 44.046875 88.921875 33.015625
|
||||
L 88.921875 0
|
||||
L 79.890625 0
|
||||
L 79.890625 32.71875
|
||||
Q 79.890625 40.578125 77.09375 44.375
|
||||
Q 74.3125 48.1875 68.609375 48.1875
|
||||
Q 61.625 48.1875 57.5625 43.546875
|
||||
Q 53.515625 38.921875 53.515625 30.90625
|
||||
L 53.515625 0
|
||||
L 44.484375 0
|
||||
L 44.484375 32.71875
|
||||
Q 44.484375 40.625 41.703125 44.40625
|
||||
Q 38.921875 48.1875 33.109375 48.1875
|
||||
Q 26.21875 48.1875 22.15625 43.53125
|
||||
Q 18.109375 38.875 18.109375 30.90625
|
||||
L 18.109375 0
|
||||
L 9.078125 0
|
||||
L 9.078125 54.6875
|
||||
L 18.109375 54.6875
|
||||
L 18.109375 46.1875
|
||||
Q 21.1875 51.21875 25.484375 53.609375
|
||||
Q 29.78125 56 35.6875 56
|
||||
Q 41.65625 56 45.828125 52.96875
|
||||
Q 50 49.953125 52 44.1875
|
||||
z
|
||||
" id="DejaVuSans-6d"/>
|
||||
<path d="M 18.3125 70.21875
|
||||
L 18.3125 54.6875
|
||||
L 36.8125 54.6875
|
||||
L 36.8125 47.703125
|
||||
L 18.3125 47.703125
|
||||
L 18.3125 18.015625
|
||||
Q 18.3125 11.328125 20.140625 9.421875
|
||||
Q 21.96875 7.515625 27.59375 7.515625
|
||||
L 36.8125 7.515625
|
||||
L 36.8125 0
|
||||
L 27.59375 0
|
||||
Q 17.1875 0 13.234375 3.875
|
||||
Q 9.28125 7.765625 9.28125 18.015625
|
||||
L 9.28125 47.703125
|
||||
L 2.6875 47.703125
|
||||
L 2.6875 54.6875
|
||||
L 9.28125 54.6875
|
||||
L 9.28125 70.21875
|
||||
z
|
||||
" id="DejaVuSans-74"/>
|
||||
<path d="M 9.421875 54.6875
|
||||
L 18.40625 54.6875
|
||||
L 18.40625 0
|
||||
L 9.421875 0
|
||||
z
|
||||
M 9.421875 75.984375
|
||||
L 18.40625 75.984375
|
||||
L 18.40625 64.59375
|
||||
L 9.421875 64.59375
|
||||
z
|
||||
" id="DejaVuSans-69"/>
|
||||
<path d="M 18.109375 8.203125
|
||||
L 18.109375 -20.796875
|
||||
L 9.078125 -20.796875
|
||||
L 9.078125 54.6875
|
||||
L 18.109375 54.6875
|
||||
L 18.109375 46.390625
|
||||
Q 20.953125 51.265625 25.265625 53.625
|
||||
Q 29.59375 56 35.59375 56
|
||||
Q 45.5625 56 51.78125 48.09375
|
||||
Q 58.015625 40.1875 58.015625 27.296875
|
||||
Q 58.015625 14.40625 51.78125 6.484375
|
||||
Q 45.5625 -1.421875 35.59375 -1.421875
|
||||
Q 29.59375 -1.421875 25.265625 0.953125
|
||||
Q 20.953125 3.328125 18.109375 8.203125
|
||||
z
|
||||
M 48.6875 27.296875
|
||||
Q 48.6875 37.203125 44.609375 42.84375
|
||||
Q 40.53125 48.484375 33.40625 48.484375
|
||||
Q 26.265625 48.484375 22.1875 42.84375
|
||||
Q 18.109375 37.203125 18.109375 27.296875
|
||||
Q 18.109375 17.390625 22.1875 11.75
|
||||
Q 26.265625 6.109375 33.40625 6.109375
|
||||
Q 40.53125 6.109375 44.609375 11.75
|
||||
Q 48.6875 17.390625 48.6875 27.296875
|
||||
z
|
||||
" id="DejaVuSans-70"/>
|
||||
<path d="M 48.78125 52.59375
|
||||
L 48.78125 44.1875
|
||||
Q 44.96875 46.296875 41.140625 47.34375
|
||||
Q 37.3125 48.390625 33.40625 48.390625
|
||||
Q 24.65625 48.390625 19.8125 42.84375
|
||||
Q 14.984375 37.3125 14.984375 27.296875
|
||||
Q 14.984375 17.28125 19.8125 11.734375
|
||||
Q 24.65625 6.203125 33.40625 6.203125
|
||||
Q 37.3125 6.203125 41.140625 7.25
|
||||
Q 44.96875 8.296875 48.78125 10.40625
|
||||
L 48.78125 2.09375
|
||||
Q 45.015625 0.34375 40.984375 -0.53125
|
||||
Q 36.96875 -1.421875 32.421875 -1.421875
|
||||
Q 20.0625 -1.421875 12.78125 6.34375
|
||||
Q 5.515625 14.109375 5.515625 27.296875
|
||||
Q 5.515625 40.671875 12.859375 48.328125
|
||||
Q 20.21875 56 33.015625 56
|
||||
Q 37.15625 56 41.109375 55.140625
|
||||
Q 45.0625 54.296875 48.78125 52.59375
|
||||
z
|
||||
" id="DejaVuSans-63"/>
|
||||
<path d="M 34.28125 27.484375
|
||||
Q 23.390625 27.484375 19.1875 25
|
||||
Q 14.984375 22.515625 14.984375 16.5
|
||||
Q 14.984375 11.71875 18.140625 8.90625
|
||||
Q 21.296875 6.109375 26.703125 6.109375
|
||||
Q 34.1875 6.109375 38.703125 11.40625
|
||||
Q 43.21875 16.703125 43.21875 25.484375
|
||||
L 43.21875 27.484375
|
||||
z
|
||||
M 52.203125 31.203125
|
||||
L 52.203125 0
|
||||
L 43.21875 0
|
||||
L 43.21875 8.296875
|
||||
Q 40.140625 3.328125 35.546875 0.953125
|
||||
Q 30.953125 -1.421875 24.3125 -1.421875
|
||||
Q 15.921875 -1.421875 10.953125 3.296875
|
||||
Q 6 8.015625 6 15.921875
|
||||
Q 6 25.140625 12.171875 29.828125
|
||||
Q 18.359375 34.515625 30.609375 34.515625
|
||||
L 43.21875 34.515625
|
||||
L 43.21875 35.40625
|
||||
Q 43.21875 41.609375 39.140625 45
|
||||
Q 35.0625 48.390625 27.6875 48.390625
|
||||
Q 23 48.390625 18.546875 47.265625
|
||||
Q 14.109375 46.140625 10.015625 43.890625
|
||||
L 10.015625 52.203125
|
||||
Q 14.9375 54.109375 19.578125 55.046875
|
||||
Q 24.21875 56 28.609375 56
|
||||
Q 40.484375 56 46.34375 49.84375
|
||||
Q 52.203125 43.703125 52.203125 31.203125
|
||||
z
|
||||
" id="DejaVuSans-61"/>
|
||||
<path d="M 2.984375 54.6875
|
||||
L 12.5 54.6875
|
||||
L 29.59375 8.796875
|
||||
L 46.6875 54.6875
|
||||
L 56.203125 54.6875
|
||||
L 35.6875 0
|
||||
L 23.484375 0
|
||||
z
|
||||
" id="DejaVuSans-76"/>
|
||||
<path d="M 56.203125 29.59375
|
||||
L 56.203125 25.203125
|
||||
L 14.890625 25.203125
|
||||
Q 15.484375 15.921875 20.484375 11.0625
|
||||
Q 25.484375 6.203125 34.421875 6.203125
|
||||
Q 39.59375 6.203125 44.453125 7.46875
|
||||
Q 49.3125 8.734375 54.109375 11.28125
|
||||
L 54.109375 2.78125
|
||||
Q 49.265625 0.734375 44.1875 -0.34375
|
||||
Q 39.109375 -1.421875 33.890625 -1.421875
|
||||
Q 20.796875 -1.421875 13.15625 6.1875
|
||||
Q 5.515625 13.8125 5.515625 26.8125
|
||||
Q 5.515625 40.234375 12.765625 48.109375
|
||||
Q 20.015625 56 32.328125 56
|
||||
Q 43.359375 56 49.78125 48.890625
|
||||
Q 56.203125 41.796875 56.203125 29.59375
|
||||
z
|
||||
M 47.21875 32.234375
|
||||
Q 47.125 39.59375 43.09375 43.984375
|
||||
Q 39.0625 48.390625 32.421875 48.390625
|
||||
Q 24.90625 48.390625 20.390625 44.140625
|
||||
Q 15.875 39.890625 15.1875 32.171875
|
||||
z
|
||||
" id="DejaVuSans-65"/>
|
||||
<path d="M 30.609375 48.390625
|
||||
Q 23.390625 48.390625 19.1875 42.75
|
||||
Q 14.984375 37.109375 14.984375 27.296875
|
||||
Q 14.984375 17.484375 19.15625 11.84375
|
||||
Q 23.34375 6.203125 30.609375 6.203125
|
||||
Q 37.796875 6.203125 41.984375 11.859375
|
||||
Q 46.1875 17.53125 46.1875 27.296875
|
||||
Q 46.1875 37.015625 41.984375 42.703125
|
||||
Q 37.796875 48.390625 30.609375 48.390625
|
||||
z
|
||||
M 30.609375 56
|
||||
Q 42.328125 56 49.015625 48.375
|
||||
Q 55.71875 40.765625 55.71875 27.296875
|
||||
Q 55.71875 13.875 49.015625 6.21875
|
||||
Q 42.328125 -1.421875 30.609375 -1.421875
|
||||
Q 18.84375 -1.421875 12.171875 6.21875
|
||||
Q 5.515625 13.875 5.515625 27.296875
|
||||
Q 5.515625 40.765625 12.171875 48.375
|
||||
Q 18.84375 56 30.609375 56
|
||||
z
|
||||
" id="DejaVuSans-6f"/>
|
||||
<path d="M 44.28125 53.078125
|
||||
L 44.28125 44.578125
|
||||
Q 40.484375 46.53125 36.375 47.5
|
||||
Q 32.28125 48.484375 27.875 48.484375
|
||||
Q 21.1875 48.484375 17.84375 46.4375
|
||||
Q 14.5 44.390625 14.5 40.28125
|
||||
Q 14.5 37.15625 16.890625 35.375
|
||||
Q 19.28125 33.59375 26.515625 31.984375
|
||||
L 29.59375 31.296875
|
||||
Q 39.15625 29.25 43.1875 25.515625
|
||||
Q 47.21875 21.78125 47.21875 15.09375
|
||||
Q 47.21875 7.46875 41.1875 3.015625
|
||||
Q 35.15625 -1.421875 24.609375 -1.421875
|
||||
Q 20.21875 -1.421875 15.453125 -0.5625
|
||||
Q 10.6875 0.296875 5.421875 2
|
||||
L 5.421875 11.28125
|
||||
Q 10.40625 8.6875 15.234375 7.390625
|
||||
Q 20.0625 6.109375 24.8125 6.109375
|
||||
Q 31.15625 6.109375 34.5625 8.28125
|
||||
Q 37.984375 10.453125 37.984375 14.40625
|
||||
Q 37.984375 18.0625 35.515625 20.015625
|
||||
Q 33.0625 21.96875 24.703125 23.78125
|
||||
L 21.578125 24.515625
|
||||
Q 13.234375 26.265625 9.515625 29.90625
|
||||
Q 5.8125 33.546875 5.8125 39.890625
|
||||
Q 5.8125 47.609375 11.28125 51.796875
|
||||
Q 16.75 56 26.8125 56
|
||||
Q 31.78125 56 36.171875 55.265625
|
||||
Q 40.578125 54.546875 44.28125 53.078125
|
||||
z
|
||||
" id="DejaVuSans-73"/>
|
||||
</defs>
|
||||
<g transform="translate(15.558281 163.257969)rotate(-90)scale(0.11 -0.11)">
|
||||
<use xlink:href="#DejaVuSans-6d"/>
|
||||
<use x="97.412109" xlink:href="#DejaVuSans-75"/>
|
||||
<use x="160.791016" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="188.574219" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="227.783203" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="255.566406" xlink:href="#DejaVuSans-70"/>
|
||||
<use x="319.042969" xlink:href="#DejaVuSans-6c"/>
|
||||
<use x="346.826172" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="374.609375" xlink:href="#DejaVuSans-63"/>
|
||||
<use x="429.589844" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="490.869141" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="530.078125" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="557.861328" xlink:href="#DejaVuSans-76"/>
|
||||
<use x="617.041016" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="678.564453" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="710.351562" xlink:href="#DejaVuSans-63"/>
|
||||
<use x="765.332031" xlink:href="#DejaVuSans-6f"/>
|
||||
<use x="826.513672" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="878.613281" xlink:href="#DejaVuSans-74"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="line2d_14">
|
||||
<path clip-path="url(#p0570563b73)" d="M 45.929206 188.794948
|
||||
L 49.490756 188.645161
|
||||
L 53.052306 188.492394
|
||||
L 56.613856 188.336556
|
||||
L 60.175406 188.177554
|
||||
L 63.736956 188.01529
|
||||
L 67.298506 187.849663
|
||||
L 70.860056 187.680567
|
||||
L 74.421606 187.507893
|
||||
L 77.983156 187.331525
|
||||
L 81.544706 187.151344
|
||||
L 85.106256 186.967225
|
||||
L 88.667806 186.779036
|
||||
L 92.229356 186.586643
|
||||
L 95.790906 186.389901
|
||||
L 99.352456 186.188662
|
||||
L 102.914006 185.982771
|
||||
L 106.475556 185.772063
|
||||
L 110.037106 185.556368
|
||||
L 113.598656 185.335507
|
||||
L 117.160206 185.109291
|
||||
L 120.721756 184.877525
|
||||
L 124.283306 184.64
|
||||
L 127.844856 184.396499
|
||||
L 131.406406 184.146795
|
||||
L 134.967956 183.890647
|
||||
L 138.529506 183.627802
|
||||
L 142.091056 183.357994
|
||||
L 145.652606 183.080943
|
||||
L 149.214156 182.796354
|
||||
L 152.775706 182.503913
|
||||
L 156.337256 182.203293
|
||||
L 159.898806 181.894144
|
||||
L 163.460356 181.576099
|
||||
L 167.021906 181.248768
|
||||
L 170.583456 180.911738
|
||||
L 174.145006 180.564572
|
||||
L 177.706556 180.206805
|
||||
L 181.268106 179.837945
|
||||
L 184.829656 179.457468
|
||||
L 188.391206 179.064815
|
||||
L 191.952756 178.659392
|
||||
L 195.514306 178.240568
|
||||
L 199.075856 177.807665
|
||||
L 202.637406 177.359962
|
||||
L 206.198956 176.896687
|
||||
L 209.760506 176.417013
|
||||
L 213.322056 175.920053
|
||||
L 216.883606 175.404856
|
||||
L 220.445156 174.8704
|
||||
L 224.006706 174.315583
|
||||
L 227.568256 173.73922
|
||||
L 231.129806 173.14003
|
||||
L 234.691356 172.516631
|
||||
L 238.252906 171.867525
|
||||
L 241.814456 171.191088
|
||||
L 245.376006 170.485557
|
||||
L 248.937556 169.749014
|
||||
L 252.499106 168.979367
|
||||
L 256.060656 168.174335
|
||||
L 259.622206 167.331419
|
||||
L 263.183756 166.44788
|
||||
L 266.745306 165.520709
|
||||
L 270.306856 164.546593
|
||||
L 273.868406 163.521874
|
||||
L 277.429956 162.442503
|
||||
L 280.991506 161.303989
|
||||
L 284.553056 160.101333
|
||||
L 288.114606 158.828958
|
||||
L 291.676156 157.48062
|
||||
L 295.237706 156.049307
|
||||
L 298.799256 154.527117
|
||||
L 302.360806 152.905112
|
||||
L 305.922356 151.17314
|
||||
L 309.483906 149.319626
|
||||
L 313.045456 147.331312
|
||||
L 316.607006 145.192935
|
||||
L 320.168556 142.886843
|
||||
L 323.730106 140.392499
|
||||
L 327.291656 137.68587
|
||||
L 330.853206 134.738652
|
||||
L 334.414756 131.517273
|
||||
L 337.976306 127.981615
|
||||
L 341.537856 124.083324
|
||||
L 345.099406 119.763597
|
||||
L 348.660956 114.950186
|
||||
L 352.222506 109.553332
|
||||
L 355.784056 103.460109
|
||||
L 359.345606 96.526442
|
||||
L 362.907156 88.565565
|
||||
L 366.468706 79.330948
|
||||
L 370.030256 68.49031
|
||||
L 373.591806 55.584789
|
||||
L 377.153356 39.962316
|
||||
L 380.714906 20.663968
|
||||
L 383.871324 -1
|
||||
" style="fill:none;stroke:#1f77b4;stroke-linecap:square;stroke-width:1.5;"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 45.929206 203.998281
|
||||
L 45.929206 21.558281
|
||||
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
|
||||
</g>
|
||||
<g id="patch_4">
|
||||
<path d="M 45.929206 203.998281
|
||||
L 402.084206 203.998281
|
||||
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
|
||||
</g>
|
||||
<g id="text_16">
|
||||
<!-- Metadata pair update cost -->
|
||||
<defs>
|
||||
<path d="M 9.8125 72.90625
|
||||
L 24.515625 72.90625
|
||||
L 43.109375 23.296875
|
||||
L 61.8125 72.90625
|
||||
L 76.515625 72.90625
|
||||
L 76.515625 0
|
||||
L 66.890625 0
|
||||
L 66.890625 64.015625
|
||||
L 48.09375 14.015625
|
||||
L 38.1875 14.015625
|
||||
L 19.390625 64.015625
|
||||
L 19.390625 0
|
||||
L 9.8125 0
|
||||
z
|
||||
" id="DejaVuSans-4d"/>
|
||||
<path d="M 45.40625 46.390625
|
||||
L 45.40625 75.984375
|
||||
L 54.390625 75.984375
|
||||
L 54.390625 0
|
||||
L 45.40625 0
|
||||
L 45.40625 8.203125
|
||||
Q 42.578125 3.328125 38.25 0.953125
|
||||
Q 33.9375 -1.421875 27.875 -1.421875
|
||||
Q 17.96875 -1.421875 11.734375 6.484375
|
||||
Q 5.515625 14.40625 5.515625 27.296875
|
||||
Q 5.515625 40.1875 11.734375 48.09375
|
||||
Q 17.96875 56 27.875 56
|
||||
Q 33.9375 56 38.25 53.625
|
||||
Q 42.578125 51.265625 45.40625 46.390625
|
||||
z
|
||||
M 14.796875 27.296875
|
||||
Q 14.796875 17.390625 18.875 11.75
|
||||
Q 22.953125 6.109375 30.078125 6.109375
|
||||
Q 37.203125 6.109375 41.296875 11.75
|
||||
Q 45.40625 17.390625 45.40625 27.296875
|
||||
Q 45.40625 37.203125 41.296875 42.84375
|
||||
Q 37.203125 48.484375 30.078125 48.484375
|
||||
Q 22.953125 48.484375 18.875 42.84375
|
||||
Q 14.796875 37.203125 14.796875 27.296875
|
||||
z
|
||||
" id="DejaVuSans-64"/>
|
||||
<path d="M 41.109375 46.296875
|
||||
Q 39.59375 47.171875 37.8125 47.578125
|
||||
Q 36.03125 48 33.890625 48
|
||||
Q 26.265625 48 22.1875 43.046875
|
||||
Q 18.109375 38.09375 18.109375 28.8125
|
||||
L 18.109375 0
|
||||
L 9.078125 0
|
||||
L 9.078125 54.6875
|
||||
L 18.109375 54.6875
|
||||
L 18.109375 46.1875
|
||||
Q 20.953125 51.171875 25.484375 53.578125
|
||||
Q 30.03125 56 36.53125 56
|
||||
Q 37.453125 56 38.578125 55.875
|
||||
Q 39.703125 55.765625 41.0625 55.515625
|
||||
z
|
||||
" id="DejaVuSans-72"/>
|
||||
</defs>
|
||||
<g transform="translate(151.276081 15.558281)scale(0.11 -0.11)">
|
||||
<use xlink:href="#DejaVuSans-4d"/>
|
||||
<use x="86.279297" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="147.802734" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="187.011719" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="248.291016" xlink:href="#DejaVuSans-64"/>
|
||||
<use x="311.767578" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="373.046875" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="412.255859" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="473.535156" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="505.322266" xlink:href="#DejaVuSans-70"/>
|
||||
<use x="568.798828" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="630.078125" xlink:href="#DejaVuSans-69"/>
|
||||
<use x="657.861328" xlink:href="#DejaVuSans-72"/>
|
||||
<use x="698.974609" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="730.761719" xlink:href="#DejaVuSans-75"/>
|
||||
<use x="794.140625" xlink:href="#DejaVuSans-70"/>
|
||||
<use x="857.617188" xlink:href="#DejaVuSans-64"/>
|
||||
<use x="921.09375" xlink:href="#DejaVuSans-61"/>
|
||||
<use x="982.373047" xlink:href="#DejaVuSans-74"/>
|
||||
<use x="1021.582031" xlink:href="#DejaVuSans-65"/>
|
||||
<use x="1083.105469" xlink:href="#DejaVuSans-20"/>
|
||||
<use x="1114.892578" xlink:href="#DejaVuSans-63"/>
|
||||
<use x="1169.873047" xlink:href="#DejaVuSans-6f"/>
|
||||
<use x="1231.054688" xlink:href="#DejaVuSans-73"/>
|
||||
<use x="1283.154297" xlink:href="#DejaVuSans-74"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="p0570563b73">
|
||||
<rect height="182.44" width="356.155" x="45.929206" y="21.558281"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 28 KiB |
|
|
@ -1,44 +0,0 @@
|
|||
#!/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
112
scripts/debug.py
|
|
@ -1,112 +0,0 @@
|
|||
#!/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:]))
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
#!/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:]))
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#!/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:])
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/// AUTOGENERATED TEST ///
|
||||
#include "lfs.h"
|
||||
#include "emubd/lfs_emubd.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
// test stuff
|
||||
static void test_assert(const char *file, unsigned line,
|
||||
const char *s, uintmax_t v, uintmax_t e) {{
|
||||
if (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(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) {{
|
||||
(void)b;
|
||||
unsigned *u = (unsigned*)p;
|
||||
*u += 1;
|
||||
return 0;
|
||||
}}
|
||||
|
||||
// lfs declarations
|
||||
lfs_t lfs;
|
||||
lfs_emubd_t bd;
|
||||
// other declarations for convenience
|
||||
lfs_file_t file;
|
||||
lfs_dir_t dir;
|
||||
struct lfs_info info;
|
||||
uint8_t buffer[1024];
|
||||
char path[1024];
|
||||
|
||||
// test configuration options
|
||||
#ifndef LFS_READ_SIZE
|
||||
#define LFS_READ_SIZE 16
|
||||
#endif
|
||||
|
||||
#ifndef LFS_PROG_SIZE
|
||||
#define LFS_PROG_SIZE LFS_READ_SIZE
|
||||
#endif
|
||||
|
||||
#ifndef LFS_BLOCK_SIZE
|
||||
#define LFS_BLOCK_SIZE 512
|
||||
#endif
|
||||
|
||||
#ifndef LFS_BLOCK_COUNT
|
||||
#define LFS_BLOCK_COUNT 1024
|
||||
#endif
|
||||
|
||||
#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 = {{
|
||||
.context = &bd,
|
||||
.read = &lfs_emubd_read,
|
||||
.prog = &lfs_emubd_prog,
|
||||
.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,
|
||||
.block_cycles = LFS_BLOCK_CYCLES,
|
||||
.cache_size = LFS_CACHE_SIZE,
|
||||
.lookahead_size = LFS_LOOKAHEAD_SIZE,
|
||||
}};
|
||||
|
||||
|
||||
// Entry point
|
||||
int main(void) {{
|
||||
lfs_emubd_create(&cfg, "blocks");
|
||||
|
||||
{tests}
|
||||
lfs_emubd_destroy(&cfg);
|
||||
}}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#!/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,484 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -euE
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Allocator tests ==="
|
||||
rm -rf blocks
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
SIZE=15000
|
||||
|
||||
lfs_mkdir() {
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "$1") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_remove() {
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "$1/eggs") => 0;
|
||||
lfs_remove(&lfs, "$1/bacon") => 0;
|
||||
lfs_remove(&lfs, "$1/pancakes") => 0;
|
||||
lfs_remove(&lfs, "$1") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_alloc_singleproc() {
|
||||
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 (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 (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, &files[n], names[n], size) => size;
|
||||
}
|
||||
}
|
||||
for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
|
||||
lfs_file_close(&lfs, &files[n]) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_alloc_multiproc() {
|
||||
for name in bacon eggs pancakes
|
||||
do
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "$1/$name",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
lfs_size_t size = strlen("$name");
|
||||
memcpy(buffer, "$name", size);
|
||||
for (int i = 0; i < $SIZE; i++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
done
|
||||
}
|
||||
|
||||
lfs_verify() {
|
||||
for name in bacon eggs pancakes
|
||||
do
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, buffer, size) => size;
|
||||
memcmp(buffer, "$name", size) => 0;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
done
|
||||
}
|
||||
|
||||
echo "--- Single-process allocation test ---"
|
||||
lfs_mkdir singleproc
|
||||
lfs_alloc_singleproc singleproc
|
||||
lfs_verify singleproc
|
||||
|
||||
echo "--- Multi-process allocation test ---"
|
||||
lfs_mkdir multiproc
|
||||
lfs_alloc_multiproc multiproc
|
||||
lfs_verify multiproc
|
||||
lfs_verify singleproc
|
||||
|
||||
echo "--- Single-process reuse test ---"
|
||||
lfs_remove singleproc
|
||||
lfs_mkdir singleprocreuse
|
||||
lfs_alloc_singleproc singleprocreuse
|
||||
lfs_verify singleprocreuse
|
||||
lfs_verify multiproc
|
||||
|
||||
echo "--- Multi-process reuse test ---"
|
||||
lfs_remove multiproc
|
||||
lfs_mkdir multiprocreuse
|
||||
lfs_alloc_singleproc multiprocreuse
|
||||
lfs_verify multiprocreuse
|
||||
lfs_verify singleprocreuse
|
||||
|
||||
echo "--- Cleanup ---"
|
||||
lfs_remove multiprocreuse
|
||||
lfs_remove singleprocreuse
|
||||
|
||||
echo "--- Exhaustion test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, 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, buffer, size);
|
||||
if (res < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
res => size;
|
||||
}
|
||||
res => LFS_ERR_NOSPC;
|
||||
|
||||
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, "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;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Exhaustion wraparound test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
|
||||
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, buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_remove(&lfs, "padding") => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size = strlen("exhaustion");
|
||||
memcpy(buffer, "exhaustion", size);
|
||||
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, buffer, size);
|
||||
if (res < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
res => size;
|
||||
}
|
||||
res => LFS_ERR_NOSPC;
|
||||
|
||||
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, "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;
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Dir exhaustion test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
|
||||
// find out max file size
|
||||
lfs_mkdir(&lfs, "exhaustiondir") => 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;
|
||||
}
|
||||
|
||||
count += 1;
|
||||
}
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
|
||||
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Chained dir exhaustion test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
|
||||
// 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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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, "exhaustiondir2") => LFS_ERR_NOSPC;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Split dir test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
|
||||
// create one block hole for half a directory
|
||||
lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
|
||||
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, "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-4)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
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, "splitdir/bump",
|
||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
|
||||
memcpy(&buffer[i], "hi", 2);
|
||||
}
|
||||
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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
|
||||
// fill completely with two files
|
||||
lfs_file_open(&lfs, &file, "exhaustion1",
|
||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_size_t size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
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-2+1)/2)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
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, "exhaustion1",
|
||||
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
// rewrite second file, this requires lookahead does not
|
||||
// use old population
|
||||
lfs_file_open(&lfs, &file, "exhaustion2",
|
||||
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Outdated lookahead and split dir 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, "exhaustion1",
|
||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_size_t size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
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-2+1)/2)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
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, "exhaustion1",
|
||||
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
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, "notasplit",
|
||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_file_write(&lfs, &file, "hi", 2) => 2;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
scripts/results.py
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
#!/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,120 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Corrupt tests ==="
|
||||
|
||||
NAMEMULT=64
|
||||
FILEMULT=1
|
||||
|
||||
lfs_mktree() {
|
||||
scripts/test.py ${1:-} << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
for (int i = 1; i < 10; i++) {
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j] = '0'+i;
|
||||
}
|
||||
buffer[$NAMEMULT] = '\0';
|
||||
lfs_mkdir(&lfs, (char*)buffer) => 0;
|
||||
|
||||
buffer[$NAMEMULT] = '/';
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j+$NAMEMULT+1] = '0'+i;
|
||||
}
|
||||
buffer[2*$NAMEMULT+1] = '\0';
|
||||
lfs_file_open(&lfs, &file, (char*)buffer,
|
||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
|
||||
lfs_size_t size = $NAMEMULT;
|
||||
for (int j = 0; j < i*$FILEMULT; j++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_chktree() {
|
||||
scripts/test.py ${1:-} << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
for (int i = 1; i < 10; i++) {
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j] = '0'+i;
|
||||
}
|
||||
buffer[$NAMEMULT] = '\0';
|
||||
lfs_stat(&lfs, (char*)buffer, &info) => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
|
||||
buffer[$NAMEMULT] = '/';
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j+$NAMEMULT+1] = '0'+i;
|
||||
}
|
||||
buffer[2*$NAMEMULT+1] = '\0';
|
||||
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
|
||||
|
||||
lfs_size_t size = $NAMEMULT;
|
||||
for (int j = 0; j < i*$FILEMULT; j++) {
|
||||
uint8_t rbuffer[1024];
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
memcmp(buffer, rbuffer, size) => 0;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
echo "--- Sanity check ---"
|
||||
rm -rf blocks
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
BLOCKS="$(ls blocks | grep -vw '[01]')"
|
||||
|
||||
echo "--- Block corruption ---"
|
||||
for b in $BLOCKS
|
||||
do
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
ln -s /dev/zero blocks/$b
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
done
|
||||
|
||||
echo "--- Block persistance ---"
|
||||
for b in $BLOCKS
|
||||
do
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
lfs_mktree
|
||||
chmod a-w blocks/$b || true
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
done
|
||||
|
||||
echo "--- Big region corruption ---"
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
for i in {2..512}
|
||||
do
|
||||
ln -s /dev/zero blocks/$(printf '%x' $i)
|
||||
done
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
|
||||
echo "--- Alternating corruption ---"
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
for i in {2..1024..2}
|
||||
do
|
||||
ln -s /dev/zero blocks/$(printf '%x' $i)
|
||||
done
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
|
||||
scripts/results.py
|
||||
|
|
@ -1,489 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Directory tests ==="
|
||||
|
||||
LARGESIZE=128
|
||||
|
||||
rm -rf blocks
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root directory ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "/") => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Directory creation ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "potato") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- File creation ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST;
|
||||
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 ---"
|
||||
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
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "cactus") => 0;
|
||||
for (int i = 0; i < $LARGESIZE; i++) {
|
||||
sprintf(path, "cactus/test%03d", i);
|
||||
lfs_mkdir(&lfs, path) => 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, "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, &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Directory remove ---"
|
||||
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, "potato") => 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;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
lfs_remove(&lfs, "potato") => 0;
|
||||
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "cactus") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "cactus") => 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 rename ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato") => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato/baked") => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato/sweet") => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato/fried") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
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
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "warmpotato") => 0;
|
||||
lfs_mkdir(&lfs, "warmpotato/mushy") => 0;
|
||||
lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERR_NOTEMPTY;
|
||||
|
||||
lfs_remove(&lfs, "warmpotato/mushy") => 0;
|
||||
lfs_rename(&lfs, "hotpotato", "warmpotato") => 0;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
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
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato") => 0;
|
||||
lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0;
|
||||
lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0;
|
||||
lfs_rename(&lfs, "warmpotato/fried", "coldpotato/fried") => 0;
|
||||
lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
|
||||
lfs_remove(&lfs, "warmpotato") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
|
||||
|
||||
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, &info);
|
||||
err >= 0 => 1;
|
||||
if (err == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(path, "coldpotato/");
|
||||
strcat(path, info.name);
|
||||
lfs_remove(&lfs, path) => 0;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "coldpotato") => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "cactus") => 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 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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
|
||||
|
||||
for (int i = 0; i < $LARGESIZE; i++) {
|
||||
sprintf(path, "cactus/tedd%03d", i);
|
||||
lfs_remove(&lfs, path) => 0;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "cactus") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Multi-block directory with files ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "prickly-pear") => 0;
|
||||
for (int i = 0; i < $LARGESIZE; i++) {
|
||||
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
|
||||
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, "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, &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 ---"
|
||||
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(path, "prickly-pear/tedd%03d", i);
|
||||
lfs_remove(&lfs, path) => 0;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "prickly-pear") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
scripts/results.py
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
#!/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,221 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== File tests ==="
|
||||
|
||||
SMALLSIZE=32
|
||||
MEDIUMSIZE=8192
|
||||
LARGESIZE=262144
|
||||
|
||||
rm -rf blocks
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple file test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, wbuffer, size) => size;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||
size = strlen("Hello World!\n");
|
||||
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
|
||||
|
||||
w_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, "$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, buffer, chunk) => chunk;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
r_test() {
|
||||
scripts/test.py << TEST
|
||||
lfs_size_t size = $1;
|
||||
lfs_size_t chunk = 29;
|
||||
srand(0);
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "$2", &info) => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => size;
|
||||
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, 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;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
echo "--- Small file test ---"
|
||||
w_test $SMALLSIZE smallavacado
|
||||
r_test $SMALLSIZE smallavacado
|
||||
|
||||
echo "--- Medium file test ---"
|
||||
w_test $MEDIUMSIZE mediumavacado
|
||||
r_test $MEDIUMSIZE mediumavacado
|
||||
|
||||
echo "--- Large file test ---"
|
||||
w_test $LARGESIZE largeavacado
|
||||
r_test $LARGESIZE largeavacado
|
||||
|
||||
echo "--- Zero file test ---"
|
||||
w_test 0 noavacado
|
||||
r_test 0 noavacado
|
||||
|
||||
echo "--- Truncate small test ---"
|
||||
w_test $SMALLSIZE mediumavacado
|
||||
r_test $SMALLSIZE mediumavacado
|
||||
w_test $MEDIUMSIZE mediumavacado
|
||||
r_test $MEDIUMSIZE mediumavacado
|
||||
|
||||
echo "--- Truncate zero test ---"
|
||||
w_test $SMALLSIZE noavacado
|
||||
r_test $SMALLSIZE noavacado
|
||||
w_test 0 noavacado
|
||||
r_test 0 noavacado
|
||||
|
||||
echo "--- Non-overlap check ---"
|
||||
r_test $SMALLSIZE smallavacado
|
||||
r_test $MEDIUMSIZE mediumavacado
|
||||
r_test $LARGESIZE largeavacado
|
||||
r_test 0 noavacado
|
||||
|
||||
echo "--- Dir check ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "largeavacado") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => $LARGESIZE;
|
||||
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, &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 files 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_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 "--- 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,51 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Formatting tests ==="
|
||||
rm -rf blocks
|
||||
|
||||
echo "--- Basic formatting ---"
|
||||
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
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC;
|
||||
TEST
|
||||
rm blocks/0 blocks/1
|
||||
|
||||
echo "--- Invalid mount ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
|
||||
TEST
|
||||
|
||||
echo "--- Expanding superblock ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
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
|
||||
|
||||
scripts/results.py
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Interspersed tests ==="
|
||||
rm -rf blocks
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Interspersed file test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 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, &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, &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;
|
||||
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;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "b") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "d") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 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, &files[0], buffer, 1) => 1;
|
||||
buffer[0] => 'a';
|
||||
lfs_file_read(&lfs, &files[1], buffer, 1) => 1;
|
||||
buffer[0] => 'b';
|
||||
lfs_file_read(&lfs, &files[2], buffer, 1) => 1;
|
||||
buffer[0] => 'c';
|
||||
lfs_file_read(&lfs, &files[3], buffer, 1) => 1;
|
||||
buffer[0] => 'd';
|
||||
}
|
||||
|
||||
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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 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, &files[0], (const void*)"e", 1) => 1;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "a") => 0;
|
||||
lfs_remove(&lfs, "b") => 0;
|
||||
lfs_remove(&lfs, "c") => 0;
|
||||
lfs_remove(&lfs, "d") => 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &files[0], (const void*)"e", 1) => 1;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &files[0]);
|
||||
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
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, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &files[0], "e", LFS_O_RDONLY) => 0;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
lfs_file_read(&lfs, &files[0], buffer, 1) => 1;
|
||||
buffer[0] => 'e';
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &files[0]);
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Remove inconveniently test ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 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, &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, &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, &files[0]);
|
||||
lfs_file_close(&lfs, &files[1]);
|
||||
lfs_file_close(&lfs, &files[2]);
|
||||
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
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, &info) => 1;
|
||||
strcmp(info.name, "g") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 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, &files[0], buffer, 1) => 1;
|
||||
buffer[0] => 'e';
|
||||
lfs_file_read(&lfs, &files[1], buffer, 1) => 1;
|
||||
buffer[0] => 'g';
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &files[0]);
|
||||
lfs_file_close(&lfs, &files[1]);
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
scripts/results.py
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Move tests ==="
|
||||
rm -rf blocks
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "a") => 0;
|
||||
lfs_mkdir(&lfs, "b") => 0;
|
||||
lfs_mkdir(&lfs, "c") => 0;
|
||||
lfs_mkdir(&lfs, "d") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "a/hi") => 0;
|
||||
lfs_mkdir(&lfs, "a/hi/hola") => 0;
|
||||
lfs_mkdir(&lfs, "a/hi/bonjour") => 0;
|
||||
lfs_mkdir(&lfs, "a/hi/ohayo") => 0;
|
||||
|
||||
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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "a/hello", "b/hello") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "a") => 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, "hi") => 0;
|
||||
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, &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 file corrupt source ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "b/hello", "c/hello") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/corrupt.py -n 1
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "b") => 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, "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) => 1;
|
||||
strcmp(info.name, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move file corrupt source and dest ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "c/hello", "d/hello") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/corrupt.py -n 2
|
||||
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) => 1;
|
||||
strcmp(info.name, "hello") => 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) => 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 ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "a/hi", "b/hi") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "a") => 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, "b") => 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, "hi") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move dir corrupt source ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "b/hi", "c/hi") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/corrupt.py -n 1
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "b") => 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, "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) => 1;
|
||||
strcmp(info.name, "hi") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move dir corrupt source and dest ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "c/hi", "d/hi") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
scripts/corrupt.py -n 2
|
||||
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) => 1;
|
||||
strcmp(info.name, "hi") => 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 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 ---"
|
||||
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/hi") => LFS_ERR_NOENT;
|
||||
lfs_dir_open(&lfs, &dir, "c/hi") => 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/hello") => LFS_ERR_NOENT;
|
||||
lfs_dir_open(&lfs, &dir, "c/hello") => 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 "--- 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
|
||||
|
||||
|
||||
scripts/results.py
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Orphan tests ==="
|
||||
rm -rf blocks
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Orphan 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
|
||||
# corrupt most recent commit, this should be the update to the previous
|
||||
# linked-list entry and should orphan the child
|
||||
scripts/corrupt.py
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
|
||||
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
|
||||
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;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
scripts/results.py
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Path tests ==="
|
||||
rm -rf blocks
|
||||
scripts/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "tea") => 0;
|
||||
lfs_mkdir(&lfs, "coffee") => 0;
|
||||
lfs_mkdir(&lfs, "soda") => 0;
|
||||
lfs_mkdir(&lfs, "tea/hottea") => 0;
|
||||
lfs_mkdir(&lfs, "tea/warmtea") => 0;
|
||||
lfs_mkdir(&lfs, "tea/coldtea") => 0;
|
||||
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
|
||||
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
|
||||
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
|
||||
lfs_mkdir(&lfs, "soda/hotsoda") => 0;
|
||||
lfs_mkdir(&lfs, "soda/warmsoda") => 0;
|
||||
lfs_mkdir(&lfs, "soda/coldsoda") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root path tests ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "/milk1") => 0;
|
||||
lfs_stat(&lfs, "/milk1", &info) => 0;
|
||||
strcmp(info.name, "milk1") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Redundant slash path tests ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "//tea//hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "///tea///hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "///milk2") => 0;
|
||||
lfs_stat(&lfs, "///milk2", &info) => 0;
|
||||
strcmp(info.name, "milk2") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Dot path tests ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "./tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/./tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/././tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/./tea/./hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "/./milk3") => 0;
|
||||
lfs_stat(&lfs, "/./milk3", &info) => 0;
|
||||
strcmp(info.name, "milk3") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Dot dot path tests ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "coffee/coldcoffee/../../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "coffee/../soda/../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "coffee/../milk4") => 0;
|
||||
lfs_stat(&lfs, "coffee/../milk4", &info) => 0;
|
||||
strcmp(info.name, "milk4") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Trailing dot path tests ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "tea/hottea/", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "tea/hottea/.", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "tea/hottea/./.", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "tea/hottea/..", &info) => 0;
|
||||
strcmp(info.name, "tea") => 0;
|
||||
lfs_stat(&lfs, "tea/hottea/../.", &info) => 0;
|
||||
strcmp(info.name, "tea") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root dot dot path tests ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "coffee/../../../../../../milk5") => 0;
|
||||
lfs_stat(&lfs, "coffee/../../../../../../milk5", &info) => 0;
|
||||
strcmp(info.name, "milk5") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root tests ---"
|
||||
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, "/", 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 ---"
|
||||
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 "--- 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,431 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Seek tests ==="
|
||||
|
||||
SMALLSIZE=4
|
||||
MEDIUMSIZE=128
|
||||
LARGESIZE=132
|
||||
|
||||
rm -rf blocks
|
||||
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(path, "hello/kitty%03d", i);
|
||||
lfs_file_open(&lfs, &file, path,
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
|
||||
lfs_size_t size = strlen("kittycatcat");
|
||||
memcpy(buffer, "kittycatcat", size);
|
||||
for (int j = 0; j < $LARGESIZE; j++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size);
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple dir seek ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
int i;
|
||||
for (i = 0; i < $SMALLSIZE; i++) {
|
||||
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, 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;
|
||||
sprintf(path, "kitty%03d", 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, path) => 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;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Large dir seek ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
int i;
|
||||
for (i = 0; i < $MEDIUMSIZE; i++) {
|
||||
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, 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;
|
||||
sprintf(path, "kitty%03d", 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, path) => 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;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple file seek ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDONLY) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
lfs_size_t size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $SMALLSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
pos = lfs_file_tell(&lfs, &file);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
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;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
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, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_CUR) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
size = lfs_file_size(&lfs, &file);
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Large file seek ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDONLY) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
lfs_size_t size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $MEDIUMSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
pos = lfs_file_tell(&lfs, &file);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
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;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
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, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_CUR) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
size = lfs_file_size(&lfs, &file);
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple file seek and write ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
lfs_size_t size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $SMALLSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
pos = lfs_file_tell(&lfs, &file);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_rewind(&lfs, &file) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
size = lfs_file_size(&lfs, &file);
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Large file seek and write ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
lfs_size_t size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $MEDIUMSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
if (i != $SMALLSIZE) {
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
}
|
||||
pos = lfs_file_tell(&lfs, &file);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_rewind(&lfs, &file) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
size = lfs_file_size(&lfs, &file);
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Boundary seek and write ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0;
|
||||
|
||||
lfs_size_t size = strlen("hedgehoghog");
|
||||
const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
|
||||
|
||||
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
|
||||
lfs_soff_t off = offsets[i];
|
||||
memcpy(buffer, "hedgehoghog", size);
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hedgehoghog", size) => 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Out-of-bounds seek ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "hello/kitty042", LFS_O_RDWR) => 0;
|
||||
|
||||
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, buffer, size) => 0;
|
||||
|
||||
memcpy(buffer, "porcupineee", size);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
lfs_file_seek(&lfs, &file, ($LARGESIZE+$SMALLSIZE)*size,
|
||||
LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "porcupineee", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file, $LARGESIZE*size,
|
||||
LFS_SEEK_SET) => $LARGESIZE*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, -(($LARGESIZE+$SMALLSIZE)*size),
|
||||
LFS_SEEK_CUR) => LFS_ERR_INVAL;
|
||||
lfs_file_tell(&lfs, &file) => ($LARGESIZE+1)*size;
|
||||
|
||||
lfs_file_seek(&lfs, &file, -(($LARGESIZE+2*$SMALLSIZE)*size),
|
||||
LFS_SEEK_END) => LFS_ERR_INVAL;
|
||||
lfs_file_tell(&lfs, &file) => ($LARGESIZE+1)*size;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
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,355 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
export TEST_FILE=$0
|
||||
trap 'export TEST_LINE=$LINENO' DEBUG
|
||||
|
||||
echo "=== Truncate tests ==="
|
||||
|
||||
SMALLSIZE=32
|
||||
MEDIUMSIZE=2048
|
||||
LARGESIZE=8192
|
||||
|
||||
rm -rf blocks
|
||||
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 "--- Write, truncate, and read ---"
|
||||
scripts/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "sequence",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
||||
|
||||
lfs_size_t size = lfs.cfg->cache_size;
|
||||
lfs_size_t qsize = size / 4;
|
||||
uint8_t *wb = buffer;
|
||||
uint8_t *rb = buffer + size;
|
||||
for (lfs_off_t j = 0; j < size; ++j) {
|
||||
wb[j] = j;
|
||||
}
|
||||
|
||||
/* Spread sequence over size */
|
||||
lfs_file_write(&lfs, &file, wb, size) => size;
|
||||
lfs_file_size(&lfs, &file) => size;
|
||||
lfs_file_tell(&lfs, &file) => size;
|
||||
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
|
||||
lfs_file_tell(&lfs, &file) => 0;
|
||||
|
||||
/* Chop off the last quarter */
|
||||
lfs_size_t trunc = size - qsize;
|
||||
lfs_file_truncate(&lfs, &file, trunc) => 0;
|
||||
lfs_file_tell(&lfs, &file) => 0;
|
||||
lfs_file_size(&lfs, &file) => trunc;
|
||||
|
||||
/* Read should produce first 3/4 */
|
||||
lfs_file_read(&lfs, &file, rb, size) => trunc;
|
||||
memcmp(rb, wb, trunc) => 0;
|
||||
|
||||
/* Move to 1/4 */
|
||||
lfs_file_size(&lfs, &file) => trunc;
|
||||
lfs_file_seek(&lfs, &file, qsize, LFS_SEEK_SET) => qsize;
|
||||
lfs_file_tell(&lfs, &file) => qsize;
|
||||
|
||||
/* Chop to 1/2 */
|
||||
trunc -= qsize;
|
||||
lfs_file_truncate(&lfs, &file, trunc) => 0;
|
||||
lfs_file_tell(&lfs, &file) => qsize;
|
||||
lfs_file_size(&lfs, &file) => trunc;
|
||||
|
||||
/* Read should produce second quarter */
|
||||
lfs_file_read(&lfs, &file, rb, size) => trunc - qsize;
|
||||
memcmp(rb, wb + qsize, trunc - qsize) => 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"
|
||||
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 (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");
|
||||
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) => startsizes[i];
|
||||
|
||||
if (startseeks[i] != startsizes[i]) {
|
||||
lfs_file_seek(&lfs, &file,
|
||||
startseeks[i], LFS_SEEK_SET) => startseeks[i];
|
||||
}
|
||||
|
||||
lfs_file_truncate(&lfs, &file, hotsizes[i]) => 0;
|
||||
lfs_file_size(&lfs, &file) => hotsizes[i];
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
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 (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];
|
||||
|
||||
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, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
}
|
||||
|
||||
for (; j < hotsizes[i]; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "\0\0\0\0", size) => 0;
|
||||
}
|
||||
|
||||
lfs_file_truncate(&lfs, &file, coldsizes[i]) => 0;
|
||||
lfs_file_size(&lfs, &file) => coldsizes[i];
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
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 (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];
|
||||
|
||||
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, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
}
|
||||
|
||||
for (; j < coldsizes[i]; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "\0\0\0\0", size) => 0;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
echo "--- Cold shrinking truncate ---"
|
||||
truncate_test \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE"
|
||||
|
||||
echo "--- Cold expanding truncate ---"
|
||||
truncate_test \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE"
|
||||
|
||||
echo "--- Warm shrinking truncate ---"
|
||||
truncate_test \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, 0, 0, 0, 0"
|
||||
|
||||
echo "--- Warm expanding truncate ---"
|
||||
truncate_test \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE"
|
||||
|
||||
echo "--- Mid-file shrinking truncate ---"
|
||||
truncate_test \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
" $LARGESIZE, $LARGESIZE, $LARGESIZE, $LARGESIZE, $LARGESIZE" \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, 0, 0, 0, 0"
|
||||
|
||||
echo "--- Mid-file expanding truncate ---"
|
||||
truncate_test \
|
||||
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
|
||||
" 0, 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
|
||||
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE"
|
||||
|
||||
scripts/results.py
|
||||
52
wear-distribution.py
Executable file
52
wear-distribution.py
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('SVG')
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
import numpy as np
|
||||
|
||||
matplotlib.rc('font', family='sans-serif', size=11)
|
||||
matplotlib.rc('axes', titlesize='medium', labelsize='medium')
|
||||
matplotlib.rc('xtick', labelsize='small')
|
||||
matplotlib.rc('ytick', labelsize='small')
|
||||
|
||||
np.random.seed(map(ord, "hello"))
|
||||
|
||||
gs = gridspec.GridSpec(nrows=2, ncols=2, height_ratios=[1, 2],
|
||||
wspace=0.25, hspace=0.25)
|
||||
|
||||
fig = plt.figure(figsize=(7, 5.5))
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
ax2 = fig.add_subplot(gs[0, 1], sharex=ax1, sharey=ax1)
|
||||
ax3 = fig.add_subplot(gs[1, :])
|
||||
|
||||
# Convolution (I think?) of two types of uniform distributions under modulo
|
||||
w1 = np.random.randint(0, 100, 30)
|
||||
w2 = np.arange(0, 30, 1)
|
||||
w3 = np.array([w2 + i for i in w1]).flatten() % 100
|
||||
|
||||
ax1.hist(w1, bins=100, range=(0,100), histtype='stepfilled')
|
||||
ax2.hist(w2, bins=100, range=(0,100), histtype='stepfilled')
|
||||
ax3.hist(w3, bins=100, range=(0,100), histtype='stepfilled')
|
||||
|
||||
ax1.set_title('Random offset')
|
||||
ax2.set_title('Linear allocation')
|
||||
ax3.set_title('Cumulative allocation')
|
||||
|
||||
ax1.set_ylabel('wear')
|
||||
ax1.set_yticks(np.arange(0, 3))
|
||||
ax3.set_ylabel('wear')
|
||||
ax3.set_xlabel('block address')
|
||||
|
||||
for ax in [ax1,ax2,ax3]:
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_xticklabels([])
|
||||
ax.set_yticklabels([])
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['top'].set_visible(False)
|
||||
|
||||
ax1.text(109.25, 1, '$\\ast$')
|
||||
|
||||
fig.tight_layout()
|
||||
plt.savefig('wear-distribution.svg', bbox_inches="tight")
|
||||
2100
wear-distribution.svg
Normal file
2100
wear-distribution.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 55 KiB |
Loading…
Reference in a new issue