Commit Graph

116 Commits

Author SHA1 Message Date
Christopher Haster
e611cf5050 Fix incorrect lookahead population before ack
Rather than tracking all in-flight blocks blocks during a lookahead,
littlefs uses an ack scheme to mark the first allocated block that
hasn't reached the disk yet. littlefs assumes all blocks since the
last ack are bad or in-flight, and uses this to know when it's out
of storage.

However, these unacked allocations were still being populated in the
lookahead buffer. If the whole block device fits in the lookahead
buffer, _and_ littlefs managed to scan around the whole storage while
an unacked block was still in-flight, it would assume the block was
free and misallocate it.

The fix is to only fill the lookahead buffer up to the last ack.
The internal free structure was restructured to simplify the runtime
calculation of lookahead size.
2018-02-08 01:52:39 -06:00
Christopher Haster
a25743a82a Fixed some minor error code differences
- Write on read-only file to return LFS_ERR_BADF
- Renaming directory onto file to return LFS_ERR_NOTEMPTY
- Changed LFS_ERR_INVAL in lfs_file_seek to assert
2018-02-04 14:36:36 -06:00
Christopher Haster
6716b5580a Fixed error check when truncating files to larger size 2018-02-04 14:09:55 -06:00
Christopher Haster
809ffde60f Merge pull request #24 from aldot/silence-shadow-warnings-1
Silence shadow warnings
2018-02-04 13:36:55 -06:00
Christopher Haster
dc513b172f Silenced more of aldot's warnings
Flags used:
-Wall -Wextra -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes
-Wunused -Wunused-parameter -Wunused-function -Wunused-value
-Wmissing-prototypes -Wmissing-declarations -Wold-style-definition
2018-02-04 13:15:30 -06:00
Bernhard Reutner-Fischer
aa50e03684 Commentary typo fix 2018-02-04 13:15:26 -06:00
Bernhard Reutner-Fischer
6d55755128 tests: Silence warnings in template
- no previous prototype for ‘test_assert’
- no previous prototype for ‘test_count’
- unused parameter ‘b’ in test_count
- function declaration isn’t a prototype for main
2018-02-04 13:15:17 -06:00
Bernhard Reutner-Fischer
029361ea16 Silence shadow warnings 2018-02-04 13:15:09 -06:00
Christopher Haster
fd04ed4f25 Added autogenerated release notes from commits 2018-02-02 02:35:07 -06:00
Bernhard Reutner-Fischer
3101bc92b3 Do not print command invocation if QUIET 2018-02-02 09:34:01 +01:00
Christopher Haster
d82e34c3ee Merge pull request #21 from aldot/doc-tweaks
documentation touch up, take 2
2018-02-01 15:06:24 -06:00
Bernhard Reutner-Fischer
436707c8d0 doc: Editorial tweaks 2018-02-01 14:56:43 -06:00
Bernhard Reutner-Fischer
3457252fe6 doc: Spelling fixes 2018-01-31 19:18:51 -06:00
Christopher Haster
6d8e0e21d0 Moved -Werror flag to CI only
The most useful part of -Werror is preventing code from being
merged that has warnings. However it is annoying for users who may have
different compilers with different warnings. Limiting -Werror to CI only
covers the main concern about warnings without limiting users.
2018-01-29 18:37:48 -06:00
Christopher Haster
88f678f4c6 Fixed self-assign warning in tests
Some of the tests were creating a variable `res`, however the test
system itself relies on it's own `res` variable. This worked out by
luck, but could lead to problems if the res variables were different
types.

Changed the generated variable in the test system to the less common
name `test`, which also works out to share the same prefix as other test
functions.
2018-01-29 18:37:48 -06:00
Christopher Haster
3ef4847434 Added remove step in tests to force rebuild
Found by user iamscottmoyers, this was an interesting bug with the test
system. If the new test.c file is generated fast enough, it may not have
a new timestamp and not get recompiled.

To fix, we can remove the specific files that need to be rebuilt (lfs and
test.o).
2018-01-29 18:37:41 -06:00
Christopher Haster
f694b14afb Merge pull request #16 from geky/versioning
Add version info for software library and on-disk structures
2018-01-29 01:20:23 -06:00
Christopher Haster
5a38d00dde Added deploy step in Travis to push new version as tags 2018-01-29 00:51:43 -06:00
Christopher Haster
035552a858 Add version info for software library and on-disk structures
An annoying part of filesystems is that the software library can change
independently of the on-disk structures. For this reason versioning is
very important, and must be handled separately for the software and
on-disk parts.

