964 Commits

Author SHA1 Message Date
Christopher Haster
a51be18765 Removed previous-version lfsp_fs_stat checks in test_compat
This function naturally doesn't exist in the previous version. We should
eventually add these calls when we can expect the previous version to
support this function, though it's a bit unclear when that should happen.

Or maybe not! Maybe this is testing more of the previous version than we
really care about.
2023-06-06 22:00:26 -05:00
Christopher Haster
a7ccc1df59 Promoted lfs_gstate_needssuperblock to be available in readonly builds
Needed for minor version reporting in lfs_fs_stat to work correctly.
2023-06-06 15:59:45 -05:00
Christopher Haster
fdee127f74 Removed use of LFS_VERSION in test_compat
LFS_VERSION -> LFS_DISK_VERSION

These tests shouldn't depend on LFS_VERSION. It's a bit subtle, but
LFS_VERSION versions the API, and LFS_DISK_VERSION versions the
on-disk format, which is what test_compat should be testing.
2023-06-06 14:55:22 -05:00
Christopher Haster
87bbf1d374 Added lfs_fs_stat for access to filesystem status/configuration
Currently this includes:

- minor_version - on-disk minor version
- block_usage - estimated number of in-use blocks
- name_max - configurable name limit
- file_max - configurable file limit
- attr_max - configurable attr limit

These are currently the only configuration operations that need to be
written to disk. Other configuration is either needed to mount, such as
block_size, or does not change the on-disk representation, such as
read/prog_size.

This also includes the current block usage, which is common in other
filesystems, though a more expensive to find in littlefs. I figure it's
not unreasonable to make lfs_fs_stat no worse than block allocation,
hopefully this isn't a mistake. It may be worth caching the current
usage after the most recent lookahead scan.

More configuration may be added to this struct in the future.
2023-06-06 13:02:16 -05:00
Christopher Haster
66f07563c3
Merge pull request #832 from littlefs-project/remove-sys-types
Remove unnecessary sys/types.h include
v2.6.1
2023-05-23 14:46:12 -05:00
Christopher Haster
5eed341059
Merge pull request #819 from benpicco/fix-AVR
Fix build for AVR
2023-05-23 14:45:34 -05:00
Christopher Haster
97e2526a81
Merge pull request #818 from littlefs-project/convince-github-littlefs-is-c
Convince GitHub littlefs is a C project
2023-05-23 14:44:48 -05:00
Christopher Haster
8a4ee65fc3 Removed unnecessary sys/types.h include
Likely included at some point for ssize_t, this is no longer needed and
causes some problems for embedded compilers.

Currently littlefs doesn't even use size_t/ssize_t in its definition of
lfs_size_t/lfs_ssize_t, so I don't think this will ever be required.

Found by LDong-Arm, vvn-git
2023-05-17 11:11:27 -05:00
Benjamin Valentin
6fda813ce8 Fix build for AVR
This fixes the overflowing left shift on 8 bit platforms.

    littlefs2/lfs.c: In function ‘lfs_dir_commitcrc’:
    littlefs2/lfs.c:1654:51: error: left shift count >= width of type [-Werror=shift-count-overflow]
             commit->ptag = ntag ^ ((0x80 & ~eperturb) << 24);
2023-05-05 12:11:20 +02:00
Christopher Haster
f2bc6a8e88 Reclassify .toml files as .c files on GitHub
With the new test framework, GitHub really wants to mark littlefs as a
python project. telling it to reclassify our test .toml files as C code
(which they are 95% of anyways) remedies this.

An alternative would be to add syntax=c vim modelines to the test/bench
files, which would also render them with C syntax highlighting on
GitHub. Unfortunately the interspersed toml metadata mucks this up,
making the result not very useful.
2023-05-04 14:01:04 -05:00
Christopher Haster
ec3ec86bcc
Merge pull request #814 from littlefs-project/devel
Minor release: v2.6
v2.6.0
2023-05-04 12:55:52 -05:00
Christopher Haster
405f33214a
Merge pull request #812 from littlefs-project/mkconsistent
Add lfs_fs_mkconsistent
2023-04-30 23:26:04 -05:00
Christopher Haster
3dca02911f
Merge pull request #811 from littlefs-project/fix-deorphan-repeatedly
Fix issue where lfs_fs_deorphan may run more than needed
2023-04-30 23:25:01 -05:00
Christopher Haster
259535ee73 Added lfs_fs_mkconsistent
lfs_fs_mkconsistent allows running the internal consistency operations
(desuperblock/deorphan/demove) on demand and without any other
filesystem changes.

