Compare commits

...

133 Commits

Author SHA1 Message Date
Christopher Haster
227936b043 WIP Stage 1 of B-trees with separate child slots
Startingto run into some problems, not using separate child slots may
end up working better, even if it it's imperfect
2018-09-05 05:07:22 -05:00
Christopher Haster
8e501d5922 WIP Initial branch for exploring B-trees 2018-08-30 12:24:55 -05:00
Christopher Haster
a3abfc1fe8 WIP Modified CRC valid bit to check all tags
Allows earlier exit when parsing bad blocks
2018-08-24 17:47:28 -05:00
Christopher Haster
a326e47710 WIP Modified lfs_dir_compact to avoid redundant erases during split 2018-08-20 22:18:26 -05:00
Christopher Haster
ed49ea492a WIP Revisited caching rules to optimize size of reads 2018-08-20 14:47:52 -05:00
Christopher Haster
791891ae3b WIP Changed unwritable superblock to ENOSPC to match similar situations 2018-08-19 10:16:49 -05:00
Christopher Haster
42ead30339 WIP Tweaked inline files to support inline_max == cache_size 2018-08-13 14:08:30 -05:00
Christopher Haster
9819ba24e8 WIP Changed deorphan to only use one pass for pred fix 2018-08-13 09:03:13 -05:00
Christopher Haster
4264b28aa3 WIP dropped lfs_fs_getattr for more implicit lfs_getattr("/") 2018-08-11 23:05:52 -05:00
Christopher Haster
533b3bf686 WIP Fixed minor memory leak
- Fixed memory leak
- Change lfs_globals_zero to use memset as this
  made leak checking more effective
2018-08-11 13:45:13 -05:00
Christopher Haster
24eaf86b0e WIP Added allocation randomization for dynamic wear-leveling 2018-08-09 12:16:46 -05:00
Christopher Haster
67bbee56f5 WIP added framework for full dynamic wear-leveling 2018-08-08 22:20:37 -05:00
Christopher Haster
efc491facb WIP Fixed issue with big-endian ctz lists intertwined in commit logic 2018-08-08 09:37:43 -05:00
Christopher Haster
fd9e609eb7 WIP Changed name of lfs_crc to match more common API 2018-08-07 14:59:44 -05:00
Christopher Haster
88f15a8d41 WIP fixed issues with expanding directories in tests
(Some where legitimate test failures)
2018-08-07 14:44:26 -05:00
Christopher Haster
2da9392fbf WIP modified ctz type tag for future compatibility with erased files 2018-08-07 09:57:54 -05:00
Christopher Haster
5422566b6a WIP Added expading superblocks and root entries 2018-08-06 20:07:09 -05:00
Christopher Haster
e0e289207a WIP Changed superblock id to acocunt for future compatibility
With expanding superblocks
2018-08-06 13:30:51 -05:00
Christopher Haster
f96e8c130d WIP Made naming consistent for internal functions 2018-08-04 23:57:43 -05:00
Christopher Haster
78b460a907 Merge remote-tracking branch 'origin/master' into v2-alpha 2018-08-04 22:01:44 -05:00
Christopher Haster
a64ff4fb95 WIP Changed littlefs-fuse target for testing 2018-08-04 20:24:02 -05:00
Christopher Haster
4f0d248e09 WIP Changed name of upper-limits from blah_size to blah_max 2018-08-04 20:24:01 -05:00
Christopher Haster
7ffbe81bb2 WIP Changed LFS_ERR_CORRUPT to match EILSEQ instead of EBADE 2018-08-04 20:24:01 -05:00
Christopher Haster
2f73652937 WIP Documented custom attribute API and changed behaviour around
nonexisting attributes
2018-08-04 20:23:51 -05:00
Christopher Haster
c3480c8c92 WIP Added test for global state stealing 2018-08-04 18:55:04 -05:00
Christopher Haster
2bcec45c1b WIP cleaned up config options 2018-08-04 16:04:24 -05:00
Christopher Haster
a98affc951 WIP Added support for cache_size as alternative to read_size and
prog_size
2018-08-04 14:48:27 -05:00
Christopher Haster
e7c4465bf5 WIP simplified and aligned globals
by storing deorphaned in the globals tag itself
2018-08-03 21:07:04 -05:00
Christopher Haster
597249eea0 WIP Added files to dir list and squished the lists together
Now with better tracking of changes to long lasting metadata pairs
2018-08-03 21:06:59 -05:00
Christopher Haster
67cf4e10f4 WIP fixed several TODOs 2018-08-01 06:43:46 -05:00
Christopher Haster
14469059bc WIP Removed redundant lfs_scan 2018-08-01 01:05:04 -05:00
Christopher Haster
3bb37d4aaf WIP Reimplemented big-endian support on top of new structures 2018-08-01 18:50:54 -05:00
Christopher Haster
a703859615 WIP added deorphan bit to globals 2018-07-31 19:41:12 -05:00
Christopher Haster
10f063571a WIP Fixed ENOSPC issues with zero-granularity blocks 2018-07-30 21:12:00 -05:00
Christopher Haster
7f41aa05a9 WIP Cleaned up file config usage 2018-07-30 16:19:09 -05:00
Christopher Haster
b8290e306c WIP removed lfs parameter to lfs traverse 2018-07-30 14:41:17 -05:00
Christopher Haster
c74bcec184 WIP fixed corruption tests 2018-07-30 14:23:51 -05:00
Christopher Haster
868099836d WIP Fixed deorphan test 2018-07-30 09:10:04 -05:00
Christopher Haster
c3c15d7636 Added support for custom attributes 2 2018-07-30 08:15:58 -05:00
Christopher Haster
3a70619f41 WIP Added support for custom attributes 1 2018-07-29 15:28:41 -05:00
Christopher Haster
22e3d33ada Picked up path corner case fixes 2018-07-28 12:07:18 -05:00
Christopher Haster
439a76e3e1 WIP Refactor file/dir iteration for dir update checks 2018-07-28 09:47:57 -05:00
Christopher Haster
510cd13df9 Bumped minor version to v1.6 2018-07-27 15:59:18 -05:00
Christopher Haster
f5e0539951 Fixed issue with release script non-standard version tags 2018-07-27 15:20:00 -05:00
Christopher Haster
066448055c Moved SPDX and license info into README
This makes is a bit easier to find the description about the SPDX tags,
and fixes the issue where GitHub doesn't detect the license text.
2018-07-27 14:02:38 -05:00
Christopher Haster
d66723ccfd Merge pull request #81 from ARMmbed/simple-versioning
Simplified release process based on feedback
2018-07-27 14:02:23 -05:00
Christopher Haster
0234c77102 Simplified release process based on feedback
Previously, littlefs had mutable versions. That is, anytime a new commit
landed on master, the bot would update the most recent version to
contain the patch. The idea was that this would make sure users always
had the most recent bug fixes. Immutable snapshots could be accessed
through the git hashes.

However, at this point multiple developers have pointed out that this is
confusing, with mutable versions being non-standard and surprising.

This new release process adopts SemVer in its entirety, with
incrementing patch numbers and immutable versions.

When a new commit lands on master:
1. The major/minor version is taken from lfs.h
2. The most recent patch version is looked up on GitHub and incremented
3. A changelog is built out of the commits to the previous version
4. A new release is created on GitHub

Additionally, any commits that land while CI is still running are
coalesced together. Which means multiple PRs can land in a single
release.
2018-07-25 14:21:58 -05:00
Christopher Haster
84adead98b Merge pull request #80 from FreddieChopin/fix-memory-leaks
Fix memory leaks
2018-07-19 17:30:48 -05:00
Freddie Chopin
0422c55b81 Fix memory leaks in lfs_mount and lfs_format
Squashed:
- Change lfs_deinit() return to void to simplify error handling
- Move lfs_deinit() before lfs_init()
- Fix memory leaks in lfs_init()
- Fix memory leaks in lfs_format()
- Fix memory leaks in lfs_mount()
2018-07-19 16:54:38 -05:00
Christopher Haster
11ad3a2414 Merge pull request #76 from ARMmbed/fix-corrupt-read
Add handling for corrupt as initial state of blocks
2018-07-17 20:32:33 -05:00
Christopher Haster
16318d003f Merge pull request #58 from dpgeorge/file-open-no-malloc
Added possibility to open multiple files with LFS_NO_MALLOC enabled
2018-07-17 20:31:20 -05:00
Damien George
961fab70c3 Added file config structure and lfs_file_opencfg
The optional config structure options up the possibility of adding
file-level configuration in a backwards compatible manner.