In this patch, littlefs provides two version numbers at compile time,
with major and minor parts, in the form of 6 macros.

LFS_VERSION        // Library version, uint32_t encoded
LFS_VERSION_MAJOR  // Major - Backwards incompatible changes
LFS_VERSION_MINOR  // Minor - Feature additions

LFS_DISK_VERSION        // On-disk version, uint32_t encoded
LFS_DISK_VERSION_MAJOR  // Major - Backwards incompatible changes
LFS_DISK_VERSION_MINOR  // Minor - Feature additions

Note that littlefs will error if it finds a major version number that
is different, or a minor version number that has regressed.
2018-01-26 14:26:25 -06:00
Christopher Haster
997c2e594e Fixed incorrect reliance on errno in emubd
When running the tests, the emubd erase function relied on the value of
errno to not change over a possible call to unlink. Annoyingly, I've
only seen this cause problems on a couple of specific Travis instances
while self-hosting littlefs on top of littlefs-fuse.
2018-01-22 19:28:29 -06:00
Christopher Haster
d88f0ac02f Added lfs_file_truncate
As a copy-on-write filesystem, the truncate function is a very nice
function to have, as it can take advantage of reusing the data already
written out to disk.
2018-01-20 19:22:44 -06:00
Christopher Haster
2ad435ed63 Added files test to littlefs-fuse tests in Travis v1.1 2018-01-20 19:22:38 -06:00
Christopher Haster
1fb6a19520 Reduced ctz traverse runtime by 2x
Unfortunately for us, the ctz skip-list does not offer very much benefit
for full traversals. Since the information about which blocks are in
use are spread throughout the file, we can't use the fast-lanes
embedded in the skip-list without missing blocks.

However, it turns out we can at least use the 2nd level of the skip-list
without missing any blocks. From an asymptotic analysis, a constant speed
up isn't interesting, but from a pragmatic perspective, a 2x speedup is
not bad.
2018-01-12 12:07:45 -06:00
Christopher Haster
db8872781a Added error code LFS_ERR_NOTEMPTY
As noted by itayzafrir, removing a non-empty directory should
error with ENOTEMPTY, not EINVAL
2018-01-12 12:07:40 -06:00
Christopher Haster
c2fab8fabb Added asserts on geometry and updated config documentation
littlefs had an unwritten assumption that the block device's program
size would be a multiple of the read size, and the block size would
be a multiple of the program size. This has already caused confusion
for users. Added a note and assert to catch unexpected geometries
early.

Also found that the prog/erase functions indicated they must return
LFS_ERR_CORRUPT to catch bad blocks. This is no longer true as errors
are found by CRC.
2018-01-11 11:56:09 -06:00
Christopher Haster
472ccc4203 Fixed file truncation without writes
In the open call, the LFS_O_TRUNC flag was correctly zeroing the file, but
it wasn't actually writing the change out to disk. This went unnoticed because
in the cases where the truncate was followed by a file write, the
updated contents would be written out correctly.

Marking the file as dirty if the file isn't already truncated fixes the
problem with the least impact. Also added better test cases around
truncating files.
2018-01-11 10:26:33 -06:00
Christopher Haster
aea3d3db46 Fixed positive seek bounds checking
This bug was a result of an annoying corner case around intermingling
signed and unsigned offsets. The boundary check that prevents seeking
a file to a position before the file was preventing valid seeks with
positive offsets.

This corner case is a bit more complicated than it looks because the
offset is signed, while the size of the file is unsigned. Simply
casting both to signed or unsigned offsets won't handle large files.
2018-01-03 15:00:04 -06:00
Christopher Haster
be22d3449f Updated links to Mbed OS 2017-12-27 12:59:54 -06:00
Christopher Haster
425aa3c694 Fixed issue with immediate exhaustion and small unaligned storage
This was a small hole in the logic that handles initializing the
lookahead buffer. To imitate exhaustion (so the block allocator
will trigger a scan), the lookahead buffer is rewound a full
lookahead and set up to look like it is exhausted. However,
unlike normal allocation, this rewind was not kept aligned to
a multiple of the scan size, which is limited by both the
lookahead buffer and the total storage size.

This bug went unnoticed for so long because it only causes
problems when the block device is both:
1. Not aligned to the lookahead buffer (not a power of 2)
2. Smaller than the lookahead buffer