This can be useful for front-loading and persisting consistency operations
when you don't want to pay for this cost on the first write to the
filesystem.

Conveniently, this also offers a way to force the on-disk minor version
to bump, if that is wanted behavior.

Idea from kasper0
2023-04-26 21:45:26 -05:00
Christopher Haster
94d9e097a6 Fixed issue where lfs_fs_deorphan may run more than needed
The underlying issue is that lfs_fs_deorphan did not updating gstate
correctly. The way it determined if there are any orphans remaining in
the filesystem was by subtracting the number of found orphans from an
internal counter.

This internal counter is a leftover from a previous implementation that
allowed leaving the lfs_fs_deorphan loop early if we know the number of
expected orphans. This can happen during recursive mdir relocations, but
with only a single bit in the gstate, can't happen during mount. If we
detect orphans during mount, we set this internal counter to 1, assuming
we will find at least one orphan.

But this presents a problem, what if we find _no_ orphans? If this happens
we never decrement the internal counter of orphans, so we would never
clear the bit in the gstate. This leads to a running lfs_fs_deorphan
on more-or-less every mutable operation in the filesystem, resulting in
an extreme performance hit.

The solution here is to not subtract the number of found orphans, but assume
that when our lfs_fs_deorphan loop finishes, we will have no orphans, because
that's the whole point of lfs_fs_deorphan.

Note that the early termination of lfs_fs_deorphan was dropped because
it would not actually change the runtime complexity of lfs_fs_deorphan,
adds code cost, and risks fragile corner cases such as this one.

---

Also added tests to assert we run lfs_fs_deorphan at most once.

Found by kasper0 and Ldd309
2023-04-26 21:41:26 -05:00
Christopher Haster
dd03c27476
Merge pull request #805 from littlefs-project/fix-dir-seek-end
Fix issue where seeking to end-of-directory return LFS_ERR_INVAL
2023-04-26 14:32:14 -05:00
Christopher Haster
23a4a089b5
Merge pull request #800 from littlefs-project/fix-boundary-truncates
Fix block-boundary truncate issues
2023-04-26 14:31:23 -05:00
Christopher Haster
b6773e68bf Merge remote-tracking branch 'origin/devel' into fix-dir-seek-end 2023-04-26 13:47:58 -05:00
Christopher Haster
922a35b3a5 Merge remote-tracking branch 'origin/devel' into fix-boundary-truncates 2023-04-26 13:30:04 -05:00
Christopher Haster
92298c749d
Merge pull request #802 from littlefs-project/assert-minimum-block-size
Add explicit assert for minimum block size of 128 bytes
2023-04-26 02:41:44 -05:00
Christopher Haster
50b394ca36
Merge pull request #801 from littlefs-project/assert-bool-cast
Add an assert for truthy-preserving bool conversions
2023-04-26 02:41:30 -05:00
Christopher Haster
a99574cd5b
Merge pull request #807 from littlefs-project/doc-link-littlefs2-rust
Add littlefs2 crate to README
2023-04-26 02:40:51 -05:00
Christopher Haster
363a8b56cf Tweaked wording of littlefs2-rust link in README.md 2023-04-26 02:02:23 -05:00
Lachezar Lechev
e43d381135 chore: add littlefs2 crate to README 2023-04-26 01:59:57 -05:00
Christopher Haster
ee6a51bbbe
Merge pull request #718 from yomimono/mention-chamelon
Add "chamelon" to the related projects section.
2023-04-26 01:57:31 -05:00
Christopher Haster
01ac033d47
Merge pull request #572 from tniessen/add-littlefs-disk-img-viewer
Add littlefs-disk-img-viewer to README
2023-04-26 01:56:31 -05:00
Christopher Haster
2a18e03cb8
Merge pull request #809 from littlefs-project/brent-cycle-detection
Adopt Brent's algorithm for cycle detection
2023-04-26 01:55:50 -05:00
Christopher Haster
6f074ebe31
Merge pull request #497 from littlefs-project/crc-rework-2
Forward-looking erase-state CRCs
2023-04-26 01:15:59 -05:00
Christopher Haster
0a7eca0bd5
Merge pull request #752 from littlefs-project/test-and-bench-runners
Add test/bench runners, benchmarks, additional scripts
2023-04-26 01:09:01 -05:00
Christopher Haster
3e25dfc16c Added FCRC tags and an explanation of how FCRCs work to SPEC.md
See SPEC.md for more info.