Also adds possibility to open multiple files with LFS_NO_MALLOC
enabled thanks to dpgeorge

Also bumped minor version to v1.5
2018-07-17 18:32:18 -05:00
Christopher Haster
236d46ad17 Fixed move handling when caught in update 2018-07-17 18:31:30 -05:00
Christopher Haster
041e90a1ca Added handling for corrupt as initial state of blocks
Before this, littlefs incorrectly assumed corrupt blocks were only the result
of our own modification. This would be fine for most cases of freshly
erased storage, but for storage with block-level ECC this wasn't always
true.

Fortunately, it's quite easy for littlefs to handle this case correctly,
as long as corrupt storage always reports that it is corrupt, which for
most forms of ECC is the case unless we perform a write on the storage.

found by rojer
2018-07-16 15:33:52 -05:00
Christopher Haster
f1719e3310 WIP cleaned up commit logic 2018-07-14 04:26:05 -05:00
Christopher Haster
37cf030445 WIP cleaned up commit logic 2018-07-13 22:51:43 -05:00
Christopher Haster
7ec75a55f2 WIP additional cleanup around attributes 2018-07-13 12:42:30 -05:00
Christopher Haster
f94d233deb Merge pull request #74 from FreddieChopin/cxx-guards
Add C++ guards to public headers
2018-07-13 10:55:16 -05:00
Freddie Chopin
577d777c20 Add C++ guards to public headers
Fixes #53
Fixes #32
2018-07-13 09:34:49 +02:00
Christopher Haster
dcfe94412a WIP other minor adjustments 2018-07-12 20:43:55 -05:00
Christopher Haster
6a99f6c8fc WIP Changed internal API to return tags over pointers 2018-07-12 20:22:06 -05:00
Christopher Haster
e6d49feb4a WIP renamed tag related things 2018-07-12 19:07:56 -05:00
Christopher Haster
f6347db3da WIP moved things around in get/traverse functions 2018-07-12 18:11:18 -05:00
Christopher Haster
c72d25203c Merge pull request #73 from FreddieChopin/fix-format-specifiers
Use PRIu32 and PRIx32 format specifiers to fix warnings
2018-07-12 16:54:13 -05:00
Christopher Haster
48cc58e25c WIP Tweaked results from find-like functions 2018-07-11 07:18:30 -05:00
Christopher Haster
92d957ab40 WIP Consolidated get functions into one 2018-07-11 05:52:50 -05:00
Freddie Chopin
7e67f9324e Use PRIu32 and PRIx32 format specifiers to fix warnings
When using "%d" or "%x" with uint32_t types, arm-none-eabi-gcc reports
warnings like below:

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

