Compare commits

...

175 Commits

Author SHA1 Message Date
geky-bot
4241f3bdc5 Generated v2 prefixes 2025-06-30 17:50:42 +00:00
Christopher Haster
8e251dd675 Merge pull request #1110 from Ryan-CW-Code/perf_gc
perf: gc might try to populate the lookahead buffer each time
2025-06-30 11:39:17 -05:00
Christopher Haster
25b9a4af85 Merge pull request #1109 from Ryan-CW-Code/never_read
refactor: value stored to 'diff' is never read
2025-06-30 11:39:05 -05:00
Christopher Haster
2acf939a00 Merge pull request #1106 from littlefs-project/fix-make-build-dep
make: Add missing BUILD_DEP include
2025-06-30 11:38:56 -05:00
ryancw
d5a86fd28d style: format code, limit to 80 columns. 2025-06-03 09:46:59 +08:00
ryancw
2349ac8c96 perf: gc might try to populate the lookahead buffer each time 2025-05-28 10:23:47 +08:00
ryancw
0755b00c21 refactor: value stored to 'diff' is never read 2025-05-27 20:00:29 +08:00
Christopher Haster
8365bbb7a2 make: Added missing BUILD_DEP include
This was preventing bench modifications from triggering relevant
bench-runner rebuilds.
2025-05-15 13:38:31 -05:00
geky-bot
df7bdf5df9 Generated v2 prefixes 2025-05-15 02:10:27 +00:00
Christopher Haster
16ceb67934 Merge pull request #1103 from littlefs-project/devel
Minor release: v2.11
2025-05-14 20:45:44 -05:00
Christopher Haster
8434536f0a Bumped minor version to v2.11 2025-05-13 13:18:31 -05:00
Christopher Haster
523319b685 Merge pull request #1104 from DvdGiessen/os-rename-between-filesystems
use shutil.move instead of os.rename to move file
2025-05-13 13:17:53 -05:00
Daniël van de Giessen
ba250a3075 use shutil.move instead of os.rename to move file
This prevents a "OSError: [Errno 18] Invalid cross-device link" if the temporary
file was created on different filesystem (such as a tmpfs mount).
2025-05-13 13:15:21 +02:00
Christopher Haster
8c458fa6bd Merge pull request #1094 from sosthene-nitrokey/shrink-fs
Add support for shrinking a filesystem
2025-05-13 00:45:32 -05:00
Christopher Haster
3149201ae5 Merge pull request #1091 from yamt/mach-o
adapt the linker sections usage to mach-o
2025-05-13 00:45:02 -05:00
Christopher Haster
6a43f3cdc3 Merge pull request #1090 from yamt/clang
drop a few unsupported CFLAGS for clang
2025-05-13 00:44:46 -05:00
Christopher Haster
d73fb8ef3c Merge pull request #1099 from littlefs-project/fix-remove-double-deorphan
Fix double deorphan caused by relocation mid dir remove
2025-05-13 00:44:26 -05:00
Christopher Haster
c1bf7cee84 Merge pull request #1100 from selimkeles/fix/bitshift_overflow
fix: added uint32_t cast to the bitshift places
2025-05-13 00:44:05 -05:00
Christopher Haster
b26bf3494c Merge pull request #1095 from DvdGiessen/lfs_crc
lfs_crc should be static if LFS_CRC is defined
2025-05-13 00:43:24 -05:00
Christopher Haster
0115cf6b74 gha: Dropped explicit CFLAGS from clang testing in CI
Thanks to yamt, GCC-specific flags should now be disabled if compiling
with clang. Dropping the explicit flags also doubles as a test that the
NO_GCC inference works.
2025-05-07 23:45:29 -05:00
Christopher Haster
bff4dfd1b1 Added NO_GCC to allow users to explicitly disable GCC-specific flags
This is the same as the implicit Clang => NO_GCC behavior introduced by
yamt, but with an explicit variable that can be assigned by users using
other, non-gcc, compilers:

  $ NO_GCC=1 make

Note, stack measurements are currently GCC specific:

  $ NO_GCC=1 make stack
  ... snip ...
  FileNotFoundError: [Errno 2] No such file or directory: 'lfs.ci'
  make: *** [Makefile:494: lfs.stack.csv] Error 1
2025-05-07 23:40:25 -05:00
Sosthène Guédon
edaaaf88ea Apply review comments 2025-05-07 10:38:43 +02:00
Sosthène Guédon
7d79423972 Rename SHRINKIFCHEAP to SHRINKNONRELOCATING 2025-05-07 10:34:24 +02:00
Sosthène Guédon
7782d3dfa3 Mention that shrinking is unlikely to work 2025-05-06 11:00:29 +02:00
selim.keles
f4a1bb328a fix: added uint32_t cast to the bitshift places
In 16 bit and 8 bit architectures, overflow and underflow issues were occuring while using functions lfs_frombe32 and lfs_fromle32
2025-05-05 13:38:05 +03:00
Sosthène Guédon
9b8f802b43 fixup! Add support for shrinking a filesystem 2025-05-05 11:37:39 +02:00
Christopher Haster
a3d6bec5f0 Fixed a double deorphan caused by relocation mid dir remove
Long story short: There is a specific case where removing a directory
can trigger a deorphan pass, but lfs_remove did not check for this,
would try to clean up the (already cleaned) directory orphan, and
trigger an assert:

  lfs.c:4890:assert: assert failed with false, expected eq true
      LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0x000 || orphans >= 0);

The specific case being a remove commit that triggers a relocation that
creates an orphan.

This is also possible in lfs_rename, but only if you're renaming a
directory that implies a remove, which is a pretty rare operation.

---

This was probably an oversight introduced in the non-recursive commit
logic rework.

Fortunately the fix is to just check if we even have an orphan before
trying to remove it. We can rely on this instead of the file type, so
this fix shouldn't even increase the code size.

Found and root-caused by Hugh-Baoa
2025-05-03 18:13:19 -05:00
Christopher Haster
0634d13e07 tests: Added non-reentrant variants of orphan/relocation tests
These are the same as the related reentrant variants, but by opting out
of powerloss testing, we can test a much larger number of states without
having to worry about the impact on powerloss testing runtime.

Bumped CYCLES from 20 -> 2000.

This reveals an orphan remove bug found by Hugh-Baoa.
2025-05-03 17:15:26 -05:00
Sosthène Guédon
2105e502c5 Add support for shrinking a filesystem
This PR adds a new `lfs_fs_shrink`, which functions similarly to
`lfs_fs_grow`, but supports reducing the block count.

This functions first checks that none of the removed block are in use.
If it is the case, it will fail.
2025-04-17 10:07:37 +02:00
Daniël van de Giessen
b823728420 lfs_crc should be static if LFS_CRC is defined 2025-04-16 18:17:21 +02:00
YAMAMOTO Takashi
0d861b7916 adapt the linker sections usage to mach-o
"make test" on macOS:

```
using runner: ./runners/test_runner
found 19 suites, 188 cases, 11242/11770 permutations

running test_alloc: 12/12 cases, 207/207 perms
running test_attrs: 4/4 cases, 20/20 perms
running test_badblocks: 4/4 cases, 300/300 perms
running test_bd: 5/5 cases, 85/85 perms
running test_compat: 17/17 cases, 205/205 perms
running test_dirs: 15/15 cases, 450/450 perms, 1756pls!
running test_entries: 8/8 cases, 32/32 perms
running test_evil: 8/8 cases, 105/105 perms
running test_exhaustion: 5/5 cases, 85/85 perms
running test_files: 10/10 cases, 7155/7155 perms, 9410pls!
running test_interspersed: 4/4 cases, 190/190 perms, 2835pls!
running test_move: 17/17 cases, 161/161 perms, 157pls!
running test_orphans: 6/6 cases, 50/50 perms, 846pls!
running test_paths: 33/33 cases, 325/325 perms
running test_powerloss: 2/2 cases, 21/21 perms
running test_relocations: 4/4 cases, 68/68 perms, 1612pls!
running test_seek: 10/10 cases, 195/195 perms, 1050pls!
running test_superblocks: 17/17 cases, 318/318 perms, 1437pls!
running test_truncate: 7/7 cases, 1270/1270 perms, 9691pls!

done: 11242/11242 passed, 0/11242 failed, 28794pls!, in 585.76s
```
2025-04-07 16:20:23 +09:00
YAMAMOTO Takashi
26bee8ad36 drop a few unsupported CFLAGS for clang 2025-04-07 16:06:01 +09:00
geky-bot
2e1df682c7 Generated v2 prefixes 2025-03-20 07:53:27 +00:00
Christopher Haster
8ed63b27be Merge pull request #1084 from elupus/fix/packing
fix: avoid assuming struct packing
2025-03-20 01:26:11 -05:00
Christopher Haster
a666730044 Merge pull request #1078 from BrianPugh/unit-test-readme
Add a little bit of documentation on how to run tests.
2025-03-20 01:25:56 -05:00
Christopher Haster
47e738b788 Merge pull request #1071 from RocLoong/patch-1
print lfs_file_size overflow
2025-03-20 01:25:33 -05:00
Christopher Haster
81b0db0cdc Merge pull request #1070 from Noxet/filebd-wrong-cast
Changed cast to correct type when trace is enabled for filebd
2025-03-20 01:24:19 -05:00
Christopher Haster
63ab1ffb65 Merge pull request #1068 from littlefs-project/fix-dir-remove-read
Fix dir iteration being broken by concurrent removes
2025-03-20 01:24:04 -05:00
Christopher Haster
ca1081e7c4 Merge pull request #1065 from amubiera/fix-unsafe-use-of-bool
Fix for "unsafe use of type bool" warning when compiling with MSVC.
2025-03-20 01:23:35 -05:00
Christopher Haster
76027f1502 Merge pull request #1064 from tim-nordell-nimbelink/fix/script_syntax_warnings
scripts: Fixed several SyntaxWarning for python test helpers
2025-03-20 01:23:19 -05:00
Christopher Haster
61a1b0b496 Tweaked lfs_gstate_iszero for terseness 2025-03-18 02:39:28 -05:00
Joakim Plate
ffafb9cbb1 fix: avoid assuming struct packing
lfs_gstate_t was assumed to be a packed array of uint32_t,
but this is not always guaranteed. Access the fields directly
instead of attempting to loop over an array of uint32_t

Fixes clang tidy warnings about use of uninitialized memory
accessed.
2025-03-14 10:03:46 +01:00
Christopher Haster
5281a20f6c README.md: Tweaked testing documentation
- Showing some of the more useful flags.
- Showing the usual flow of bug -> reproduce -> gdb.
- Being a bit pedantic since this is the README.md.
2025-03-13 13:23:20 -05:00
Brian Pugh
f55520380d Add a little bit of documentation on how to run tests. 2025-02-27 17:41:29 -08:00
Rocloong
936919d134 LFS_TRACE: Fixed sign mismatch in lfs_file_size 2025-02-13 15:46:39 -06:00
Christopher Haster
d2c3a47627 gha: Added test-yes-trace build/test job to CI
To hopefully catch typos like the one found by Noxet in the future.

Nothing is actually testing that these trace statements compile
otherwise.
2025-02-06 01:20:29 -06:00
Jonathan Sönnerup
0320e7db0e Changed cast to correct type when trace is enabled for filebd 2025-02-05 16:16:53 +01:00
Christopher Haster
caba4f31df Fixed dir iteration being broken by concurrent removes
When removing a file, we mark all open handles as "removed" (
pair={-1,-1}) to avoid trying to later read metadata that no longer
exists. Unfortunately, this also includes open dir handles that happen
to be pointing at the removed file, causing them to return
LFS_ERR_CORRUPT on the next read.

The good news is this is _not_ actual filesystem corruption, only a
logic error in lfs_dir_read.

We actually already have logic in place to nudge the dir to the next id,
but it was unreachable with the existing logic. I suspect this worked at
one point but was broken during a refactor due to lack of testing.

---

Fortunately, all we need to do is _not_ clobber the handle if the
internal type is a dir. Then the dir-nudging logic can correctly take
over.

I've also added test_dirs_remove_read to test this and prevent another
regression, adapted from tests provided by tpwrules that identified the
original bug.

Found by tpwrules
2025-02-03 22:52:24 -06:00
Amilcar Ubiera
152d03043c Fix for "unsafe use of type bool" warning when compiling with MSVC. 2025-02-03 18:59:14 -05:00
Tim Nordell
8d01895b32 scripts: Fixed several SyntaxWarning for python test helpers
Many of these require a r'' string context to avoid errors like:

  scripts/test.py:105: SyntaxWarning: invalid escape sequence '\s'
2025-01-13 16:54:13 -06:00
geky-bot
e35c27008c Generated v2 prefixes 2024-12-20 15:30:07 +00:00
Christopher Haster
0494ce7169 Merge pull request #1058 from littlefs-project/fix-seek-eob-cache
Fixed incorrect cache reuse when seeking from end-of-block
2024-12-20 09:02:13 -06:00
Christopher Haster
366100b140 Fixed incorrect cache reuse when seeking from end-of-block
In v2.5, we introduced an optimization to avoid rereading data when
seeking inside the file cache. Unfortunately this used a slightly
wrong condition to check if the cache was "live", which meant seeks from
end-of-blocks could end up with invalid caches and wrong data. Not
great.

The problem is the nuance of when a file's cache is "live":

1. The file is marked as LFS_F_READING or LFS_F_WRITING.

   But we can't reuse the cache when writing, so we only care about
   LFS_F_READING.

2. file->off != lfs->cfg->block_size (end-of-block).

   This is an optimization to avoid eagerly reading blocks we may not
   actually care about.

We weren't checking for the end-of-block case, which meant if you seeked
_from_ the end of a block to a seemingly valid location in the file
cache, you could end up with an invalid cache.

Note that end-of-block may not be powers-of-two due to CTZ skip-list
pointers.

---

The fix is to check for the end-of-block case in lfs_file_seek. Note
this now matches the need-new-block logic in lfs_file_flushedread.

This logic change may also make lfs_file_seek call lfs_file_flush more
often, but only in cases where lfs_file_flush is a noop.

I've also extended the test_seek tests to cover a few more boundary-read
cases and prevent a regression in the future.

Found by wjl and lrodorigo
2024-12-19 02:39:10 -06:00
geky-bot
6d47ce747d Generated v2 prefixes 2024-12-11 23:20:07 +00:00
Christopher Haster
630a0d87c2 Merge pull request #1050 from littlefs-project/devel
Minor release: v2.10
2024-12-11 16:56:45 -06:00
Christopher Haster
3d0386489b Bumped minor version to v2.10 2024-12-11 16:23:10 -06:00
Christopher Haster
b8e4433b34 Merge pull request #1052 from wangdongustc/assert_null_sync
Assert on NULL IO functions
2024-12-10 11:48:48 -06:00
Dong Wang
dae656aa53 Fix prettyasserts.py for pointer asserts 2024-12-10 22:54:58 +08:00
Dong Wang
469c863c18 Assert on NULL IO function 2024-12-10 22:54:54 +08:00
Christopher Haster
215613e41f gha: Fixed x86-only statuses
Looks like I missed a line during refactoring, resulted in only x86
sizes being reported in GitHub statuses.

If we wanted to limited these to one architecture, thumb would have
probably been a better pick.
2024-12-09 14:56:12 -06:00
Christopher Haster
2fcecc8894 Merge pull request #1046 from littlefs-project/fix-trailing-slashes
paths: Revisit path parsing, fix trailing slash behavior
2024-12-06 13:48:26 -06:00
Christopher Haster
78f9a5fcd3 Merge pull request #1038 from littlefs-project/link-ramcrc32bd-ramrsbd
Add links to ramcrc32bd and ramrsbd
2024-12-06 13:47:47 -06:00
Christopher Haster
83fe41b605 Merge pull request #1031 from littlefs-project/fix-enospc-issues
Fix metadata_max==prog_size commit->end calculation
2024-12-06 13:47:36 -06:00
Christopher Haster
d7a911923b Merge pull request #1027 from littlefs-project/fix-seek-overflow-ub
Fix seek undefined behavior on signed integer overflow
2024-12-06 13:47:20 -06:00
Christopher Haster
2ba4280a5e Merge pull request #997 from littlefs-project/fix-trace-format-again
Fix some more LFS_TRACE format specifiers
2024-12-06 13:47:06 -06:00
Christopher Haster
c961e1fe66 Merge pull request #1004 from yamt/user-define-header
Add an alternative way to override LFS_MALLOC etc
2024-12-06 13:45:56 -06:00
Christopher Haster
bd01a4c0ee Merge pull request #1013 from wdfk-prog/feature_2.9.3
Write the detect cycles function as a function to optimize code
2024-12-06 13:44:37 -06:00
Christopher Haster
999ef6656f paths: Changed CREAT with a trailing slash to return NOTDIR
- before: lfs_file_open("missing/") => LFS_ERR_ISDIR
- after:  lfs_file_open("missing/") => LFS_ERR_NOTDIR

As noted by bmcdonnell-fb, returning LFS_ERR_ISDIR here was inconsistent
with the case where the file exists:

  case                           before          after
  lfs_file_open("dir_a")      => LFS_ERR_ISDIR   LFS_ERR_ISDIR
  lfs_file_open("dir_a/")     => LFS_ERR_ISDIR   LFS_ERR_ISDIR
  lfs_file_open("reg_a/")     => LFS_ERR_NOTDIR  LFS_ERR_NOTDIR
  lfs_file_open("missing_a/") => LFS_ERR_ISDIR   LFS_ERR_NOTDIR

Note this is consistent with the behavior of lfs_stat:

  lfs_file_open("reg_a/") => LFS_ERR_NOTDIR
  lfs_stat("reg_a/")      => LFS_ERR_NOTDIR

And the only other function that can "create" files, lfs_rename:

  lfs_file_open("missing_a/")       => LFS_ERR_NOTDIR
  lfs_rename("reg_a", "missing_a/") => LFS_ERR_NOTDIR

There is some ongoing discussion about if these should return NOTDIR,
ISDIR, or INVAL, but this is at least an improvement over the
rename/open mismatch.
2024-11-25 15:40:44 -06:00
Christopher Haster
b735c8fd7f paths: Added tests over NOENT + trailing slash/dot
- test_paths_noent_trailing_slashes
- test_paths_noent_trailing_dots
- test_paths_noent_trailing_dotdots

These managed to slip through our path testing but should be tested, if
anything just to know exactly what errors these return.
2024-11-25 15:40:15 -06:00
Christopher Haster
30947054d4 paths: Extended tests to cover open with CREAT/EXCL
These flags change the behavior of open quite significantly. It's useful
to cover these in our path tests so the behavior is locked down.
2024-11-25 15:40:15 -06:00
Christopher Haster
80ca1ea300 paths: Reject empty paths
Before this, the empty path ("") was treated as an alias for the root.
This was unintentional and just a side-effect of how the path parser
worked.

Now, the empty path should always result in LFS_ERR_INVAL:

- before: lfs_stat("") => 0
- after:  lfs_stat("") => LFS_ERR_INVAL
2024-11-25 15:40:15 -06:00
Christopher Haster
815f0d85a5 paths: Fixed dots followed by dotdots
Unlike normal files, dots (".") should not change the depth when
attempting to skip dotdot ("..") entries.

A weird nuance in the path parser, but at least it had a relatively easy
fix.

Added test_paths_dot_dotdots to prevent a regression.
2024-11-25 15:40:15 -06:00
Christopher Haster
dc92dec6d3 paths: Reject dotdots above root
This changes the behavior of paths that attempt to navigate above root
to now return LFS_ERR_INVAL:

- before: lfs_stat("/../a") => 0
- after:  lfs_stat("/../a") => LFS_ERR_INVAL

This is a bit of an opinionated change while making other path
resolution tweaks.

In terms of POSIX-compatibility, it's a bit unclear exactly what dotdots
above the root should do.

POSIX notes:

> As a special case, in the root directory, dot-dot may refer to the
> root directory itself.

But the word choice of "may" implies it is up to the implementation.

I originally implement this as a root-loop simply because that is what
my Linux machine does, but I now think that's not the best option. Since
we're making other path-related tweaks, we might as well try to adopt
behavior that is, in my opinion, safer and less... weird...

This should also help make paths more consistent with future theoretical
openat-list APIs, where saturating at the current directory is sort of
the least expected behavior.
2024-11-25 15:40:07 -06:00
Christopher Haster
a6035071be paths: Fixed/doc trailing slash/dot POSIX incompatibilities
- lfs_mkdir now accepts trailing slashes:
  - before: lfs_mkdir("a/") => LFS_ERR_NOENT
  - after:  lfs_mkdir("a/") => 0

- lfs_stat, lfs_getattr, etc, now reject trailing slashes if the file is
  not a directory:
  - before: lfs_stat("reg_a/") => 0
  - after:  lfs_stat("reg_a/") => LFS_ERR_NOTDIR

  Note trailing slashes are accepted if the file is a directory:
  - before: lfs_stat("dir_a/") => 0
  - after:  lfs_stat("dir_a/") => 0

- lfs_file_open now returns LFS_ERR_NOTDIR if the file exists but the
  path contains trailing slashes:
  - before: lfs_file_open("reg_a/") => LFS_ERR_NOENT
  - after:  lfs_file_open("reg_a/") => LFS_ERR_NOTDIR

To make these work, the internal lfs_dir_find API required some
interesting changes:

- lfs_dir_find no longer sets id=0x3ff on not finding a parent entry in
  the path. Instead, lfs_path_islast can be used to determine if the
  modified path references a parent entry or child entry based on the
  remainder of the path string.

  Note this is only necessary for functions that create new entries
  (lfs_mkdir, lfs_rename, lfs_file_open).

- Trailing slashes mean we can no longer rely on the modified path being
  NULL-terminated. lfs_path_namelen provides an alternative to strlen
  that stops at slash or NULL.

- lfs_path_isdir also tells you if the modified path must reference a
  dir (contains trailing slashes). I considered handling this entirely
  in lfs_dir_find, but the behavior of entry-creating functions is too
  nuanced.

  At least lfs_dir_find returns LFS_ERR_NOTDIR if the file exists on
  disk.

Like strlen, lfs_path_namelen/islast/isdir are all O(n) where n is the
name length. This isn't great, but if you're using filenames large
enough for this to actually matter... uh... open an issue on GitHub and
we might improve this in the future.

---

There are a couple POSIX incompatibilities that I think are not
worth fixing:

- Root modifications return EINVAL instead of EBUSY:
  - littlefs: remove("/") => EINVAL
  - POSIX:    remove("/") => EBUSY
  Reason: This would be the only use of EBUSY in the system.

- We accept modifications of directories with trailing dots:
  - littlefs: remove("a/.") => 0
  - POSIX:    remove("a/.") => EBUSY
  Reason: Not worth implementing.

- We do not check for existence of directories followed by dotdots:
  - littlefs: stat("a/missing/..") => 0
  - POSIX:    stat("a/missing/..") => ENOENT
  Reason: Difficult to implement non-recursively.

- We accept modifications of directories with trailing dotdots:
  - littlefs: rename("a/b/..", "c") => 0
  - POSIX:    rename("a/b/..", "c") => EBUSY
  Reason: Not worth implementing.

These are at least now documented in tests/test_paths.toml, which isn't
the greatest location, but it's at least something until a better
document is created.

Note that these don't really belong in SPEC.md because path parsing is
a function of the driver and has no impact on disk.
2024-11-25 15:39:29 -06:00
Christopher Haster
232e736aae paths: Added trailing slashes and dots tests
As expected these are failing and will need some work to pass.

The issue with lfs_file_open allowing trailing slashes was found by
rob-zeno, and the issue with lfs_mkdir disallowing trailing slashes was
found by XinStellaris, PoppaChubby, pavel-kirienko, inf265, Xywzel,
steverpalmer, and likely others.
2024-11-23 19:03:36 -06:00
Christopher Haster
0de0389c6f paths: Reworked test_paths to cover more corner cases
This should be a superset of the previous test_paths test suite, while
covering a couple more things (more APIs, more path synonyms, utf8,
non-printable ascii, non-utf8, etc).

Not yet tested are some corner cases with known bugs, mainly around
trailing slashes.
2024-11-23 18:20:06 -06:00
Christopher Haster
1407db9556 Added links to ramcrc32bd and ramrsbd
These two small libraries provide examples of error-correction
compatible with littlefs (or any filesystem really).

It would be nice to eventually provide these as drop-in solutions, but
right now it's not really possible without breaking changes to
littlefs's block device API.

In the meantime, ramcrc32bd and ramrsbd at least provide example
implementations that can be adapted to users' own block devices.
2024-11-01 17:09:45 -05:00
Christopher Haster
ea431bd6ae Added some checks that metadata_max makes sense
Like the read/prog/block_size checks, these are just asserts. If these
invariants are broken the filesystem will break in surprising ways.
2024-10-04 13:45:57 -05:00
Christopher Haster
2d62d2f4c9 Fixed metadata_max==prog_size commit->end calculation
The inconsistency here between the use of block_size vs metadata_max was
suspicious. Turns out there's a bug when metadata_max == prog_size.

We correctly use metadata_max for the block_size/2 check, but we weren't
using it for the block_size-40 check. The second check seems unnecessary
after the first, but it protects against running out of space in a
commit for commit-related metadata (checksums, tail pointers, etc) when
we can't program half-blocks.

Turns out this is also needed when limiting metadata_max to a single
prog, otherwise we risk erroring with LFS_ERR_NOSPC early.

Found by ajheck, dpkristensen, NLLK, and likely others.
2024-10-04 13:45:43 -05:00
Christopher Haster
1f82c0f27f Added some metadata_max testing
- Added METADATA_MAX to test_runner.
- Added METADATA_MAX to bench_runner.
- Added a simple metadata_max test to test_superblocks, for lack of
  better location.

There have been several issues floating around related to metadata_max
and LFS_ERR_NOSPC which makes me think there's a bug in our metadata_max
logic.

metadata_max was a quick patch and is relatively untested, so an
undetected bug isn't too surprising. This commit adds at least some
testing over metadata_max.

Sure enough, the new test_superblocks_metadata_max test reveals a
curious LFS_ERR_NAMETOOLONG error that shouldn't be there.

More investigation needed.
2024-10-04 13:06:23 -05:00
wdfk-prog
a2c2e49e6b Write the detect cycles function as a function to optimize code 2024-10-04 10:37:25 +08:00
Christopher Haster
abaec45652 Fixed seek undefined behavior on signed integer overflow
In the previous implementation of lfs_file_seek, we calculated the new
offset using signed arithmetic before checking for possible
overflow/underflow conditions. This results in undefined behavior in C.

Fortunately for us, littlefs is now limited to 31-bit file sizes for API
reasons, so we don't have to be too clever here. Doing the arithmetic
with unsigned integers and just checking if we're in a valid range
afterwards should work.

Found by m-kostrzewa and lucic71
2024-09-24 14:01:20 -05:00
Christopher Haster
f1c430e779 Added some tests around seek integer overflow/underflow
Original tests provided by m-kostrzewa, these identify signed overflow
(undefined behavior) when compiled with -fsanitize=undefined.
2024-09-24 14:01:08 -05:00
YAMAMOTO Takashi
4a845be0be Rename LFS_USER_DEFINES to LFS_DEFINES 2024-09-24 12:29:13 -05:00
YAMAMOTO Takashi
e1636d05ab Add an alternative way to override LFS_MALLOC etc
With the existing method, (-DLFS_MALLOC=my_malloc)
users often had to use compiler options like -include, which
was not so portable.
This change introduces another way to provide partial overrides of
lfs_util.h using a user-provided header.
2024-09-24 12:29:13 -05:00
Christopher Haster
b78afe2518 Merge pull request #1026 from yamt/update-gh-actions
Update github actions to the latest versions
2024-09-24 12:25:04 -05:00
Christopher Haster
798073c2a7 gha: Dropped minor/patch version pinning of actions
With GitHub forcibly deprecating old versions of actions, pinning the
minor/patch version is more likely to cause breakage than not.
2024-09-20 16:05:15 -05:00
Christopher Haster
7db9e1663a gha: Switched to standard da for cross-workflow downloads
Looks like cross-workflow downloads has finally been added to the
standard download-artifact action, so we might as well switch to it to
reduce dependencies.

dawidd6's version was also missing the merge-multiple feature which is
necessary to work around breaking changes in download-artifact's v4
bump.

Weirdly it needs GITHUB_TOKEN for some reason? Not sure why this
couldn't be implicit.
2024-09-20 16:05:12 -05:00
Christopher Haster
2c4b262c35 gha: Merge artifacts on download
Turns out major versions break things.

Old behavior: Artifacts with same name are merged
New behavior: Artifacts with same name error

Using a pattern and merging on download should fix this at least on the
job-side. Though I do wonder if we'll start running into artifact limit
issues with the new way artifacts are handled...
2024-09-20 16:04:35 -05:00
YAMAMOTO Takashi
72a4b57f4e gha: Make the artifact names unique 2024-09-19 17:26:49 -05:00
YAMAMOTO Takashi
6e7269890a gha: Update github actions to the latest versions 2024-09-19 17:18:15 -05:00
Christopher Haster
ac207586ba Fixed some more LFS_TRACE format specifiers
- block_cycles is signed and should use PRId32
- flags is signed (which is a bit weird) and should be cast for %x

Unfortunately exactly what PRI* expands to is dependant on both the
compiler and the underlying architecture, so I don't think it's possible
for us to catch these mistakes with CI...

Found by stefano-zanotti
2024-06-25 16:08:00 -05:00
geky-bot
0e09a8a5f4 Generated v2 prefixes 2024-04-29 22:00:12 +00:00
Christopher Haster
d01280e649 Merge pull request #968 from littlefs-project/link-pico-littlefs-usb
Add links to pico-littlefs-usb (FAT12 emulation) and mklittlefs
2024-04-29 16:21:49 -05:00
Christopher Haster
6e52140d51 Merge pull request #959 from littlefs-project/fix-expanded-magic
Duplicate the superblock entry during superblock expansion, fix missing magic
2024-04-29 14:26:38 -05:00
Christopher Haster
0bbb8bc88b Reorganized external project links a bit
These were grouped up a bit better at one point, but that sort of
drifted as new project were added:

1. Official repos (mainly littlefs-fuse)
2. Non-C reimplementations/wrappers
3. Utilities
4. Non-littlefs related projects

Eventually, maybe when these move out of the README.md, these categories
should probably be actually codified as headers or something.
2024-04-17 13:46:33 -05:00
Christopher Haster
78082336e7 Added a link to mklittlefs
Implemented by earlephilhower, mklittlefs is a command line interface
that seems to be used by the ESP8266 and RP2040 ecosystems. It deserves
a mention.

Also tweaked mklfs's description a bit.
2024-04-17 13:39:11 -05:00
Christopher Haster
8336ecd203 Added a link to pico-littlefs-usb (FAT12 emulation)
Implemented by oyama, pico-littlefs-usb provides an easy interface to
littlefs by emulating a FAT12 filesystem over USB.

There are some tradeoffs to this, but being able to mount a littlefs
device without installing additional drivers is very nice. Maybe in the
future devices could provide both a FAT and raw endpoint for
easy/advanced filesystem access.
2024-04-17 13:09:04 -05:00
geky-bot
d0b248791f Generated v2 prefixes 2024-04-17 18:03:09 +00:00
Christopher Haster
68d28b5114 Merge pull request #966 from BrianPugh/fix-divide-by-zero-full-filesystem
Fix DivideByZero exception when filesystem is completely full.
2024-04-17 12:38:22 -05:00
Christopher Haster
1bc14933b7 Tweaked on-disk config comments for consistency
- Prefer "defaults to blablabla when zero" to hint that this is the
  default state when both explicitly set to zero and implicitly set to
  zero thanks to C's initializers.

- Prefer "disk" when referencing something stored "on disk". Other terms
  can quickly get ambiguous. Except maybe "block device"...
2024-04-17 00:16:20 -05:00
Christopher Haster
01b6a47ea8 Extended test_alloc to test inferred block_count
The block allocator is an area where inferred block counts (when
cfg.block_count=0) are more likely to cause problems.

As is shown by the recent divide-by-zero-exhaustion issue.
2024-04-17 00:04:56 -05:00
Brian Pugh
749a45650f Fix DivideByZero exception when filesystem is completely full. 2024-04-16 20:32:12 -07:00
Christopher Haster
11b036cc6c Prevented unnecessary superblock rewrites if old version in superblock chain
Because multiple, out-of-date superblocks can exist in our superblock
chain, we need to be careful to make sure newer superblock entries
override older superblock entries.

If we see an older on-disk minor version in the superblock chain, we
were correctly overriding the on-disk minor version, but we were also
leaving the "needs superblock" bit set in our consistency state.

This isn't a hard-error, but would lead to a superblock rewrite every
mount. The rewrite would make no progress, as the out-of-date version is
effectively immutable at this point, and just waste prog cycles.

This should fix that by clearing the "needs superblock" bit if we see a
newer on-disk minor version.
2024-03-19 00:49:28 -05:00
Christopher Haster
25ee90fdf1 Clarified what is accessible at specific superblock offsets in SPEC.md
It used to be the case that the entire superblock entry could be found
at specific offsets, but this was only possible while the superblock
entry was immutable. Now that the superblock entry is very mutable
(block-count changes, lfs2.0 -> lfs2.1 version bumps, etc), the correct
superblock entry may end up later in the metadata log.

At the very least, the "littlefs" magic string is still immutable and at
the specific offset offset=8. This is arguably the most useful
fixed-offset item.
2024-03-19 00:49:28 -05:00
Christopher Haster
a60a986c9c Duplicate the superblock entry during superblock expansion
The documentation does not match the implementation here. The intended
behavior of superblock expansion was to duplicate the current superblock
entry into the new superblock:

   .--------.  .--------.
  .|littlefs|->|littlefs|
  ||bs=4096 | ||bs=4096 |
  ||bc=256  | ||bc=256  |
  ||crc32   | ||root dir|
  ||        | ||crc32   |
  |'--------' |'--------'
  '--------'  '--------'

The main benefit is that we can rely on the magic string "littlefs"
always residing in blocks 0x{0,1}, even if the superblock chain has
multiple superblocks.

The downside is that earlier superblocks in the superblock chain may
contain out-of-date configuration. This is a bit annoying, and risks
hard-to-reach bugs, but in theory shouldn't break anything as long as
the filesystem is aware of this.

Unfortunately this was lost at some point during refactoring in the
early v2-alpha work. A lot of code was moving around in this stage, so
it's a bit hard to track down the change and if it was intentional. The
result is superblock expansion creates a valid linked-list of
superblocks, but only the last superblock contains a valid superblock
entry:

   .--------.  .--------.
  .|crc32   |->|littlefs|
  ||        | ||bs=4096 |
  ||        | ||bc=256  |
  ||        | ||root dir|
  ||        | ||crc32   |
  |'--------' |'--------'
  '--------'  '--------'

What's interesting is this isn't invalid as far as lfs_mount is
concerned. lfs_mount is happy as long as a superblock entry exists
anywhere in the superblock chain. This is good for compat flexibility,
but is the main reason this has gone unnoticed for so long.

---

With the benefit of more time to think about the problem, it may have
been more preferable to copy only the "littlefs" magic string and NOT
the superblock entry:

   .--------.  .--------.
  .|littlefs|->|littlefs|
  ||crc32c  | ||bs=4096 |
  ||        | ||bc=256  |
  ||        | ||root dir|
  ||        | ||crc32   |
  |'--------' |'--------'
  '--------'  '--------'

This would allow for simple "littlefs" magic string checks without the
risks associated with out-of-date superblock entries.

Unfortunately the current implementation errors if it finds a "littlefs"
magic string without an associated superblock entry, so such a change
would not be compatible with old drivers.

---

This commit tweaks superblock expansion to duplicate the superblock
entry instead of simply moving it to the new superblock. And adds tests
over the magic string "littlefs" both before and after superblock
expansion.

Found by rojer and Nikola Kosturski
2024-03-19 00:48:56 -05:00
geky-bot
e164b3285b Generated v2 prefixes 2024-03-08 23:23:45 +00:00
Christopher Haster
4dd30c1b8f Merge pull request #948 from littlefs-project/fix-sync-ordering
Fix sync issue where data writes could appear before metadata writes
2024-03-08 16:49:59 -06:00
Christopher Haster
5c0d332ecd Merge pull request #939 from Graveflo/master
Add nim-littlefs to readme
2024-03-08 16:49:11 -06:00
Christopher Haster
cf68333a55 Merge pull request #937 from littlefs-project/fix-pending-rm-get-underflow
Fix synthetic move underflows in lfs_dir_get
2024-03-08 16:48:50 -06:00
Christopher Haster
7873d811a0 Fixed memory leak in emubd's out-of-order write emulation
We need to decrement the saved block state on sync, when we reset
out-of-order emulation. Otherwise we leak blocks out the wazoo.
2024-02-27 21:39:34 -06:00
Christopher Haster
fc2aa3350c Fixed issue with exhaustive + out-of-order powerloss testing
Unlike the heuristic based testing, exhaustive powerloss testing
effectively forks the current test and runs both the interrupted and
uninterrupted test states to completion. But emubd wasn't expecting
bd->cfg->powerloss_cb to return.

The fix here is to keep track to both the old+new out-of-order block
states and unrevert them if bd->cfg->powerloss_cb returns.

This may leak the temporary copy, but powerloss testing is already
inherently leaky.
2024-02-27 21:14:59 -06:00
Christopher Haster
6352185949 Fixed sync issue where data writes could appear before metadata writes
Long story short we aren't calling sync correctly in littlefs. This
fixes that.

Some forms of storage, mainly anything with an FTL, eMMC, SD, etc, do
not guarantee a strict write order for writes to different blocks. In
theory this is what bd sync is for, to tell the bd when it is important
for the writes to be ordered.

Currently, littlefs calls bd sync after committing metadata. This is
useful as it ensures that user code can rely on lfs_file_sync for
ordering external side-effects.

But this is insufficient for handling storage with out-of-order writes.

Consider the simple case of a file with one data block:

1. lfs_file_write(blablabla) => writes data into a new data block

2. lfs_file_sync() => commits metadata to point to the new data block

But with out-of-order writes, the bd is free to reorder things such that
the metadata is updated _before_ the data is written. If we lose power,
that would be bad.

The solution to this is to call bd sync twice: Once before we commit
the metadata to tell the bd that these writes must be ordered, and once
after we commit the metadata to allow ordering with user code.

As a small optimization, we only call bd sync if the current file is not
inlined and has actually been modified (LFS_F_DIRTY). It's possible for
inlined files to be interleaved with writes to other files.

Found by MFaehling and alex31
2024-02-27 14:00:10 -06:00
Christopher Haster
f2a6f45eef Added out-of-order write testing to emubd
Some forms of storage, mainly anything with an FTL, eMMC, SD, etc, do
not guarantee a strict write order for writes to different blocks. It
would be good to test that this doesn't break littlefs.

This adds LFS_EMUBD_POWERLOSS_OOO to lfs_emubd, which tells lfs_emubd to
try to break any order-dependent code on powerloss.

The behavior right now is a bit simple, but does result in test
breakage:

1. Save the state of the block on first write (erase really) after
   sync/init.

2. On powerloss, revert the first write to its original state.

This might be a bit confusing when debugging, since the block will
appear to time-travel, but doing anything fancier would make emubd quite
a bit more complicated.

You could also get a bit fancier with which/how many blocks to revert,
but this should at least be sufficient to make sure bd sync calls are in
the right place.
2024-02-27 13:59:37 -06:00
Ryan McConnell
2752d8c486 add nim-littlefs to readme 2024-02-07 02:53:16 -05:00
Christopher Haster
ddbfcaa722 Fixed synthetic move underflows in lfs_dir_get
By "luck" the previous code somehow managed to not be broken, though it
was possible to traverse the same file twice in lfs_fs_traverse/size
(which is not an error).

The problem was an underlying assumption in lfs_dir_get that it would
never be called when the requested id is pending removal because of a
powerloss. The assumption was either:

1. lfs_dir_find would need to be called first to find the id, and it
   would correctly toss out pending-rms with LFS_ERR_NOENT.

2. lfs_fs_mkconsistent would be implicitly called before any filesystem
   traversals, cleaning up any pending-rms. This is at least true for
   allocator scans.

But, as noted by andriyndev, both lfs_fs_traverse and lfs_fs_size can
call lfs_fs_get with a pending-rm id if called in a readonly context.

---

By "luck" this somehow manages to not break anything:

1. If the pending-rm id is >0, the id is decremented by 1 in lfs_fs_get,
   returning the previous file entry during traversal. Worst case, this
   reports any blocks owned by the previous file entry twice.

   Note this is not an error, lfs_fs_traverse/size may return the same
   block multiple times due to underlying copy-on-write structures.

2. More concerning, if the pending-rm id is 0, the id is decremented by
   1 in lfs_fs_get and underflows. This underflow propagates into the
   type field of the tag we are searching for, decrementing it from
   0x200 (LFS_TYPE_STRUCT) to 0x1ff (LFS_TYPE_INTERNAL(UNUSED)).

   Fortunately, since this happens to underflow to the INTERNAL tag
   type, the type intended to never exist on disk, we should never find
   a matching tag during our lfs_fs_get search. The result? lfs_dir_get
   returns LFS_ERR_NOENT, which is actually what we want.

Also note that LFS_ERR_NOENT does not terminate the mdir traversal
early. If it did we would have missed files instead of duplicating
files, which is a slightly worse situation.

---

The fix is to add an explicit check for pending-rms in lfs_dir_get, just
like in lfs_dir_find. This avoids relying on unintended underflow
propagation, and should make the internal API behavior more consistent.

This is especially important for potential future gc extensions.

Found by andriyndev
2024-02-04 15:12:31 -06:00
geky-bot
49df8ba47c Generated v2 prefixes 2024-01-23 18:48:17 +00:00
Christopher Haster
f53a0cc961 Merge pull request #929 from littlefs-project/devel
Minor release: v2.9
2024-01-23 12:33:13 -06:00
Christopher Haster
42910bc8e5 Bumped minor version to v2.9 2024-01-19 14:37:37 -06:00
Christopher Haster
a3e1d12ce1 Merge pull request #915 from littlefs-project/well-done
Rename internal functions _raw* -> _*_
2024-01-19 13:58:29 -06:00
Christopher Haster
a70870c628 Renamed internal functions _raw* -> _*_
So instead of lfs_file_rawopencfg, it's now lfs_file_opencfg_.

The "raw" prefix is annoying, doesn't really add meaning ("internal"
would have been better), and gets in the way of finding the relevant
function implementations.

I have been using _s as suffixes for unimportant name collisions in
other codebases, and it seems to work well at reducing wasted brain
cycles naming things. Adopting it here avoids the need for "raw"
prefixes.

It's quite a bit like the use of prime symbols to resolve name
collisions in math, e.g. x' = x + 1. Which is even supported in Haskell
and is quite nice there.

And the main benefit: Now if you search for the public API name, you get
the internal function first, which is probably what you care about.

Here is the exact script:

  sed -i 's/_raw\([a-z0-9_]*\)\>/_\1_/g' $(git ls-tree -r HEAD --name-only | grep '.*\.c')
2024-01-19 13:20:56 -06:00
Christopher Haster
ceb17a0f4a Merge pull request #917 from tomscii/fix_return_value_of_lfs_rename
Fix return value of lfs_rename()
2024-01-19 13:19:21 -06:00
Christopher Haster
a8a0905777 Merge pull request #916 from littlefs-project/ci-ubuntu-latest
Change CI to just run on ubuntu-latest
2024-01-19 13:19:07 -06:00
Christopher Haster
13d78616fe Merge pull request #914 from littlefs-project/inline-max
Add inline_max, to optionally limit the size of inlined files
2024-01-19 13:18:54 -06:00
Christopher Haster
8b8fd14187 Added inline_max, to optionally limit the size of inlined files
Inlined files live in metadata and decrease storage requirements, but
may be limited to improve metadata-related performance. This is
especially important given the current plague of metadata performance.

Though decreasing inline_max may make metadata more dense and increase
block usage, so it's important to benchmark if optimizing for speed.

The underlying limits of inlined files haven't changed:
1. Inlined files need to fit in RAM, so <= cache_size
2. Inlined files need to fit in a single attr, so <= attr_max
3. Inlined files need to fit in 1/8 of a block to avoid metadata
   overflow issues, this is after limiting by metadata_max,
   so <= min(metadata_max, block_size)/8

By default, the largest possible inline_max is used. This preserves
backwards compatibility and is probably a good default for most use
cases.

This does have the awkward effect of requiring inline_max=-1 to
indicate disabled inlined files, but I don't think there's a good
way around this.
2024-01-19 13:00:27 -06:00
Christopher Haster
09972a1710 Merge pull request #913 from littlefs-project/gc-compactions
Extend lfs_fs_gc to compact metadata, compact_thresh
2024-01-19 12:51:11 -06:00
Christopher Haster
ed7bd05435 Merge pull request #912 from littlefs-project/relaxed-lookahead
Relaxed lookahead alignment, other internal block alloc readability improvements
2024-01-19 12:27:14 -06:00
Christopher Haster
b5cd957f42 Extended lfs_fs_gc to compact metadata, compact_thresh
This extends lfs_fs_gc to now handle three things:

1. Calls mkconsistent if not already consistent
2. Compacts metadata > compact_thresh
3. Populates the block allocator

Which should be all of the janitorial work that can be done without
additional on-disk data structures.

Normally, metadata compaction occurs when an mdir is full, and results in
mdirs that are at most block_size/2.

Now, if you call lfs_fs_gc, littlefs will eagerly compact any mdirs that
exceed the compact_thresh configuration option. Because the resulting
mdirs are at most block_size/2, it only makes sense for compact_thresh to
be >= block_size/2 and <= block_size.

Additionally, there are some special values:

- compact_thresh=0  => defaults to ~88% block_size, may change
- compact_thresh=-1 => disables metadata compaction during lfs_fs_gc

Note that compact_thresh only affects lfs_fs_gc. Normal compactions
still only occur when full.
2024-01-19 12:25:45 -06:00
Christopher Haster
1195d606ae Merge pull request #909 from littlefs-project/easy-util-defines
Add some easier util overrides: LFS_MALLOC/FREE/CRC
2024-01-19 12:24:16 -06:00
Christopher Haster
1711bdef76 Merge pull request #886 from BrianPugh/macro-sanity-check
Add value-range checks for user-definable macros at compile-time
2024-01-19 12:23:36 -06:00
Christopher Haster
f522ed907a Added tests over rename type errors 2024-01-17 00:10:30 -06:00
Tom Szilagyi
4f32738cd6 Fix return value of lfs_rename()
When lfs_rename() is called trying to rename (move) a file to an
existing directory, LFS_ERR_ISDIR is (correctly) returned. However, in
the opposite case, if one tries to rename (move) a directory to a path
currently occupied by a regular file, LFS_ERR_NOTDIR should be
returned (since the error is that the destination is NOT a directory),
but in reality, LFS_ERR_ISDIR is returned in this case as well.

This commit fixes the code so that in the latter case, LFS_ERR_NOTDIR
is returned.
2024-01-17 00:06:52 -06:00
Christopher Haster
6691718b18 Restricted LFS_FILE_MAX to signed 32-bits, <2^31, <=2147483647
I think realistically no one is using this. It's already only partially
supported and untested.

Worst case, if someone does depend on this we can always revert.
2024-01-16 23:40:30 -06:00
Christopher Haster
1fefcbbcba Rearranged compile-time constant checks to live near lfs_init
lfs_init handles the checks/asserts of most configuration, moving these
checks near lfs_init attempts to keep all of these checks nearby each
other.

Also updated the comments to avoid somtimes-ambiguous range notation.

And removed negative bounds checks. Negative bounds should be obviously
incorrect, and 0 is _technically_ not illegal for any define (though
admittedly unlikely to be correct).
2024-01-16 23:39:51 -06:00
Christopher Haster
60567677b9 Relaxed alignment requirements for lfs_malloc
The only reason we needed this alignment was for the lookahead buffer.

Now that the lookahead buffer is relaxed to operate on bytes, we can
relax our malloc alignment requirement all the way down to the byte
level, since we mainly use lfs_malloc to allocate byte-level buffers.

This does introduce a risk that we might need word-level mallocs in the
future. If that happens we will need to decide if changing the malloc
alignment is a breaking change, or gate alignment requirements behind
user provided defines.

Found by HiFiPhile.
2024-01-16 00:27:07 -06:00
geky-bot
90bfea568b Generated v2 prefixes 2023-12-21 06:59:10 +00:00
Christopher Haster
897b571318 Changed CI to just run on ubuntu-latest
If we already have to bump this version as GitHub phases out older
Ubuntu runners (which is reasonable), I don't really see the value of
pinning a specific version. We might as well just respond to any
broken dependencies caused by GitHub's implicit updates as they
happen...

It's not like CI is truly continuous.
2023-12-21 00:33:44 -06:00
Christopher Haster
3513ff1afc Merge pull request #911 from littlefs-project/fix-release-structs
Fix struct sizes missing from generated release notes
2023-12-21 00:08:16 -06:00
Christopher Haster
8a22bd6e67 Merge pull request #910 from littlefs-project/fix-superblock-expansion-thresh
Increase threshold for superblock expansion from ~50% -> ~88% full
2023-12-21 00:07:55 -06:00
Christopher Haster
9b82db72d8 Merge pull request #898 from zchen24/patch-1
Update DESIGN.md minor typo
2023-12-21 00:06:29 -06:00
Zihan Chen
99b84ee3db Update DESIGN.md, fix minor typo 2023-12-20 23:42:26 -06:00
Christopher Haster
b1b10c0e75 Relaxed lookahead buffer alignment
This drops the lookahead buffer from operating on 32-bit words to
operating on 8-bit bytes, and removes any alignment requirement. This
may have some minor performance impact, but it is unlikely to be
significant when you consider IO overhead.

The original motivation for 32-bit alignment was an attempt at
future-proofing in case we wanted some more complex on-disk data
structure. This never happened, and even if it did, it could have been
added via additional config options.

This has been a significant pain point for users, since providing
word-aligned byte-sized buffers in C can be a bit annoying.
2023-12-20 00:39:11 -06:00
Christopher Haster
1f9c3c04b1 Reworked the block allocator so the logic is hopefully simpler
Some of this is just better documentation, some of this is reworking the
logic to be more intention driven... if that makes sense...
2023-12-20 00:24:56 -06:00
Christopher Haster
7b68441888 Renamed a number of internal block-allocator fields
- Renamed lfs.free      -> lfs.lookahead
- Renamed lfs.free.off  -> lfs.lookahead.start
- Renamed lfs.free.i    -> lfs.lookahead.next
- Renamed lfs.free.ack  -> lfs.lookahead.ckpoint
- Renamed lfs_alloc_ack -> lfs_alloc_ckpoint

These have been named a bit confusingly, and I think the new names make
their relevant purposes a bit clearer.

At the very it's clear lfs.lookahead is related to the lookahead buffer.
(and doesn't imply a closed free-bitmap).
2023-12-20 00:17:08 -06:00
Christopher Haster
b9b95ab4bc Increase threshold for superblock expansion from ~50% -> ~88% full
Superblock expansion is an irreversible operation. In an effort to
prevent superblock expansion from claiming valuable scratch space
(important for small, <~8 block filesystems), littlefs prevents
superblock expansion when the disk is "mostly full".

In true computer-scientist fashion, this "mostly full" threshold was
set to ~50%.

As pointed out by gbolgradov and rojer, >~50% utilization is not
uncommon, and it can lead to a situation where superblock expansion does
not occur in a relatively healthy filesystem, causing focused wear at
the root.

To remedy this, the threshold is now increased to ~88% (7/8) full.

This may change in the future and should probably be eventually user
configurable.

Found by gbolgradov and rojer
2023-12-19 16:51:17 -06:00
Christopher Haster
9a620c730c Added LFS_CRC, easier override for lfs_crc
Now you can override littlefs's CRC implementation with some simple
defines:

  -DLFS_CRC=lfs_crc

The motivation for this is the same for LFS_MALLOC/LFS_FREE. I think
these are the main "system-level" utils that users want to override.

Don't override with this something that's not CRC32! Your filesystem
will no longer be compatible with other tools! This is only intended for
provided hardware acceleration!
2023-12-19 14:12:10 -06:00
Christopher Haster
a0c6c54345 Added LFS_MALLOC/FREE, easier overrides for lfs_malloc/free
Now you can override littlefs's malloc with some simple defines:

  -DLFS_MALLOC=my_malloc
  -DLFS_FREE=my_free

This is probably what most users expected when wanting to override
malloc/free in littlefs, but it hasn't been available, since instead
littlefs provides a file-level override of builtin utils.

The thinking was that there's just too many builtins that could be
overriden, lfs_max/min/alignup/npw2/etc/etc/etc, so allowing users to
just override the util file provides the best flexibility without a ton
of ifdefs.

But it's become clear this is awkward for users that just want to
replace malloc.

Maybe the original goal was too optimistic, maybe there's a better way
to structure this file, or maybe the best API is just a bunch of ifdefs,
I have no idea! This will hopefully continue to evolve.
2023-12-19 13:57:17 -06:00
Zihan Chen
10bcff1af8 Update DESIGN.md minor typo 2023-11-26 11:10:24 -08:00
geky-bot
cf26100aa4 Generated v2 prefixes 2023-10-31 19:00:10 +00:00
Brian Pugh
c531a5e88f Replace erroneous LFS_FILE_MAX upper bound 4294967296 to 4294967295 2023-10-30 11:18:20 -07:00
Brian Pugh
8f9427dd53 Add value-range checks for user-definable macros 2023-10-29 13:50:38 -07:00
geky-bot
982cfd19f7 Generated v2 prefixes 2023-09-22 17:17:09 +00:00
geky-bot
52ff26ac6b Generated v2 prefixes 2023-09-03 18:50:42 +00:00
geky-bot
47c253cd44 Generated v2 prefixes 2023-06-30 17:56:00 +00:00
geky-bot
b144146f62 Generated v2 prefixes 2023-05-23 20:52:37 +00:00
geky-bot
816d8702c0 Generated v2 prefixes 2023-05-04 18:31:30 +00:00
geky-bot
bf82e7925c Generated v2 prefixes 2022-11-10 21:10:59 +00:00
geky-bot
103c82f3c8 Generated v2 prefixes 2022-04-14 05:02:16 +00:00
geky-bot
7127786d39 Generated v2 prefixes 2022-03-22 06:01:37 +00:00
geky-bot
4dcad66e16 Generated v2 prefixes 2021-06-12 21:48:51 +00:00
geky-bot
b816d7f898 Generated v2 prefixes 2021-01-20 02:41:58 +00:00
geky bot
c40271bcf3 Generated v2 prefixes 2020-12-08 09:20:13 +00:00
geky bot
197ad15e47 Generated v2 prefixes 2020-04-09 12:00:34 +00:00
geky bot
3e7b9da578 Generated v2 prefixes 2020-04-01 03:33:16 +00:00
geky bot
1f204e6d84 Generated v2 prefixes 2019-12-02 01:09:00 +00:00
geky bot
893325aeb9 Generated v2 prefixes 2019-10-15 16:52:45 +00:00
geky bot
c39b1de658 Generated v2 prefixes 2019-09-19 16:38:42 +00:00
geky bot
f3608e84c7 Generated v2 prefixes 2019-08-09 01:20:09 +00:00
geky bot
c08e977799 Generated v2 prefixes 2019-07-29 07:12:22 +00:00
geky bot
bc7be77625 Generated v2 prefixes 2019-07-02 01:28:14 +00:00
geky bot
96c8b6dcb3 Generated v2 prefixes 2019-05-23 22:27:35 +00:00
geky bot
895767cc9d Generated v2 prefixes 2019-04-17 00:01:02 +00:00
geky bot
87d3abba61 Generated v2 prefixes 2019-04-16 03:28:41 +00:00
geky bot
19f4eae52c Generated v2 prefixes 2019-04-12 22:49:07 +00:00
geky bot
8d4fd46a4c Generated v2 prefixes 2019-04-11 02:08:05 +00:00
61 changed files with 22406 additions and 13808 deletions

View File

@@ -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

View File

@@ -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,69 +30,72 @@ 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
run: |
# rip version from lfs.h
LFS_VERSION="$(grep -o '^#define LFS_VERSION .*$' lfs.h \
# rip version from lfs2.h
LFS2_VERSION="$(grep -o '^#define LFS2_VERSION .*$' lfs2.h \
| awk '{print $3}')"
LFS_VERSION_MAJOR="$((0xffff & ($LFS_VERSION >> 16)))"
LFS_VERSION_MINOR="$((0xffff & ($LFS_VERSION >> 0)))"
LFS2_VERSION_MAJOR="$((0xffff & ($LFS2_VERSION >> 16)))"
LFS2_VERSION_MINOR="$((0xffff & ($LFS2_VERSION >> 0)))"
# find a new patch version based on what we find in our tags
LFS_VERSION_PATCH="$( \
LFS2_VERSION_PATCH="$( \
( git describe --tags --abbrev=0 \
--match="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.*" \
--match="v$LFS2_VERSION_MAJOR.$LFS2_VERSION_MINOR.*" \
|| echo 'v0.0.-1' ) \
| awk -F '.' '{print $3+1}')"
# found new version
LFS_VERSION="v$LFS_VERSION_MAJOR`
`.$LFS_VERSION_MINOR`
`.$LFS_VERSION_PATCH"
echo "LFS_VERSION=$LFS_VERSION"
echo "LFS_VERSION=$LFS_VERSION" >> $GITHUB_ENV
echo "LFS_VERSION_MAJOR=$LFS_VERSION_MAJOR" >> $GITHUB_ENV
echo "LFS_VERSION_MINOR=$LFS_VERSION_MINOR" >> $GITHUB_ENV
echo "LFS_VERSION_PATCH=$LFS_VERSION_PATCH" >> $GITHUB_ENV
LFS2_VERSION="v$LFS2_VERSION_MAJOR`
`.$LFS2_VERSION_MINOR`
`.$LFS2_VERSION_PATCH"
echo "LFS2_VERSION=$LFS2_VERSION"
echo "LFS2_VERSION=$LFS2_VERSION" >> $GITHUB_ENV
echo "LFS2_VERSION_MAJOR=$LFS2_VERSION_MAJOR" >> $GITHUB_ENV
echo "LFS2_VERSION_MINOR=$LFS2_VERSION_MINOR" >> $GITHUB_ENV
echo "LFS2_VERSION_PATCH=$LFS2_VERSION_PATCH" >> $GITHUB_ENV
# try to find previous version?
- name: find-prev-version
continue-on-error: true
run: |
LFS_PREV_VERSION="$( \
LFS2_PREV_VERSION="$( \
git describe --tags --abbrev=0 --match 'v*' \
|| true)"
echo "LFS_PREV_VERSION=$LFS_PREV_VERSION"
echo "LFS_PREV_VERSION=$LFS_PREV_VERSION" >> $GITHUB_ENV
echo "LFS2_PREV_VERSION=$LFS2_PREV_VERSION"
echo "LFS2_PREV_VERSION=$LFS2_PREV_VERSION" >> $GITHUB_ENV
# try to find results from tests
- name: create-table
run: |
# previous results to compare against?
[ -n "$LFS_PREV_VERSION" ] && curl -sS \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/$LFS_PREV_VERSION`
[ -n "$LFS2_PREV_VERSION" ] && curl -sS \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/$LFS2_PREV_VERSION`
`?per_page=100" \
| jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[]' \
>> prev-status.json \
@@ -202,10 +205,10 @@ jobs:
# find changes from history
- name: create-changes
run: |
[ -n "$LFS_PREV_VERSION" ] || exit 0
[ -n "$LFS2_PREV_VERSION" ] || exit 0
# use explicit link to github commit so that release notes can
# be copied elsewhere
git log "$LFS_PREV_VERSION.." \
git log "$LFS2_PREV_VERSION.." \
--grep='^Merge' --invert-grep \
--format="format:[\`%h\`](`
`https://github.com/$GITHUB_REPOSITORY/commit/%h) %s" \
@@ -217,25 +220,25 @@ jobs:
- name: create-major-branches
run: |
# create major branch
git branch "v$LFS_VERSION_MAJOR" HEAD
git branch "v$LFS2_VERSION_MAJOR" HEAD
# create major prefix branch
git config user.name ${{secrets.BOT_USER}}
git config user.email ${{secrets.BOT_EMAIL}}
git fetch "https://github.com/$GITHUB_REPOSITORY.git" \
"v$LFS_VERSION_MAJOR-prefix" || true
./scripts/changeprefix.py --git "lfs" "lfs$LFS_VERSION_MAJOR"
git branch "v$LFS_VERSION_MAJOR-prefix" $( \
"v$LFS2_VERSION_MAJOR-prefix" || true
./scripts/changeprefix.py --git "lfs2" "lfs2$LFS2_VERSION_MAJOR"
git branch "v$LFS2_VERSION_MAJOR-prefix" $( \
git commit-tree $(git write-tree) \
$(git rev-parse --verify -q FETCH_HEAD | sed -e 's/^/-p /') \
-p HEAD \
-m "Generated v$LFS_VERSION_MAJOR prefixes")
-m "Generated v$LFS2_VERSION_MAJOR prefixes")
git reset --hard
# push!
git push --atomic origin \
"v$LFS_VERSION_MAJOR" \
"v$LFS_VERSION_MAJOR-prefix"
"v$LFS2_VERSION_MAJOR" \
"v$LFS2_VERSION_MAJOR-prefix"
# build release notes
- name: create-release
@@ -251,10 +254,10 @@ jobs:
curl -sS -X POST -H "authorization: token ${{secrets.BOT_TOKEN}}" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases" \
-d "$(jq -n --rawfile release release.txt '{
tag_name: env.LFS_VERSION,
name: env.LFS_VERSION | rtrimstr(".0"),
tag_name: env.LFS2_VERSION,
name: env.LFS2_VERSION | rtrimstr(".0"),
target_commitish: "${{github.event.workflow_run.head_sha}}",
draft: env.LFS_VERSION | endswith(".0"),
draft: env.LFS2_VERSION | endswith(".0"),
body: $release,
}' | tee /dev/stderr)"

View File

@@ -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

View File

@@ -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
@@ -102,142 +102,142 @@ jobs:
- name: cov
if: ${{matrix.arch == 'x86_64'}}
run: |
make lfs.cov.csv
./scripts/cov.py -u lfs.cov.csv
make lfs2.cov.csv
./scripts/cov.py -u lfs2.cov.csv
mkdir -p cov
cp lfs.cov.csv cov/cov.csv
cp lfs2.cov.csv cov/cov.csv
# find compile-time measurements
- name: sizes
run: |
make clean
CFLAGS="$CFLAGS \
-DLFS_NO_ASSERT \
-DLFS_NO_DEBUG \
-DLFS_NO_WARN \
-DLFS_NO_ERROR" \
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 \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR" \
make lfs2.code.csv lfs2.data.csv lfs2.stack.csv lfs2.structs.csv
./scripts/structs.py -u lfs2.structs.csv
./scripts/summary.py lfs2.code.csv lfs2.data.csv lfs2.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}}.code.csv
cp lfs.data.csv sizes/${{matrix.arch}}.data.csv
cp lfs.stack.csv sizes/${{matrix.arch}}.stack.csv
cp lfs.structs.csv sizes/${{matrix.arch}}.structs.csv
cp lfs2.code.csv sizes/${{matrix.arch}}.code.csv
cp lfs2.data.csv sizes/${{matrix.arch}}.data.csv
cp lfs2.stack.csv sizes/${{matrix.arch}}.stack.csv
cp lfs2.structs.csv sizes/${{matrix.arch}}.structs.csv
- name: sizes-readonly
run: |
make clean
CFLAGS="$CFLAGS \
-DLFS_NO_ASSERT \
-DLFS_NO_DEBUG \
-DLFS_NO_WARN \
-DLFS_NO_ERROR \
-DLFS_READONLY" \
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 \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-DLFS2_READONLY" \
make lfs2.code.csv lfs2.data.csv lfs2.stack.csv lfs2.structs.csv
./scripts/structs.py -u lfs2.structs.csv
./scripts/summary.py lfs2.code.csv lfs2.data.csv lfs2.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}}-readonly.code.csv
cp lfs.data.csv sizes/${{matrix.arch}}-readonly.data.csv
cp lfs.stack.csv sizes/${{matrix.arch}}-readonly.stack.csv
cp lfs.structs.csv sizes/${{matrix.arch}}-readonly.structs.csv
cp lfs2.code.csv sizes/${{matrix.arch}}-readonly.code.csv
cp lfs2.data.csv sizes/${{matrix.arch}}-readonly.data.csv
cp lfs2.stack.csv sizes/${{matrix.arch}}-readonly.stack.csv
cp lfs2.structs.csv sizes/${{matrix.arch}}-readonly.structs.csv
- name: sizes-threadsafe
run: |
make clean
CFLAGS="$CFLAGS \
-DLFS_NO_ASSERT \
-DLFS_NO_DEBUG \
-DLFS_NO_WARN \
-DLFS_NO_ERROR \
-DLFS_THREADSAFE" \
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 \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-DLFS2_THREADSAFE" \
make lfs2.code.csv lfs2.data.csv lfs2.stack.csv lfs2.structs.csv
./scripts/structs.py -u lfs2.structs.csv
./scripts/summary.py lfs2.code.csv lfs2.data.csv lfs2.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}}-threadsafe.code.csv
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
cp lfs2.code.csv sizes/${{matrix.arch}}-threadsafe.code.csv
cp lfs2.data.csv sizes/${{matrix.arch}}-threadsafe.data.csv
cp lfs2.stack.csv sizes/${{matrix.arch}}-threadsafe.stack.csv
cp lfs2.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 \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-DLFS2_MULTIVERSION" \
make lfs2.code.csv lfs2.data.csv lfs2.stack.csv lfs2.structs.csv
./scripts/structs.py -u lfs2.structs.csv
./scripts/summary.py lfs2.code.csv lfs2.data.csv lfs2.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
cp lfs2.code.csv sizes/${{matrix.arch}}-multiversion.code.csv
cp lfs2.data.csv sizes/${{matrix.arch}}-multiversion.data.csv
cp lfs2.stack.csv sizes/${{matrix.arch}}-multiversion.stack.csv
cp lfs2.structs.csv sizes/${{matrix.arch}}-multiversion.structs.csv
- name: sizes-migrate
run: |
make clean
CFLAGS="$CFLAGS \
-DLFS_NO_ASSERT \
-DLFS_NO_DEBUG \
-DLFS_NO_WARN \
-DLFS_NO_ERROR \
-DLFS_MIGRATE" \
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 \
-DLFS2_NO_ASSERT \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-DLFS2_MIGRATE" \
make lfs2.code.csv lfs2.data.csv lfs2.stack.csv lfs2.structs.csv
./scripts/structs.py -u lfs2.structs.csv
./scripts/summary.py lfs2.code.csv lfs2.data.csv lfs2.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}}-migrate.code.csv
cp lfs.data.csv sizes/${{matrix.arch}}-migrate.data.csv
cp lfs.stack.csv sizes/${{matrix.arch}}-migrate.stack.csv
cp lfs.structs.csv sizes/${{matrix.arch}}-migrate.structs.csv
cp lfs2.code.csv sizes/${{matrix.arch}}-migrate.code.csv
cp lfs2.data.csv sizes/${{matrix.arch}}-migrate.data.csv
cp lfs2.stack.csv sizes/${{matrix.arch}}-migrate.stack.csv
cp lfs2.structs.csv sizes/${{matrix.arch}}-migrate.structs.csv
- name: sizes-error-asserts
run: |
make clean
CFLAGS="$CFLAGS \
-DLFS_NO_DEBUG \
-DLFS_NO_WARN \
-DLFS_NO_ERROR \
-D'LFS_ASSERT(test)=do {if(!(test)) {return -1;}} while(0)'" \
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 \
-DLFS2_NO_DEBUG \
-DLFS2_NO_WARN \
-DLFS2_NO_ERROR \
-D'LFS2_ASSERT(test)=do {if(!(test)) {return -1;}} while(0)'" \
make lfs2.code.csv lfs2.data.csv lfs2.stack.csv lfs2.structs.csv
./scripts/structs.py -u lfs2.structs.csv
./scripts/summary.py lfs2.code.csv lfs2.data.csv lfs2.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}}-error-asserts.code.csv
cp lfs.data.csv sizes/${{matrix.arch}}-error-asserts.data.csv
cp lfs.stack.csv sizes/${{matrix.arch}}-error-asserts.stack.csv
cp lfs.structs.csv sizes/${{matrix.arch}}-error-asserts.structs.csv
cp lfs2.code.csv sizes/${{matrix.arch}}-error-asserts.code.csv
cp lfs2.data.csv sizes/${{matrix.arch}}-error-asserts.data.csv
cp lfs2.stack.csv sizes/${{matrix.arch}}-error-asserts.stack.csv
cp lfs2.structs.csv sizes/${{matrix.arch}}-error-asserts.structs.csv
# 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
@@ -357,11 +357,11 @@ jobs:
TESTFLAGS="$TESTFLAGS -P${{matrix.pls}} test_dirs test_relocations" \
make test
# run with LFS_NO_INTRINSICS to make sure that works
# run with LFS2_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
@@ -372,13 +372,52 @@ jobs:
python3 --version
- name: test-no-intrinsics
run: |
CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test
CFLAGS="$CFLAGS -DLFS2_NO_INTRINSICS" make test
# run LFS_MULTIVERSION tests
test-multiversion:
runs-on: ubuntu-22.04
test-shrink:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- 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-no-intrinsics
run: |
CFLAGS="$CFLAGS -DLFS2_SHRINKNONRELOCATING" make test
# run with all trace options enabled to at least make sure these
# all compile
test-yes-trace:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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-yes-trace
run: |
CFLAGS="$CFLAGS \
-DLFS2_YES_TRACE \
-DLFS2_RAMBD_YES_TRACE \
-DLFS2_FILEBD_YES_TRACE \
-DLFS2_RAMBD_YES_TRACE" \
make test
# run LFS2_MULTIVERSION tests
test-multiversion:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: install
run: |
# need a few things
@@ -389,13 +428,13 @@ jobs:
python3 --version
- name: test-multiversion
run: |
CFLAGS="$CFLAGS -DLFS_MULTIVERSION" make test
CFLAGS="$CFLAGS -DLFS2_MULTIVERSION" make test
# run tests on the older version lfs2.0
test-lfs2_0:
runs-on: ubuntu-22.04
# run tests on the older version lfs22.0
test-lfs22_0:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: install
run: |
# need a few things
@@ -404,17 +443,17 @@ jobs:
pip3 install toml
gcc --version
python3 --version
- name: test-lfs2_0
- name: test-lfs22_0
run: |
CFLAGS="$CFLAGS -DLFS_MULTIVERSION" \
CFLAGS="$CFLAGS -DLFS2_MULTIVERSION" \
TESTFLAGS="$TESTFLAGS -DDISK_VERSION=0x00020000" \
make test
# 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
@@ -431,12 +470,11 @@ jobs:
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
# compile/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
@@ -446,20 +484,16 @@ jobs:
python3 --version
- name: test-clang
run: |
# override CFLAGS since Clang does not support -fcallgraph-info
# and -ftrack-macro-expansions
make \
CC=clang \
CFLAGS="$CFLAGS -MMD -g3 -I. -std=c99 -Wall -Wextra -pedantic" \
test
CC=clang \
make test
# run benchmarks
#
# 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
@@ -474,24 +508,24 @@ jobs:
make bench
# find bench results
make lfs.bench.csv
./scripts/summary.py lfs.bench.csv \
make lfs2.bench.csv
./scripts/summary.py lfs2.bench.csv \
-bsuite \
-freaded=bench_readed \
-fproged=bench_proged \
-ferased=bench_erased
mkdir -p bench
cp lfs.bench.csv bench/bench.csv
cp lfs2.bench.csv bench/bench.csv
# find perfbd results
make lfs.perfbd.csv
./scripts/perfbd.py -u lfs.perfbd.csv
make lfs2.perfbd.csv
./scripts/perfbd.py -u lfs2.perfbd.csv
mkdir -p bench
cp lfs.perfbd.csv bench/perfbd.csv
cp lfs2.perfbd.csv bench/perfbd.csv
# create bench statuses
- name: upload-bench
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: bench
path: bench
@@ -525,24 +559,24 @@ 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
# checkout the current pr target into lfs2p
- uses: actions/checkout@v4
if: ${{github.event_name == 'pull_request'}}
with:
ref: ${{github.event.pull_request.base.ref}}
path: lfsp
path: lfs2p
- name: install
if: ${{github.event_name == 'pull_request'}}
run: |
@@ -552,27 +586,27 @@ jobs:
pip3 install toml
gcc --version
python3 --version
# adjust prefix of lfsp
# adjust prefix of lfs2p
- name: changeprefix
if: ${{github.event_name == 'pull_request'}}
run: |
./scripts/changeprefix.py lfs lfsp lfsp/*.h lfsp/*.c
./scripts/changeprefix.py lfs2 lfs2p lfs2p/*.h lfs2p/*.c
- name: test-compat
if: ${{github.event_name == 'pull_request'}}
run: |
TESTS=tests/test_compat.toml \
SRC="$(find . lfsp -name '*.c' -maxdepth 1 \
SRC="$(find . lfs2p -name '*.c' -maxdepth 1 \
-and -not -name '*.t.*' \
-and -not -name '*.b.*')" \
CFLAGS="-DLFSP=lfsp/lfsp.h" \
CFLAGS="-DLFS2P=lfs2p/lfs2p.h" \
make test
# 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 +616,7 @@ jobs:
gcc --version
python3 --version
fusermount -V
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
repository: littlefs-project/littlefs-fuse
ref: v2
@@ -605,8 +639,8 @@ jobs:
# self-host test
make -C littlefs-fuse
littlefs-fuse/lfs --format $LOOP
littlefs-fuse/lfs $LOOP mount
littlefs-fuse/lfs2 --format $LOOP
littlefs-fuse/lfs2 $LOOP mount
ls mount
mkdir mount/littlefs
@@ -619,10 +653,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 +666,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
@@ -662,8 +696,8 @@ jobs:
make -C v2
# run self-host test with v1
v1/lfs --format $LOOP
v1/lfs $LOOP mount
v1/lfs2 --format $LOOP
v1/lfs2 $LOOP mount
ls mount
mkdir mount/littlefs
@@ -678,8 +712,8 @@ jobs:
cd ../..
fusermount -u mount
v2/lfs --migrate $LOOP
v2/lfs $LOOP mount
v2/lfs2 --migrate $LOOP
v2/lfs2 $LOOP mount
# run self-host test with v2 right where we left off
ls mount
@@ -691,10 +725,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 +738,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 +899,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

20
.gitignore vendored
View File

@@ -9,21 +9,21 @@
*.gcno
*.gcda
*.perf
lfs
lfs2
liblfs.a
# Testing things
runners/test_runner
runners/bench_runner
lfs.code.csv
lfs.data.csv
lfs.stack.csv
lfs.structs.csv
lfs.cov.csv
lfs.perf.csv
lfs.perfbd.csv
lfs.test.csv
lfs.bench.csv
lfs2.code.csv
lfs2.data.csv
lfs2.stack.csv
lfs2.structs.csv
lfs2.cov.csv
lfs2.perf.csv
lfs2.perfbd.csv
lfs2.test.csv
lfs2.bench.csv
# Misc
tags

View File

@@ -59,7 +59,7 @@ This leaves us with three major requirements for an embedded filesystem.
RAM to temporarily store filesystem metadata.
For ROM, this means we need to keep our design simple and reuse code paths
were possible. For RAM we have a stronger requirement, all RAM usage is
where possible. For RAM we have a stronger requirement, all RAM usage is
bounded. This means RAM usage does not grow as the filesystem changes in
size or number of files. This creates a unique challenge as even presumably
simple operations, such as traversing the filesystem, become surprisingly
@@ -626,7 +626,7 @@ log&#8322;_n_ pointers that skip to different preceding elements of the
skip-list.
The name comes from heavy use of the [CTZ instruction][wikipedia-ctz], which
lets us calculate the power-of-two factors efficiently. For a give block _n_,
lets us calculate the power-of-two factors efficiently. For a given block _n_,
that block contains ctz(_n_)+1 pointers.
```

124
Makefile
View File

@@ -2,7 +2,7 @@
BUILDDIR ?= .
# overridable target/src/tools/flags/etc
ifneq ($(wildcard test.c main.c),)
TARGET ?= $(BUILDDIR)/lfs
TARGET ?= $(BUILDDIR)/lfs2
else
TARGET ?= $(BUILDDIR)/liblfs.a
endif
@@ -18,6 +18,12 @@ VALGRIND ?= valgrind
GDB ?= gdb
PERF ?= perf
# guess clang or gcc (clang sometimes masquerades as gcc because of
# course it does)
ifneq ($(shell $(CC) --version | grep clang),)
NO_GCC = 1
endif
SRC ?= $(filter-out $(wildcard *.t.* *.b.*),$(wildcard *.c))
OBJ := $(SRC:%.c=$(BUILDDIR)/%.o)
DEP := $(SRC:%.c=$(BUILDDIR)/%.d)
@@ -59,19 +65,22 @@ BENCH_PERF := $(BENCH_RUNNER:%=%.perf)
BENCH_TRACE := $(BENCH_RUNNER:%=%.trace)
BENCH_CSV := $(BENCH_RUNNER:%=%.csv)
CFLAGS += -fcallgraph-info=su
CFLAGS += -g3
CFLAGS += -I.
CFLAGS += -std=c99 -Wall -Wextra -pedantic
CFLAGS += -Wmissing-prototypes
ifndef NO_GCC
CFLAGS += -fcallgraph-info=su
CFLAGS += -ftrack-macro-expansion=0
endif
ifdef DEBUG
CFLAGS += -O0
else
CFLAGS += -Os
endif
ifdef TRACE
CFLAGS += -DLFS_YES_TRACE
CFLAGS += -DLFS2_YES_TRACE
endif
ifdef YES_COV
CFLAGS += --coverage
@@ -201,43 +210,43 @@ help:
## Find the per-function code size
.PHONY: code
code: CODEFLAGS+=-S
code: $(OBJ) $(BUILDDIR)/lfs.code.csv
code: $(OBJ) $(BUILDDIR)/lfs2.code.csv
./scripts/code.py $(OBJ) $(CODEFLAGS)
## Compare per-function code size
.PHONY: code-diff
code-diff: $(OBJ)
./scripts/code.py $^ $(CODEFLAGS) -d $(BUILDDIR)/lfs.code.csv
./scripts/code.py $^ $(CODEFLAGS) -d $(BUILDDIR)/lfs2.code.csv
## Find the per-function data size
.PHONY: data
data: DATAFLAGS+=-S
data: $(OBJ) $(BUILDDIR)/lfs.data.csv
data: $(OBJ) $(BUILDDIR)/lfs2.data.csv
./scripts/data.py $(OBJ) $(DATAFLAGS)
## Compare per-function data size
.PHONY: data-diff
data-diff: $(OBJ)
./scripts/data.py $^ $(DATAFLAGS) -d $(BUILDDIR)/lfs.data.csv
./scripts/data.py $^ $(DATAFLAGS) -d $(BUILDDIR)/lfs2.data.csv
## Find the per-function stack usage
.PHONY: stack
stack: STACKFLAGS+=-S
stack: $(CI) $(BUILDDIR)/lfs.stack.csv
stack: $(CI) $(BUILDDIR)/lfs2.stack.csv
./scripts/stack.py $(CI) $(STACKFLAGS)
## Compare per-function stack usage
.PHONY: stack-diff
stack-diff: $(CI)
./scripts/stack.py $^ $(STACKFLAGS) -d $(BUILDDIR)/lfs.stack.csv
./scripts/stack.py $^ $(STACKFLAGS) -d $(BUILDDIR)/lfs2.stack.csv
## Find function sizes
.PHONY: funcs
funcs: SUMMARYFLAGS+=-S
funcs: \
$(BUILDDIR)/lfs.code.csv \
$(BUILDDIR)/lfs.data.csv \
$(BUILDDIR)/lfs.stack.csv
$(BUILDDIR)/lfs2.code.csv \
$(BUILDDIR)/lfs2.data.csv \
$(BUILDDIR)/lfs2.stack.csv
$(strip ./scripts/summary.py $^ \
-bfunction \
-fcode=code_size \
@@ -258,26 +267,26 @@ funcs-diff: $(OBJ) $(CI)
-fdata=data_size \
-fstack=stack_limit --max=stack \
$(SUMMARYFLAGS) -d <(./scripts/summary.py \
$(BUILDDIR)/lfs.code.csv \
$(BUILDDIR)/lfs.data.csv \
$(BUILDDIR)/lfs.stack.csv \
$(BUILDDIR)/lfs2.code.csv \
$(BUILDDIR)/lfs2.data.csv \
$(BUILDDIR)/lfs2.stack.csv \
-q $(SUMMARYFLAGS) -o-))
## Find struct sizes
.PHONY: structs
structs: STRUCTSFLAGS+=-S
structs: $(OBJ) $(BUILDDIR)/lfs.structs.csv
structs: $(OBJ) $(BUILDDIR)/lfs2.structs.csv
./scripts/structs.py $(OBJ) $(STRUCTSFLAGS)
## Compare struct sizes
.PHONY: structs-diff
structs-diff: $(OBJ)
./scripts/structs.py $^ $(STRUCTSFLAGS) -d $(BUILDDIR)/lfs.structs.csv
./scripts/structs.py $^ $(STRUCTSFLAGS) -d $(BUILDDIR)/lfs2.structs.csv
## Find the line/branch coverage after a test run
.PHONY: cov
cov: COVFLAGS+=-s
cov: $(GCDA) $(BUILDDIR)/lfs.cov.csv
cov: $(GCDA) $(BUILDDIR)/lfs2.cov.csv
$(strip ./scripts/cov.py $(GCDA) \
$(patsubst %,-F%,$(SRC)) \
$(COVFLAGS))
@@ -287,12 +296,12 @@ cov: $(GCDA) $(BUILDDIR)/lfs.cov.csv
cov-diff: $(GCDA)
$(strip ./scripts/cov.py $^ \
$(patsubst %,-F%,$(SRC)) \
$(COVFLAGS) -d $(BUILDDIR)/lfs.cov.csv)
$(COVFLAGS) -d $(BUILDDIR)/lfs2.cov.csv)
## Find the perf results after bench run with YES_PERF
.PHONY: perf
perf: PERFFLAGS+=-S
perf: $(BENCH_PERF) $(BUILDDIR)/lfs.perf.csv
perf: $(BENCH_PERF) $(BUILDDIR)/lfs2.perf.csv
$(strip ./scripts/perf.py $(BENCH_PERF) \
$(patsubst %,-F%,$(SRC)) \
$(PERFFLAGS))
@@ -302,12 +311,12 @@ perf: $(BENCH_PERF) $(BUILDDIR)/lfs.perf.csv
perf-diff: $(BENCH_PERF)
$(strip ./scripts/perf.py $^ \
$(patsubst %,-F%,$(SRC)) \
$(PERFFLAGS) -d $(BUILDDIR)/lfs.perf.csv)
$(PERFFLAGS) -d $(BUILDDIR)/lfs2.perf.csv)
## Find the perfbd results after a bench run
.PHONY: perfbd
perfbd: PERFBDFLAGS+=-S
perfbd: $(BENCH_TRACE) $(BUILDDIR)/lfs.perfbd.csv
perfbd: $(BENCH_TRACE) $(BUILDDIR)/lfs2.perfbd.csv
$(strip ./scripts/perfbd.py $(BENCH_RUNNER) $(BENCH_TRACE) \
$(patsubst %,-F%,$(SRC)) \
$(PERFBDFLAGS))
@@ -317,15 +326,15 @@ perfbd: $(BENCH_TRACE) $(BUILDDIR)/lfs.perfbd.csv
perfbd-diff: $(BENCH_TRACE)
$(strip ./scripts/perfbd.py $(BENCH_RUNNER) $^ \
$(patsubst %,-F%,$(SRC)) \
$(PERFBDFLAGS) -d $(BUILDDIR)/lfs.perfbd.csv)
$(PERFBDFLAGS) -d $(BUILDDIR)/lfs2.perfbd.csv)
## Find a summary of compile-time sizes
.PHONY: summary sizes
summary sizes: \
$(BUILDDIR)/lfs.code.csv \
$(BUILDDIR)/lfs.data.csv \
$(BUILDDIR)/lfs.stack.csv \
$(BUILDDIR)/lfs.structs.csv
$(BUILDDIR)/lfs2.code.csv \
$(BUILDDIR)/lfs2.data.csv \
$(BUILDDIR)/lfs2.stack.csv \
$(BUILDDIR)/lfs2.structs.csv
$(strip ./scripts/summary.py $^ \
-fcode=code_size \
-fdata=data_size \
@@ -347,10 +356,10 @@ summary-diff sizes-diff: $(OBJ) $(CI)
-fstack=stack_limit --max=stack \
-fstructs=struct_size \
-Y $(SUMMARYFLAGS) -d <(./scripts/summary.py \
$(BUILDDIR)/lfs.code.csv \
$(BUILDDIR)/lfs.data.csv \
$(BUILDDIR)/lfs.stack.csv \
$(BUILDDIR)/lfs.structs.csv \
$(BUILDDIR)/lfs2.code.csv \
$(BUILDDIR)/lfs2.data.csv \
$(BUILDDIR)/lfs2.stack.csv \
$(BUILDDIR)/lfs2.structs.csv \
-q $(SUMMARYFLAGS) -o-))
## Build the test-runner
@@ -391,7 +400,7 @@ test-list: test-runner
## Summarize the testmarks
.PHONY: testmarks
testmarks: SUMMARYFLAGS+=-spassed
testmarks: $(TEST_CSV) $(BUILDDIR)/lfs.test.csv
testmarks: $(TEST_CSV) $(BUILDDIR)/lfs2.test.csv
$(strip ./scripts/summary.py $(TEST_CSV) \
-bsuite \
-fpassed=test_passed \
@@ -403,7 +412,7 @@ testmarks-diff: $(TEST_CSV)
$(strip ./scripts/summary.py $^ \
-bsuite \
-fpassed=test_passed \
$(SUMMARYFLAGS) -d $(BUILDDIR)/lfs.test.csv)
$(SUMMARYFLAGS) -d $(BUILDDIR)/lfs2.test.csv)
## Build the bench-runner
.PHONY: bench-runner build-bench
@@ -443,7 +452,7 @@ bench-list: bench-runner
## Summarize the benchmarks
.PHONY: benchmarks
benchmarks: SUMMARYFLAGS+=-Serased -Sproged -Sreaded
benchmarks: $(BENCH_CSV) $(BUILDDIR)/lfs.bench.csv
benchmarks: $(BENCH_CSV) $(BUILDDIR)/lfs2.bench.csv
$(strip ./scripts/summary.py $(BENCH_CSV) \
-bsuite \
-freaded=bench_readed \
@@ -459,53 +468,54 @@ benchmarks-diff: $(BENCH_CSV)
-freaded=bench_readed \
-fproged=bench_proged \
-ferased=bench_erased \
$(SUMMARYFLAGS) -d $(BUILDDIR)/lfs.bench.csv)
$(SUMMARYFLAGS) -d $(BUILDDIR)/lfs2.bench.csv)
# rules
-include $(DEP)
-include $(TEST_DEP)
-include $(BENCH_DEP)
.SUFFIXES:
.SECONDARY:
$(BUILDDIR)/lfs: $(OBJ)
$(BUILDDIR)/lfs2: $(OBJ)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
$(BUILDDIR)/liblfs.a: $(OBJ)
$(AR) rcs $@ $^
$(BUILDDIR)/lfs.code.csv: $(OBJ)
$(BUILDDIR)/lfs2.code.csv: $(OBJ)
./scripts/code.py $^ -q $(CODEFLAGS) -o $@
$(BUILDDIR)/lfs.data.csv: $(OBJ)
$(BUILDDIR)/lfs2.data.csv: $(OBJ)
./scripts/data.py $^ -q $(DATAFLAGS) -o $@
$(BUILDDIR)/lfs.stack.csv: $(CI)
$(BUILDDIR)/lfs2.stack.csv: $(CI)
./scripts/stack.py $^ -q $(STACKFLAGS) -o $@
$(BUILDDIR)/lfs.structs.csv: $(OBJ)
$(BUILDDIR)/lfs2.structs.csv: $(OBJ)
./scripts/structs.py $^ -q $(STRUCTSFLAGS) -o $@
$(BUILDDIR)/lfs.cov.csv: $(GCDA)
$(BUILDDIR)/lfs2.cov.csv: $(GCDA)
$(strip ./scripts/cov.py $^ \
$(patsubst %,-F%,$(SRC)) \
-q $(COVFLAGS) -o $@)
$(BUILDDIR)/lfs.perf.csv: $(BENCH_PERF)
$(BUILDDIR)/lfs2.perf.csv: $(BENCH_PERF)
$(strip ./scripts/perf.py $^ \
$(patsubst %,-F%,$(SRC)) \
-q $(PERFFLAGS) -o $@)
$(BUILDDIR)/lfs.perfbd.csv: $(BENCH_TRACE)
$(BUILDDIR)/lfs2.perfbd.csv: $(BENCH_TRACE)
$(strip ./scripts/perfbd.py $(BENCH_RUNNER) $^ \
$(patsubst %,-F%,$(SRC)) \
-q $(PERFBDFLAGS) -o $@)
$(BUILDDIR)/lfs.test.csv: $(TEST_CSV)
$(BUILDDIR)/lfs2.test.csv: $(TEST_CSV)
cp $^ $@
$(BUILDDIR)/lfs.bench.csv: $(BENCH_CSV)
$(BUILDDIR)/lfs2.bench.csv: $(BENCH_CSV)
cp $^ $@
$(BUILDDIR)/runners/test_runner: $(TEST_OBJ)
@@ -526,10 +536,10 @@ $(BUILDDIR)/%.s: %.c
$(CC) -S $(CFLAGS) $< -o $@
$(BUILDDIR)/%.c: %.a.c
./scripts/prettyasserts.py -p LFS_ASSERT $< -o $@
./scripts/prettyasserts.py -p LFS2_ASSERT $< -o $@
$(BUILDDIR)/%.c: $(BUILDDIR)/%.a.c
./scripts/prettyasserts.py -p LFS_ASSERT $< -o $@
./scripts/prettyasserts.py -p LFS2_ASSERT $< -o $@
$(BUILDDIR)/%.t.a.c: %.toml
./scripts/test.py -c $< $(TESTCFLAGS) -o $@
@@ -546,17 +556,17 @@ $(BUILDDIR)/%.b.a.c: %.c $(BENCHES)
## Clean everything
.PHONY: clean
clean:
rm -f $(BUILDDIR)/lfs
rm -f $(BUILDDIR)/lfs2
rm -f $(BUILDDIR)/liblfs.a
rm -f $(BUILDDIR)/lfs.code.csv
rm -f $(BUILDDIR)/lfs.data.csv
rm -f $(BUILDDIR)/lfs.stack.csv
rm -f $(BUILDDIR)/lfs.structs.csv
rm -f $(BUILDDIR)/lfs.cov.csv
rm -f $(BUILDDIR)/lfs.perf.csv
rm -f $(BUILDDIR)/lfs.perfbd.csv
rm -f $(BUILDDIR)/lfs.test.csv
rm -f $(BUILDDIR)/lfs.bench.csv
rm -f $(BUILDDIR)/lfs2.code.csv
rm -f $(BUILDDIR)/lfs2.data.csv
rm -f $(BUILDDIR)/lfs2.stack.csv
rm -f $(BUILDDIR)/lfs2.structs.csv
rm -f $(BUILDDIR)/lfs2.cov.csv
rm -f $(BUILDDIR)/lfs2.perf.csv
rm -f $(BUILDDIR)/lfs2.perfbd.csv
rm -f $(BUILDDIR)/lfs2.test.csv
rm -f $(BUILDDIR)/lfs2.bench.csv
rm -f $(OBJ)
rm -f $(DEP)
rm -f $(ASM)

123
README.md
View File

@@ -32,14 +32,14 @@ main runs. The program can be interrupted at any time without losing track
of how many times it has been booted and without corrupting the filesystem:
``` c
#include "lfs.h"
#include "lfs2.h"
// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;
lfs2_t lfs2;
lfs2_file_t file;
// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
const struct lfs2_config cfg = {
// block device operations
.read = user_provided_block_device_read,
.prog = user_provided_block_device_prog,
@@ -59,30 +59,30 @@ const struct lfs_config cfg = {
// entry point
int main(void) {
// mount the filesystem
int err = lfs_mount(&lfs, &cfg);
int err = lfs2_mount(&lfs2, &cfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err) {
lfs_format(&lfs, &cfg);
lfs_mount(&lfs, &cfg);
lfs2_format(&lfs2, &cfg);
lfs2_mount(&lfs2, &cfg);
}
// read current count
uint32_t boot_count = 0;
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
lfs2_file_open(&lfs2, &file, "boot_count", LFS2_O_RDWR | LFS2_O_CREAT);
lfs2_file_read(&lfs2, &file, &boot_count, sizeof(boot_count));
// update boot count
boot_count += 1;
lfs_file_rewind(&lfs, &file);
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
lfs2_file_rewind(&lfs2, &file);
lfs2_file_write(&lfs2, &file, &boot_count, sizeof(boot_count));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs, &file);
lfs2_file_close(&lfs2, &file);
// release any resources we were using
lfs_unmount(&lfs);
lfs2_unmount(&lfs2);
// print the boot count
printf("boot_count: %d\n", boot_count);
@@ -92,7 +92,7 @@ int main(void) {
## Usage
Detailed documentation (or at least as much detail as is currently available)
can be found in the comments in [lfs.h](lfs.h).
can be found in the comments in [lfs2.h](lfs2.h).
littlefs takes in a configuration structure that defines how the filesystem
operates. The configuration struct provides the filesystem with the block
@@ -100,9 +100,9 @@ device operations and dimensions, tweakable parameters that tradeoff memory
usage for performance, and optional static buffers if the user wants to avoid
dynamic memory.
The state of the littlefs is stored in the `lfs_t` type which is left up
The state of the littlefs is stored in the `lfs2_t` type which is left up
to the user to allocate, allowing multiple filesystems to be in use
simultaneously. With the `lfs_t` and configuration struct, a user can
simultaneously. With the `lfs2_t` and configuration struct, a user can
format a block device or mount the filesystem.
Once mounted, the littlefs provides a full set of POSIX-like file and
@@ -119,11 +119,11 @@ Littlefs is written in C, and specifically should compile with any compiler
that conforms to the `C99` standard.
All littlefs calls have the potential to return a negative error code. The
errors can be either one of those found in the `enum lfs_error` in
[lfs.h](lfs.h), or an error returned by the user's block device operations.
errors can be either one of those found in the `enum lfs2_error` in
[lfs2.h](lfs2.h), or an error returned by the user's block device operations.
In the configuration struct, the `prog` and `erase` function provided by the
user may return a `LFS_ERR_CORRUPT` error if the implementation already can
user may return a `LFS2_ERR_CORRUPT` error if the implementation already can
detect corrupt blocks. However, the wear leveling does not depend on the return
code of these functions, instead all data is read back and checked for
integrity.
@@ -192,13 +192,54 @@ More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and
## Testing
The littlefs comes with a test suite designed to run on a PC using the
[emulated block device](bd/lfs_testbd.h) found in the `bd` directory.
[emulated block device](bd/lfs2_testbd.h) found in the `bd` directory.
The tests assume a Linux environment and can be started with make:
``` bash
make test
```
Tests are implemented in C in the .toml files found in the `tests` directory.
When developing a feature or fixing a bug, it is frequently useful to run a
single test case or suite of tests:
``` bash
./scripts/test.py -l runners/test_runner # list available test suites
./scripts/test.py -L runners/test_runner test_dirs # list available test cases
./scripts/test.py runners/test_runner test_dirs # run a specific test suite
```
If an assert fails in a test, test.py will try to print information about the
failure:
``` bash
tests/test_dirs.toml:1:failure: test_dirs_root:1g12gg2 (PROG_SIZE=16, ERASE_SIZE=512) failed
tests/test_dirs.toml:5:assert: assert failed with 0, expected eq 42
lfs2_mount(&lfs2, cfg) => 42;
```
This includes the test id, which can be passed to test.py to run only that
specific test permutation:
``` bash
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 # run a specific test permutation
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 --gdb # drop into gdb on failure
```
Some other flags that may be useful:
```bash
./scripts/test.py runners/test_runner -b -j # run tests in parallel
./scripts/test.py runners/test_runner -v -O- # redirect stdout to stdout
./scripts/test.py runners/test_runner -ddisk # capture resulting disk image
```
See `-h/--help` for a full list of available flags:
``` bash
./scripts/test.py --help
```
## License
The littlefs is provided under the [BSD-3-Clause] license. See
@@ -231,11 +272,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 +315,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

47
SPEC.md
View File

@@ -1,7 +1,7 @@
## littlefs technical specification
This is the technical specification of the little filesystem with on-disk
version lfs2.1. This document covers the technical details of how the littlefs
version lfs22.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).
@@ -294,7 +294,7 @@ Metadata tag fields:
What follows is an exhaustive list of metadata in littlefs.
---
#### `0x401` LFS_TYPE_CREATE
#### `0x401` LFS2_TYPE_CREATE
Creates a new file with this id. Note that files in a metadata block
don't necessarily need a create tag. All a create does is move over any
@@ -305,14 +305,14 @@ The create and delete tags allow littlefs to keep files in a directory
ordered alphabetically by filename.
---
#### `0x4ff` LFS_TYPE_DELETE
#### `0x4ff` LFS2_TYPE_DELETE
Deletes the file with this id. An inverse to create, this tag moves over
any files neighboring this id similar to a deletion from an imaginary
array of files.
---
#### `0x0xx` LFS_TYPE_NAME
#### `0x0xx` LFS2_TYPE_NAME
Associates the id with a file name and file type.
@@ -345,14 +345,14 @@ Name fields:
2. **file name** - File name stored as an ASCII string.
---
#### `0x001` LFS_TYPE_REG
#### `0x001` LFS2_TYPE_REG
Initializes the id + name as a regular file.
How each file is stored depends on its struct tag, which is described below.
---
#### `0x002` LFS_TYPE_DIR
#### `0x002` LFS2_TYPE_DIR
Initializes the id + name as a directory.
@@ -361,7 +361,7 @@ each pair containing any number of files in alphabetical order. A pointer to
the directory is stored in the struct tag, which is described below.
---
#### `0x0ff` LFS_TYPE_SUPERBLOCK
#### `0x0ff` LFS2_TYPE_SUPERBLOCK
Initializes the id as a superblock entry.
@@ -441,12 +441,13 @@ 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
#### `0x2xx` LFS2_TYPE_STRUCT
Associates the id with an on-disk data structure.
@@ -457,7 +458,7 @@ Any type of struct supersedes all other structs associated with the id. For
example, appending a ctz-struct replaces an inline-struct on the same file.
---
#### `0x200` LFS_TYPE_DIRSTRUCT
#### `0x200` LFS2_TYPE_DIRSTRUCT
Gives the id a directory data structure.
@@ -499,7 +500,7 @@ Dir-struct fields:
in the directory.
---
#### `0x201` LFS_TYPE_INLINESTRUCT
#### `0x201` LFS2_TYPE_INLINESTRUCT
Gives the id an inline data structure.
@@ -523,7 +524,7 @@ Inline-struct fields:
1. **Inline data** - File data stored directly in the metadata-pair.
---
#### `0x202` LFS_TYPE_CTZSTRUCT
#### `0x202` LFS2_TYPE_CTZSTRUCT
Gives the id a CTZ skip-list data structure.
@@ -578,7 +579,7 @@ CTZ-struct fields:
2. **File size (32-bits)** - Size of the file in bytes.
---
#### `0x3xx` LFS_TYPE_USERATTR
#### `0x3xx` LFS2_TYPE_USERATTR
Attaches a user attribute to an id.
@@ -612,7 +613,7 @@ User-attr fields:
2. **Attr data** - The data associated with the user attribute.
---
#### `0x6xx` LFS_TYPE_TAIL
#### `0x6xx` LFS2_TYPE_TAIL
Provides the tail pointer for the metadata pair itself.
@@ -678,7 +679,7 @@ Tail fields:
2. **Metadata pair (8-bytes)** - Pointer to the next metadata-pair.
---
#### `0x600` LFS_TYPE_SOFTTAIL
#### `0x600` LFS2_TYPE_SOFTTAIL
Provides a tail pointer that points to the next metadata pair in the
filesystem.
@@ -687,7 +688,7 @@ In this case, the next metadata pair is not a part of our current directory
and should only be followed when traversing the entire filesystem.
---
#### `0x601` LFS_TYPE_HARDTAIL
#### `0x601` LFS2_TYPE_HARDTAIL
Provides a tail pointer that points to the next metadata pair in the
directory.
@@ -698,7 +699,7 @@ metadata pair should only contain filenames greater than any filename in the
current pair.
---
#### `0x7xx` LFS_TYPE_GSTATE
#### `0x7xx` LFS2_TYPE_GSTATE
Provides delta bits for global state entries.
@@ -728,7 +729,7 @@ is stored in the chunk field. Currently, the only global state is move state,
which is outlined below.
---
#### `0x7ff` LFS_TYPE_MOVESTATE
#### `0x7ff` LFS2_TYPE_MOVESTATE
Provides delta bits for the global move state.
@@ -781,7 +782,7 @@ Move state fields:
the move.
---
#### `0x5xx` LFS_TYPE_CRC
#### `0x5xx` LFS2_TYPE_CRC
Last but not least, the CRC tag marks the end of a commit and provides a
checksum for any commits to the metadata block.
@@ -826,9 +827,9 @@ CRC fields:
are made about the contents.
---
#### `0x5ff` LFS_TYPE_FCRC
#### `0x5ff` LFS2_TYPE_FCRC
Added in lfs2.1, the optional FCRC tag contains a checksum of some amount of
Added in lfs22.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.

739
bd/lfs2_emubd.c Normal file
View File

@@ -0,0 +1,739 @@
/*
* Emulating block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#include "bd/lfs2_emubd.h"
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#endif
// access to lazily-allocated/copy-on-write blocks
//
// Note we can only modify a block if we have exclusive access to it (rc == 1)
//
static lfs2_emubd_block_t *lfs2_emubd_incblock(lfs2_emubd_block_t *block) {
if (block) {
block->rc += 1;
}
return block;
}
static void lfs2_emubd_decblock(lfs2_emubd_block_t *block) {
if (block) {
block->rc -= 1;
if (block->rc == 0) {
free(block);
}
}
}
static lfs2_emubd_block_t *lfs2_emubd_mutblock(
const struct lfs2_config *cfg,
lfs2_emubd_block_t **block) {
lfs2_emubd_t *bd = cfg->context;
lfs2_emubd_block_t *block_ = *block;
if (block_ && block_->rc == 1) {
// rc == 1? can modify
return block_;
} else if (block_) {
// rc > 1? need to create a copy
lfs2_emubd_block_t *nblock = malloc(
sizeof(lfs2_emubd_block_t) + bd->cfg->erase_size);
if (!nblock) {
return NULL;
}
memcpy(nblock, block_,
sizeof(lfs2_emubd_block_t) + bd->cfg->erase_size);
nblock->rc = 1;
lfs2_emubd_decblock(block_);
*block = nblock;
return nblock;
} else {
// no block? need to allocate
lfs2_emubd_block_t *nblock = malloc(
sizeof(lfs2_emubd_block_t) + bd->cfg->erase_size);
if (!nblock) {
return NULL;
}
nblock->rc = 1;
nblock->wear = 0;
// zero for consistency
memset(nblock->data,
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
bd->cfg->erase_size);
*block = nblock;
return nblock;
}
}
// emubd create/destroy
int lfs2_emubd_create(const struct lfs2_config *cfg,
const struct lfs2_emubd_config *bdcfg) {
LFS2_EMUBD_TRACE("lfs2_emubd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
".erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, "
".powerloss_data=%p, .track_branches=%d})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
(void*)bdcfg,
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
bdcfg->erase_count, bdcfg->erase_value, bdcfg->erase_cycles,
bdcfg->badblock_behavior, bdcfg->power_cycles,
bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
bdcfg->powerloss_data, bdcfg->track_branches);
lfs2_emubd_t *bd = cfg->context;
bd->cfg = bdcfg;
// allocate our block array, all blocks start as uninitialized
bd->blocks = malloc(bd->cfg->erase_count * sizeof(lfs2_emubd_block_t*));
if (!bd->blocks) {
LFS2_EMUBD_TRACE("lfs2_emubd_create -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
memset(bd->blocks, 0, bd->cfg->erase_count * sizeof(lfs2_emubd_block_t*));
// setup testing things
bd->readed = 0;
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) {
bd->disk = malloc(sizeof(lfs2_emubd_disk_t));
if (!bd->disk) {
LFS2_EMUBD_TRACE("lfs2_emubd_create -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
bd->disk->rc = 1;
bd->disk->scratch = NULL;
#ifdef _WIN32
bd->disk->fd = open(bd->cfg->disk_path,
O_RDWR | O_CREAT | O_BINARY, 0666);
#else
bd->disk->fd = open(bd->cfg->disk_path,
O_RDWR | O_CREAT, 0666);
#endif
if (bd->disk->fd < 0) {
int err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_create -> %d", err);
return err;
}
// if we're emulating erase values, we can keep a block around in
// memory of just the erase state to speed up emulated erases
if (bd->cfg->erase_value != -1) {
bd->disk->scratch = malloc(bd->cfg->erase_size);
if (!bd->disk->scratch) {
LFS2_EMUBD_TRACE("lfs2_emubd_create -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
memset(bd->disk->scratch,
bd->cfg->erase_value,
bd->cfg->erase_size);
// go ahead and erase all of the disk, otherwise the file will not
// match our internal representation
for (size_t i = 0; i < bd->cfg->erase_count; i++) {
ssize_t res = write(bd->disk->fd,
bd->disk->scratch,
bd->cfg->erase_size);
if (res < 0) {
int err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_create -> %d", err);
return err;
}
}
}
}
LFS2_EMUBD_TRACE("lfs2_emubd_create -> %d", 0);
return 0;
}
int lfs2_emubd_destroy(const struct lfs2_config *cfg) {
LFS2_EMUBD_TRACE("lfs2_emubd_destroy(%p)", (void*)cfg);
lfs2_emubd_t *bd = cfg->context;
// decrement reference counts
for (lfs2_block_t i = 0; i < bd->cfg->erase_count; i++) {
lfs2_emubd_decblock(bd->blocks[i]);
}
free(bd->blocks);
// clean up other resources
lfs2_emubd_decblock(bd->ooo_data);
if (bd->disk) {
bd->disk->rc -= 1;
if (bd->disk->rc == 0) {
close(bd->disk->fd);
free(bd->disk->scratch);
free(bd->disk);
}
}
LFS2_EMUBD_TRACE("lfs2_emubd_destroy -> %d", 0);
return 0;
}
// powerloss hook
static int lfs2_emubd_powerloss(const struct lfs2_config *cfg) {
lfs2_emubd_t *bd = cfg->context;
// emulate out-of-order writes?
lfs2_emubd_block_t *ooo_data = NULL;
if (bd->cfg->powerloss_behavior == LFS2_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] = lfs2_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 == LFS2_EMUBD_POWERLOSS_OOO
&& bd->ooo_block != -1) {
lfs2_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
int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
LFS2_EMUBD_TRACE("lfs2_emubd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_emubd_t *bd = cfg->context;
// check if read is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
LFS2_ASSERT(off % bd->cfg->read_size == 0);
LFS2_ASSERT(size % bd->cfg->read_size == 0);
LFS2_ASSERT(off+size <= bd->cfg->erase_size);
// get the block
const lfs2_emubd_block_t *b = bd->blocks[block];
if (b) {
// block bad?
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
bd->cfg->badblock_behavior == LFS2_EMUBD_BADBLOCK_READERROR) {
LFS2_EMUBD_TRACE("lfs2_emubd_read -> %d", LFS2_ERR_CORRUPT);
return LFS2_ERR_CORRUPT;
}
// read data
memcpy(buffer, &b->data[off], size);
} else {
// zero for consistency
memset(buffer,
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
size);
}
// track reads
bd->readed += size;
if (bd->cfg->read_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->read_sleep/1000000000,
.tv_nsec=bd->cfg->read_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_read -> %d", err);
return err;
}
}
LFS2_EMUBD_TRACE("lfs2_emubd_read -> %d", 0);
return 0;
}
int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
LFS2_EMUBD_TRACE("lfs2_emubd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_emubd_t *bd = cfg->context;
// check if write is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
LFS2_ASSERT(off % bd->cfg->prog_size == 0);
LFS2_ASSERT(size % bd->cfg->prog_size == 0);
LFS2_ASSERT(off+size <= bd->cfg->erase_size);
// get the block
lfs2_emubd_block_t *b = lfs2_emubd_mutblock(cfg, &bd->blocks[block]);
if (!b) {
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
// block bad?
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS2_EMUBD_BADBLOCK_PROGERROR) {
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", LFS2_ERR_CORRUPT);
return LFS2_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS2_EMUBD_BADBLOCK_PROGNOOP ||
bd->cfg->badblock_behavior ==
LFS2_EMUBD_BADBLOCK_ERASENOOP) {
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", 0);
return 0;
}
}
// were we erased properly?
if (bd->cfg->erase_value != -1) {
for (lfs2_off_t i = 0; i < size; i++) {
LFS2_ASSERT(b->data[off+i] == bd->cfg->erase_value);
}
}
// prog data
memcpy(&b->data[off], buffer, size);
// mirror to disk file?
if (bd->disk) {
off_t res1 = lseek(bd->disk->fd,
(off_t)block*bd->cfg->erase_size + (off_t)off,
SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", err);
return err;
}
ssize_t res2 = write(bd->disk->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", err);
return err;
}
}
// track progs
bd->proged += size;
if (bd->cfg->prog_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->prog_sleep/1000000000,
.tv_nsec=bd->cfg->prog_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", err);
return err;
}
}
// lose power?
if (bd->power_cycles > 0) {
bd->power_cycles -= 1;
if (bd->power_cycles == 0) {
int err = lfs2_emubd_powerloss(cfg);
if (err) {
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", err);
return err;
}
}
}
LFS2_EMUBD_TRACE("lfs2_emubd_prog -> %d", 0);
return 0;
}
int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
LFS2_EMUBD_TRACE("lfs2_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
(void*)cfg, block, ((lfs2_emubd_t*)cfg->context)->cfg->erase_size);
lfs2_emubd_t *bd = cfg->context;
// check if erase is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
// emulate out-of-order writes? save first write
if (bd->cfg->powerloss_behavior == LFS2_EMUBD_POWERLOSS_OOO
&& bd->ooo_block == -1) {
bd->ooo_block = block;
bd->ooo_data = lfs2_emubd_incblock(bd->blocks[block]);
}
// get the block
lfs2_emubd_block_t *b = lfs2_emubd_mutblock(cfg, &bd->blocks[block]);
if (!b) {
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
// block bad?
if (bd->cfg->erase_cycles) {
if (b->wear >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS2_EMUBD_BADBLOCK_ERASEERROR) {
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", LFS2_ERR_CORRUPT);
return LFS2_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS2_EMUBD_BADBLOCK_ERASENOOP) {
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", 0);
return 0;
}
} else {
// mark wear
b->wear += 1;
}
}
// emulate an erase value?
if (bd->cfg->erase_value != -1) {
memset(b->data, bd->cfg->erase_value, bd->cfg->erase_size);
// mirror to disk file?
if (bd->disk) {
off_t res1 = lseek(bd->disk->fd,
(off_t)block*bd->cfg->erase_size,
SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", err);
return err;
}
ssize_t res2 = write(bd->disk->fd,
bd->disk->scratch,
bd->cfg->erase_size);
if (res2 < 0) {
int err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", err);
return err;
}
}
}
// track erases
bd->erased += bd->cfg->erase_size;
if (bd->cfg->erase_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->erase_sleep/1000000000,
.tv_nsec=bd->cfg->erase_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", err);
return err;
}
}
// lose power?
if (bd->power_cycles > 0) {
bd->power_cycles -= 1;
if (bd->power_cycles == 0) {
int err = lfs2_emubd_powerloss(cfg);
if (err) {
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", err);
return err;
}
}
}
LFS2_EMUBD_TRACE("lfs2_emubd_erase -> %d", 0);
return 0;
}
int lfs2_emubd_sync(const struct lfs2_config *cfg) {
LFS2_EMUBD_TRACE("lfs2_emubd_sync(%p)", (void*)cfg);
lfs2_emubd_t *bd = cfg->context;
// emulate out-of-order writes? reset first write, writes
// cannot be out-of-order across sync
if (bd->cfg->powerloss_behavior == LFS2_EMUBD_POWERLOSS_OOO) {
lfs2_emubd_decblock(bd->ooo_data);
bd->ooo_block = -1;
bd->ooo_data = NULL;
}
LFS2_EMUBD_TRACE("lfs2_emubd_sync -> %d", 0);
return 0;
}
/// Additional extended API for driving test features ///
static int lfs2_emubd_crc_(const struct lfs2_config *cfg,
lfs2_block_t block, uint32_t *crc) {
lfs2_emubd_t *bd = cfg->context;
// check if crc is valid
LFS2_ASSERT(block < cfg->block_count);
// crc the block
uint32_t crc_ = 0xffffffff;
const lfs2_emubd_block_t *b = bd->blocks[block];
if (b) {
crc_ = lfs2_crc(crc_, b->data, cfg->block_size);
} else {
uint8_t erase_value = (bd->cfg->erase_value != -1)
? bd->cfg->erase_value
: 0;
for (lfs2_size_t i = 0; i < cfg->block_size; i++) {
crc_ = lfs2_crc(crc_, &erase_value, 1);
}
}
*crc = 0xffffffff ^ crc_;
return 0;
}
int lfs2_emubd_crc(const struct lfs2_config *cfg,
lfs2_block_t block, uint32_t *crc) {
LFS2_EMUBD_TRACE("lfs2_emubd_crc(%p, %"PRIu32", %p)",
(void*)cfg, block, crc);
int err = lfs2_emubd_crc_(cfg, block, crc);
LFS2_EMUBD_TRACE("lfs2_emubd_crc -> %d", err);
return err;
}
int lfs2_emubd_bdcrc(const struct lfs2_config *cfg, uint32_t *crc) {
LFS2_EMUBD_TRACE("lfs2_emubd_bdcrc(%p, %p)", (void*)cfg, crc);
uint32_t crc_ = 0xffffffff;
for (lfs2_block_t i = 0; i < cfg->block_count; i++) {
uint32_t i_crc;
int err = lfs2_emubd_crc_(cfg, i, &i_crc);
if (err) {
LFS2_EMUBD_TRACE("lfs2_emubd_bdcrc -> %d", err);
return err;
}
crc_ = lfs2_crc(crc_, &i_crc, sizeof(uint32_t));
}
*crc = 0xffffffff ^ crc_;
LFS2_EMUBD_TRACE("lfs2_emubd_bdcrc -> %d", 0);
return 0;
}
lfs2_emubd_sio_t lfs2_emubd_readed(const struct lfs2_config *cfg) {
LFS2_EMUBD_TRACE("lfs2_emubd_readed(%p)", (void*)cfg);
lfs2_emubd_t *bd = cfg->context;
LFS2_EMUBD_TRACE("lfs2_emubd_readed -> %"PRIu64, bd->readed);
return bd->readed;
}
lfs2_emubd_sio_t lfs2_emubd_proged(const struct lfs2_config *cfg) {
LFS2_EMUBD_TRACE("lfs2_emubd_proged(%p)", (void*)cfg);
lfs2_emubd_t *bd = cfg->context;
LFS2_EMUBD_TRACE("lfs2_emubd_proged -> %"PRIu64, bd->proged);
return bd->proged;
}
lfs2_emubd_sio_t lfs2_emubd_erased(const struct lfs2_config *cfg) {
LFS2_EMUBD_TRACE("lfs2_emubd_erased(%p)", (void*)cfg);
lfs2_emubd_t *bd = cfg->context;
LFS2_EMUBD_TRACE("lfs2_emubd_erased -> %"PRIu64, bd->erased);
return bd->erased;
}
int lfs2_emubd_setreaded(const struct lfs2_config *cfg, lfs2_emubd_io_t readed) {
LFS2_EMUBD_TRACE("lfs2_emubd_setreaded(%p, %"PRIu64")", (void*)cfg, readed);
lfs2_emubd_t *bd = cfg->context;
bd->readed = readed;
LFS2_EMUBD_TRACE("lfs2_emubd_setreaded -> %d", 0);
return 0;
}
int lfs2_emubd_setproged(const struct lfs2_config *cfg, lfs2_emubd_io_t proged) {
LFS2_EMUBD_TRACE("lfs2_emubd_setproged(%p, %"PRIu64")", (void*)cfg, proged);
lfs2_emubd_t *bd = cfg->context;
bd->proged = proged;
LFS2_EMUBD_TRACE("lfs2_emubd_setproged -> %d", 0);
return 0;
}
int lfs2_emubd_seterased(const struct lfs2_config *cfg, lfs2_emubd_io_t erased) {
LFS2_EMUBD_TRACE("lfs2_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
lfs2_emubd_t *bd = cfg->context;
bd->erased = erased;
LFS2_EMUBD_TRACE("lfs2_emubd_seterased -> %d", 0);
return 0;
}
lfs2_emubd_swear_t lfs2_emubd_wear(const struct lfs2_config *cfg,
lfs2_block_t block) {
LFS2_EMUBD_TRACE("lfs2_emubd_wear(%p, %"PRIu32")", (void*)cfg, block);
lfs2_emubd_t *bd = cfg->context;
// check if block is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
// get the wear
lfs2_emubd_wear_t wear;
const lfs2_emubd_block_t *b = bd->blocks[block];
if (b) {
wear = b->wear;
} else {
wear = 0;
}
LFS2_EMUBD_TRACE("lfs2_emubd_wear -> %"PRIi32, wear);
return wear;
}
int lfs2_emubd_setwear(const struct lfs2_config *cfg,
lfs2_block_t block, lfs2_emubd_wear_t wear) {
LFS2_EMUBD_TRACE("lfs2_emubd_setwear(%p, %"PRIu32", %"PRIi32")",
(void*)cfg, block, wear);
lfs2_emubd_t *bd = cfg->context;
// check if block is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
// set the wear
lfs2_emubd_block_t *b = lfs2_emubd_mutblock(cfg, &bd->blocks[block]);
if (!b) {
LFS2_EMUBD_TRACE("lfs2_emubd_setwear -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
b->wear = wear;
LFS2_EMUBD_TRACE("lfs2_emubd_setwear -> %d", 0);
return 0;
}
lfs2_emubd_spowercycles_t lfs2_emubd_powercycles(
const struct lfs2_config *cfg) {
LFS2_EMUBD_TRACE("lfs2_emubd_powercycles(%p)", (void*)cfg);
lfs2_emubd_t *bd = cfg->context;
LFS2_EMUBD_TRACE("lfs2_emubd_powercycles -> %"PRIi32, bd->power_cycles);
return bd->power_cycles;
}
int lfs2_emubd_setpowercycles(const struct lfs2_config *cfg,
lfs2_emubd_powercycles_t power_cycles) {
LFS2_EMUBD_TRACE("lfs2_emubd_setpowercycles(%p, %"PRIi32")",
(void*)cfg, power_cycles);
lfs2_emubd_t *bd = cfg->context;
bd->power_cycles = power_cycles;
LFS2_EMUBD_TRACE("lfs2_emubd_powercycles -> %d", 0);
return 0;
}
int lfs2_emubd_copy(const struct lfs2_config *cfg, lfs2_emubd_t *copy) {
LFS2_EMUBD_TRACE("lfs2_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
lfs2_emubd_t *bd = cfg->context;
// lazily copy over our block array
copy->blocks = malloc(bd->cfg->erase_count * sizeof(lfs2_emubd_block_t*));
if (!copy->blocks) {
LFS2_EMUBD_TRACE("lfs2_emubd_copy -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
for (size_t i = 0; i < bd->cfg->erase_count; i++) {
copy->blocks[i] = lfs2_emubd_incblock(bd->blocks[i]);
}
// other state
copy->readed = bd->readed;
copy->proged = bd->proged;
copy->erased = bd->erased;
copy->power_cycles = bd->power_cycles;
copy->ooo_block = bd->ooo_block;
copy->ooo_data = lfs2_emubd_incblock(bd->ooo_data);
copy->disk = bd->disk;
if (copy->disk) {
copy->disk->rc += 1;
}
copy->cfg = bd->cfg;
LFS2_EMUBD_TRACE("lfs2_emubd_copy -> %d", 0);
return 0;
}

View File

@@ -6,13 +6,13 @@
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_EMUBD_H
#define LFS_EMUBD_H
#ifndef LFS2_EMUBD_H
#define LFS2_EMUBD_H
#include "lfs.h"
#include "lfs_util.h"
#include "bd/lfs_rambd.h"
#include "bd/lfs_filebd.h"
#include "lfs2.h"
#include "lfs2_util.h"
#include "bd/lfs2_rambd.h"
#include "bd/lfs2_filebd.h"
#ifdef __cplusplus
extern "C"
@@ -21,11 +21,11 @@ extern "C"
// Block device specific tracing
#ifndef LFS_EMUBD_TRACE
#ifdef LFS_EMUBD_YES_TRACE
#define LFS_EMUBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#ifndef LFS2_EMUBD_TRACE
#ifdef LFS2_EMUBD_YES_TRACE
#define LFS2_EMUBD_TRACE(...) LFS2_TRACE(__VA_ARGS__)
#else
#define LFS_EMUBD_TRACE(...)
#define LFS2_EMUBD_TRACE(...)
#endif
#endif
@@ -35,49 +35,50 @@ 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_behavior_t;
typedef enum lfs2_emubd_badblock_behavior {
LFS2_EMUBD_BADBLOCK_PROGERROR = 0, // Error on prog
LFS2_EMUBD_BADBLOCK_ERASEERROR = 1, // Error on erase
LFS2_EMUBD_BADBLOCK_READERROR = 2, // Error on read
LFS2_EMUBD_BADBLOCK_PROGNOOP = 3, // Prog does nothing silently
LFS2_EMUBD_BADBLOCK_ERASENOOP = 4, // Erase does nothing silently
} lfs2_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_behavior_t;
typedef enum lfs2_emubd_powerloss_behavior {
LFS2_EMUBD_POWERLOSS_NOOP = 0, // Progs are atomic
LFS2_EMUBD_POWERLOSS_OOO = 1, // Blocks are written out-of-order
} lfs2_emubd_powerloss_behavior_t;
// Type for measuring read/program/erase operations
typedef uint64_t lfs_emubd_io_t;
typedef int64_t lfs_emubd_sio_t;
typedef uint64_t lfs2_emubd_io_t;
typedef int64_t lfs2_emubd_sio_t;
// Type for measuring wear
typedef uint32_t lfs_emubd_wear_t;
typedef int32_t lfs_emubd_swear_t;
typedef uint32_t lfs2_emubd_wear_t;
typedef int32_t lfs2_emubd_swear_t;
// Type for tracking power-cycles
typedef uint32_t lfs_emubd_powercycles_t;
typedef int32_t lfs_emubd_spowercycles_t;
typedef uint32_t lfs2_emubd_powercycles_t;
typedef int32_t lfs2_emubd_spowercycles_t;
// Type for delays in nanoseconds
typedef uint64_t lfs_emubd_sleep_t;
typedef int64_t lfs_emubd_ssleep_t;
typedef uint64_t lfs2_emubd_sleep_t;
typedef int64_t lfs2_emubd_ssleep_t;
// emubd config, this is required for testing
struct lfs_emubd_config {
struct lfs2_emubd_config {
// Minimum size of a read operation in bytes.
lfs_size_t read_size;
lfs2_size_t read_size;
// Minimum size of a program operation in bytes.
lfs_size_t prog_size;
lfs2_size_t prog_size;
// Size of an erase operation in bytes.
lfs_size_t erase_size;
lfs2_size_t erase_size;
// Number of erase blocks on the device.
lfs_size_t erase_count;
lfs2_size_t erase_count;
// 8-bit erase value to use for simulating erases. -1 does not simulate
// erases, which can speed up testing by avoiding the extra block-device
@@ -89,15 +90,15 @@ struct lfs_emubd_config {
uint32_t erase_cycles;
// The mode determining how bad-blocks fail
lfs_emubd_badblock_behavior_t badblock_behavior;
lfs2_emubd_badblock_behavior_t badblock_behavior;
// Number of write operations (erase/prog) before triggering a power-loss.
// power_cycles=0 disables this. The exact behavior of power-loss is
// controlled by a combination of powerloss_behavior and powerloss_cb.
lfs_emubd_powercycles_t power_cycles;
lfs2_emubd_powercycles_t power_cycles;
// The mode determining how power-loss affects disk
lfs_emubd_powerloss_behavior_t powerloss_behavior;
lfs2_emubd_powerloss_behavior_t powerloss_behavior;
// Function to call to emulate power-loss. The exact behavior of power-loss
// is up to the runner to provide.
@@ -116,122 +117,124 @@ struct lfs_emubd_config {
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_emubd_sleep_t read_sleep;
lfs2_emubd_sleep_t read_sleep;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_emubd_sleep_t prog_sleep;
lfs2_emubd_sleep_t prog_sleep;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_emubd_sleep_t erase_sleep;
lfs2_emubd_sleep_t erase_sleep;
};
// A reference counted block
typedef struct lfs_emubd_block {
typedef struct lfs2_emubd_block {
uint32_t rc;
lfs_emubd_wear_t wear;
lfs2_emubd_wear_t wear;
uint8_t data[];
} lfs_emubd_block_t;
} lfs2_emubd_block_t;
// Disk mirror
typedef struct lfs_emubd_disk {
typedef struct lfs2_emubd_disk {
uint32_t rc;
int fd;
uint8_t *scratch;
} lfs_emubd_disk_t;
} lfs2_emubd_disk_t;
// emubd state
typedef struct lfs_emubd {
typedef struct lfs2_emubd {
// array of copy-on-write blocks
lfs_emubd_block_t **blocks;
lfs2_emubd_block_t **blocks;
// some other test state
lfs_emubd_io_t readed;
lfs_emubd_io_t proged;
lfs_emubd_io_t erased;
lfs_emubd_powercycles_t power_cycles;
lfs_emubd_disk_t *disk;
lfs2_emubd_io_t readed;
lfs2_emubd_io_t proged;
lfs2_emubd_io_t erased;
lfs2_emubd_powercycles_t power_cycles;
lfs2_ssize_t ooo_block;
lfs2_emubd_block_t *ooo_data;
lfs2_emubd_disk_t *disk;
const struct lfs_emubd_config *cfg;
} lfs_emubd_t;
const struct lfs2_emubd_config *cfg;
} lfs2_emubd_t;
/// Block device API ///
// Create an emulating block device using the geometry in lfs_config
int lfs_emubd_create(const struct lfs_config *cfg,
const struct lfs_emubd_config *bdcfg);
// Create an emulating block device using the geometry in lfs2_config
int lfs2_emubd_create(const struct lfs2_config *cfg,
const struct lfs2_emubd_config *bdcfg);
// Clean up memory associated with block device
int lfs_emubd_destroy(const struct lfs_config *cfg);
int lfs2_emubd_destroy(const struct lfs2_config *cfg);
// Read a block
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block);
// Sync the block device
int lfs_emubd_sync(const struct lfs_config *cfg);
int lfs2_emubd_sync(const struct lfs2_config *cfg);
/// Additional extended API for driving test features ///
// A CRC of a block for debugging purposes
int lfs_emubd_crc(const struct lfs_config *cfg,
lfs_block_t block, uint32_t *crc);
int lfs2_emubd_crc(const struct lfs2_config *cfg,
lfs2_block_t block, uint32_t *crc);
// A CRC of the entire block device for debugging purposes
int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc);
int lfs2_emubd_bdcrc(const struct lfs2_config *cfg, uint32_t *crc);
// Get total amount of bytes read
lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg);
lfs2_emubd_sio_t lfs2_emubd_readed(const struct lfs2_config *cfg);
// Get total amount of bytes programmed
lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg);
lfs2_emubd_sio_t lfs2_emubd_proged(const struct lfs2_config *cfg);
// Get total amount of bytes erased
lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg);
lfs2_emubd_sio_t lfs2_emubd_erased(const struct lfs2_config *cfg);
// Manually set amount of bytes read
int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed);
int lfs2_emubd_setreaded(const struct lfs2_config *cfg, lfs2_emubd_io_t readed);
// Manually set amount of bytes programmed
int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged);
int lfs2_emubd_setproged(const struct lfs2_config *cfg, lfs2_emubd_io_t proged);
// Manually set amount of bytes erased
int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased);
int lfs2_emubd_seterased(const struct lfs2_config *cfg, lfs2_emubd_io_t erased);
// Get simulated wear on a given block
lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
lfs_block_t block);
lfs2_emubd_swear_t lfs2_emubd_wear(const struct lfs2_config *cfg,
lfs2_block_t block);
// Manually set simulated wear on a given block
int lfs_emubd_setwear(const struct lfs_config *cfg,
lfs_block_t block, lfs_emubd_wear_t wear);
int lfs2_emubd_setwear(const struct lfs2_config *cfg,
lfs2_block_t block, lfs2_emubd_wear_t wear);
// Get the remaining power-cycles
lfs_emubd_spowercycles_t lfs_emubd_powercycles(
const struct lfs_config *cfg);
lfs2_emubd_spowercycles_t lfs2_emubd_powercycles(
const struct lfs2_config *cfg);
// Manually set the remaining power-cycles
int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
lfs_emubd_powercycles_t power_cycles);
int lfs2_emubd_setpowercycles(const struct lfs2_config *cfg,
lfs2_emubd_powercycles_t power_cycles);
// Create a copy-on-write copy of the state of this block device
int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy);
int lfs2_emubd_copy(const struct lfs2_config *cfg, lfs2_emubd_t *copy);
#ifdef __cplusplus

View File

@@ -5,7 +5,7 @@
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs_filebd.h"
#include "bd/lfs2_filebd.h"
#include <fcntl.h>
#include <unistd.h>
@@ -15,9 +15,9 @@
#include <windows.h>
#endif
int lfs_filebd_create(const struct lfs_config *cfg, const char *path,
const struct lfs_filebd_config *bdcfg) {
LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, "
int lfs2_filebd_create(const struct lfs2_config *cfg, const char *path,
const struct lfs2_filebd_config *bdcfg) {
LFS2_FILEBD_TRACE("lfs2_filebd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
"\"%s\", "
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
@@ -29,7 +29,7 @@ int lfs_filebd_create(const struct lfs_config *cfg, const char *path,
(void*)bdcfg,
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
bdcfg->erase_count);
lfs_filebd_t *bd = cfg->context;
lfs2_filebd_t *bd = cfg->context;
bd->cfg = bdcfg;
// open file
@@ -41,39 +41,39 @@ int lfs_filebd_create(const struct lfs_config *cfg, const char *path,
if (bd->fd < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err);
LFS2_FILEBD_TRACE("lfs2_filebd_create -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_create -> %d", 0);
LFS2_FILEBD_TRACE("lfs2_filebd_create -> %d", 0);
return 0;
}
int lfs_filebd_destroy(const struct lfs_config *cfg) {
LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg);
lfs_filebd_t *bd = cfg->context;
int lfs2_filebd_destroy(const struct lfs2_config *cfg) {
LFS2_FILEBD_TRACE("lfs2_filebd_destroy(%p)", (void*)cfg);
lfs2_filebd_t *bd = cfg->context;
int err = close(bd->fd);
if (err < 0) {
err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err);
LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0);
LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", 0);
return 0;
}
int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_read(%p, "
int lfs2_filebd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
LFS2_FILEBD_TRACE("lfs2_filebd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_filebd_t *bd = cfg->context;
lfs2_filebd_t *bd = cfg->context;
// check if read is valid
LFS_ASSERT(block < bd->cfg->erase_count);
LFS_ASSERT(off % bd->cfg->read_size == 0);
LFS_ASSERT(size % bd->cfg->read_size == 0);
LFS_ASSERT(off+size <= bd->cfg->erase_size);
LFS2_ASSERT(block < bd->cfg->erase_count);
LFS2_ASSERT(off % bd->cfg->read_size == 0);
LFS2_ASSERT(size % bd->cfg->read_size == 0);
LFS2_ASSERT(off+size <= bd->cfg->erase_size);
// zero for reproducibility (in case file is truncated)
memset(buffer, 0, size);
@@ -83,74 +83,74 @@ int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
(off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", err);
return err;
}
ssize_t res2 = read(bd->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0);
LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", 0);
return 0;
}
int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_prog(%p, "
int lfs2_filebd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
LFS2_FILEBD_TRACE("lfs2_filebd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_filebd_t *bd = cfg->context;
lfs2_filebd_t *bd = cfg->context;
// check if write is valid
LFS_ASSERT(block < bd->cfg->erase_count);
LFS_ASSERT(off % bd->cfg->prog_size == 0);
LFS_ASSERT(size % bd->cfg->prog_size == 0);
LFS_ASSERT(off+size <= bd->cfg->erase_size);
LFS2_ASSERT(block < bd->cfg->erase_count);
LFS2_ASSERT(off % bd->cfg->prog_size == 0);
LFS2_ASSERT(size % bd->cfg->prog_size == 0);
LFS2_ASSERT(off+size <= bd->cfg->erase_size);
// program data
off_t res1 = lseek(bd->fd,
(off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err);
return err;
}
ssize_t res2 = write(bd->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0);
LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", 0);
return 0;
}
int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
(void*)cfg, block, ((lfs_file_t*)cfg->context)->cfg->erase_size);
lfs_filebd_t *bd = cfg->context;
int lfs2_filebd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
LFS2_FILEBD_TRACE("lfs2_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
(void*)cfg, block, ((lfs2_filebd_t*)cfg->context)->cfg->erase_size);
lfs2_filebd_t *bd = cfg->context;
// check if erase is valid
LFS_ASSERT(block < bd->cfg->erase_count);
LFS2_ASSERT(block < bd->cfg->erase_count);
// erase is a noop
(void)block;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0);
LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", 0);
return 0;
}
int lfs_filebd_sync(const struct lfs_config *cfg) {
LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg);
int lfs2_filebd_sync(const struct lfs2_config *cfg) {
LFS2_FILEBD_TRACE("lfs2_filebd_sync(%p)", (void*)cfg);
// file sync
lfs_filebd_t *bd = cfg->context;
lfs2_filebd_t *bd = cfg->context;
#ifdef _WIN32
int err = FlushFileBuffers((HANDLE) _get_osfhandle(bd->fd)) ? 0 : -1;
#else
@@ -158,10 +158,10 @@ int lfs_filebd_sync(const struct lfs_config *cfg) {
#endif
if (err) {
err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0);
return err;
}
LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0);
return 0;
}

82
bd/lfs2_filebd.h Normal file
View File

@@ -0,0 +1,82 @@
/*
* Block device emulated in a file
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_FILEBD_H
#define LFS2_FILEBD_H
#include "lfs2.h"
#include "lfs2_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Block device specific tracing
#ifndef LFS2_FILEBD_TRACE
#ifdef LFS2_FILEBD_YES_TRACE
#define LFS2_FILEBD_TRACE(...) LFS2_TRACE(__VA_ARGS__)
#else
#define LFS2_FILEBD_TRACE(...)
#endif
#endif
// filebd config
struct lfs2_filebd_config {
// Minimum size of a read operation in bytes.
lfs2_size_t read_size;
// Minimum size of a program operation in bytes.
lfs2_size_t prog_size;
// Size of an erase operation in bytes.
lfs2_size_t erase_size;
// Number of erase blocks on the device.
lfs2_size_t erase_count;
};
// filebd state
typedef struct lfs2_filebd {
int fd;
const struct lfs2_filebd_config *cfg;
} lfs2_filebd_t;
// Create a file block device
int lfs2_filebd_create(const struct lfs2_config *cfg, const char *path,
const struct lfs2_filebd_config *bdcfg);
// Clean up memory associated with block device
int lfs2_filebd_destroy(const struct lfs2_config *cfg);
// Read a block
int lfs2_filebd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs2_filebd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs2_filebd_erase(const struct lfs2_config *cfg, lfs2_block_t block);
// Sync the block device
int lfs2_filebd_sync(const struct lfs2_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

118
bd/lfs2_rambd.c Normal file
View File

@@ -0,0 +1,118 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs2_rambd.h"
int lfs2_rambd_create(const struct lfs2_config *cfg,
const struct lfs2_rambd_config *bdcfg) {
LFS2_RAMBD_TRACE("lfs2_rambd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
".buffer=%p})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
(void*)bdcfg,
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
bdcfg->erase_count, bdcfg->buffer);
lfs2_rambd_t *bd = cfg->context;
bd->cfg = bdcfg;
// allocate buffer?
if (bd->cfg->buffer) {
bd->buffer = bd->cfg->buffer;
} else {
bd->buffer = lfs2_malloc(bd->cfg->erase_size * bd->cfg->erase_count);
if (!bd->buffer) {
LFS2_RAMBD_TRACE("lfs2_rambd_create -> %d", LFS2_ERR_NOMEM);
return LFS2_ERR_NOMEM;
}
}
// zero for reproducibility
memset(bd->buffer, 0, bd->cfg->erase_size * bd->cfg->erase_count);
LFS2_RAMBD_TRACE("lfs2_rambd_create -> %d", 0);
return 0;
}
int lfs2_rambd_destroy(const struct lfs2_config *cfg) {
LFS2_RAMBD_TRACE("lfs2_rambd_destroy(%p)", (void*)cfg);
// clean up memory
lfs2_rambd_t *bd = cfg->context;
if (!bd->cfg->buffer) {
lfs2_free(bd->buffer);
}
LFS2_RAMBD_TRACE("lfs2_rambd_destroy -> %d", 0);
return 0;
}
int lfs2_rambd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
LFS2_RAMBD_TRACE("lfs2_rambd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_rambd_t *bd = cfg->context;
// check if read is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
LFS2_ASSERT(off % bd->cfg->read_size == 0);
LFS2_ASSERT(size % bd->cfg->read_size == 0);
LFS2_ASSERT(off+size <= bd->cfg->erase_size);
// read data
memcpy(buffer, &bd->buffer[block*bd->cfg->erase_size + off], size);
LFS2_RAMBD_TRACE("lfs2_rambd_read -> %d", 0);
return 0;
}
int lfs2_rambd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
LFS2_RAMBD_TRACE("lfs2_rambd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs2_rambd_t *bd = cfg->context;
// check if write is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
LFS2_ASSERT(off % bd->cfg->prog_size == 0);
LFS2_ASSERT(size % bd->cfg->prog_size == 0);
LFS2_ASSERT(off+size <= bd->cfg->erase_size);
// program data
memcpy(&bd->buffer[block*bd->cfg->erase_size + off], buffer, size);
LFS2_RAMBD_TRACE("lfs2_rambd_prog -> %d", 0);
return 0;
}
int lfs2_rambd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
LFS2_RAMBD_TRACE("lfs2_rambd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
(void*)cfg, block, ((lfs2_rambd_t*)cfg->context)->cfg->erase_size);
lfs2_rambd_t *bd = cfg->context;
// check if erase is valid
LFS2_ASSERT(block < bd->cfg->erase_count);
// erase is a noop
(void)block;
LFS2_RAMBD_TRACE("lfs2_rambd_erase -> %d", 0);
return 0;
}
int lfs2_rambd_sync(const struct lfs2_config *cfg) {
LFS2_RAMBD_TRACE("lfs2_rambd_sync(%p)", (void*)cfg);
// sync is a noop
(void)cfg;
LFS2_RAMBD_TRACE("lfs2_rambd_sync -> %d", 0);
return 0;
}

85
bd/lfs2_rambd.h Normal file
View File

@@ -0,0 +1,85 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_RAMBD_H
#define LFS2_RAMBD_H
#include "lfs2.h"
#include "lfs2_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Block device specific tracing
#ifndef LFS2_RAMBD_TRACE
#ifdef LFS2_RAMBD_YES_TRACE
#define LFS2_RAMBD_TRACE(...) LFS2_TRACE(__VA_ARGS__)
#else
#define LFS2_RAMBD_TRACE(...)
#endif
#endif
// rambd config
struct lfs2_rambd_config {
// Minimum size of a read operation in bytes.
lfs2_size_t read_size;
// Minimum size of a program operation in bytes.
lfs2_size_t prog_size;
// Size of an erase operation in bytes.
lfs2_size_t erase_size;
// Number of erase blocks on the device.
lfs2_size_t erase_count;
// Optional statically allocated buffer for the block device.
void *buffer;
};
// rambd state
typedef struct lfs2_rambd {
uint8_t *buffer;
const struct lfs2_rambd_config *cfg;
} lfs2_rambd_t;
// Create a RAM block device
int lfs2_rambd_create(const struct lfs2_config *cfg,
const struct lfs2_rambd_config *bdcfg);
// Clean up memory associated with block device
int lfs2_rambd_destroy(const struct lfs2_config *cfg);
// Read a block
int lfs2_rambd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs2_rambd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs2_rambd_erase(const struct lfs2_config *cfg, lfs2_block_t block);
// Sync the block device
int lfs2_rambd_sync(const struct lfs2_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -1,645 +0,0 @@
/*
* Emulating block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#include "bd/lfs_emubd.h"
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#endif
// access to lazily-allocated/copy-on-write blocks
//
// Note we can only modify a block if we have exclusive access to it (rc == 1)
//
static lfs_emubd_block_t *lfs_emubd_incblock(lfs_emubd_block_t *block) {
if (block) {
block->rc += 1;
}
return block;
}
static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
if (block) {
block->rc -= 1;
if (block->rc == 0) {
free(block);
}
}
}
static lfs_emubd_block_t *lfs_emubd_mutblock(
const struct lfs_config *cfg,
lfs_emubd_block_t **block) {
lfs_emubd_t *bd = cfg->context;
lfs_emubd_block_t *block_ = *block;
if (block_ && block_->rc == 1) {
// rc == 1? can modify
return block_;
} else if (block_) {
// rc > 1? need to create a copy
lfs_emubd_block_t *nblock = malloc(
sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
if (!nblock) {
return NULL;
}
memcpy(nblock, block_,
sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
nblock->rc = 1;
lfs_emubd_decblock(block_);
*block = nblock;
return nblock;
} else {
// no block? need to allocate
lfs_emubd_block_t *nblock = malloc(
sizeof(lfs_emubd_block_t) + bd->cfg->erase_size);
if (!nblock) {
return NULL;
}
nblock->rc = 1;
nblock->wear = 0;
// zero for consistency
memset(nblock->data,
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
bd->cfg->erase_size);
*block = nblock;
return nblock;
}
}
// emubd create/destroy
int lfs_emubd_create(const struct lfs_config *cfg,
const struct lfs_emubd_config *bdcfg) {
LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
".erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, "
".powerloss_data=%p, .track_branches=%d})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
(void*)bdcfg,
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
bdcfg->erase_count, bdcfg->erase_value, bdcfg->erase_cycles,
bdcfg->badblock_behavior, bdcfg->power_cycles,
bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
bdcfg->powerloss_data, bdcfg->track_branches);
lfs_emubd_t *bd = cfg->context;
bd->cfg = bdcfg;
// allocate our block array, all blocks start as uninitialized
bd->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
if (!bd->blocks) {
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
memset(bd->blocks, 0, bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
// setup testing things
bd->readed = 0;
bd->proged = 0;
bd->erased = 0;
bd->power_cycles = bd->cfg->power_cycles;
bd->disk = NULL;
if (bd->cfg->disk_path) {
bd->disk = malloc(sizeof(lfs_emubd_disk_t));
if (!bd->disk) {
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
bd->disk->rc = 1;
bd->disk->scratch = NULL;
#ifdef _WIN32
bd->disk->fd = open(bd->cfg->disk_path,
O_RDWR | O_CREAT | O_BINARY, 0666);
#else
bd->disk->fd = open(bd->cfg->disk_path,
O_RDWR | O_CREAT, 0666);
#endif
if (bd->disk->fd < 0) {
int err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
return err;
}
// if we're emulating erase values, we can keep a block around in
// memory of just the erase state to speed up emulated erases
if (bd->cfg->erase_value != -1) {
bd->disk->scratch = malloc(bd->cfg->erase_size);
if (!bd->disk->scratch) {
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
memset(bd->disk->scratch,
bd->cfg->erase_value,
bd->cfg->erase_size);
// go ahead and erase all of the disk, otherwise the file will not
// match our internal representation
for (size_t i = 0; i < bd->cfg->erase_count; i++) {
ssize_t res = write(bd->disk->fd,
bd->disk->scratch,
bd->cfg->erase_size);
if (res < 0) {
int err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
return err;
}
}
}
}
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", 0);
return 0;
}
int lfs_emubd_destroy(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
// decrement reference counts
for (lfs_block_t i = 0; i < bd->cfg->erase_count; i++) {
lfs_emubd_decblock(bd->blocks[i]);
}
free(bd->blocks);
// clean up other resources
if (bd->disk) {
bd->disk->rc -= 1;
if (bd->disk->rc == 0) {
close(bd->disk->fd);
free(bd->disk->scratch);
free(bd->disk);
}
}
LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0);
return 0;
}
// block device API
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_EMUBD_TRACE("lfs_emubd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_emubd_t *bd = cfg->context;
// check if read is valid
LFS_ASSERT(block < bd->cfg->erase_count);
LFS_ASSERT(off % bd->cfg->read_size == 0);
LFS_ASSERT(size % bd->cfg->read_size == 0);
LFS_ASSERT(off+size <= bd->cfg->erase_size);
// get the block
const lfs_emubd_block_t *b = bd->blocks[block];
if (b) {
// block bad?
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) {
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
}
// read data
memcpy(buffer, &b->data[off], size);
} else {
// zero for consistency
memset(buffer,
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
size);
}
// track reads
bd->readed += size;
if (bd->cfg->read_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->read_sleep/1000000000,
.tv_nsec=bd->cfg->read_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err);
return err;
}
}
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0);
return 0;
}
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_EMUBD_TRACE("lfs_emubd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_emubd_t *bd = cfg->context;
// check if write is valid
LFS_ASSERT(block < bd->cfg->erase_count);
LFS_ASSERT(off % bd->cfg->prog_size == 0);
LFS_ASSERT(size % bd->cfg->prog_size == 0);
LFS_ASSERT(off+size <= bd->cfg->erase_size);
// 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);
return LFS_ERR_NOMEM;
}
// block bad?
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS_EMUBD_BADBLOCK_PROGERROR) {
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS_EMUBD_BADBLOCK_PROGNOOP ||
bd->cfg->badblock_behavior ==
LFS_EMUBD_BADBLOCK_ERASENOOP) {
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
return 0;
}
}
// were we erased properly?
if (bd->cfg->erase_value != -1) {
for (lfs_off_t i = 0; i < size; i++) {
LFS_ASSERT(b->data[off+i] == bd->cfg->erase_value);
}
}
// prog data
memcpy(&b->data[off], buffer, size);
// mirror to disk file?
if (bd->disk) {
off_t res1 = lseek(bd->disk->fd,
(off_t)block*bd->cfg->erase_size + (off_t)off,
SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
return err;
}
ssize_t res2 = write(bd->disk->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
return err;
}
}
// track progs
bd->proged += size;
if (bd->cfg->prog_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->prog_sleep/1000000000,
.tv_nsec=bd->cfg->prog_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
return err;
}
}
// lose power?
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);
}
}
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
return 0;
}
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
(void*)cfg, block, ((lfs_emubd_t*)cfg->context)->cfg->erase_size);
lfs_emubd_t *bd = cfg->context;
// check if erase is valid
LFS_ASSERT(block < bd->cfg->erase_count);
// 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);
return LFS_ERR_NOMEM;
}
// block bad?
if (bd->cfg->erase_cycles) {
if (b->wear >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS_EMUBD_BADBLOCK_ERASEERROR) {
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS_EMUBD_BADBLOCK_ERASENOOP) {
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
return 0;
}
} else {
// mark wear
b->wear += 1;
}
}
// emulate an erase value?
if (bd->cfg->erase_value != -1) {
memset(b->data, bd->cfg->erase_value, bd->cfg->erase_size);
// mirror to disk file?
if (bd->disk) {
off_t res1 = lseek(bd->disk->fd,
(off_t)block*bd->cfg->erase_size,
SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
return err;
}
ssize_t res2 = write(bd->disk->fd,
bd->disk->scratch,
bd->cfg->erase_size);
if (res2 < 0) {
int err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
return err;
}
}
}
// track erases
bd->erased += bd->cfg->erase_size;
if (bd->cfg->erase_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->erase_sleep/1000000000,
.tv_nsec=bd->cfg->erase_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
return err;
}
}
// lose power?
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);
}
}
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
return 0;
}
int lfs_emubd_sync(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
// do nothing
(void)cfg;
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,
lfs_block_t block, uint32_t *crc) {
lfs_emubd_t *bd = cfg->context;
// check if crc is valid
LFS_ASSERT(block < cfg->block_count);
// crc the block
uint32_t crc_ = 0xffffffff;
const lfs_emubd_block_t *b = bd->blocks[block];
if (b) {
crc_ = lfs_crc(crc_, b->data, cfg->block_size);
} else {
uint8_t erase_value = (bd->cfg->erase_value != -1)
? bd->cfg->erase_value
: 0;
for (lfs_size_t i = 0; i < cfg->block_size; i++) {
crc_ = lfs_crc(crc_, &erase_value, 1);
}
}
*crc = 0xffffffff ^ crc_;
return 0;
}
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);
LFS_EMUBD_TRACE("lfs_emubd_crc -> %d", err);
return err;
}
int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc) {
LFS_EMUBD_TRACE("lfs_emubd_bdcrc(%p, %p)", (void*)cfg, 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);
if (err) {
LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", err);
return err;
}
crc_ = lfs_crc(crc_, &i_crc, sizeof(uint32_t));
}
*crc = 0xffffffff ^ crc_;
LFS_EMUBD_TRACE("lfs_emubd_bdcrc -> %d", 0);
return 0;
}
lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_readed(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_readed -> %"PRIu64, bd->readed);
return bd->readed;
}
lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_proged(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_proged -> %"PRIu64, bd->proged);
return bd->proged;
}
lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_erased(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_erased -> %"PRIu64, bd->erased);
return bd->erased;
}
int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed) {
LFS_EMUBD_TRACE("lfs_emubd_setreaded(%p, %"PRIu64")", (void*)cfg, readed);
lfs_emubd_t *bd = cfg->context;
bd->readed = readed;
LFS_EMUBD_TRACE("lfs_emubd_setreaded -> %d", 0);
return 0;
}
int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged) {
LFS_EMUBD_TRACE("lfs_emubd_setproged(%p, %"PRIu64")", (void*)cfg, proged);
lfs_emubd_t *bd = cfg->context;
bd->proged = proged;
LFS_EMUBD_TRACE("lfs_emubd_setproged -> %d", 0);
return 0;
}
int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) {
LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
lfs_emubd_t *bd = cfg->context;
bd->erased = erased;
LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0);
return 0;
}
lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg,
lfs_block_t block) {
LFS_EMUBD_TRACE("lfs_emubd_wear(%p, %"PRIu32")", (void*)cfg, block);
lfs_emubd_t *bd = cfg->context;
// check if block is valid
LFS_ASSERT(block < bd->cfg->erase_count);
// get the wear
lfs_emubd_wear_t wear;
const lfs_emubd_block_t *b = bd->blocks[block];
if (b) {
wear = b->wear;
} else {
wear = 0;
}
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", %"PRIi32")",
(void*)cfg, block, wear);
lfs_emubd_t *bd = cfg->context;
// check if block is valid
LFS_ASSERT(block < bd->cfg->erase_count);
// set the wear
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
if (!b) {
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
b->wear = wear;
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0);
return 0;
}
lfs_emubd_spowercycles_t lfs_emubd_powercycles(
const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %"PRIi32, bd->power_cycles);
return bd->power_cycles;
}
int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
lfs_emubd_powercycles_t power_cycles) {
LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")",
(void*)cfg, power_cycles);
lfs_emubd_t *bd = cfg->context;
bd->power_cycles = power_cycles;
LFS_EMUBD_TRACE("lfs_emubd_powercycles -> %d", 0);
return 0;
}
int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
lfs_emubd_t *bd = cfg->context;
// lazily copy over our block array
copy->blocks = malloc(bd->cfg->erase_count * sizeof(lfs_emubd_block_t*));
if (!copy->blocks) {
LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
for (size_t i = 0; i < bd->cfg->erase_count; i++) {
copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
}
// other state
copy->readed = bd->readed;
copy->proged = bd->proged;
copy->erased = bd->erased;
copy->power_cycles = bd->power_cycles;
copy->disk = bd->disk;
if (copy->disk) {
copy->disk->rc += 1;
}
copy->cfg = bd->cfg;
LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0);
return 0;
}

View File

@@ -1,82 +0,0 @@
/*
* Block device emulated in a file
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_FILEBD_H
#define LFS_FILEBD_H
#include "lfs.h"
#include "lfs_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Block device specific tracing
#ifndef LFS_FILEBD_TRACE
#ifdef LFS_FILEBD_YES_TRACE
#define LFS_FILEBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#else
#define LFS_FILEBD_TRACE(...)
#endif
#endif
// filebd config
struct lfs_filebd_config {
// Minimum size of a read operation in bytes.
lfs_size_t read_size;
// Minimum size of a program operation in bytes.
lfs_size_t prog_size;
// Size of an erase operation in bytes.
lfs_size_t erase_size;
// Number of erase blocks on the device.
lfs_size_t erase_count;
};
// filebd state
typedef struct lfs_filebd {
int fd;
const struct lfs_filebd_config *cfg;
} lfs_filebd_t;
// Create a file block device
int lfs_filebd_create(const struct lfs_config *cfg, const char *path,
const struct lfs_filebd_config *bdcfg);
// Clean up memory associated with block device
int lfs_filebd_destroy(const struct lfs_config *cfg);
// Read a block
int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block);
// Sync the block device
int lfs_filebd_sync(const struct lfs_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -1,118 +0,0 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bd/lfs_rambd.h"
int lfs_rambd_create(const struct lfs_config *cfg,
const struct lfs_rambd_config *bdcfg) {
LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
"%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
".buffer=%p})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
(void*)bdcfg,
bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
bdcfg->erase_count, bdcfg->buffer);
lfs_rambd_t *bd = cfg->context;
bd->cfg = bdcfg;
// allocate buffer?
if (bd->cfg->buffer) {
bd->buffer = bd->cfg->buffer;
} else {
bd->buffer = lfs_malloc(bd->cfg->erase_size * bd->cfg->erase_count);
if (!bd->buffer) {
LFS_RAMBD_TRACE("lfs_rambd_create -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
}
// zero for reproducibility
memset(bd->buffer, 0, bd->cfg->erase_size * bd->cfg->erase_count);
LFS_RAMBD_TRACE("lfs_rambd_create -> %d", 0);
return 0;
}
int lfs_rambd_destroy(const struct lfs_config *cfg) {
LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)cfg);
// clean up memory
lfs_rambd_t *bd = cfg->context;
if (!bd->cfg->buffer) {
lfs_free(bd->buffer);
}
LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0);
return 0;
}
int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_rambd_t *bd = cfg->context;
// check if read is valid
LFS_ASSERT(block < bd->cfg->erase_count);
LFS_ASSERT(off % bd->cfg->read_size == 0);
LFS_ASSERT(size % bd->cfg->read_size == 0);
LFS_ASSERT(off+size <= bd->cfg->erase_size);
// read data
memcpy(buffer, &bd->buffer[block*bd->cfg->erase_size + off], size);
LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0);
return 0;
}
int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_rambd_t *bd = cfg->context;
// check if write is valid
LFS_ASSERT(block < bd->cfg->erase_count);
LFS_ASSERT(off % bd->cfg->prog_size == 0);
LFS_ASSERT(size % bd->cfg->prog_size == 0);
LFS_ASSERT(off+size <= bd->cfg->erase_size);
// program data
memcpy(&bd->buffer[block*bd->cfg->erase_size + off], buffer, size);
LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0);
return 0;
}
int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
(void*)cfg, block, ((lfs_rambd_t*)cfg->context)->cfg->erase_size);
lfs_rambd_t *bd = cfg->context;
// check if erase is valid
LFS_ASSERT(block < bd->cfg->erase_count);
// erase is a noop
(void)block;
LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0);
return 0;
}
int lfs_rambd_sync(const struct lfs_config *cfg) {
LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)cfg);
// sync is a noop
(void)cfg;
LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0);
return 0;
}

View File

@@ -1,85 +0,0 @@
/*
* Block device emulated in RAM
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_RAMBD_H
#define LFS_RAMBD_H
#include "lfs.h"
#include "lfs_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Block device specific tracing
#ifndef LFS_RAMBD_TRACE
#ifdef LFS_RAMBD_YES_TRACE
#define LFS_RAMBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#else
#define LFS_RAMBD_TRACE(...)
#endif
#endif
// rambd config
struct lfs_rambd_config {
// Minimum size of a read operation in bytes.
lfs_size_t read_size;
// Minimum size of a program operation in bytes.
lfs_size_t prog_size;
// Size of an erase operation in bytes.
lfs_size_t erase_size;
// Number of erase blocks on the device.
lfs_size_t erase_count;
// Optional statically allocated buffer for the block device.
void *buffer;
};
// rambd state
typedef struct lfs_rambd {
uint8_t *buffer;
const struct lfs_rambd_config *cfg;
} lfs_rambd_t;
// Create a RAM block device
int lfs_rambd_create(const struct lfs_config *cfg,
const struct lfs_rambd_config *bdcfg);
// Clean up memory associated with block device
int lfs_rambd_destroy(const struct lfs_config *cfg);
// Read a block
int lfs_rambd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs_rambd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_rambd_erase(const struct lfs_config *cfg, lfs_block_t block);
// Sync the block device
int lfs_rambd_sync(const struct lfs_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -7,55 +7,55 @@ defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// first create the files
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
for (lfs2_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, name,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint32_t file_prng = i;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
for (lfs2_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs2_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = BENCH_PRNG(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
// then read the files
BENCH_START();
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
for (lfs2_size_t i = 0; i < N; i++) {
lfs2_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: BENCH_PRNG(&prng) % N;
sprintf(name, "file%08x", i_);
lfs_file_t file;
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, name, LFS2_O_RDONLY) => 0;
uint32_t file_prng = i_;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
lfs_file_read(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
for (lfs2_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
lfs2_file_read(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
for (lfs2_size_t k = 0; k < CHUNK_SIZE; k++) {
assert(buffer[k] == BENCH_PRNG(&file_prng));
}
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.bench_dir_creat]
@@ -67,37 +67,37 @@ defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
BENCH_START();
uint32_t prng = 42;
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
for (lfs2_size_t i = 0; i < N; i++) {
lfs2_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: BENCH_PRNG(&prng) % N;
sprintf(name, "file%08x", i_);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, name,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
uint32_t file_prng = i_;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
for (lfs2_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs2_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = BENCH_PRNG(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.bench_dir_remove]
@@ -109,45 +109,45 @@ defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// first create the files
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
for (lfs2_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, name,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint32_t file_prng = i;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
for (lfs2_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs2_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = BENCH_PRNG(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
// then remove the files
BENCH_START();
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
for (lfs2_size_t i = 0; i < N; i++) {
lfs2_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: BENCH_PRNG(&prng) % N;
sprintf(name, "file%08x", i_);
int err = lfs_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOENT);
int err = lfs2_remove(&lfs2, name);
assert(!err || err == LFS2_ERR_NOENT);
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.bench_dir_read]
@@ -155,52 +155,52 @@ defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// first create the files
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
for (lfs2_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, name,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint32_t file_prng = i;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
for (lfs2_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs2_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = BENCH_PRNG(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
// then read the directory
BENCH_START();
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "/") => 0;
struct lfs2_info info;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (int i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_REG);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.bench_dir_mkdir]
@@ -210,26 +210,26 @@ code = '''
defines.ORDER = [0, 1, 2]
defines.N = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
BENCH_START();
uint32_t prng = 42;
char name[256];
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
for (lfs2_size_t i = 0; i < N; i++) {
lfs2_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: BENCH_PRNG(&prng) % N;
printf("hm %d\n", i);
sprintf(name, "dir%08x", i_);
int err = lfs_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST);
int err = lfs2_mkdir(&lfs2, name);
assert(!err || err == LFS2_ERR_EXIST);
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.bench_dir_rmdir]
@@ -239,32 +239,32 @@ code = '''
defines.ORDER = [0, 1, 2]
defines.N = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// first create the dirs
char name[256];
for (lfs_size_t i = 0; i < N; i++) {
for (lfs2_size_t i = 0; i < N; i++) {
sprintf(name, "dir%08x", i);
lfs_mkdir(&lfs, name) => 0;
lfs2_mkdir(&lfs2, name) => 0;
}
// then remove the dirs
BENCH_START();
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
for (lfs2_size_t i = 0; i < N; i++) {
lfs2_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: BENCH_PRNG(&prng) % N;
sprintf(name, "dir%08x", i_);
int err = lfs_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOENT);
int err = lfs2_remove(&lfs2, name);
assert(!err || err == LFS2_ERR_NOENT);
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -6,51 +6,51 @@ defines.ORDER = [0, 1, 2]
defines.SIZE = '128*1024'
defines.CHUNK_SIZE = 64
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_size_t chunks = (SIZE+CHUNK_SIZE-1)/CHUNK_SIZE;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_size_t chunks = (SIZE+CHUNK_SIZE-1)/CHUNK_SIZE;
// first write the file
lfs_file_t file;
lfs2_file_t file;
uint8_t buffer[CHUNK_SIZE];
lfs_file_open(&lfs, &file, "file",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t i = 0; i < chunks; i++) {
lfs2_file_open(&lfs2, &file, "file",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
for (lfs2_size_t i = 0; i < chunks; i++) {
uint32_t chunk_prng = i;
for (lfs_size_t j = 0; j < CHUNK_SIZE; j++) {
for (lfs2_size_t j = 0; j < CHUNK_SIZE; j++) {
buffer[j] = BENCH_PRNG(&chunk_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_close(&lfs2, &file) => 0;
// then read the file
BENCH_START();
lfs_file_open(&lfs, &file, "file", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "file", LFS2_O_RDONLY) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < chunks; i++) {
lfs_off_t i_
for (lfs2_size_t i = 0; i < chunks; i++) {
lfs2_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (chunks-1-i)
: BENCH_PRNG(&prng) % chunks;
lfs_file_seek(&lfs, &file, i_*CHUNK_SIZE, LFS_SEEK_SET)
lfs2_file_seek(&lfs2, &file, i_*CHUNK_SIZE, LFS2_SEEK_SET)
=> i_*CHUNK_SIZE;
lfs_file_read(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_read(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
uint32_t chunk_prng = i_;
for (lfs_size_t j = 0; j < CHUNK_SIZE; j++) {
for (lfs2_size_t j = 0; j < CHUNK_SIZE; j++) {
assert(buffer[j] == BENCH_PRNG(&chunk_prng));
}
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.bench_file_write]
@@ -61,35 +61,35 @@ defines.ORDER = [0, 1, 2]
defines.SIZE = '128*1024'
defines.CHUNK_SIZE = 64
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_size_t chunks = (SIZE+CHUNK_SIZE-1)/CHUNK_SIZE;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_size_t chunks = (SIZE+CHUNK_SIZE-1)/CHUNK_SIZE;
BENCH_START();
lfs_file_t file;
lfs_file_open(&lfs, &file, "file",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "file",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint8_t buffer[CHUNK_SIZE];
uint32_t prng = 42;
for (lfs_size_t i = 0; i < chunks; i++) {
lfs_off_t i_
for (lfs2_size_t i = 0; i < chunks; i++) {
lfs2_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (chunks-1-i)
: BENCH_PRNG(&prng) % chunks;
uint32_t chunk_prng = i_;
for (lfs_size_t j = 0; j < CHUNK_SIZE; j++) {
for (lfs2_size_t j = 0; j < CHUNK_SIZE; j++) {
buffer[j] = BENCH_PRNG(&chunk_prng);
}
lfs_file_seek(&lfs, &file, i_*CHUNK_SIZE, LFS_SEEK_SET)
lfs2_file_seek(&lfs2, &file, i_*CHUNK_SIZE, LFS2_SEEK_SET)
=> i_*CHUNK_SIZE;
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -4,53 +4,53 @@ defines.N = [0, 1024]
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// create files?
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
for (lfs2_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, name,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
for (lfs2_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs2_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = i+j+k;
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs2_file_write(&lfs2, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
BENCH_START();
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.bench_superblocks_missing]
code = '''
lfs_t lfs;
lfs2_t lfs2;
BENCH_START();
int err = lfs_mount(&lfs, cfg);
int err = lfs2_mount(&lfs2, cfg);
assert(err != 0);
BENCH_STOP();
'''
[cases.bench_superblocks_format]
code = '''
lfs_t lfs;
lfs2_t lfs2;
BENCH_START();
lfs_format(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
BENCH_STOP();
'''

6331
lfs.c

File diff suppressed because it is too large Load Diff

6545
lfs2.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,20 @@
/*
* lfs util functions
* lfs2 util functions
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "lfs_util.h"
#include "lfs2_util.h"
// Only compile if user does not provide custom config
#ifndef LFS_CONFIG
#ifndef LFS2_CONFIG
// If user provides their own CRC impl we don't need this
#ifndef LFS2_CRC
// Software CRC implementation with small lookup table
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
@@ -29,6 +31,7 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
return crc;
}
#endif
#endif

View File

@@ -1,41 +1,59 @@
/*
* lfs utility functions
* lfs2 utility functions
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_UTIL_H
#define LFS_UTIL_H
#ifndef LFS2_UTIL_H
#define LFS2_UTIL_H
// Users can override lfs_util.h with their own configuration by defining
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x)
#define LFS2_STRINGIZE2(x) #x
// Users can override lfs2_util.h with their own configuration by defining
// LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h).
//
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
// provided by the config file. To start, I would suggest copying lfs_util.h
// If LFS2_CONFIG is used, none of the default utils will be emitted and must be
// provided by the config file. To start, I would suggest copying lfs2_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)
#ifdef LFS2_CONFIG
#include LFS2_STRINGIZE(LFS2_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 LFS2_MALLOC(sz) my_malloc(sz)
//
// And build littlefs with the header by defining LFS2_DEFINES.
// (-DLFS2_DEFINES=my_defines.h)
#ifdef LFS2_DEFINES
#include LFS2_STRINGIZE(LFS2_DEFINES)
#endif
// System includes
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#ifndef LFS_NO_MALLOC
#ifndef LFS2_NO_MALLOC
#include <stdlib.h>
#endif
#ifndef LFS_NO_ASSERT
#ifndef LFS2_NO_ASSERT
#include <assert.h>
#endif
#if !defined(LFS_NO_DEBUG) || \
!defined(LFS_NO_WARN) || \
!defined(LFS_NO_ERROR) || \
defined(LFS_YES_TRACE)
#if !defined(LFS2_NO_DEBUG) || \
!defined(LFS2_NO_WARN) || \
!defined(LFS2_NO_ERROR) || \
defined(LFS2_YES_TRACE)
#include <stdio.h>
#endif
@@ -50,81 +68,81 @@ extern "C"
// code footprint
// Logging functions
#ifndef LFS_TRACE
#ifdef LFS_YES_TRACE
#define LFS_TRACE_(fmt, ...) \
#ifndef LFS2_TRACE
#ifdef LFS2_YES_TRACE
#define LFS2_TRACE_(fmt, ...) \
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS2_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "")
#else
#define LFS_TRACE(...)
#define LFS2_TRACE(...)
#endif
#endif
#ifndef LFS_DEBUG
#ifndef LFS_NO_DEBUG
#define LFS_DEBUG_(fmt, ...) \
#ifndef LFS2_DEBUG
#ifndef LFS2_NO_DEBUG
#define LFS2_DEBUG_(fmt, ...) \
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "")
#define LFS2_DEBUG(...) LFS2_DEBUG_(__VA_ARGS__, "")
#else
#define LFS_DEBUG(...)
#define LFS2_DEBUG(...)
#endif
#endif
#ifndef LFS_WARN
#ifndef LFS_NO_WARN
#define LFS_WARN_(fmt, ...) \
#ifndef LFS2_WARN
#ifndef LFS2_NO_WARN
#define LFS2_WARN_(fmt, ...) \
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "")
#define LFS2_WARN(...) LFS2_WARN_(__VA_ARGS__, "")
#else
#define LFS_WARN(...)
#define LFS2_WARN(...)
#endif
#endif
#ifndef LFS_ERROR
#ifndef LFS_NO_ERROR
#define LFS_ERROR_(fmt, ...) \
#ifndef LFS2_ERROR
#ifndef LFS2_NO_ERROR
#define LFS2_ERROR_(fmt, ...) \
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "")
#define LFS2_ERROR(...) LFS2_ERROR_(__VA_ARGS__, "")
#else
#define LFS_ERROR(...)
#define LFS2_ERROR(...)
#endif
#endif
// Runtime assertions
#ifndef LFS_ASSERT
#ifndef LFS_NO_ASSERT
#define LFS_ASSERT(test) assert(test)
#ifndef LFS2_ASSERT
#ifndef LFS2_NO_ASSERT
#define LFS2_ASSERT(test) assert(test)
#else
#define LFS_ASSERT(test)
#define LFS2_ASSERT(test)
#endif
#endif
// Builtin functions, these may be replaced by more efficient
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
// toolchain-specific implementations. LFS2_NO_INTRINSICS falls back to a more
// expensive basic C implementation for debugging purposes
// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
static inline uint32_t lfs2_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
static inline uint32_t lfs2_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
// Align to nearest multiple of a size
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
static inline uint32_t lfs2_aligndown(uint32_t a, uint32_t alignment) {
return a - (a % alignment);
}
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return lfs_aligndown(a + alignment-1, alignment);
static inline uint32_t lfs2_alignup(uint32_t a, uint32_t alignment) {
return lfs2_aligndown(a + alignment-1, alignment);
}
// Find the smallest power of 2 greater than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
static inline uint32_t lfs2_npw2(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a-1);
#else
uint32_t r = 0;
@@ -139,18 +157,18 @@ static inline uint32_t lfs_npw2(uint32_t a) {
}
// Count the number of trailing binary zeros in a
// lfs_ctz(0) may be undefined
static inline uint32_t lfs_ctz(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
// lfs2_ctz(0) may be undefined
static inline uint32_t lfs2_ctz(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && defined(__GNUC__)
return __builtin_ctz(a);
#else
return lfs_npw2((a & -a) + 1) - 1;
return lfs2_npw2((a & -a) + 1) - 1;
#endif
}
// Count the number of binary ones in a
static inline uint32_t lfs_popc(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
static inline uint32_t lfs2_popc(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return __builtin_popcount(a);
#else
a = a - ((a >> 1) & 0x55555555);
@@ -161,36 +179,36 @@ static inline uint32_t lfs_popc(uint32_t a) {
// Find the sequence comparison of a and b, this is the distance
// between a and b ignoring overflow
static inline int lfs_scmp(uint32_t a, uint32_t b) {
static inline int lfs2_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b);
}
// Convert between 32-bit little-endian and native order
static inline uint32_t lfs_fromle32(uint32_t a) {
static inline uint32_t lfs2_fromle32(uint32_t a) {
#if (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
return a;
#elif !defined(LFS_NO_INTRINSICS) && ( \
#elif !defined(LFS2_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap32(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8) |
(((uint8_t*)&a)[2] << 16) |
(((uint8_t*)&a)[3] << 24);
return ((uint32_t)((uint8_t*)&a)[0] << 0) |
((uint32_t)((uint8_t*)&a)[1] << 8) |
((uint32_t)((uint8_t*)&a)[2] << 16) |
((uint32_t)((uint8_t*)&a)[3] << 24);
#endif
}
static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a);
static inline uint32_t lfs2_tole32(uint32_t a) {
return lfs2_fromle32(a);
}
// Convert between 32-bit big-endian and native order
static inline uint32_t lfs_frombe32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
static inline uint32_t lfs2_frombe32(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
@@ -200,24 +218,34 @@ static inline uint32_t lfs_frombe32(uint32_t a) {
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
return a;
#else
return (((uint8_t*)&a)[0] << 24) |
(((uint8_t*)&a)[1] << 16) |
(((uint8_t*)&a)[2] << 8) |
(((uint8_t*)&a)[3] << 0);
return ((uint32_t)((uint8_t*)&a)[0] << 24) |
((uint32_t)((uint8_t*)&a)[1] << 16) |
((uint32_t)((uint8_t*)&a)[2] << 8) |
((uint32_t)((uint8_t*)&a)[3] << 0);
#endif
}
static inline uint32_t lfs_tobe32(uint32_t a) {
return lfs_frombe32(a);
static inline uint32_t lfs2_tobe32(uint32_t a) {
return lfs2_frombe32(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
#ifdef LFS2_CRC
static inline uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) {
return LFS2_CRC(crc, buffer, size);
}
#else
uint32_t lfs2_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
static inline void *lfs_malloc(size_t size) {
#ifndef LFS_NO_MALLOC
//
// littlefs current has no alignment requirements, as it only allocates
// byte-level buffers.
static inline void *lfs2_malloc(size_t size) {
#if defined(LFS2_MALLOC)
return LFS2_MALLOC(size);
#elif !defined(LFS2_NO_MALLOC)
return malloc(size);
#else
(void)size;
@@ -226,8 +254,10 @@ 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
static inline void lfs2_free(void *p) {
#if defined(LFS2_FREE)
LFS2_FREE(p);
#elif !defined(LFS2_NO_MALLOC)
free(p);
#else
(void)p;

View File

@@ -9,7 +9,7 @@
#endif
#include "runners/bench_runner.h"
#include "bd/lfs_emubd.h"
#include "bd/lfs2_emubd.h"
#include <getopt.h>
#include <sys/types.h>
@@ -123,8 +123,13 @@ typedef struct bench_id {
// bench suites are linked into a custom ld section
#if defined(__APPLE__)
extern struct bench_suite __start__bench_suites __asm("section$start$__DATA$_bench_suites");
extern struct bench_suite __stop__bench_suites __asm("section$end$__DATA$_bench_suites");
#else
extern struct bench_suite __start__bench_suites;
extern struct bench_suite __stop__bench_suites;
#endif
const struct bench_suite *bench_suites = &__start__bench_suites;
#define BENCH_SUITE_COUNT \
@@ -297,11 +302,11 @@ void bench_define_suite(const struct bench_suite *suite) {
suite->define_names, suite->define_count};
// make sure our cache is large enough
if (lfs_max(suite->define_count, BENCH_IMPLICIT_DEFINE_COUNT)
if (lfs2_max(suite->define_count, BENCH_IMPLICIT_DEFINE_COUNT)
> bench_define_cache_count) {
// align to power of two to avoid any superlinear growth
size_t ncount = 1 << lfs_npw2(
lfs_max(suite->define_count, BENCH_IMPLICIT_DEFINE_COUNT));
size_t ncount = 1 << lfs2_npw2(
lfs2_max(suite->define_count, BENCH_IMPLICIT_DEFINE_COUNT));
bench_define_cache = realloc(bench_define_cache, ncount*sizeof(intmax_t));
bench_define_cache_mask = realloc(bench_define_cache_mask,
sizeof(unsigned)*(
@@ -317,14 +322,14 @@ void bench_define_suite(const struct bench_suite *suite) {
size_t permutations = 1;
for (size_t i = 0; i < bench_override_count; i++) {
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
BENCH_IMPLICIT_DEFINE_COUNT);
d++) {
// define name match?
const char *name = bench_define_name(d);
if (name && strcmp(name, bench_overrides[i].name) == 0) {
count = lfs_max(count, d+1);
count = lfs2_max(count, d+1);
permutations *= bench_overrides[i].permutations;
break;
}
@@ -336,7 +341,7 @@ void bench_define_suite(const struct bench_suite *suite) {
// make sure our override arrays are big enough
if (count * permutations > bench_override_define_capacity) {
// align to power of two to avoid any superlinear growth
size_t ncapacity = 1 << lfs_npw2(count * permutations);
size_t ncapacity = 1 << lfs2_npw2(count * permutations);
bench_override_defines = realloc(
bench_override_defines,
sizeof(bench_define_t)*ncapacity);
@@ -351,7 +356,7 @@ void bench_define_suite(const struct bench_suite *suite) {
size_t p = 1;
for (size_t i = 0; i < bench_override_count; i++) {
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
BENCH_IMPLICIT_DEFINE_COUNT);
d++) {
@@ -433,9 +438,9 @@ FILE *bench_trace_file = NULL;
uint32_t bench_trace_cycles = 0;
uint64_t bench_trace_time = 0;
uint64_t bench_trace_open_time = 0;
lfs_emubd_sleep_t bench_read_sleep = 0.0;
lfs_emubd_sleep_t bench_prog_sleep = 0.0;
lfs_emubd_sleep_t bench_erase_sleep = 0.0;
lfs2_emubd_sleep_t bench_read_sleep = 0.0;
lfs2_emubd_sleep_t bench_prog_sleep = 0.0;
lfs2_emubd_sleep_t bench_erase_sleep = 0.0;
// this determines both the backtrace buffer and the trace printf buffer, if
// trace ends up interleaved or truncated this may need to be increased
@@ -557,13 +562,13 @@ uint32_t bench_prng(uint32_t *state) {
// bench recording state
static struct lfs_config *bench_cfg = NULL;
static lfs_emubd_io_t bench_last_readed = 0;
static lfs_emubd_io_t bench_last_proged = 0;
static lfs_emubd_io_t bench_last_erased = 0;
lfs_emubd_io_t bench_readed = 0;
lfs_emubd_io_t bench_proged = 0;
lfs_emubd_io_t bench_erased = 0;
static struct lfs2_config *bench_cfg = NULL;
static lfs2_emubd_io_t bench_last_readed = 0;
static lfs2_emubd_io_t bench_last_proged = 0;
static lfs2_emubd_io_t bench_last_erased = 0;
lfs2_emubd_io_t bench_readed = 0;
lfs2_emubd_io_t bench_proged = 0;
lfs2_emubd_io_t bench_erased = 0;
void bench_reset(void) {
bench_readed = 0;
@@ -576,11 +581,11 @@ void bench_reset(void) {
void bench_start(void) {
assert(bench_cfg);
lfs_emubd_sio_t readed = lfs_emubd_readed(bench_cfg);
lfs2_emubd_sio_t readed = lfs2_emubd_readed(bench_cfg);
assert(readed >= 0);
lfs_emubd_sio_t proged = lfs_emubd_proged(bench_cfg);
lfs2_emubd_sio_t proged = lfs2_emubd_proged(bench_cfg);
assert(proged >= 0);
lfs_emubd_sio_t erased = lfs_emubd_erased(bench_cfg);
lfs2_emubd_sio_t erased = lfs2_emubd_erased(bench_cfg);
assert(erased >= 0);
bench_last_readed = readed;
@@ -590,11 +595,11 @@ void bench_start(void) {
void bench_stop(void) {
assert(bench_cfg);
lfs_emubd_sio_t readed = lfs_emubd_readed(bench_cfg);
lfs2_emubd_sio_t readed = lfs2_emubd_readed(bench_cfg);
assert(readed >= 0);
lfs_emubd_sio_t proged = lfs_emubd_proged(bench_cfg);
lfs2_emubd_sio_t proged = lfs2_emubd_proged(bench_cfg);
assert(proged >= 0);
lfs_emubd_sio_t erased = lfs_emubd_erased(bench_cfg);
lfs2_emubd_sio_t erased = lfs2_emubd_erased(bench_cfg);
assert(erased >= 0);
bench_readed += readed - bench_last_readed;
@@ -611,7 +616,7 @@ static void perm_printid(
// case[:permutation]
printf("%s:", case_->name);
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
BENCH_IMPLICIT_DEFINE_COUNT);
d++) {
@@ -643,7 +648,7 @@ bool bench_seen_insert(
// use the currently set defines
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
BENCH_IMPLICIT_DEFINE_COUNT);
d++) {
@@ -1071,7 +1076,7 @@ void perm_list_defines(
// collect defines
for (size_t d = 0;
d < lfs_max(suite->define_count,
d < lfs2_max(suite->define_count,
BENCH_IMPLICIT_DEFINE_COUNT);
d++) {
if (d < BENCH_IMPLICIT_DEFINE_COUNT
@@ -1091,7 +1096,7 @@ void perm_list_permutation_defines(
// collect permutation_defines
for (size_t d = 0;
d < lfs_max(suite->define_count,
d < lfs2_max(suite->define_count,
BENCH_IMPLICIT_DEFINE_COUNT);
d++) {
if (bench_define_ispermutation(d)) {
@@ -1306,14 +1311,14 @@ void perm_run(
}
// create block device and configuration
lfs_emubd_t bd;
lfs2_emubd_t bd;
struct lfs_config cfg = {
struct lfs2_config cfg = {
.context = &bd,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read = lfs2_emubd_read,
.prog = lfs2_emubd_prog,
.erase = lfs2_emubd_erase,
.sync = lfs2_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@@ -1321,9 +1326,12 @@ 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 = {
struct lfs2_emubd_config bdcfg = {
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.erase_size = ERASE_SIZE,
@@ -1337,7 +1345,7 @@ void perm_run(
.erase_sleep = bench_erase_sleep,
};
int err = lfs_emubd_create(&cfg, &bdcfg);
int err = lfs2_emubd_create(&cfg, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@@ -1361,7 +1369,7 @@ void perm_run(
printf("\n");
// cleanup
err = lfs_emubd_destroy(&cfg);
err = lfs2_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@@ -1737,7 +1745,7 @@ invalid_define:
// comma-separated read/prog/erase/count
if (*optarg == '{') {
lfs_size_t sizes[4];
lfs2_size_t sizes[4];
size_t count = 0;
char *s = optarg + 1;
@@ -1786,7 +1794,7 @@ invalid_define:
// leb16-encoded read/prog/erase/count
if (*optarg == ':') {
lfs_size_t sizes[4];
lfs2_size_t sizes[4];
size_t count = 0;
char *s = optarg + 1;
@@ -2009,7 +2017,7 @@ getopt_done: ;
if (d >= define_count) {
// align to power of two to avoid any superlinear growth
size_t ncount = 1 << lfs_npw2(d+1);
size_t ncount = 1 << lfs2_npw2(d+1);
defines = realloc(defines,
ncount*sizeof(bench_define_t));
memset(defines+define_count, 0,

View File

@@ -8,16 +8,16 @@
#define BENCH_RUNNER_H
// override LFS_TRACE
// override LFS2_TRACE
void bench_trace(const char *fmt, ...);
#define LFS_TRACE_(fmt, ...) \
#define LFS2_TRACE_(fmt, ...) \
bench_trace("%s:%d:trace: " fmt "%s\n", \
__FILE__, \
__LINE__, \
__VA_ARGS__)
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS_EMUBD_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS2_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "")
#define LFS2_EMUBD_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "")
// provide BENCH_START/BENCH_STOP macros
void bench_start(void);
@@ -28,7 +28,7 @@ void bench_stop(void);
// note these are indirectly included in any generated files
#include "bd/lfs_emubd.h"
#include "bd/lfs2_emubd.h"
#include <stdio.h>
// give source a chance to define feature macros
@@ -37,7 +37,7 @@ void bench_stop(void);
// generated bench configurations
struct lfs_config;
struct lfs2_config;
enum bench_flags {
BENCH_REENTRANT = 0x1,
@@ -58,7 +58,7 @@ struct bench_case {
const bench_define_t *defines;
bool (*filter)(void);
void (*run)(struct lfs_config *cfg);
void (*run)(struct lfs2_config *cfg);
};
struct bench_suite {
@@ -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)
@@ -121,17 +127,20 @@ intmax_t bench_define(size_t define);
BENCH_DEF(ERASE_SIZE, 0) \
BENCH_DEF(ERASE_COUNT, (1024*1024)/BLOCK_SIZE) \
BENCH_DEF(BLOCK_SIZE, ERASE_SIZE) \
BENCH_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1))\
BENCH_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
BENCH_DEF(BLOCK_COUNT, ERASE_COUNT/lfs2_max(BLOCK_SIZE/ERASE_SIZE,1))\
BENCH_DEF(CACHE_SIZE, lfs2_max(64,lfs2_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) \
BENCH_DEF(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR) \
BENCH_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP)
BENCH_DEF(BADBLOCK_BEHAVIOR, LFS2_EMUBD_BADBLOCK_PROGERROR) \
BENCH_DEF(POWERLOSS_BEHAVIOR, LFS2_EMUBD_POWERLOSS_NOOP)
#define BENCH_GEOMETRY_DEFINE_COUNT 4
#define BENCH_IMPLICIT_DEFINE_COUNT 13
#define BENCH_IMPLICIT_DEFINE_COUNT 16
#endif

View File

@@ -9,7 +9,7 @@
#endif
#include "runners/test_runner.h"
#include "bd/lfs_emubd.h"
#include "bd/lfs2_emubd.h"
#include <getopt.h>
#include <sys/types.h>
@@ -118,11 +118,11 @@ typedef struct test_geometry {
typedef struct test_powerloss {
const char *name;
void (*run)(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_);
const lfs_emubd_powercycles_t *cycles;
const lfs2_emubd_powercycles_t *cycles;
size_t cycle_count;
} test_powerloss_t;
@@ -130,14 +130,19 @@ typedef struct test_id {
const char *name;
const test_define_t *defines;
size_t define_count;
const lfs_emubd_powercycles_t *cycles;
const lfs2_emubd_powercycles_t *cycles;
size_t cycle_count;
} test_id_t;
// test suites are linked into a custom ld section
#if defined(__APPLE__)
extern struct test_suite __start__test_suites __asm("section$start$__DATA$_test_suites");
extern struct test_suite __stop__test_suites __asm("section$end$__DATA$_test_suites");
#else
extern struct test_suite __start__test_suites;
extern struct test_suite __stop__test_suites;
#endif
const struct test_suite *test_suites = &__start__test_suites;
#define TEST_SUITE_COUNT \
@@ -310,11 +315,11 @@ void test_define_suite(const struct test_suite *suite) {
suite->define_names, suite->define_count};
// make sure our cache is large enough
if (lfs_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT)
if (lfs2_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT)
> test_define_cache_count) {
// align to power of two to avoid any superlinear growth
size_t ncount = 1 << lfs_npw2(
lfs_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT));
size_t ncount = 1 << lfs2_npw2(
lfs2_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT));
test_define_cache = realloc(test_define_cache, ncount*sizeof(intmax_t));
test_define_cache_mask = realloc(test_define_cache_mask,
sizeof(unsigned)*(
@@ -330,14 +335,14 @@ void test_define_suite(const struct test_suite *suite) {
size_t permutations = 1;
for (size_t i = 0; i < test_override_count; i++) {
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
TEST_IMPLICIT_DEFINE_COUNT);
d++) {
// define name match?
const char *name = test_define_name(d);
if (name && strcmp(name, test_overrides[i].name) == 0) {
count = lfs_max(count, d+1);
count = lfs2_max(count, d+1);
permutations *= test_overrides[i].permutations;
break;
}
@@ -349,7 +354,7 @@ void test_define_suite(const struct test_suite *suite) {
// make sure our override arrays are big enough
if (count * permutations > test_override_define_capacity) {
// align to power of two to avoid any superlinear growth
size_t ncapacity = 1 << lfs_npw2(count * permutations);
size_t ncapacity = 1 << lfs2_npw2(count * permutations);
test_override_defines = realloc(
test_override_defines,
sizeof(test_define_t)*ncapacity);
@@ -364,7 +369,7 @@ void test_define_suite(const struct test_suite *suite) {
size_t p = 1;
for (size_t i = 0; i < test_override_count; i++) {
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
TEST_IMPLICIT_DEFINE_COUNT);
d++) {
@@ -449,9 +454,9 @@ FILE *test_trace_file = NULL;
uint32_t test_trace_cycles = 0;
uint64_t test_trace_time = 0;
uint64_t test_trace_open_time = 0;
lfs_emubd_sleep_t test_read_sleep = 0.0;
lfs_emubd_sleep_t test_prog_sleep = 0.0;
lfs_emubd_sleep_t test_erase_sleep = 0.0;
lfs2_emubd_sleep_t test_read_sleep = 0.0;
lfs2_emubd_sleep_t test_prog_sleep = 0.0;
lfs2_emubd_sleep_t test_erase_sleep = 0.0;
// this determines both the backtrace buffer and the trace printf buffer, if
// trace ends up interleaved or truncated this may need to be increased
@@ -576,13 +581,13 @@ uint32_t test_prng(uint32_t *state) {
static void perm_printid(
const struct test_suite *suite,
const struct test_case *case_,
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count) {
(void)suite;
// case[:permutation[:powercycles]]
printf("%s:", case_->name);
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
TEST_IMPLICIT_DEFINE_COUNT);
d++) {
@@ -623,7 +628,7 @@ bool test_seen_insert(
// use the currently set defines
for (size_t d = 0;
d < lfs_max(
d < lfs2_max(
suite->define_count,
TEST_IMPLICIT_DEFINE_COUNT);
d++) {
@@ -665,12 +670,12 @@ void test_seen_cleanup(test_seen_t *seen) {
}
static void run_powerloss_none(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_);
static void run_powerloss_cycles(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_);
@@ -681,7 +686,7 @@ static void case_forperm(
const struct test_case *case_,
const test_define_t *defines,
size_t define_count,
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
void (*cb)(
void *data,
@@ -1106,7 +1111,7 @@ void perm_list_defines(
// collect defines
for (size_t d = 0;
d < lfs_max(suite->define_count,
d < lfs2_max(suite->define_count,
TEST_IMPLICIT_DEFINE_COUNT);
d++) {
if (d < TEST_IMPLICIT_DEFINE_COUNT
@@ -1128,7 +1133,7 @@ void perm_list_permutation_defines(
// collect permutation_defines
for (size_t d = 0;
d < lfs_max(suite->define_count,
d < lfs2_max(suite->define_count,
TEST_IMPLICIT_DEFINE_COUNT);
d++) {
if (test_define_ispermutation(d)) {
@@ -1322,7 +1327,7 @@ static void list_geometries(void) {
// scenarios to run tests under power-loss
static void run_powerloss_none(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@@ -1331,14 +1336,14 @@ static void run_powerloss_none(
(void)suite;
// create block device and configuration
lfs_emubd_t bd;
lfs2_emubd_t bd;
struct lfs_config cfg = {
struct lfs2_config cfg = {
.context = &bd,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read = lfs2_emubd_read,
.prog = lfs2_emubd_prog,
.erase = lfs2_emubd_erase,
.sync = lfs2_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@@ -1346,12 +1351,15 @@ static void run_powerloss_none(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.compact_thresh = COMPACT_THRESH,
.metadata_max = METADATA_MAX,
.inline_max = INLINE_MAX,
#ifdef LFS2_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
struct lfs2_emubd_config bdcfg = {
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.erase_size = ERASE_SIZE,
@@ -1365,7 +1373,7 @@ static void run_powerloss_none(
.erase_sleep = test_erase_sleep,
};
int err = lfs_emubd_create(&cfg, &bdcfg);
int err = lfs2_emubd_create(&cfg, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@@ -1383,7 +1391,7 @@ static void run_powerloss_none(
printf("\n");
// cleanup
err = lfs_emubd_destroy(&cfg);
err = lfs2_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@@ -1396,7 +1404,7 @@ static void powerloss_longjmp(void *c) {
}
static void run_powerloss_linear(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@@ -1405,16 +1413,16 @@ static void run_powerloss_linear(
(void)suite;
// create block device and configuration
lfs_emubd_t bd;
lfs2_emubd_t bd;
jmp_buf powerloss_jmp;
volatile lfs_emubd_powercycles_t i = 1;
volatile lfs2_emubd_powercycles_t i = 1;
struct lfs_config cfg = {
struct lfs2_config cfg = {
.context = &bd,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read = lfs2_emubd_read,
.prog = lfs2_emubd_prog,
.erase = lfs2_emubd_erase,
.sync = lfs2_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@@ -1422,12 +1430,15 @@ static void run_powerloss_linear(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.compact_thresh = COMPACT_THRESH,
.metadata_max = METADATA_MAX,
.inline_max = INLINE_MAX,
#ifdef LFS2_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
struct lfs2_emubd_config bdcfg = {
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.erase_size = ERASE_SIZE,
@@ -1445,7 +1456,7 @@ static void run_powerloss_linear(
.powerloss_data = &powerloss_jmp,
};
int err = lfs_emubd_create(&cfg, &bdcfg);
int err = lfs2_emubd_create(&cfg, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@@ -1467,13 +1478,13 @@ static void run_powerloss_linear(
printf("powerloss ");
perm_printid(suite, case_, NULL, 0);
printf(":");
for (lfs_emubd_powercycles_t j = 1; j <= i; j++) {
for (lfs2_emubd_powercycles_t j = 1; j <= i; j++) {
leb16_print(j);
}
printf("\n");
i += 1;
lfs_emubd_setpowercycles(&cfg, i);
lfs2_emubd_setpowercycles(&cfg, i);
}
printf("finished ");
@@ -1481,7 +1492,7 @@ static void run_powerloss_linear(
printf("\n");
// cleanup
err = lfs_emubd_destroy(&cfg);
err = lfs2_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@@ -1489,7 +1500,7 @@ static void run_powerloss_linear(
}
static void run_powerloss_log(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@@ -1498,16 +1509,16 @@ static void run_powerloss_log(
(void)suite;
// create block device and configuration
lfs_emubd_t bd;
lfs2_emubd_t bd;
jmp_buf powerloss_jmp;
volatile lfs_emubd_powercycles_t i = 1;
volatile lfs2_emubd_powercycles_t i = 1;
struct lfs_config cfg = {
struct lfs2_config cfg = {
.context = &bd,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read = lfs2_emubd_read,
.prog = lfs2_emubd_prog,
.erase = lfs2_emubd_erase,
.sync = lfs2_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@@ -1515,12 +1526,15 @@ static void run_powerloss_log(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.compact_thresh = COMPACT_THRESH,
.metadata_max = METADATA_MAX,
.inline_max = INLINE_MAX,
#ifdef LFS2_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
struct lfs2_emubd_config bdcfg = {
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.erase_size = ERASE_SIZE,
@@ -1538,7 +1552,7 @@ static void run_powerloss_log(
.powerloss_data = &powerloss_jmp,
};
int err = lfs_emubd_create(&cfg, &bdcfg);
int err = lfs2_emubd_create(&cfg, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@@ -1560,13 +1574,13 @@ static void run_powerloss_log(
printf("powerloss ");
perm_printid(suite, case_, NULL, 0);
printf(":");
for (lfs_emubd_powercycles_t j = 1; j <= i; j *= 2) {
for (lfs2_emubd_powercycles_t j = 1; j <= i; j *= 2) {
leb16_print(j);
}
printf("\n");
i *= 2;
lfs_emubd_setpowercycles(&cfg, i);
lfs2_emubd_setpowercycles(&cfg, i);
}
printf("finished ");
@@ -1574,7 +1588,7 @@ static void run_powerloss_log(
printf("\n");
// cleanup
err = lfs_emubd_destroy(&cfg);
err = lfs2_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@@ -1582,23 +1596,23 @@ static void run_powerloss_log(
}
static void run_powerloss_cycles(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
(void)suite;
// create block device and configuration
lfs_emubd_t bd;
lfs2_emubd_t bd;
jmp_buf powerloss_jmp;
volatile size_t i = 0;
struct lfs_config cfg = {
struct lfs2_config cfg = {
.context = &bd,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read = lfs2_emubd_read,
.prog = lfs2_emubd_prog,
.erase = lfs2_emubd_erase,
.sync = lfs2_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@@ -1606,12 +1620,15 @@ static void run_powerloss_cycles(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.compact_thresh = COMPACT_THRESH,
.metadata_max = METADATA_MAX,
.inline_max = INLINE_MAX,
#ifdef LFS2_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
struct lfs2_emubd_config bdcfg = {
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.erase_size = ERASE_SIZE,
@@ -1629,7 +1646,7 @@ static void run_powerloss_cycles(
.powerloss_data = &powerloss_jmp,
};
int err = lfs_emubd_create(&cfg, &bdcfg);
int err = lfs2_emubd_create(&cfg, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@@ -1654,7 +1671,7 @@ static void run_powerloss_cycles(
printf("\n");
i += 1;
lfs_emubd_setpowercycles(&cfg,
lfs2_emubd_setpowercycles(&cfg,
(i < cycle_count) ? cycles[i] : 0);
}
@@ -1663,7 +1680,7 @@ static void run_powerloss_cycles(
printf("\n");
// cleanup
err = lfs_emubd_destroy(&cfg);
err = lfs2_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@@ -1671,15 +1688,15 @@ static void run_powerloss_cycles(
}
struct powerloss_exhaustive_state {
struct lfs_config *cfg;
struct lfs2_config *cfg;
lfs_emubd_t *branches;
lfs2_emubd_t *branches;
size_t branch_count;
size_t branch_capacity;
};
struct powerloss_exhaustive_cycles {
lfs_emubd_powercycles_t *cycles;
lfs2_emubd_powercycles_t *cycles;
size_t cycle_count;
size_t cycle_capacity;
};
@@ -1687,9 +1704,9 @@ struct powerloss_exhaustive_cycles {
static void powerloss_exhaustive_branch(void *c) {
struct powerloss_exhaustive_state *state = c;
// append to branches
lfs_emubd_t *branch = mappend(
lfs2_emubd_t *branch = mappend(
(void**)&state->branches,
sizeof(lfs_emubd_t),
sizeof(lfs2_emubd_t),
&state->branch_count,
&state->branch_capacity);
if (!branch) {
@@ -1698,22 +1715,22 @@ static void powerloss_exhaustive_branch(void *c) {
}
// create copy-on-write copy
int err = lfs_emubd_copy(state->cfg, branch);
int err = lfs2_emubd_copy(state->cfg, branch);
if (err) {
fprintf(stderr, "error: exhaustive: could not create bd copy\n");
exit(-1);
}
// also trigger on next power cycle
lfs_emubd_setpowercycles(state->cfg, 1);
lfs2_emubd_setpowercycles(state->cfg, 1);
}
static void run_powerloss_exhaustive_layer(
struct powerloss_exhaustive_cycles *cycles,
const struct test_suite *suite,
const struct test_case *case_,
struct lfs_config *cfg,
struct lfs_emubd_config *bdcfg,
struct lfs2_config *cfg,
struct lfs2_emubd_config *bdcfg,
size_t depth) {
(void)suite;
@@ -1726,14 +1743,14 @@ static void run_powerloss_exhaustive_layer(
// run through the test without additional powerlosses, collecting possible
// branches as we do so
lfs_emubd_setpowercycles(state.cfg, depth > 0 ? 1 : 0);
lfs2_emubd_setpowercycles(state.cfg, depth > 0 ? 1 : 0);
bdcfg->powerloss_data = &state;
// run the tests
case_->run(cfg);
// aggressively clean up memory here to try to keep our memory usage low
int err = lfs_emubd_destroy(cfg);
int err = lfs2_emubd_destroy(cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@@ -1742,9 +1759,9 @@ static void run_powerloss_exhaustive_layer(
// recurse into each branch
for (size_t i = 0; i < state.branch_count; i++) {
// first push and print the branch
lfs_emubd_powercycles_t *cycle = mappend(
lfs2_emubd_powercycles_t *cycle = mappend(
(void**)&cycles->cycles,
sizeof(lfs_emubd_powercycles_t),
sizeof(lfs2_emubd_powercycles_t),
&cycles->cycle_count,
&cycles->cycle_capacity);
if (!cycle) {
@@ -1772,7 +1789,7 @@ static void run_powerloss_exhaustive_layer(
}
static void run_powerloss_exhaustive(
const lfs_emubd_powercycles_t *cycles,
const lfs2_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@@ -1780,14 +1797,14 @@ static void run_powerloss_exhaustive(
(void)suite;
// create block device and configuration
lfs_emubd_t bd;
lfs2_emubd_t bd;
struct lfs_config cfg = {
struct lfs2_config cfg = {
.context = &bd,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read = lfs2_emubd_read,
.prog = lfs2_emubd_prog,
.erase = lfs2_emubd_erase,
.sync = lfs2_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@@ -1795,12 +1812,15 @@ static void run_powerloss_exhaustive(
.block_cycles = BLOCK_CYCLES,
.cache_size = CACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS_MULTIVERSION
.compact_thresh = COMPACT_THRESH,
.metadata_max = METADATA_MAX,
.inline_max = INLINE_MAX,
#ifdef LFS2_MULTIVERSION
.disk_version = DISK_VERSION,
#endif
};
struct lfs_emubd_config bdcfg = {
struct lfs2_emubd_config bdcfg = {
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.erase_size = ERASE_SIZE,
@@ -1817,7 +1837,7 @@ static void run_powerloss_exhaustive(
.powerloss_data = NULL,
};
int err = lfs_emubd_create(&cfg, &bdcfg);
int err = lfs2_emubd_create(&cfg, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@@ -2306,7 +2326,7 @@ invalid_define:
// comma-separated read/prog/erase/count
if (*optarg == '{') {
lfs_size_t sizes[4];
lfs2_size_t sizes[4];
size_t count = 0;
char *s = optarg + 1;
@@ -2355,7 +2375,7 @@ invalid_define:
// leb16-encoded read/prog/erase/count
if (*optarg == ':') {
lfs_size_t sizes[4];
lfs2_size_t sizes[4];
size_t count = 0;
char *s = optarg + 1;
@@ -2451,16 +2471,16 @@ geometry_next:
// comma-separated permutation
if (*optarg == '{') {
lfs_emubd_powercycles_t *cycles = NULL;
lfs2_emubd_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
char *s = optarg + 1;
while (true) {
char *parsed = NULL;
*(lfs_emubd_powercycles_t*)mappend(
*(lfs2_emubd_powercycles_t*)mappend(
(void**)&cycles,
sizeof(lfs_emubd_powercycles_t),
sizeof(lfs2_emubd_powercycles_t),
&cycle_count,
&cycle_capacity)
= strtoumax(s, &parsed, 0);
@@ -2488,7 +2508,7 @@ geometry_next:
// leb16-encoded permutation
if (*optarg == ':') {
lfs_emubd_powercycles_t *cycles = NULL;
lfs2_emubd_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
@@ -2500,9 +2520,9 @@ geometry_next:
break;
}
*(lfs_emubd_powercycles_t*)mappend(
*(lfs2_emubd_powercycles_t*)mappend(
(void**)&cycles,
sizeof(lfs_emubd_powercycles_t),
sizeof(lfs2_emubd_powercycles_t),
&cycle_count,
&cycle_capacity) = x;
s = parsed;
@@ -2681,7 +2701,7 @@ getopt_done: ;
for (; argc > optind; optind++) {
test_define_t *defines = NULL;
size_t define_count = 0;
lfs_emubd_powercycles_t *cycles = NULL;
lfs2_emubd_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
// parse name, can be suite or case
@@ -2722,7 +2742,7 @@ getopt_done: ;
if (d >= define_count) {
// align to power of two to avoid any superlinear growth
size_t ncount = 1 << lfs_npw2(d+1);
size_t ncount = 1 << lfs2_npw2(d+1);
defines = realloc(defines,
ncount*sizeof(test_define_t));
memset(defines+define_count, 0,
@@ -2737,9 +2757,9 @@ getopt_done: ;
size_t cycle_capacity = 0;
while (*cycles_ != '\0') {
char *parsed = NULL;
*(lfs_emubd_powercycles_t*)mappend(
*(lfs2_emubd_powercycles_t*)mappend(
(void**)&cycles,
sizeof(lfs_emubd_powercycles_t),
sizeof(lfs2_emubd_powercycles_t),
&cycle_count,
&cycle_capacity)
= leb16_parse(cycles_, &parsed);

View File

@@ -8,20 +8,20 @@
#define TEST_RUNNER_H
// override LFS_TRACE
// override LFS2_TRACE
void test_trace(const char *fmt, ...);
#define LFS_TRACE_(fmt, ...) \
#define LFS2_TRACE_(fmt, ...) \
test_trace("%s:%d:trace: " fmt "%s\n", \
__FILE__, \
__LINE__, \
__VA_ARGS__)
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS_EMUBD_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS2_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "")
#define LFS2_EMUBD_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "")
// note these are indirectly included in any generated files
#include "bd/lfs_emubd.h"
#include "bd/lfs2_emubd.h"
#include <stdio.h>
// give source a chance to define feature macros
@@ -30,7 +30,7 @@ void test_trace(const char *fmt, ...);
// generated test configurations
struct lfs_config;
struct lfs2_config;
enum test_flags {
TEST_REENTRANT = 0x1,
@@ -51,7 +51,7 @@ struct test_case {
const test_define_t *defines;
bool (*filter)(void);
void (*run)(struct lfs_config *cfg);
void (*run)(struct lfs2_config *cfg);
};
struct test_suite {
@@ -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)
@@ -116,18 +122,21 @@ intmax_t test_define(size_t define);
TEST_DEF(ERASE_SIZE, 0) \
TEST_DEF(ERASE_COUNT, (1024*1024)/ERASE_SIZE) \
TEST_DEF(BLOCK_SIZE, ERASE_SIZE) \
TEST_DEF(BLOCK_COUNT, ERASE_COUNT/lfs_max(BLOCK_SIZE/ERASE_SIZE,1)) \
TEST_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
TEST_DEF(BLOCK_COUNT, ERASE_COUNT/lfs2_max(BLOCK_SIZE/ERASE_SIZE,1)) \
TEST_DEF(CACHE_SIZE, lfs2_max(64,lfs2_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) \
TEST_DEF(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR) \
TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP) \
TEST_DEF(BADBLOCK_BEHAVIOR, LFS2_EMUBD_BADBLOCK_PROGERROR) \
TEST_DEF(POWERLOSS_BEHAVIOR, LFS2_EMUBD_POWERLOSS_NOOP) \
TEST_DEF(DISK_VERSION, 0)
#define TEST_GEOMETRY_DEFINE_COUNT 4
#define TEST_IMPLICIT_DEFINE_COUNT 14
#define TEST_IMPLICIT_DEFINE_COUNT 17
#endif

View File

@@ -350,7 +350,7 @@ def compile(bench_paths, **args):
# create case run function
f.writeln('void __bench__%s__run('
'__attribute__((unused)) struct lfs_config *cfg) {'
'__attribute__((unused)) struct lfs2_config *cfg) {'
% (case.name))
f.writeln(4*' '+'// bench case %s' % case.name)
if case.code_lineno is not None:
@@ -399,17 +399,20 @@ def compile(bench_paths, **args):
'void);'
% (case.name))
f.writeln('extern void __bench__%s__run('
'struct lfs_config *cfg);'
'struct lfs2_config *cfg);'
% (case.name))
f.writeln()
# create suite struct
#
f.writeln('#if defined(__APPLE__)')
f.writeln('__attribute__((section("__DATA,_bench_suites")))')
f.writeln('#else')
# note we place this in the custom bench_suites section with
# minimum alignment, otherwise GCC ups the alignment to
# 32-bytes for some reason
f.writeln('__attribute__((section("_bench_suites"), '
'aligned(1)))')
f.writeln('#endif')
f.writeln('const struct bench_suite __bench__%s__suite = {'
% suite.name)
f.writeln(4*' '+'.name = "%s",' % suite.name)

View File

@@ -4,7 +4,7 @@
# of a codebase that don't conflict at compile time.
#
# Example:
# $ ./scripts/changeprefix.py lfs lfs3
# $ ./scripts/changeprefix.py lfs2 lfs23
#
# Copyright (c) 2022, The littlefs authors.
# Copyright (c) 2019, Arm Limited. All rights reserved.
@@ -73,7 +73,7 @@ def changefile(from_prefix, to_prefix, from_path, to_path, *,
shutil.copystat(from_path, to_path)
if to_path_temp:
os.rename(to_path, from_path)
shutil.move(to_path, from_path)
elif from_path != '-':
os.remove(from_path)

View File

@@ -5,7 +5,7 @@
# by Linux's Bloat-O-Meter.
#
# Example:
# ./scripts/code.py lfs.o lfs_util.o -Ssize
# ./scripts/code.py lfs2.o lfs2_util.o -Ssize
#
# Copyright (c) 2022, The littlefs authors.
# Copyright (c) 2020, Arm Limited. All rights reserved.

View File

@@ -4,7 +4,7 @@
#
# Example:
# ./scripts/cov.py \
# lfs.t.a.gcda lfs_util.t.a.gcda \
# lfs2.t.a.gcda lfs2_util.t.a.gcda \
# -Flfs.c -Flfs_util.c -slines
#
# Copyright (c) 2022, The littlefs authors.

View File

@@ -5,7 +5,7 @@
# by Linux's Bloat-O-Meter.
#
# Example:
# ./scripts/data.py lfs.o lfs_util.o -Ssize
# ./scripts/data.py lfs2.o lfs2_util.o -Ssize
#
# Copyright (c) 2022, The littlefs authors.
# Copyright (c) 2020, Arm Limited. All rights reserved.

View File

@@ -3,7 +3,7 @@
# Preprocessor that makes asserts easier to debug.
#
# Example:
# ./scripts/prettyasserts.py -p LFS_ASSERT lfs.c -o lfs.a.c
# ./scripts/prettyasserts.py -p LFS2_ASSERT lfs2.c -o lfs2.a.c
#
# Copyright (c) 2022, The littlefs authors.
# Copyright (c) 2020, Arm Limited. All rights reserved.
@@ -35,10 +35,10 @@ LEXEMES = {
'assert': ['assert'],
'arrow': ['=>'],
'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
'paren': ['\(', '\)'],
'paren': [r'\(', r'\)'],
'cmp': CMP.keys(),
'logic': ['\&\&', '\|\|'],
'sep': [':', ';', '\{', '\}', ','],
'logic': [r'\&\&', r'\|\|'],
'sep': [':', ';', r'\{', r'\}', ','],
'op': ['->'], # specifically ops that conflict with cmp
}
@@ -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)

View File

@@ -4,7 +4,7 @@
# report as infinite stack usage.
#
# Example:
# ./scripts/stack.py lfs.ci lfs_util.ci -Slimit
# ./scripts/stack.py lfs2.ci lfs2_util.ci -Slimit
#
# Copyright (c) 2022, The littlefs authors.
# SPDX-License-Identifier: BSD-3-Clause

View File

@@ -3,7 +3,7 @@
# Script to find struct sizes.
#
# Example:
# ./scripts/structs.py lfs.o lfs_util.o -Ssize
# ./scripts/structs.py lfs2.o lfs2_util.o -Ssize
#
# Copyright (c) 2022, The littlefs authors.
# SPDX-License-Identifier: BSD-3-Clause

View File

@@ -3,10 +3,10 @@
# Script to summarize the outputs of other scripts. Operates on CSV files.
#
# Example:
# ./scripts/code.py lfs.o lfs_util.o -q -o lfs.code.csv
# ./scripts/data.py lfs.o lfs_util.o -q -o lfs.data.csv
# ./scripts/summary.py lfs.code.csv lfs.data.csv -q -o lfs.csv
# ./scripts/summary.py -Y lfs.csv -f code=code_size,data=data_size
# ./scripts/code.py lfs2.o lfs2_util.o -q -o lfs2.code.csv
# ./scripts/data.py lfs2.o lfs2_util.o -q -o lfs2.data.csv
# ./scripts/summary.py lfs2.code.csv lfs2.data.csv -q -o lfs2.csv
# ./scripts/summary.py -Y lfs2.csv -f code=code_size,data=data_size
#
# Copyright (c) 2022, The littlefs authors.
# SPDX-License-Identifier: BSD-3-Clause

View File

@@ -102,9 +102,9 @@ class TestCase:
# the runner itself.
for v_ in csplit(v):
m = re.search(r'\brange\b\s*\('
'(?P<start>[^,\s]*)'
'\s*(?:,\s*(?P<stop>[^,\s]*)'
'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
r'(?P<start>[^,\s]*)'
r'\s*(?:,\s*(?P<stop>[^,\s]*)'
r'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
v_)
if m:
start = (int(m.group('start'), 0)
@@ -163,8 +163,8 @@ class TestSuite:
code_linenos = []
for i, line in enumerate(f):
match = re.match(
'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
'|' '(?P<code>code\s*=)',
r'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
r'|' r'(?P<code>code\s*=)',
line)
if match and match.group('case'):
case_linenos.append((i+1, match.group('name')))
@@ -358,7 +358,7 @@ def compile(test_paths, **args):
# create case run function
f.writeln('void __test__%s__run('
'__attribute__((unused)) struct lfs_config *cfg) {'
'__attribute__((unused)) struct lfs2_config *cfg) {'
% (case.name))
f.writeln(4*' '+'// test case %s' % case.name)
if case.code_lineno is not None:
@@ -407,17 +407,20 @@ def compile(test_paths, **args):
'void);'
% (case.name))
f.writeln('extern void __test__%s__run('
'struct lfs_config *cfg);'
'struct lfs2_config *cfg);'
% (case.name))
f.writeln()
# create suite struct
#
f.writeln('#if defined(__APPLE__)')
f.writeln('__attribute__((section("__DATA,_test_suites")))')
f.writeln('#else')
# note we place this in the custom test_suites section with
# minimum alignment, otherwise GCC ups the alignment to
# 32-bytes for some reason
f.writeln('__attribute__((section("_test_suites"), '
'aligned(1)))')
f.writeln('#endif')
f.writeln('const struct test_suite __test__%s__suite = {'
% suite.name)
f.writeln(4*' '+'.name = "%s",' % suite.name)
@@ -602,9 +605,9 @@ def find_perms(runner_, ids=[], **args):
errors='replace',
close_fds=False)
pattern = re.compile(
'^(?P<case>[^\s]+)'
'\s+(?P<flags>[^\s]+)'
'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
r'^(?P<case>[^\s]+)'
r'\s+(?P<flags>[^\s]+)'
r'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
# skip the first line
for line in it.islice(proc.stdout, 1, None):
m = pattern.match(line)
@@ -632,8 +635,8 @@ def find_perms(runner_, ids=[], **args):
errors='replace',
close_fds=False)
pattern = re.compile(
'^(?P<case>[^\s]+)'
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
r'^(?P<case>[^\s]+)'
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
# skip the first line
for line in it.islice(proc.stdout, 1, None):
m = pattern.match(line)
@@ -676,8 +679,8 @@ def find_path(runner_, id, **args):
errors='replace',
close_fds=False)
pattern = re.compile(
'^(?P<case>[^\s]+)'
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
r'^(?P<case>[^\s]+)'
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
# skip the first line
for line in it.islice(proc.stdout, 1, None):
m = pattern.match(line)
@@ -706,7 +709,7 @@ def find_defines(runner_, id, **args):
errors='replace',
close_fds=False)
defines = co.OrderedDict()
pattern = re.compile('^(?P<define>\w+)=(?P<value>.+)')
pattern = re.compile(r'^(?P<define>\w+)=(?P<value>.+)')
for line in proc.stdout:
m = pattern.match(line)
if m:
@@ -781,12 +784,12 @@ def run_stage(name, runner_, ids, stdout_, trace_, output_, **args):
failures = []
killed = False
pattern = re.compile('^(?:'
'(?P<op>running|finished|skipped|powerloss) '
'(?P<id>(?P<case>[^:]+)[^\s]*)'
'|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
' *(?P<message>.*)'
')$')
pattern = re.compile(r'^(?:'
r'(?P<op>running|finished|skipped|powerloss) '
r'(?P<id>(?P<case>[^:]+)[^\s]*)'
r'|' r'(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
r' *(?P<message>.*)'
r')$')
locals = th.local()
children = set()

File diff suppressed because it is too large Load Diff

View File

@@ -1,316 +1,316 @@
[cases.test_attrs_get_set]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
uint8_t buffer[1024];
memset(buffer, 0, sizeof(buffer));
lfs_setattr(&lfs, "hello", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "hello", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "hello", 'A', "aaaa", 4) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "bbbbbb", 6) => 0;
lfs2_setattr(&lfs2, "hello", 'C', "ccccc", 5) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "", 0) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "hello", 'B', "", 0) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 0;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_removeattr(&lfs, "hello", 'B') => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => LFS_ERR_NOATTR;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs2_removeattr(&lfs2, "hello", 'B') => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => LFS2_ERR_NOATTR;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "hello", 'B', "dddddd", 6) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "hello", 'B', "eee", 3) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 3;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "hello", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC;
lfs2_setattr(&lfs2, "hello", 'B', "fffffffff", 9) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 9;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
memset(buffer, 0, sizeof(buffer));
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 9) => 9;
lfs2_getattr(&lfs2, "hello", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_attrs_get_set_root]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
uint8_t buffer[1024];
memset(buffer, 0, sizeof(buffer));
lfs_setattr(&lfs, "/", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "/", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "/", 'A', "aaaa", 4) => 0;
lfs2_setattr(&lfs2, "/", 'B', "bbbbbb", 6) => 0;
lfs2_setattr(&lfs2, "/", 'C', "ccccc", 5) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'B', "", 0) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "/", 'B', "", 0) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 0;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_removeattr(&lfs, "/", 'B') => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => LFS_ERR_NOATTR;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs2_removeattr(&lfs2, "/", 'B') => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => LFS2_ERR_NOATTR;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "/", 'B', "dddddd", 6) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs2_setattr(&lfs2, "/", 'B', "eee", 3) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 3;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
lfs2_setattr(&lfs2, "/", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC;
lfs2_setattr(&lfs2, "/", 'B', "fffffffff", 9) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 9;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
memset(buffer, 0, sizeof(buffer));
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 9) => 9;
lfs2_getattr(&lfs2, "/", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_attrs_get_set_file]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
uint8_t buffer[1024];
memset(buffer, 0, sizeof(buffer));
struct lfs_attr attrs1[] = {
struct lfs2_attr attrs1[] = {
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5},
};
struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
memcpy(buffer, "aaaa", 4);
memcpy(buffer+4, "bbbbbb", 6);
memcpy(buffer+10, "ccccc", 5);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memset(buffer, 0, 15);
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "dddddd", 6);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 3;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "eee", 3);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[0].size = LFS_ATTR_MAX+1;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1)
=> LFS_ERR_NOSPC;
attrs1[0].size = LFS2_ATTR_MAX+1;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1)
=> LFS2_ERR_NOSPC;
struct lfs_attr attrs2[] = {
struct lfs2_attr attrs2[] = {
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5},
};
struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3};
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDWR, &cfg2) => 0;
struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDWR, &cfg2) => 0;
memcpy(buffer+4, "fffffffff", 9);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
attrs1[0].size = 4;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
memset(buffer, 0, sizeof(buffer));
struct lfs_attr attrs3[] = {
struct lfs2_attr attrs3[] = {
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5},
};
struct lfs_file_config cfg3 = {.attrs=attrs3, .attr_count=3};
struct lfs2_file_config cfg3 = {.attrs=attrs3, .attr_count=3};
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg3) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg3) => 0;
lfs2_file_close(&lfs2, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_attrs_deferred_file]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello");
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello");
lfs2_file_close(&lfs2, &file);
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_setattr(&lfs, "hello/hello", 'B', "fffffffff", 9) => 0;
lfs_setattr(&lfs, "hello/hello", 'C', "ccccc", 5) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_setattr(&lfs2, "hello/hello", 'B', "fffffffff", 9) => 0;
lfs2_setattr(&lfs2, "hello/hello", 'C', "ccccc", 5) => 0;
uint8_t buffer[1024];
memset(buffer, 0, sizeof(buffer));
struct lfs_attr attrs1[] = {
struct lfs2_attr attrs1[] = {
{'B', "gggg", 4},
{'C', "", 0},
{'D', "hhhh", 4},
};
struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 9;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 5;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => LFS_ERR_NOATTR;
lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 9;
lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 5;
lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => LFS2_ERR_NOATTR;
memcmp(buffer, "fffffffff", 9) => 0;
memcmp(buffer+9, "ccccc\0\0\0\0", 9) => 0;
memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0;
lfs_file_sync(&lfs, &file) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 4;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 0;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4;
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 4;
lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => 4;
memcmp(buffer, "gggg\0\0\0\0\0", 9) => 0;
memcmp(buffer+9, "\0\0\0\0\0\0\0\0\0", 9) => 0;
memcmp(buffer+18, "hhhh\0\0\0\0\0", 9) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -6,78 +6,78 @@ defines.ERASE_COUNT = 256 # small bd so test runs faster
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
'LFS2_EMUBD_BADBLOCK_PROGERROR',
'LFS2_EMUBD_BADBLOCK_ERASEERROR',
'LFS2_EMUBD_BADBLOCK_READERROR',
'LFS2_EMUBD_BADBLOCK_PROGNOOP',
'LFS2_EMUBD_BADBLOCK_ERASENOOP',
]
defines.NAMEMULT = 64
defines.FILEMULT = 1
code = '''
for (lfs_block_t badblock = 2; badblock < BLOCK_COUNT; badblock++) {
lfs_emubd_setwear(cfg, badblock-1, 0) => 0;
lfs_emubd_setwear(cfg, badblock, 0xffffffff) => 0;
for (lfs2_block_t badblock = 2; badblock < BLOCK_COUNT; badblock++) {
lfs2_emubd_setwear(cfg, badblock-1, 0) => 0;
lfs2_emubd_setwear(cfg, badblock, 0xffffffff) => 0;
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 1; i < 10; i++) {
uint8_t buffer[1024];
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs_mkdir(&lfs, (char*)buffer) => 0;
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs_file_t file;
lfs_file_open(&lfs, &file, (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_size_t size = NAMEMULT;
lfs2_size_t size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 1; i < 10; i++) {
uint8_t buffer[1024];
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
struct lfs_info info;
lfs_stat(&lfs, (char*)buffer, &info) => 0;
info.type => LFS_TYPE_DIR;
struct lfs2_info info;
lfs2_stat(&lfs2, (char*)buffer, &info) => 0;
info.type => LFS2_TYPE_DIR;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs_file_t file;
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0;
int size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
uint8_t rbuffer[1024];
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(buffer, rbuffer, size) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
}
'''
@@ -86,78 +86,78 @@ defines.ERASE_COUNT = 256 # small bd so test runs faster
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
'LFS2_EMUBD_BADBLOCK_PROGERROR',
'LFS2_EMUBD_BADBLOCK_ERASEERROR',
'LFS2_EMUBD_BADBLOCK_READERROR',
'LFS2_EMUBD_BADBLOCK_PROGNOOP',
'LFS2_EMUBD_BADBLOCK_ERASENOOP',
]
defines.NAMEMULT = 64
defines.FILEMULT = 1
code = '''
for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
lfs_emubd_setwear(cfg, i+2, 0xffffffff) => 0;
for (lfs2_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
lfs2_emubd_setwear(cfg, i+2, 0xffffffff) => 0;
}
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 1; i < 10; i++) {
uint8_t buffer[1024];
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs_mkdir(&lfs, (char*)buffer) => 0;
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs_file_t file;
lfs_file_open(&lfs, &file, (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_size_t size = NAMEMULT;
lfs2_size_t size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 1; i < 10; i++) {
uint8_t buffer[1024];
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
struct lfs_info info;
lfs_stat(&lfs, (char*)buffer, &info) => 0;
info.type => LFS_TYPE_DIR;
struct lfs2_info info;
lfs2_stat(&lfs2, (char*)buffer, &info) => 0;
info.type => LFS2_TYPE_DIR;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs_file_t file;
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0;
lfs_size_t size = NAMEMULT;
lfs2_size_t size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
uint8_t rbuffer[1024];
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(buffer, rbuffer, size) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_badblocks_alternating_corruption] # (causes cascading failures)
@@ -165,78 +165,78 @@ defines.ERASE_COUNT = 256 # small bd so test runs faster
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
'LFS2_EMUBD_BADBLOCK_PROGERROR',
'LFS2_EMUBD_BADBLOCK_ERASEERROR',
'LFS2_EMUBD_BADBLOCK_READERROR',
'LFS2_EMUBD_BADBLOCK_PROGNOOP',
'LFS2_EMUBD_BADBLOCK_ERASENOOP',
]
defines.NAMEMULT = 64
defines.FILEMULT = 1
code = '''
for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
lfs_emubd_setwear(cfg, (2*i) + 2, 0xffffffff) => 0;
for (lfs2_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
lfs2_emubd_setwear(cfg, (2*i) + 2, 0xffffffff) => 0;
}
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 1; i < 10; i++) {
uint8_t buffer[1024];
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
lfs_mkdir(&lfs, (char*)buffer) => 0;
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs_file_t file;
lfs_file_open(&lfs, &file, (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_size_t size = NAMEMULT;
lfs2_size_t size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 1; i < 10; i++) {
uint8_t buffer[1024];
for (int j = 0; j < NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[NAMEMULT] = '\0';
struct lfs_info info;
lfs_stat(&lfs, (char*)buffer, &info) => 0;
info.type => LFS_TYPE_DIR;
struct lfs2_info info;
lfs2_stat(&lfs2, (char*)buffer, &info) => 0;
info.type => LFS2_TYPE_DIR;
buffer[NAMEMULT] = '/';
for (int j = 0; j < NAMEMULT; j++) {
buffer[j+NAMEMULT+1] = '0'+i;
}
buffer[2*NAMEMULT+1] = '\0';
lfs_file_t file;
lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0;
lfs_size_t size = NAMEMULT;
lfs2_size_t size = NAMEMULT;
for (int j = 0; j < i*FILEMULT; j++) {
uint8_t rbuffer[1024];
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(buffer, rbuffer, size) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# other corner cases
@@ -244,17 +244,17 @@ code = '''
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
'LFS2_EMUBD_BADBLOCK_PROGERROR',
'LFS2_EMUBD_BADBLOCK_ERASEERROR',
'LFS2_EMUBD_BADBLOCK_READERROR',
'LFS2_EMUBD_BADBLOCK_PROGNOOP',
'LFS2_EMUBD_BADBLOCK_ERASENOOP',
]
code = '''
lfs_emubd_setwear(cfg, 0, 0xffffffff) => 0;
lfs_emubd_setwear(cfg, 1, 0xffffffff) => 0;
lfs2_emubd_setwear(cfg, 0, 0xffffffff) => 0;
lfs2_emubd_setwear(cfg, 1, 0xffffffff) => 0;
lfs_t lfs;
lfs_format(&lfs, cfg) => LFS_ERR_NOSPC;
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => LFS2_ERR_NOSPC;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_CORRUPT;
'''

View File

@@ -8,23 +8,23 @@
defines.READ = ['READ_SIZE', 'BLOCK_SIZE']
defines.PROG = ['PROG_SIZE', 'BLOCK_SIZE']
code = '''
uint8_t buffer[lfs_max(READ, PROG)];
uint8_t buffer[lfs2_max(READ, PROG)];
// write data
cfg->erase(cfg, 0) => 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs_off_t j = 0; j < PROG; j++) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs2_off_t j = 0; j < PROG; j++) {
buffer[j] = (i+j) % 251;
}
cfg->prog(cfg, 0, i, buffer, PROG) => 0;
}
// read data
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, 0, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (i+j) % 251);
}
}
'''
@@ -33,14 +33,14 @@ code = '''
defines.READ = ['READ_SIZE', 'BLOCK_SIZE']
defines.PROG = ['PROG_SIZE', 'BLOCK_SIZE']
code = '''
uint8_t buffer[lfs_max(READ, PROG)];
lfs_block_t block;
uint8_t buffer[lfs2_max(READ, PROG)];
lfs2_block_t block;
// write block 0
block = 0;
cfg->erase(cfg, block) => 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs_off_t j = 0; j < PROG; j++) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs2_off_t j = 0; j < PROG; j++) {
buffer[j] = (block+i+j) % 251;
}
cfg->prog(cfg, block, i, buffer, PROG) => 0;
@@ -48,19 +48,19 @@ code = '''
// read block 0
block = 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
// write block 1
block = 1;
cfg->erase(cfg, block) => 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs_off_t j = 0; j < PROG; j++) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs2_off_t j = 0; j < PROG; j++) {
buffer[j] = (block+i+j) % 251;
}
cfg->prog(cfg, block, i, buffer, PROG) => 0;
@@ -68,21 +68,21 @@ code = '''
// read block 1
block = 1;
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
// read block 0 again
block = 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
'''
@@ -91,14 +91,14 @@ code = '''
defines.READ = ['READ_SIZE', 'BLOCK_SIZE']
defines.PROG = ['PROG_SIZE', 'BLOCK_SIZE']
code = '''
uint8_t buffer[lfs_max(READ, PROG)];
lfs_block_t block;
uint8_t buffer[lfs2_max(READ, PROG)];
lfs2_block_t block;
// write block 0
block = 0;
cfg->erase(cfg, block) => 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs_off_t j = 0; j < PROG; j++) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs2_off_t j = 0; j < PROG; j++) {
buffer[j] = (block+i+j) % 251;
}
cfg->prog(cfg, block, i, buffer, PROG) => 0;
@@ -106,19 +106,19 @@ code = '''
// read block 0
block = 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
// write block n-1
block = cfg->block_count-1;
cfg->erase(cfg, block) => 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs_off_t j = 0; j < PROG; j++) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs2_off_t j = 0; j < PROG; j++) {
buffer[j] = (block+i+j) % 251;
}
cfg->prog(cfg, block, i, buffer, PROG) => 0;
@@ -126,21 +126,21 @@ code = '''
// read block n-1
block = cfg->block_count-1;
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
// read block 0 again
block = 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
'''
@@ -149,26 +149,26 @@ code = '''
defines.READ = ['READ_SIZE', 'BLOCK_SIZE']
defines.PROG = ['PROG_SIZE', 'BLOCK_SIZE']
code = '''
uint8_t buffer[lfs_max(READ, PROG)];
uint8_t buffer[lfs2_max(READ, PROG)];
// write/read every power of 2
lfs_block_t block = 1;
lfs2_block_t block = 1;
while (block < cfg->block_count) {
// write
cfg->erase(cfg, block) => 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs_off_t j = 0; j < PROG; j++) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs2_off_t j = 0; j < PROG; j++) {
buffer[j] = (block+i+j) % 251;
}
cfg->prog(cfg, block, i, buffer, PROG) => 0;
}
// read
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
@@ -179,11 +179,11 @@ code = '''
block = 1;
while (block < cfg->block_count) {
// read
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
@@ -195,31 +195,31 @@ code = '''
defines.READ = ['READ_SIZE', 'BLOCK_SIZE']
defines.PROG = ['PROG_SIZE', 'BLOCK_SIZE']
code = '''
uint8_t buffer[lfs_max(READ, PROG)];
uint8_t buffer[lfs2_max(READ, PROG)];
// write/read every fibonacci number on our device
lfs_block_t block = 1;
lfs_block_t block_ = 1;
lfs2_block_t block = 1;
lfs2_block_t block_ = 1;
while (block < cfg->block_count) {
// write
cfg->erase(cfg, block) => 0;
for (lfs_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs_off_t j = 0; j < PROG; j++) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += PROG) {
for (lfs2_off_t j = 0; j < PROG; j++) {
buffer[j] = (block+i+j) % 251;
}
cfg->prog(cfg, block, i, buffer, PROG) => 0;
}
// read
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
lfs_block_t nblock = block + block_;
lfs2_block_t nblock = block + block_;
block_ = block;
block = nblock;
}
@@ -229,15 +229,15 @@ code = '''
block_ = 1;
while (block < cfg->block_count) {
// read
for (lfs_off_t i = 0; i < cfg->block_size; i += READ) {
for (lfs2_off_t i = 0; i < cfg->block_size; i += READ) {
cfg->read(cfg, block, i, buffer, READ) => 0;
for (lfs_off_t j = 0; j < READ; j++) {
LFS_ASSERT(buffer[j] == (block+i+j) % 251);
for (lfs2_off_t j = 0; j < READ; j++) {
LFS2_ASSERT(buffer[j] == (block+i+j) % 251);
}
}
lfs_block_t nblock = block + block_;
lfs2_block_t nblock = block + block_;
block_ = block;
block = nblock;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -10,92 +10,92 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// write hi0 20
char path[1024];
lfs_size_t size;
lfs2_size_t size;
sprintf(path, "hi0"); size = 20;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi2 20
sprintf(path, "hi2"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi3 20
sprintf(path, "hi3"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi0 20
sprintf(path, "hi0"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi2 20
sprintf(path, "hi2"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi3 20
sprintf(path, "hi3"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_entries_shrink]
@@ -103,92 +103,92 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// write hi0 20
char path[1024];
lfs_size_t size;
lfs2_size_t size;
sprintf(path, "hi0"); size = 20;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi2 20
sprintf(path, "hi2"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi3 20
sprintf(path, "hi3"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi0 20
sprintf(path, "hi0"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi2 20
sprintf(path, "hi2"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi3 20
sprintf(path, "hi3"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_entries_spill]
@@ -196,76 +196,76 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// write hi0 200
char path[1024];
lfs_size_t size;
lfs2_size_t size;
sprintf(path, "hi0"); size = 200;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_entries_push_spill]
@@ -273,92 +273,92 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// write hi0 200
char path[1024];
lfs_size_t size;
lfs2_size_t size;
sprintf(path, "hi0"); size = 200;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_entries_push_spill_two]
@@ -366,107 +366,107 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// write hi0 200
char path[1024];
lfs_size_t size;
lfs2_size_t size;
sprintf(path, "hi0"); size = 200;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi4 200
sprintf(path, "hi4"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 20
sprintf(path, "hi1"); size = 20;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi4 200
sprintf(path, "hi4"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_entries_drop]
@@ -474,169 +474,169 @@ code = '''
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// write hi0 200
char path[1024];
lfs_size_t size;
lfs2_size_t size;
sprintf(path, "hi0"); size = 200;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi1 200
sprintf(path, "hi1"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
// write hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs_remove(&lfs, "hi1") => 0;
struct lfs_info info;
lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT;
lfs2_remove(&lfs2, "hi1") => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "hi1", &info) => LFS2_ERR_NOENT;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi2 200
sprintf(path, "hi2"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_remove(&lfs, "hi2") => 0;
lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT;
lfs2_remove(&lfs2, "hi2") => 0;
lfs2_stat(&lfs2, "hi2", &info) => LFS2_ERR_NOENT;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read hi3 200
sprintf(path, "hi3"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_remove(&lfs, "hi3") => 0;
lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT;
lfs2_remove(&lfs2, "hi3") => 0;
lfs2_stat(&lfs2, "hi3", &info) => LFS2_ERR_NOENT;
// read hi0 200
sprintf(path, "hi0"); size = 200;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => size;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_remove(&lfs, "hi0") => 0;
lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT;
lfs2_remove(&lfs2, "hi0") => 0;
lfs2_stat(&lfs2, "hi0", &info) => LFS2_ERR_NOENT;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_entries_create_too_big]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
char path[1024];
memset(path, 'm', 200);
path[200] = '\0';
lfs_size_t size = 400;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_size_t size = 400;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
uint8_t wbuffer[1024];
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
size = 400;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
uint8_t rbuffer[1024];
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_entries_resize_too_big]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
char path[1024];
memset(path, 'm', 200);
path[200] = '\0';
lfs_size_t size = 40;
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_size_t size = 40;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
uint8_t wbuffer[1024];
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
size = 40;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
uint8_t rbuffer[1024];
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
size = 400;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
size = 400;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -4,303 +4,303 @@
# invalid pointer tests (outside of block_count)
[cases.test_evil_invalid_tail_pointer]
defines.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL']
defines.TAIL_TYPE = ['LFS2_TYPE_HARDTAIL', 'LFS2_TYPE_SOFTTAIL']
defines.INVALSET = [0x3, 0x1, 0x2]
in = "lfs.c"
in = "lfs2.c"
code = '''
// create littlefs
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// change tail-pointer to invalid pointers
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
(lfs_block_t[2]){
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8),
(lfs2_block_t[2]){
(INVALSET & 0x1) ? 0xcccccccc : 0,
(INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0;
lfs_deinit(&lfs) => 0;
lfs2_deinit(&lfs2) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_CORRUPT;
'''
[cases.test_evil_invalid_dir_pointer]
defines.INVALSET = [0x3, 0x1, 0x2]
in = "lfs.c"
in = "lfs2.c"
code = '''
// create littlefs
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// make a dir
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "dir_here") => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "dir_here") => 0;
lfs2_unmount(&lfs2) => 0;
// change the dir pointer to be invalid
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
// make sure id 1 == our directory
uint8_t buffer[1024];
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer)
=> LFS_MKTAG(LFS_TYPE_DIR, 1, strlen("dir_here"));
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("dir_here")), buffer)
=> LFS2_MKTAG(LFS2_TYPE_DIR, 1, strlen("dir_here"));
assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0);
// change dir pointer
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, 8),
(lfs_block_t[2]){
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, 8),
(lfs2_block_t[2]){
(INVALSET & 0x1) ? 0xcccccccc : 0,
(INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0;
lfs_deinit(&lfs) => 0;
lfs2_deinit(&lfs2) => 0;
// test that accessing our bad dir fails, note there's a number
// of ways to access the dir, some can fail, but some don't
lfs_mount(&lfs, cfg) => 0;
struct lfs_info info;
lfs_stat(&lfs, "dir_here", &info) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "dir_here", &info) => 0;
assert(strcmp(info.name, "dir_here") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT;
lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT;
lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT;
lfs_file_t file;
lfs_file_open(&lfs, &file, "dir_here/file_here",
LFS_O_RDONLY) => LFS_ERR_CORRUPT;
lfs_file_open(&lfs, &file, "dir_here/file_here",
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_CORRUPT;
lfs_unmount(&lfs) => 0;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "dir_here") => LFS2_ERR_CORRUPT;
lfs2_stat(&lfs2, "dir_here/file_here", &info) => LFS2_ERR_CORRUPT;
lfs2_dir_open(&lfs2, &dir, "dir_here/dir_here") => LFS2_ERR_CORRUPT;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "dir_here/file_here",
LFS2_O_RDONLY) => LFS2_ERR_CORRUPT;
lfs2_file_open(&lfs2, &file, "dir_here/file_here",
LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_CORRUPT;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_evil_invalid_file_pointer]
in = "lfs.c"
in = "lfs2.c"
defines.SIZE = [10, 1000, 100000] # faked file size
code = '''
// create littlefs
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// make a file
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "file_here",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "file_here",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// change the file pointer to be invalid
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
// make sure id 1 == our file
uint8_t buffer[1024];
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here"));
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS2_MKTAG(LFS2_TYPE_REG, 1, strlen("file_here"));
assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0);
// change file pointer
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)),
&(struct lfs_ctz){0xcccccccc, lfs_tole32(SIZE)}})) => 0;
lfs_deinit(&lfs) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_CTZSTRUCT, 1, sizeof(struct lfs2_ctz)),
&(struct lfs2_ctz){0xcccccccc, lfs2_tole32(SIZE)}})) => 0;
lfs2_deinit(&lfs2) => 0;
// test that accessing our bad file fails, note there's a number
// of ways to access the dir, some can fail, but some don't
lfs_mount(&lfs, cfg) => 0;
struct lfs_info info;
lfs_stat(&lfs, "file_here", &info) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "file_here", &info) => 0;
assert(strcmp(info.name, "file_here") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
assert(info.size == SIZE);
lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "file_here", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, SIZE) => LFS2_ERR_CORRUPT;
lfs2_file_close(&lfs2, &file) => 0;
// any allocs that traverse CTZ must unfortunately must fail
if (SIZE > 2*BLOCK_SIZE) {
lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_evil_invalid_ctz_pointer] # invalid pointer in CTZ skip-list test
defines.SIZE = ['2*BLOCK_SIZE', '3*BLOCK_SIZE', '4*BLOCK_SIZE']
in = "lfs.c"
in = "lfs2.c"
code = '''
// create littlefs
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// make a file
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "file_here",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "file_here",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (int i = 0; i < SIZE; i++) {
char c = 'c';
lfs_file_write(&lfs, &file, &c, 1) => 1;
lfs2_file_write(&lfs2, &file, &c, 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// change pointer in CTZ skip-list to be invalid
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
// make sure id 1 == our file and get our CTZ structure
uint8_t buffer[4*BLOCK_SIZE];
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here"));
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("file_here")), buffer)
=> LFS2_MKTAG(LFS2_TYPE_REG, 1, strlen("file_here"));
assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0);
struct lfs_ctz ctz;
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, 1, sizeof(struct lfs_ctz)), &ctz)
=> LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz));
lfs_ctz_fromle32(&ctz);
struct lfs2_ctz ctz;
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x700, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_STRUCT, 1, sizeof(struct lfs2_ctz)), &ctz)
=> LFS2_MKTAG(LFS2_TYPE_CTZSTRUCT, 1, sizeof(struct lfs2_ctz));
lfs2_ctz_fromle32(&ctz);
// rewrite block to contain bad pointer
uint8_t bbuffer[BLOCK_SIZE];
cfg->read(cfg, ctz.head, 0, bbuffer, BLOCK_SIZE) => 0;
uint32_t bad = lfs_tole32(0xcccccccc);
uint32_t bad = lfs2_tole32(0xcccccccc);
memcpy(&bbuffer[0], &bad, sizeof(bad));
memcpy(&bbuffer[4], &bad, sizeof(bad));
cfg->erase(cfg, ctz.head) => 0;
cfg->prog(cfg, ctz.head, 0, bbuffer, BLOCK_SIZE) => 0;
lfs_deinit(&lfs) => 0;
lfs2_deinit(&lfs2) => 0;
// test that accessing our bad file fails, note there's a number
// of ways to access the dir, some can fail, but some don't
lfs_mount(&lfs, cfg) => 0;
struct lfs_info info;
lfs_stat(&lfs, "file_here", &info) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "file_here", &info) => 0;
assert(strcmp(info.name, "file_here") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
assert(info.size == SIZE);
lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "file_here", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, SIZE) => LFS2_ERR_CORRUPT;
lfs2_file_close(&lfs2, &file) => 0;
// any allocs that traverse CTZ must unfortunately must fail
if (SIZE > 2*BLOCK_SIZE) {
lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT;
lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_evil_invalid_gstate_pointer]
defines.INVALSET = [0x3, 0x1, 0x2]
in = "lfs.c"
in = "lfs2.c"
code = '''
// create littlefs
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// create an invalid gstate
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_fs_prepmove(&lfs2, 1, (lfs2_block_t [2]){
(INVALSET & 0x1) ? 0xcccccccc : 0,
(INVALSET & 0x2) ? 0xcccccccc : 0});
lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0;
lfs_deinit(&lfs) => 0;
lfs2_dir_commit(&lfs2, &mdir, NULL, 0) => 0;
lfs2_deinit(&lfs2) => 0;
// test that mount fails gracefully
// mount may not fail, but our first alloc should fail when
// we try to fix the gstate
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "should_fail") => LFS2_ERR_CORRUPT;
lfs2_unmount(&lfs2) => 0;
'''
# cycle detection/recovery tests
[cases.test_evil_mdir_loop] # metadata-pair threaded-list loop test
in = "lfs.c"
in = "lfs2.c"
code = '''
// create littlefs
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// change tail-pointer to point to ourself
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
(lfs_block_t[2]){0, 1}})) => 0;
lfs_deinit(&lfs) => 0;
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8),
(lfs2_block_t[2]){0, 1}})) => 0;
lfs2_deinit(&lfs2) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_CORRUPT;
'''
[cases.test_evil_mdir_loop2] # metadata-pair threaded-list 2-length loop test
in = "lfs.c"
in = "lfs2.c"
code = '''
// create littlefs with child dir
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
// find child
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_block_t pair[2];
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs_pair_fromle32(pair);
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_block_t pair[2];
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x7ff, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs2_pair_fromle32(pair);
// change tail-pointer to point to root
lfs_dir_fetch(&lfs, &mdir, pair) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8),
(lfs_block_t[2]){0, 1}})) => 0;
lfs_deinit(&lfs) => 0;
lfs2_dir_fetch(&lfs2, &mdir, pair) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8),
(lfs2_block_t[2]){0, 1}})) => 0;
lfs2_deinit(&lfs2) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_CORRUPT;
'''
[cases.test_evil_mdir_loop_child] # metadata-pair threaded-list 1-length child loop test
in = "lfs.c"
in = "lfs2.c"
code = '''
// create littlefs with child dir
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
// find child
lfs_init(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_block_t pair[2];
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_get(&lfs, &mdir,
LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs_pair_fromle32(pair);
lfs2_init(&lfs2, cfg) => 0;
lfs2_mdir_t mdir;
lfs2_block_t pair[2];
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_get(&lfs2, &mdir,
LFS2_MKTAG(0x7ff, 0x3ff, 0),
LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair)
=> LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair));
lfs2_pair_fromle32(pair);
// change tail-pointer to point to ourself
lfs_dir_fetch(&lfs, &mdir, pair) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0;
lfs_deinit(&lfs) => 0;
lfs2_dir_fetch(&lfs2, &mdir, pair) => 0;
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0;
lfs2_deinit(&lfs2) => 0;
// test that mount fails gracefully
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_CORRUPT;
'''

View File

@@ -4,50 +4,50 @@ defines.ERASE_CYCLES = 10
defines.ERASE_COUNT = 256 # small bd so test runs faster
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
defines.BADBLOCK_BEHAVIOR = [
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
'LFS2_EMUBD_BADBLOCK_PROGERROR',
'LFS2_EMUBD_BADBLOCK_ERASEERROR',
'LFS2_EMUBD_BADBLOCK_READERROR',
'LFS2_EMUBD_BADBLOCK_PROGNOOP',
'LFS2_EMUBD_BADBLOCK_ERASENOOP',
]
defines.FILES = 10
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "roadrunner") => 0;
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "roadrunner") => 0;
lfs2_unmount(&lfs2) => 0;
uint32_t cycle = 0;
while (true) {
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
char path[1024];
sprintf(path, "roadrunner/test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
for (lfs_size_t j = 0; j < size; j++) {
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) {
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
@@ -57,37 +57,37 @@ code = '''
char path[1024];
sprintf(path, "roadrunner/test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
char r;
lfs_file_read(&lfs, &file, &r, 1) => 1;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
assert(r == c);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
char path[1024];
sprintf(path, "roadrunner/test%d", i);
struct lfs_info info;
lfs_stat(&lfs, path, &info) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, path, &info) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
LFS_WARN("completed %d cycles", cycle);
LFS2_WARN("completed %d cycles", cycle);
'''
# test running a filesystem to exhaustion
@@ -97,47 +97,47 @@ defines.ERASE_CYCLES = 10
defines.ERASE_COUNT = 256 # small bd so test runs faster
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
defines.BADBLOCK_BEHAVIOR = [
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
'LFS2_EMUBD_BADBLOCK_PROGERROR',
'LFS2_EMUBD_BADBLOCK_ERASEERROR',
'LFS2_EMUBD_BADBLOCK_READERROR',
'LFS2_EMUBD_BADBLOCK_PROGNOOP',
'LFS2_EMUBD_BADBLOCK_ERASENOOP',
]
defines.FILES = 10
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
uint32_t cycle = 0;
while (true) {
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
char path[1024];
sprintf(path, "test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
for (lfs_size_t j = 0; j < size; j++) {
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) {
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
@@ -147,37 +147,37 @@ code = '''
char path[1024];
sprintf(path, "test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
char r;
lfs_file_read(&lfs, &file, &r, 1) => 1;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
assert(r == c);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
char path[1024];
struct lfs_info info;
struct lfs2_info info;
sprintf(path, "test%d", i);
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
LFS_WARN("completed %d cycles", cycle);
LFS2_WARN("completed %d cycles", cycle);
'''
# These are a sort of high-level litmus test for wear-leveling. One definition
@@ -196,47 +196,47 @@ code = '''
const uint32_t run_block_count[2] = {BLOCK_COUNT/2, BLOCK_COUNT};
for (int run = 0; run < 2; run++) {
for (lfs_block_t b = 0; b < BLOCK_COUNT; b++) {
lfs_emubd_setwear(cfg, b,
for (lfs2_block_t b = 0; b < BLOCK_COUNT; b++) {
lfs2_emubd_setwear(cfg, b,
(b < run_block_count[run]) ? 0 : ERASE_CYCLES) => 0;
}
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "roadrunner") => 0;
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "roadrunner") => 0;
lfs2_unmount(&lfs2) => 0;
uint32_t cycle = 0;
while (true) {
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
char path[1024];
sprintf(path, "roadrunner/test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
for (lfs_size_t j = 0; j < size; j++) {
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) {
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
@@ -246,43 +246,43 @@ code = '''
char path[1024];
sprintf(path, "roadrunner/test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
char r;
lfs_file_read(&lfs, &file, &r, 1) => 1;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
assert(r == c);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
char path[1024];
struct lfs_info info;
struct lfs2_info info;
sprintf(path, "roadrunner/test%d", i);
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
run_cycles[run] = cycle;
LFS_WARN("completed %d blocks %d cycles",
LFS2_WARN("completed %d blocks %d cycles",
run_block_count[run], run_cycles[run]);
}
// check we increased the lifetime by 2x with ~10% error
LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
'''
# wear-level test + expanding superblock
@@ -296,44 +296,44 @@ code = '''
const uint32_t run_block_count[2] = {BLOCK_COUNT/2, BLOCK_COUNT};
for (int run = 0; run < 2; run++) {
for (lfs_block_t b = 0; b < BLOCK_COUNT; b++) {
lfs_emubd_setwear(cfg, b,
for (lfs2_block_t b = 0; b < BLOCK_COUNT; b++) {
lfs2_emubd_setwear(cfg, b,
(b < run_block_count[run]) ? 0 : ERASE_CYCLES) => 0;
}
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
uint32_t cycle = 0;
while (true) {
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
char path[1024];
sprintf(path, "test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
for (lfs_size_t j = 0; j < size; j++) {
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) {
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
@@ -343,43 +343,43 @@ code = '''
char path[1024];
sprintf(path, "test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << ((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
char r;
lfs_file_read(&lfs, &file, &r, 1) => 1;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
assert(r == c);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
char path[1024];
struct lfs_info info;
struct lfs2_info info;
sprintf(path, "test%d", i);
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
run_cycles[run] = cycle;
LFS_WARN("completed %d blocks %d cycles",
LFS2_WARN("completed %d blocks %d cycles",
run_block_count[run], run_cycles[run]);
}
// check we increased the lifetime by 2x with ~10% error
LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
'''
# test that we wear blocks roughly evenly
@@ -391,42 +391,42 @@ defines.CYCLES = 100
defines.FILES = 10
if = 'BLOCK_CYCLES < CYCLES/10'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "roadrunner") => 0;
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "roadrunner") => 0;
lfs2_unmount(&lfs2) => 0;
uint32_t cycle = 0;
while (cycle < CYCLES) {
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// chose name, roughly random seed, and random 2^n size
char path[1024];
sprintf(path, "roadrunner/test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << 4; //((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << 4; //((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
for (lfs_size_t j = 0; j < size; j++) {
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
assert(res == 1 || res == LFS_ERR_NOSPC);
if (res == LFS_ERR_NOSPC) {
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
lfs_unmount(&lfs) => 0;
lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1);
assert(res == 1 || res == LFS2_ERR_NOSPC);
if (res == LFS2_ERR_NOSPC) {
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
int err = lfs_file_close(&lfs, &file);
assert(err == 0 || err == LFS_ERR_NOSPC);
if (err == LFS_ERR_NOSPC) {
lfs_unmount(&lfs) => 0;
int err = lfs2_file_close(&lfs2, &file);
assert(err == 0 || err == LFS2_ERR_NOSPC);
if (err == LFS2_ERR_NOSPC) {
lfs2_unmount(&lfs2) => 0;
goto exhausted;
}
}
@@ -436,45 +436,45 @@ code = '''
char path[1024];
sprintf(path, "roadrunner/test%d", i);
uint32_t prng = cycle * i;
lfs_size_t size = 1 << 4; //((TEST_PRNG(&prng) % 10)+2);
lfs2_size_t size = 1 << 4; //((TEST_PRNG(&prng) % 10)+2);
lfs_file_t file;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < size; j++) {
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
char c = 'a' + (TEST_PRNG(&prng) % 26);
char r;
lfs_file_read(&lfs, &file, &r, 1) => 1;
lfs2_file_read(&lfs2, &file, &r, 1) => 1;
assert(r == c);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
cycle += 1;
}
exhausted:
// should still be readable
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (uint32_t i = 0; i < FILES; i++) {
// check for errors
char path[1024];
struct lfs_info info;
struct lfs2_info info;
sprintf(path, "roadrunner/test%d", i);
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
LFS_WARN("completed %d cycles", cycle);
LFS2_WARN("completed %d cycles", cycle);
// check the wear on our block device
lfs_emubd_wear_t minwear = -1;
lfs_emubd_wear_t totalwear = 0;
lfs_emubd_wear_t maxwear = 0;
lfs2_emubd_wear_t minwear = -1;
lfs2_emubd_wear_t totalwear = 0;
lfs2_emubd_wear_t maxwear = 0;
// skip 0 and 1 as superblock movement is intentionally avoided
for (lfs_block_t b = 2; b < BLOCK_COUNT; b++) {
lfs_emubd_wear_t wear = lfs_emubd_wear(cfg, b);
for (lfs2_block_t b = 2; b < BLOCK_COUNT; b++) {
lfs2_emubd_wear_t wear = lfs2_emubd_wear(cfg, b);
printf("%08x: wear %d\n", b, wear);
assert(wear >= 0);
if (wear < minwear) {
@@ -485,21 +485,21 @@ exhausted:
}
totalwear += wear;
}
lfs_emubd_wear_t avgwear = totalwear / BLOCK_COUNT;
LFS_WARN("max wear: %d cycles", maxwear);
LFS_WARN("avg wear: %d cycles", totalwear / (int)BLOCK_COUNT);
LFS_WARN("min wear: %d cycles", minwear);
lfs2_emubd_wear_t avgwear = totalwear / BLOCK_COUNT;
LFS2_WARN("max wear: %d cycles", maxwear);
LFS2_WARN("avg wear: %d cycles", totalwear / (int)BLOCK_COUNT);
LFS2_WARN("min wear: %d cycles", minwear);
// find standard deviation^2
lfs_emubd_wear_t dev2 = 0;
for (lfs_block_t b = 2; b < BLOCK_COUNT; b++) {
lfs_emubd_wear_t wear = lfs_emubd_wear(cfg, b);
lfs2_emubd_wear_t dev2 = 0;
for (lfs2_block_t b = 2; b < BLOCK_COUNT; b++) {
lfs2_emubd_wear_t wear = lfs2_emubd_wear(cfg, b);
assert(wear >= 0);
lfs_emubd_swear_t diff = wear - avgwear;
lfs2_emubd_swear_t diff = wear - avgwear;
dev2 += diff*diff;
}
dev2 /= totalwear;
LFS_WARN("std dev^2: %d", dev2);
LFS2_WARN("std dev^2: %d", dev2);
assert(dev2 < 8);
'''

View File

@@ -1,516 +1,539 @@
[cases.test_files_simple]
defines.INLINE_MAX = [0, -1, 8]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_size_t size = strlen("Hello World!")+1;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "hello",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_size_t size = strlen("Hello World!")+1;
uint8_t buffer[1024];
strcpy((char*)buffer, "Hello World!");
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
assert(strcmp((char*)buffer, "Hello World!") == 0);
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// write
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint32_t prng = 1;
uint8_t buffer[1024];
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// read
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
prng = 1;
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_files_rewrite]
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// write
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
uint8_t buffer[1024];
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint32_t prng = 1;
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// read
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1;
prng = 1;
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// rewrite
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY) => 0;
prng = 2;
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// read
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => lfs_max(SIZE1, SIZE2);
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => lfs2_max(SIZE1, SIZE2);
prng = 2;
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
if (SIZE1 > SIZE2) {
prng = 1;
for (lfs_size_t b = 0; b < SIZE2; b++) {
for (lfs2_size_t b = 0; b < SIZE2; b++) {
TEST_PRNG(&prng);
}
for (lfs_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_files_append]
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// write
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
uint8_t buffer[1024];
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint32_t prng = 1;
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// read
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1;
prng = 1;
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// append
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_APPEND) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_APPEND) => 0;
prng = 2;
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// read
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1 + SIZE2;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1 + SIZE2;
prng = 1;
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
prng = 2;
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_files_truncate]
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// write
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
uint8_t buffer[1024];
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
uint32_t prng = 1;
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// read
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE1;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE1;
prng = 1;
for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// truncate
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
prng = 2;
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// read
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE2;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE2;
prng = 2;
for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[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 = [
'LFS2_EMUBD_POWERLOSS_NOOP',
'LFS2_EMUBD_POWERLOSS_OOO',
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
lfs_file_t file;
lfs2_file_t file;
uint8_t buffer[1024];
err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY);
assert(err == LFS_ERR_NOENT || err == 0);
err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY);
assert(err == LFS2_ERR_NOENT || err == 0);
if (err == 0) {
// can only be 0 (new file) or full size
lfs_size_t size = lfs_file_size(&lfs, &file);
lfs2_size_t size = lfs2_file_size(&lfs2, &file);
assert(size == 0 || size == SIZE);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
// write
lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
uint32_t prng = 1;
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
prng = 1;
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_files_reentrant_write_sync]
defines = [
# append (O(n))
{MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]},
{MODE='LFS2_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='LFS2_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 = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
lfs_file_t file;
lfs2_file_t file;
uint8_t buffer[1024];
err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY);
assert(err == LFS_ERR_NOENT || err == 0);
err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY);
assert(err == LFS2_ERR_NOENT || err == 0);
if (err == 0) {
// with syncs we could be any size, but it at least must be valid data
lfs_size_t size = lfs_file_size(&lfs, &file);
lfs2_size_t size = lfs2_file_size(&lfs2, &file);
assert(size <= SIZE);
uint32_t prng = 1;
for (lfs_size_t i = 0; i < size; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, size-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < size; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, size-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
// write
lfs_file_open(&lfs, &file, "avacado",
LFS_O_WRONLY | LFS_O_CREAT | MODE) => 0;
lfs_size_t size = lfs_file_size(&lfs, &file);
lfs2_file_open(&lfs2, &file, "avacado",
LFS2_O_WRONLY | LFS2_O_CREAT | MODE) => 0;
lfs2_size_t size = lfs2_file_size(&lfs2, &file);
assert(size <= SIZE);
uint32_t prng = 1;
lfs_size_t skip = (MODE == LFS_O_APPEND) ? size : 0;
for (lfs_size_t b = 0; b < skip; b++) {
lfs2_size_t skip = (MODE == LFS2_O_APPEND) ? size : 0;
for (lfs2_size_t b = 0; b < skip; b++) {
TEST_PRNG(&prng);
}
for (lfs_size_t i = skip; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = skip; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
for (lfs2_size_t b = 0; b < chunk; b++) {
buffer[b] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
lfs_file_sync(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk;
lfs2_file_sync(&lfs2, &file) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// read
lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
prng = 1;
for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk; b++) {
for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i);
lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk; b++) {
assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
}
}
lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_files_many]
defines.N = 300
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// create N files of 7 bytes
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 0; i < N; i++) {
lfs_file_t file;
lfs2_file_t file;
char path[1024];
sprintf(path, "file_%03d", i);
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
char wbuffer[1024];
lfs_size_t size = 7;
lfs2_size_t size = 7;
sprintf(wbuffer, "Hi %03d", i);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
char rbuffer[1024];
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(strcmp(rbuffer, wbuffer) == 0);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_files_many_power_cycle]
defines.N = 300
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// create N files of 7 bytes
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 0; i < N; i++) {
lfs_file_t file;
lfs2_file_t file;
char path[1024];
sprintf(path, "file_%03d", i);
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
char wbuffer[1024];
lfs_size_t size = 7;
lfs2_size_t size = 7;
sprintf(wbuffer, "Hi %03d", i);
lfs_file_write(&lfs, &file, wbuffer, size) => size;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
char rbuffer[1024];
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(strcmp(rbuffer, wbuffer) == 0);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_files_many_power_loss]
defines.N = 300
reentrant = true
defines.POWERLOSS_BEHAVIOR = [
'LFS2_EMUBD_POWERLOSS_NOOP',
'LFS2_EMUBD_POWERLOSS_OOO',
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
// create N files of 7 bytes
for (int i = 0; i < N; i++) {
lfs_file_t file;
lfs2_file_t file;
char path[1024];
sprintf(path, "file_%03d", i);
err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT);
err = lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT);
char wbuffer[1024];
lfs_size_t size = 7;
lfs2_size_t size = 7;
sprintf(wbuffer, "Hi %03d", i);
if ((lfs_size_t)lfs_file_size(&lfs, &file) != size) {
lfs_file_write(&lfs, &file, wbuffer, size) => size;
if ((lfs2_size_t)lfs2_file_size(&lfs2, &file) != size) {
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
char rbuffer[1024];
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(strcmp(rbuffer, wbuffer) == 0);
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -3,268 +3,272 @@
defines.SIZE = [10, 100]
defines.FILES = [4, 10, 26]
code = '''
lfs_t lfs;
lfs_file_t files[FILES];
lfs2_t lfs2;
lfs2_file_t files[FILES];
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int j = 0; j < FILES; j++) {
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_file_open(&lfs, &files[j], path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_open(&lfs2, &files[j], path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
}
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < FILES; j++) {
lfs_file_write(&lfs, &files[j], &alphas[j], 1) => 1;
lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1;
}
}
for (int j = 0; j < FILES; j++) {
lfs_file_close(&lfs, &files[j]);
lfs2_file_close(&lfs2, &files[j]);
}
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "/") => 0;
struct lfs2_info info;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
for (int j = 0; j < FILES; j++) {
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, path) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
assert(info.size == SIZE);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
for (int j = 0; j < FILES; j++) {
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0;
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < FILES; j++) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &files[j], buffer, 1) => 1;
lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1;
assert(buffer[0] == alphas[j]);
}
}
for (int j = 0; j < FILES; j++) {
lfs_file_close(&lfs, &files[j]);
lfs2_file_close(&lfs2, &files[j]);
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_interspersed_remove_files]
defines.SIZE = [10, 100]
defines.FILES = [4, 10, 26]
code = '''
lfs_t lfs;
lfs2_t lfs2;
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int j = 0; j < FILES; j++) {
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
for (int i = 0; i < SIZE; i++) {
lfs_file_write(&lfs, &file, &alphas[j], 1) => 1;
lfs2_file_write(&lfs2, &file, &alphas[j], 1) => 1;
}
lfs_file_close(&lfs, &file);
lfs2_file_close(&lfs2, &file);
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "zzz", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (int j = 0; j < FILES; j++) {
lfs_file_write(&lfs, &file, (const void*)"~", 1) => 1;
lfs_file_sync(&lfs, &file) => 0;
lfs2_file_write(&lfs2, &file, (const void*)"~", 1) => 1;
lfs2_file_sync(&lfs2, &file) => 0;
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_remove(&lfs, path) => 0;
lfs2_remove(&lfs2, path) => 0;
}
lfs_file_close(&lfs, &file);
lfs2_file_close(&lfs2, &file);
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "/") => 0;
struct lfs2_info info;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, "zzz") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
assert(info.size == FILES);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_file_open(&lfs, &file, "zzz", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_RDONLY) => 0;
for (int i = 0; i < FILES; i++) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &file, buffer, 1) => 1;
lfs2_file_read(&lfs2, &file, buffer, 1) => 1;
assert(buffer[0] == '~');
}
lfs_file_close(&lfs, &file);
lfs2_file_close(&lfs2, &file);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_interspersed_remove_inconveniently]
defines.SIZE = [10, 100]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t files[3];
lfs_file_open(&lfs, &files[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &files[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &files[2], "g", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t files[3];
lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &files[1], "f", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &files[2], "g", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (int i = 0; i < SIZE/2; i++) {
lfs_file_write(&lfs, &files[0], (const void*)"e", 1) => 1;
lfs_file_write(&lfs, &files[1], (const void*)"f", 1) => 1;
lfs_file_write(&lfs, &files[2], (const void*)"g", 1) => 1;
lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1;
lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1;
lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1;
}
lfs_remove(&lfs, "f") => 0;
lfs2_remove(&lfs2, "f") => 0;
for (int i = 0; i < SIZE/2; i++) {
lfs_file_write(&lfs, &files[0], (const void*)"e", 1) => 1;
lfs_file_write(&lfs, &files[1], (const void*)"f", 1) => 1;
lfs_file_write(&lfs, &files[2], (const void*)"g", 1) => 1;
lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1;
lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1;
lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1;
}
lfs_file_close(&lfs, &files[0]);
lfs_file_close(&lfs, &files[1]);
lfs_file_close(&lfs, &files[2]);
lfs2_file_close(&lfs2, &files[0]);
lfs2_file_close(&lfs2, &files[1]);
lfs2_file_close(&lfs2, &files[2]);
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "/") => 0;
struct lfs2_info info;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, "e") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
assert(info.size == SIZE);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, "g") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
assert(info.size == SIZE);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs_file_open(&lfs, &files[0], "e", LFS_O_RDONLY) => 0;
lfs_file_open(&lfs, &files[1], "g", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &files[1], "g", LFS2_O_RDONLY) => 0;
for (int i = 0; i < SIZE; i++) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &files[0], buffer, 1) => 1;
lfs2_file_read(&lfs2, &files[0], buffer, 1) => 1;
assert(buffer[0] == 'e');
lfs_file_read(&lfs, &files[1], buffer, 1) => 1;
lfs2_file_read(&lfs2, &files[1], buffer, 1) => 1;
assert(buffer[0] == 'g');
}
lfs_file_close(&lfs, &files[0]);
lfs_file_close(&lfs, &files[1]);
lfs2_file_close(&lfs2, &files[0]);
lfs2_file_close(&lfs2, &files[1]);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_interspersed_reentrant_files]
defines.SIZE = [10, 100]
defines.FILES = [4, 10, 26]
reentrant = true
defines.POWERLOSS_BEHAVIOR = [
'LFS2_EMUBD_POWERLOSS_NOOP',
'LFS2_EMUBD_POWERLOSS_OOO',
]
code = '''
lfs_t lfs;
lfs_file_t files[FILES];
lfs2_t lfs2;
lfs2_file_t files[FILES];
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
int err = lfs_mount(&lfs, cfg);
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
for (int j = 0; j < FILES; j++) {
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_file_open(&lfs, &files[j], path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
lfs2_file_open(&lfs2, &files[j], path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
}
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < FILES; j++) {
lfs_ssize_t size = lfs_file_size(&lfs, &files[j]);
lfs2_ssize_t size = lfs2_file_size(&lfs2, &files[j]);
assert(size >= 0);
if ((int)size <= i) {
lfs_file_write(&lfs, &files[j], &alphas[j], 1) => 1;
lfs_file_sync(&lfs, &files[j]) => 0;
lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1;
lfs2_file_sync(&lfs2, &files[j]) => 0;
}
}
}
for (int j = 0; j < FILES; j++) {
lfs_file_close(&lfs, &files[j]);
lfs2_file_close(&lfs2, &files[j]);
}
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "/") => 0;
struct lfs2_info info;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS2_TYPE_DIR);
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
for (int j = 0; j < FILES; j++) {
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
assert(strcmp(info.name, path) == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
assert(info.size == SIZE);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
for (int j = 0; j < FILES; j++) {
char path[1024];
sprintf(path, "%c", alphas[j]);
lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0;
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < FILES; j++) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &files[j], buffer, 1) => 1;
lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1;
assert(buffer[0] == alphas[j]);
}
}
for (int j = 0; j < FILES; j++) {
lfs_file_close(&lfs, &files[j]);
lfs2_file_close(&lfs2, &files[j]);
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +1,25 @@
[cases.test_orphans_normal]
in = "lfs.c"
in = "lfs2.c"
if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_mkdir(&lfs, "parent") => 0;
lfs_mkdir(&lfs, "parent/orphan") => 0;
lfs_mkdir(&lfs, "parent/child") => 0;
lfs_remove(&lfs, "parent/orphan") => 0;
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "parent") => 0;
lfs2_mkdir(&lfs2, "parent/orphan") => 0;
lfs2_mkdir(&lfs2, "parent/child") => 0;
lfs2_remove(&lfs2, "parent/orphan") => 0;
lfs2_unmount(&lfs2) => 0;
// corrupt the child's most recent commit, this should be the update
// to the linked-list entry, which should orphan the orphan. Note this
// makes a lot of assumptions about the remove operation.
lfs_mount(&lfs, cfg) => 0;
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "parent/child") => 0;
lfs_block_t block = dir.m.pair[0];
lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "parent/child") => 0;
lfs2_block_t block = dir.m.pair[0];
lfs2_dir_close(&lfs2, &dir) => 0;
lfs2_unmount(&lfs2) => 0;
uint8_t buffer[BLOCK_SIZE];
cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
int off = BLOCK_SIZE-1;
@@ -31,194 +31,195 @@ code = '''
cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
cfg->sync(cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
struct lfs_info info;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_fs_size(&lfs) => 8;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_fs_size(&lfs) => 8;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
// this mkdir should both create a dir and deorphan, so size
// should be unchanged
lfs_mkdir(&lfs, "parent/otherchild") => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_stat(&lfs, "parent/otherchild", &info) => 0;
lfs_fs_size(&lfs) => 8;
lfs_unmount(&lfs) => 0;
lfs2_mkdir(&lfs2, "parent/otherchild") => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_stat(&lfs2, "parent/otherchild", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_stat(&lfs, "parent/child", &info) => 0;
lfs_stat(&lfs, "parent/otherchild", &info) => 0;
lfs_fs_size(&lfs) => 8;
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_stat(&lfs2, "parent/child", &info) => 0;
lfs2_stat(&lfs2, "parent/otherchild", &info) => 0;
lfs2_fs_size(&lfs2) => 8;
lfs2_unmount(&lfs2) => 0;
'''
# test that we only run deorphan once per power-cycle
[cases.test_orphans_no_orphans]
in = 'lfs.c'
in = 'lfs2.c'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, 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;
lfs2_fs_preporphans(&lfs2, +1) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_commit(&lfs2, &mdir, NULL, 0) => 0;
// we should have orphans at this state
assert(lfs_gstate_hasorphans(&lfs.gstate));
lfs_unmount(&lfs) => 0;
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
lfs2_unmount(&lfs2) => 0;
// mount
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// we should detect orphans
assert(lfs_gstate_hasorphans(&lfs.gstate));
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
// force consistency
lfs_fs_forceconsistency(&lfs) => 0;
lfs2_fs_forceconsistency(&lfs2) => 0;
// we should no longer have orphans
assert(!lfs_gstate_hasorphans(&lfs.gstate));
assert(!lfs2_gstate_hasorphans(&lfs2.gstate));
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_orphans_one_orphan]
in = 'lfs.c'
in = 'lfs2.c'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, 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;
lfs2_mdir_t orphan;
lfs2_alloc_ckpoint(&lfs2);
lfs2_dir_alloc(&lfs2, &orphan) => 0;
lfs2_dir_commit(&lfs2, &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;
lfs2_fs_preporphans(&lfs2, +1) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_pair_tole32(orphan.pair);
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 8), orphan.pair})) => 0;
// we should have orphans at this state
assert(lfs_gstate_hasorphans(&lfs.gstate));
lfs_unmount(&lfs) => 0;
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
lfs2_unmount(&lfs2) => 0;
// mount
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// we should detect orphans
assert(lfs_gstate_hasorphans(&lfs.gstate));
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
// force consistency
lfs_fs_forceconsistency(&lfs) => 0;
lfs2_fs_forceconsistency(&lfs2) => 0;
// we should no longer have orphans
assert(!lfs_gstate_hasorphans(&lfs.gstate));
assert(!lfs2_gstate_hasorphans(&lfs2.gstate));
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# test that we can persist gstate with lfs_fs_mkconsistent
# test that we can persist gstate with lfs2_fs_mkconsistent
[cases.test_orphans_mkconsistent_no_orphans]
in = 'lfs.c'
in = 'lfs2.c'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, 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;
lfs2_fs_preporphans(&lfs2, +1) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_dir_commit(&lfs2, &mdir, NULL, 0) => 0;
// we should have orphans at this state
assert(lfs_gstate_hasorphans(&lfs.gstate));
lfs_unmount(&lfs) => 0;
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
lfs2_unmount(&lfs2) => 0;
// mount
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// we should detect orphans
assert(lfs_gstate_hasorphans(&lfs.gstate));
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
// force consistency
lfs_fs_mkconsistent(&lfs) => 0;
lfs2_fs_mkconsistent(&lfs2) => 0;
// we should no longer have orphans
assert(!lfs_gstate_hasorphans(&lfs.gstate));
assert(!lfs2_gstate_hasorphans(&lfs2.gstate));
// remount
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// we should still have no orphans
assert(!lfs_gstate_hasorphans(&lfs.gstate));
lfs_unmount(&lfs) => 0;
assert(!lfs2_gstate_hasorphans(&lfs2.gstate));
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_orphans_mkconsistent_one_orphan]
in = 'lfs.c'
in = 'lfs2.c'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, 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;
lfs2_mdir_t orphan;
lfs2_alloc_ckpoint(&lfs2);
lfs2_dir_alloc(&lfs2, &orphan) => 0;
lfs2_dir_commit(&lfs2, &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;
lfs2_fs_preporphans(&lfs2, +1) => 0;
lfs2_mdir_t mdir;
lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0;
lfs2_pair_tole32(orphan.pair);
lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 8), orphan.pair})) => 0;
// we should have orphans at this state
assert(lfs_gstate_hasorphans(&lfs.gstate));
lfs_unmount(&lfs) => 0;
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
lfs2_unmount(&lfs2) => 0;
// mount
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// we should detect orphans
assert(lfs_gstate_hasorphans(&lfs.gstate));
assert(lfs2_gstate_hasorphans(&lfs2.gstate));
// force consistency
lfs_fs_mkconsistent(&lfs) => 0;
lfs2_fs_mkconsistent(&lfs2) => 0;
// we should no longer have orphans
assert(!lfs_gstate_hasorphans(&lfs.gstate));
assert(!lfs2_gstate_hasorphans(&lfs2.gstate));
// remount
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// we should still have no orphans
assert(!lfs_gstate_hasorphans(&lfs.gstate));
lfs_unmount(&lfs) => 0;
assert(!lfs2_gstate_hasorphans(&lfs2.gstate));
lfs2_unmount(&lfs2) => 0;
'''
# reentrant testing for orphans, basically just spam mkdir/remove
[cases.test_orphans_reentrant]
reentrant = true
# TODO fix this case, caused by non-DAG trees
if = '!(DEPTH == 3 && CACHE_SIZE != 64)'
# NOTE the second condition is required
if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT'
defines = [
{FILES=6, DEPTH=1, CYCLES=20},
{FILES=26, DEPTH=1, CYCLES=20},
{FILES=3, DEPTH=3, CYCLES=20},
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
uint32_t prng = 1;
@@ -231,43 +232,109 @@ code = '''
}
// if it does not exist, we create it, else we destroy
struct lfs_info info;
int res = lfs_stat(&lfs, full_path, &info);
if (res == LFS_ERR_NOENT) {
struct lfs2_info info;
int res = lfs2_stat(&lfs2, full_path, &info);
if (res == LFS2_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs_mkdir(&lfs, path);
assert(!err || err == LFS_ERR_EXIST);
err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
}
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
}
} else {
// is valid dir?
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
// try to delete path in reverse order, ignore if dir is not empty
for (int d = DEPTH-1; d >= 0; d--) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs_remove(&lfs, path);
assert(!err || err == LFS_ERR_NOTEMPTY);
err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
}
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
}
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# non-reentrant testing for orphans, this is the same as reentrant
# testing, but we test way more states than we could under powerloss
[cases.test_orphans_nonreentrant]
# TODO fix this case, caused by non-DAG trees
# NOTE the second condition is required
if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT'
defines = [
{FILES=6, DEPTH=1, CYCLES=2000},
{FILES=26, DEPTH=1, CYCLES=2000},
{FILES=3, DEPTH=3, CYCLES=2000},
]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
uint32_t prng = 1;
const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
for (unsigned i = 0; i < CYCLES; i++) {
// create random path
char full_path[256];
for (unsigned d = 0; d < DEPTH; d++) {
sprintf(&full_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]);
}
// if it does not exist, we create it, else we destroy
struct lfs2_info info;
int res = lfs2_stat(&lfs2, full_path, &info);
if (res == LFS2_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
int err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
}
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
}
} else {
// is valid dir?
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
// try to delete path in reverse order, ignore if dir is not empty
for (int d = DEPTH-1; d >= 0; d--) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
int err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
}
}
lfs2_unmount(&lfs2) => 0;
'''

File diff suppressed because it is too large Load Diff

View File

@@ -5,87 +5,87 @@
# only a revision count
[cases.test_powerloss_only_rev]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, 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;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "notebook") => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "notebook/paper",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
char buffer[256];
strcpy(buffer, "hello");
lfs_size_t size = strlen("hello");
lfs2_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;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_sync(&lfs2, &file) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
char rbuffer[256];
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper", LFS2_O_RDONLY) => 0;
for (int i = 0; i < 5; i++) {
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 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]};
lfs2_mount(&lfs2, cfg) => 0;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "notebook") => 0;
lfs2_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;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs2_unmount(&lfs2) => 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));
memcpy(bbuffer, &(uint32_t){lfs2_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;
lfs2_mount(&lfs2, cfg) => 0;
// can read?
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper", LFS2_O_RDONLY) => 0;
for (int i = 0; i < 5; i++) {
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// can write?
lfs_file_open(&lfs, &file, "notebook/paper",
LFS_O_WRONLY | LFS_O_APPEND) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper",
LFS2_O_WRONLY | LFS2_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;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_sync(&lfs2, &file) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper", LFS2_O_RDONLY) => 0;
strcpy(buffer, "hello");
size = strlen("hello");
for (int i = 0; i < 5; i++) {
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &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;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# partial prog, may not be byte in order!
@@ -96,45 +96,45 @@ if = '''
'''
defines.BYTE_OFF = ["0", "PROG_SIZE-1", "PROG_SIZE/2"]
defines.BYTE_VALUE = [0x33, 0xcc]
in = "lfs.c"
in = "lfs2.c"
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, 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;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_mkdir(&lfs2, "notebook") => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "notebook/paper",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
char buffer[256];
strcpy(buffer, "hello");
lfs_size_t size = strlen("hello");
lfs2_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;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_sync(&lfs2, &file) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
char rbuffer[256];
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper", LFS2_O_RDONLY) => 0;
for (int i = 0; i < 5; i++) {
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 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;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_dir_t dir;
lfs2_dir_open(&lfs2, &dir, "notebook") => 0;
lfs2_block_t block = dir.m.pair[0];
lfs2_off_t off = dir.m.off;
lfs2_dir_close(&lfs2, &dir) => 0;
lfs2_unmount(&lfs2) => 0;
// tweak byte
uint8_t bbuffer[BLOCK_SIZE];
@@ -145,41 +145,41 @@ code = '''
cfg->erase(cfg, block) => 0;
cfg->prog(cfg, block, 0, bbuffer, BLOCK_SIZE) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// can read?
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper", LFS2_O_RDONLY) => 0;
for (int i = 0; i < 5; i++) {
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// can write?
lfs_file_open(&lfs, &file, "notebook/paper",
LFS_O_WRONLY | LFS_O_APPEND) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper",
LFS2_O_WRONLY | LFS2_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;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_sync(&lfs2, &file) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "notebook/paper", LFS2_O_RDONLY) => 0;
strcpy(buffer, "hello");
size = strlen("hello");
for (int i = 0; i < 5; i++) {
lfs_file_read(&lfs, &file, rbuffer, size) => size;
lfs2_file_read(&lfs2, &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;
lfs2_file_read(&lfs2, &file, rbuffer, size) => size;
assert(memcmp(rbuffer, buffer, size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -4,44 +4,44 @@ defines.ITERATIONS = 20
defines.COUNT = 10
defines.BLOCK_CYCLES = [8, 1]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// fill up filesystem so only ~16 blocks are left
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
uint8_t buffer[512];
memset(buffer, 0, 512);
while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
lfs_file_write(&lfs, &file, buffer, 512) => 512;
while (BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) {
lfs2_file_write(&lfs2, &file, buffer, 512) => 512;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// make a child dir to use in bounded space
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (unsigned j = 0; j < ITERATIONS; j++) {
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_dir_t dir;
struct lfs_info info;
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_t dir;
struct lfs2_info info;
lfs2_dir_open(&lfs2, &dir, "child") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
strcmp(info.name, path) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
if (j == (unsigned)ITERATIONS-1) {
break;
@@ -50,31 +50,31 @@ code = '''
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_remove(&lfs, path) => 0;
lfs2_remove(&lfs2, path) => 0;
}
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_dir_t dir;
struct lfs_info info;
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_dir_t dir;
struct lfs2_info info;
lfs2_dir_open(&lfs2, &dir, "child") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
strcmp(info.name, path) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_remove(&lfs, path) => 0;
lfs2_remove(&lfs2, path) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_relocations_outdated_head]
@@ -82,87 +82,87 @@ defines.ITERATIONS = 20
defines.COUNT = 10
defines.BLOCK_CYCLES = [8, 1]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// fill up filesystem so only ~16 blocks are left
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
uint8_t buffer[512];
memset(buffer, 0, 512);
while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
lfs_file_write(&lfs, &file, buffer, 512) => 512;
while (BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) {
lfs2_file_write(&lfs2, &file, buffer, 512) => 512;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
// make a child dir to use in bounded space
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
lfs2_mkdir(&lfs2, "child") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (unsigned j = 0; j < ITERATIONS; j++) {
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_dir_t dir;
struct lfs_info info;
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_t dir;
struct lfs2_info info;
lfs2_dir_open(&lfs2, &dir, "child") => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 0;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hi", 2) => 2;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_rewind(&lfs2, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hi", 2) => 2;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_rewind(&lfs2, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs2_dir_read(&lfs2, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs2_dir_read(&lfs2, &dir, &info) => 0;
lfs2_dir_close(&lfs2, &dir) => 0;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_remove(&lfs, path) => 0;
lfs2_remove(&lfs2, path) => 0;
}
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# reentrant testing for relocations, this is the same as the
@@ -179,11 +179,11 @@ defines = [
{FILES=3, DEPTH=3, CYCLES=20, BLOCK_CYCLES=1},
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
uint32_t prng = 1;
@@ -196,44 +196,44 @@ code = '''
}
// if it does not exist, we create it, else we destroy
struct lfs_info info;
int res = lfs_stat(&lfs, full_path, &info);
if (res == LFS_ERR_NOENT) {
struct lfs2_info info;
int res = lfs2_stat(&lfs2, full_path, &info);
if (res == LFS2_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs_mkdir(&lfs, path);
assert(!err || err == LFS_ERR_EXIST);
err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
}
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
}
} else {
// is valid dir?
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
// try to delete path in reverse order, ignore if dir is not empty
for (unsigned d = DEPTH-1; d+1 > 0; d--) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs_remove(&lfs, path);
assert(!err || err == LFS_ERR_NOTEMPTY);
err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
}
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
}
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# reentrant testing for relocations, but now with random renames!
@@ -248,11 +248,11 @@ defines = [
{FILES=3, DEPTH=3, CYCLES=20, BLOCK_CYCLES=1},
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
uint32_t prng = 1;
@@ -265,30 +265,30 @@ code = '''
}
// if it does not exist, we create it, else we destroy
struct lfs_info info;
int res = lfs_stat(&lfs, full_path, &info);
assert(!res || res == LFS_ERR_NOENT);
if (res == LFS_ERR_NOENT) {
struct lfs2_info info;
int res = lfs2_stat(&lfs2, full_path, &info);
assert(!res || res == LFS2_ERR_NOENT);
if (res == LFS2_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs_mkdir(&lfs, path);
assert(!err || err == LFS_ERR_EXIST);
err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
}
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
}
} else {
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
// create new random path
char new_path[256];
@@ -297,9 +297,9 @@ code = '''
}
// if new path does not exist, rename, otherwise destroy
res = lfs_stat(&lfs, new_path, &info);
assert(!res || res == LFS_ERR_NOENT);
if (res == LFS_ERR_NOENT) {
res = lfs2_stat(&lfs2, new_path, &info);
assert(!res || res == LFS2_ERR_NOENT);
if (res == LFS2_ERR_NOENT) {
// stop once some dir is renamed
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
@@ -307,8 +307,8 @@ code = '''
path[2*d+2] = '\0';
strcpy(&path[128+2*d], &new_path[2*d]);
path[128+2*d+2] = '\0';
err = lfs_rename(&lfs, path, path+128);
assert(!err || err == LFS_ERR_NOTEMPTY);
err = lfs2_rename(&lfs2, path, path+128);
assert(!err || err == LFS2_ERR_NOTEMPTY);
if (!err) {
strcpy(path, path+128);
}
@@ -318,12 +318,12 @@ code = '''
char path[1024];
strcpy(path, new_path);
path[2*d+2] = '\0';
lfs_stat(&lfs, path, &info) => 0;
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.type == LFS2_TYPE_DIR);
}
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
} else {
// try to delete path in reverse order,
// ignore if dir is not empty
@@ -331,13 +331,181 @@ code = '''
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
err = lfs_remove(&lfs, path);
assert(!err || err == LFS_ERR_NOTEMPTY);
err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
}
lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
}
}
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# non-reentrant testing for orphans, this is the same as reentrant
# testing, but we test way more states than we could under powerloss
[cases.test_relocations_nonreentrant]
# TODO fix this case, caused by non-DAG trees
# NOTE the second condition is required
if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT'
defines = [
{FILES=6, DEPTH=1, CYCLES=2000, BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=2000, BLOCK_CYCLES=1},
{FILES=3, DEPTH=3, CYCLES=2000, BLOCK_CYCLES=1},
]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
uint32_t prng = 1;
const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
for (unsigned i = 0; i < CYCLES; i++) {
// create random path
char full_path[256];
for (unsigned d = 0; d < DEPTH; d++) {
sprintf(&full_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]);
}
// if it does not exist, we create it, else we destroy
struct lfs2_info info;
int res = lfs2_stat(&lfs2, full_path, &info);
if (res == LFS2_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
int err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
}
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
}
} else {
// is valid dir?
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
// try to delete path in reverse order, ignore if dir is not empty
for (unsigned d = DEPTH-1; d+1 > 0; d--) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
int err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
}
}
lfs2_unmount(&lfs2) => 0;
'''
# non-reentrant testing for relocations, but now with random renames!
[cases.test_relocations_nonreentrant_renames]
# TODO fix this case, caused by non-DAG trees
# NOTE the second condition is required
if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT'
defines = [
{FILES=6, DEPTH=1, CYCLES=2000, BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=2000, BLOCK_CYCLES=1},
{FILES=3, DEPTH=3, CYCLES=2000, BLOCK_CYCLES=1},
]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
uint32_t prng = 1;
const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
for (unsigned i = 0; i < CYCLES; i++) {
// create random path
char full_path[256];
for (unsigned d = 0; d < DEPTH; d++) {
sprintf(&full_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]);
}
// if it does not exist, we create it, else we destroy
struct lfs2_info info;
int res = lfs2_stat(&lfs2, full_path, &info);
assert(!res || res == LFS2_ERR_NOENT);
if (res == LFS2_ERR_NOENT) {
// create each directory in turn, ignore if dir already exists
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
int err = lfs2_mkdir(&lfs2, path);
assert(!err || err == LFS2_ERR_EXIST);
}
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
}
} else {
assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
// create new random path
char new_path[256];
for (unsigned d = 0; d < DEPTH; d++) {
sprintf(&new_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]);
}
// if new path does not exist, rename, otherwise destroy
res = lfs2_stat(&lfs2, new_path, &info);
assert(!res || res == LFS2_ERR_NOENT);
if (res == LFS2_ERR_NOENT) {
// stop once some dir is renamed
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(&path[2*d], &full_path[2*d]);
path[2*d+2] = '\0';
strcpy(&path[128+2*d], &new_path[2*d]);
path[128+2*d+2] = '\0';
int err = lfs2_rename(&lfs2, path, path+128);
assert(!err || err == LFS2_ERR_NOTEMPTY);
if (!err) {
strcpy(path, path+128);
}
}
for (unsigned d = 0; d < DEPTH; d++) {
char path[1024];
strcpy(path, new_path);
path[2*d+2] = '\0';
lfs2_stat(&lfs2, path, &info) => 0;
assert(strcmp(info.name, &path[2*d+1]) == 0);
assert(info.type == LFS2_TYPE_DIR);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
} else {
// try to delete path in reverse order,
// ignore if dir is not empty
for (unsigned d = DEPTH-1; d+1 > 0; d--) {
char path[1024];
strcpy(path, full_path);
path[2*d+2] = '\0';
int err = lfs2_remove(&lfs2, path);
assert(!err || err == LFS2_ERR_NOTEMPTY);
}
lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT;
}
}
}
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -10,66 +10,66 @@ defines = [
{COUNT=4, SKIP=2},
]
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
size_t size = strlen("kittycatcat");
uint8_t buffer[1024];
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs_file_write(&lfs, &file, buffer, size);
lfs2_file_write(&lfs2, &file, buffer, size);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY) => 0;
lfs_soff_t pos = -1;
lfs2_soff_t pos = -1;
size = strlen("kittycatcat");
for (int i = 0; i < SKIP; i++) {
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
pos = lfs_file_tell(&lfs, &file);
pos = lfs2_file_tell(&lfs2, &file);
}
assert(pos >= 0);
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_rewind(&lfs, &file) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_rewind(&lfs2, &file) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, size, LFS_SEEK_CUR) => 3*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, size, LFS2_SEEK_CUR) => 3*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_CUR) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_CUR) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs_file_size(&lfs, &file);
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
size = lfs2_file_size(&lfs2, &file);
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# simple file seek and write
@@ -83,115 +83,262 @@ defines = [
{COUNT=4, SKIP=2},
]
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
size_t size = strlen("kittycatcat");
uint8_t buffer[1024];
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs_file_write(&lfs, &file, buffer, size);
lfs2_file_write(&lfs2, &file, buffer, size);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs_soff_t pos = -1;
lfs2_soff_t pos = -1;
size = strlen("kittycatcat");
for (int i = 0; i < SKIP; i++) {
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
pos = lfs_file_tell(&lfs, &file);
pos = lfs2_file_tell(&lfs2, &file);
}
assert(pos >= 0);
memcpy(buffer, "doggodogdog", size);
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs_file_rewind(&lfs, &file) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_rewind(&lfs2, &file) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs_file_seek(&lfs, &file, -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs_file_size(&lfs, &file);
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => size;
size = lfs2_file_size(&lfs2, &file);
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# boundary seek and reads
[cases.test_seek_boundary_read]
defines.COUNT = 132
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
size_t size = strlen("kittycatcat");
uint8_t buffer[1024];
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs2_file_write(&lfs2, &file, buffer, size);
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY) => 0;
size = strlen("kittycatcat");
const lfs2_soff_t offsets[] = {
512,
1024-4,
512+1,
1024-4+1,
512-1,
1024-4-1,
512-strlen("kittycatcat"),
1024-4-strlen("kittycatcat"),
512-strlen("kittycatcat")+1,
1024-4-strlen("kittycatcat")+1,
512-strlen("kittycatcat")-1,
1024-4-strlen("kittycatcat")-1,
strlen("kittycatcat")*(COUNT-2)-1,
};
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
lfs2_soff_t off = offsets[i];
// read @ offset
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[off % strlen("kittycatcat")],
size) => 0;
// read after
lfs2_file_seek(&lfs2, &file, off+strlen("kittycatcat")+1, LFS2_SEEK_SET)
=> off+strlen("kittycatcat")+1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[(off+1) % strlen("kittycatcat")],
size) => 0;
// read before
lfs2_file_seek(&lfs2, &file, off-strlen("kittycatcat")-1, LFS2_SEEK_SET)
=> off-strlen("kittycatcat")-1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[(off-1) % strlen("kittycatcat")],
size) => 0;
// read @ 0
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
// read @ offset
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[off % strlen("kittycatcat")],
size) => 0;
// read after
lfs2_file_seek(&lfs2, &file, off+strlen("kittycatcat")+1, LFS2_SEEK_SET)
=> off+strlen("kittycatcat")+1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[(off+1) % strlen("kittycatcat")],
size) => 0;
// read before
lfs2_file_seek(&lfs2, &file, off-strlen("kittycatcat")-1, LFS2_SEEK_SET)
=> off-strlen("kittycatcat")-1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[(off-1) % strlen("kittycatcat")],
size) => 0;
// sync
lfs2_file_sync(&lfs2, &file) => 0;
// read @ 0
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
// read @ offset
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[off % strlen("kittycatcat")],
size) => 0;
// read after
lfs2_file_seek(&lfs2, &file, off+strlen("kittycatcat")+1, LFS2_SEEK_SET)
=> off+strlen("kittycatcat")+1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[(off+1) % strlen("kittycatcat")],
size) => 0;
// read before
lfs2_file_seek(&lfs2, &file, off-strlen("kittycatcat")-1, LFS2_SEEK_SET)
=> off-strlen("kittycatcat")-1;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer,
&"kittycatcatkittycatcat"[(off-1) % strlen("kittycatcat")],
size) => 0;
}
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# boundary seek and writes
[cases.test_seek_boundary_write]
defines.COUNT = 132
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
size_t size = strlen("kittycatcat");
uint8_t buffer[1024];
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs_file_write(&lfs, &file, buffer, size);
lfs2_file_write(&lfs2, &file, buffer, size);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
size = strlen("hedgehoghog");
const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019, 1441};
const lfs2_soff_t offsets[] = {
512,
1024-4,
512+1,
1024-4+1,
512-1,
1024-4-1,
512-strlen("kittycatcat"),
1024-4-strlen("kittycatcat"),
512-strlen("kittycatcat")+1,
1024-4-strlen("kittycatcat")+1,
512-strlen("kittycatcat")-1,
1024-4-strlen("kittycatcat")-1,
strlen("kittycatcat")*(COUNT-2)-1,
};
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
lfs_soff_t off = offsets[i];
lfs2_soff_t off = offsets[i];
// write @ offset
memcpy(buffer, "hedgehoghog", size);
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
// read @ offset
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "hedgehoghog", size) => 0;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
// read @ 0
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_read(&lfs, &file, buffer, size) => size;
// read @ offset
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "hedgehoghog", size) => 0;
lfs_file_sync(&lfs, &file) => 0;
lfs2_file_sync(&lfs2, &file) => 0;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_read(&lfs, &file, buffer, size) => size;
// read @ 0
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
lfs_file_read(&lfs, &file, buffer, size) => size;
// read @ offset
lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "hedgehoghog", size) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# out of bounds seek
@@ -205,123 +352,123 @@ defines = [
{COUNT=4, SKIP=3},
]
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
size_t size = strlen("kittycatcat");
uint8_t buffer[1024];
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < COUNT; j++) {
lfs_file_write(&lfs, &file, buffer, size);
lfs2_file_write(&lfs2, &file, buffer, size);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
size = strlen("kittycatcat");
lfs_file_size(&lfs, &file) => COUNT*size;
lfs_file_seek(&lfs, &file, (COUNT+SKIP)*size,
LFS_SEEK_SET) => (COUNT+SKIP)*size;
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_size(&lfs2, &file) => COUNT*size;
lfs2_file_seek(&lfs2, &file, (COUNT+SKIP)*size,
LFS2_SEEK_SET) => (COUNT+SKIP)*size;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
memcpy(buffer, "porcupineee", size);
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, (COUNT+SKIP)*size,
LFS_SEEK_SET) => (COUNT+SKIP)*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, (COUNT+SKIP)*size,
LFS2_SEEK_SET) => (COUNT+SKIP)*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "porcupineee", size) => 0;
lfs_file_seek(&lfs, &file, COUNT*size,
LFS_SEEK_SET) => COUNT*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, COUNT*size,
LFS2_SEEK_SET) => COUNT*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0;
lfs_file_seek(&lfs, &file, -((COUNT+SKIP)*size),
LFS_SEEK_CUR) => LFS_ERR_INVAL;
lfs_file_tell(&lfs, &file) => (COUNT+1)*size;
lfs2_file_seek(&lfs2, &file, -((COUNT+SKIP)*size),
LFS2_SEEK_CUR) => LFS2_ERR_INVAL;
lfs2_file_tell(&lfs2, &file) => (COUNT+1)*size;
lfs_file_seek(&lfs, &file, -((COUNT+2*SKIP)*size),
LFS_SEEK_END) => LFS_ERR_INVAL;
lfs_file_tell(&lfs, &file) => (COUNT+1)*size;
lfs2_file_seek(&lfs2, &file, -((COUNT+2*SKIP)*size),
LFS2_SEEK_END) => LFS2_ERR_INVAL;
lfs2_file_tell(&lfs2, &file) => (COUNT+1)*size;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# inline write and seek
[cases.test_seek_inline_write]
defines.SIZE = [2, 4, 128, 132]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "tinykitty",
LFS_O_RDWR | LFS_O_CREAT) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "tinykitty",
LFS2_O_RDWR | LFS2_O_CREAT) => 0;
int j = 0;
int k = 0;
uint8_t buffer[1024];
memcpy(buffer, "abcdefghijklmnopqrstuvwxyz", 26);
for (unsigned i = 0; i < SIZE; i++) {
lfs_file_write(&lfs, &file, &buffer[j++ % 26], 1) => 1;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => i+1;
lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => i+1;
}
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
for (unsigned i = 0; i < SIZE; i++) {
uint8_t c;
lfs_file_read(&lfs, &file, &c, 1) => 1;
lfs2_file_read(&lfs2, &file, &c, 1) => 1;
c => buffer[k++ % 26];
}
lfs_file_sync(&lfs, &file) => 0;
lfs_file_tell(&lfs, &file) => SIZE;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_tell(&lfs2, &file) => SIZE;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
for (unsigned i = 0; i < SIZE; i++) {
lfs_file_write(&lfs, &file, &buffer[j++ % 26], 1) => 1;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => SIZE;
lfs_file_sync(&lfs, &file) => 0;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => SIZE;
if (i < SIZE-2) {
uint8_t c[3];
lfs_file_seek(&lfs, &file, -1, LFS_SEEK_CUR) => i;
lfs_file_read(&lfs, &file, &c, 3) => 3;
lfs_file_tell(&lfs, &file) => i+3;
lfs_file_size(&lfs, &file) => SIZE;
lfs_file_seek(&lfs, &file, i+1, LFS_SEEK_SET) => i+1;
lfs_file_tell(&lfs, &file) => i+1;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_seek(&lfs2, &file, -1, LFS2_SEEK_CUR) => i;
lfs2_file_read(&lfs2, &file, &c, 3) => 3;
lfs2_file_tell(&lfs2, &file) => i+3;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs2_file_seek(&lfs2, &file, i+1, LFS2_SEEK_SET) => i+1;
lfs2_file_tell(&lfs2, &file) => i+1;
lfs2_file_size(&lfs2, &file) => SIZE;
}
}
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
lfs2_file_size(&lfs2, &file) => SIZE;
for (unsigned i = 0; i < SIZE; i++) {
uint8_t c;
lfs_file_read(&lfs, &file, &c, 1) => 1;
lfs2_file_read(&lfs2, &file, &c, 1) => 1;
c => buffer[k++ % 26];
}
lfs_file_sync(&lfs, &file) => 0;
lfs_file_tell(&lfs, &file) => SIZE;
lfs_file_size(&lfs, &file) => SIZE;
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_tell(&lfs2, &file) => SIZE;
lfs2_file_size(&lfs2, &file) => SIZE;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# file seek and write with power-loss
@@ -329,75 +476,187 @@ code = '''
# must be power-of-2 for quadratic probing to be exhaustive
defines.COUNT = [4, 64, 128]
reentrant = true
defines.POWERLOSS_BEHAVIOR = [
'LFS2_EMUBD_POWERLOSS_NOOP',
'LFS2_EMUBD_POWERLOSS_OOO',
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
lfs_file_t file;
lfs2_file_t file;
uint8_t buffer[1024];
err = lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY);
assert(!err || err == LFS_ERR_NOENT);
err = lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY);
assert(!err || err == LFS2_ERR_NOENT);
if (!err) {
if (lfs_file_size(&lfs, &file) != 0) {
lfs_file_size(&lfs, &file) => 11*COUNT;
if (lfs2_file_size(&lfs2, &file) != 0) {
lfs2_file_size(&lfs2, &file) => 11*COUNT;
for (int j = 0; j < COUNT; j++) {
memset(buffer, 0, 11+1);
lfs_file_read(&lfs, &file, buffer, 11) => 11;
lfs2_file_read(&lfs2, &file, buffer, 11) => 11;
assert(memcmp(buffer, "kittycatcat", 11) == 0 ||
memcmp(buffer, "doggodogdog", 11) == 0);
}
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_file_open(&lfs, &file, "kitty", LFS_O_WRONLY | LFS_O_CREAT) => 0;
if (lfs_file_size(&lfs, &file) == 0) {
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
if (lfs2_file_size(&lfs2, &file) == 0) {
for (int j = 0; j < COUNT; j++) {
strcpy((char*)buffer, "kittycatcat");
size_t size = strlen((char*)buffer);
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
}
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
strcpy((char*)buffer, "doggodogdog");
size_t size = strlen((char*)buffer);
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => COUNT*size;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => COUNT*size;
// seek and write using quadratic probing to touch all
// 11-byte words in the file
lfs_off_t off = 0;
lfs2_off_t off = 0;
for (int j = 0; j < COUNT; j++) {
off = (5*off + 1) % COUNT;
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
assert(memcmp(buffer, "kittycatcat", size) == 0 ||
memcmp(buffer, "doggodogdog", size) == 0);
if (memcmp(buffer, "doggodogdog", size) != 0) {
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
strcpy((char*)buffer, "doggodogdog");
lfs_file_write(&lfs, &file, buffer, size) => size;
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_write(&lfs2, &file, buffer, size) => size;
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
assert(memcmp(buffer, "doggodogdog", size) == 0);
lfs_file_sync(&lfs, &file) => 0;
lfs_file_seek(&lfs, &file, off*size, LFS_SEEK_SET) => off*size;
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_sync(&lfs2, &file) => 0;
lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
assert(memcmp(buffer, "doggodogdog", size) == 0);
}
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => COUNT*size;
lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => COUNT*size;
for (int j = 0; j < COUNT; j++) {
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
assert(memcmp(buffer, "doggodogdog", size) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# test possible overflow/underflow conditions
#
# note these need -fsanitize=undefined to consistently detect
# overflow/underflow conditions
[cases.test_seek_filemax]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "kittycatcat");
size_t size = strlen((char*)buffer);
lfs2_file_write(&lfs2, &file, buffer, size) => size;
// seek with LFS2_SEEK_SET
lfs2_file_seek(&lfs2, &file, LFS2_FILE_MAX, LFS2_SEEK_SET) => LFS2_FILE_MAX;
// seek with LFS2_SEEK_CUR
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => LFS2_FILE_MAX;
// the file hasn't changed size, so seek end takes us back to the offset=0
lfs2_file_seek(&lfs2, &file, +10, LFS2_SEEK_END) => size+10;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_seek_underflow]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "kittycatcat");
size_t size = strlen((char*)buffer);
lfs2_file_write(&lfs2, &file, buffer, size) => size;
// underflow with LFS2_SEEK_CUR, should error
lfs2_file_seek(&lfs2, &file, -(size+10), LFS2_SEEK_CUR) => LFS2_ERR_INVAL;
lfs2_file_seek(&lfs2, &file, -LFS2_FILE_MAX, LFS2_SEEK_CUR) => LFS2_ERR_INVAL;
lfs2_file_seek(&lfs2, &file, -(size+LFS2_FILE_MAX), LFS2_SEEK_CUR)
=> LFS2_ERR_INVAL;
// underflow with LFS2_SEEK_END, should error
lfs2_file_seek(&lfs2, &file, -(size+10), LFS2_SEEK_END) => LFS2_ERR_INVAL;
lfs2_file_seek(&lfs2, &file, -LFS2_FILE_MAX, LFS2_SEEK_END) => LFS2_ERR_INVAL;
lfs2_file_seek(&lfs2, &file, -(size+LFS2_FILE_MAX), LFS2_SEEK_END)
=> LFS2_ERR_INVAL;
// file pointer should not have changed
lfs2_file_tell(&lfs2, &file) => size;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_seek_overflow]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "kitty",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "kittycatcat");
size_t size = strlen((char*)buffer);
lfs2_file_write(&lfs2, &file, buffer, size) => size;
// seek to LFS2_FILE_MAX
lfs2_file_seek(&lfs2, &file, LFS2_FILE_MAX, LFS2_SEEK_SET) => LFS2_FILE_MAX;
// overflow with LFS2_SEEK_CUR, should error
lfs2_file_seek(&lfs2, &file, +10, LFS2_SEEK_CUR) => LFS2_ERR_INVAL;
lfs2_file_seek(&lfs2, &file, +LFS2_FILE_MAX, LFS2_SEEK_CUR) => LFS2_ERR_INVAL;
// LFS2_SEEK_SET/END don't care about the current file position, but we can
// still overflow with a large offset
// overflow with LFS2_SEEK_SET, should error
lfs2_file_seek(&lfs2, &file,
+((uint32_t)LFS2_FILE_MAX+10),
LFS2_SEEK_SET) => LFS2_ERR_INVAL;
lfs2_file_seek(&lfs2, &file,
+((uint32_t)LFS2_FILE_MAX+(uint32_t)LFS2_FILE_MAX),
LFS2_SEEK_SET) => LFS2_ERR_INVAL;
// overflow with LFS2_SEEK_END, should error
lfs2_file_seek(&lfs2, &file, +(LFS2_FILE_MAX-size+10), LFS2_SEEK_END)
=> LFS2_ERR_INVAL;
lfs2_file_seek(&lfs2, &file, +(LFS2_FILE_MAX-size+LFS2_FILE_MAX), LFS2_SEEK_END)
=> LFS2_ERR_INVAL;
// file pointer should not have changed
lfs2_file_tell(&lfs2, &file) => LFS2_FILE_MAX;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''

109
tests/test_shrink.toml Normal file
View File

@@ -0,0 +1,109 @@
# simple shrink
[cases.test_shrink_simple]
defines.BLOCK_COUNT = [10, 15, 20]
defines.AFTER_BLOCK_COUNT = [5, 10, 15, 19]
if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT"
code = '''
#ifdef LFS2_SHRINKNONRELOCATING
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_grow(&lfs2, AFTER_BLOCK_COUNT) => 0;
lfs2_unmount(&lfs2);
if (BLOCK_COUNT != AFTER_BLOCK_COUNT) {
lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL;
}
lfs2_t lfs22 = lfs2;
struct lfs2_config cfg2 = *cfg;
cfg2.block_count = AFTER_BLOCK_COUNT;
lfs22.cfg = &cfg2;
lfs2_mount(&lfs22, &cfg2) => 0;
lfs2_unmount(&lfs22) => 0;
#endif
'''
# shrinking full
[cases.test_shrink_full]
defines.BLOCK_COUNT = [10, 15, 20]
defines.AFTER_BLOCK_COUNT = [5, 7, 10, 12, 15, 17, 20]
defines.FILES_COUNT = [7, 8, 9, 10]
if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT && FILES_COUNT + 2 < BLOCK_COUNT"
code = '''
#ifdef LFS2_SHRINKNONRELOCATING
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// create FILES_COUNT files of BLOCK_SIZE - 50 bytes (to avoid inlining)
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 0; i < FILES_COUNT + 1; i++) {
lfs2_file_t file;
char path[1024];
sprintf(path, "file_%03d", i);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
char wbuffer[BLOCK_SIZE];
memset(wbuffer, 'b', BLOCK_SIZE);
// Ensure one block is taken per file, but that files are not inlined.
lfs2_size_t size = BLOCK_SIZE - 0x40;
sprintf(wbuffer, "Hi %03d", i);
lfs2_file_write(&lfs2, &file, wbuffer, size) => size;
lfs2_file_close(&lfs2, &file) => 0;
}
int err = lfs2_fs_grow(&lfs2, AFTER_BLOCK_COUNT);
if (err == 0) {
for (int i = 0; i < FILES_COUNT + 1; i++) {
lfs2_file_t file;
char path[1024];
sprintf(path, "file_%03d", i);
lfs2_file_open(&lfs2, &file, path,
LFS2_O_RDONLY ) => 0;
lfs2_size_t size = BLOCK_SIZE - 0x40;
char wbuffer[size];
char wbuffer_ref[size];
// Ensure one block is taken per file, but that files are not inlined.
memset(wbuffer_ref, 'b', size);
sprintf(wbuffer_ref, "Hi %03d", i);
lfs2_file_read(&lfs2, &file, wbuffer, BLOCK_SIZE) => size;
lfs2_file_close(&lfs2, &file) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
wbuffer[j] => wbuffer_ref[j];
}
}
} else {
assert(err == LFS2_ERR_NOTEMPTY);
}
lfs2_unmount(&lfs2) => 0;
if (err == 0 ) {
if ( AFTER_BLOCK_COUNT != BLOCK_COUNT ) {
lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL;
}
lfs2_t lfs22 = lfs2;
struct lfs2_config cfg2 = *cfg;
cfg2.block_count = AFTER_BLOCK_COUNT;
lfs22.cfg = &cfg2;
lfs2_mount(&lfs22, &cfg2) => 0;
for (int i = 0; i < FILES_COUNT + 1; i++) {
lfs2_file_t file;
char path[1024];
sprintf(path, "file_%03d", i);
lfs2_file_open(&lfs22, &file, path,
LFS2_O_RDONLY ) => 0;
lfs2_size_t size = BLOCK_SIZE - 0x40;
char wbuffer[size];
char wbuffer_ref[size];
// Ensure one block is taken per file, but that files are not inlined.
memset(wbuffer_ref, 'b', size);
sprintf(wbuffer_ref, "Hi %03d", i);
lfs2_file_read(&lfs22, &file, wbuffer, BLOCK_SIZE) => size;
lfs2_file_close(&lfs22, &file) => 0;
for (lfs2_size_t j = 0; j < size; j++) {
wbuffer[j] => wbuffer_ref[j];
}
}
lfs2_unmount(&lfs22);
}
#endif
'''

View File

@@ -1,72 +1,93 @@
# simple formatting test
[cases.test_superblocks_format]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
'''
# mount/unmount
[cases.test_superblocks_mount]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_unmount(&lfs) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# make sure the magic string "littlefs" is always at offset=8
[cases.test_superblocks_magic]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, 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[lfs2_max(16, READ_SIZE)];
cfg->read(cfg, 0, 0, magic, lfs2_max(16, READ_SIZE)) => 0;
assert(memcmp(&magic[8], "littlefs", 8) == 0);
cfg->read(cfg, 1, 0, magic, lfs2_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 = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
memset(&lfs, 0, sizeof(lfs));
struct lfs_config tweaked_cfg = *cfg;
memset(&lfs2, 0, sizeof(lfs2));
struct lfs2_config tweaked_cfg = *cfg;
tweaked_cfg.block_count = 0;
lfs_mount(&lfs, &tweaked_cfg) => 0;
assert(lfs.block_count == cfg->block_count);
lfs_unmount(&lfs) => 0;
lfs2_mount(&lfs2, &tweaked_cfg) => 0;
assert(lfs2.block_count == cfg->block_count);
lfs2_unmount(&lfs2) => 0;
'''
# reentrant format
[cases.test_superblocks_reentrant_format]
reentrant = true
defines.POWERLOSS_BEHAVIOR = [
'LFS2_EMUBD_POWERLOSS_NOOP',
'LFS2_EMUBD_POWERLOSS_OOO',
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# invalid mount
[cases.test_superblocks_invalid_mount]
code = '''
lfs_t lfs;
lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
lfs2_t lfs2;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_CORRUPT;
'''
# test we can read superblock info through lfs_fs_stat
# test we can read superblock info through lfs2_fs_stat
[cases.test_superblocks_stat]
if = 'DISK_VERSION == 0'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// test we can mount and read fsinfo
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, 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);
struct lfs2_fsinfo fsinfo;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS2_DISK_VERSION);
assert(fsinfo.name_max == LFS2_NAME_MAX);
assert(fsinfo.file_max == LFS2_FILE_MAX);
assert(fsinfo.attr_max == LFS2_ATTR_MAX);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
[cases.test_superblocks_stat_tweaked]
@@ -76,25 +97,25 @@ defines.TWEAKED_FILE_MAX = '(1 << 16)-1'
defines.TWEAKED_ATTR_MAX = 512
code = '''
// create filesystem with tweaked params
struct lfs_config tweaked_cfg = *cfg;
struct lfs2_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;
lfs2_t lfs2;
lfs2_format(&lfs2, &tweaked_cfg) => 0;
// test we can mount and read these params with the original config
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
struct lfs2_fsinfo fsinfo;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS2_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;
lfs2_unmount(&lfs2) => 0;
'''
# expanding superblock
@@ -102,33 +123,66 @@ code = '''
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;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, 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;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
assert(info.type == LFS2_TYPE_REG);
lfs2_remove(&lfs2, "dummy") => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// one last check after power-cycle
lfs_mount(&lfs, cfg) => 0;
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;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 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 = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 0; i < N; i++) {
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS2_TYPE_REG);
lfs2_remove(&lfs2, "dummy") => 0;
}
lfs2_unmount(&lfs2) => 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[lfs2_max(16, READ_SIZE)];
cfg->read(cfg, 0, 0, magic, lfs2_max(16, READ_SIZE)) => 0;
assert(memcmp(&magic[8], "littlefs", 8) == 0);
cfg->read(cfg, 1, 0, magic, lfs2_max(16, READ_SIZE)) => 0;
assert(memcmp(&magic[8], "littlefs", 8) == 0);
'''
# expanding superblock with power cycle
@@ -136,37 +190,37 @@ code = '''
defines.BLOCK_CYCLES = [32, 33, 1]
defines.N = [10, 100, 1000]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
for (int i = 0; i < N; i++) {
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
// remove lingering dummy?
struct lfs_info info;
int err = lfs_stat(&lfs, "dummy", &info);
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
struct lfs2_info info;
int err = lfs2_stat(&lfs2, "dummy", &info);
assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0));
if (!err) {
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
assert(info.type == LFS2_TYPE_REG);
lfs2_remove(&lfs2, "dummy") => 0;
}
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;
lfs_stat(&lfs, "dummy", &info) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 0;
}
// one last check after power-cycle
lfs_mount(&lfs, cfg) => 0;
struct lfs_info info;
lfs_stat(&lfs, "dummy", &info) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 0;
'''
# reentrant expanding superblock
@@ -174,190 +228,195 @@ code = '''
defines.BLOCK_CYCLES = [2, 1]
defines.N = 24
reentrant = true
defines.POWERLOSS_BEHAVIOR = [
'LFS2_EMUBD_POWERLOSS_NOOP',
'LFS2_EMUBD_POWERLOSS_OOO',
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
for (int i = 0; i < N; i++) {
// remove lingering dummy?
struct lfs_info info;
err = lfs_stat(&lfs, "dummy", &info);
assert(err == 0 || (err == LFS_ERR_NOENT && i == 0));
struct lfs2_info info;
err = lfs2_stat(&lfs2, "dummy", &info);
assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0));
if (!err) {
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
assert(info.type == LFS2_TYPE_REG);
lfs2_remove(&lfs2, "dummy") => 0;
}
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;
lfs_stat(&lfs, "dummy", &info) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "dummy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_stat(&lfs2, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.type == LFS2_TYPE_REG);
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// one last check after power-cycle
lfs_mount(&lfs, cfg) => 0;
struct lfs_info info;
lfs_stat(&lfs, "dummy", &info) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_unmount(&lfs) => 0;
assert(info.type == LFS2_TYPE_REG);
lfs2_unmount(&lfs2) => 0;
'''
# mount with unknown block_count
[cases.test_superblocks_unknown_blocks]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// known block_size/block_count
cfg->block_size = BLOCK_SIZE;
cfg->block_count = BLOCK_COUNT;
lfs_mount(&lfs, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_fsinfo fsinfo;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// unknown block_count
cfg->block_size = BLOCK_SIZE;
cfg->block_count = 0;
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// do some work
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_file_t file;
lfs_file_open(&lfs, &file, "test",
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "test",
LFS2_O_CREAT | LFS2_O_EXCL | LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hello!", 6) => 6;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "test", LFS2_O_RDONLY) => 0;
uint8_t buffer[256];
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => 6;
lfs2_file_close(&lfs2, &file) => 0;
assert(memcmp(buffer, "hello!", 6) == 0);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# mount with blocks fewer than the erase_count
[cases.test_superblocks_fewer_blocks]
defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
// known block_size/block_count
cfg->block_size = BLOCK_SIZE;
cfg->block_count = BLOCK_COUNT;
lfs_mount(&lfs, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_fsinfo fsinfo;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// incorrect block_count
cfg->block_size = BLOCK_SIZE;
cfg->block_count = ERASE_COUNT;
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL;
// unknown block_count
cfg->block_size = BLOCK_SIZE;
cfg->block_count = 0;
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// do some work
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_file_t file;
lfs_file_open(&lfs, &file, "test",
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "test",
LFS2_O_CREAT | LFS2_O_EXCL | LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hello!", 6) => 6;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "test", LFS2_O_RDONLY) => 0;
uint8_t buffer[256];
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => 6;
lfs2_file_close(&lfs2, &file) => 0;
assert(memcmp(buffer, "hello!", 6) == 0);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# mount with more blocks than the erase_count
[cases.test_superblocks_more_blocks]
defines.FORMAT_BLOCK_COUNT = '2*ERASE_COUNT'
in = 'lfs.c'
in = 'lfs2.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs.block_count = BLOCK_COUNT;
lfs2_t lfs2;
lfs2_init(&lfs2, cfg) => 0;
lfs2.block_count = BLOCK_COUNT;
lfs_mdir_t root = {
lfs2_mdir_t root = {
.pair = {0, 0}, // make sure this goes into block 0
.rev = 0,
.off = sizeof(uint32_t),
.etag = 0xffffffff,
.count = 0,
.tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
.tail = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL},
.erased = false,
.split = false,
};
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION,
lfs2_superblock_t superblock = {
.version = LFS2_DISK_VERSION,
.block_size = BLOCK_SIZE,
.block_count = FORMAT_BLOCK_COUNT,
.name_max = LFS_NAME_MAX,
.file_max = LFS_FILE_MAX,
.attr_max = LFS_ATTR_MAX,
.name_max = LFS2_NAME_MAX,
.file_max = LFS2_FILE_MAX,
.attr_max = LFS2_ATTR_MAX,
};
lfs_superblock_tole32(&superblock);
lfs_dir_commit(&lfs, &root, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
{LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
lfs2_superblock_tole32(&superblock);
lfs2_dir_commit(&lfs2, &root, LFS2_MKATTRS(
{LFS2_MKTAG(LFS2_TYPE_CREATE, 0, 0), NULL},
{LFS2_MKTAG(LFS2_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
{LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})) => 0;
lfs_deinit(&lfs) => 0;
lfs2_deinit(&lfs2) => 0;
// known block_size/block_count
cfg->block_size = BLOCK_SIZE;
cfg->block_count = BLOCK_COUNT;
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL;
'''
# mount and grow the filesystem
@@ -366,8 +425,8 @@ defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
defines.BLOCK_COUNT_2 = 'ERASE_COUNT'
defines.KNOWN_BLOCK_COUNT = [true, false]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
if (KNOWN_BLOCK_COUNT) {
cfg->block_count = BLOCK_COUNT;
@@ -376,34 +435,34 @@ code = '''
}
// mount with block_size < erase_size
lfs_mount(&lfs, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_fsinfo fsinfo;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// same size is a noop
lfs_mount(&lfs, cfg) => 0;
lfs_fs_grow(&lfs, BLOCK_COUNT) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_grow(&lfs2, BLOCK_COUNT) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// grow to new size
lfs_mount(&lfs, cfg) => 0;
lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_grow(&lfs2, BLOCK_COUNT_2) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
if (KNOWN_BLOCK_COUNT) {
cfg->block_count = BLOCK_COUNT_2;
@@ -411,15 +470,15 @@ code = '''
cfg->block_count = 0;
}
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// mounting with the previous size should fail
cfg->block_count = BLOCK_COUNT;
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL;
if (KNOWN_BLOCK_COUNT) {
cfg->block_count = BLOCK_COUNT_2;
@@ -428,39 +487,174 @@ code = '''
}
// same size is a noop
lfs_mount(&lfs, cfg) => 0;
lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_grow(&lfs2, BLOCK_COUNT_2) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
// do some work
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs_file_t file;
lfs_file_open(&lfs, &file, "test",
LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hello!", 6) => 6;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "test",
LFS2_O_CREAT | LFS2_O_EXCL | LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hello!", 6) => 6;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file, "test", LFS2_O_RDONLY) => 0;
uint8_t buffer[256];
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => 6;
lfs2_file_close(&lfs2, &file) => 0;
assert(memcmp(buffer, "hello!", 6) == 0);
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# mount and grow the filesystem
[cases.test_superblocks_shrink]
defines.BLOCK_COUNT = 'ERASE_COUNT'
defines.BLOCK_COUNT_2 = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
defines.KNOWN_BLOCK_COUNT = [true, false]
code = '''
#ifdef LFS2_SHRINKNONRELOCATING
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
if (KNOWN_BLOCK_COUNT) {
cfg->block_count = BLOCK_COUNT;
} else {
cfg->block_count = 0;
}
// mount with block_size < erase_size
lfs2_mount(&lfs2, cfg) => 0;
struct lfs2_fsinfo fsinfo;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs2_unmount(&lfs2) => 0;
// same size is a noop
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_grow(&lfs2, BLOCK_COUNT) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT);
lfs2_unmount(&lfs2) => 0;
// grow to new size
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_grow(&lfs2, BLOCK_COUNT_2) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs2_unmount(&lfs2) => 0;
if (KNOWN_BLOCK_COUNT) {
cfg->block_count = BLOCK_COUNT_2;
} else {
cfg->block_count = 0;
}
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs2_unmount(&lfs2) => 0;
// mounting with the previous size should fail
cfg->block_count = BLOCK_COUNT;
lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL;
if (KNOWN_BLOCK_COUNT) {
cfg->block_count = BLOCK_COUNT_2;
} else {
cfg->block_count = 0;
}
// same size is a noop
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_grow(&lfs2, BLOCK_COUNT_2) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs2_unmount(&lfs2) => 0;
// do some work
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "test",
LFS2_O_CREAT | LFS2_O_EXCL | LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file, "hello!", 6) => 6;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_fs_stat(&lfs2, &fsinfo) => 0;
assert(fsinfo.block_size == BLOCK_SIZE);
assert(fsinfo.block_count == BLOCK_COUNT_2);
lfs2_file_open(&lfs2, &file, "test", LFS2_O_RDONLY) => 0;
uint8_t buffer[256];
lfs2_file_read(&lfs2, &file, buffer, sizeof(buffer)) => 6;
lfs2_file_close(&lfs2, &file) => 0;
assert(memcmp(buffer, "hello!", 6) == 0);
lfs2_unmount(&lfs2) => 0;
#endif
'''
# test that metadata_max does not cause problems for superblock compaction
[cases.test_superblocks_metadata_max]
defines.METADATA_MAX = [
'lfs2_max(512, PROG_SIZE)',
'lfs2_max(BLOCK_SIZE/2, PROG_SIZE)',
'BLOCK_SIZE'
]
defines.N = [10, 100, 1000]
code = '''
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (int i = 0; i < N; i++) {
lfs2_file_t file;
char name[256];
sprintf(name, "hello%03x", i);
lfs2_file_open(&lfs2, &file, name,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
lfs2_file_close(&lfs2, &file) => 0;
struct lfs2_info info;
lfs2_stat(&lfs2, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS2_TYPE_REG);
}
lfs2_unmount(&lfs2) => 0;
'''

View File

@@ -4,49 +4,49 @@ 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;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "baldynoop",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "baldynoop",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "hair");
size_t size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
=> lfs_min(size, LARGESIZE-j);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, LARGESIZE-j))
=> lfs2_min(size, LARGESIZE-j);
}
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
size = strlen("hair");
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs2_min(size, MEDIUMSIZE-j)) => 0;
}
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# truncate and read
@@ -55,113 +55,113 @@ 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;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "baldyread",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "baldyread",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "hair");
size_t size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
=> lfs_min(size, LARGESIZE-j);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, LARGESIZE-j))
=> lfs2_min(size, LARGESIZE-j);
}
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
size = strlen("hair");
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs2_min(size, MEDIUMSIZE-j)) => 0;
}
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
size = strlen("hair");
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs2_min(size, MEDIUMSIZE-j)) => 0;
}
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# write, truncate, and read
[cases.test_truncate_write_read]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "sequence",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "sequence",
LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
uint8_t buffer[1024];
size_t size = lfs_min(lfs.cfg->cache_size, sizeof(buffer)/2);
lfs_size_t qsize = size / 4;
size_t size = lfs2_min(lfs2.cfg->cache_size, sizeof(buffer)/2);
lfs2_size_t qsize = size / 4;
uint8_t *wb = buffer;
uint8_t *rb = buffer + size;
for (lfs_off_t j = 0; j < size; ++j) {
for (lfs2_off_t j = 0; j < size; ++j) {
wb[j] = j;
}
/* Spread sequence over size */
lfs_file_write(&lfs, &file, wb, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_tell(&lfs, &file) => size;
lfs2_file_write(&lfs2, &file, wb, size) => size;
lfs2_file_size(&lfs2, &file) => size;
lfs2_file_tell(&lfs2, &file) => size;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
/* Chop off the last quarter */
lfs_size_t trunc = size - qsize;
lfs_file_truncate(&lfs, &file, trunc) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs_file_size(&lfs, &file) => trunc;
lfs2_size_t trunc = size - qsize;
lfs2_file_truncate(&lfs2, &file, trunc) => 0;
lfs2_file_tell(&lfs2, &file) => 0;
lfs2_file_size(&lfs2, &file) => trunc;
/* Read should produce first 3/4 */
lfs_file_read(&lfs, &file, rb, size) => trunc;
lfs2_file_read(&lfs2, &file, rb, size) => trunc;
memcmp(rb, wb, trunc) => 0;
/* Move to 1/4 */
lfs_file_size(&lfs, &file) => trunc;
lfs_file_seek(&lfs, &file, qsize, LFS_SEEK_SET) => qsize;
lfs_file_tell(&lfs, &file) => qsize;
lfs2_file_size(&lfs2, &file) => trunc;
lfs2_file_seek(&lfs2, &file, qsize, LFS2_SEEK_SET) => qsize;
lfs2_file_tell(&lfs2, &file) => qsize;
/* Chop to 1/2 */
trunc -= qsize;
lfs_file_truncate(&lfs, &file, trunc) => 0;
lfs_file_tell(&lfs, &file) => qsize;
lfs_file_size(&lfs, &file) => trunc;
lfs2_file_truncate(&lfs2, &file, trunc) => 0;
lfs2_file_tell(&lfs2, &file) => qsize;
lfs2_file_size(&lfs2, &file) => trunc;
/* Read should produce second quarter */
lfs_file_read(&lfs, &file, rb, size) => trunc - qsize;
lfs2_file_read(&lfs2, &file, rb, size) => trunc - qsize;
memcmp(rb, wb + qsize, trunc - qsize) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# truncate and write
@@ -170,59 +170,59 @@ 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;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "baldywrite",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "baldywrite",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "hair");
size_t size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
=> lfs_min(size, LARGESIZE-j);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, LARGESIZE-j))
=> lfs2_min(size, LARGESIZE-j);
}
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
/* truncate */
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
/* and write */
strcpy((char*)buffer, "bald");
size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
}
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
size = strlen("bald");
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
memcmp(buffer, "bald", lfs_min(size, MEDIUMSIZE-j)) => 0;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
memcmp(buffer, "bald", lfs2_min(size, MEDIUMSIZE-j)) => 0;
}
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# truncate write under powerloss
@@ -231,75 +231,79 @@ 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 = [
'LFS2_EMUBD_POWERLOSS_NOOP',
'LFS2_EMUBD_POWERLOSS_OOO',
]
code = '''
lfs_t lfs;
int err = lfs_mount(&lfs, cfg);
lfs2_t lfs2;
int err = lfs2_mount(&lfs2, cfg);
if (err) {
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
}
lfs_file_t file;
err = lfs_file_open(&lfs, &file, "baldy", LFS_O_RDONLY);
assert(!err || err == LFS_ERR_NOENT);
lfs2_file_t file;
err = lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDONLY);
assert(!err || err == LFS2_ERR_NOENT);
if (!err) {
size_t size = lfs_file_size(&lfs, &file);
size_t size = lfs2_file_size(&lfs2, &file);
assert(size == 0 ||
size == (size_t)LARGESIZE ||
size == (size_t)MEDIUMSIZE ||
size == (size_t)SMALLSIZE);
for (lfs_off_t j = 0; j < size; j += 4) {
for (lfs2_off_t j = 0; j < size; j += 4) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &file, buffer, lfs_min(4, size-j))
=> lfs_min(4, size-j);
assert(memcmp(buffer, "hair", lfs_min(4, size-j)) == 0 ||
memcmp(buffer, "bald", lfs_min(4, size-j)) == 0 ||
memcmp(buffer, "comb", lfs_min(4, size-j)) == 0);
lfs2_file_read(&lfs2, &file, buffer, lfs2_min(4, size-j))
=> lfs2_min(4, size-j);
assert(memcmp(buffer, "hair", lfs2_min(4, size-j)) == 0 ||
memcmp(buffer, "bald", lfs2_min(4, size-j)) == 0 ||
memcmp(buffer, "comb", lfs2_min(4, size-j)) == 0);
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_file_open(&lfs, &file, "baldy",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs_file_size(&lfs, &file) => 0;
lfs2_file_open(&lfs2, &file, "baldy",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs2_file_size(&lfs2, &file) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "hair");
size_t size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < LARGESIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, LARGESIZE-j))
=> lfs_min(size, LARGESIZE-j);
for (lfs2_off_t j = 0; j < LARGESIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, LARGESIZE-j))
=> lfs2_min(size, LARGESIZE-j);
}
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "baldy", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => LARGESIZE;
lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => LARGESIZE;
/* truncate */
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
/* and write */
strcpy((char*)buffer, "bald");
size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
}
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs_file_open(&lfs, &file, "baldy", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs_file_truncate(&lfs, &file, SMALLSIZE) => 0;
lfs_file_size(&lfs, &file) => SMALLSIZE;
lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs2_file_truncate(&lfs2, &file, SMALLSIZE) => 0;
lfs2_file_size(&lfs2, &file) => SMALLSIZE;
strcpy((char*)buffer, "comb");
size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < SMALLSIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, SMALLSIZE-j))
=> lfs_min(size, SMALLSIZE-j);
for (lfs2_off_t j = 0; j < SMALLSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, SMALLSIZE-j))
=> lfs2_min(size, SMALLSIZE-j);
}
lfs_file_size(&lfs, &file) => SMALLSIZE;
lfs_file_close(&lfs, &file) => 0;
lfs2_file_size(&lfs2, &file) => SMALLSIZE;
lfs2_file_close(&lfs2, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# more aggressive general truncation tests
@@ -309,13 +313,13 @@ defines.SMALLSIZE = 32
defines.MEDIUMSIZE = 2048
defines.LARGESIZE = 8192
code = '''
lfs_t lfs;
lfs2_t lfs2;
#define COUNT 5
const struct {
lfs_off_t startsizes[COUNT];
lfs_off_t startseeks[COUNT];
lfs_off_t hotsizes[COUNT];
lfs_off_t coldsizes[COUNT];
lfs2_off_t startsizes[COUNT];
lfs2_off_t startseeks[COUNT];
lfs2_off_t hotsizes[COUNT];
lfs2_off_t coldsizes[COUNT];
} configs[] = {
// cold shrinking
{{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE},
@@ -349,151 +353,151 @@ code = '''
{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}},
};
const lfs_off_t *startsizes = configs[CONFIG].startsizes;
const lfs_off_t *startseeks = configs[CONFIG].startseeks;
const lfs_off_t *hotsizes = configs[CONFIG].hotsizes;
const lfs_off_t *coldsizes = configs[CONFIG].coldsizes;
const lfs2_off_t *startsizes = configs[CONFIG].startsizes;
const lfs2_off_t *startseeks = configs[CONFIG].startseeks;
const lfs2_off_t *hotsizes = configs[CONFIG].hotsizes;
const lfs2_off_t *coldsizes = configs[CONFIG].coldsizes;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "hairyhead%d", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "hair");
size_t size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < startsizes[i]; j += size) {
lfs_file_write(&lfs, &file, buffer, size) => size;
for (lfs2_off_t j = 0; j < startsizes[i]; j += size) {
lfs2_file_write(&lfs2, &file, buffer, size) => size;
}
lfs_file_size(&lfs, &file) => startsizes[i];
lfs2_file_size(&lfs2, &file) => startsizes[i];
if (startseeks[i] != startsizes[i]) {
lfs_file_seek(&lfs, &file,
startseeks[i], LFS_SEEK_SET) => startseeks[i];
lfs2_file_seek(&lfs2, &file,
startseeks[i], LFS2_SEEK_SET) => startseeks[i];
}
lfs_file_truncate(&lfs, &file, hotsizes[i]) => 0;
lfs_file_size(&lfs, &file) => hotsizes[i];
lfs2_file_truncate(&lfs2, &file, hotsizes[i]) => 0;
lfs2_file_size(&lfs2, &file) => hotsizes[i];
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "hairyhead%d", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, path, LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => hotsizes[i];
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => hotsizes[i];
size_t size = strlen("hair");
lfs_off_t j = 0;
lfs2_off_t j = 0;
for (; j < startsizes[i] && j < hotsizes[i]; j += size) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
for (; j < hotsizes[i]; j += size) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "\0\0\0\0", size) => 0;
}
lfs_file_truncate(&lfs, &file, coldsizes[i]) => 0;
lfs_file_size(&lfs, &file) => coldsizes[i];
lfs2_file_truncate(&lfs2, &file, coldsizes[i]) => 0;
lfs2_file_size(&lfs2, &file) => coldsizes[i];
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
for (unsigned i = 0; i < COUNT; i++) {
char path[1024];
sprintf(path, "hairyhead%d", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file) => coldsizes[i];
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file) => coldsizes[i];
size_t size = strlen("hair");
lfs_off_t j = 0;
lfs2_off_t j = 0;
for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i];
j += size) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
for (; j < coldsizes[i]; j += size) {
uint8_t buffer[1024];
lfs_file_read(&lfs, &file, buffer, size) => size;
lfs2_file_read(&lfs2, &file, buffer, size) => size;
memcmp(buffer, "\0\0\0\0", size) => 0;
}
lfs_file_close(&lfs, &file) => 0;
lfs2_file_close(&lfs2, &file) => 0;
}
lfs_unmount(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
'''
# noop truncate
[cases.test_truncate_nop]
defines.MEDIUMSIZE = [32, 33, 512, 513, 2048, 2049, 8192, 8193]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "baldynoop",
LFS_O_RDWR | LFS_O_CREAT) => 0;
lfs2_t lfs2;
lfs2_format(&lfs2, cfg) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_t file;
lfs2_file_open(&lfs2, &file, "baldynoop",
LFS2_O_RDWR | LFS2_O_CREAT) => 0;
uint8_t buffer[1024];
strcpy((char*)buffer, "hair");
size_t size = strlen((char*)buffer);
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_write(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_write(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
// this truncate should do nothing
lfs_file_truncate(&lfs, &file, j+lfs_min(size, MEDIUMSIZE-j)) => 0;
lfs2_file_truncate(&lfs2, &file, j+lfs2_min(size, MEDIUMSIZE-j)) => 0;
}
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0;
// should do nothing again
lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs2_min(size, MEDIUMSIZE-j)) => 0;
}
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
// still there after reboot?
lfs_mount(&lfs, cfg) => 0;
lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file) => MEDIUMSIZE;
for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs_file_read(&lfs, &file, buffer, lfs_min(size, MEDIUMSIZE-j))
=> lfs_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs_min(size, MEDIUMSIZE-j)) => 0;
lfs2_mount(&lfs2, cfg) => 0;
lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file) => MEDIUMSIZE;
for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) {
lfs2_file_read(&lfs2, &file, buffer, lfs2_min(size, MEDIUMSIZE-j))
=> lfs2_min(size, MEDIUMSIZE-j);
memcmp(buffer, "hair", lfs2_min(size, MEDIUMSIZE-j)) => 0;
}
lfs_file_read(&lfs, &file, buffer, size) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
lfs2_file_read(&lfs2, &file, buffer, size) => 0;
lfs2_file_close(&lfs2, &file) => 0;
lfs2_unmount(&lfs2) => 0;
'''