Also considered adding an explanation to DESIGN.md, but there's not a
great place for it. Maybe FCRCs are too low-level for the high-level
design document. Though may be worth reconsidering if DESIGN.md gets
revisited.
2023-04-21 14:49:49 -05:00
Christopher Haster
9e28c75482 Bumped minor version to v2.6 and on-disk minor version to lfs2.1 2023-04-21 00:57:00 -05:00
Christopher Haster
4c9360020e Added ability to bump on-disk minor version
This just means a rewrite of the superblock entry with the new minor
version.

Though it's interesting to note, we don't need to rewrite the superblock
entry until the first write operation in the filesystem, an optimization
that is already in use for the fixing of orphans and in-flight moves.

To keep track of any outdated minor version found during lfs_mount, we
can carve out a bit from the reserved bits in our gstate. These are
currently used for a counter tracking the number of orphans in the
filesystem, but this is usually a very small number so this hopefully
won't be an issue.

In-device gstate tag:

  [--       32      --]
  [1|- 11 -| 10 |1| 9 ]
   ^----^-----^--^--^-- 1-bit has orphans
        '-----|--|--|-- 11-bit move type
              '--|--|-- 10-bit move id
                 '--|-- 1-bit needs superblock
                    '-- 9-bit orphan count
2023-04-21 00:56:55 -05:00
Christopher Haster
ca0da3d490 Added compatibility testing on pull-request to GitHub test action
This uses the "github.event.pull_request.base.ref" variable as the
"lfsp" target for compatibility testing.
2023-04-21 00:29:28 -05:00
Christopher Haster
116332d3f7 Added tests for forwards and backwards disk compatibility
This is a bit tricky since we need two different version of littlefs in
order to test for most compatibility concerns.

Fortunately we already have scripts/changeprefix.py for version-specific
symbols, so it's not that hard to link in the previous version of
littlefs in CI as a separate set of symbols, "lfsp_" in this case.

So that we can at least test the compatibility tests locally, I've added
an ifdef against the expected define "LFSP" to define a set of aliases
mapping "lfsp_" symbols to "lfs_" symbols. This is manual at the moment,
and a bit hacky, but gets the job done.

---

Also changed BUILDDIR creation to derive subdirectories from a few
Makefile variables. This makes the subdirectories less manual and more
flexible for things like LFSP. Note this wasn't possible until BUILDDIR
was changed to default to "." when omitted.
2023-04-21 00:28:55 -05:00
Christopher Haster
f0cc1db793 Tweaked changeprefix.py to not rename dir component in paths
This wasn't implemented correctly anyways, as it would need to recursively
rename directories that may not exist. Things would also get a bit
complicated if only some files in a directory were renamed.

Doable, but not needed for our use case.

For now just ignore any directory components. Though this may be worth
changing if the source directory structure becomes more complicated in
the future (maybe with a -r/--recursive flag?).
2023-04-19 18:33:47 -05:00
Christopher Haster
bf045dd13c
Tweaked link to littlefs-disk-img-viewer to go to github repo 2023-04-19 11:48:06 -05:00
Christopher Haster
b33a5b3f85 Fixed issue where seeking to end-of-directory return LFS_ERR_INVAL
This was just an oversight. Seeking to the end of the directory should
not error, but instead restore the directory to the state where the next
read returns 0.

Note this matches the behavior of lfs_file_tell/lfs_file_seek.

Found by sosthene-nitrokey
2023-04-18 15:10:07 -05:00
Christopher Haster
384a498762 Extend dir seek tests to include seeking to end of directory 2023-04-18 14:55:43 -05:00
Christopher Haster
b0a4a44e5b Added explicit assert for minimum block size of 128 bytes
There was already an assert for this, but because it included the
underlying equation for the requirement it was too confusing for
users that had no prior knowledge for why the assert could trigger.

The math works out such that 128 bytes is a reasonable minimum
requirement, so I've added that number as an explicit assert.
Hopefully this makes this sort of situation easier to debug.

Note that this requirement would need to be increased to 512 bytes if
block addresses are ever increased to 64-bits. DESIGN.md goes into more
detail why this is.
2023-04-17 19:58:09 -05:00
Christopher Haster
aae897ffd0 Added an assert for truthy-preserving bool conversions
This has caught enough people that an explicit assert is warranted.
How littlefs, a c99 project, should be integrated with c89 projects
is still an open question, but no one deserves to debug this sort of
undetected casting issue.

Found by johnernberg and XinStellaris
2023-04-17 19:19:42 -05:00
Christopher Haster
e57402c8e9 Added ability to revert to inline file in lfs_file_truncate
Before, once converted to a CTZ skip-list, a file would remain a CTZ
skip-list even if truncated back to a size that could be inlined.

