forked from Imagelibrary/littlefs
Compare commits
59 Commits
brent-cycl
...
configurab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79cc75d18f | ||
|
|
eb9af7abe5 | ||
|
|
b72c96d440 | ||
|
|
265692e709 | ||
|
|
c5fb3f181b | ||
|
|
8610f7c36b | ||
|
|
a51be18765 | ||
|
|
a7ccc1df59 | ||
|
|
fdee127f74 | ||
|
|
87bbf1d374 | ||
|
|
66f07563c3 | ||
|
|
5eed341059 | ||
|
|
97e2526a81 | ||
|
|
8a4ee65fc3 | ||
|
|
6fda813ce8 | ||
|
|
f2bc6a8e88 | ||
|
|
ec3ec86bcc | ||
|
|
405f33214a | ||
|
|
3dca02911f | ||
|
|
259535ee73 | ||
|
|
94d9e097a6 | ||
|
|
dd03c27476 | ||
|
|
23a4a089b5 | ||
|
|
b6773e68bf | ||
|
|
922a35b3a5 | ||
|
|
92298c749d | ||
|
|
50b394ca36 | ||
|
|
a99574cd5b | ||
|
|
363a8b56cf | ||
|
|
e43d381135 | ||
|
|
ee6a51bbbe | ||
|
|
01ac033d47 | ||
|
|
2a18e03cb8 | ||
|
|
6f074ebe31 | ||
|
|
0a7eca0bd5 | ||
|
|
3e25dfc16c | ||
|
|
9e28c75482 | ||
|
|
4c9360020e | ||
|
|
ca0da3d490 | ||
|
|
116332d3f7 | ||
|
|
f0cc1db793 | ||
|
|
bf045dd13c | ||
|
|
b33a5b3f85 | ||
|
|
384a498762 | ||
|
|
b0a4a44e5b | ||
|
|
aae897ffd0 | ||
|
|
e57402c8e9 | ||
|
|
6dc18c38c1 | ||
|
|
d5dc4872cb | ||
|
|
24795e6b74 | ||
|
|
7b151e1abb | ||
|
|
ba1c76435a | ||
|
|
d1b254da2c | ||
|
|
2f26966710 | ||
|
|
b4091c6871 | ||
|
|
91ad673c45 | ||
|
|
52dd83096b | ||
|
|
d9333ecbd4 | ||
|
|
3ae87f4e29 |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# 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
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -102,7 +102,7 @@ jobs:
|
||||
# sizes table
|
||||
i=0
|
||||
j=0
|
||||
for c in "" readonly threadsafe migrate error-asserts
|
||||
for c in "" readonly threadsafe multiversion migrate error-asserts
|
||||
do
|
||||
# per-config results
|
||||
c_or_default=${c:-default}
|
||||
|
||||
98
.github/workflows/test.yml
vendored
98
.github/workflows/test.yml
vendored
@@ -170,6 +170,27 @@ 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
|
||||
@@ -353,6 +374,42 @@ 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
|
||||
@@ -371,7 +428,8 @@ jobs:
|
||||
# on one geometry
|
||||
- name: test-valgrind
|
||||
run: |
|
||||
TESTFLAGS="$TESTFLAGS --valgrind -Gdefault -Pnone" make test
|
||||
TESTFLAGS="$TESTFLAGS --valgrind --context=1024 -Gdefault -Pnone" \
|
||||
make test
|
||||
|
||||
# test that compilation is warning free under clang
|
||||
# run with Clang, mostly to check for Clang-specific warnings
|
||||
@@ -473,6 +531,42 @@ 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
|
||||
@@ -648,7 +742,7 @@ jobs:
|
||||
# sizes table
|
||||
i=0
|
||||
j=0
|
||||
for c in "" readonly threadsafe migrate error-asserts
|
||||
for c in "" readonly threadsafe multiversion migrate error-asserts
|
||||
do
|
||||
# per-config results
|
||||
c_or_default=${c:-default}
|
||||
|
||||
27
Makefile
27
Makefile
@@ -1,15 +1,5 @@
|
||||
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
|
||||
# overrideable build dir, default is in-place
|
||||
BUILDDIR ?= .
|
||||
|
||||
# overridable target/src/tools/flags/etc
|
||||
ifneq ($(wildcard test.c main.c),)
|
||||
TARGET ?= $(BUILDDIR)/lfs
|
||||
@@ -163,6 +153,18 @@ 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
|
||||
|
||||
@@ -514,6 +516,9 @@ $(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 $@
|
||||
|
||||
|
||||
13
README.md
13
README.md
@@ -226,6 +226,13 @@ 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.
|
||||
@@ -243,8 +250,12 @@ 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.
|
||||
|
||||
- [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
|
||||
@@ -256,3 +267,5 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
[SPIFFS]: https://github.com/pellepl/spiffs
|
||||
[Dhara]: https://github.com/dlbeer/dhara
|
||||
[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. 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 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).
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
@@ -133,12 +133,6 @@ 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:
|
||||
|
||||
```
|
||||
@@ -191,6 +185,53 @@ 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
|
||||
@@ -785,3 +826,41 @@ 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`.
|
||||
|
||||
---
|
||||
|
||||
@@ -584,13 +584,14 @@ lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
|
||||
wear = 0;
|
||||
}
|
||||
|
||||
LFS_EMUBD_TRACE("lfs_emubd_wear -> %"PRIu32, wear);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_wear -> %"PRIi32, wear);
|
||||
return wear;
|
||||
}
|
||||
|
||||
int lfs_emubd_setwear(const struct lfs_config *cfg,
|
||||
lfs_block_t block, lfs_emubd_wear_t wear) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32")", (void*)cfg, block);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32", %"PRIi32")",
|
||||
(void*)cfg, block, wear);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if block is valid
|
||||
@@ -599,12 +600,12 @@ int lfs_emubd_setwear(const struct lfs_config *cfg,
|
||||
// set the wear
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
if (!b) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %"PRIu32, LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
b->wear = wear;
|
||||
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %"PRIu32, 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
47
lfs.h
47
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 0x00020005
|
||||
#define LFS_VERSION 0x00020006
|
||||
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
|
||||
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
|
||||
|
||||
// Version of On-disk data structures
|
||||
// Major (top-nibble), incremented on backwards incompatible changes
|
||||
// Minor (bottom-nibble), incremented on feature additions
|
||||
#define LFS_DISK_VERSION 0x00020000
|
||||
#define LFS_DISK_VERSION 0x00020001
|
||||
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
|
||||
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
|
||||
|
||||
@@ -112,6 +112,8 @@ enum lfs_type {
|
||||
LFS_TYPE_SOFTTAIL = 0x600,
|
||||
LFS_TYPE_HARDTAIL = 0x601,
|
||||
LFS_TYPE_MOVESTATE = 0x7ff,
|
||||
LFS_TYPE_CCRC = 0x500,
|
||||
LFS_TYPE_FCRC = 0x5ff,
|
||||
|
||||
// internal chip sources
|
||||
LFS_FROM_NOOP = 0x000,
|
||||
@@ -261,6 +263,14 @@ 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
|
||||
@@ -278,6 +288,21 @@ struct lfs_info {
|
||||
char name[LFS_NAME_MAX+1];
|
||||
};
|
||||
|
||||
// Filesystem info structure
|
||||
struct lfs_fsinfo {
|
||||
// On-disk version.
|
||||
uint32_t disk_version;
|
||||
|
||||
// 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 {
|
||||
@@ -657,6 +682,12 @@ 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
|
||||
@@ -674,6 +705,18 @@ 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);
|
||||
|
||||
#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
|
||||
#ifdef LFS_MIGRATE
|
||||
// Attempts to migrate a previous version of littlefs
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
// System includes
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
@@ -1346,6 +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 = {
|
||||
@@ -1415,6 +1418,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 = {
|
||||
@@ -1501,6 +1507,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 = {
|
||||
@@ -1585,6 +1594,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 = {
|
||||
@@ -1767,6 +1779,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 = {
|
||||
|
||||
@@ -91,6 +91,7 @@ intmax_t test_define(size_t define);
|
||||
#define ERASE_CYCLES_i 8
|
||||
#define BADBLOCK_BEHAVIOR_i 9
|
||||
#define POWERLOSS_BEHAVIOR_i 10
|
||||
#define DISK_VERSION_i 11
|
||||
|
||||
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
|
||||
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
|
||||
@@ -103,6 +104,7 @@ 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) \
|
||||
@@ -115,9 +117,10 @@ intmax_t test_define(size_t define);
|
||||
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(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP) \
|
||||
TEST_DEF(DISK_VERSION, 0)
|
||||
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 11
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 12
|
||||
#define TEST_GEOMETRY_DEFINE_COUNT 4
|
||||
|
||||
|
||||
|
||||
@@ -107,7 +107,10 @@ def main(from_prefix, to_prefix, paths=[], *,
|
||||
elif no_renames:
|
||||
to_path = from_path
|
||||
else:
|
||||
to_path, _ = changeprefix(from_prefix, to_prefix, from_path)
|
||||
to_path = os.path.join(
|
||||
os.path.dirname(from_path),
|
||||
changeprefix(from_prefix, to_prefix,
|
||||
os.path.basename(from_path))[0])
|
||||
|
||||
# rename contents
|
||||
changefile(from_prefix, to_prefix, from_path, to_path,
|
||||
|
||||
@@ -24,6 +24,8 @@ TAG_TYPES = {
|
||||
'gstate': (0x700, 0x700),
|
||||
'movestate': (0x7ff, 0x7ff),
|
||||
'crc': (0x700, 0x500),
|
||||
'ccrc': (0x780, 0x500),
|
||||
'fcrc': (0x7ff, 0x5ff),
|
||||
}
|
||||
|
||||
class Tag:
|
||||
@@ -99,7 +101,16 @@ class Tag:
|
||||
return struct.unpack('b', struct.pack('B', self.chunk))[0]
|
||||
|
||||
def is_(self, type):
|
||||
return (self.type & TAG_TYPES[type][0]) == TAG_TYPES[type][1]
|
||||
try:
|
||||
if ' ' in type:
|
||||
type1, type3 = type.split()
|
||||
return (self.is_(type1) and
|
||||
(self.type & ~TAG_TYPES[type1][0]) == int(type3, 0))
|
||||
|
||||
return self.type == int(type, 0)
|
||||
|
||||
except (ValueError, KeyError):
|
||||
return (self.type & TAG_TYPES[type][0]) == TAG_TYPES[type][1]
|
||||
|
||||
def mkmask(self):
|
||||
return Tag(
|
||||
@@ -109,14 +120,20 @@ class Tag:
|
||||
|
||||
def chid(self, nid):
|
||||
ntag = Tag(self.type, nid, self.size)
|
||||
if hasattr(self, 'off'): ntag.off = self.off
|
||||
if hasattr(self, 'data'): ntag.data = self.data
|
||||
if hasattr(self, 'crc'): ntag.crc = self.crc
|
||||
if hasattr(self, 'off'): ntag.off = self.off
|
||||
if hasattr(self, 'data'): ntag.data = self.data
|
||||
if hasattr(self, 'ccrc'): ntag.crc = self.crc
|
||||
if hasattr(self, 'erased'): ntag.erased = self.erased
|
||||
return ntag
|
||||
|
||||
def typerepr(self):
|
||||
if self.is_('crc') and getattr(self, 'crc', 0xffffffff) != 0xffffffff:
|
||||
return 'crc (bad)'
|
||||
if (self.is_('ccrc')
|
||||
and getattr(self, 'ccrc', 0xffffffff) != 0xffffffff):
|
||||
crc_status = ' (bad)'
|
||||
elif self.is_('fcrc') and getattr(self, 'erased', False):
|
||||
crc_status = ' (era)'
|
||||
else:
|
||||
crc_status = ''
|
||||
|
||||
reverse_types = {v: k for k, v in TAG_TYPES.items()}
|
||||
for prefix in range(12):
|
||||
@@ -124,12 +141,12 @@ class Tag:
|
||||
if (mask, self.type & mask) in reverse_types:
|
||||
type = reverse_types[mask, self.type & mask]
|
||||
if prefix > 0:
|
||||
return '%s %#0*x' % (
|
||||
type, prefix//4, self.type & ((1 << prefix)-1))
|
||||
return '%s %#x%s' % (
|
||||
type, self.type & ((1 << prefix)-1), crc_status)
|
||||
else:
|
||||
return type
|
||||
return '%s%s' % (type, crc_status)
|
||||
else:
|
||||
return '%02x' % self.type
|
||||
return '%02x%s' % (self.type, crc_status)
|
||||
|
||||
def idrepr(self):
|
||||
return repr(self.id) if self.id != 0x3ff else '.'
|
||||
@@ -172,6 +189,8 @@ class MetadataPair:
|
||||
|
||||
self.rev, = struct.unpack('<I', block[0:4])
|
||||
crc = binascii.crc32(block[0:4])
|
||||
fcrctag = None
|
||||
fcrcdata = None
|
||||
|
||||
# parse tags
|
||||
corrupt = False
|
||||
@@ -182,11 +201,11 @@ class MetadataPair:
|
||||
while len(block) - off >= 4:
|
||||
ntag, = struct.unpack('>I', block[off:off+4])
|
||||
|
||||
tag = Tag(int(tag) ^ ntag)
|
||||
tag = Tag((int(tag) ^ ntag) & 0x7fffffff)
|
||||
tag.off = off + 4
|
||||
tag.data = block[off+4:off+tag.dsize]
|
||||
if tag.is_('crc'):
|
||||
crc = binascii.crc32(block[off:off+4+4], crc)
|
||||
if tag.is_('ccrc'):
|
||||
crc = binascii.crc32(block[off:off+2*4], crc)
|
||||
else:
|
||||
crc = binascii.crc32(block[off:off+tag.dsize], crc)
|
||||
tag.crc = crc
|
||||
@@ -194,16 +213,29 @@ class MetadataPair:
|
||||
|
||||
self.all_.append(tag)
|
||||
|
||||
if tag.is_('crc'):
|
||||
if tag.is_('fcrc') and len(tag.data) == 8:
|
||||
fcrctag = tag
|
||||
fcrcdata = struct.unpack('<II', tag.data)
|
||||
elif tag.is_('ccrc'):
|
||||
# is valid commit?
|
||||
if crc != 0xffffffff:
|
||||
corrupt = True
|
||||
if not corrupt:
|
||||
self.log = self.all_.copy()
|
||||
# end of commit?
|
||||
if fcrcdata:
|
||||
fcrcsize, fcrc = fcrcdata
|
||||
fcrc_ = 0xffffffff ^ binascii.crc32(
|
||||
block[off:off+fcrcsize])
|
||||
if fcrc_ == fcrc:
|
||||
fcrctag.erased = True
|
||||
corrupt = True
|
||||
|
||||
# reset tag parsing
|
||||
crc = 0
|
||||
tag = Tag(int(tag) ^ ((tag.type & 1) << 31))
|
||||
fcrctag = None
|
||||
fcrcdata = None
|
||||
|
||||
# find active ids
|
||||
self.ids = list(it.takewhile(
|
||||
@@ -280,7 +312,7 @@ class MetadataPair:
|
||||
f.write('\n')
|
||||
|
||||
for tag in tags:
|
||||
f.write("%08x: %08x %-13s %4s %4s" % (
|
||||
f.write("%08x: %08x %-14s %3s %4s" % (
|
||||
tag.off, tag,
|
||||
tag.typerepr(), tag.idrepr(), tag.sizerepr()))
|
||||
if truncate:
|
||||
|
||||
1453
tests/test_compat.toml
Normal file
1453
tests/test_compat.toml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -810,7 +810,8 @@ code = '''
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
for (int j = 2; j < COUNT; j++) {
|
||||
// try seeking to each dir entry
|
||||
for (int j = 0; j < COUNT; j++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "hello") => 0;
|
||||
@@ -822,16 +823,15 @@ 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,6 +861,52 @@ 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]
|
||||
@@ -877,7 +923,7 @@ code = '''
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
for (int j = 2; j < COUNT; j++) {
|
||||
for (int j = 0; j < COUNT; j++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "/") => 0;
|
||||
@@ -889,16 +935,15 @@ 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];
|
||||
@@ -928,5 +973,51 @@ 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;
|
||||
'''
|
||||
|
||||
|
||||
@@ -59,6 +59,150 @@ 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
|
||||
|
||||
185
tests/test_powerloss.toml
Normal file
185
tests/test_powerloss.toml
Normal file
@@ -0,0 +1,185 @@
|
||||
# There are already a number of tests that test general operations under
|
||||
# power-loss (see the reentrant attribute). These tests are for explicitly
|
||||
# testing specific corner cases.
|
||||
|
||||
# only a revision count
|
||||
[cases.test_powerloss_only_rev]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mkdir(&lfs, "notebook") => 0;
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "notebook/paper",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
char buffer[256];
|
||||
strcpy(buffer, "hello");
|
||||
lfs_size_t size = strlen("hello");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
char rbuffer[256];
|
||||
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// get pair/rev count
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "notebook") => 0;
|
||||
lfs_block_t pair[2] = {dir.m.pair[0], dir.m.pair[1]};
|
||||
uint32_t rev = dir.m.rev;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// write just the revision count
|
||||
uint8_t bbuffer[BLOCK_SIZE];
|
||||
cfg->read(cfg, pair[1], 0, bbuffer, BLOCK_SIZE) => 0;
|
||||
|
||||
memcpy(bbuffer, &(uint32_t){lfs_tole32(rev+1)}, sizeof(uint32_t));
|
||||
|
||||
cfg->erase(cfg, pair[1]) => 0;
|
||||
cfg->prog(cfg, pair[1], 0, bbuffer, BLOCK_SIZE) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
|
||||
// can read?
|
||||
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
// can write?
|
||||
lfs_file_open(&lfs, &file, "notebook/paper",
|
||||
LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
||||
strcpy(buffer, "goodbye");
|
||||
size = strlen("goodbye");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
|
||||
strcpy(buffer, "hello");
|
||||
size = strlen("hello");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
strcpy(buffer, "goodbye");
|
||||
size = strlen("goodbye");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# partial prog, may not be byte in order!
|
||||
[cases.test_powerloss_partial_prog]
|
||||
if = '''
|
||||
PROG_SIZE < BLOCK_SIZE
|
||||
&& (DISK_VERSION == 0 || DISK_VERSION >= 0x00020001)
|
||||
'''
|
||||
defines.BYTE_OFF = ["0", "PROG_SIZE-1", "PROG_SIZE/2"]
|
||||
defines.BYTE_VALUE = [0x33, 0xcc]
|
||||
in = "lfs.c"
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mkdir(&lfs, "notebook") => 0;
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "notebook/paper",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
char buffer[256];
|
||||
strcpy(buffer, "hello");
|
||||
lfs_size_t size = strlen("hello");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
char rbuffer[256];
|
||||
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// imitate a partial prog, value should not matter, if littlefs
|
||||
// doesn't notice the partial prog testbd will assert
|
||||
|
||||
// get offset to next prog
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "notebook") => 0;
|
||||
lfs_block_t block = dir.m.pair[0];
|
||||
lfs_off_t off = dir.m.off;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// tweak byte
|
||||
uint8_t bbuffer[BLOCK_SIZE];
|
||||
cfg->read(cfg, block, 0, bbuffer, BLOCK_SIZE) => 0;
|
||||
|
||||
bbuffer[off + BYTE_OFF] = BYTE_VALUE;
|
||||
|
||||
cfg->erase(cfg, block) => 0;
|
||||
cfg->prog(cfg, block, 0, bbuffer, BLOCK_SIZE) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
|
||||
// can read?
|
||||
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
// can write?
|
||||
lfs_file_open(&lfs, &file, "notebook/paper",
|
||||
LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
||||
strcpy(buffer, "goodbye");
|
||||
size = strlen("goodbye");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
|
||||
strcpy(buffer, "hello");
|
||||
size = strlen("hello");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
strcpy(buffer, "goodbye");
|
||||
size = strlen("goodbye");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_read(&lfs, &file, rbuffer, size) => size;
|
||||
assert(memcmp(rbuffer, buffer, size) == 0);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
@@ -34,6 +34,54 @@ 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]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# simple truncate
|
||||
[cases.test_truncate_simple]
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
defines.LARGESIZE = 8192
|
||||
defines.MEDIUMSIZE = [31, 32, 33, 511, 512, 513, 2047, 2048, 2049]
|
||||
defines.LARGESIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
if = 'MEDIUMSIZE < LARGESIZE'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -14,7 +15,8 @@ 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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
|
||||
@@ -37,8 +39,9 @@ code = '''
|
||||
|
||||
size = strlen("hair");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, 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) => 0;
|
||||
|
||||
@@ -48,8 +51,9 @@ code = '''
|
||||
|
||||
# truncate and read
|
||||
[cases.test_truncate_read]
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
defines.LARGESIZE = 8192
|
||||
defines.MEDIUMSIZE = [31, 32, 33, 511, 512, 513, 2047, 2048, 2049]
|
||||
defines.LARGESIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
if = 'MEDIUMSIZE < LARGESIZE'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -62,7 +66,8 @@ 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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
|
||||
@@ -78,8 +83,9 @@ code = '''
|
||||
|
||||
size = strlen("hair");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, 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) => 0;
|
||||
|
||||
@@ -92,8 +98,9 @@ code = '''
|
||||
|
||||
size = strlen("hair");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, 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) => 0;
|
||||
|
||||
@@ -148,7 +155,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;
|
||||
@@ -159,8 +166,9 @@ code = '''
|
||||
|
||||
# truncate and write
|
||||
[cases.test_truncate_write]
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
defines.LARGESIZE = 8192
|
||||
defines.MEDIUMSIZE = [31, 32, 33, 511, 512, 513, 2047, 2048, 2049]
|
||||
defines.LARGESIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
if = 'MEDIUMSIZE < LARGESIZE'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -173,7 +181,8 @@ 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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => LARGESIZE;
|
||||
|
||||
@@ -184,13 +193,16 @@ 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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
|
||||
@@ -203,8 +215,9 @@ code = '''
|
||||
|
||||
size = strlen("bald");
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "bald", size) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, 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) => 0;
|
||||
|
||||
@@ -215,7 +228,7 @@ code = '''
|
||||
# truncate write under powerloss
|
||||
[cases.test_truncate_reentrant_write]
|
||||
defines.SMALLSIZE = [4, 512]
|
||||
defines.MEDIUMSIZE = [32, 1024]
|
||||
defines.MEDIUMSIZE = [0, 3, 4, 5, 31, 32, 33, 511, 512, 513, 1023, 1024, 1025]
|
||||
defines.LARGESIZE = 2048
|
||||
reentrant = true
|
||||
code = '''
|
||||
@@ -236,10 +249,11 @@ 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, 4) => 4;
|
||||
assert(memcmp(buffer, "hair", 4) == 0 ||
|
||||
memcmp(buffer, "bald", 4) == 0 ||
|
||||
memcmp(buffer, "comb", 4) == 0);
|
||||
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_close(&lfs, &file) => 0;
|
||||
}
|
||||
@@ -251,19 +265,23 @@ 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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
|
||||
=> lfs_min(size, LARGESIZE-j);
|
||||
}
|
||||
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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
@@ -275,7 +293,8 @@ 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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, SMALLSIZE-j))
|
||||
=> lfs_min(size, SMALLSIZE-j);
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => SMALLSIZE;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
@@ -429,7 +448,7 @@ code = '''
|
||||
|
||||
# noop truncate
|
||||
[cases.test_truncate_nop]
|
||||
defines.MEDIUMSIZE = [32, 2048]
|
||||
defines.MEDIUMSIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -442,10 +461,11 @@ 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, size) => size;
|
||||
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
|
||||
=> lfs_min(size, MEDIUMSIZE-j);
|
||||
|
||||
// this truncate should do nothing
|
||||
lfs_file_truncate(&lfs, &file, j+size) => 0;
|
||||
lfs_file_truncate(&lfs, &file, j+lfs_min(size, MEDIUMSIZE-j)) => 0;
|
||||
}
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
|
||||
@@ -455,8 +475,9 @@ code = '''
|
||||
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
|
||||
|
||||
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, 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) => 0;
|
||||
|
||||
@@ -468,8 +489,9 @@ 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, size) => size;
|
||||
memcmp(buffer, "hair", size) => 0;
|
||||
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) => 0;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
|
||||
Reference in New Issue
Block a user