While this seems like a strange corner case for a block device,
this turned out to be very common for internal flash, especially
when a handleful of blocks are reserved for code.
2017-12-27 12:59:32 -06:00
Christopher Haster
5ee20e8d77 Fixed pipefail issue that was preventing CI from reporting errors 2017-11-22 14:49:48 -06:00
Christopher Haster
bf78b09d37 Added directory list for synchronizing in flight directories
As it was, if a user operated on a directory while at the same
time iterating over the directory, the directory objects could
fall out of sync. In the best case, files may be skipped while
removing everything in a file, in the worst case, a very poorly
timed directory relocate could be missed.

Simple fix is to add the same directory tracking that is currently
in use for files, at a small code+complexity cost.
2017-11-22 14:49:43 -06:00
Christopher Haster
e169d06c57 Removed vestigial function declaration 2017-11-21 22:01:20 -06:00
Christopher Haster
996cd8af22 Revisited documentation
Mostly changed the wording around the goals/features of littlefs based
on feedback from other developers.

Also added in project links now that there are a few of those floating
around. And made the README a bit easier to navigate.
2017-11-20 00:08:02 -06:00
Christopher Haster
78c79ecb9e Added QUIET flag to tests so CI is readable 2017-11-16 17:54:44 -06:00
Christopher Haster
f9f4f5ccec Fixed standard name mismatch LFS_ERR_EXISTS -> LFS_ERR_EXIST
Matches the standard EEXIST name found on most systems. Other than
this name, all other common constant names were consistent in this
manner.
2017-11-16 17:50:14 -06:00
Christopher Haster
843e3c6c75 Added sticky-bit for preventing file syncs after write errors
Short story, files are no longer committed to directories during
file sync/close if the last write did not complete successfully.
This avoids a set of interesting user-experience issues related
to the end-of-life behaviour of the filesystem.

As a filesystem approaches end-of-life, the chances of running into
LFS_ERR_NOSPC grows rather quickly. Since this condition occurs after
at the end of a devices life, it's likely that operating in these
conditions hasn't been tested thoroughly.

In the specific case of file-writes, you can hit an LFS_ERR_NOSPC after
parts of the file have been written out. If the program simply continues
and closes the file, the file is written out half completed. Since
littlefs has a strong garuntee the prevents half-writes, it's unlikely
this state of the file would be expected.

To make things worse, since close is also responsible for memory
cleanup, it's actually _impossible_ to continue working as it was
without leaking memory.

By prevent the file commits, end-of-life behaviour should at least retain
a previous copy of the filesystem without any surprises.
2017-11-16 17:25:41 -06:00
Christopher Haster
2612e1b3fa Modified lfs_ctz_extend to be a little bit safer
Specifically around error handling. As is, incorrectly handled
errors could cause higher code to get uninitialized blocks,
potentially leading to writes to arbitray blocks on storage.
2017-11-16 15:10:17 -06:00
Christopher Haster
6664723e18 Fixed issue with committing directories to bad-blocks that are stuck
This is only an issue in the weird case that are worn down block is
left in the odd state of not being able to change the data that resides
on the block. That being said, this does pop up often when simulating
wear on block devices.

Currently, directory commits checked if the write succeeded by crcing the
block to avoid the additional RAM cost for another buffer. However,
before this commit, directory commits just checked if the block crc was
valid, rather than comparing to the expected crc. This would usually
work, unless the block was stuck in a state with valid crc.

The fix is to simply compare with the expected crc to find errors.
2017-11-16 14:53:45 -06:00
Christopher Haster
3f31c8cba3 Fixed corner case with immediate exhaustion and lookahead==block_count
The previous math for determining if we scanned all of disk wasn't set
up correctly in the lfs_mount function. If lookahead == block_count
the lfs_alloc function would think we had already searched the entire
disk.

This is only an issue if we manage to exhaust a block on the first
pass after mount, since lfs_alloc_ack resets the lookahead region
into a valid state after a succesful block allocation.
2017-11-10 10:53:33 -06:00
Christopher Haster
f4aeb8331a Fixed issue with aggressively rounding down lookahead configuration
The littlefs allows buffers to be passed statically in the case
that a system does not have a heap. Unfortunately, this means we
can't round up in the case of an unaligned lookahead buffer.

Double unfortunately, rounding down after clamping to the block device
size could result in a lookahead of zero for block devices < 32 blocks
large.

The assert in littlefs does catch this case, but rounding down prevents
support for < 32 block devices.