This was just a shortcut in implementation. And since the fix for boundary
truncates needed special handling for size==0, it made sense to extend
this special condition to allow reverting to inline files.

---

The only case I can think of, where reverting to an inline file would be
detrimental, is if it's a readonly file that you would otherwise not need
to pay the metadata overhead for. But as a tradeoff, inlining the file
would free up the block it was on, so it's unclear if this really is
a net loss.

If the truncate is followed by a write, reverting to an inline file will
always be beneficial. We assume writes will change the data, so in the
non-inlined case there's no way to avoid copying the underlying block.
Even if we assume padding issues are solved.
2023-04-17 18:18:06 -05:00
Christopher Haster
6dc18c38c1 Fixed block-boundary truncate issue
There has been a bug in the filesystem for a while where truncating to a
block boundary suffers from an off-by-one mistake that corrupts the
internal representation of the CTZ skip-list.

This mostly appears when the file_size == block_size, as file_size >
block_size includes CTZ skip-list metadata, so the underlying block
boundaries appear at slightly different offsets.

---

The reason for off-by-one issue is a nuance in lfs_ctz_find that we sort
of abuse to get two different behaviors.

Consider the situation where this bug occurs:

   block 0     block 1
  .--------.  .--------.
  | abcdef |<-| {ptr0} |
  | ghijkl |  | yzabcd |
  | mnopqr |  |        |
  | stuvwx |  |        |
  '--------'  '--------'

With these 24-byte blocks, there's an ambiguity if we wanted to point to
offset 24. We could point before the block boundary, or we could point
after the block boundary

Before:

   block 0     block 1
  .--------.  .--------.
  | abcdef |<-| {ptr0} |
  | ghijkl |  | yzabcd |
  | mnopqr |  |        |
  | stuvwx |  |        |
  '-------^'  '--------'
          '-- off=24 is here

After:

   block 0     block 1
  .--------.  .--------.
  | abcdef |<-| {ptr0} |
  | ghijkl |  | yzabcd |
  | mnopqr |  | ^      |
  | stuvwx |  | |      |
  '--------'  '-|------'
                '-- off=24 is here

When we want these two offsets depends on the context. We want the
offset to be conservative if it represents a size, but eager if it is
being used to prepare a block for writing.

The workaround/hack is to prefer the eager offset, after the block boundary,
but use `size-1` as the argument if we need the conservative offset.

This finds the correct block, but is off-by-one in the calculated
block-offset. Fortunately we happen to not use the block-offset in the
places we need this workaround/hack.

---

To get back to the bug, the wrong mode of lfs_ctz_find was used in
lfs_file_truncate, leading to internal corruption of the CTZ skip-list.

The correct behavior is size-1, with care to avoid underflow.

Also I've tweaked the code to make it clear the calculated block-offset
goes unused in these situations.

Thanks to ghost, ajaybhargav, and others for reporting the issue,
colin-foster-advantage for a reproducible test case, and rvanschoren,
hgspbs for the initial solution.
2023-04-17 17:49:57 -05:00
Christopher Haster
d5dc4872cb Expanded truncate tests to test more corner cases
Removed the weird alignment requirement from the general truncate tests.
This explicitly hid off-by-one truncation errors.

These tests now reveal the same issue as the block-sized truncation test
while also testing for other potential off-by-one errors.
2023-04-17 12:10:19 -05:00
Sosthène Guédon
24795e6b74
Add missing iterations in tests 2023-03-13 11:39:06 +01:00
Colin Foster
7b151e1abb Add test scenario for truncating to a block size
When truncation is done on a file to the block size, there seems to be
an error where it points to an incorrect block. Perform a write /
truncate / readback operation to verify this issue.

Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
2023-01-26 11:55:53 -08:00
Christopher Haster
ba1c76435a Fixed issue where deorphan could get stuck circling between two half-orphans
This of course should never happen normally, two half-orphans requires
two parents, which is disallowed in littlefs for this reason. But it can
happen if there is an outdated half-orphan later in the metadata
linked-list. The two half-orphans can cause the deorphan step to get
stuck, constantly "fixing" the first half-orphan before it has a chance
to remove the problematic, outdated half-orphan later in the list.

The solution here is to do a full check for half-orphans before
restarting the half-orphan loop. This strategy has the potential to
visit more metadata blocks unnecessarily, but avoids situations where
removing a later half-orphan will eventually cause an earlier
half-orphan to resolve itself.