In file included from lfs.c:8:
lfs_util.h:45:12: warning: format '%d' expects argument of type 'int', but argument 4 has type 'lfs_block_t' {aka 'long unsigned int'} [-Wformat=]
     printf("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
            ^~~~~~~~~~~~~~~~
lfs.c:2512:21: note: in expansion of macro 'LFS_DEBUG'
                     LFS_DEBUG("Found partial move %d %d",
                     ^~~~~~~~~
lfs.c:2512:55: note: format string is defined here
                     LFS_DEBUG("Found partial move %d %d",
                                                      ~^
                                                      %ld

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

Fix this by replacing "%d" and "%x" with `"%" PRIu32` and `"%" PRIx32`.
2018-07-11 12:32:21 +02:00
Christopher Haster
5152b973f0 WIP Moved findscan into fetch as primitive 2018-07-11 04:00:34 -05:00
Christopher Haster
5a17fa42e4 Fixed script issue with bash expansion inside makefile parameter
This was causing code sizes to be reported with several of the logging
functions still built in. A useful number, but not the minimum
achievable code size.
2018-07-10 17:18:45 -05:00
Christopher Haster
eed1eec5fd Fixed information leaks through reused caches
As a shortcut, littlefs never bother to zero any of the buffers is used.
It didn't need to because it would always write out the entirety of the
data it needed.

Unfortunately, this, combined with the extra padding used to align
buffers to the nearest prog size, would lead to uninitialized data
getting written out to disk.

This means unrelated file data could be written to different parts of
storage, or worse, information leaked from the malloc calls could be
written out to disk unnecessarily.

found by rojer
2018-07-10 11:18:46 -05:00
Christopher Haster
4a86370327 Added quality of life improvements for main.c/test.c issues
1. Added check for main.c and test.c to decide compilation target
2. Added step to remove test.c after successful test completion

The test.c file, which contains the expanded test main, is useful when
debugging why tests are failing. However, keeping the test.c file around
causes problems when a later attempt is made to compile a larger project
containing the littlefs directory.

Under (hopefully) normal operation, tests always pass. So it should be ok
to remove the test.c file after a successful test run. Hopefully this
behaviour doesn't cause too much confusion for contributors using the
tests.

On the other side of things, compiling the library with no main ends
(successfully) with the "main not found" error message. By defaulting
to lfs.a if neither test.c/main.c is avoid this in the common cases

found by armijnhemel and Sim4n6
2018-07-10 11:17:50 -05:00
Christopher Haster
43e6aadef6 WIP consolidated find/parent scanning functions 2018-07-09 17:29:40 -05:00
Christopher Haster
0276e00fac WIP reclaimed 0x3ff as invalid id 2018-07-09 14:53:55 -05:00
Christopher Haster
5b9755f141 WIP restructured tags for more flexible bit encoding 2018-07-09 14:13:31 -05:00
Christopher Haster
f5488f7192 WIP changed type to be retrieved from name not struct 2018-07-09 13:09:23 -05:00
Christopher Haster
2947006660 WIP removed old move logic 2018-07-09 11:47:04 -05:00
Christopher Haster
d5f0e236b8 WIP fixed bug with globals poisoning from dirs
Needs to be cleaned up
2018-07-08 14:21:29 -05:00
Christopher Haster
0d3350665e WIP Fixed issues around wrong ids after bad commits 2018-07-04 01:35:04 -05:00
Christopher Haster
ba4f17173f Merge pull request #57 from dpgeorge/fix-warnings
Fix some compiler warnings: shadowed variable and unused parameters
2018-07-02 12:01:34 -05:00
Damien George
51346b8bf4 Fixed shadowed variable warnings
- Fixed shadowed variable warnings in lfs_dir_find.
- Fixed unused parameter warnings when LFS_NO_MALLOC is enabled.
- Added extra warning flags to CFLAGS.
- Updated tests so they don't shadow the "size" variable for -Wshadow
2018-07-02 10:29:19 -05:00
Christopher Haster
5064cf82fd WIP switched back to deorphan for remove
Utilizing moves wouldn't have worked for rename anyways, and it requires
additional code for much less traveled code path (directory removes)
2018-07-01 22:29:42 -05:00
Christopher Haster
93a2e0bbe5 Merge pull request #62 from ARMmbed/license-bsd-3
v1.4 - Change license to BSD-3-Clause
2018-06-21 13:10:57 -05:00
Christopher Haster
ff13345e96 WIP Restructured tags for better support for global things 2018-05-29 20:08:42 -05:00
Christopher Haster
82948861d2 WIP TEMP move work 2018-05-29 12:35:23 -05:00
Christopher Haster
e9bf206c54 WIP Adopted ISDIR as internal error for root as argument 2018-05-29 01:22:09 -05:00
Christopher Haster
2223bef125 WIP Restructured metadata dir structures 2018-05-29 01:11:26 -05:00
Christopher Haster
d56f326dfb WIP better structure for dir/file lists 2018-05-29 00:51:21 -05:00
Christopher Haster
09b69c5b2a WIP name updates 2018-05-28 19:49:20 -05:00
Christopher Haster
a848b0ebbd WIP almost got move working 2018-05-28 17:46:32 -05:00
Christopher Haster
6cda0442ea WIP made basic tests pass
(format/dirs/files/seek/truncate/interspersed/paths)
2018-05-28 09:43:51 -05:00
Christopher Haster
0e0f015fbd WIP WIP yes two wips 2018-05-28 02:08:16 -05:00
Christopher Haster
fbd25ac533 WIP minor change to fix rename issue 2018-05-27 11:46:22 -05:00
Christopher Haster
de0b719b56 WIP progress so far 2018-05-27 10:15:28 -05:00
Christopher Haster
ca9e43158d WIP fixed enough things to pass basic file testing 2018-05-26 13:50:06 -05:00
Christopher Haster
cd045ed2eb WIP Moved move things into a better place 2018-05-25 19:22:19 -05:00
Christopher Haster
39267fd8b2 WIP file stuff 2018-05-22 23:57:19 -05:00
Christopher Haster
6c2c7e3f3d WIP more progressed 2018-05-22 21:58:14 -05:00
Christopher Haster
2759f012c0 WIP progressed more 2018-05-21 00:56:20 -05:00
Christopher Haster
f4d6ca5552 WIP fixed up dir find 2018-05-20 14:01:11 -05:00
Christopher Haster
57fbc52cfc WIP added wip journalling things for dirs 2018-05-19 18:25:47 -05:00
Christopher Haster
c8323112ee WIP Added tests over entries + attributes 2018-04-16 02:37:32 -05:00
Christopher Haster
7d3c2be49a WIP Fixed big-endian support 2018-04-11 01:29:59 -05:00
Christopher Haster
0e4eb788fe WIP added test config for no inline files 2018-04-11 01:29:59 -05:00
Christopher Haster
6af43aec5b Added lfs_fs_size function for finding a count of used blocks
This has existed for some time in the form of the lfs_traverse
function, however lfs_traverse is relatively unconventional and
has proven to not have been the most intuitive for users.
2018-04-11 01:29:59 -05:00
Christopher Haster
9af404db09 WIP added file/fs set/get attr implementations 2018-04-11 01:29:59 -05:00
Christopher Haster
0347416b89 WIP simplified attribute handling a bit to better match commit regions 2018-04-11 01:29:59 -05:00
Christopher Haster
eb70143469 WIP Clumsy setattrs/getattrs 2018-04-11 01:29:59 -05:00
Christopher Haster
2aee22aa49 WIP custom attributes 2018-04-11 01:29:59 -05:00
Christopher Haster
a82ea60658 WIP added some comments 2018-04-11 01:29:59 -05:00
Christopher Haster
7c0f32dc0b WIP Bumped versions 2018-04-11 01:29:59 -05:00
Christopher Haster
e48a2c488b WIP cleaned up TODOs 2018-04-11 01:29:59 -05:00
Christopher Haster
f37fa75d66 WIP added support for inline files up to 1023 bytes 2018-04-11 01:29:59 -05:00
Christopher Haster
bba71b23f4 WIP Added limits on name/attrs/inline sizes 2018-04-11 01:29:59 -05:00
Christopher Haster
e4a35b78e7 WIP Refactored lfs_dir_set function to umbrella append/update/remove 2018-04-11 01:29:59 -05:00
Christopher Haster
b56d82ff34 WIP Added lfs_dir_get 2018-04-11 01:29:59 -05:00
Christopher Haster
ab9750f5ed WIP moved superblock to entry append 2018-04-11 01:29:59 -05:00
Christopher Haster
2f32222914 WIP fixed bugs 2018-04-11 01:29:59 -05:00
Christopher Haster
7ad2d58ed0 WIP Fixed issue with modifying dir after append in update 2018-04-11 01:29:59 -05:00
Christopher Haster
689159e31d WIP Better implementation of inline files, now with overflowing 2018-04-11 01:29:59 -05:00
Christopher Haster
9a97a97e4c WIP moved asserts out 2018-04-11 01:29:59 -05:00
Christopher Haster
345f7f3235 WIP added hacky taped on inline files 2018-04-11 01:29:59 -05:00
Christopher Haster
a418c2068d WIP Fixed big-endian support 2018-04-11 01:29:59 -05:00
Christopher Haster
d7ed7a41e9 WIP added entry size field 2018-04-11 01:29:59 -05:00
Christopher Haster
960e152261 WIP separated dir_remove for two types of arguments 2018-04-11 01:29:59 -05:00
Christopher Haster
28a5a27bb9 WIP minor improvement to from-memory commits 2018-04-11 01:29:59 -05:00
Christopher Haster
72475f64f6 WIP Allowed taking advantage of empty space earlier in dir search 2018-04-11 01:29:59 -05:00
Christopher Haster
8773d7c81f WIP added callbacks for stuff 2018-04-11 01:29:59 -05:00
Christopher Haster
d636299daf WIP Moved entry tag updates out 2018-04-11 01:29:59 -05:00
Christopher Haster
2d6a37f775 WIP Naive implementation of resizable entries 2018-04-11 01:29:59 -05:00
Christopher Haster
f58408c974 WIP something something flexible updates 2018-04-11 01:29:59 -05:00
Christopher Haster
e1f05ee046 WIP adopted lisp-like dsl for more flexibility 2018-04-11 01:29:59 -05:00
Christopher Haster
f54ad304fc WIP Changed commit DSL to support disk->disk copies 2018-04-11 01:29:59 -05:00
Christopher Haster
2a738463b3 Separated type/struct fields in dir entries
The separation of data-structure vs entry type has been implicit for a
while now, and even taken advantage of to simplify the traverse logic.
2018-04-11 01:29:59 -05:00
25 changed files with 4490 additions and 1692 deletions

View File

@@ -18,16 +18,17 @@ script:
- make test QUIET=1
# run tests with a few different configurations
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_PROG_SIZE=1"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=4"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16"
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048"
- make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
- make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS"
# compile and find the code size with the smallest configuration
- make clean size
OBJ="$(ls lfs*.o | tr '\n' ' ')"
CFLAGS+="-DLFS_NO{ASSERT,DEBUG,WARN,ERROR}"
CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
| tee sizes
# update status if we succeeded, compare with master if possible
@@ -102,7 +103,7 @@ jobs:
- NAME=littlefs-fuse
install:
- sudo apt-get install libfuse-dev
- git clone --depth 1 https://github.com/geky/littlefs-fuse
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha
- fusermount -V
- gcc --version
before_script:
@@ -134,53 +135,44 @@ jobs:
- STAGE=deploy
- NAME=deploy
script:
# Update tag for version defined in lfs.h
# Find version defined in lfs.h
- LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3)
- LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16)))
- LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >> 0)))
- LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR"
- echo "littlefs version $LFS_VERSION"
# Grab latests patch from repo tags, default to 0
- LFS_VERSION_PATCH=$(curl -f -u "$GEKY_BOT_RELEASES"
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs
| jq 'map(.ref | match(
"refs/tags/v'"$LFS_VERSION_MAJOR"'\\.'"$LFS_VERSION_MINOR"'\\.(.*)$")
.captures[].string | tonumber + 1) | max // 0')
# We have our new version
- LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
- echo "VERSION $LFS_VERSION"
- |
curl -u $GEKY_BOT_RELEASES -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
-d "{
\"ref\": \"refs/tags/$LFS_VERSION\",
\"sha\": \"$TRAVIS_COMMIT\"
}"
- |
curl -f -u $GEKY_BOT_RELEASES -X PATCH \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/$LFS_VERSION \
-d "{
\"sha\": \"$TRAVIS_COMMIT\"
}"
# Create release notes from commits
- LFS_PREV_VERSION="v$LFS_VERSION_MAJOR.$(($LFS_VERSION_MINOR-1))"
- |
if [ $(git tag -l "$LFS_PREV_VERSION") ]
# Check that we're the most recent commit
CURRENT_COMMIT=$(curl -f -u "$GEKY_BOT_RELEASES" \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/commits/master \
| jq -re '.sha')
if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ]
then
curl -u $GEKY_BOT_RELEASES -X POST \
# Build release notes
PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
if [ ! -z "$PREV" ]
then
echo "PREV $PREV"
CHANGES=$'### Changes\n\n'$( \
git log --oneline $PREV.. --grep='^Merge' --invert-grep)
printf "CHANGES\n%s\n\n" "$CHANGES"
fi
# Create the release
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
-d "{
\"tag_name\": \"$LFS_VERSION\",
\"name\": \"$LFS_VERSION\"
\"target_commitish\": \"$TRAVIS_COMMIT\",
\"name\": \"${LFS_VERSION%.0}\",
\"body\": $(jq -sR '.' <<< "$CHANGES")
}"
RELEASE=$(
curl -f -u $GEKY_BOT_RELEASES \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/tags/$LFS_VERSION
)
CHANGES=$(
git log --oneline $LFS_PREV_VERSION.. --grep='^Merge' --invert-grep
)
curl -f -u $GEKY_BOT_RELEASES -X PATCH \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/$(
jq -r '.id' <<< "$RELEASE"
) \
-d "$(
jq -s '{
"body": ((.[0] // "" | sub("(?<=\n)#+ Changes.*"; ""; "mi"))
+ "### Changes\n\n" + .[1])
}' <(jq '.body' <<< "$RELEASE") <(jq -sR '.' <<< "$CHANGES")
)"
fi
# Manage statuses