The solution is to simply require a 32-bit aligned buffer with an
assert. This avoids runtime problems while allowing a user to pass
in the correct buffer for < 32 block devices. Rounding up can be
handled at higher API levels.
2017-11-10 10:53:30 -06:00
Christopher Haster
db51a395ba Removed stray newline in LFS_ERROR for version 2017-11-09 19:44:23 -06:00
Christopher Haster
2ab150cc50 Removed toolchain specific warnings
- Comparisons with differently signed integer types
- Incorrectly signed constant
- Unreachable default returns
- Leaked uninitialized variables in relocate goto statements
2017-10-30 16:55:45 -05:00
Christopher Haster
0825d34f3d Adopted alternative implementation for lfs_ctz_index
Same runtime cost, however reduces the logic and avoids one of
the two big branches. See the DESIGN.md for more info.

Now uses these equations instead of the messy guess and correct method:
n = (N - w/8(popcount(N/(B-2w/8)) + 2)) / (B-2w/8)
off = N - (B-w2/8)n - w/8popcount(n)
2017-10-30 12:03:33 -05:00
Christopher Haster
46e22b2a38 Adopted lfs_ctz_index implementation using popcount
This reduces the O(n^2logn) runtime to read a file to only O(nlog).
The extra O(n) did not touch the disk, so it isn't a problem until the
files become very large, but this solution comes with very little cost.

Long story short, you can find the block index + offset pair for a
CTZ linked-list with this series of formulas:

n' = floor(N / (B - 2w/8))
N' = (B - 2w/8)n' + (w/8)popcount(n')
off' = N - N'
n, off =
  n'-1, off'+B                if off' <  0
  n',   off'+(w/8)(ctz(n')+1) if off' >= 0

For the long story, you will need to see the updated DESIGN.md
2017-10-18 00:44:30 -05:00
Christopher Haster
4fdca15a0d Slight name change with ctz skip-list functions
changed:
lfs_index -> lfs_ctz_index
lfs_index_find -> lfs_ctz_find
lfs_index_append -> lfs_ctz_append
lfs_index_traverse -> lfs_ctz_traverse
2017-10-18 00:41:43 -05:00
Christopher Haster
454b588f73 Updated SPEC.md and DESIGN.md based on recent changes
- Added math behind CTZ limits
- Added documentation over atomic moves
2017-10-12 20:31:33 -05:00
Christopher Haster
f3578e3250 Removed clamping to block size in ctz linked-list
Initially, I was concerned that the number of pointers in the ctz
linked-list could exceed the storage in a block. Long story short
this isn't really possible outside of extremely small block sizes.

Since clamping impacts the layout of files on disk, removing the
block size removed quite a bit of logic and corner cases. Replaced
with an assert on block size during initialization.

---

Long story long, the minimum block size needed to store all ctz
pointers in a filesystem can be found with this formula:

B = (w/8)*log2(2^w / (B - 2*(w/8)))

where:
B = block size in bytes
w = pointer width in bits

It's not a very pretty formula, but does give us some useful info
if we apply some math:

min block size:
32 bit ctz linked-list = 104 bytes
64 bit ctz linked-list = 448 bytes

For littlefs, 128 bytes is a perfectly reasonable minimum block size.
2017-10-12 20:31:33 -05:00
Christopher Haster
83d4c614a0 Updated copyright
Due to employee contract
Per ARM license remains under Apache 2.0
2017-10-12 20:29:10 -05:00
Christopher Haster
539409e2fb Refactored deduplicate/deorphan step to single deorphan step
Deduplication and deorphan steps aren't required under indentical
conditions, but they can be processed in the same iteration of the
filesystem. Since lfs_alloc (requires deorphan) occurs on most write
calls to the filesystem (requires deduplication), it was simpler to
just compine the steps into a single lfs_deorphan step.

Also traded out the places where lfs_rename/lfs_remove just defer
operations to the deorphan step. This adds a bit of code, but also
significantly speeds up directory operations.
2017-10-10 17:14:46 -05:00
Christopher Haster
2936514b5e Added atomic move using dirty tag in entry type
The "move problem" has been present in littlefs for a while, but I haven't
come across a solution worth implementing for various reasons.

The problem is simple: how do we move directory entries across
directories atomically? Since multiple directory entries are involved,
we can't rely entirely on the atomic block updates. It ends up being
a bit of a puzzle.

To make the problem more complicated, any directory block update can
fail due to wear, and cause the directory block to need to be relocated.
This happens rarely, but brings a large number of corner cases.

---

The solution in this patch is simple:
1. Mark source as "moving"
2. Copy source to destination
3. Remove source

If littlefs ever runs into a "moving" entry, that means a power loss
occured during a move. Either the destination entry exists or it
doesn't. In this case we just search the entire filesystem for the
destination entry.

This is expensive, however the chance of a power loss during a move
is relatively low.
2017-10-10 06:18:46 -05:00