forked from Imagelibrary/littlefs
Compare commits
2 Commits
v2.8.1
...
rbyd-devel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d272bb62a | ||
|
|
44a196af42 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,4 +0,0 @@
|
||||
# GitHub really wants to mark littlefs as a python project, telling it to
|
||||
# reclassify our test .toml files as C code (which they are 95% of anyways)
|
||||
# remedies this
|
||||
*.toml linguist-language=c
|
||||
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@@ -81,9 +81,7 @@ jobs:
|
||||
- name: find-prev-version
|
||||
continue-on-error: true
|
||||
run: |
|
||||
LFS_PREV_VERSION="$( \
|
||||
git describe --tags --abbrev=0 --match 'v*' \
|
||||
|| true)"
|
||||
LFS_PREV_VERSION="$(git describe --tags --abbrev=0 --match 'v*')"
|
||||
echo "LFS_PREV_VERSION=$LFS_PREV_VERSION"
|
||||
echo "LFS_PREV_VERSION=$LFS_PREV_VERSION" >> $GITHUB_ENV
|
||||
|
||||
@@ -104,7 +102,7 @@ jobs:
|
||||
# sizes table
|
||||
i=0
|
||||
j=0
|
||||
for c in "" readonly threadsafe multiversion migrate error-asserts
|
||||
for c in "" readonly threadsafe migrate error-asserts
|
||||
do
|
||||
# per-config results
|
||||
c_or_default=${c:-default}
|
||||
@@ -242,7 +240,6 @@ jobs:
|
||||
run: |
|
||||
# create release and patch version tag (vN.N.N)
|
||||
# only draft if not a patch release
|
||||
touch release.txt
|
||||
[ -e table.txt ] && cat table.txt >> release.txt
|
||||
echo >> release.txt
|
||||
[ -e changes.txt ] && cat changes.txt >> release.txt
|
||||
|
||||
98
.github/workflows/test.yml
vendored
98
.github/workflows/test.yml
vendored
@@ -170,27 +170,6 @@ jobs:
|
||||
cp lfs.data.csv sizes/${{matrix.arch}}-threadsafe.data.csv
|
||||
cp lfs.stack.csv sizes/${{matrix.arch}}-threadsafe.stack.csv
|
||||
cp lfs.structs.csv sizes/${{matrix.arch}}-threadsafe.structs.csv
|
||||
- name: sizes-multiversion
|
||||
run: |
|
||||
make clean
|
||||
CFLAGS="$CFLAGS \
|
||||
-DLFS_NO_ASSERT \
|
||||
-DLFS_NO_DEBUG \
|
||||
-DLFS_NO_WARN \
|
||||
-DLFS_NO_ERROR \
|
||||
-DLFS_MULTIVERSION" \
|
||||
make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv
|
||||
./scripts/structs.py -u lfs.structs.csv
|
||||
./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \
|
||||
-bfunction \
|
||||
-fcode=code_size \
|
||||
-fdata=data_size \
|
||||
-fstack=stack_limit --max=stack_limit
|
||||
mkdir -p sizes
|
||||
cp lfs.code.csv sizes/${{matrix.arch}}-multiversion.code.csv
|
||||
cp lfs.data.csv sizes/${{matrix.arch}}-multiversion.data.csv
|
||||
cp lfs.stack.csv sizes/${{matrix.arch}}-multiversion.stack.csv
|
||||
cp lfs.structs.csv sizes/${{matrix.arch}}-multiversion.structs.csv
|
||||
- name: sizes-migrate
|
||||
run: |
|
||||
make clean
|
||||
@@ -374,42 +353,6 @@ jobs:
|
||||
run: |
|
||||
CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test
|
||||
|
||||
# run LFS_MULTIVERSION tests
|
||||
test-multiversion:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc python3 python3-pip
|
||||
pip3 install toml
|
||||
gcc --version
|
||||
python3 --version
|
||||
- name: test-multiversion
|
||||
run: |
|
||||
CFLAGS="$CFLAGS -DLFS_MULTIVERSION" make test
|
||||
|
||||
# run tests on the older version lfs2.0
|
||||
test-lfs2_0:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc python3 python3-pip
|
||||
pip3 install toml
|
||||
gcc --version
|
||||
python3 --version
|
||||
- name: test-lfs2_0
|
||||
run: |
|
||||
CFLAGS="$CFLAGS -DLFS_MULTIVERSION" \
|
||||
TESTFLAGS="$TESTFLAGS -DDISK_VERSION=0x00020000" \
|
||||
make test
|
||||
|
||||
# run under Valgrind to check for memory errors
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -428,8 +371,7 @@ jobs:
|
||||
# on one geometry
|
||||
- name: test-valgrind
|
||||
run: |
|
||||
TESTFLAGS="$TESTFLAGS --valgrind --context=1024 -Gdefault -Pnone" \
|
||||
make test
|
||||
TESTFLAGS="$TESTFLAGS --valgrind -Gdefault -Pnone" make test
|
||||
|
||||
# test that compilation is warning free under clang
|
||||
# run with Clang, mostly to check for Clang-specific warnings
|
||||
@@ -531,42 +473,6 @@ jobs:
|
||||
path: status
|
||||
retention-days: 1
|
||||
|
||||
# run compatibility tests using the current master as the previous version
|
||||
test-compat:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
# checkout the current pr target into lfsp
|
||||
- uses: actions/checkout@v2
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
with:
|
||||
ref: ${{github.event.pull_request.base.ref}}
|
||||
path: lfsp
|
||||
- name: install
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
run: |
|
||||
# need a few things
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc python3 python3-pip
|
||||
pip3 install toml
|
||||
gcc --version
|
||||
python3 --version
|
||||
# adjust prefix of lfsp
|
||||
- name: changeprefix
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
run: |
|
||||
./scripts/changeprefix.py lfs lfsp lfsp/*.h lfsp/*.c
|
||||
- name: test-compat
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
run: |
|
||||
TESTS=tests/test_compat.toml \
|
||||
SRC="$(find . lfsp -name '*.c' -maxdepth 1 \
|
||||
-and -not -name '*.t.*' \
|
||||
-and -not -name '*.b.*')" \
|
||||
CFLAGS="-DLFSP=lfsp/lfsp.h" \
|
||||
make test
|
||||
|
||||
# self-host with littlefs-fuse for a fuzz-like test
|
||||
fuse:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -742,7 +648,7 @@ jobs:
|
||||
# sizes table
|
||||
i=0
|
||||
j=0
|
||||
for c in "" readonly threadsafe multiversion migrate error-asserts
|
||||
for c in "" readonly threadsafe migrate error-asserts
|
||||
do
|
||||
# per-config results
|
||||
c_or_default=${c:-default}
|
||||
|
||||
30
Makefile
30
Makefile
@@ -1,5 +1,15 @@
|
||||
# overrideable build dir, default is in-place
|
||||
ifdef BUILDDIR
|
||||
# bit of a hack, but we want to make sure BUILDDIR directory structure
|
||||
# is correct before any commands
|
||||
$(if $(findstring n,$(MAKEFLAGS)),, $(shell mkdir -p \
|
||||
$(BUILDDIR)/ \
|
||||
$(BUILDDIR)/bd \
|
||||
$(BUILDDIR)/runners \
|
||||
$(BUILDDIR)/tests \
|
||||
$(BUILDDIR)/benches))
|
||||
endif
|
||||
BUILDDIR ?= .
|
||||
|
||||
# overridable target/src/tools/flags/etc
|
||||
ifneq ($(wildcard test.c main.c),)
|
||||
TARGET ?= $(BUILDDIR)/lfs
|
||||
@@ -63,7 +73,6 @@ CFLAGS += -fcallgraph-info=su
|
||||
CFLAGS += -g3
|
||||
CFLAGS += -I.
|
||||
CFLAGS += -std=c99 -Wall -Wextra -pedantic
|
||||
CFLAGS += -Wmissing-prototypes
|
||||
CFLAGS += -ftrack-macro-expansion=0
|
||||
ifdef DEBUG
|
||||
CFLAGS += -O0
|
||||
@@ -154,18 +163,6 @@ TESTFLAGS += --perf-path="$(PERF)"
|
||||
BENCHFLAGS += --perf-path="$(PERF)"
|
||||
endif
|
||||
|
||||
# this is a bit of a hack, but we want to make sure the BUILDDIR
|
||||
# directory structure is correct before we run any commands
|
||||
ifneq ($(BUILDDIR),.)
|
||||
$(if $(findstring n,$(MAKEFLAGS)),, $(shell mkdir -p \
|
||||
$(addprefix $(BUILDDIR)/,$(dir \
|
||||
$(SRC) \
|
||||
$(TESTS) \
|
||||
$(TEST_SRC) \
|
||||
$(BENCHES) \
|
||||
$(BENCH_SRC)))))
|
||||
endif
|
||||
|
||||
|
||||
# commands
|
||||
|
||||
@@ -355,7 +352,6 @@ summary-diff sizes-diff: $(OBJ) $(CI)
|
||||
|
||||
## Build the test-runner
|
||||
.PHONY: test-runner build-test
|
||||
test-runner build-test: CFLAGS+=-Wno-missing-prototypes
|
||||
ifndef NO_COV
|
||||
test-runner build-test: CFLAGS+=--coverage
|
||||
endif
|
||||
@@ -407,7 +403,6 @@ testmarks-diff: $(TEST_CSV)
|
||||
|
||||
## Build the bench-runner
|
||||
.PHONY: bench-runner build-bench
|
||||
bench-runner build-bench: CFLAGS+=-Wno-missing-prototypes
|
||||
ifdef YES_COV
|
||||
bench-runner build-bench: CFLAGS+=--coverage
|
||||
endif
|
||||
@@ -519,9 +514,6 @@ $(BUILDDIR)/runners/bench_runner: $(BENCH_OBJ)
|
||||
$(BUILDDIR)/%.o $(BUILDDIR)/%.ci: %.c
|
||||
$(CC) -c -MMD $(CFLAGS) $< -o $(BUILDDIR)/$*.o
|
||||
|
||||
$(BUILDDIR)/%.o $(BUILDDIR)/%.ci: $(BUILDDIR)/%.c
|
||||
$(CC) -c -MMD $(CFLAGS) $< -o $(BUILDDIR)/$*.o
|
||||
|
||||
$(BUILDDIR)/%.s: %.c
|
||||
$(CC) -S $(CFLAGS) $< -o $@
|
||||
|
||||
|
||||
18
README.md
18
README.md
@@ -226,13 +226,6 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
to create images of the filesystem on your PC. Check if littlefs will fit
|
||||
your needs, create images for a later download to the target memory or
|
||||
inspect the content of a binary image of the target memory.
|
||||
|
||||
- [littlefs2-rust] - A Rust wrapper for littlefs. This project allows you
|
||||
to use littlefs in a Rust-friendly API, reaping the benefits of Rust's memory
|
||||
safety and other guarantees.
|
||||
|
||||
- [littlefs-disk-img-viewer] - A memory-efficient web application for viewing
|
||||
littlefs disk images in your web browser.
|
||||
|
||||
- [mklfs] - A command line tool built by the [Lua RTOS] guys for making
|
||||
littlefs images from a host PC. Supports Windows, Mac OS, and Linux.
|
||||
@@ -250,16 +243,8 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
MCUs. It offers static wear-leveling and power-resilience with only a fixed
|
||||
_O(|address|)_ pointer structure stored on each block and in RAM.
|
||||
|
||||
- [ChaN's FatFs] - A lightweight reimplementation of the infamous FAT filesystem
|
||||
for microcontroller-scale devices. Due to limitations of FAT it can't provide
|
||||
power-loss resilience, but it does allow easy interop with PCs.
|
||||
|
||||
- [chamelon] - A pure-OCaml implementation of (most of) littlefs, designed for
|
||||
use with the MirageOS library operating system project. It is interoperable
|
||||
with the reference implementation, with some caveats.
|
||||
|
||||
[BSD-3-Clause]: https://spdx.org/licenses/BSD-3-Clause.html
|
||||
[littlefs-disk-img-viewer]: https://github.com/tniessen/littlefs-disk-img-viewer
|
||||
[littlefs-fuse]: https://github.com/geky/littlefs-fuse
|
||||
[FUSE]: https://github.com/libfuse/libfuse
|
||||
[littlefs-js]: https://github.com/geky/littlefs-js
|
||||
@@ -270,7 +255,4 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
[LittleFileSystem]: https://os.mbed.com/docs/mbed-os/latest/apis/littlefilesystem.html
|
||||
[SPIFFS]: https://github.com/pellepl/spiffs
|
||||
[Dhara]: https://github.com/dlbeer/dhara
|
||||
[ChaN's FatFs]: http://elm-chan.org/fsw/ff/00index_e.html
|
||||
[littlefs-python]: https://pypi.org/project/littlefs-python/
|
||||
[littlefs2-rust]: https://crates.io/crates/littlefs2
|
||||
[chamelon]: https://github.com/yomimono/chamelon
|
||||
|
||||
101
SPEC.md
101
SPEC.md
@@ -1,10 +1,10 @@
|
||||
## littlefs technical specification
|
||||
|
||||
This is the technical specification of the little filesystem with on-disk
|
||||
version lfs2.1. 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).
|
||||
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).
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
@@ -133,6 +133,12 @@ tags XORed together, starting with `0xffffffff`.
|
||||
'-------------------' '-------------------'
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
@@ -185,53 +191,6 @@ Here's a more complete example of metadata block containing 4 entries:
|
||||
'---- most recent D
|
||||
```
|
||||
|
||||
Two things to note before we get into the details around tag encoding:
|
||||
|
||||
1. Each tag contains a valid bit used to indicate if the tag and containing
|
||||
commit is valid. After XORing, this bit should always be zero.
|
||||
|
||||
At the end of each commit, the valid bit of the previous tag is XORed
|
||||
with the lowest bit in the type field of the CRC tag. This allows
|
||||
the CRC tag to force the next commit to fail the valid bit test if it
|
||||
has not yet been written to.
|
||||
|
||||
2. The valid bit alone is not enough info to know if the next commit has been
|
||||
erased. We don't know the order bits will be programmed in a program block,
|
||||
so it's possible that the next commit had an attempted program that left the
|
||||
valid bit unchanged.
|
||||
|
||||
To ensure we only ever program erased bytes, each commit can contain an
|
||||
optional forward-CRC (FCRC). An FCRC contains a checksum of some amount of
|
||||
bytes in the next commit at the time it was erased.
|
||||
|
||||
```
|
||||
.-------------------. \ \
|
||||
| revision count | | |
|
||||
|-------------------| | |
|
||||
| metadata | | |
|
||||
| | +---. +-- current commit
|
||||
| | | | |
|
||||
|-------------------| | | |
|
||||
| FCRC ---|-. | |
|
||||
|-------------------| / | | |
|
||||
| CRC -----|-' /
|
||||
|-------------------| |
|
||||
| padding | | padding (does't need CRC)
|
||||
| | |
|
||||
|-------------------| \ | \
|
||||
| erased? | +-' |
|
||||
| | | | +-- next commit
|
||||
| v | / |
|
||||
| | /
|
||||
| |
|
||||
'-------------------'
|
||||
```
|
||||
|
||||
If the FCRC is missing or the checksum does not match, we must assume a
|
||||
commit was attempted but failed due to power-loss.
|
||||
|
||||
Note that end-of-block commits do not need an FCRC.
|
||||
|
||||
## Metadata tags
|
||||
|
||||
So in littlefs, 32-bit tags describe every type of metadata. And this means
|
||||
@@ -826,41 +785,3 @@ CRC fields:
|
||||
are made about the contents.
|
||||
|
||||
---
|
||||
#### `0x5ff` LFS_TYPE_FCRC
|
||||
|
||||
Added in lfs2.1, the optional FCRC tag contains a checksum of some amount of
|
||||
bytes in the next commit at the time it was erased. This allows us to ensure
|
||||
that we only ever program erased bytes, even if a previous commit failed due
|
||||
to power-loss.
|
||||
|
||||
When programming a commit, the FCRC size must be at least as large as the
|
||||
program block size. However, the program block is not saved on disk, and can
|
||||
change between mounts, so the FCRC size on disk may be different than the
|
||||
current program block size.
|
||||
|
||||
If the FCRC is missing or the checksum does not match, we must assume a
|
||||
commit was attempted but failed due to power-loss.
|
||||
|
||||
Layout of the FCRC tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][-- 32 --|-- 32 --]
|
||||
^ ^ ^ ^ ^- fcrc size ^- fcrc
|
||||
| | | '- size (8)
|
||||
| | '------ id (0x3ff)
|
||||
| '------------ type (0x5ff)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
FCRC fields:
|
||||
|
||||
1. **FCRC size (32-bits)** - Number of bytes after this commit's CRC tag's
|
||||
padding to include in the FCRC.
|
||||
|
||||
2. **FCRC (32-bits)** - CRC of the bytes after this commit's CRC tag's padding
|
||||
when erased. Like the CRC tag, this uses a CRC-32 with a polynomial of
|
||||
`0x04c11db7` initialized with `0xffffffff`.
|
||||
|
||||
---
|
||||
|
||||
105
bd/lfs_emubd.c
105
bd/lfs_emubd.c
@@ -48,7 +48,6 @@ static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
|
||||
static lfs_emubd_block_t *lfs_emubd_mutblock(
|
||||
const struct lfs_config *cfg,
|
||||
lfs_emubd_block_t **block) {
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
lfs_emubd_block_t *block_ = *block;
|
||||
if (block_ && block_->rc == 1) {
|
||||
// rc == 1? can modify
|
||||
@@ -57,13 +56,13 @@ static lfs_emubd_block_t *lfs_emubd_mutblock(
|
||||
} else if (block_) {
|
||||
// rc > 1? need to create a copy
|
||||
lfs_emubd_block_t *nblock = malloc(
|
||||
sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
|
||||
sizeof(lfs_emubd_block_t) + cfg->block_size);
|
||||
if (!nblock) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(nblock, block_,
|
||||
sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
|
||||
sizeof(lfs_emubd_block_t) + cfg->block_size);
|
||||
nblock->rc = 1;
|
||||
|
||||
lfs_emubd_decblock(block_);
|
||||
@@ -73,7 +72,7 @@ static lfs_emubd_block_t *lfs_emubd_mutblock(
|
||||
} else {
|
||||
// no block? need to allocate
|
||||
lfs_emubd_block_t *nblock = malloc(
|
||||
sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
|
||||
sizeof(lfs_emubd_block_t) + cfg->block_size);
|
||||
if (!nblock) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -82,9 +81,10 @@ static lfs_emubd_block_t *lfs_emubd_mutblock(
|
||||
nblock->wear = 0;
|
||||
|
||||
// zero for consistency
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
memset(nblock->data,
|
||||
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
|
||||
bd->cfg->erase_size);
|
||||
cfg->block_size);
|
||||
|
||||
*block = nblock;
|
||||
return nblock;
|
||||
@@ -94,22 +94,22 @@ static lfs_emubd_block_t *lfs_emubd_mutblock(
|
||||
|
||||
// emubd create/destroy
|
||||
|
||||
int lfs_emubd_create(const struct lfs_config *cfg,
|
||||
int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_emubd_config *bdcfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
|
||||
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
|
||||
".erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
|
||||
"\"%s\", "
|
||||
"%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
|
||||
".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
|
||||
".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, "
|
||||
".powerloss_data=%p, .track_branches=%d})",
|
||||
(void*)cfg, cfg->context,
|
||||
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
|
||||
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
|
||||
(void*)bdcfg,
|
||||
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
|
||||
bdcfg->erase_count, bdcfg->erase_value, bdcfg->erase_cycles,
|
||||
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
|
||||
path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles,
|
||||
bdcfg->badblock_behavior, bdcfg->power_cycles,
|
||||
bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
|
||||
bdcfg->powerloss_data, bdcfg->track_branches);
|
||||
@@ -117,12 +117,12 @@ int lfs_emubd_create(const struct lfs_config *cfg,
|
||||
bd->cfg = bdcfg;
|
||||
|
||||
// allocate our block array, all blocks start as uninitialized
|
||||
bd->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
|
||||
bd->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
|
||||
if (!bd->blocks) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
memset(bd->blocks, 0, bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
|
||||
memset(bd->blocks, 0, cfg->block_count * sizeof(lfs_emubd_block_t*));
|
||||
|
||||
// setup testing things
|
||||
bd->readed = 0;
|
||||
@@ -134,7 +134,7 @@ int lfs_emubd_create(const struct lfs_config *cfg,
|
||||
if (bd->cfg->disk_path) {
|
||||
bd->disk = malloc(sizeof(lfs_emubd_disk_t));
|
||||
if (!bd->disk) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
bd->disk->rc = 1;
|
||||
@@ -156,21 +156,21 @@ int lfs_emubd_create(const struct lfs_config *cfg,
|
||||
// if we're emulating erase values, we can keep a block around in
|
||||
// memory of just the erase state to speed up emulated erases
|
||||
if (bd->cfg->erase_value != -1) {
|
||||
bd->disk->scratch = malloc(bd->cfg->erase_size);
|
||||
bd->disk->scratch = malloc(cfg->block_size);
|
||||
if (!bd->disk->scratch) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
memset(bd->disk->scratch,
|
||||
bd->cfg->erase_value,
|
||||
bd->cfg->erase_size);
|
||||
cfg->block_size);
|
||||
|
||||
// go ahead and erase all of the disk, otherwise the file will not
|
||||
// match our internal representation
|
||||
for (size_t i = 0; i < bd->cfg->erase_count; i++) {
|
||||
for (size_t i = 0; i < cfg->block_count; i++) {
|
||||
ssize_t res = write(bd->disk->fd,
|
||||
bd->disk->scratch,
|
||||
bd->cfg->erase_size);
|
||||
cfg->block_size);
|
||||
if (res < 0) {
|
||||
int err = -errno;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
|
||||
@@ -180,16 +180,33 @@ int lfs_emubd_create(const struct lfs_config *cfg,
|
||||
}
|
||||
}
|
||||
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
|
||||
LFS_EMUBD_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);
|
||||
static const struct lfs_emubd_config defaults = {.erase_value=-1};
|
||||
int err = lfs_emubd_createcfg(cfg, path, &defaults);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int lfs_emubd_destroy(const struct lfs_config *cfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// decrement reference counts
|
||||
for (lfs_block_t i = 0; i < bd->cfg->erase_count; i++) {
|
||||
for (lfs_block_t i = 0; i < cfg->block_count; i++) {
|
||||
lfs_emubd_decblock(bd->blocks[i]);
|
||||
}
|
||||
free(bd->blocks);
|
||||
@@ -220,10 +237,10 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if read is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(off % bd->cfg->read_size == 0);
|
||||
LFS_ASSERT(size % bd->cfg->read_size == 0);
|
||||
LFS_ASSERT(off+size <= bd->cfg->erase_size);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
LFS_ASSERT(off % cfg->read_size == 0);
|
||||
LFS_ASSERT(size % cfg->read_size == 0);
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// get the block
|
||||
const lfs_emubd_block_t *b = bd->blocks[block];
|
||||
@@ -270,10 +287,10 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if write is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(off % bd->cfg->prog_size == 0);
|
||||
LFS_ASSERT(size % bd->cfg->prog_size == 0);
|
||||
LFS_ASSERT(off+size <= bd->cfg->erase_size);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
LFS_ASSERT(off % cfg->prog_size == 0);
|
||||
LFS_ASSERT(size % cfg->prog_size == 0);
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// get the block
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
@@ -310,7 +327,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
// mirror to disk file?
|
||||
if (bd->disk) {
|
||||
off_t res1 = lseek(bd->disk->fd,
|
||||
(off_t)block*bd->cfg->erase_size + (off_t)off,
|
||||
(off_t)block*cfg->block_size + (off_t)off,
|
||||
SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
int err = -errno;
|
||||
@@ -355,11 +372,11 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
|
||||
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
|
||||
(void*)cfg, block, ((lfs_emubd_t*)cfg->context)->cfg->erase_size);
|
||||
(void*)cfg, block, cfg->block_size);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if erase is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// get the block
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
@@ -388,12 +405,12 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
|
||||
// emulate an erase value?
|
||||
if (bd->cfg->erase_value != -1) {
|
||||
memset(b->data, bd->cfg->erase_value, bd->cfg->erase_size);
|
||||
memset(b->data, bd->cfg->erase_value, cfg->block_size);
|
||||
|
||||
// mirror to disk file?
|
||||
if (bd->disk) {
|
||||
off_t res1 = lseek(bd->disk->fd,
|
||||
(off_t)block*bd->cfg->erase_size,
|
||||
(off_t)block*cfg->block_size,
|
||||
SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
int err = -errno;
|
||||
@@ -403,7 +420,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
|
||||
ssize_t res2 = write(bd->disk->fd,
|
||||
bd->disk->scratch,
|
||||
bd->cfg->erase_size);
|
||||
cfg->block_size);
|
||||
if (res2 < 0) {
|
||||
int err = -errno;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
|
||||
@@ -413,7 +430,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
}
|
||||
|
||||
// track erases
|
||||
bd->erased += bd->cfg->erase_size;
|
||||
bd->erased += cfg->block_size;
|
||||
if (bd->cfg->erase_sleep) {
|
||||
int err = nanosleep(&(struct timespec){
|
||||
.tv_sec=bd->cfg->erase_sleep/1000000000,
|
||||
@@ -556,7 +573,7 @@ lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if block is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// get the wear
|
||||
lfs_emubd_wear_t wear;
|
||||
@@ -578,7 +595,7 @@ int lfs_emubd_setwear(const struct lfs_config *cfg,
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if block is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// set the wear
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
@@ -618,13 +635,13 @@ int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// lazily copy over our block array
|
||||
copy->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
|
||||
copy->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
|
||||
if (!copy->blocks) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bd->cfg->erase_count; i++) {
|
||||
for (size_t i = 0; i < cfg->block_count; i++) {
|
||||
copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,18 +67,6 @@ typedef int64_t lfs_emubd_ssleep_t;
|
||||
|
||||
// emubd config, this is required for testing
|
||||
struct lfs_emubd_config {
|
||||
// Minimum size of a read operation in bytes.
|
||||
lfs_size_t read_size;
|
||||
|
||||
// Minimum size of a program operation in bytes.
|
||||
lfs_size_t prog_size;
|
||||
|
||||
// Size of an erase operation in bytes.
|
||||
lfs_size_t erase_size;
|
||||
|
||||
// Number of erase blocks on the device.
|
||||
lfs_size_t erase_count;
|
||||
|
||||
// 8-bit erase value to use for simulating erases. -1 does not simulate
|
||||
// erases, which can speed up testing by avoiding the extra block-device
|
||||
// operations to store the erase value.
|
||||
@@ -161,7 +149,11 @@ typedef struct lfs_emubd {
|
||||
/// Block device API ///
|
||||
|
||||
// Create an emulating block device using the geometry in lfs_config
|
||||
int lfs_emubd_create(const struct lfs_config *cfg,
|
||||
//
|
||||
// Note that filebd is used if a path is provided, if path is NULL
|
||||
// emubd will use rambd which can be much faster.
|
||||
int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
|
||||
int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_emubd_config *bdcfg);
|
||||
|
||||
// Clean up memory associated with block device
|
||||
|
||||
@@ -15,22 +15,18 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
int lfs_filebd_create(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_filebd_config *bdcfg) {
|
||||
int lfs_filebd_create(const struct lfs_config *cfg, const char *path) {
|
||||
LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
|
||||
"\"%s\", "
|
||||
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".erase_size=%"PRIu32", .erase_count=%"PRIu32"})",
|
||||
".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,
|
||||
path,
|
||||
(void*)bdcfg,
|
||||
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
|
||||
bdcfg->erase_count);
|
||||
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
|
||||
path, (void*)bdcfg, bdcfg->erase_value);
|
||||
lfs_filebd_t *bd = cfg->context;
|
||||
bd->cfg = bdcfg;
|
||||
|
||||
// open file
|
||||
#ifdef _WIN32
|
||||
@@ -70,17 +66,17 @@ int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_filebd_t *bd = cfg->context;
|
||||
|
||||
// check if read is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(off % bd->cfg->read_size == 0);
|
||||
LFS_ASSERT(size % bd->cfg->read_size == 0);
|
||||
LFS_ASSERT(off+size <= bd->cfg->erase_size);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
LFS_ASSERT(off % cfg->read_size == 0);
|
||||
LFS_ASSERT(size % cfg->read_size == 0);
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// zero for reproducibility (in case file is truncated)
|
||||
memset(buffer, 0, size);
|
||||
|
||||
// read
|
||||
off_t res1 = lseek(bd->fd,
|
||||
(off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
|
||||
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
int err = -errno;
|
||||
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
|
||||
@@ -106,14 +102,14 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_filebd_t *bd = cfg->context;
|
||||
|
||||
// check if write is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(off % bd->cfg->prog_size == 0);
|
||||
LFS_ASSERT(size % bd->cfg->prog_size == 0);
|
||||
LFS_ASSERT(off+size <= bd->cfg->erase_size);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
LFS_ASSERT(off % cfg->prog_size == 0);
|
||||
LFS_ASSERT(size % cfg->prog_size == 0);
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// program data
|
||||
off_t res1 = lseek(bd->fd,
|
||||
(off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
|
||||
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
int err = -errno;
|
||||
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
|
||||
@@ -133,11 +129,10 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
|
||||
int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
|
||||
(void*)cfg, block, ((lfs_file_t*)cfg->context)->cfg->erase_size);
|
||||
lfs_filebd_t *bd = cfg->context;
|
||||
(void*)cfg, block, cfg->block_size);
|
||||
|
||||
// check if erase is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// erase is a noop
|
||||
(void)block;
|
||||
|
||||
@@ -26,31 +26,14 @@ extern "C"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// filebd config
|
||||
struct lfs_filebd_config {
|
||||
// Minimum size of a read operation in bytes.
|
||||
lfs_size_t read_size;
|
||||
|
||||
// Minimum size of a program operation in bytes.
|
||||
lfs_size_t prog_size;
|
||||
|
||||
// Size of an erase operation in bytes.
|
||||
lfs_size_t erase_size;
|
||||
|
||||
// Number of erase blocks on the device.
|
||||
lfs_size_t erase_count;
|
||||
};
|
||||
|
||||
// filebd state
|
||||
typedef struct lfs_filebd {
|
||||
int fd;
|
||||
const struct lfs_filebd_config *cfg;
|
||||
} lfs_filebd_t;
|
||||
|
||||
|
||||
// Create a file block device
|
||||
int lfs_filebd_create(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_filebd_config *bdcfg);
|
||||
// Create a file block device using the geometry in lfs_config
|
||||
int lfs_filebd_create(const struct lfs_config *cfg, const char *path);
|
||||
|
||||
// Clean up memory associated with block device
|
||||
int lfs_filebd_destroy(const struct lfs_config *cfg);
|
||||
|
||||
@@ -7,19 +7,18 @@
|
||||
*/
|
||||
#include "bd/lfs_rambd.h"
|
||||
|
||||
int lfs_rambd_create(const struct lfs_config *cfg,
|
||||
int lfs_rambd_createcfg(const struct lfs_config *cfg,
|
||||
const struct lfs_rambd_config *bdcfg) {
|
||||
LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
|
||||
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
|
||||
".buffer=%p})",
|
||||
LFS_RAMBD_TRACE("lfs_rambd_createcfg(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
|
||||
"%p {.buffer=%p})",
|
||||
(void*)cfg, cfg->context,
|
||||
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
|
||||
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
|
||||
(void*)bdcfg,
|
||||
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
|
||||
bdcfg->erase_count, bdcfg->buffer);
|
||||
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
|
||||
(void*)bdcfg, bdcfg->buffer);
|
||||
lfs_rambd_t *bd = cfg->context;
|
||||
bd->cfg = bdcfg;
|
||||
|
||||
@@ -27,20 +26,35 @@ int lfs_rambd_create(const struct lfs_config *cfg,
|
||||
if (bd->cfg->buffer) {
|
||||
bd->buffer = bd->cfg->buffer;
|
||||
} else {
|
||||
bd->buffer = lfs_malloc(bd->cfg->erase_size * bd->cfg->erase_count);
|
||||
bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count);
|
||||
if (!bd->buffer) {
|
||||
LFS_RAMBD_TRACE("lfs_rambd_create -> %d", LFS_ERR_NOMEM);
|
||||
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
// zero for reproducibility
|
||||
memset(bd->buffer, 0, bd->cfg->erase_size * bd->cfg->erase_count);
|
||||
memset(bd->buffer, 0, cfg->block_size * cfg->block_count);
|
||||
|
||||
LFS_RAMBD_TRACE("lfs_rambd_create -> %d", 0);
|
||||
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_rambd_create(const struct lfs_config *cfg) {
|
||||
LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32"})",
|
||||
(void*)cfg, cfg->context,
|
||||
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
|
||||
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
|
||||
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count);
|
||||
static const struct lfs_rambd_config defaults = {0};
|
||||
int err = lfs_rambd_createcfg(cfg, &defaults);
|
||||
LFS_RAMBD_TRACE("lfs_rambd_create -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int lfs_rambd_destroy(const struct lfs_config *cfg) {
|
||||
LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)cfg);
|
||||
// clean up memory
|
||||
@@ -60,13 +74,13 @@ int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_rambd_t *bd = cfg->context;
|
||||
|
||||
// check if read is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(off % bd->cfg->read_size == 0);
|
||||
LFS_ASSERT(size % bd->cfg->read_size == 0);
|
||||
LFS_ASSERT(off+size <= bd->cfg->erase_size);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
LFS_ASSERT(off % cfg->read_size == 0);
|
||||
LFS_ASSERT(size % cfg->read_size == 0);
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// read data
|
||||
memcpy(buffer, &bd->buffer[block*bd->cfg->erase_size + off], size);
|
||||
memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size);
|
||||
|
||||
LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0);
|
||||
return 0;
|
||||
@@ -80,13 +94,13 @@ int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_rambd_t *bd = cfg->context;
|
||||
|
||||
// check if write is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(off % bd->cfg->prog_size == 0);
|
||||
LFS_ASSERT(size % bd->cfg->prog_size == 0);
|
||||
LFS_ASSERT(off+size <= bd->cfg->erase_size);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
LFS_ASSERT(off % cfg->prog_size == 0);
|
||||
LFS_ASSERT(size % cfg->prog_size == 0);
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// program data
|
||||
memcpy(&bd->buffer[block*bd->cfg->erase_size + off], buffer, size);
|
||||
memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size);
|
||||
|
||||
LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0);
|
||||
return 0;
|
||||
@@ -94,11 +108,10 @@ int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
|
||||
int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
|
||||
(void*)cfg, block, ((lfs_rambd_t*)cfg->context)->cfg->erase_size);
|
||||
lfs_rambd_t *bd = cfg->context;
|
||||
(void*)cfg, block, cfg->block_size);
|
||||
|
||||
// check if erase is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// erase is a noop
|
||||
(void)block;
|
||||
|
||||
@@ -26,20 +26,8 @@ extern "C"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// rambd config
|
||||
// rambd config (optional)
|
||||
struct lfs_rambd_config {
|
||||
// Minimum size of a read operation in bytes.
|
||||
lfs_size_t read_size;
|
||||
|
||||
// Minimum size of a program operation in bytes.
|
||||
lfs_size_t prog_size;
|
||||
|
||||
// Size of an erase operation in bytes.
|
||||
lfs_size_t erase_size;
|
||||
|
||||
// Number of erase blocks on the device.
|
||||
lfs_size_t erase_count;
|
||||
|
||||
// Optional statically allocated buffer for the block device.
|
||||
void *buffer;
|
||||
};
|
||||
@@ -51,8 +39,9 @@ typedef struct lfs_rambd {
|
||||
} lfs_rambd_t;
|
||||
|
||||
|
||||
// Create a RAM block device
|
||||
int lfs_rambd_create(const struct lfs_config *cfg,
|
||||
// Create a RAM block device using the geometry in lfs_config
|
||||
int lfs_rambd_create(const struct lfs_config *cfg);
|
||||
int lfs_rambd_createcfg(const struct lfs_config *cfg,
|
||||
const struct lfs_rambd_config *bdcfg);
|
||||
|
||||
// Clean up memory associated with block device
|
||||
|
||||
569
lfs.c
569
lfs.c
@@ -46,8 +46,8 @@ static int lfs_bd_read(lfs_t *lfs,
|
||||
lfs_block_t block, lfs_off_t off,
|
||||
void *buffer, lfs_size_t size) {
|
||||
uint8_t *data = buffer;
|
||||
if (off+size > lfs->cfg->block_size
|
||||
|| (lfs->block_count && block >= lfs->block_count)) {
|
||||
if (block >= lfs->cfg->block_count ||
|
||||
off+size > lfs->cfg->block_size) {
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ static int lfs_bd_read(lfs_t *lfs,
|
||||
}
|
||||
|
||||
// load to cache, first condition can no longer fail
|
||||
LFS_ASSERT(!lfs->block_count || block < lfs->block_count);
|
||||
LFS_ASSERT(block < lfs->cfg->block_count);
|
||||
rcache->block = block;
|
||||
rcache->off = lfs_aligndown(off, lfs->cfg->read_size);
|
||||
rcache->size = lfs_min(
|
||||
@@ -176,7 +176,7 @@ static int lfs_bd_crc(lfs_t *lfs,
|
||||
static int lfs_bd_flush(lfs_t *lfs,
|
||||
lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) {
|
||||
if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) {
|
||||
LFS_ASSERT(pcache->block < lfs->block_count);
|
||||
LFS_ASSERT(pcache->block < lfs->cfg->block_count);
|
||||
lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size);
|
||||
int err = lfs->cfg->prog(lfs->cfg, pcache->block,
|
||||
pcache->off, pcache->buffer, diff);
|
||||
@@ -229,7 +229,7 @@ static int lfs_bd_prog(lfs_t *lfs,
|
||||
lfs_block_t block, lfs_off_t off,
|
||||
const void *buffer, lfs_size_t size) {
|
||||
const uint8_t *data = buffer;
|
||||
LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->block_count);
|
||||
LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->cfg->block_count);
|
||||
LFS_ASSERT(off + size <= lfs->cfg->block_size);
|
||||
|
||||
while (size > 0) {
|
||||
@@ -273,7 +273,7 @@ static int lfs_bd_prog(lfs_t *lfs,
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
|
||||
LFS_ASSERT(block < lfs->block_count);
|
||||
LFS_ASSERT(block < lfs->cfg->block_count);
|
||||
int err = lfs->cfg->erase(lfs->cfg, block);
|
||||
LFS_ASSERT(err <= 0);
|
||||
return err;
|
||||
@@ -409,7 +409,7 @@ static inline bool lfs_gstate_hasorphans(const lfs_gstate_t *a) {
|
||||
}
|
||||
|
||||
static inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) {
|
||||
return lfs_tag_size(a->tag) & 0x1ff;
|
||||
return lfs_tag_size(a->tag);
|
||||
}
|
||||
|
||||
static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) {
|
||||
@@ -417,10 +417,6 @@ static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool lfs_gstate_needssuperblock(const lfs_gstate_t *a) {
|
||||
return lfs_tag_size(a->tag) >> 9;
|
||||
}
|
||||
|
||||
static inline bool lfs_gstate_hasmovehere(const lfs_gstate_t *a,
|
||||
const lfs_block_t *pair) {
|
||||
return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0;
|
||||
@@ -518,28 +514,6 @@ static void lfs_mlist_append(lfs_t *lfs, struct lfs_mlist *mlist) {
|
||||
lfs->mlist = mlist;
|
||||
}
|
||||
|
||||
// some other filesystem operations
|
||||
static uint32_t lfs_fs_disk_version(lfs_t *lfs) {
|
||||
(void)lfs;
|
||||
#ifdef LFS_MULTIVERSION
|
||||
if (lfs->cfg->disk_version) {
|
||||
return lfs->cfg->disk_version;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
return LFS_DISK_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t lfs_fs_disk_version_major(lfs_t *lfs) {
|
||||
return 0xffff & (lfs_fs_disk_version(lfs) >> 16);
|
||||
|
||||
}
|
||||
|
||||
static uint16_t lfs_fs_disk_version_minor(lfs_t *lfs) {
|
||||
return 0xffff & (lfs_fs_disk_version(lfs) >> 0);
|
||||
}
|
||||
|
||||
|
||||
/// Internal operations predeclared here ///
|
||||
#ifndef LFS_READONLY
|
||||
@@ -567,8 +541,6 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2],
|
||||
static int lfs_fs_forceconsistency(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock);
|
||||
|
||||
#ifdef LFS_MIGRATE
|
||||
static int lfs1_traverse(lfs_t *lfs,
|
||||
int (*cb)(void*, lfs_block_t), void *data);
|
||||
@@ -597,7 +569,7 @@ static int lfs_rawunmount(lfs_t *lfs);
|
||||
static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
|
||||
lfs_t *lfs = (lfs_t*)p;
|
||||
lfs_block_t off = ((block - lfs->free.off)
|
||||
+ lfs->block_count) % lfs->block_count;
|
||||
+ lfs->cfg->block_count) % lfs->cfg->block_count;
|
||||
|
||||
if (off < lfs->free.size) {
|
||||
lfs->free.buffer[off / 32] |= 1U << (off % 32);
|
||||
@@ -611,7 +583,7 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
|
||||
// is to prevent blocks from being garbage collected in the middle of a
|
||||
// commit operation
|
||||
static void lfs_alloc_ack(lfs_t *lfs) {
|
||||
lfs->free.ack = lfs->block_count;
|
||||
lfs->free.ack = lfs->cfg->block_count;
|
||||
}
|
||||
|
||||
// drop the lookahead buffer, this is done during mounting and failed
|
||||
@@ -622,26 +594,6 @@ static void lfs_alloc_drop(lfs_t *lfs) {
|
||||
lfs_alloc_ack(lfs);
|
||||
}
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_fs_rawgc(lfs_t *lfs) {
|
||||
// Move free offset at the first unused block (lfs->free.i)
|
||||
// lfs->free.i is equal lfs->free.size when all blocks are used
|
||||
lfs->free.off = (lfs->free.off + lfs->free.i) % lfs->block_count;
|
||||
lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack);
|
||||
lfs->free.i = 0;
|
||||
|
||||
// find mask of free blocks from tree
|
||||
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
|
||||
int err = lfs_fs_rawtraverse(lfs, lfs_alloc_lookahead, lfs, true);
|
||||
if (err) {
|
||||
lfs_alloc_drop(lfs);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
|
||||
while (true) {
|
||||
@@ -652,7 +604,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
|
||||
|
||||
if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) {
|
||||
// found a free block
|
||||
*block = (lfs->free.off + off) % lfs->block_count;
|
||||
*block = (lfs->free.off + off) % lfs->cfg->block_count;
|
||||
|
||||
// eagerly find next off so an alloc ack can
|
||||
// discredit old lookahead blocks
|
||||
@@ -674,8 +626,16 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
|
||||
return LFS_ERR_NOSPC;
|
||||
}
|
||||
|
||||
int err = lfs_fs_rawgc(lfs);
|
||||
if(err) {
|
||||
lfs->free.off = (lfs->free.off + lfs->free.size)
|
||||
% lfs->cfg->block_count;
|
||||
lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack);
|
||||
lfs->free.i = 0;
|
||||
|
||||
// find mask of free blocks from tree
|
||||
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
|
||||
int err = lfs_fs_rawtraverse(lfs, lfs_alloc_lookahead, lfs, true);
|
||||
if (err) {
|
||||
lfs_alloc_drop(lfs);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -889,7 +849,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
|
||||
// iterate over directory and attrs
|
||||
lfs_tag_t tag;
|
||||
const void *buffer;
|
||||
struct lfs_diskoff disk = {0};
|
||||
struct lfs_diskoff disk;
|
||||
while (true) {
|
||||
{
|
||||
if (off+lfs_tag_dsize(ptag) < dir->off) {
|
||||
@@ -1079,8 +1039,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
||||
|
||||
// if either block address is invalid we return LFS_ERR_CORRUPT here,
|
||||
// otherwise later writes to the pair could fail
|
||||
if (lfs->block_count
|
||||
&& (pair[0] >= lfs->block_count || pair[1] >= lfs->block_count)) {
|
||||
if (pair[0] >= lfs->cfg->block_count || pair[1] >= lfs->cfg->block_count) {
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
@@ -1146,8 +1105,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
||||
|
||||
// next commit not yet programmed?
|
||||
if (!lfs_tag_isvalid(tag)) {
|
||||
// we only might be erased if the last tag was a crc
|
||||
maybeerased = (lfs_tag_type2(ptag) == LFS_TYPE_CCRC);
|
||||
maybeerased = true;
|
||||
break;
|
||||
// out of range?
|
||||
} else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
|
||||
@@ -1192,11 +1150,14 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
||||
dir->tail[1] = temptail[1];
|
||||
dir->split = tempsplit;
|
||||
|
||||
// reset crc, hasfcrc
|
||||
// reset crc
|
||||
crc = 0xffffffff;
|
||||
continue;
|
||||
}
|
||||
|
||||
// fcrc is only valid when last tag was a crc
|
||||
hasfcrc = false;
|
||||
|
||||
// crc the entry first, hopefully leaving it in the cache
|
||||
err = lfs_bd_crc(lfs,
|
||||
NULL, &lfs->rcache, lfs->cfg->block_size,
|
||||
@@ -1290,33 +1251,20 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
||||
|
||||
// did we end on a valid commit? we may have an erased block
|
||||
dir->erased = false;
|
||||
if (maybeerased && dir->off % lfs->cfg->prog_size == 0) {
|
||||
#ifdef LFS_MULTIVERSION
|
||||
// note versions < lfs2.1 did not have fcrc tags, if
|
||||
// we're < lfs2.1 treat missing fcrc as erased data
|
||||
//
|
||||
// we don't strictly need to do this, but otherwise writing
|
||||
// to lfs2.0 disks becomes very inefficient
|
||||
if (lfs_fs_disk_version(lfs) < 0x00020001) {
|
||||
dir->erased = true;
|
||||
|
||||
} else
|
||||
#endif
|
||||
if (hasfcrc) {
|
||||
// check for an fcrc matching the next prog's erased state, if
|
||||
// this failed most likely a previous prog was interrupted, we
|
||||
// need a new erase
|
||||
uint32_t fcrc_ = 0xffffffff;
|
||||
int err = lfs_bd_crc(lfs,
|
||||
NULL, &lfs->rcache, lfs->cfg->block_size,
|
||||
dir->pair[0], dir->off, fcrc.size, &fcrc_);
|
||||
if (err && err != LFS_ERR_CORRUPT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// found beginning of erased part?
|
||||
dir->erased = (fcrc_ == fcrc.crc);
|
||||
if (maybeerased && hasfcrc && dir->off % lfs->cfg->prog_size == 0) {
|
||||
// check for an fcrc matching the next prog's erased state, if
|
||||
// this failed most likely a previous prog was interrupted, we
|
||||
// need a new erase
|
||||
uint32_t fcrc_ = 0xffffffff;
|
||||
int err = lfs_bd_crc(lfs,
|
||||
NULL, &lfs->rcache, lfs->cfg->block_size,
|
||||
dir->pair[0], dir->off, fcrc.size, &fcrc_);
|
||||
if (err && err != LFS_ERR_CORRUPT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// found beginning of erased part?
|
||||
dir->erased = (fcrc_ == fcrc.crc);
|
||||
}
|
||||
|
||||
// synthetic move
|
||||
@@ -1641,7 +1589,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
|
||||
}
|
||||
|
||||
// space for fcrc?
|
||||
uint8_t eperturb = (uint8_t)-1;
|
||||
uint8_t eperturb = -1;
|
||||
if (noff >= end && noff <= lfs->cfg->block_size - lfs->cfg->prog_size) {
|
||||
// first read the leading byte, this always contains a bit
|
||||
// we can perturb to avoid writes that don't change the fcrc
|
||||
@@ -1652,34 +1600,22 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef LFS_MULTIVERSION
|
||||
// unfortunately fcrcs break mdir fetching < lfs2.1, so only write
|
||||
// these if we're a >= lfs2.1 filesystem
|
||||
if (lfs_fs_disk_version(lfs) <= 0x00020000) {
|
||||
// don't write fcrc
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// find the expected fcrc, don't bother avoiding a reread
|
||||
// of the eperturb, it should still be in our cache
|
||||
struct lfs_fcrc fcrc = {
|
||||
.size = lfs->cfg->prog_size,
|
||||
.crc = 0xffffffff
|
||||
};
|
||||
err = lfs_bd_crc(lfs,
|
||||
NULL, &lfs->rcache, lfs->cfg->prog_size,
|
||||
commit->block, noff, fcrc.size, &fcrc.crc);
|
||||
if (err && err != LFS_ERR_CORRUPT) {
|
||||
return err;
|
||||
}
|
||||
// find the expected fcrc, don't bother avoiding a reread
|
||||
// of the eperturb, it should still be in our cache
|
||||
struct lfs_fcrc fcrc = {.size=lfs->cfg->prog_size, .crc=0xffffffff};
|
||||
err = lfs_bd_crc(lfs,
|
||||
NULL, &lfs->rcache, lfs->cfg->prog_size,
|
||||
commit->block, noff, fcrc.size, &fcrc.crc);
|
||||
if (err && err != LFS_ERR_CORRUPT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_fcrc_tole32(&fcrc);
|
||||
err = lfs_dir_commitattr(lfs, commit,
|
||||
LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)),
|
||||
&fcrc);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
lfs_fcrc_tole32(&fcrc);
|
||||
err = lfs_dir_commitattr(lfs, commit,
|
||||
LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)),
|
||||
&fcrc);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1710,7 +1646,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
|
||||
|
||||
commit->off = noff;
|
||||
// perturb valid bit?
|
||||
commit->ptag = ntag ^ ((0x80UL & ~eperturb) << 24);
|
||||
commit->ptag = ntag ^ ((0x80 & ~eperturb) << 24);
|
||||
// reset crc for next commit
|
||||
commit->crc = 0xffffffff;
|
||||
|
||||
@@ -2153,7 +2089,7 @@ static int lfs_dir_splittingcompact(lfs_t *lfs, lfs_mdir_t *dir,
|
||||
|
||||
// do we have extra space? littlefs can't reclaim this space
|
||||
// by itself, so expand cautiously
|
||||
if ((lfs_size_t)size < lfs->block_count/2) {
|
||||
if ((lfs_size_t)size < lfs->cfg->block_count/2) {
|
||||
LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev);
|
||||
int err = lfs_dir_split(lfs, dir, attrs, attrcount,
|
||||
source, begin, end);
|
||||
@@ -2771,6 +2707,11 @@ static int lfs_dir_rawseek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
|
||||
dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0);
|
||||
|
||||
while (off > 0) {
|
||||
int diff = lfs_min(dir->m.count - dir->id, off);
|
||||
dir->id += diff;
|
||||
dir->pos += diff;
|
||||
off -= diff;
|
||||
|
||||
if (dir->id == dir->m.count) {
|
||||
if (!dir->m.split) {
|
||||
return LFS_ERR_INVAL;
|
||||
@@ -2783,11 +2724,6 @@ static int lfs_dir_rawseek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
|
||||
|
||||
dir->id = 0;
|
||||
}
|
||||
|
||||
int diff = lfs_min(dir->m.count - dir->id, off);
|
||||
dir->id += diff;
|
||||
dir->pos += diff;
|
||||
off -= diff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -3524,7 +3460,7 @@ static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file,
|
||||
// find out which block we're extending from
|
||||
int err = lfs_ctz_find(lfs, NULL, &file->cache,
|
||||
file->ctz.head, file->ctz.size,
|
||||
file->pos-1, &file->block, &(lfs_off_t){0});
|
||||
file->pos-1, &file->block, &file->off);
|
||||
if (err) {
|
||||
file->flags |= LFS_F_ERRED;
|
||||
return err;
|
||||
@@ -3702,55 +3638,26 @@ static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
|
||||
lfs_off_t pos = file->pos;
|
||||
lfs_off_t oldsize = lfs_file_rawsize(lfs, file);
|
||||
if (size < oldsize) {
|
||||
// revert to inline file?
|
||||
if (size <= lfs_min(0x3fe, lfs_min(
|
||||
lfs->cfg->cache_size,
|
||||
(lfs->cfg->metadata_max ?
|
||||
lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) {
|
||||
// flush+seek to head
|
||||
lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET);
|
||||
if (res < 0) {
|
||||
return (int)res;
|
||||
}
|
||||
|
||||
// read our data into rcache temporarily
|
||||
lfs_cache_drop(lfs, &lfs->rcache);
|
||||
res = lfs_file_flushedread(lfs, file,
|
||||
lfs->rcache.buffer, size);
|
||||
if (res < 0) {
|
||||
return (int)res;
|
||||
}
|
||||
|
||||
file->ctz.head = LFS_BLOCK_INLINE;
|
||||
file->ctz.size = size;
|
||||
file->flags |= LFS_F_DIRTY | LFS_F_READING | LFS_F_INLINE;
|
||||
file->cache.block = file->ctz.head;
|
||||
file->cache.off = 0;
|
||||
file->cache.size = lfs->cfg->cache_size;
|
||||
memcpy(file->cache.buffer, lfs->rcache.buffer, size);
|
||||
|
||||
} else {
|
||||
// need to flush since directly changing metadata
|
||||
int err = lfs_file_flush(lfs, file);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// lookup new head in ctz skip list
|
||||
err = lfs_ctz_find(lfs, NULL, &file->cache,
|
||||
file->ctz.head, file->ctz.size,
|
||||
size-1, &file->block, &(lfs_off_t){0});
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// need to set pos/block/off consistently so seeking back to
|
||||
// the old position does not get confused
|
||||
file->pos = size;
|
||||
file->ctz.head = file->block;
|
||||
file->ctz.size = size;
|
||||
file->flags |= LFS_F_DIRTY | LFS_F_READING;
|
||||
// need to flush since directly changing metadata
|
||||
int err = lfs_file_flush(lfs, file);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// lookup new head in ctz skip list
|
||||
err = lfs_ctz_find(lfs, NULL, &file->cache,
|
||||
file->ctz.head, file->ctz.size,
|
||||
size, &file->block, &file->off);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// need to set pos/block/off consistently so seeking back to
|
||||
// the old position does not get confused
|
||||
file->pos = size;
|
||||
file->ctz.head = file->block;
|
||||
file->ctz.size = size;
|
||||
file->flags |= LFS_F_DIRTY | LFS_F_READING;
|
||||
} else if (size > oldsize) {
|
||||
// flush+seek if not already at end
|
||||
lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_END);
|
||||
@@ -4108,24 +4015,8 @@ static int lfs_rawremoveattr(lfs_t *lfs, const char *path, uint8_t type) {
|
||||
/// Filesystem operations ///
|
||||
static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
lfs->cfg = cfg;
|
||||
lfs->block_count = cfg->block_count; // May be 0
|
||||
int err = 0;
|
||||
|
||||
#ifdef LFS_MULTIVERSION
|
||||
// this driver only supports minor version < current minor version
|
||||
LFS_ASSERT(!lfs->cfg->disk_version || (
|
||||
(0xffff & (lfs->cfg->disk_version >> 16))
|
||||
== LFS_DISK_VERSION_MAJOR
|
||||
&& (0xffff & (lfs->cfg->disk_version >> 0))
|
||||
<= LFS_DISK_VERSION_MINOR));
|
||||
#endif
|
||||
|
||||
// check that bool is a truthy-preserving type
|
||||
//
|
||||
// note the most common reason for this failure is a before-c99 compiler,
|
||||
// which littlefs currently does not support
|
||||
LFS_ASSERT((bool)0x80000000);
|
||||
|
||||
// validate that the lfs-cfg sizes were initiated properly before
|
||||
// performing any arithmetic logics with them
|
||||
LFS_ASSERT(lfs->cfg->read_size != 0);
|
||||
@@ -4138,10 +4029,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->prog_size == 0);
|
||||
LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0);
|
||||
|
||||
// check that the block size is large enough to fit all ctz pointers
|
||||
LFS_ASSERT(lfs->cfg->block_size >= 128);
|
||||
// this is the exact calculation for all ctz pointers, if this fails
|
||||
// and the simpler assert above does not, math must be broken
|
||||
// check that the block size is large enough to fit ctz pointers
|
||||
LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
|
||||
<= lfs->cfg->block_size);
|
||||
|
||||
@@ -4251,8 +4139,6 @@ static int lfs_deinit(lfs_t *lfs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
int err = 0;
|
||||
@@ -4262,13 +4148,11 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LFS_ASSERT(cfg->block_count != 0);
|
||||
|
||||
// create free lookahead
|
||||
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
|
||||
lfs->free.off = 0;
|
||||
lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size,
|
||||
lfs->block_count);
|
||||
lfs->cfg->block_count);
|
||||
lfs->free.i = 0;
|
||||
lfs_alloc_ack(lfs);
|
||||
|
||||
@@ -4281,9 +4165,9 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
|
||||
// write one superblock
|
||||
lfs_superblock_t superblock = {
|
||||
.version = lfs_fs_disk_version(lfs),
|
||||
.version = LFS_DISK_VERSION,
|
||||
.block_size = lfs->cfg->block_size,
|
||||
.block_count = lfs->block_count,
|
||||
.block_count = lfs->cfg->block_count,
|
||||
.name_max = lfs->name_max,
|
||||
.file_max = lfs->file_max,
|
||||
.attr_max = lfs->attr_max,
|
||||
@@ -4379,33 +4263,14 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
// check version
|
||||
uint16_t major_version = (0xffff & (superblock.version >> 16));
|
||||
uint16_t minor_version = (0xffff & (superblock.version >> 0));
|
||||
if (major_version != lfs_fs_disk_version_major(lfs)
|
||||
|| minor_version > lfs_fs_disk_version_minor(lfs)) {
|
||||
LFS_ERROR("Invalid version "
|
||||
"v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16,
|
||||
major_version,
|
||||
minor_version,
|
||||
lfs_fs_disk_version_major(lfs),
|
||||
lfs_fs_disk_version_minor(lfs));
|
||||
if ((major_version != LFS_DISK_VERSION_MAJOR ||
|
||||
minor_version > LFS_DISK_VERSION_MINOR)) {
|
||||
LFS_ERROR("Invalid version v%"PRIu16".%"PRIu16,
|
||||
major_version, minor_version);
|
||||
err = LFS_ERR_INVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// found older minor version? set an in-device only bit in the
|
||||
// gstate so we know we need to rewrite the superblock before
|
||||
// the first write
|
||||
if (minor_version < lfs_fs_disk_version_minor(lfs)) {
|
||||
LFS_DEBUG("Found older minor version "
|
||||
"v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
|
||||
major_version,
|
||||
minor_version,
|
||||
lfs_fs_disk_version_major(lfs),
|
||||
lfs_fs_disk_version_minor(lfs));
|
||||
// note this bit is reserved on disk, so fetching more gstate
|
||||
// will not interfere here
|
||||
lfs_fs_prepsuperblock(lfs, true);
|
||||
}
|
||||
|
||||
// check superblock configuration
|
||||
if (superblock.name_max) {
|
||||
if (superblock.name_max > lfs->name_max) {
|
||||
@@ -4440,17 +4305,13 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
lfs->attr_max = superblock.attr_max;
|
||||
}
|
||||
|
||||
// this is where we get the block_count from disk if block_count=0
|
||||
if (lfs->cfg->block_count
|
||||
&& superblock.block_count != lfs->cfg->block_count) {
|
||||
if (superblock.block_count != lfs->cfg->block_count) {
|
||||
LFS_ERROR("Invalid block count (%"PRIu32" != %"PRIu32")",
|
||||
superblock.block_count, lfs->cfg->block_count);
|
||||
err = LFS_ERR_INVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
lfs->block_count = superblock.block_count;
|
||||
|
||||
if (superblock.block_size != lfs->cfg->block_size) {
|
||||
LFS_ERROR("Invalid block size (%"PRIu32" != %"PRIu32")",
|
||||
superblock.block_size, lfs->cfg->block_size);
|
||||
@@ -4466,6 +4327,12 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
}
|
||||
}
|
||||
|
||||
// found superblock?
|
||||
if (lfs_pair_isnull(lfs->root)) {
|
||||
err = LFS_ERR_INVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// update littlefs with gstate
|
||||
if (!lfs_gstate_iszero(&lfs->gstate)) {
|
||||
LFS_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32,
|
||||
@@ -4478,7 +4345,7 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
|
||||
// setup free lookahead, to distribute allocations uniformly across
|
||||
// boots, we start the allocator at a random location
|
||||
lfs->free.off = lfs->seed % lfs->block_count;
|
||||
lfs->free.off = lfs->seed % lfs->cfg->block_count;
|
||||
lfs_alloc_drop(lfs);
|
||||
|
||||
return 0;
|
||||
@@ -4494,46 +4361,6 @@ static int lfs_rawunmount(lfs_t *lfs) {
|
||||
|
||||
|
||||
/// Filesystem filesystem operations ///
|
||||
static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
|
||||
// if the superblock is up-to-date, we must be on the most recent
|
||||
// minor version of littlefs
|
||||
if (!lfs_gstate_needssuperblock(&lfs->gstate)) {
|
||||
fsinfo->disk_version = lfs_fs_disk_version(lfs);
|
||||
|
||||
// otherwise we need to read the minor version on disk
|
||||
} else {
|
||||
// fetch the superblock
|
||||
lfs_mdir_t dir;
|
||||
int err = lfs_dir_fetch(lfs, &dir, lfs->root);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_superblock_t superblock;
|
||||
lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0),
|
||||
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
|
||||
&superblock);
|
||||
if (tag < 0) {
|
||||
return tag;
|
||||
}
|
||||
lfs_superblock_fromle32(&superblock);
|
||||
|
||||
// read the on-disk version
|
||||
fsinfo->disk_version = superblock.version;
|
||||
}
|
||||
|
||||
// filesystem geometry
|
||||
fsinfo->block_size = lfs->cfg->block_size;
|
||||
fsinfo->block_count = lfs->block_count;
|
||||
|
||||
// other on-disk configuration, we cache all of these for internal use
|
||||
fsinfo->name_max = lfs->name_max;
|
||||
fsinfo->file_max = lfs->file_max;
|
||||
fsinfo->attr_max = lfs->attr_max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_fs_rawtraverse(lfs_t *lfs,
|
||||
int (*cb)(void *data, lfs_block_t block), void *data,
|
||||
bool includeorphans) {
|
||||
@@ -4744,15 +4571,10 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
|
||||
}
|
||||
#endif
|
||||
|
||||
static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock) {
|
||||
lfs->gstate.tag = (lfs->gstate.tag & ~LFS_MKTAG(0, 0, 0x200))
|
||||
| (uint32_t)needssuperblock << 9;
|
||||
}
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) {
|
||||
LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0x000 || orphans >= 0);
|
||||
LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) < 0x1ff || orphans <= 0);
|
||||
LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) < 0x3ff || orphans <= 0);
|
||||
lfs->gstate.tag += orphans;
|
||||
lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x800, 0, 0)) |
|
||||
((uint32_t)lfs_gstate_hasorphans(&lfs->gstate) << 31));
|
||||
@@ -4771,45 +4593,6 @@ static void lfs_fs_prepmove(lfs_t *lfs,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_fs_desuperblock(lfs_t *lfs) {
|
||||
if (!lfs_gstate_needssuperblock(&lfs->gstate)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LFS_DEBUG("Rewriting superblock {0x%"PRIx32", 0x%"PRIx32"}",
|
||||
lfs->root[0],
|
||||
lfs->root[1]);
|
||||
|
||||
lfs_mdir_t root;
|
||||
int err = lfs_dir_fetch(lfs, &root, lfs->root);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write a new superblock
|
||||
lfs_superblock_t superblock = {
|
||||
.version = lfs_fs_disk_version(lfs),
|
||||
.block_size = lfs->cfg->block_size,
|
||||
.block_count = lfs->block_count,
|
||||
.name_max = lfs->name_max,
|
||||
.file_max = lfs->file_max,
|
||||
.attr_max = lfs->attr_max,
|
||||
};
|
||||
|
||||
lfs_superblock_tole32(&superblock);
|
||||
err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
|
||||
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
|
||||
&superblock}));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_fs_prepsuperblock(lfs, false);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_fs_demove(lfs_t *lfs) {
|
||||
if (!lfs_gstate_hasmove(&lfs->gdisk)) {
|
||||
@@ -4852,6 +4635,8 @@ static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t found = 0;
|
||||
|
||||
// Check for orphans in two separate passes:
|
||||
// - 1 for half-orphans (relocations)
|
||||
// - 2 for full-orphans (removes/renames)
|
||||
@@ -4922,6 +4707,8 @@ static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) {
|
||||
return state;
|
||||
}
|
||||
|
||||
found += 1;
|
||||
|
||||
// did our commit create more orphans?
|
||||
if (state == LFS_OK_ORPHANED) {
|
||||
moreorphans = true;
|
||||
@@ -4956,6 +4743,8 @@ static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) {
|
||||
return state;
|
||||
}
|
||||
|
||||
found += 1;
|
||||
|
||||
// did our commit create more orphans?
|
||||
if (state == LFS_OK_ORPHANED) {
|
||||
moreorphans = true;
|
||||
@@ -4973,18 +4762,15 @@ static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) {
|
||||
}
|
||||
|
||||
// mark orphans as fixed
|
||||
return lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate));
|
||||
return lfs_fs_preporphans(lfs, -lfs_min(
|
||||
lfs_gstate_getorphans(&lfs->gstate),
|
||||
found));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_fs_forceconsistency(lfs_t *lfs) {
|
||||
int err = lfs_fs_desuperblock(lfs);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = lfs_fs_demove(lfs);
|
||||
int err = lfs_fs_demove(lfs);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -4998,36 +4784,6 @@ static int lfs_fs_forceconsistency(lfs_t *lfs) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_fs_rawmkconsistent(lfs_t *lfs) {
|
||||
// lfs_fs_forceconsistency does most of the work here
|
||||
int err = lfs_fs_forceconsistency(lfs);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// do we have any pending gstate?
|
||||
lfs_gstate_t delta = {0};
|
||||
lfs_gstate_xor(&delta, &lfs->gdisk);
|
||||
lfs_gstate_xor(&delta, &lfs->gstate);
|
||||
if (!lfs_gstate_iszero(&delta)) {
|
||||
// lfs_dir_commit will implicitly write out any pending gstate
|
||||
lfs_mdir_t root;
|
||||
err = lfs_dir_fetch(lfs, &root, lfs->root);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = lfs_dir_commit(lfs, &root, NULL, 0);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int lfs_fs_size_count(void *p, lfs_block_t block) {
|
||||
(void)block;
|
||||
lfs_size_t *size = p;
|
||||
@@ -5045,45 +4801,6 @@ static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) {
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_fs_rawgrow(lfs_t *lfs, lfs_size_t block_count) {
|
||||
// shrinking is not supported
|
||||
LFS_ASSERT(block_count >= lfs->block_count);
|
||||
|
||||
if (block_count > lfs->block_count) {
|
||||
lfs->block_count = block_count;
|
||||
|
||||
// fetch the root
|
||||
lfs_mdir_t root;
|
||||
int err = lfs_dir_fetch(lfs, &root, lfs->root);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// update the superblock
|
||||
lfs_superblock_t superblock;
|
||||
lfs_stag_t tag = lfs_dir_get(lfs, &root, LFS_MKTAG(0x7ff, 0x3ff, 0),
|
||||
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
|
||||
&superblock);
|
||||
if (tag < 0) {
|
||||
return tag;
|
||||
}
|
||||
lfs_superblock_fromle32(&superblock);
|
||||
|
||||
superblock.block_count = lfs->block_count;
|
||||
|
||||
lfs_superblock_tole32(&superblock);
|
||||
err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
|
||||
{tag, &superblock}));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LFS_MIGRATE
|
||||
////// Migration from littelfs v1 below this //////
|
||||
|
||||
@@ -5507,10 +5224,6 @@ static int lfs1_unmount(lfs_t *lfs) {
|
||||
/// v1 migration ///
|
||||
static int lfs_rawmigrate(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
struct lfs1 lfs1;
|
||||
|
||||
// Indeterminate filesystem size not allowed for migration.
|
||||
LFS_ASSERT(cfg->block_count != 0);
|
||||
|
||||
int err = lfs1_mount(lfs, &lfs1, cfg);
|
||||
if (err) {
|
||||
return err;
|
||||
@@ -6207,20 +5920,6 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
|
||||
return err;
|
||||
}
|
||||
|
||||
int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
|
||||
int err = LFS_LOCK(lfs->cfg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
LFS_TRACE("lfs_fs_stat(%p, %p)", (void*)lfs, (void*)fsinfo);
|
||||
|
||||
err = lfs_fs_rawstat(lfs, fsinfo);
|
||||
|
||||
LFS_TRACE("lfs_fs_stat -> %d", err);
|
||||
LFS_UNLOCK(lfs->cfg);
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_ssize_t lfs_fs_size(lfs_t *lfs) {
|
||||
int err = LFS_LOCK(lfs->cfg);
|
||||
if (err) {
|
||||
@@ -6250,54 +5949,6 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void *, lfs_block_t), void *data) {
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
int lfs_fs_gc(lfs_t *lfs) {
|
||||
int err = LFS_LOCK(lfs->cfg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
LFS_TRACE("lfs_fs_gc(%p)", (void*)lfs);
|
||||
|
||||
err = lfs_fs_rawgc(lfs);
|
||||
|
||||
LFS_TRACE("lfs_fs_gc -> %d", err);
|
||||
LFS_UNLOCK(lfs->cfg);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
int lfs_fs_mkconsistent(lfs_t *lfs) {
|
||||
int err = LFS_LOCK(lfs->cfg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
LFS_TRACE("lfs_fs_mkconsistent(%p)", (void*)lfs);
|
||||
|
||||
err = lfs_fs_rawmkconsistent(lfs);
|
||||
|
||||
LFS_TRACE("lfs_fs_mkconsistent -> %d", err);
|
||||
LFS_UNLOCK(lfs->cfg);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count) {
|
||||
int err = LFS_LOCK(lfs->cfg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
LFS_TRACE("lfs_fs_grow(%p, %"PRIu32")", (void*)lfs, block_count);
|
||||
|
||||
err = lfs_fs_rawgrow(lfs, block_count);
|
||||
|
||||
LFS_TRACE("lfs_fs_grow -> %d", err);
|
||||
LFS_UNLOCK(lfs->cfg);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LFS_MIGRATE
|
||||
int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
int err = LFS_LOCK(cfg);
|
||||
|
||||
74
lfs.h
74
lfs.h
@@ -21,14 +21,14 @@ extern "C"
|
||||
// Software library version
|
||||
// Major (top-nibble), incremented on backwards incompatible changes
|
||||
// Minor (bottom-nibble), incremented on feature additions
|
||||
#define LFS_VERSION 0x00020008
|
||||
#define LFS_VERSION 0x00020005
|
||||
#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 0x00020001
|
||||
#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))
|
||||
|
||||
@@ -263,14 +263,6 @@ struct lfs_config {
|
||||
// can help bound the metadata compaction time. Must be <= block_size.
|
||||
// Defaults to block_size when zero.
|
||||
lfs_size_t metadata_max;
|
||||
|
||||
#ifdef LFS_MULTIVERSION
|
||||
// On-disk version to use when writing in the form of 16-bit major version
|
||||
// + 16-bit minor version. This limiting metadata to what is supported by
|
||||
// older minor versions. Note that some features will be lost. Defaults to
|
||||
// to the most recent minor version when zero.
|
||||
uint32_t disk_version;
|
||||
#endif
|
||||
};
|
||||
|
||||
// File info structure
|
||||
@@ -288,27 +280,6 @@ struct lfs_info {
|
||||
char name[LFS_NAME_MAX+1];
|
||||
};
|
||||
|
||||
// Filesystem info structure
|
||||
struct lfs_fsinfo {
|
||||
// On-disk version.
|
||||
uint32_t disk_version;
|
||||
|
||||
// Size of a logical block in bytes.
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of logical blocks in filesystem.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Upper limit on the length of file names in bytes.
|
||||
lfs_size_t name_max;
|
||||
|
||||
// Upper limit on the size of files in bytes.
|
||||
lfs_size_t file_max;
|
||||
|
||||
// Upper limit on the size of custom attributes in bytes.
|
||||
lfs_size_t attr_max;
|
||||
};
|
||||
|
||||
// Custom attribute structure, used to describe custom attributes
|
||||
// committed atomically during file writes.
|
||||
struct lfs_attr {
|
||||
@@ -439,7 +410,6 @@ typedef struct lfs {
|
||||
} free;
|
||||
|
||||
const struct lfs_config *cfg;
|
||||
lfs_size_t block_count;
|
||||
lfs_size_t name_max;
|
||||
lfs_size_t file_max;
|
||||
lfs_size_t attr_max;
|
||||
@@ -689,12 +659,6 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
/// Filesystem-level filesystem operations
|
||||
|
||||
// Find on-disk info about the filesystem
|
||||
//
|
||||
// Fills out the fsinfo structure based on the filesystem found on-disk.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo);
|
||||
|
||||
// Finds the current size of the filesystem
|
||||
//
|
||||
// Note: Result is best effort. If files share COW structures, the returned
|
||||
@@ -712,40 +676,6 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs);
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
|
||||
|
||||
// Attempt to proactively find free blocks
|
||||
//
|
||||
// Calling this function is not required, but may allowing the offloading of
|
||||
// the expensive block allocation scan to a less time-critical code path.
|
||||
//
|
||||
// Note: littlefs currently does not persist any found free blocks to disk.
|
||||
// This may change in the future.
|
||||
//
|
||||
// Returns a negative error code on failure. Finding no free blocks is
|
||||
// not an error.
|
||||
int lfs_fs_gc(lfs_t *lfs);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Attempt to make the filesystem consistent and ready for writing
|
||||
//
|
||||
// Calling this function is not required, consistency will be implicitly
|
||||
// enforced on the first operation that writes to the filesystem, but this
|
||||
// function allows the work to be performed earlier and without other
|
||||
// filesystem changes.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_mkconsistent(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Grows the filesystem to a new size, updating the superblock with the new
|
||||
// block count.
|
||||
//
|
||||
// Note: This is irreversible.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
#ifdef LFS_MIGRATE
|
||||
// Attempts to migrate a previous version of littlefs
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
// System includes
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
@@ -1271,9 +1271,9 @@ static void list_geometries(void) {
|
||||
builtin_geometries[g].name,
|
||||
READ_SIZE,
|
||||
PROG_SIZE,
|
||||
ERASE_SIZE,
|
||||
ERASE_COUNT,
|
||||
ERASE_SIZE*ERASE_COUNT);
|
||||
BLOCK_SIZE,
|
||||
BLOCK_COUNT,
|
||||
BLOCK_SIZE*BLOCK_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1324,10 +1324,6 @@ void perm_run(
|
||||
};
|
||||
|
||||
struct lfs_emubd_config bdcfg = {
|
||||
.read_size = READ_SIZE,
|
||||
.prog_size = PROG_SIZE,
|
||||
.erase_size = ERASE_SIZE,
|
||||
.erase_count = ERASE_COUNT,
|
||||
.erase_value = ERASE_VALUE,
|
||||
.erase_cycles = ERASE_CYCLES,
|
||||
.badblock_behavior = BADBLOCK_BEHAVIOR,
|
||||
@@ -1337,7 +1333,7 @@ void perm_run(
|
||||
.erase_sleep = bench_erase_sleep,
|
||||
};
|
||||
|
||||
int err = lfs_emubd_create(&cfg, &bdcfg);
|
||||
int err = lfs_emubd_createcfg(&cfg, bench_disk_path, &bdcfg);
|
||||
if (err) {
|
||||
fprintf(stderr, "error: could not create block device: %d\n", err);
|
||||
exit(-1);
|
||||
@@ -1765,19 +1761,19 @@ invalid_define:
|
||||
= BENCH_LIT(sizes[0]);
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= BENCH_LIT(sizes[1]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= BENCH_LIT(sizes[2]);
|
||||
} else if (count >= 2) {
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= BENCH_LIT(sizes[0]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= BENCH_LIT(sizes[1]);
|
||||
} else {
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= BENCH_LIT(sizes[0]);
|
||||
}
|
||||
if (count >= 4) {
|
||||
geometry->defines[ERASE_COUNT_i]
|
||||
geometry->defines[BLOCK_COUNT_i]
|
||||
= BENCH_LIT(sizes[3]);
|
||||
}
|
||||
optarg = s;
|
||||
@@ -1809,19 +1805,19 @@ invalid_define:
|
||||
= BENCH_LIT(sizes[0]);
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= BENCH_LIT(sizes[1]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= BENCH_LIT(sizes[2]);
|
||||
} else if (count >= 2) {
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= BENCH_LIT(sizes[0]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= BENCH_LIT(sizes[1]);
|
||||
} else {
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= BENCH_LIT(sizes[0]);
|
||||
}
|
||||
if (count >= 4) {
|
||||
geometry->defines[ERASE_COUNT_i]
|
||||
geometry->defines[BLOCK_COUNT_i]
|
||||
= BENCH_LIT(sizes[3]);
|
||||
}
|
||||
optarg = s;
|
||||
|
||||
@@ -89,22 +89,18 @@ intmax_t bench_define(size_t define);
|
||||
|
||||
#define READ_SIZE_i 0
|
||||
#define PROG_SIZE_i 1
|
||||
#define ERASE_SIZE_i 2
|
||||
#define ERASE_COUNT_i 3
|
||||
#define BLOCK_SIZE_i 4
|
||||
#define BLOCK_COUNT_i 5
|
||||
#define CACHE_SIZE_i 6
|
||||
#define LOOKAHEAD_SIZE_i 7
|
||||
#define BLOCK_CYCLES_i 8
|
||||
#define ERASE_VALUE_i 9
|
||||
#define ERASE_CYCLES_i 10
|
||||
#define BADBLOCK_BEHAVIOR_i 11
|
||||
#define POWERLOSS_BEHAVIOR_i 12
|
||||
#define BLOCK_SIZE_i 2
|
||||
#define BLOCK_COUNT_i 3
|
||||
#define CACHE_SIZE_i 4
|
||||
#define LOOKAHEAD_SIZE_i 5
|
||||
#define BLOCK_CYCLES_i 6
|
||||
#define ERASE_VALUE_i 7
|
||||
#define ERASE_CYCLES_i 8
|
||||
#define BADBLOCK_BEHAVIOR_i 9
|
||||
#define POWERLOSS_BEHAVIOR_i 10
|
||||
|
||||
#define READ_SIZE bench_define(READ_SIZE_i)
|
||||
#define PROG_SIZE bench_define(PROG_SIZE_i)
|
||||
#define ERASE_SIZE bench_define(ERASE_SIZE_i)
|
||||
#define ERASE_COUNT bench_define(ERASE_COUNT_i)
|
||||
#define BLOCK_SIZE bench_define(BLOCK_SIZE_i)
|
||||
#define BLOCK_COUNT bench_define(BLOCK_COUNT_i)
|
||||
#define CACHE_SIZE bench_define(CACHE_SIZE_i)
|
||||
@@ -117,11 +113,9 @@ intmax_t bench_define(size_t define);
|
||||
|
||||
#define BENCH_IMPLICIT_DEFINES \
|
||||
BENCH_DEF(READ_SIZE, PROG_SIZE) \
|
||||
BENCH_DEF(PROG_SIZE, ERASE_SIZE) \
|
||||
BENCH_DEF(ERASE_SIZE, 0) \
|
||||
BENCH_DEF(ERASE_COUNT, (1024*1024)/BLOCK_SIZE) \
|
||||
BENCH_DEF(BLOCK_SIZE, ERASE_SIZE) \
|
||||
BENCH_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1))\
|
||||
BENCH_DEF(PROG_SIZE, BLOCK_SIZE) \
|
||||
BENCH_DEF(BLOCK_SIZE, 0) \
|
||||
BENCH_DEF(BLOCK_COUNT, (1024*1024)/BLOCK_SIZE) \
|
||||
BENCH_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
|
||||
BENCH_DEF(LOOKAHEAD_SIZE, 16) \
|
||||
BENCH_DEF(BLOCK_CYCLES, -1) \
|
||||
@@ -131,7 +125,7 @@ intmax_t bench_define(size_t define);
|
||||
BENCH_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP)
|
||||
|
||||
#define BENCH_GEOMETRY_DEFINE_COUNT 4
|
||||
#define BENCH_IMPLICIT_DEFINE_COUNT 13
|
||||
#define BENCH_IMPLICIT_DEFINE_COUNT 11
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1312,9 +1312,9 @@ static void list_geometries(void) {
|
||||
builtin_geometries[g].name,
|
||||
READ_SIZE,
|
||||
PROG_SIZE,
|
||||
ERASE_SIZE,
|
||||
ERASE_COUNT,
|
||||
ERASE_SIZE*ERASE_COUNT);
|
||||
BLOCK_SIZE,
|
||||
BLOCK_COUNT,
|
||||
BLOCK_SIZE*BLOCK_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1346,16 +1346,9 @@ static void run_powerloss_none(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lfs_emubd_config bdcfg = {
|
||||
.read_size = READ_SIZE,
|
||||
.prog_size = PROG_SIZE,
|
||||
.erase_size = ERASE_SIZE,
|
||||
.erase_count = ERASE_COUNT,
|
||||
.erase_value = ERASE_VALUE,
|
||||
.erase_cycles = ERASE_CYCLES,
|
||||
.badblock_behavior = BADBLOCK_BEHAVIOR,
|
||||
@@ -1365,7 +1358,7 @@ static void run_powerloss_none(
|
||||
.erase_sleep = test_erase_sleep,
|
||||
};
|
||||
|
||||
int err = lfs_emubd_create(&cfg, &bdcfg);
|
||||
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
|
||||
if (err) {
|
||||
fprintf(stderr, "error: could not create block device: %d\n", err);
|
||||
exit(-1);
|
||||
@@ -1422,16 +1415,9 @@ static void run_powerloss_linear(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lfs_emubd_config bdcfg = {
|
||||
.read_size = READ_SIZE,
|
||||
.prog_size = PROG_SIZE,
|
||||
.erase_size = ERASE_SIZE,
|
||||
.erase_count = ERASE_COUNT,
|
||||
.erase_value = ERASE_VALUE,
|
||||
.erase_cycles = ERASE_CYCLES,
|
||||
.badblock_behavior = BADBLOCK_BEHAVIOR,
|
||||
@@ -1445,7 +1431,7 @@ static void run_powerloss_linear(
|
||||
.powerloss_data = &powerloss_jmp,
|
||||
};
|
||||
|
||||
int err = lfs_emubd_create(&cfg, &bdcfg);
|
||||
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
|
||||
if (err) {
|
||||
fprintf(stderr, "error: could not create block device: %d\n", err);
|
||||
exit(-1);
|
||||
@@ -1515,16 +1501,9 @@ static void run_powerloss_log(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lfs_emubd_config bdcfg = {
|
||||
.read_size = READ_SIZE,
|
||||
.prog_size = PROG_SIZE,
|
||||
.erase_size = ERASE_SIZE,
|
||||
.erase_count = ERASE_COUNT,
|
||||
.erase_value = ERASE_VALUE,
|
||||
.erase_cycles = ERASE_CYCLES,
|
||||
.badblock_behavior = BADBLOCK_BEHAVIOR,
|
||||
@@ -1538,7 +1517,7 @@ static void run_powerloss_log(
|
||||
.powerloss_data = &powerloss_jmp,
|
||||
};
|
||||
|
||||
int err = lfs_emubd_create(&cfg, &bdcfg);
|
||||
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
|
||||
if (err) {
|
||||
fprintf(stderr, "error: could not create block device: %d\n", err);
|
||||
exit(-1);
|
||||
@@ -1606,16 +1585,9 @@ static void run_powerloss_cycles(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lfs_emubd_config bdcfg = {
|
||||
.read_size = READ_SIZE,
|
||||
.prog_size = PROG_SIZE,
|
||||
.erase_size = ERASE_SIZE,
|
||||
.erase_count = ERASE_COUNT,
|
||||
.erase_value = ERASE_VALUE,
|
||||
.erase_cycles = ERASE_CYCLES,
|
||||
.badblock_behavior = BADBLOCK_BEHAVIOR,
|
||||
@@ -1629,7 +1601,7 @@ static void run_powerloss_cycles(
|
||||
.powerloss_data = &powerloss_jmp,
|
||||
};
|
||||
|
||||
int err = lfs_emubd_create(&cfg, &bdcfg);
|
||||
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
|
||||
if (err) {
|
||||
fprintf(stderr, "error: could not create block device: %d\n", err);
|
||||
exit(-1);
|
||||
@@ -1795,16 +1767,9 @@ static void run_powerloss_exhaustive(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lfs_emubd_config bdcfg = {
|
||||
.read_size = READ_SIZE,
|
||||
.prog_size = PROG_SIZE,
|
||||
.erase_size = ERASE_SIZE,
|
||||
.erase_count = ERASE_COUNT,
|
||||
.erase_value = ERASE_VALUE,
|
||||
.erase_cycles = ERASE_CYCLES,
|
||||
.badblock_behavior = BADBLOCK_BEHAVIOR,
|
||||
@@ -1817,7 +1782,7 @@ static void run_powerloss_exhaustive(
|
||||
.powerloss_data = NULL,
|
||||
};
|
||||
|
||||
int err = lfs_emubd_create(&cfg, &bdcfg);
|
||||
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
|
||||
if (err) {
|
||||
fprintf(stderr, "error: could not create block device: %d\n", err);
|
||||
exit(-1);
|
||||
@@ -2334,19 +2299,19 @@ invalid_define:
|
||||
= TEST_LIT(sizes[0]);
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= TEST_LIT(sizes[1]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= TEST_LIT(sizes[2]);
|
||||
} else if (count >= 2) {
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= TEST_LIT(sizes[0]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= TEST_LIT(sizes[1]);
|
||||
} else {
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= TEST_LIT(sizes[0]);
|
||||
}
|
||||
if (count >= 4) {
|
||||
geometry->defines[ERASE_COUNT_i]
|
||||
geometry->defines[BLOCK_COUNT_i]
|
||||
= TEST_LIT(sizes[3]);
|
||||
}
|
||||
optarg = s;
|
||||
@@ -2378,19 +2343,19 @@ invalid_define:
|
||||
= TEST_LIT(sizes[0]);
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= TEST_LIT(sizes[1]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= TEST_LIT(sizes[2]);
|
||||
} else if (count >= 2) {
|
||||
geometry->defines[PROG_SIZE_i]
|
||||
= TEST_LIT(sizes[0]);
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= TEST_LIT(sizes[1]);
|
||||
} else {
|
||||
geometry->defines[ERASE_SIZE_i]
|
||||
geometry->defines[BLOCK_SIZE_i]
|
||||
= TEST_LIT(sizes[0]);
|
||||
}
|
||||
if (count >= 4) {
|
||||
geometry->defines[ERASE_COUNT_i]
|
||||
geometry->defines[BLOCK_COUNT_i]
|
||||
= TEST_LIT(sizes[3]);
|
||||
}
|
||||
optarg = s;
|
||||
|
||||
@@ -82,23 +82,18 @@ intmax_t test_define(size_t define);
|
||||
|
||||
#define READ_SIZE_i 0
|
||||
#define PROG_SIZE_i 1
|
||||
#define ERASE_SIZE_i 2
|
||||
#define ERASE_COUNT_i 3
|
||||
#define BLOCK_SIZE_i 4
|
||||
#define BLOCK_COUNT_i 5
|
||||
#define CACHE_SIZE_i 6
|
||||
#define LOOKAHEAD_SIZE_i 7
|
||||
#define BLOCK_CYCLES_i 8
|
||||
#define ERASE_VALUE_i 9
|
||||
#define ERASE_CYCLES_i 10
|
||||
#define BADBLOCK_BEHAVIOR_i 11
|
||||
#define POWERLOSS_BEHAVIOR_i 12
|
||||
#define DISK_VERSION_i 13
|
||||
#define BLOCK_SIZE_i 2
|
||||
#define BLOCK_COUNT_i 3
|
||||
#define CACHE_SIZE_i 4
|
||||
#define LOOKAHEAD_SIZE_i 5
|
||||
#define BLOCK_CYCLES_i 6
|
||||
#define ERASE_VALUE_i 7
|
||||
#define ERASE_CYCLES_i 8
|
||||
#define BADBLOCK_BEHAVIOR_i 9
|
||||
#define POWERLOSS_BEHAVIOR_i 10
|
||||
|
||||
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
|
||||
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
|
||||
#define ERASE_SIZE TEST_DEFINE(ERASE_SIZE_i)
|
||||
#define ERASE_COUNT TEST_DEFINE(ERASE_COUNT_i)
|
||||
#define BLOCK_SIZE TEST_DEFINE(BLOCK_SIZE_i)
|
||||
#define BLOCK_COUNT TEST_DEFINE(BLOCK_COUNT_i)
|
||||
#define CACHE_SIZE TEST_DEFINE(CACHE_SIZE_i)
|
||||
@@ -108,26 +103,22 @@ intmax_t test_define(size_t define);
|
||||
#define ERASE_CYCLES TEST_DEFINE(ERASE_CYCLES_i)
|
||||
#define BADBLOCK_BEHAVIOR TEST_DEFINE(BADBLOCK_BEHAVIOR_i)
|
||||
#define POWERLOSS_BEHAVIOR TEST_DEFINE(POWERLOSS_BEHAVIOR_i)
|
||||
#define DISK_VERSION TEST_DEFINE(DISK_VERSION_i)
|
||||
|
||||
#define TEST_IMPLICIT_DEFINES \
|
||||
TEST_DEF(READ_SIZE, PROG_SIZE) \
|
||||
TEST_DEF(PROG_SIZE, ERASE_SIZE) \
|
||||
TEST_DEF(ERASE_SIZE, 0) \
|
||||
TEST_DEF(ERASE_COUNT, (1024*1024)/ERASE_SIZE) \
|
||||
TEST_DEF(BLOCK_SIZE, ERASE_SIZE) \
|
||||
TEST_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1)) \
|
||||
TEST_DEF(PROG_SIZE, BLOCK_SIZE) \
|
||||
TEST_DEF(BLOCK_SIZE, 0) \
|
||||
TEST_DEF(BLOCK_COUNT, (1024*1024)/BLOCK_SIZE) \
|
||||
TEST_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
|
||||
TEST_DEF(LOOKAHEAD_SIZE, 16) \
|
||||
TEST_DEF(BLOCK_CYCLES, -1) \
|
||||
TEST_DEF(ERASE_VALUE, 0xff) \
|
||||
TEST_DEF(ERASE_CYCLES, 0) \
|
||||
TEST_DEF(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR) \
|
||||
TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP) \
|
||||
TEST_DEF(DISK_VERSION, 0)
|
||||
TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP)
|
||||
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 11
|
||||
#define TEST_GEOMETRY_DEFINE_COUNT 4
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 14
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -107,10 +107,7 @@ def main(from_prefix, to_prefix, paths=[], *,
|
||||
elif no_renames:
|
||||
to_path = from_path
|
||||
else:
|
||||
to_path = os.path.join(
|
||||
os.path.dirname(from_path),
|
||||
changeprefix(from_prefix, to_prefix,
|
||||
os.path.basename(from_path))[0])
|
||||
to_path, _ = changeprefix(from_prefix, to_prefix, from_path)
|
||||
|
||||
# rename contents
|
||||
changefile(from_prefix, to_prefix, from_path, to_path,
|
||||
|
||||
@@ -6,7 +6,6 @@ if = 'BLOCK_CYCLES == -1'
|
||||
[cases.test_alloc_parallel]
|
||||
defines.FILES = 3
|
||||
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
|
||||
defines.GC = [false, true]
|
||||
code = '''
|
||||
const char *names[] = {"bacon", "eggs", "pancakes"};
|
||||
lfs_file_t files[FILES];
|
||||
@@ -25,9 +24,6 @@ code = '''
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
}
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
if (GC) {
|
||||
lfs_fs_gc(&lfs) => 0;
|
||||
}
|
||||
size_t size = strlen(names[n]);
|
||||
for (lfs_size_t i = 0; i < SIZE; i += size) {
|
||||
lfs_file_write(&lfs, &files[n], names[n], size) => size;
|
||||
@@ -59,7 +55,6 @@ code = '''
|
||||
[cases.test_alloc_serial]
|
||||
defines.FILES = 3
|
||||
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
|
||||
defines.GC = [false, true]
|
||||
code = '''
|
||||
const char *names[] = {"bacon", "eggs", "pancakes"};
|
||||
|
||||
@@ -80,9 +75,6 @@ code = '''
|
||||
uint8_t buffer[1024];
|
||||
memcpy(buffer, names[n], size);
|
||||
for (int i = 0; i < SIZE; i += size) {
|
||||
if (GC) {
|
||||
lfs_fs_gc(&lfs) => 0;
|
||||
}
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
@@ -255,9 +247,6 @@ code = '''
|
||||
}
|
||||
res => LFS_ERR_NOSPC;
|
||||
|
||||
// note that lfs_fs_gc should not error here
|
||||
lfs_fs_gc(&lfs) => 0;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
@@ -309,9 +298,6 @@ code = '''
|
||||
}
|
||||
res => LFS_ERR_NOSPC;
|
||||
|
||||
// note that lfs_fs_gc should not error here
|
||||
lfs_fs_gc(&lfs) => 0;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
@@ -351,8 +337,6 @@ code = '''
|
||||
count += 1;
|
||||
}
|
||||
err => LFS_ERR_NOSPC;
|
||||
// note that lfs_fs_gc should not error here
|
||||
lfs_fs_gc(&lfs) => 0;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
@@ -451,8 +435,6 @@ code = '''
|
||||
break;
|
||||
}
|
||||
}
|
||||
// note that lfs_fs_gc should not error here
|
||||
lfs_fs_gc(&lfs) => 0;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
@@ -478,8 +460,8 @@ code = '''
|
||||
|
||||
# chained dir exhaustion test
|
||||
[cases.test_alloc_chained_dir_exhaustion]
|
||||
if = 'ERASE_SIZE == 512'
|
||||
defines.ERASE_COUNT = 1024
|
||||
if = 'BLOCK_SIZE == 512'
|
||||
defines.BLOCK_COUNT = 1024
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -556,8 +538,8 @@ code = '''
|
||||
|
||||
# split dir test
|
||||
[cases.test_alloc_split_dir]
|
||||
if = 'ERASE_SIZE == 512'
|
||||
defines.ERASE_COUNT = 1024
|
||||
if = 'BLOCK_SIZE == 512'
|
||||
defines.BLOCK_COUNT = 1024
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -605,8 +587,8 @@ code = '''
|
||||
|
||||
# outdated lookahead test
|
||||
[cases.test_alloc_outdated_lookahead]
|
||||
if = 'ERASE_SIZE == 512'
|
||||
defines.ERASE_COUNT = 1024
|
||||
if = 'BLOCK_SIZE == 512'
|
||||
defines.BLOCK_COUNT = 1024
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -673,8 +655,8 @@ code = '''
|
||||
|
||||
# outdated lookahead and split dir test
|
||||
[cases.test_alloc_outdated_lookahead_split_dir]
|
||||
if = 'ERASE_SIZE == 512'
|
||||
defines.ERASE_COUNT = 1024
|
||||
if = 'BLOCK_SIZE == 512'
|
||||
defines.BLOCK_COUNT = 1024
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
if = '(int32_t)BLOCK_CYCLES == -1'
|
||||
|
||||
[cases.test_badblocks_single]
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.ERASE_CYCLES = 0xffffffff
|
||||
defines.ERASE_VALUE = [0x00, 0xff, -1]
|
||||
defines.BADBLOCK_BEHAVIOR = [
|
||||
@@ -82,7 +82,7 @@ code = '''
|
||||
'''
|
||||
|
||||
[cases.test_badblocks_region_corruption] # (causes cascading failures)
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.ERASE_CYCLES = 0xffffffff
|
||||
defines.ERASE_VALUE = [0x00, 0xff, -1]
|
||||
defines.BADBLOCK_BEHAVIOR = [
|
||||
@@ -161,7 +161,7 @@ code = '''
|
||||
'''
|
||||
|
||||
[cases.test_badblocks_alternating_corruption] # (causes cascading failures)
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.ERASE_CYCLES = 0xffffffff
|
||||
defines.ERASE_VALUE = [0x00, 0xff, -1]
|
||||
defines.BADBLOCK_BEHAVIOR = [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -810,8 +810,7 @@ code = '''
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// try seeking to each dir entry
|
||||
for (int j = 0; j < COUNT; j++) {
|
||||
for (int j = 2; j < COUNT; j++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "hello") => 0;
|
||||
@@ -823,15 +822,16 @@ code = '''
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
|
||||
lfs_soff_t pos;
|
||||
for (int i = 0; i < j; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "kitty%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
pos = lfs_dir_tell(&lfs, &dir);
|
||||
assert(pos >= 0);
|
||||
}
|
||||
lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
|
||||
assert(pos >= 0);
|
||||
|
||||
lfs_dir_seek(&lfs, &dir, pos) => 0;
|
||||
char path[1024];
|
||||
@@ -861,52 +861,6 @@ code = '''
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
}
|
||||
|
||||
// try seeking to end of dir
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "hello") => 0;
|
||||
struct lfs_info info;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "kitty%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
|
||||
assert(pos >= 0);
|
||||
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
|
||||
lfs_dir_seek(&lfs, &dir, pos) => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
|
||||
lfs_dir_rewind(&lfs, &dir) => 0;
|
||||
char path[1024];
|
||||
sprintf(path, "kitty%03d", 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
|
||||
lfs_dir_seek(&lfs, &dir, pos) => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_dirs_toot_seek]
|
||||
@@ -923,7 +877,7 @@ code = '''
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
for (int j = 0; j < COUNT; j++) {
|
||||
for (int j = 2; j < COUNT; j++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "/") => 0;
|
||||
@@ -935,15 +889,16 @@ code = '''
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
|
||||
lfs_soff_t pos;
|
||||
for (int i = 0; i < j; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "hi%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
pos = lfs_dir_tell(&lfs, &dir);
|
||||
assert(pos >= 0);
|
||||
}
|
||||
lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
|
||||
assert(pos >= 0);
|
||||
|
||||
lfs_dir_seek(&lfs, &dir, pos) => 0;
|
||||
char path[1024];
|
||||
@@ -973,51 +928,5 @@ code = '''
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
}
|
||||
|
||||
// try seeking to end of dir
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "/") => 0;
|
||||
struct lfs_info info;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "hi%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
|
||||
assert(pos >= 0);
|
||||
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
|
||||
lfs_dir_seek(&lfs, &dir, pos) => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
|
||||
lfs_dir_rewind(&lfs, &dir) => 0;
|
||||
char path[1024];
|
||||
sprintf(path, "hi%03d", 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
|
||||
lfs_dir_seek(&lfs, &dir, pos) => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# test running a filesystem to exhaustion
|
||||
[cases.test_exhaustion_normal]
|
||||
defines.ERASE_CYCLES = 10
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
|
||||
defines.BADBLOCK_BEHAVIOR = [
|
||||
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
||||
@@ -94,7 +94,7 @@ exhausted:
|
||||
# which also requires expanding superblocks
|
||||
[cases.test_exhaustion_superblocks]
|
||||
defines.ERASE_CYCLES = 10
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
|
||||
defines.BADBLOCK_BEHAVIOR = [
|
||||
'LFS_EMUBD_BADBLOCK_PROGERROR',
|
||||
@@ -188,7 +188,7 @@ exhausted:
|
||||
# wear-level test running a filesystem to exhaustion
|
||||
[cases.test_exhuastion_wear_leveling]
|
||||
defines.ERASE_CYCLES = 20
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
|
||||
defines.FILES = 10
|
||||
code = '''
|
||||
@@ -288,7 +288,7 @@ exhausted:
|
||||
# wear-level test + expanding superblock
|
||||
[cases.test_exhaustion_wear_leveling_superblocks]
|
||||
defines.ERASE_CYCLES = 20
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
|
||||
defines.FILES = 10
|
||||
code = '''
|
||||
@@ -385,7 +385,7 @@ exhausted:
|
||||
# test that we wear blocks roughly evenly
|
||||
[cases.test_exhaustion_wear_distribution]
|
||||
defines.ERASE_CYCLES = 0xffffffff
|
||||
defines.ERASE_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_COUNT = 256 # small bd so test runs faster
|
||||
defines.BLOCK_CYCLES = [5, 4, 3, 2, 1]
|
||||
defines.CYCLES = 100
|
||||
defines.FILES = 10
|
||||
|
||||
@@ -59,150 +59,6 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# test that we only run deorphan once per power-cycle
|
||||
[cases.test_orphans_no_orphans]
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// mark the filesystem as having orphans
|
||||
lfs_fs_preporphans(&lfs, +1) => 0;
|
||||
lfs_mdir_t mdir;
|
||||
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
|
||||
lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0;
|
||||
|
||||
// we should have orphans at this state
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// mount
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// we should detect orphans
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
// force consistency
|
||||
lfs_fs_forceconsistency(&lfs) => 0;
|
||||
// we should no longer have orphans
|
||||
assert(!lfs_gstate_hasorphans(&lfs.gstate));
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_orphans_one_orphan]
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// create an orphan
|
||||
lfs_mdir_t orphan;
|
||||
lfs_alloc_ack(&lfs);
|
||||
lfs_dir_alloc(&lfs, &orphan) => 0;
|
||||
lfs_dir_commit(&lfs, &orphan, NULL, 0) => 0;
|
||||
|
||||
// append our orphan and mark the filesystem as having orphans
|
||||
lfs_fs_preporphans(&lfs, +1) => 0;
|
||||
lfs_mdir_t mdir;
|
||||
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
|
||||
lfs_pair_tole32(orphan.pair);
|
||||
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
|
||||
{LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), orphan.pair})) => 0;
|
||||
|
||||
// we should have orphans at this state
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// mount
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// we should detect orphans
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
// force consistency
|
||||
lfs_fs_forceconsistency(&lfs) => 0;
|
||||
// we should no longer have orphans
|
||||
assert(!lfs_gstate_hasorphans(&lfs.gstate));
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# test that we can persist gstate with lfs_fs_mkconsistent
|
||||
[cases.test_orphans_mkconsistent_no_orphans]
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// mark the filesystem as having orphans
|
||||
lfs_fs_preporphans(&lfs, +1) => 0;
|
||||
lfs_mdir_t mdir;
|
||||
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
|
||||
lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0;
|
||||
|
||||
// we should have orphans at this state
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// mount
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// we should detect orphans
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
// force consistency
|
||||
lfs_fs_mkconsistent(&lfs) => 0;
|
||||
// we should no longer have orphans
|
||||
assert(!lfs_gstate_hasorphans(&lfs.gstate));
|
||||
|
||||
// remount
|
||||
lfs_unmount(&lfs) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// we should still have no orphans
|
||||
assert(!lfs_gstate_hasorphans(&lfs.gstate));
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_orphans_mkconsistent_one_orphan]
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// create an orphan
|
||||
lfs_mdir_t orphan;
|
||||
lfs_alloc_ack(&lfs);
|
||||
lfs_dir_alloc(&lfs, &orphan) => 0;
|
||||
lfs_dir_commit(&lfs, &orphan, NULL, 0) => 0;
|
||||
|
||||
// append our orphan and mark the filesystem as having orphans
|
||||
lfs_fs_preporphans(&lfs, +1) => 0;
|
||||
lfs_mdir_t mdir;
|
||||
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
|
||||
lfs_pair_tole32(orphan.pair);
|
||||
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
|
||||
{LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), orphan.pair})) => 0;
|
||||
|
||||
// we should have orphans at this state
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// mount
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// we should detect orphans
|
||||
assert(lfs_gstate_hasorphans(&lfs.gstate));
|
||||
// force consistency
|
||||
lfs_fs_mkconsistent(&lfs) => 0;
|
||||
// we should no longer have orphans
|
||||
assert(!lfs_gstate_hasorphans(&lfs.gstate));
|
||||
|
||||
// remount
|
||||
lfs_unmount(&lfs) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// we should still have no orphans
|
||||
assert(!lfs_gstate_hasorphans(&lfs.gstate));
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# reentrant testing for orphans, basically just spam mkdir/remove
|
||||
[cases.test_orphans_reentrant]
|
||||
reentrant = true
|
||||
|
||||
@@ -90,10 +90,7 @@ code = '''
|
||||
|
||||
# partial prog, may not be byte in order!
|
||||
[cases.test_powerloss_partial_prog]
|
||||
if = '''
|
||||
PROG_SIZE < BLOCK_SIZE
|
||||
&& (DISK_VERSION == 0 || DISK_VERSION >= 0x00020001)
|
||||
'''
|
||||
if = "PROG_SIZE < BLOCK_SIZE"
|
||||
defines.BYTE_OFF = ["0", "PROG_SIZE-1", "PROG_SIZE/2"]
|
||||
defines.BYTE_VALUE = [0x33, 0xcc]
|
||||
in = "lfs.c"
|
||||
|
||||
@@ -14,21 +14,6 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# mount/unmount from interpretting a previous superblock block_count
|
||||
[cases.test_superblocks_mount_unknown_block_count]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
memset(&lfs, 0, sizeof(lfs));
|
||||
struct lfs_config tweaked_cfg = *cfg;
|
||||
tweaked_cfg.block_count = 0;
|
||||
lfs_mount(&lfs, &tweaked_cfg) => 0;
|
||||
assert(lfs.block_count == cfg->block_count);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
# reentrant format
|
||||
[cases.test_superblocks_reentrant_format]
|
||||
reentrant = true
|
||||
@@ -49,54 +34,6 @@ code = '''
|
||||
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
|
||||
'''
|
||||
|
||||
# test we can read superblock info through lfs_fs_stat
|
||||
[cases.test_superblocks_stat]
|
||||
if = 'DISK_VERSION == 0'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
// test we can mount and read fsinfo
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
|
||||
struct lfs_fsinfo fsinfo;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.disk_version == LFS_DISK_VERSION);
|
||||
assert(fsinfo.name_max == LFS_NAME_MAX);
|
||||
assert(fsinfo.file_max == LFS_FILE_MAX);
|
||||
assert(fsinfo.attr_max == LFS_ATTR_MAX);
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_superblocks_stat_tweaked]
|
||||
if = 'DISK_VERSION == 0'
|
||||
defines.TWEAKED_NAME_MAX = 63
|
||||
defines.TWEAKED_FILE_MAX = '(1 << 16)-1'
|
||||
defines.TWEAKED_ATTR_MAX = 512
|
||||
code = '''
|
||||
// create filesystem with tweaked params
|
||||
struct lfs_config tweaked_cfg = *cfg;
|
||||
tweaked_cfg.name_max = TWEAKED_NAME_MAX;
|
||||
tweaked_cfg.file_max = TWEAKED_FILE_MAX;
|
||||
tweaked_cfg.attr_max = TWEAKED_ATTR_MAX;
|
||||
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, &tweaked_cfg) => 0;
|
||||
|
||||
// test we can mount and read these params with the original config
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
|
||||
struct lfs_fsinfo fsinfo;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.disk_version == LFS_DISK_VERSION);
|
||||
assert(fsinfo.name_max == TWEAKED_NAME_MAX);
|
||||
assert(fsinfo.file_max == TWEAKED_FILE_MAX);
|
||||
assert(fsinfo.attr_max == TWEAKED_ATTR_MAX);
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# expanding superblock
|
||||
[cases.test_superblocks_expand]
|
||||
defines.BLOCK_CYCLES = [32, 33, 1]
|
||||
@@ -212,255 +149,3 @@ code = '''
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# mount with unknown block_count
|
||||
[cases.test_superblocks_unknown_blocks]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
// known block_size/block_count
|
||||
cfg->block_size = BLOCK_SIZE;
|
||||
cfg->block_count = BLOCK_COUNT;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_fsinfo fsinfo;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// unknown block_count
|
||||
cfg->block_size = BLOCK_SIZE;
|
||||
cfg->block_count = 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// do some work
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "test",
|
||||
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
|
||||
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
|
||||
uint8_t buffer[256];
|
||||
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
assert(memcmp(buffer, "hello!", 6) == 0);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# mount with blocks fewer than the erase_count
|
||||
[cases.test_superblocks_fewer_blocks]
|
||||
defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
// known block_size/block_count
|
||||
cfg->block_size = BLOCK_SIZE;
|
||||
cfg->block_count = BLOCK_COUNT;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_fsinfo fsinfo;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// incorrect block_count
|
||||
cfg->block_size = BLOCK_SIZE;
|
||||
cfg->block_count = ERASE_COUNT;
|
||||
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
||||
|
||||
// unknown block_count
|
||||
cfg->block_size = BLOCK_SIZE;
|
||||
cfg->block_count = 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// do some work
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "test",
|
||||
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
|
||||
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
|
||||
uint8_t buffer[256];
|
||||
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
assert(memcmp(buffer, "hello!", 6) == 0);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# mount with more blocks than the erase_count
|
||||
[cases.test_superblocks_more_blocks]
|
||||
defines.FORMAT_BLOCK_COUNT = '2*ERASE_COUNT'
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_init(&lfs, cfg) => 0;
|
||||
lfs.block_count = BLOCK_COUNT;
|
||||
|
||||
lfs_mdir_t root = {
|
||||
.pair = {0, 0}, // make sure this goes into block 0
|
||||
.rev = 0,
|
||||
.off = sizeof(uint32_t),
|
||||
.etag = 0xffffffff,
|
||||
.count = 0,
|
||||
.tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
||||
.erased = false,
|
||||
.split = false,
|
||||
};
|
||||
|
||||
lfs_superblock_t superblock = {
|
||||
.version = LFS_DISK_VERSION,
|
||||
.block_size = BLOCK_SIZE,
|
||||
.block_count = FORMAT_BLOCK_COUNT,
|
||||
.name_max = LFS_NAME_MAX,
|
||||
.file_max = LFS_FILE_MAX,
|
||||
.attr_max = LFS_ATTR_MAX,
|
||||
};
|
||||
|
||||
lfs_superblock_tole32(&superblock);
|
||||
lfs_dir_commit(&lfs, &root, LFS_MKATTRS(
|
||||
{LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
|
||||
{LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
|
||||
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
|
||||
&superblock})) => 0;
|
||||
lfs_deinit(&lfs) => 0;
|
||||
|
||||
// known block_size/block_count
|
||||
cfg->block_size = BLOCK_SIZE;
|
||||
cfg->block_count = BLOCK_COUNT;
|
||||
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
||||
'''
|
||||
|
||||
# mount and grow the filesystem
|
||||
[cases.test_superblocks_grow]
|
||||
defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
|
||||
defines.BLOCK_COUNT_2 = 'ERASE_COUNT'
|
||||
defines.KNOWN_BLOCK_COUNT = [true, false]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
if (KNOWN_BLOCK_COUNT) {
|
||||
cfg->block_count = BLOCK_COUNT;
|
||||
} else {
|
||||
cfg->block_count = 0;
|
||||
}
|
||||
|
||||
// mount with block_size < erase_size
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_fsinfo fsinfo;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// same size is a noop
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_grow(&lfs, BLOCK_COUNT) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// grow to new size
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
if (KNOWN_BLOCK_COUNT) {
|
||||
cfg->block_count = BLOCK_COUNT_2;
|
||||
} else {
|
||||
cfg->block_count = 0;
|
||||
}
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// mounting with the previous size should fail
|
||||
cfg->block_count = BLOCK_COUNT;
|
||||
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
|
||||
|
||||
if (KNOWN_BLOCK_COUNT) {
|
||||
cfg->block_count = BLOCK_COUNT_2;
|
||||
} else {
|
||||
cfg->block_count = 0;
|
||||
}
|
||||
|
||||
// same size is a noop
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// do some work
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "test",
|
||||
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
|
||||
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_fs_stat(&lfs, &fsinfo) => 0;
|
||||
assert(fsinfo.block_size == BLOCK_SIZE);
|
||||
assert(fsinfo.block_count == BLOCK_COUNT_2);
|
||||
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
|
||||
uint8_t buffer[256];
|
||||
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
assert(memcmp(buffer, "hello!", 6) == 0);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# simple truncate
|
||||
[cases.test_truncate_simple]
|
||||
defines.MEDIUMSIZE = [31, 32, 33, 511, 512, 513, 2047, 2048, 2049]
|
||||
defines.LARGESIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
if = 'MEDIUMSIZE < LARGESIZE'
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
defines.LARGESIZE = 8192
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -15,8 +14,7 @@ code = '''
|
||||
strcpy((char*)buffer, "hair");
|
||||
size_t size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
|
||||
@@ -39,9 +37,8 @@ code = '''
|
||||
|
||||
size = strlen("hair");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
}
|
||||
lfs_file_read(&lfs, &file, buffer, size) => 0;
|
||||
|
||||
@@ -51,9 +48,8 @@ code = '''
|
||||
|
||||
# truncate and read
|
||||
[cases.test_truncate_read]
|
||||
defines.MEDIUMSIZE = [31, 32, 33, 511, 512, 513, 2047, 2048, 2049]
|
||||
defines.LARGESIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
if = 'MEDIUMSIZE < LARGESIZE'
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
defines.LARGESIZE = 8192
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -66,8 +62,7 @@ code = '''
|
||||
strcpy((char*)buffer, "hair");
|
||||
size_t size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
|
||||
@@ -83,9 +78,8 @@ code = '''
|
||||
|
||||
size = strlen("hair");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
}
|
||||
lfs_file_read(&lfs, &file, buffer, size) => 0;
|
||||
|
||||
@@ -98,9 +92,8 @@ code = '''
|
||||
|
||||
size = strlen("hair");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
}
|
||||
lfs_file_read(&lfs, &file, buffer, size) => 0;
|
||||
|
||||
@@ -155,7 +148,7 @@ code = '''
|
||||
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;
|
||||
@@ -166,9 +159,8 @@ code = '''
|
||||
|
||||
# truncate and write
|
||||
[cases.test_truncate_write]
|
||||
defines.MEDIUMSIZE = [31, 32, 33, 511, 512, 513, 2047, 2048, 2049]
|
||||
defines.LARGESIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
if = 'MEDIUMSIZE < LARGESIZE'
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
defines.LARGESIZE = 8192
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -181,8 +173,7 @@ code = '''
|
||||
strcpy((char*)buffer, "hair");
|
||||
size_t size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
|
||||
@@ -193,16 +184,13 @@ code = '''
|
||||
lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDWR) => 0;
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
|
||||
/* truncate */
|
||||
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
|
||||
/* and write */
|
||||
strcpy((char*)buffer, "bald");
|
||||
size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
|
||||
@@ -215,9 +203,8 @@ code = '''
|
||||
|
||||
size = strlen("bald");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
memcmp(buffer, "bald", lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "bald", size) => 0;
|
||||
}
|
||||
lfs_file_read(&lfs, &file, buffer, size) => 0;
|
||||
|
||||
@@ -228,7 +215,7 @@ code = '''
|
||||
# truncate write under powerloss
|
||||
[cases.test_truncate_reentrant_write]
|
||||
defines.SMALLSIZE = [4, 512]
|
||||
defines.MEDIUMSIZE = [0, 3, 4, 5, 31, 32, 33, 511, 512, 513, 1023, 1024, 1025]
|
||||
defines.MEDIUMSIZE = [32, 1024]
|
||||
defines.LARGESIZE = 2048
|
||||
reentrant = true
|
||||
code = '''
|
||||
@@ -249,11 +236,10 @@ code = '''
|
||||
size == (size_t)SMALLSIZE);
|
||||
for (lfs_off_t j = 0; j < size; j += 4) {
|
||||
uint8_t buffer[1024];
|
||||
lfs_file_read(&lfs, &file, buffer, lfs_min(4, size-j))
|
||||
=> lfs_min(4, size-j);
|
||||
assert(memcmp(buffer, "hair", lfs_min(4, size-j)) == 0 ||
|
||||
memcmp(buffer, "bald", lfs_min(4, size-j)) == 0 ||
|
||||
memcmp(buffer, "comb", lfs_min(4, size-j)) == 0);
|
||||
lfs_file_read(&lfs, &file, buffer, 4) => 4;
|
||||
assert(memcmp(buffer, "hair", 4) == 0 ||
|
||||
memcmp(buffer, "bald", 4) == 0 ||
|
||||
memcmp(buffer, "comb", 4) == 0);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
@@ -265,23 +251,19 @@ code = '''
|
||||
strcpy((char*)buffer, "hair");
|
||||
size_t size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file, "baldy", LFS_O_RDWR) => 0;
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
/* truncate */
|
||||
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
/* and write */
|
||||
strcpy((char*)buffer, "bald");
|
||||
size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
@@ -293,8 +275,7 @@ code = '''
|
||||
strcpy((char*)buffer, "comb");
|
||||
size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < SMALLSIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, SMALLSIZE-j))
|
||||
=> lfs_min(size, SMALLSIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => SMALLSIZE;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
@@ -448,7 +429,7 @@ code = '''
|
||||
|
||||
# noop truncate
|
||||
[cases.test_truncate_nop]
|
||||
defines.MEDIUMSIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -461,11 +442,10 @@ code = '''
|
||||
strcpy((char*)buffer, "hair");
|
||||
size_t size = strlen((char*)buffer);
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
// this truncate should do nothing
|
||||
lfs_file_truncate(&lfs, &file, j+lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
lfs_file_truncate(&lfs, &file, j+size) => 0;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
|
||||
@@ -475,9 +455,8 @@ code = '''
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
}
|
||||
lfs_file_read(&lfs, &file, buffer, size) => 0;
|
||||
|
||||
@@ -489,9 +468,8 @@ code = '''
|
||||
lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0;
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user