View File

@@ -22,15 +22,3 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
*Note*:
Individual files contain the following tag instead of the full license text.
SPDX-License-Identifier: BSD-3-Clause
This enables machine processing of license information based on the SPDX
License Identifiers that are here available: http://spdx.org/licenses/

View File

@@ -1,4 +1,7 @@
TARGET = lfs
TARGET = lfs.a
ifneq ($(wildcard test.c main.c),)
override TARGET = lfs
endif
CC ?= gcc
AR ?= ar
@@ -22,7 +25,7 @@ ifdef WORD
override CFLAGS += -m$(WORD)
endif
override CFLAGS += -I.
override CFLAGS += -std=c99 -Wall -pedantic
override CFLAGS += -std=c99 -Wall -pedantic -Wshadow -Wunused-parameter
all: $(TARGET)
@@ -34,8 +37,11 @@ size: $(OBJ)
.SUFFIXES:
test: test_format test_dirs test_files test_seek test_truncate \
test_interspersed test_alloc test_paths test_orphan test_move test_corrupt
test_entries test_interspersed test_alloc test_paths test_attrs \
test_move test_orphan test_corrupt
@rm test.c
test_%: tests/test_%.sh
ifdef QUIET
@./$< | sed -n '/^[-=]/p'
else
@@ -44,7 +50,7 @@ endif
-include $(DEP)
$(TARGET): $(OBJ)
lfs: $(OBJ)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
%.a: $(OBJ)

View File

