mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-11-16 12:34:34 +00:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
630a0d87c2 | ||
|
|
3d0386489b | ||
|
|
b8e4433b34 | ||
|
|
dae656aa53 | ||
|
|
469c863c18 | ||
|
|
215613e41f | ||
|
|
2fcecc8894 | ||
|
|
78f9a5fcd3 | ||
|
|
83fe41b605 | ||
|
|
d7a911923b | ||
|
|
2ba4280a5e | ||
|
|
c961e1fe66 | ||
|
|
bd01a4c0ee | ||
|
|
999ef6656f | ||
|
|
b735c8fd7f | ||
|
|
30947054d4 | ||
|
|
80ca1ea300 | ||
|
|
815f0d85a5 | ||
|
|
dc92dec6d3 | ||
|
|
a6035071be | ||
|
|
232e736aae | ||
|
|
0de0389c6f | ||
|
|
1407db9556 | ||
|
|
ea431bd6ae | ||
|
|
2d62d2f4c9 | ||
|
|
1f82c0f27f | ||
|
|
a2c2e49e6b | ||
|
|
abaec45652 | ||
|
|
f1c430e779 | ||
|
|
4a845be0be | ||
|
|
e1636d05ab | ||
|
|
b78afe2518 | ||
|
|
798073c2a7 | ||
|
|
7db9e1663a | ||
|
|
2c4b262c35 | ||
|
|
72a4b57f4e | ||
|
|
6e7269890a | ||
|
|
ac207586ba | ||
|
|
d01280e649 | ||
|
|
6e52140d51 | ||
|
|
0bbb8bc88b | ||
|
|
78082336e7 | ||
|
|
8336ecd203 | ||
|
|
68d28b5114 | ||
|
|
1bc14933b7 | ||
|
|
01b6a47ea8 | ||
|
|
749a45650f | ||
|
|
11b036cc6c | ||
|
|
25ee90fdf1 | ||
|
|
a60a986c9c | ||
|
|
4dd30c1b8f | ||
|
|
5c0d332ecd | ||
|
|
cf68333a55 | ||
|
|
7873d811a0 | ||
|
|
fc2aa3350c | ||
|
|
6352185949 | ||
|
|
f2a6f45eef | ||
|
|
2752d8c486 | ||
|
|
ddbfcaa722 | ||
|
|
f53a0cc961 | ||
|
|
42910bc8e5 | ||
|
|
a3e1d12ce1 | ||
|
|
a70870c628 | ||
|
|
ceb17a0f4a | ||
|
|
a8a0905777 | ||
|
|
13d78616fe | ||
|
|
8b8fd14187 | ||
|
|
09972a1710 | ||
|
|
ed7bd05435 | ||
|
|
b5cd957f42 | ||
|
|
1195d606ae | ||
|
|
1711bdef76 | ||
|
|
f522ed907a | ||
|
|
4f32738cd6 | ||
|
|
6691718b18 | ||
|
|
1fefcbbcba | ||
|
|
60567677b9 | ||
|
|
897b571318 | ||
|
|
b1b10c0e75 | ||
|
|
1f9c3c04b1 | ||
|
|
7b68441888 | ||
|
|
9a620c730c | ||
|
|
a0c6c54345 | ||
|
|
c531a5e88f | ||
|
|
8f9427dd53 |
2
.github/workflows/post-release.yml
vendored
2
.github/workflows/post-release.yml
vendored
@@ -10,7 +10,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
post-release:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# trigger post-release in dependency repo, this indirection allows the
|
||||
# dependency repo to be updated often without affecting this repo. At
|
||||
|
||||
31
.github/workflows/release.yml
vendored
31
.github/workflows/release.yml
vendored
@@ -11,7 +11,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# need to manually check for a couple things
|
||||
# - tests passed?
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
github.event.workflow_run.head_sha == github.sha}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{github.event.workflow_run.head_sha}}
|
||||
# need workflow access since we push branches
|
||||
@@ -30,26 +30,29 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
# try to get results from tests
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: ${{github.event.workflow_run.name}}
|
||||
run_id: ${{github.event.workflow_run.id}}
|
||||
name: sizes
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
run-id: ${{github.event.workflow_run.id}}
|
||||
pattern: '{sizes,sizes-*}'
|
||||
merge-multiple: true
|
||||
path: sizes
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: ${{github.event.workflow_run.name}}
|
||||
run_id: ${{github.event.workflow_run.id}}
|
||||
name: cov
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
run-id: ${{github.event.workflow_run.id}}
|
||||
pattern: '{cov,cov-*}'
|
||||
merge-multiple: true
|
||||
path: cov
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: ${{github.event.workflow_run.name}}
|
||||
run_id: ${{github.event.workflow_run.id}}
|
||||
name: bench
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
run-id: ${{github.event.workflow_run.id}}
|
||||
pattern: '{bench,bench-*}'
|
||||
merge-multiple: true
|
||||
path: bench
|
||||
|
||||
- name: find-version
|
||||
|
||||
22
.github/workflows/status.yml
vendored
22
.github/workflows/status.yml
vendored
@@ -11,14 +11,15 @@ defaults:
|
||||
jobs:
|
||||
# forward custom statuses
|
||||
status:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: ${{github.event.workflow_run.name}}
|
||||
run_id: ${{github.event.workflow_run.id}}
|
||||
name: status
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
run-id: ${{github.event.workflow_run.id}}
|
||||
pattern: '{status,status-*}'
|
||||
merge-multiple: true
|
||||
path: status
|
||||
- name: update-status
|
||||
continue-on-error: true
|
||||
@@ -60,19 +61,20 @@ jobs:
|
||||
|
||||
# forward custom pr-comments
|
||||
comment:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# only run on success (we don't want garbage comments!)
|
||||
if: ${{github.event.workflow_run.conclusion == 'success'}}
|
||||
|
||||
steps:
|
||||
# generated comment?
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: ${{github.event.workflow_run.name}}
|
||||
run_id: ${{github.event.workflow_run.id}}
|
||||
name: comment
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
run-id: ${{github.event.workflow_run.id}}
|
||||
pattern: '{comment,comment-*}'
|
||||
merge-multiple: true
|
||||
path: comment
|
||||
- name: update-comment
|
||||
continue-on-error: true
|
||||
|
||||
95
.github/workflows/test.yml
vendored
95
.github/workflows/test.yml
vendored
@@ -14,14 +14,14 @@ env:
|
||||
jobs:
|
||||
# run tests
|
||||
test:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [x86_64, thumb, mips, powerpc]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -235,9 +235,9 @@ jobs:
|
||||
|
||||
# create size statuses
|
||||
- name: upload-sizes
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sizes
|
||||
name: sizes-${{matrix.arch}}
|
||||
path: sizes
|
||||
- name: status-sizes
|
||||
run: |
|
||||
@@ -273,16 +273,16 @@ jobs:
|
||||
}' | tee status/$(basename $f .csv).json
|
||||
done
|
||||
- name: upload-status-sizes
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: status
|
||||
name: status-sizes-${{matrix.arch}}
|
||||
path: status
|
||||
retention-days: 1
|
||||
|
||||
# create cov statuses
|
||||
- name: upload-cov
|
||||
if: ${{matrix.arch == 'x86_64'}}
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cov
|
||||
path: cov
|
||||
@@ -317,11 +317,11 @@ jobs:
|
||||
target_step: env.STEP,
|
||||
}' | tee status/$(basename $f .csv)-$s.json
|
||||
done
|
||||
- name: upload-status-sizes
|
||||
- name: upload-status-cov
|
||||
if: ${{matrix.arch == 'x86_64'}}
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: status
|
||||
name: status-cov
|
||||
path: status
|
||||
retention-days: 1
|
||||
|
||||
@@ -329,14 +329,14 @@ jobs:
|
||||
#
|
||||
# this grows exponentially, so it doesn't turn out to be that many
|
||||
test-pls:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pls: [1, 2]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -359,9 +359,9 @@ jobs:
|
||||
|
||||
# run with LFS_NO_INTRINSICS to make sure that works
|
||||
test-no-intrinsics:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -376,9 +376,9 @@ jobs:
|
||||
|
||||
# run LFS_MULTIVERSION tests
|
||||
test-multiversion:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -393,9 +393,9 @@ jobs:
|
||||
|
||||
# run tests on the older version lfs2.0
|
||||
test-lfs2_0:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -412,9 +412,9 @@ jobs:
|
||||
|
||||
# run under Valgrind to check for memory errors
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -434,9 +434,9 @@ jobs:
|
||||
# test that compilation is warning free under clang
|
||||
# run with Clang, mostly to check for Clang-specific warnings
|
||||
test-clang:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -457,9 +457,9 @@ jobs:
|
||||
#
|
||||
# note there's no real benefit to running these on multiple archs
|
||||
bench:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -491,7 +491,7 @@ jobs:
|
||||
|
||||
# create bench statuses
|
||||
- name: upload-bench
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bench
|
||||
path: bench
|
||||
@@ -525,20 +525,20 @@ jobs:
|
||||
}' | tee status/$(basename $f .csv)-$s.json
|
||||
done
|
||||
- name: upload-status-bench
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: status
|
||||
name: status-bench
|
||||
path: status
|
||||
retention-days: 1
|
||||
|
||||
# run compatibility tests using the current master as the previous version
|
||||
test-compat:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
# checkout the current pr target into lfsp
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
with:
|
||||
ref: ${{github.event.pull_request.base.ref}}
|
||||
@@ -569,10 +569,10 @@ jobs:
|
||||
|
||||
# self-host with littlefs-fuse for a fuzz-like test
|
||||
fuse:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{!endsWith(github.ref, '-prefix')}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -582,7 +582,7 @@ jobs:
|
||||
gcc --version
|
||||
python3 --version
|
||||
fusermount -V
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: littlefs-project/littlefs-fuse
|
||||
ref: v2
|
||||
@@ -619,10 +619,10 @@ jobs:
|
||||
|
||||
# test migration using littlefs-fuse
|
||||
migrate:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{!endsWith(github.ref, '-prefix')}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
@@ -632,12 +632,12 @@ jobs:
|
||||
gcc --version
|
||||
python3 --version
|
||||
fusermount -V
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: littlefs-project/littlefs-fuse
|
||||
ref: v2
|
||||
path: v2
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: littlefs-project/littlefs-fuse
|
||||
ref: v1
|
||||
@@ -691,10 +691,10 @@ jobs:
|
||||
|
||||
# status related tasks that run after tests
|
||||
status:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, bench]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
- name: install
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
@@ -704,23 +704,26 @@ jobs:
|
||||
pip3 install toml
|
||||
gcc --version
|
||||
python3 --version
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: sizes
|
||||
pattern: '{sizes,sizes-*}'
|
||||
merge-multiple: true
|
||||
path: sizes
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: cov
|
||||
pattern: '{cov,cov-*}'
|
||||
merge-multiple: true
|
||||
path: cov
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{github.event_name == 'pull_request'}}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: bench
|
||||
pattern: '{bench,bench-*}'
|
||||
merge-multiple: true
|
||||
path: bench
|
||||
|
||||
# try to find results from tests
|
||||
@@ -862,7 +865,7 @@ jobs:
|
||||
body: $comment,
|
||||
}' | tee comment/comment.json
|
||||
- name: upload-comment
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: comment
|
||||
path: comment
|
||||
|
||||
42
README.md
42
README.md
@@ -231,11 +231,31 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
to use littlefs in a Rust-friendly API, reaping the benefits of Rust's memory
|
||||
safety and other guarantees.
|
||||
|
||||
- [nim-littlefs] - A Nim wrapper and API for littlefs. Includes a fuse
|
||||
implementation based on [littlefs-fuse]
|
||||
|
||||
- [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.
|
||||
|
||||
- [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.
|
||||
- [mklfs] - A command line tool for creating littlefs images. Used in the Lua
|
||||
RTOS ecosystem.
|
||||
|
||||
- [mklittlefs] - A command line tool for creating littlefs images. Used in the
|
||||
ESP8266 and RP2040 ecosystem.
|
||||
|
||||
- [pico-littlefs-usb] - An interface for littlefs that emulates a FAT12
|
||||
filesystem over USB. Allows mounting littlefs on a host PC without additional
|
||||
drivers.
|
||||
|
||||
- [ramcrc32bd] - An example block device using littlefs's 32-bit CRC for
|
||||
error-correction.
|
||||
|
||||
- [ramrsbd] - An example block device using Reed-Solomon codes for
|
||||
error-correction.
|
||||
|
||||
- [Mbed OS] - The easiest way to get started with littlefs is to jump into Mbed
|
||||
which already has block device drivers for most forms of embedded storage.
|
||||
@@ -254,23 +274,23 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
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
|
||||
[littlefs-js-demo]:http://littlefs.geky.net/demo.html
|
||||
[littlefs-python]: https://pypi.org/project/littlefs-python/
|
||||
[littlefs2-rust]: https://crates.io/crates/littlefs2
|
||||
[nim-littlefs]: https://github.com/Graveflo/nim-littlefs
|
||||
[chamelon]: https://github.com/yomimono/chamelon
|
||||
[littlefs-disk-img-viewer]: https://github.com/tniessen/littlefs-disk-img-viewer
|
||||
[mklfs]: https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src
|
||||
[Lua RTOS]: https://github.com/whitecatboard/Lua-RTOS-ESP32
|
||||
[mklittlefs]: https://github.com/earlephilhower/mklittlefs
|
||||
[pico-littlefs-usb]: https://github.com/oyama/pico-littlefs-usb
|
||||
[ramcrc32bd]: https://github.com/geky/ramcrc32bd
|
||||
[ramrsbd]: https://github.com/geky/ramrsbd
|
||||
[Mbed OS]: https://github.com/armmbed/mbed-os
|
||||
[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
|
||||
|
||||
7
SPEC.md
7
SPEC.md
@@ -441,9 +441,10 @@ Superblock fields:
|
||||
|
||||
7. **Attr max (32-bits)** - Maximum size of file attributes in bytes.
|
||||
|
||||
The superblock must always be the first entry (id 0) in a metadata pair as well
|
||||
as be the first entry written to the block. This means that the superblock
|
||||
entry can be read from a device using offsets alone.
|
||||
The superblock must always be the first entry (id 0) in the metadata pair, and
|
||||
the name tag must always be the first tag in the metadata pair. This makes it
|
||||
so that the magic string "littlefs" will always reside at offset=8 in a valid
|
||||
littlefs superblock.
|
||||
|
||||
---
|
||||
#### `0x2xx` LFS_TYPE_STRUCT
|
||||
|
||||
114
bd/lfs_emubd.c
114
bd/lfs_emubd.c
@@ -129,6 +129,8 @@ int lfs_emubd_create(const struct lfs_config *cfg,
|
||||
bd->proged = 0;
|
||||
bd->erased = 0;
|
||||
bd->power_cycles = bd->cfg->power_cycles;
|
||||
bd->ooo_block = -1;
|
||||
bd->ooo_data = NULL;
|
||||
bd->disk = NULL;
|
||||
|
||||
if (bd->cfg->disk_path) {
|
||||
@@ -195,6 +197,7 @@ int lfs_emubd_destroy(const struct lfs_config *cfg) {
|
||||
free(bd->blocks);
|
||||
|
||||
// clean up other resources
|
||||
lfs_emubd_decblock(bd->ooo_data);
|
||||
if (bd->disk) {
|
||||
bd->disk->rc -= 1;
|
||||
if (bd->disk->rc == 0) {
|
||||
@@ -209,6 +212,75 @@ int lfs_emubd_destroy(const struct lfs_config *cfg) {
|
||||
}
|
||||
|
||||
|
||||
// powerloss hook
|
||||
static int lfs_emubd_powerloss(const struct lfs_config *cfg) {
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// emulate out-of-order writes?
|
||||
lfs_emubd_block_t *ooo_data = NULL;
|
||||
if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
|
||||
&& bd->ooo_block != -1) {
|
||||
// since writes between syncs are allowed to be out-of-order, it
|
||||
// shouldn't hurt to restore the first write on powerloss, right?
|
||||
ooo_data = bd->blocks[bd->ooo_block];
|
||||
bd->blocks[bd->ooo_block] = lfs_emubd_incblock(bd->ooo_data);
|
||||
|
||||
// mirror to disk file?
|
||||
if (bd->disk
|
||||
&& (bd->blocks[bd->ooo_block]
|
||||
|| bd->cfg->erase_value != -1)) {
|
||||
off_t res1 = lseek(bd->disk->fd,
|
||||
(off_t)bd->ooo_block*bd->cfg->erase_size,
|
||||
SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ssize_t res2 = write(bd->disk->fd,
|
||||
(bd->blocks[bd->ooo_block])
|
||||
? bd->blocks[bd->ooo_block]->data
|
||||
: bd->disk->scratch,
|
||||
bd->cfg->erase_size);
|
||||
if (res2 < 0) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// simulate power loss
|
||||
bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
|
||||
|
||||
// if we continue, undo out-of-order write emulation
|
||||
if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
|
||||
&& bd->ooo_block != -1) {
|
||||
lfs_emubd_decblock(bd->blocks[bd->ooo_block]);
|
||||
bd->blocks[bd->ooo_block] = ooo_data;
|
||||
|
||||
// mirror to disk file?
|
||||
if (bd->disk
|
||||
&& (bd->blocks[bd->ooo_block]
|
||||
|| bd->cfg->erase_value != -1)) {
|
||||
off_t res1 = lseek(bd->disk->fd,
|
||||
(off_t)bd->ooo_block*bd->cfg->erase_size,
|
||||
SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ssize_t res2 = write(bd->disk->fd,
|
||||
(bd->blocks[bd->ooo_block])
|
||||
? bd->blocks[bd->ooo_block]->data
|
||||
: bd->disk->scratch,
|
||||
bd->cfg->erase_size);
|
||||
if (res2 < 0) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// block device API
|
||||
|
||||
@@ -344,8 +416,11 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
if (bd->power_cycles > 0) {
|
||||
bd->power_cycles -= 1;
|
||||
if (bd->power_cycles == 0) {
|
||||
// simulate power loss
|
||||
bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
|
||||
int err = lfs_emubd_powerloss(cfg);
|
||||
if (err) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,10 +436,17 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
// check if erase is valid
|
||||
LFS_ASSERT(block < bd->cfg->erase_count);
|
||||
|
||||
// emulate out-of-order writes? save first write
|
||||
if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
|
||||
&& bd->ooo_block == -1) {
|
||||
bd->ooo_block = block;
|
||||
bd->ooo_data = lfs_emubd_incblock(bd->blocks[block]);
|
||||
}
|
||||
|
||||
// get the block
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
if (!b) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
|
||||
@@ -430,8 +512,11 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
if (bd->power_cycles > 0) {
|
||||
bd->power_cycles -= 1;
|
||||
if (bd->power_cycles == 0) {
|
||||
// simulate power loss
|
||||
bd->cfg->powerloss_cb(bd->cfg->powerloss_data);
|
||||
int err = lfs_emubd_powerloss(cfg);
|
||||
if (err) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,17 +526,24 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
|
||||
int lfs_emubd_sync(const struct lfs_config *cfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// do nothing
|
||||
(void)cfg;
|
||||
// emulate out-of-order writes? reset first write, writes
|
||||
// cannot be out-of-order across sync
|
||||
if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) {
|
||||
lfs_emubd_decblock(bd->ooo_data);
|
||||
bd->ooo_block = -1;
|
||||
bd->ooo_data = NULL;
|
||||
}
|
||||
|
||||
LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Additional extended API for driving test features ///
|
||||
|
||||
static int lfs_emubd_rawcrc(const struct lfs_config *cfg,
|
||||
static int lfs_emubd_crc_(const struct lfs_config *cfg,
|
||||
lfs_block_t block, uint32_t *crc) {
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
@@ -480,7 +572,7 @@ int lfs_emubd_crc(const struct lfs_config *cfg,
|
||||
lfs_block_t block, uint32_t *crc) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_crc(%p, %"PRIu32", %p)",
|
||||
(void*)cfg, block, crc);
|
||||
int err = lfs_emubd_rawcrc(cfg, block, crc);
|
||||
int err = lfs_emubd_crc_(cfg, block, crc);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err);
|
||||
return err;
|
||||
}
|
||||
@@ -491,7 +583,7 @@ int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) {
|
||||
uint32_t crc_ = 0xffffffff;
|
||||
for (lfs_block_t i = 0; i < cfg->block_count; i++) {
|
||||
uint32_t i_crc;
|
||||
int err = lfs_emubd_rawcrc(cfg, i, &i_crc);
|
||||
int err = lfs_emubd_crc_(cfg, i, &i_crc);
|
||||
if (err) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err);
|
||||
return err;
|
||||
@@ -633,6 +725,8 @@ int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
|
||||
copy->proged = bd->proged;
|
||||
copy->erased = bd->erased;
|
||||
copy->power_cycles = bd->power_cycles;
|
||||
copy->ooo_block = bd->ooo_block;
|
||||
copy->ooo_data = lfs_emubd_incblock(bd->ooo_data);
|
||||
copy->disk = bd->disk;
|
||||
if (copy->disk) {
|
||||
copy->disk->rc += 1;
|
||||
|
||||
@@ -36,17 +36,18 @@ extern "C"
|
||||
// Not that read-noop is not allowed. Read _must_ return a consistent (but
|
||||
// may be arbitrary) value on every read.
|
||||
typedef enum lfs_emubd_badblock_behavior {
|
||||
LFS_EMUBD_BADBLOCK_PROGERROR,
|
||||
LFS_EMUBD_BADBLOCK_ERASEERROR,
|
||||
LFS_EMUBD_BADBLOCK_READERROR,
|
||||
LFS_EMUBD_BADBLOCK_PROGNOOP,
|
||||
LFS_EMUBD_BADBLOCK_ERASENOOP,
|
||||
LFS_EMUBD_BADBLOCK_PROGERROR = 0, // Error on prog
|
||||
LFS_EMUBD_BADBLOCK_ERASEERROR = 1, // Error on erase
|
||||
LFS_EMUBD_BADBLOCK_READERROR = 2, // Error on read
|
||||
LFS_EMUBD_BADBLOCK_PROGNOOP = 3, // Prog does nothing silently
|
||||
LFS_EMUBD_BADBLOCK_ERASENOOP = 4, // Erase does nothing silently
|
||||
} lfs_emubd_badblock_behavior_t;
|
||||
|
||||
// Mode determining how power-loss behaves during testing. For now this
|
||||
// only supports a noop behavior, leaving the data on-disk untouched.
|
||||
typedef enum lfs_emubd_powerloss_behavior {
|
||||
LFS_EMUBD_POWERLOSS_NOOP,
|
||||
LFS_EMUBD_POWERLOSS_NOOP = 0, // Progs are atomic
|
||||
LFS_EMUBD_POWERLOSS_OOO = 1, // Blocks are written out-of-order
|
||||
} lfs_emubd_powerloss_behavior_t;
|
||||
|
||||
// Type for measuring read/program/erase operations
|
||||
@@ -152,6 +153,8 @@ typedef struct lfs_emubd {
|
||||
lfs_emubd_io_t proged;
|
||||
lfs_emubd_io_t erased;
|
||||
lfs_emubd_powercycles_t power_cycles;
|
||||
lfs_ssize_t ooo_block;
|
||||
lfs_emubd_block_t *ooo_data;
|
||||
lfs_emubd_disk_t *disk;
|
||||
|
||||
const struct lfs_emubd_config *cfg;
|
||||
|
||||
94
lfs.h
94
lfs.h
@@ -21,7 +21,7 @@ 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 0x0002000a
|
||||
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
|
||||
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
|
||||
|
||||
@@ -52,16 +52,15 @@ typedef uint32_t lfs_block_t;
|
||||
#endif
|
||||
|
||||
// Maximum size of a file in bytes, may be redefined to limit to support other
|
||||
// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
|
||||
// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return
|
||||
// incorrect values due to using signed integers. Stored in superblock and
|
||||
// must be respected by other littlefs drivers.
|
||||
// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
#ifndef LFS_FILE_MAX
|
||||
#define LFS_FILE_MAX 2147483647
|
||||
#endif
|
||||
|
||||
// Maximum size of custom attributes in bytes, may be redefined, but there is
|
||||
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022.
|
||||
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. Stored
|
||||
// in superblock and must be respected by other littlefs drivers.
|
||||
#ifndef LFS_ATTR_MAX
|
||||
#define LFS_ATTR_MAX 1022
|
||||
#endif
|
||||
@@ -205,7 +204,8 @@ struct lfs_config {
|
||||
// program sizes.
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of erasable blocks on the device.
|
||||
// Number of erasable blocks on the device. Defaults to block_count stored
|
||||
// on disk when zero.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Number of erase cycles before littlefs evicts metadata logs and moves
|
||||
@@ -226,9 +226,20 @@ struct lfs_config {
|
||||
// Size of the lookahead buffer in bytes. A larger lookahead buffer
|
||||
// increases the number of blocks found during an allocation pass. The
|
||||
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
|
||||
// can track 8 blocks. Must be a multiple of 8.
|
||||
// can track 8 blocks.
|
||||
lfs_size_t lookahead_size;
|
||||
|
||||
// Threshold for metadata compaction during lfs_fs_gc in bytes. Metadata
|
||||
// pairs that exceed this threshold will be compacted during lfs_fs_gc.
|
||||
// Defaults to ~88% block_size when zero, though the default may change
|
||||
// in the future.
|
||||
//
|
||||
// Note this only affects lfs_fs_gc. Normal compactions still only occur
|
||||
// when full.
|
||||
//
|
||||
// Set to -1 to disable metadata compaction during lfs_fs_gc.
|
||||
lfs_size_t compact_thresh;
|
||||
|
||||
// Optional statically allocated read buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *read_buffer;
|
||||
@@ -237,25 +248,24 @@ struct lfs_config {
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *prog_buffer;
|
||||
|
||||
// Optional statically allocated lookahead buffer. Must be lookahead_size
|
||||
// and aligned to a 32-bit boundary. By default lfs_malloc is used to
|
||||
// allocate this buffer.
|
||||
// Optional statically allocated lookahead buffer. Must be lookahead_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *lookahead_buffer;
|
||||
|
||||
// Optional upper limit on length of file names in bytes. No downside for
|
||||
// larger names except the size of the info struct which is controlled by
|
||||
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
|
||||
// superblock and must be respected by other littlefs drivers.
|
||||
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX or name_max stored on
|
||||
// disk when zero.
|
||||
lfs_size_t name_max;
|
||||
|
||||
// Optional upper limit on files in bytes. No downside for larger files
|
||||
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
|
||||
// in superblock and must be respected by other littlefs drivers.
|
||||
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX or file_max stored
|
||||
// on disk when zero.
|
||||
lfs_size_t file_max;
|
||||
|
||||
// Optional upper limit on custom attributes in bytes. No downside for
|
||||
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
|
||||
// LFS_ATTR_MAX when zero.
|
||||
// LFS_ATTR_MAX or attr_max stored on disk when zero.
|
||||
lfs_size_t attr_max;
|
||||
|
||||
// Optional upper limit on total space given to metadata pairs in bytes. On
|
||||
@@ -264,6 +274,15 @@ struct lfs_config {
|
||||
// Defaults to block_size when zero.
|
||||
lfs_size_t metadata_max;
|
||||
|
||||
// Optional upper limit on inlined files in bytes. Inlined files live in
|
||||
// metadata and decrease storage requirements, but may be limited to
|
||||
// improve metadata-related performance. Must be <= cache_size, <=
|
||||
// attr_max, and <= block_size/8. Defaults to the largest possible
|
||||
// inline_max when zero.
|
||||
//
|
||||
// Set to -1 to disable inlined files.
|
||||
lfs_size_t inline_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
|
||||
@@ -430,19 +449,20 @@ typedef struct lfs {
|
||||
lfs_gstate_t gdisk;
|
||||
lfs_gstate_t gdelta;
|
||||
|
||||
struct lfs_free {
|
||||
lfs_block_t off;
|
||||
struct lfs_lookahead {
|
||||
lfs_block_t start;
|
||||
lfs_block_t size;
|
||||
lfs_block_t i;
|
||||
lfs_block_t ack;
|
||||
uint32_t *buffer;
|
||||
} free;
|
||||
lfs_block_t next;
|
||||
lfs_block_t ckpoint;
|
||||
uint8_t *buffer;
|
||||
} lookahead;
|
||||
|
||||
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;
|
||||
lfs_size_t inline_max;
|
||||
|
||||
#ifdef LFS_MIGRATE
|
||||
struct lfs1 *lfs1;
|
||||
@@ -712,18 +732,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
|
||||
//
|
||||
@@ -736,6 +744,24 @@ int lfs_fs_gc(lfs_t *lfs);
|
||||
int lfs_fs_mkconsistent(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Attempt any janitorial work
|
||||
//
|
||||
// This currently:
|
||||
// 1. Calls mkconsistent if not already consistent
|
||||
// 2. Compacts metadata > compact_thresh
|
||||
// 3. Populates the block allocator
|
||||
//
|
||||
// Though additional janitorial work may be added in the future.
|
||||
//
|
||||
// Calling this function is not required, but may allow the offloading of
|
||||
// expensive janitorial work to a less time-critical code path.
|
||||
//
|
||||
// Returns a negative error code on failure. Accomplishing nothing is not
|
||||
// an error.
|
||||
int lfs_fs_gc(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Grows the filesystem to a new size, updating the superblock with the new
|
||||
// block count.
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#ifndef LFS_CONFIG
|
||||
|
||||
|
||||
// If user provides their own CRC impl we don't need this
|
||||
#ifndef LFS_CRC
|
||||
// Software CRC implementation with small lookup table
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
||||
static const uint32_t rtable[16] = {
|
||||
@@ -29,6 +31,7 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
||||
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
40
lfs_util.h
40
lfs_util.h
@@ -8,6 +8,9 @@
|
||||
#ifndef LFS_UTIL_H
|
||||
#define LFS_UTIL_H
|
||||
|
||||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
||||
#define LFS_STRINGIZE2(x) #x
|
||||
|
||||
// Users can override lfs_util.h with their own configuration by defining
|
||||
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
||||
//
|
||||
@@ -15,11 +18,26 @@
|
||||
// provided by the config file. To start, I would suggest copying lfs_util.h
|
||||
// and modifying as needed.
|
||||
#ifdef LFS_CONFIG
|
||||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
||||
#define LFS_STRINGIZE2(x) #x
|
||||
#include LFS_STRINGIZE(LFS_CONFIG)
|
||||
#else
|
||||
|
||||
// Alternatively, users can provide a header file which defines
|
||||
// macros and other things consumed by littlefs.
|
||||
//
|
||||
// For example, provide my_defines.h, which contains
|
||||
// something like:
|
||||
//
|
||||
// #include <stddef.h>
|
||||
// extern void *my_malloc(size_t sz);
|
||||
// #define LFS_MALLOC(sz) my_malloc(sz)
|
||||
//
|
||||
// And build littlefs with the header by defining LFS_DEFINES.
|
||||
// (-DLFS_DEFINES=my_defines.h)
|
||||
|
||||
#ifdef LFS_DEFINES
|
||||
#include LFS_STRINGIZE(LFS_DEFINES)
|
||||
#endif
|
||||
|
||||
// System includes
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -212,12 +230,22 @@ static inline uint32_t lfs_tobe32(uint32_t a) {
|
||||
}
|
||||
|
||||
// Calculate CRC-32 with polynomial = 0x04c11db7
|
||||
#ifdef LFS_CRC
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
||||
return LFS_CRC(crc, buffer, size)
|
||||
}
|
||||
#else
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
|
||||
#endif
|
||||
|
||||
// Allocate memory, only used if buffers are not provided to littlefs
|
||||
// Note, memory must be 64-bit aligned
|
||||
//
|
||||
// littlefs current has no alignment requirements, as it only allocates
|
||||
// byte-level buffers.
|
||||
static inline void *lfs_malloc(size_t size) {
|
||||
#ifndef LFS_NO_MALLOC
|
||||
#if defined(LFS_MALLOC)
|
||||
return LFS_MALLOC(size);
|
||||
#elif !defined(LFS_NO_MALLOC)
|
||||
return malloc(size);
|
||||
#else
|
||||
(void)size;
|
||||
@@ -227,7 +255,9 @@ static inline void *lfs_malloc(size_t size) {
|
||||
|
||||
// Deallocate memory, only used if buffers are not provided to littlefs
|
||||
static inline void lfs_free(void *p) {
|
||||
#ifndef LFS_NO_MALLOC
|
||||
#if defined(LFS_FREE)
|
||||
LFS_FREE(p);
|
||||
#elif !defined(LFS_NO_MALLOC)
|
||||
free(p);
|
||||
#else
|
||||
(void)p;
|
||||
|
||||
@@ -1321,6 +1321,9 @@ void perm_run(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
};
|
||||
|
||||
struct lfs_emubd_config bdcfg = {
|
||||
|
||||
@@ -95,11 +95,14 @@ intmax_t bench_define(size_t define);
|
||||
#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 COMPACT_THRESH_i 8
|
||||
#define METADATA_MAX_i 9
|
||||
#define INLINE_MAX_i 10
|
||||
#define BLOCK_CYCLES_i 11
|
||||
#define ERASE_VALUE_i 12
|
||||
#define ERASE_CYCLES_i 13
|
||||
#define BADBLOCK_BEHAVIOR_i 14
|
||||
#define POWERLOSS_BEHAVIOR_i 15
|
||||
|
||||
#define READ_SIZE bench_define(READ_SIZE_i)
|
||||
#define PROG_SIZE bench_define(PROG_SIZE_i)
|
||||
@@ -109,6 +112,9 @@ intmax_t bench_define(size_t define);
|
||||
#define BLOCK_COUNT bench_define(BLOCK_COUNT_i)
|
||||
#define CACHE_SIZE bench_define(CACHE_SIZE_i)
|
||||
#define LOOKAHEAD_SIZE bench_define(LOOKAHEAD_SIZE_i)
|
||||
#define COMPACT_THRESH bench_define(COMPACT_THRESH_i)
|
||||
#define METADATA_MAX bench_define(METADATA_MAX_i)
|
||||
#define INLINE_MAX bench_define(INLINE_MAX_i)
|
||||
#define BLOCK_CYCLES bench_define(BLOCK_CYCLES_i)
|
||||
#define ERASE_VALUE bench_define(ERASE_VALUE_i)
|
||||
#define ERASE_CYCLES bench_define(ERASE_CYCLES_i)
|
||||
@@ -124,6 +130,9 @@ intmax_t bench_define(size_t define);
|
||||
BENCH_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1))\
|
||||
BENCH_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
|
||||
BENCH_DEF(LOOKAHEAD_SIZE, 16) \
|
||||
BENCH_DEF(COMPACT_THRESH, 0) \
|
||||
BENCH_DEF(METADATA_MAX, 0) \
|
||||
BENCH_DEF(INLINE_MAX, 0) \
|
||||
BENCH_DEF(BLOCK_CYCLES, -1) \
|
||||
BENCH_DEF(ERASE_VALUE, 0xff) \
|
||||
BENCH_DEF(ERASE_CYCLES, 0) \
|
||||
@@ -131,7 +140,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 16
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1346,6 +1346,9 @@ static void run_powerloss_none(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
@@ -1422,6 +1425,9 @@ static void run_powerloss_linear(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
@@ -1515,6 +1521,9 @@ static void run_powerloss_log(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
@@ -1606,6 +1615,9 @@ static void run_powerloss_cycles(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
@@ -1795,6 +1807,9 @@ static void run_powerloss_exhaustive(
|
||||
.block_cycles = BLOCK_CYCLES,
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
#endif
|
||||
|
||||
@@ -88,12 +88,15 @@ intmax_t test_define(size_t define);
|
||||
#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 COMPACT_THRESH_i 8
|
||||
#define METADATA_MAX_i 9
|
||||
#define INLINE_MAX_i 10
|
||||
#define BLOCK_CYCLES_i 11
|
||||
#define ERASE_VALUE_i 12
|
||||
#define ERASE_CYCLES_i 13
|
||||
#define BADBLOCK_BEHAVIOR_i 14
|
||||
#define POWERLOSS_BEHAVIOR_i 15
|
||||
#define DISK_VERSION_i 16
|
||||
|
||||
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
|
||||
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
|
||||
@@ -103,6 +106,9 @@ intmax_t test_define(size_t define);
|
||||
#define BLOCK_COUNT TEST_DEFINE(BLOCK_COUNT_i)
|
||||
#define CACHE_SIZE TEST_DEFINE(CACHE_SIZE_i)
|
||||
#define LOOKAHEAD_SIZE TEST_DEFINE(LOOKAHEAD_SIZE_i)
|
||||
#define COMPACT_THRESH TEST_DEFINE(COMPACT_THRESH_i)
|
||||
#define METADATA_MAX TEST_DEFINE(METADATA_MAX_i)
|
||||
#define INLINE_MAX TEST_DEFINE(INLINE_MAX_i)
|
||||
#define BLOCK_CYCLES TEST_DEFINE(BLOCK_CYCLES_i)
|
||||
#define ERASE_VALUE TEST_DEFINE(ERASE_VALUE_i)
|
||||
#define ERASE_CYCLES TEST_DEFINE(ERASE_CYCLES_i)
|
||||
@@ -119,6 +125,9 @@ intmax_t test_define(size_t define);
|
||||
TEST_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1)) \
|
||||
TEST_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
|
||||
TEST_DEF(LOOKAHEAD_SIZE, 16) \
|
||||
TEST_DEF(COMPACT_THRESH, 0) \
|
||||
TEST_DEF(METADATA_MAX, 0) \
|
||||
TEST_DEF(INLINE_MAX, 0) \
|
||||
TEST_DEF(BLOCK_CYCLES, -1) \
|
||||
TEST_DEF(ERASE_VALUE, 0xff) \
|
||||
TEST_DEF(ERASE_CYCLES, 0) \
|
||||
@@ -127,7 +136,7 @@ intmax_t test_define(size_t define);
|
||||
TEST_DEF(DISK_VERSION, 0)
|
||||
|
||||
#define TEST_GEOMETRY_DEFINE_COUNT 4
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 14
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 17
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,6 +86,13 @@ def write_header(f, limit=LIMIT):
|
||||
f.writeln("}")
|
||||
f.writeln()
|
||||
f.writeln("__attribute__((unused))")
|
||||
f.writeln("static void __pretty_assert_print_ptr(")
|
||||
f.writeln(" const void *v, size_t size) {")
|
||||
f.writeln(" (void)size;")
|
||||
f.writeln(" printf(\"%p\", v);")
|
||||
f.writeln("}")
|
||||
f.writeln()
|
||||
f.writeln("__attribute__((unused))")
|
||||
f.writeln("static void __pretty_assert_print_mem(")
|
||||
f.writeln(" const void *v, size_t size) {")
|
||||
f.writeln(" const uint8_t *v_ = v;")
|
||||
@@ -183,6 +190,23 @@ def write_header(f, limit=LIMIT):
|
||||
f.writeln(" _rh, strlen(_rh)); \\")
|
||||
f.writeln(" } \\")
|
||||
f.writeln("} while (0)")
|
||||
for op, cmp in sorted(CMP.items()):
|
||||
# Only EQ and NE are supported when compared to NULL.
|
||||
if cmp not in ['eq', 'ne']:
|
||||
continue
|
||||
f.writeln("#define __PRETTY_ASSERT_PTR_%s(lh, rh) do { \\"
|
||||
% cmp.upper())
|
||||
f.writeln(" const void *_lh = (const void*)(uintptr_t)lh; \\")
|
||||
f.writeln(" const void *_rh = (const void*)(uintptr_t)rh; \\")
|
||||
f.writeln(" if (!(_lh %s _rh)) { \\" % op)
|
||||
f.writeln(" __pretty_assert_fail( \\")
|
||||
f.writeln(" __FILE__, __LINE__, \\")
|
||||
f.writeln(" __pretty_assert_print_ptr, \"%s\", \\"
|
||||
% cmp)
|
||||
f.writeln(" (const void*){_lh}, 0, \\")
|
||||
f.writeln(" (const void*){_rh}, 0); \\")
|
||||
f.writeln(" } \\")
|
||||
f.writeln("} while (0)")
|
||||
f.writeln()
|
||||
f.writeln()
|
||||
|
||||
@@ -301,6 +325,8 @@ def p_assert(p):
|
||||
cmp = p.expect('cmp') ; p.accept('ws')
|
||||
rh = p_expr(p) ; p.accept('ws')
|
||||
p.expect(')')
|
||||
if rh == 'NULL' or lh == 'NULL':
|
||||
return mkassert('ptr', CMP[cmp], lh, rh)
|
||||
return mkassert('int', CMP[cmp], lh, rh)
|
||||
except ParseFailure:
|
||||
p.pop(state)
|
||||
|
||||
@@ -7,17 +7,23 @@ if = 'BLOCK_CYCLES == -1'
|
||||
defines.FILES = 3
|
||||
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
|
||||
defines.GC = [false, true]
|
||||
defines.COMPACT_THRESH = ['-1', '0', 'BLOCK_SIZE/2']
|
||||
defines.INFER_BC = [false, true]
|
||||
code = '''
|
||||
const char *names[] = {"bacon", "eggs", "pancakes"};
|
||||
lfs_file_t files[FILES];
|
||||
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_config cfg_ = *cfg;
|
||||
if (INFER_BC) {
|
||||
cfg_.block_count = 0;
|
||||
}
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
lfs_mkdir(&lfs, "breakfast") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
@@ -38,7 +44,7 @@ code = '''
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
@@ -60,17 +66,23 @@ code = '''
|
||||
defines.FILES = 3
|
||||
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
|
||||
defines.GC = [false, true]
|
||||
defines.COMPACT_THRESH = ['-1', '0', 'BLOCK_SIZE/2']
|
||||
defines.INFER_BC = [false, true]
|
||||
code = '''
|
||||
const char *names[] = {"bacon", "eggs", "pancakes"};
|
||||
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_config cfg_ = *cfg;
|
||||
if (INFER_BC) {
|
||||
cfg_.block_count = 0;
|
||||
}
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
lfs_mkdir(&lfs, "breakfast") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
lfs_file_t file;
|
||||
@@ -89,7 +101,7 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
}
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
@@ -111,19 +123,24 @@ code = '''
|
||||
defines.FILES = 3
|
||||
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
|
||||
defines.CYCLES = [1, 10]
|
||||
defines.INFER_BC = [false, true]
|
||||
code = '''
|
||||
const char *names[] = {"bacon", "eggs", "pancakes"};
|
||||
lfs_file_t files[FILES];
|
||||
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
struct lfs_config cfg_ = *cfg;
|
||||
if (INFER_BC) {
|
||||
cfg_.block_count = 0;
|
||||
}
|
||||
|
||||
for (int c = 0; c < CYCLES; c++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
lfs_mkdir(&lfs, "breakfast") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
@@ -141,7 +158,7 @@ code = '''
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
@@ -157,7 +174,7 @@ code = '''
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
@@ -173,19 +190,24 @@ code = '''
|
||||
defines.FILES = 3
|
||||
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
|
||||
defines.CYCLES = [1, 10]
|
||||
defines.INFER_BC = [false, true]
|
||||
code = '''
|
||||
const char *names[] = {"bacon", "eggs", "pancakes"};
|
||||
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
struct lfs_config cfg_ = *cfg;
|
||||
if (INFER_BC) {
|
||||
cfg_.block_count = 0;
|
||||
}
|
||||
|
||||
for (int c = 0; c < CYCLES; c++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
lfs_mkdir(&lfs, "breakfast") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
for (int n = 0; n < FILES; n++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
char path[1024];
|
||||
sprintf(path, "breakfast/%s", names[n]);
|
||||
lfs_file_t file;
|
||||
@@ -230,10 +252,15 @@ code = '''
|
||||
|
||||
# exhaustion test
|
||||
[cases.test_alloc_exhaustion]
|
||||
defines.INFER_BC = [false, true]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_config cfg_ = *cfg;
|
||||
if (INFER_BC) {
|
||||
cfg_.block_count = 0;
|
||||
}
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size_t size = strlen("exhaustion");
|
||||
@@ -261,7 +288,7 @@ code = '''
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
|
||||
size = strlen("exhaustion");
|
||||
lfs_file_size(&lfs, &file) => size;
|
||||
@@ -274,10 +301,15 @@ code = '''
|
||||
# exhaustion wraparound test
|
||||
[cases.test_alloc_exhaustion_wraparound]
|
||||
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-4)) / 3)'
|
||||
defines.INFER_BC = [false, true]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_config cfg_ = *cfg;
|
||||
if (INFER_BC) {
|
||||
cfg_.block_count = 0;
|
||||
}
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
@@ -315,7 +347,7 @@ code = '''
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
|
||||
size = strlen("exhaustion");
|
||||
lfs_file_size(&lfs, &file) => size;
|
||||
@@ -328,10 +360,15 @@ code = '''
|
||||
|
||||
# dir exhaustion test
|
||||
[cases.test_alloc_dir_exhaustion]
|
||||
defines.INFER_BC = [false, true]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
struct lfs_config cfg_ = *cfg;
|
||||
if (INFER_BC) {
|
||||
cfg_.block_count = 0;
|
||||
}
|
||||
lfs_mount(&lfs, &cfg_) => 0;
|
||||
|
||||
// find out max file size
|
||||
lfs_mkdir(&lfs, "exhaustiondir") => 0;
|
||||
|
||||
@@ -181,6 +181,10 @@ code = '''
|
||||
defines.N = [5, 11]
|
||||
if = 'BLOCK_COUNT >= 4*N'
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
@@ -439,6 +443,10 @@ code = '''
|
||||
defines.N = [5, 25]
|
||||
if = 'N < BLOCK_COUNT/2'
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
@@ -747,6 +755,11 @@ code = '''
|
||||
lfs_file_open(&lfs, &file, "potato",
|
||||
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
|
||||
|
||||
lfs_file_open(&lfs, &file, "tacoto", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_rename(&lfs, "tacoto", "potato") => LFS_ERR_ISDIR;
|
||||
lfs_rename(&lfs, "potato", "tacoto") => LFS_ERR_NOTDIR;
|
||||
|
||||
lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST;
|
||||
lfs_file_open(&lfs, &file, "/",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
|
||||
@@ -770,6 +783,10 @@ code = '''
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, "potato") == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(strcmp(info.name, "tacoto") == 0);
|
||||
assert(info.size == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
@@ -790,6 +807,10 @@ code = '''
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, "potato") == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
assert(strcmp(info.name, "tacoto") == 0);
|
||||
assert(info.size == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
[cases.test_files_simple]
|
||||
defines.INLINE_MAX = [0, -1, 8]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -25,6 +26,7 @@ code = '''
|
||||
[cases.test_files_large]
|
||||
defines.SIZE = [32, 8192, 262144, 0, 7, 8193]
|
||||
defines.CHUNKSIZE = [31, 16, 33, 1, 1023]
|
||||
defines.INLINE_MAX = [0, -1, 8]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -67,6 +69,7 @@ code = '''
|
||||
defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
|
||||
defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
|
||||
defines.CHUNKSIZE = [31, 16, 1]
|
||||
defines.INLINE_MAX = [0, -1, 8]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -152,6 +155,7 @@ code = '''
|
||||
defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
|
||||
defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
|
||||
defines.CHUNKSIZE = [31, 16, 1]
|
||||
defines.INLINE_MAX = [0, -1, 8]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -232,6 +236,7 @@ code = '''
|
||||
defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
|
||||
defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
|
||||
defines.CHUNKSIZE = [31, 16, 1]
|
||||
defines.INLINE_MAX = [0, -1, 8]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
@@ -303,7 +308,12 @@ code = '''
|
||||
[cases.test_files_reentrant_write]
|
||||
defines.SIZE = [32, 0, 7, 2049]
|
||||
defines.CHUNKSIZE = [31, 16, 65]
|
||||
defines.INLINE_MAX = [0, -1, 8]
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
@@ -354,11 +364,20 @@ code = '''
|
||||
[cases.test_files_reentrant_write_sync]
|
||||
defines = [
|
||||
# append (O(n))
|
||||
{MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]},
|
||||
{MODE='LFS_O_APPEND',
|
||||
SIZE=[32, 0, 7, 2049],
|
||||
CHUNKSIZE=[31, 16, 65],
|
||||
INLINE_MAX=[0, -1, 8]},
|
||||
# truncate (O(n^2))
|
||||
{MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]},
|
||||
{MODE='LFS_O_TRUNC',
|
||||
SIZE=[32, 0, 7, 200],
|
||||
CHUNKSIZE=[31, 16, 65],
|
||||
INLINE_MAX=[0, -1, 8]},
|
||||
# rewrite (O(n^2))
|
||||
{MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]},
|
||||
{MODE=0,
|
||||
SIZE=[32, 0, 7, 200],
|
||||
CHUNKSIZE=[31, 16, 65],
|
||||
INLINE_MAX=[0, -1, 8]},
|
||||
]
|
||||
reentrant = true
|
||||
code = '''
|
||||
@@ -485,6 +504,10 @@ code = '''
|
||||
[cases.test_files_many_power_loss]
|
||||
defines.N = 300
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
|
||||
@@ -195,6 +195,10 @@ code = '''
|
||||
defines.SIZE = [10, 100]
|
||||
defines.FILES = [4, 10, 26]
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_file_t files[FILES];
|
||||
|
||||
@@ -357,6 +357,10 @@ code = '''
|
||||
|
||||
[cases.test_move_reentrant_file]
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
@@ -839,6 +843,10 @@ code = '''
|
||||
|
||||
[cases.test_reentrant_dir]
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
|
||||
@@ -98,7 +98,7 @@ code = '''
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// create an orphan
|
||||
lfs_mdir_t orphan;
|
||||
lfs_alloc_ack(&lfs);
|
||||
lfs_alloc_ckpoint(&lfs);
|
||||
lfs_dir_alloc(&lfs, &orphan) => 0;
|
||||
lfs_dir_commit(&lfs, &orphan, NULL, 0) => 0;
|
||||
|
||||
@@ -170,7 +170,7 @@ code = '''
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
// create an orphan
|
||||
lfs_mdir_t orphan;
|
||||
lfs_alloc_ack(&lfs);
|
||||
lfs_alloc_ckpoint(&lfs);
|
||||
lfs_dir_alloc(&lfs, &orphan) => 0;
|
||||
lfs_dir_commit(&lfs, &orphan, NULL, 0) => 0;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -329,6 +329,10 @@ code = '''
|
||||
# must be power-of-2 for quadratic probing to be exhaustive
|
||||
defines.COUNT = [4, 64, 128]
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
@@ -401,3 +405,111 @@ code = '''
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
# test possible overflow/underflow conditions
|
||||
#
|
||||
# note these need -fsanitize=undefined to consistently detect
|
||||
# overflow/underflow conditions
|
||||
|
||||
[cases.test_seek_filemax]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "kitty",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
uint8_t buffer[1024];
|
||||
strcpy((char*)buffer, "kittycatcat");
|
||||
size_t size = strlen((char*)buffer);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
// seek with LFS_SEEK_SET
|
||||
lfs_file_seek(&lfs, &file, LFS_FILE_MAX, LFS_SEEK_SET) => LFS_FILE_MAX;
|
||||
|
||||
// seek with LFS_SEEK_CUR
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => LFS_FILE_MAX;
|
||||
|
||||
// the file hasn't changed size, so seek end takes us back to the offset=0
|
||||
lfs_file_seek(&lfs, &file, +10, LFS_SEEK_END) => size+10;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_seek_underflow]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "kitty",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
uint8_t buffer[1024];
|
||||
strcpy((char*)buffer, "kittycatcat");
|
||||
size_t size = strlen((char*)buffer);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
// underflow with LFS_SEEK_CUR, should error
|
||||
lfs_file_seek(&lfs, &file, -(size+10), LFS_SEEK_CUR) => LFS_ERR_INVAL;
|
||||
lfs_file_seek(&lfs, &file, -LFS_FILE_MAX, LFS_SEEK_CUR) => LFS_ERR_INVAL;
|
||||
lfs_file_seek(&lfs, &file, -(size+LFS_FILE_MAX), LFS_SEEK_CUR)
|
||||
=> LFS_ERR_INVAL;
|
||||
|
||||
// underflow with LFS_SEEK_END, should error
|
||||
lfs_file_seek(&lfs, &file, -(size+10), LFS_SEEK_END) => LFS_ERR_INVAL;
|
||||
lfs_file_seek(&lfs, &file, -LFS_FILE_MAX, LFS_SEEK_END) => LFS_ERR_INVAL;
|
||||
lfs_file_seek(&lfs, &file, -(size+LFS_FILE_MAX), LFS_SEEK_END)
|
||||
=> LFS_ERR_INVAL;
|
||||
|
||||
// file pointer should not have changed
|
||||
lfs_file_tell(&lfs, &file) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_seek_overflow]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "kitty",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
uint8_t buffer[1024];
|
||||
strcpy((char*)buffer, "kittycatcat");
|
||||
size_t size = strlen((char*)buffer);
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
// seek to LFS_FILE_MAX
|
||||
lfs_file_seek(&lfs, &file, LFS_FILE_MAX, LFS_SEEK_SET) => LFS_FILE_MAX;
|
||||
|
||||
// overflow with LFS_SEEK_CUR, should error
|
||||
lfs_file_seek(&lfs, &file, +10, LFS_SEEK_CUR) => LFS_ERR_INVAL;
|
||||
lfs_file_seek(&lfs, &file, +LFS_FILE_MAX, LFS_SEEK_CUR) => LFS_ERR_INVAL;
|
||||
|
||||
// LFS_SEEK_SET/END don't care about the current file position, but we can
|
||||
// still overflow with a large offset
|
||||
|
||||
// overflow with LFS_SEEK_SET, should error
|
||||
lfs_file_seek(&lfs, &file,
|
||||
+((uint32_t)LFS_FILE_MAX+10),
|
||||
LFS_SEEK_SET) => LFS_ERR_INVAL;
|
||||
lfs_file_seek(&lfs, &file,
|
||||
+((uint32_t)LFS_FILE_MAX+(uint32_t)LFS_FILE_MAX),
|
||||
LFS_SEEK_SET) => LFS_ERR_INVAL;
|
||||
|
||||
// overflow with LFS_SEEK_END, should error
|
||||
lfs_file_seek(&lfs, &file, +(LFS_FILE_MAX-size+10), LFS_SEEK_END)
|
||||
=> LFS_ERR_INVAL;
|
||||
lfs_file_seek(&lfs, &file, +(LFS_FILE_MAX-size+LFS_FILE_MAX), LFS_SEEK_END)
|
||||
=> LFS_ERR_INVAL;
|
||||
|
||||
// file pointer should not have changed
|
||||
lfs_file_tell(&lfs, &file) => LFS_FILE_MAX;
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
@@ -14,6 +14,24 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# make sure the magic string "littlefs" is always at offset=8
|
||||
[cases.test_superblocks_magic]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
|
||||
// check our magic string
|
||||
//
|
||||
// note if we lose power we may not have the magic string in both blocks!
|
||||
// but we don't lose power in this test so we can assert the magic string
|
||||
// is present in both
|
||||
uint8_t magic[lfs_max(16, READ_SIZE)];
|
||||
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||
'''
|
||||
|
||||
# mount/unmount from interpretting a previous superblock block_count
|
||||
[cases.test_superblocks_mount_unknown_block_count]
|
||||
code = '''
|
||||
@@ -28,10 +46,13 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
# reentrant format
|
||||
[cases.test_superblocks_reentrant_format]
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
@@ -131,6 +152,39 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# make sure the magic string "littlefs" is always at offset=8
|
||||
[cases.test_superblocks_magic_expand]
|
||||
defines.BLOCK_CYCLES = [32, 33, 1]
|
||||
defines.N = [10, 100, 1000]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "dummy",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
struct lfs_info info;
|
||||
lfs_stat(&lfs, "dummy", &info) => 0;
|
||||
assert(strcmp(info.name, "dummy") == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
lfs_remove(&lfs, "dummy") => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
// check our magic string
|
||||
//
|
||||
// note if we lose power we may not have the magic string in both blocks!
|
||||
// but we don't lose power in this test so we can assert the magic string
|
||||
// is present in both
|
||||
uint8_t magic[lfs_max(16, READ_SIZE)];
|
||||
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||
'''
|
||||
|
||||
# expanding superblock with power cycle
|
||||
[cases.test_superblocks_expand_power_cycle]
|
||||
defines.BLOCK_CYCLES = [32, 33, 1]
|
||||
@@ -174,6 +228,10 @@ code = '''
|
||||
defines.BLOCK_CYCLES = [2, 1]
|
||||
defines.N = 24
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
@@ -213,6 +271,7 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
# mount with unknown block_count
|
||||
[cases.test_superblocks_unknown_blocks]
|
||||
code = '''
|
||||
@@ -464,3 +523,30 @@ code = '''
|
||||
assert(memcmp(buffer, "hello!", 6) == 0);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# test that metadata_max does not cause problems for superblock compaction
|
||||
[cases.test_superblocks_metadata_max]
|
||||
defines.METADATA_MAX = [
|
||||
'lfs_max(512, PROG_SIZE)',
|
||||
'lfs_max(BLOCK_SIZE/2, PROG_SIZE)',
|
||||
'BLOCK_SIZE'
|
||||
]
|
||||
defines.N = [10, 100, 1000]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
lfs_file_t file;
|
||||
char name[256];
|
||||
sprintf(name, "hello%03x", i);
|
||||
lfs_file_open(&lfs, &file, name,
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
struct lfs_info info;
|
||||
lfs_stat(&lfs, name, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
@@ -231,6 +231,10 @@ defines.SMALLSIZE = [4, 512]
|
||||
defines.MEDIUMSIZE = [0, 3, 4, 5, 31, 32, 33, 511, 512, 513, 1023, 1024, 1025]
|
||||
defines.LARGESIZE = 2048
|
||||
reentrant = true
|
||||
defines.POWERLOSS_BEHAVIOR = [
|
||||
'LFS_EMUBD_POWERLOSS_NOOP',
|
||||
'LFS_EMUBD_POWERLOSS_OOO',
|
||||
]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
int err = lfs_mount(&lfs, cfg);
|
||||
|
||||
Reference in New Issue
Block a user