Found with heuristic powerloss testing with test_relocations_reentrant_renames
after 192 nested powerlosses.
2022-12-17 12:42:05 -06:00
Christopher Haster
d1b254da2c Reverted removal of 1-bit counter threaded through tags
Initially I thought the fcrc would be sufficient for all of the
end-of-commit context, since indicating that there is a new commit is a
simple as invalidating the fcrc. But it turns out there are cases that
make this impossible.

The surprising, and actually common, case, is that of an fcrc that
will end up containing a full commit. This is common as soon as the
prog_size is big, as small commits are padded to the prog_size at
minimum.

  .------------------. \
  |     metadata     | |
  |                  | |
  |                  | +-.
  |------------------| | |
  |   foward CRC ------------.
  |------------------| / |   |
  |   commit CRC    -----'   |
  |------------------|       |
  |     padding      |       |
  |                  |       |
  |------------------| \   \ |
  |     metadata     | |   | |
  |                  | +-. | |
  |                  | | | +-'
  |------------------| / | |
  |   commit CRC --------' |
  |------------------|     |
  |                  |     /
  '------------------'

When the commit + crc is all contained in the fcrc, something silly
happens with the math behind crcs. Everything in the commit gets
canceled out:

  crc(m) = m(x) x^|P|-1 mod P(x)

  m ++ crc(m) = m(x) x^|P|-1 + (m(x) x^|P|-1 mod P(x))

  crc(m ++ crc(m)) = (m(x) x^|P|-1 + (m(x) x^|P|-1 mod P(x))) x^|P|-1 mod P(x)

  crc(m ++ crc(m)) = (m(x) x^|P|-1 + m(x) x^|P|-1) x^|P|-1 mod P(x)

  crc(m ++ crc(m)) = 0 * x^|P|-1 mod P(x)

This is the reason the crc of a message + naive crc is zero. Even with an
initializer/bit-fiddling, the crc of the whole commit ends up as some
constant.

So no manipulation of the commit can change the fcrc...

But even if this did work, or we changed this scheme to use two
different checksums, it would still require calculating the fcrc of
the whole commit to know if we need to tweak the first bit to invalidate
the unlikely-but-problematic case where we happen to match the fcrc. This
would add a large amount of complexity to the commit code.

It's much simpler and cheaper to keep the 1-bit counter in the tag, even
if it adds another moving part to the system.
2022-12-17 12:42:05 -06:00
Christopher Haster
2f26966710 Continued implementation of forward-crcs, adopted new test runners
This fixes most of the remaining bugs (except one with multiple padding
commits + noop erases in test_badblocks), with some other code tweaks.

The biggest change was dropping reliance on end-of-block commits to know
when to stop parsing commits. We can just continue to parse tags and
rely on the crc for catch bad commits, avoiding a backwards-compatiblity
hiccup. So no new commit tag.

Also renamed nprogcrc -> fcrc and commitcrc -> ccrc and made naming in
the code a bit more consistent.
2022-12-17 12:42:05 -06:00
Christopher Haster
b4091c6871 Switched to separate-tag encoding of forward-looking CRCs
Previously forward-looking CRCs was just two new CRC types, one for
commits with forward-looking CRCs, one without. These both contained the
CRC needed to complete the current commit (note that the commit CRC
must come last!).

         [--   32   --|--   32   --|--   32   --|--   32   --]
with:    [  crc3 tag  | nprog size |  nprog crc | commit crc ]
without: [  crc2 tag  | commit crc ]

This meant there had to be several checks for the two possible structure
sizes, messying up the implementation.

         [--   32   --|--   32   --|--   32   --|--   32   --|--   32   --]
with:    [nprogcrc tag| nprog size |  nprog crc | commit tag | commit crc ]
without: [ commit tag | commit crc ]

But we already have a mechanism for storing optional metadata! The
different metadata tags! So why not use a separate tage for the
forward-looking CRC, separate from the commit CRC?

I wasn't sure this would actually help that much, there are still
necessary conditions for wether or not a forward-looking CRC is there,
but in the end it simplified the code quite nicely, and resulted in a ~200 byte
code-cost saving.
2022-12-17 12:42:05 -06:00
Christopher Haster
91ad673c45 Cleaned up a few additional commit corner cases
- General cleanup from integration, including cleaning up some older
  commit code
- Partial-prog tests do not make sense when prog_size == block_size
  (there can't be partial-progs!)
- Fixed signed-comparison issue in modified filebd
2022-12-17 12:42:05 -06:00