@@ -146,6 +146,19 @@ The tests assume a Linux environment and can be started with make:
make test
```
## License
The littlefs is provided under the [BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html)
license. See [LICENSE.md](LICENSE.md) for more information. Contributions to
this project are accepted under the same license.
Individual files contain the following tag instead of the full license text.
SPDX-License-Identifier: BSD-3-Clause
This enables machine processing of license information based on the SPDX
License Identifiers that are here available: http://spdx.org/licenses/
## Related projects
[Mbed OS](https://github.com/ARMmbed/mbed-os/tree/master/features/filesystem/littlefs) -

View File

@@ -16,6 +16,41 @@
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
#include <inttypes.h>
// Emulated block device utils
static inline void lfs_emubd_tole32(lfs_emubd_t *emu) {
emu->cfg.read_size = lfs_tole32(emu->cfg.read_size);
emu->cfg.prog_size = lfs_tole32(emu->cfg.prog_size);
emu->cfg.block_size = lfs_tole32(emu->cfg.block_size);
emu->cfg.block_count = lfs_tole32(emu->cfg.block_count);
emu->stats.read_count = lfs_tole32(emu->stats.read_count);
emu->stats.prog_count = lfs_tole32(emu->stats.prog_count);
emu->stats.erase_count = lfs_tole32(emu->stats.erase_count);
for (int i = 0; i < sizeof(emu->history.blocks) /
sizeof(emu->history.blocks[0]); i++) {
emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]);
}
}
static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) {
emu->cfg.read_size = lfs_fromle32(emu->cfg.read_size);
emu->cfg.prog_size = lfs_fromle32(emu->cfg.prog_size);
emu->cfg.block_size = lfs_fromle32(emu->cfg.block_size);
emu->cfg.block_count = lfs_fromle32(emu->cfg.block_count);
emu->stats.read_count = lfs_fromle32(emu->stats.read_count);
emu->stats.prog_count = lfs_fromle32(emu->stats.prog_count);
emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count);
for (int i = 0; i < sizeof(emu->history.blocks) /
sizeof(emu->history.blocks[0]); i++) {
emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]);
}
}
// Block device emulated on existing filesystem
@@ -45,20 +80,39 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
}
// Load stats to continue incrementing
snprintf(emu->child, LFS_NAME_MAX, "stats");
snprintf(emu->child, LFS_NAME_MAX, ".stats");
FILE *f = fopen(emu->path, "r");
if (!f) {
return -errno;
memset(&emu->stats, 0, sizeof(emu->stats));
} else {
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
if (res < 1) {
return -errno;
}
// Load history
snprintf(emu->child, LFS_NAME_MAX, ".history");
f = fopen(emu->path, "r");
if (!f) {
memset(&emu->history, 0, sizeof(emu->history));
} else {
size_t res = fread(&emu->history, sizeof(emu->history), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
err = fclose(f);
if (err) {
return -errno;
}
}
return 0;
@@ -85,7 +139,7 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
memset(data, 0, size);
// Read data
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block);
FILE *f = fopen(emu->path, "rb");
if (!f && errno != ENOENT) {
@@ -124,7 +178,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
assert(block < cfg->block_count);
// Program data
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block);
FILE *f = fopen(emu->path, "r+b");
if (!f) {
@@ -160,6 +214,13 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
return -errno;
}
// update history and stats
if (block != emu->history.blocks[0]) {
memcpy(&emu->history.blocks[1], &emu->history.blocks[0],
sizeof(emu->history) - sizeof(emu->history.blocks[0]));
emu->history.blocks[0] = block;
}
emu->stats.prog_count += 1;
return 0;
}
@@ -171,7 +232,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
assert(block < cfg->block_count);
// Erase the block
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
snprintf(emu->child, LFS_NAME_MAX, "%" PRIx32, block);
struct stat st;
int err = stat(emu->path, &st);
if (err && errno != ENOENT) {
@@ -205,13 +266,15 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
lfs_emubd_t *emu = cfg->context;
// Just write out info/stats for later lookup
snprintf(emu->child, LFS_NAME_MAX, "config");
snprintf(emu->child, LFS_NAME_MAX, ".config");
FILE *f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs_emubd_tole32(emu);
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
@@ -221,13 +284,33 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
return -errno;
}
snprintf(emu->child, LFS_NAME_MAX, "stats");
snprintf(emu->child, LFS_NAME_MAX, ".stats");
f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs_emubd_tole32(emu);
res = fwrite(&emu->stats, sizeof(emu->stats), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
snprintf(emu->child, LFS_NAME_MAX, ".history");
f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs_emubd_tole32(emu);
res = fwrite(&emu->history, sizeof(emu->history), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
@@ -239,4 +322,3 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
return 0;
}

View File

@@ -10,6 +10,11 @@
#include "lfs.h"
#include "lfs_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Config options
#ifndef LFS_EMUBD_READ_SIZE
@@ -40,6 +45,10 @@ typedef struct lfs_emubd {
uint64_t erase_count;
} stats;
struct {
lfs_block_t blocks[4];
} history;
struct {
uint32_t read_size;
uint32_t prog_size;
@@ -75,4 +84,8 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
int lfs_emubd_sync(const struct lfs_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

4458
lfs.c

File diff suppressed because it is too large Load Diff

400
lfs.h
View File

@@ -10,20 +10,25 @@
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
/// Version info ///
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00010004
#define LFS_VERSION 0x00020000
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
// Version of On-disk data structures
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_DISK_VERSION 0x00010001
#define LFS_DISK_VERSION 0x00020000
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
@@ -39,51 +44,97 @@ typedef int32_t lfs_soff_t;
typedef uint32_t lfs_block_t;
// Max name size in bytes
// Maximum size of all attributes per file in bytes, may be redefined but a
// a smaller LFS_ATTR_MAX has no benefit. Stored in 12-bits and limited
// to <= 0xfff. Stored in superblock and must be respected by other
// littlefs drivers.
#ifndef LFS_ATTR_MAX
#define LFS_ATTR_MAX 0xfff
#endif
// Maximum name size in bytes, may be redefined to reduce the size of the
// info struct. Limited to <= LFS_ATTR_MAX. Stored in superblock and must
// be respected by other littlefs drivers.
#ifndef LFS_NAME_MAX
#define LFS_NAME_MAX 255
#define LFS_NAME_MAX 0xff
#endif
// Maximum inline file size in bytes. Large inline files require a larger
// cache size, but if a file can be inline it does not need its own data
// block. Limited to <= LFS_ATTR_MAX and <= cache_size. Stored in superblock
// and must be respected by other littlefs drivers.
#ifndef LFS_INLINE_MAX
#define LFS_INLINE_MAX 0xfff
#endif
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -52, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -84, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NAMETOOLONG = -36, // File name too long
};
// File types
enum lfs_type {
LFS_TYPE_REG = 0x11,
LFS_TYPE_DIR = 0x22,
LFS_TYPE_SUPERBLOCK = 0x2e,
// file types
LFS_TYPE_REG = 0x004,
LFS_TYPE_DIR = 0x005,
// internally used types
LFS_TYPE_USER = 0x100,
LFS_TYPE_SUPERBLOCK = 0x002,
LFS_TYPE_ROOT = 0x003,
LFS_TYPE_CHILD = 0x001,
LFS_TYPE_NAME = 0x000,
LFS_TYPE_DELETE = 0x060,
LFS_TYPE_STRUCT = 0x020,
LFS_TYPE_GLOBALS = 0x0e0,
LFS_TYPE_TAIL = 0x080,
LFS_TYPE_SOFTTAIL = 0x080,
LFS_TYPE_HARDTAIL = 0x081,
LFS_TYPE_CRC = 0x0a0,
LFS_TYPE_DIRSTRUCT = 0x020,
LFS_TYPE_INLINESTRUCT = 0x021,
LFS_TYPE_CTZSTRUCT = 0x022,
// internal chip sources
LFS_FROM_REGION = 0x000,
LFS_FROM_DISK = 0x200,
LFS_FROM_MOVE = 0x040,
LFS_FROM_COMPACT = 0x041,
LFS_FROM_SPLIT = 0x042,
LFS_FROM_ATTRS = 0x043,
};
// File open flags
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
LFS_O_RDONLY = 1, // Open a file as read only
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
// internally used flags
LFS_F_DIRTY = 0x10000, // File does not match storage
LFS_F_WRITING = 0x20000, // File has been written since last flush
LFS_F_READING = 0x40000, // File has been read since last flush
LFS_F_ERRED = 0x80000, // An error occured during write
LFS_F_DIRTY = 0x010000, // File does not match storage
LFS_F_WRITING = 0x020000, // File has been written since last flush
LFS_F_READING = 0x040000, // File has been read since last flush
LFS_F_ERRED = 0x080000, // An error occured during write
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
};
// File seek flags
@@ -121,26 +172,34 @@ struct lfs_config {
// are propogated to the user.
int (*sync)(const struct lfs_config *c);
// Minimum size of a block read. This determines the size of read buffers.
// This may be larger than the physical read size to improve performance
// by caching more of the block device.
// Minimum size of a block read. All read operations will be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of a block program. This determines the size of program
// buffers. This may be larger than the physical program size to improve
// performance by caching more of the block device.
// Must be a multiple of the read size.
// Minimum size of a block program. All program operations will be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, this should be
// kept small as each file currently takes up an entire block.
// Must be a multiple of the program size.
// Must be a multiple of the read, program, and cache sizes.
lfs_size_t block_size;
// Number of erasable blocks on the device.
lfs_size_t block_count;
// Number of erase cycles before we should move data to another block.
// May be zero to never move data, in which case no block-level
// wear-leveling is performed.
uint32_t block_cycles;
// Size of block caches. Each cache buffers a portion of a block in RAM.
// This determines the size of the read cache, the program cache, and a
// cache per file. Larger caches can improve performance by storing more
// data. Must be a multiple of the read and program sizes.
lfs_size_t cache_size;
// Number of blocks to lookahead during block allocation. A larger
// lookahead reduces the number of passes required to allocate a block.
// The lookahead buffer requires only 1 bit per block so it can be quite
@@ -157,11 +216,25 @@ struct lfs_config {
// lookahead block.
void *lookahead_buffer;
// Optional, statically allocated buffer for files. Must be program sized.
// If enabled, only one file may be opened at a time.
void *file_buffer;
};
// Optional upper limit on file attributes in bytes. No downside for larger
// attributes size but must be less than LFS_ATTR_MAX. Defaults to
// LFS_ATTR_MAX when zero.Stored in superblock and must be respected by
// other littlefs drivers.
lfs_size_t attr_max;
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers.
lfs_size_t name_max;
// Optional upper limit on inlined files in bytes. Large inline files
// require a larger cache size, but if a file can be inlined it does not
// need its own data block. Must be smaller than cache_size and less than
// LFS_INLINE_MAX. Defaults to min(LFS_INLINE_MAX, read_size) when zero.
// Stored in superblock and must be respected by other littlefs drivers.
lfs_size_t inline_max;
};
// File info structure
struct lfs_info {
@@ -175,76 +248,130 @@ struct lfs_info {
char name[LFS_NAME_MAX+1];
};
// Custom attribute structure
struct lfs_attr {
// 8-bit type of attribute, provided by user and used to
// identify the attribute
uint8_t type;
// Pointer to buffer containing the attribute
void *buffer;
// Size of attribute in bytes, limited to LFS_ATTR_MAX
lfs_size_t size;
// Pointer to next attribute in linked list
struct lfs_attr *next;
};
// Optional configuration provided during lfs_file_opencfg
struct lfs_file_config {
// Optional, statically allocated buffer for files. Must be program sized.
// If NULL, malloc will be used by default.
void *buffer;
// Optional, linked list of custom attributes related to the file. If the
// file is opened with read access, the attributes will be read from
// during the open call. If the file is opened with write access, the
// attributes will be written to disk every file sync or close. This
// write occurs atomically with update to the file's contents.
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller
// than the buffer, it will be padded with zeros. If the stored attribute
// is larger, then it will be silently truncated. If the attribute is not
// found, it will be created implicitly.
struct lfs_attr *attrs;
};
/// littlefs data structures ///
typedef struct lfs_entry {
lfs_off_t off;
struct lfs_disk_entry {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
union {
struct {
lfs_block_t head;
lfs_size_t size;
} file;
lfs_block_t dir[2];
} u;
} d;
} lfs_entry_t;
typedef struct lfs_mattr {
int32_t tag;
const void *buffer;
const struct lfs_mattr *next;
} lfs_mattr_t;
typedef struct lfs_cache {
lfs_block_t block;
lfs_off_t off;
lfs_size_t size;
uint8_t *buffer;
} lfs_cache_t;
typedef union lfs_global {
uint32_t u32[3];
struct {
lfs_block_t movepair[2];
uint16_t moveid;
uint8_t deorphaned;
} l;
struct {
lfs_block_t movepair[2];
uint16_t moveid;
uint8_t orphans;
} g;
} lfs_global_t;
typedef struct lfs_mdir {
lfs_block_t pair[2];
uint32_t rev;
uint32_t etag;
lfs_off_t off;
uint16_t count;
bool erased;
bool split;
lfs_block_t tail[2];
lfs_global_t locals;
} lfs_mdir_t;
typedef struct lfs_mlist {
struct lfs_mlist *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
} lfs_mlist_t;
typedef struct lfs_dir {
struct lfs_dir *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
lfs_off_t pos;
lfs_block_t head[2];
} lfs_dir_t;
typedef struct lfs_file {
struct lfs_file *next;
lfs_block_t pair[2];
lfs_off_t poff;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
lfs_block_t head;
lfs_size_t size;
struct lfs_ctz {
lfs_block_t head;
lfs_size_t size;
} ctz;
uint32_t flags;
lfs_off_t pos;
lfs_block_t block;
lfs_off_t off;
lfs_cache_t cache;
const struct lfs_file_config *cfg;
} lfs_file_t;
typedef struct lfs_dir {
struct lfs_dir *next;
lfs_block_t pair[2];
lfs_off_t off;
lfs_block_t head[2];
lfs_off_t pos;
struct lfs_disk_dir {
uint32_t rev;
lfs_size_t size;
lfs_block_t tail[2];
} d;
} lfs_dir_t;
typedef struct lfs_superblock {
lfs_off_t off;
char magic[8];
uint32_t version;
struct lfs_disk_superblock {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
lfs_block_t root[2];
uint32_t block_size;
uint32_t block_count;
uint32_t version;
char magic[8];
} d;
lfs_size_t block_size;
lfs_size_t block_count;
lfs_size_t attr_max;
lfs_size_t name_max;
lfs_size_t inline_max;
} lfs_superblock_t;
typedef struct lfs_free {
@@ -257,17 +384,23 @@ typedef struct lfs_free {
// The littlefs type
typedef struct lfs {
const struct lfs_config *cfg;
lfs_block_t root[2];
lfs_file_t *files;
lfs_dir_t *dirs;
lfs_cache_t rcache;
lfs_cache_t pcache;
lfs_block_t root[2];
lfs_mlist_t *mlist;
uint32_t seed;
lfs_global_t globals;
lfs_global_t locals;
lfs_free_t free;
bool deorphaned;
const struct lfs_config *cfg;
lfs_size_t block_size;
lfs_size_t block_count;
lfs_size_t attr_max;
lfs_size_t name_max;
lfs_size_t inline_max;
} lfs_t;
@@ -276,7 +409,8 @@ typedef struct lfs {
// Format a block device with the littlefs
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted.
// object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
@@ -285,7 +419,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config);
//
// Requires a littlefs object and config struct. Multiple filesystems
// may be mounted simultaneously with multiple littlefs objects. Both
// lfs and config must be allocated while mounted.
// lfs and config must be allocated while mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
@@ -318,19 +453,57 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
// Returns a negative error code on failure.
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
// Get a custom attribute
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
// the buffer, it will be padded with zeros. If the stored attribute is larger,
// then it will be silently truncated.
//
// Returns the size of the attribute, or a negative error code on failure.
// Note, the returned size is the size of the attribute on disk, irrespective
// of the size of the buffer. This can be used to dynamically allocate a buffer
// or check for existance.
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
uint8_t type, void *buffer, lfs_size_t size);
// Set custom attributes
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
// implicitly created, and setting the size of an attribute to zero deletes
// the attribute.
//
// Returns a negative error code on failure.
int lfs_setattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size);
/// File operations ///
// Open a file
//
// The mode that the file is opened in is determined
// by the flags, which are values from the enum lfs_open_flags
// that are bitwise-ored together.
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs_open_flags that are bitwise-ored together.
//
// Returns a negative error code on failure.
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags);
// Open a file with extra configuration
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs_open_flags that are bitwise-ored together.
//
// The config struct provides additional config options per file as described
// above. The config struct must be allocated while the file is open, and the
// config struct must be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags,
const struct lfs_file_config *config);
// Close a file
//
// Any pending writes are written out to storage as though
@@ -439,7 +612,15 @@ lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
/// Miscellaneous littlefs specific operations ///
/// Filesystem-level filesystem operations
// Finds the current size of the filesystem
//
// Note: Result is best effort. If files share COW structures, the returned
// size may be larger than the filesystem actually is.
//
// Returns the number of allocated blocks, or a negative error code on failure.
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
// Traverse through all blocks in use by the filesystem
//
@@ -448,16 +629,11 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
// blocks are in use or how much of the storage is available.
//
// Returns a negative error code on failure.
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
// Prunes any recoverable errors that may have occured in the filesystem
//
// Not needed to be called by user unless an operation is interrupted
// but the filesystem is still mounted. This is already called on first
// allocation.
//
// Returns a negative error code on failure.
int lfs_deorphan(lfs_t *lfs);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -11,7 +11,7 @@
// Software CRC implementation with small lookup table
void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) {
uint32_t lfs_crc32(uint32_t crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
@@ -22,9 +22,11 @@ void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) {
const uint8_t *data = buffer;
for (size_t i = 0; i < size; i++) {
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf];
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf];
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
}
return crc;
}

View File

@@ -11,8 +11,8 @@
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
//
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
// provided by the config file. To start I would suggest copying lfs_util.h and
// modifying as needed.
// provided by the config file. To start, I would suggest copying lfs_util.h
// and modifying as needed.
#ifdef LFS_CONFIG
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
#define LFS_STRINGIZE2(x) #x
@@ -23,6 +23,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#ifndef LFS_NO_MALLOC
#include <stdlib.h>
@@ -34,6 +35,11 @@
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
// Macros, may be replaced by system specific wrappers. Arguments to these
// macros must not have side-effects as the macros can be removed for a smaller
@@ -82,6 +88,15 @@ static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
// Align to nearest multiple of a size
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
return a - (a % alignment);
}
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return lfs_aligndown(a + alignment-1, alignment);
}
// Find the next smallest power of 2 less than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
@@ -125,7 +140,7 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b);
}
// Convert from 32-bit little-endian to native order
// Convert between 32-bit little-endian and native order
static inline uint32_t lfs_fromle32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
@@ -145,19 +160,41 @@ static inline uint32_t lfs_fromle32(uint32_t a) {
#endif
}
// Convert to 32-bit little-endian from native order
static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a);
}
// Convert between 16-bit little-endian and native order
static inline uint16_t lfs_fromle16(uint16_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap16(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8);
#endif
}
static inline uint16_t lfs_tole16(uint16_t a) {
return lfs_fromle16(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
uint32_t lfs_crc32(uint32_t crc, const void *buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs
static inline void *lfs_malloc(size_t size) {
#ifndef LFS_NO_MALLOC
return malloc(size);
#else
(void)size;
return NULL;
#endif
}
@@ -166,9 +203,15 @@ static inline void *lfs_malloc(size_t size) {
static inline void lfs_free(void *p) {
#ifndef LFS_NO_MALLOC
free(p);
#else
(void)p;
#endif
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
#endif

43
tests/corrupt.py Executable file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python
import struct
import sys
import os
import argparse
def corrupt(block):
with open(block, 'r+b') as file:
# skip rev
file.read(4)
# go to last commit
tag = 0xffffffff
while True:
try:
ntag, = struct.unpack('<I', file.read(4))
except struct.error:
break
tag ^= ntag
file.seek(tag & 0xfff, os.SEEK_CUR)
# lob off last 3 bytes
file.seek(-((tag & 0xfff) + 3), os.SEEK_CUR)
file.truncate()
def main(args):
if args.n or not args.blocks:
with open('blocks/.history', 'rb') as file:
for i in range(int(args.n or 1)):
last, = struct.unpack('<I', file.read(4))
args.blocks.append('blocks/%x' % last)
for block in args.blocks:
print 'corrupting %s' % block
corrupt(block)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-n')
parser.add_argument('blocks', nargs='*')
main(parser.parse_args())

107
tests/debug.py Executable file
View File

@@ -0,0 +1,107 @@
#!/usr/bin/env python2
import struct
import binascii
TYPES = {
(0x1ff, 0x004): 'reg',
(0x1ff, 0x005): 'dir',
(0x1ff, 0x002): 'superblock',
(0x1ff, 0x003): 'root',
(0x1ff, 0x001): 'child',
(0x1ff, 0x060): 'delete',
(0x1f0, 0x0e0): 'globals',
(0x1ff, 0x080): 'tail soft',
(0x1ff, 0x081): 'tail hard',
(0x1f0, 0x0a0): 'crc',
(0x1ff, 0x020): 'struct dir',
(0x1ff, 0x021): 'struct inline',
(0x1ff, 0x022): 'struct ctz',
(0x100, 0x100): 'attr',
}
def typeof(type):
for prefix in range(9):
mask = 0x1ff & ~((1 << prefix)-1)
if (mask, type & mask) in TYPES:
return TYPES[mask, type & mask] + (
' [%0*x]' % (prefix/4, type & ((1 << prefix)-1))
if prefix else '')
else:
return '[%02x]' % type
def main(*blocks):
# find most recent block
file = None
rev = None
crc = None
versions = []
for block in blocks:
try:
nfile = open(block, 'rb')
ndata = nfile.read(4)
ncrc = binascii.crc32(ndata)
nrev, = struct.unpack('<I', ndata)
assert rev != nrev
if not file or ((rev - nrev) & 0x80000000):
file = nfile
rev = nrev
crc = ncrc
versions.append((nrev, '%s (rev %d)' % (block, nrev)))
except (IOError, struct.error):
pass
if not file:
print 'Bad metadata pair {%s}' % ', '.join(blocks)
return 1
print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True))
# go through each tag, print useful information
print "%-4s %-8s %-14s %3s %3s %s" % (
'off', 'tag', 'type', 'id', 'len', 'dump')
tag = 0xffffffff
off = 4
while True:
try:
data = file.read(4)
crc = binascii.crc32(data, crc)
ntag, = struct.unpack('<I', data)
except struct.error:
break
tag ^= ntag
off += 4
type = (tag & 0x7fc00000) >> 22
id = (tag & 0x003ff000) >> 12
size = (tag & 0x00000fff) >> 0
iscrc = (type & 0x1f0) == 0x0f0
data = file.read(size)
if iscrc:
crc = binascii.crc32(data[:4], crc)
else:
crc = binascii.crc32(data, crc)
print '%04x: %08x %-14s %3s %3d %-23s %-8s' % (
off, tag,
typeof(type) + (' bad!' if iscrc and ~crc else ''),
id if id != 0x3ff else '.', size,
' '.join('%02x' % ord(c) for c in data[:8]),
''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8]))
off += tag & 0xfff
if iscrc:
crc = 0
tag ^= (type & 1) << 31
return 0
if __name__ == "__main__":
import sys
sys.exit(main(*sys.argv[1:]))

View File

@@ -7,7 +7,7 @@ import os
import re
def main():
with open('blocks/config') as file:
with open('blocks/.config') as file:
s = struct.unpack('<LLLL', file.read())
print 'read_size: %d' % s[0]
print 'prog_size: %d' % s[1]
@@ -18,7 +18,7 @@ def main():
os.path.getsize(os.path.join('blocks', f))
for f in os.listdir('blocks') if re.match('\d+', f))
with open('blocks/stats') as file:
with open('blocks/.stats') as file:
s = struct.unpack('<QQQ', file.read())
print 'read_count: %d' % s[0]
print 'prog_count: %d' % s[1]

View File

@@ -66,7 +66,7 @@ uintmax_t test;
#endif
#ifndef LFS_PROG_SIZE
#define LFS_PROG_SIZE 16
#define LFS_PROG_SIZE LFS_READ_SIZE
#endif
#ifndef LFS_BLOCK_SIZE
@@ -77,6 +77,14 @@ uintmax_t test;
#define LFS_BLOCK_COUNT 1024
#endif
#ifndef LFS_BLOCK_CYCLES
#define LFS_BLOCK_CYCLES 1024
#endif
#ifndef LFS_CACHE_SIZE
#define LFS_CACHE_SIZE 64
#endif
#ifndef LFS_LOOKAHEAD
#define LFS_LOOKAHEAD 128
#endif
@@ -88,11 +96,13 @@ const struct lfs_config cfg = {{
.erase = &lfs_emubd_erase,
.sync = &lfs_emubd_sync,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.lookahead = LFS_LOOKAHEAD,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.block_cycles = LFS_BLOCK_CYCLES,
.cache_size = LFS_CACHE_SIZE,
.lookahead = LFS_LOOKAHEAD,
}};

View File

@@ -10,7 +10,7 @@ def generate(test):
template = file.read()
lines = []
for line in re.split('(?<=[;{}])\n', test.read()):
for line in re.split('(?<=(?:.;| [{}]))\n', test.read()):
match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE)
if match:
tab, test, expect = match.groups()

View File

@@ -194,55 +194,98 @@ tests/test.py << TEST
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "exhaustion", size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dir exhaustion test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
// find out max file size
lfs_mkdir(&lfs, "exhaustiondir") => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-6)*(cfg.block_size-8);
i += size) {
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
int count = 0;
int err;
while (true) {
err = lfs_file_write(&lfs, &file[0], buffer, size);
if (err < 0) {
break;
}
count += 1;
}
err => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
// see if dir fits with max file size
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count; i++) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_size-8);
i += size) {
// see if dir fits with > max file size
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count+1; i++) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Chained dir exhaustion test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
// find out max file size
lfs_mkdir(&lfs, "exhaustiondir") => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs_mkdir(&lfs, (char*)buffer) => 0;
}
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-24)*(cfg.block_size-8);
i += size) {
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
int count = 0;
int err;
while (true) {
err = lfs_file_write(&lfs, &file[0], buffer, size);
if (err < 0) {
break;
}
count += 1;
}
err => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs_remove(&lfs, (char*)buffer) => 0;
}
// see that chained dir fails
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count+1; i++) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
@@ -251,19 +294,25 @@ tests/test.py << TEST
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-26)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
// shorten file to try a second chained dir
while (true) {
err = lfs_mkdir(&lfs, "exhaustiondir");
if (err != LFS_ERR_NOSPC) {
break;
}
lfs_ssize_t filesize = lfs_file_size(&lfs, &file[0]);
filesize > 0 => true;
lfs_file_truncate(&lfs, &file[0], filesize - size) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
}
err => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Split dir test ---"
@@ -274,28 +323,38 @@ TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
// create one block whole for half a directory
// create one block hole for half a directory
lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file[0], (void*)"hi", 2) => 2;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file[0], buffer, cfg.block_size) => cfg.block_size;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-6)*(cfg.block_size-8);
i < (cfg.block_count-4)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
// remount to force reset of lookahead
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
// open hole
lfs_remove(&lfs, "bump") => 0;
lfs_mkdir(&lfs, "splitdir") => 0;
lfs_file_open(&lfs, &file[0], "splitdir/bump",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file[0], buffer, size) => LFS_ERR_NOSPC;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file[0], buffer, 2*cfg.block_size) => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
@@ -314,7 +373,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -325,7 +384,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8);
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -333,7 +392,6 @@ tests/test.py << TEST
// remount to force reset of lookahead
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
// rewrite one file
@@ -343,7 +401,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -357,7 +415,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8);
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -377,7 +435,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -388,7 +446,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8);
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -396,7 +454,6 @@ tests/test.py << TEST
// remount to force reset of lookahead
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
// rewrite one file with a hole of one block
@@ -406,7 +463,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2 - 1)*(cfg.block_size-8);
i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}

262
tests/test_attrs.sh Executable file
View File

@@ -0,0 +1,262 @@
#!/bin/bash
set -eu
echo "=== Attr tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_open(&lfs, &file[0], "hello/hello",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file[0], "hello", strlen("hello"))
=> strlen("hello");
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Set/get attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_setattr(&lfs, "hello", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "hello", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "", 0) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Set/get root attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_setattr(&lfs, "/", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "/", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'B', "", 0) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Set/get file attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
struct lfs_attr a1 = {'A', buffer, 4};
struct lfs_attr b1 = {'B', buffer+4, 6, &a1};
struct lfs_attr c1 = {'C', buffer+10, 5, &b1};
struct lfs_file_config cfg1 = {.attrs = &c1};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer, "aaaa", 4);
memcpy(buffer+4, "bbbbbb", 6);
memcpy(buffer+10, "ccccc", 5);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
b1.size = 0;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "dddddd", 6);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
b1.size = 3;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "eee", 3);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
a1.size = LFS_ATTR_MAX+1;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1)
=> LFS_ERR_NOSPC;
struct lfs_attr a2 = {'A', buffer, 4};
struct lfs_attr b2 = {'B', buffer+4, 9, &a2};
struct lfs_attr c2 = {'C', buffer+13, 5, &b2};
struct lfs_file_config cfg2 = {.attrs = &c2};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDWR, &cfg2) => 0;
memcpy(buffer+4, "fffffffff", 9);
lfs_file_close(&lfs, &file[0]) => 0;
a1.size = 4;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
struct lfs_attr a2 = {'A', buffer, 4};
struct lfs_attr b2 = {'B', buffer+4, 9, &a2};
struct lfs_attr c2 = {'C', buffer+13, 5, &b2};
struct lfs_file_config cfg2 = {.attrs = &c2};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Deferred file attributes ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
struct lfs_attr a1 = {'B', "gggg", 4};
struct lfs_attr b1 = {'C', "", 0, &a1};
struct lfs_attr c1 = {'D', "hhhh", 4, &b1};
struct lfs_file_config cfg1 = {.attrs = &c1};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 9;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 5;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 0;
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]) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 4;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 0;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4;
memcmp(buffer, "gggg\0\0\0\0\0", 9) => 0;
memcmp(buffer+9, "\0\0\0\0\0\0\0\0\0", 9) => 0;
memcmp(buffer+18, "hhhh\0\0\0\0\0", 9) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -71,24 +71,25 @@ echo "--- Sanity check ---"
rm -rf blocks
lfs_mktree
lfs_chktree
BLOCKS="$(ls blocks | grep -vw '[01]')"
echo "--- Block corruption ---"
for i in {0..33}
for b in $BLOCKS
do
rm -rf blocks
mkdir blocks
ln -s /dev/zero blocks/$(printf '%x' $i)
ln -s /dev/zero blocks/$b
lfs_mktree
lfs_chktree
done
echo "--- Block persistance ---"
for i in {0..33}
for b in $BLOCKS
do
rm -rf blocks
mkdir blocks
lfs_mktree
chmod a-w blocks/$(printf '%x' $i)
chmod a-w blocks/$b
lfs_mktree
lfs_chktree
done
@@ -96,7 +97,7 @@ done
echo "--- Big region corruption ---"
rm -rf blocks
mkdir blocks
for i in {2..255}
for i in {2..512}
do
ln -s /dev/zero blocks/$(printf '%x' $i)
done
@@ -106,7 +107,7 @@ lfs_chktree
echo "--- Alternating corruption ---"
rm -rf blocks
mkdir blocks
for i in {2..511..2}
for i in {2..1024..2}
do
ln -s /dev/zero blocks/$(printf '%x' $i)
done

221
tests/test_entries.sh Executable file
View File

@@ -0,0 +1,221 @@
#!/bin/bash
set -eu
# Note: These tests are intended for 512 byte inline size at different
# inline sizes they should still pass, but won't be testing anything
echo "=== Entry tests ==="
rm -rf blocks
function read_file {
cat << TEST
size = $2;
lfs_file_open(&lfs, &file[0], "$1", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
TEST
}
function write_file {
cat << TEST
size = $2;
lfs_file_open(&lfs, &file[0], "$1",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
TEST
}
echo "--- Entry grow test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
$(write_file "hi0" 20)
$(write_file "hi1" 20)
$(write_file "hi2" 20)
$(write_file "hi3" 20)
$(read_file "hi1" 20)
$(write_file "hi1" 200)
$(read_file "hi0" 20)
$(read_file "hi1" 200)
$(read_file "hi2" 20)
$(read_file "hi3" 20)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry shrink test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
$(write_file "hi0" 20)
$(write_file "hi1" 200)
$(write_file "hi2" 20)
$(write_file "hi3" 20)
$(read_file "hi1" 200)
$(write_file "hi1" 20)
$(read_file "hi0" 20)
$(read_file "hi1" 20)
$(read_file "hi2" 20)
$(read_file "hi3" 20)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry spill test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 200)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
$(read_file "hi0" 200)
$(read_file "hi1" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry push spill test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 20)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
$(read_file "hi1" 20)
$(write_file "hi1" 200)
$(read_file "hi0" 200)
$(read_file "hi1" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry push spill two test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 20)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
$(write_file "hi4" 200)
$(read_file "hi1" 20)
$(write_file "hi1" 200)
$(read_file "hi0" 200)
$(read_file "hi1" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
$(read_file "hi4" 200)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry drop test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 200)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
lfs_remove(&lfs, "hi1") => 0;
lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT;
$(read_file "hi0" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
lfs_remove(&lfs, "hi2") => 0;
lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT;
$(read_file "hi0" 200)
$(read_file "hi3" 200)
lfs_remove(&lfs, "hi3") => 0;
lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT;
$(read_file "hi0" 200)
lfs_remove(&lfs, "hi0") => 0;
lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Create too big ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
memset(buffer, 'm', 200);
buffer[200] = '\0';
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Resize too big ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
memset(buffer, 'm', 200);
buffer[200] = '\0';
size = 40;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
size = 40;
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -30,7 +30,7 @@ TEST
w_test() {
tests/test.py << TEST
lfs_size_t size = $1;
size = $1;
lfs_size_t chunk = 31;
srand(0);
lfs_mount(&lfs, &cfg) => 0;
@@ -50,7 +50,7 @@ TEST
r_test() {
tests/test.py << TEST
lfs_size_t size = $1;
size = $1;
lfs_size_t chunk = 29;
srand(0);
lfs_mount(&lfs, &cfg) => 0;

View File

@@ -9,14 +9,6 @@ tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Invalid superblocks ---"
ln -f -s /dev/zero blocks/0
ln -f -s /dev/zero blocks/1
tests/test.py << TEST
lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT;
TEST
rm blocks/0 blocks/1
echo "--- Basic mounting ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
@@ -26,22 +18,32 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
echo "--- Invalid mount ---"
echo "--- Invalid superblocks ---"
ln -f -s /dev/zero blocks/0
ln -f -s /dev/zero blocks/1
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC;
TEST
rm blocks/0 blocks/1
echo "--- Invalid mount ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
TEST
echo "--- Valid corrupt mount ---"
echo "--- Expanding superblock ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < 100; i++) {
lfs_mkdir(&lfs, "dummy") => 0;
lfs_remove(&lfs, "dummy") => 0;
}
lfs_unmount(&lfs) => 0;
TEST
rm blocks/0
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "dummy") => 0;
lfs_unmount(&lfs) => 0;
TEST

View File

@@ -59,7 +59,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "b/hello", "c/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
rm -v blocks/7
tests/corrupt.py -n 1
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0;
@@ -86,8 +86,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "c/hello", "d/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
rm -v blocks/8
rm -v blocks/a
tests/corrupt.py -n 2
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
@@ -108,6 +107,32 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move file after corrupt ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "c/hello", "d/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "d") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move dir ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
@@ -140,7 +165,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "b/hi", "c/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
rm -v blocks/7
tests/corrupt.py -n 1
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0;
@@ -156,8 +181,6 @@ tests/test.py << TEST
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
@@ -169,8 +192,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "c/hi", "d/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
rm -v blocks/9
rm -v blocks/a
tests/corrupt.py -n 2
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
@@ -179,8 +201,6 @@ tests/test.py << TEST
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
@@ -189,6 +209,36 @@ tests/test.py << TEST
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move dir after corrupt ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "c/hi", "d/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "d") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
@@ -199,9 +249,9 @@ tests/test.py << TEST
lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "d/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c/hi") => 0;
lfs_dir_open(&lfs, &dir[0], "d/hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
@@ -217,9 +267,55 @@ tests/test.py << TEST
lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "d/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c/hello") => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file[0], "c/hello", LFS_O_RDONLY) => 0;
lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, 5) => 5;
memcmp(buffer, "hola\n", 5) => 0;
lfs_file_read(&lfs, &file[0], buffer, 8) => 8;
memcmp(buffer, "bonjour\n", 8) => 0;
lfs_file_read(&lfs, &file[0], buffer, 6) => 6;
memcmp(buffer, "ohayo\n", 6) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move state stealing ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "b") => 0;
lfs_remove(&lfs, "c") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "d/hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hola") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "bonjour") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "ohayo") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, 5) => 5;
memcmp(buffer, "hola\n", 5) => 0;
lfs_file_read(&lfs, &file[0], buffer, 8) => 8;

View File

@@ -15,25 +15,29 @@ tests/test.py << TEST
lfs_mkdir(&lfs, "parent/child") => 0;
lfs_remove(&lfs, "parent/orphan") => 0;
TEST
# remove most recent file, this should be the update to the previous
# corrupt most recent commit, this should be the update to the previous
# linked-list entry and should orphan the child
rm -v blocks/8
tests/corrupt.py
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
unsigned before = 0;
lfs_traverse(&lfs, test_count, &before) => 0;
test_log("before", before);
lfs_deorphan(&lfs) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
unsigned after = 0;
lfs_traverse(&lfs, test_count, &after) => 0;
test_log("after", after);
lfs_ssize_t before = lfs_fs_size(&lfs);
before => 8;
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_ssize_t orphaned = lfs_fs_size(&lfs);
orphaned => 8;
lfs_mkdir(&lfs, "parent/otherchild") => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_ssize_t deorphaned = lfs_fs_size(&lfs);
deorphaned => 8;
int diff = before - after;
diff => 2;
lfs_unmount(&lfs) => 0;
TEST

View File

@@ -139,5 +139,23 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
echo "--- Max path test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
memset(buffer, 'w', LFS_NAME_MAX+1);
buffer[LFS_NAME_MAX+2] = '\0';
lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG;
memcpy(buffer, "coffee/", strlen("coffee/"));
memset(buffer+strlen("coffee/"), 'w', LFS_NAME_MAX+1);
buffer[strlen("coffee/")+LFS_NAME_MAX+2] = '\0';
lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -153,7 +153,7 @@ tests/test.py << TEST
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs_file_close(&lfs, &file[0]) => 0;
@@ -202,7 +202,7 @@ tests/test.py << TEST
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs_file_close(&lfs, &file[0]) => 0;
@@ -243,7 +243,7 @@ tests/test.py << TEST
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs_file_close(&lfs, &file[0]) => 0;
@@ -286,7 +286,7 @@ tests/test.py << TEST
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs_file_close(&lfs, &file[0]) => 0;