Commit Graph

73 Commits

Author SHA1 Message Date
Christopher Haster
eaa9220aad Renamed lfs_entry_t -> lfs_mattr_t
Attributes are used to describe more than just entries, so calling these
list of attributes "entries" was inaccurate. However, the name
"attributes" would conflict with "user attributes", user-facing
attributes with a very similar purpose. "user attributes" must be kept
distinct due to differences in binary layout (internal attributes can
use a more compact tag+buffer representation, but expecting users to
jump through hoops to get their data to look like that isn't very
user-friendly).

Decided to go with "mattr" as shorthand for "meta-attributes", similar
to "metadata".
2018-10-13 18:14:38 -05:00
Christopher Haster
11a3c8d062 Continued progress with reintroducing testing on the new metadata logging
Now with some tweaks to commit/compact, and a committers for entrylists and
moves specifically. No longer relying on a commitwith callback, the
types of commits are now infered from their tags.

This means we can now commit things atomically with special commits,
such as moves. Now lfs_rename can move entries to new names correctly.
2018-10-13 17:47:01 -05:00
Christopher Haster
0bdaeb7f8b More testing progress, combined dir/commit traversal
Passing more tests now with the journalling change, but still have more
work to do.

The most humorous bug was a bug where during the three step move
process, the entry move logic would dumbly copy over any tags associated
with the moving entry, including the tag used to temporarily mark the
entry as "moving".

Also combined dir and commit traversal using a "stop_at_commit" flag in
directory struct as a short-term hack to combine the code paths.
2018-10-13 17:44:37 -05:00
Christopher Haster
0405ceb171 Cleaned up enough things to pass basic file testing 2018-10-13 13:41:05 -05:00
Christopher Haster
a3c67d9697 Reorganized the internal operations to make more sense
Also refactored lfs_dir_compact a bit, adding begin and end as arguments
since they simplify a bit of the logic and can be found out much easier
earlier in the commit logic.

Also changed add -> append and drop -> delete and cleaned up some of the
logic around there.
2018-10-13 13:38:04 -05:00
Christopher Haster
0695862b38 Completed transition of files with journalling metadata
This was the simpler part of transitioning since file operations only
interact with metadata at sync time.

Also switched from array to linked-list of entries.
2018-10-13 13:33:29 -05:00
Christopher Haster
fe553e8af4 More progress integrating journaling
- Integrated into lfs_file_t_, duplicating functions where necessary
- Added lfs_dir_fetchwith_ as common parent to both lfs_dir_fetch_ and
  lfs_dir_find_
- Added similar parent with lfs_dir_commitwith_
- Made matching find/get operations with getbuffer/getentry and
  findbuffer/findentry
- lfs_dir_alloc now populates tail, since almost all directory block
  allocations need to populate tail
2018-10-13 13:31:47 -05:00
Christopher Haster
87f3e01a17 Progressed integration of journaling metadata pairs
- Integrated journaling into lfs_dir_t_ struct and operations,
  duplicating functions where necessary
- Added internal lfs_tag_t and lfs_stag_t
- Consolidated lfs_region and lfs_entry structures
2018-10-13 13:31:42 -05:00
Christopher Haster
8070abec34 Added rudimentary framework for journaling metadata pairs
This is a big change stemming from the fact that resizable entries
were surprisingly complicated to implement and came in with a sizable
code cost.

The theory is that the journalling has a comparable cost to resizable
entries. Both need to handle overflowing blocks, and managing offsets is
comparable to managing attribute IDs. But by jumping all the way to full
journaling, we can statically wear-level the metadata written to
metadata pairs.

The idea of journaling littlefs's metadata has been mentioned several times in
discussions and fits well into how littlefs works. You could even view the
existing metadata log as a log of size 2.

The downside of this approach is that changing the metadata in this way
would break compatibility from the existing layout on disk. Something
that resizable entries does not do.

That being said, adopting journaling at the metadata layer offers a big
improvement to littlefs's performance and wear-leveling, with very
little cost (maybe even none or negative after resizable entries?).
2018-10-13 13:22:53 -05:00
Christopher Haster
746b90965c Added lfs_fs_size for finding a count of used blocks
This has existed for some time in the form of the lfs_traverse
function, through which a user could provide a simple callback that
would just count the number of blocks lfs_traverse finds. However,
this approach is relatively unconventional and has proven to be confusing
for most users.
2018-10-09 23:02:57 -05:00
Christopher Haster
93244a3734 Added file-level and fs-level custom attribute APIs
In the form of lfs_file_setattr, lfs_file_getattr, lfs_fs_setattr,
lfs_fs_getattr.

This enables atomic updates of custom attributes as described in
6c754c8, and provides a custom attribute API that allows custom attributes
to be stored on the filesystem itself.
2018-10-09 23:02:50 -05:00
Christopher Haster
6c754c8023 Added support for atomically committing custom attributes
Although it's simple and probably what most users expect, the previous
custom attributes API suffered from one problem: the inability to update
attributes atomically.

If we consider our timestamp use case, updating a file would require:
1. Update the file
2. Update the timestamp

If a power loss occurs during this sequence of updates, we could end up
with a file with an incorrect timestamp.

Is this a big deal? Probably not, but it could be a surprise only found
after a power-loss. And littlefs was developed with the _specifically_
to avoid suprises during power-loss.

The littlefs is perfectly capable of bundling multiple attribute updates
in a single directory commit. That's kind of what it was designed to do.
So all we need is a new committer opcode for list of attributes, and
then poking that list of attributes through the API.

We could provide the single-attribute functions, but don't, because the
fewer functions makes for a smaller codebase, and these are already the
more advanced functions so we can expect more from users. This also
changes semantics about what happens when we don't find an attribute,
since erroring would throw away all of the other attributes we're
processing.

To atomically commit both custom attributes and file updates, we need a
new API, lfs_file_setattr. Unfortunately the semantics are a bit more
confusing than lfs_setattr, since the attributes aren't written out
immediately.
2018-10-09 23:02:09 -05:00
Christopher Haster
6ffc8d3480 Added simple custom attributes
A much requested feature (mostly because of littlefs's notable lack of
timestamps), this commits adds support for user-specified custom
attributes.

Planned (though underestimated) since v1, custom attributes provide a
route for OSs and applications to provide their own metadata in
littlefs, without limiting portability.

However, unlike custom attributes that can be found on much more
powerful PC filesystems, these custom attributes are very limited,
intended for only a handful of bytes for very important metadata. Each
attribute has only a single byte to identify the attribute, and the
size of all attributes attached to a file is limited to 64 bytes.

Custom attributes can be accessed through the lfs_getattr, lfs_setattr,
and lfs_removeattr functions.
2018-10-09 23:02:09 -05:00
Christopher Haster
65ea6b3d0f Bumped versions, cleaned up some TODOs and missing comments 2018-10-09 23:02:09 -05:00
Christopher Haster
6774276124 Expanded inline files up to a limit of 1023 bytes
One of the big benefits of inline files is that small files no longer need to
take up a full block. This opens up an opportunity to provide much better
support for storage devices with only a handful of very large blocks. Such as
the internal flash found on most microcontrollers.

After investigating some use cases for a filesystem on internal flash,
it has become apparent that the 255-byte limit is going to be too
restrictive to be useful in many cases. Most uses I found needed files
~4-64 bytes in size, but it wasn't uncommon to find files ~512 bytes in
length.

To try to remedy this, I've pushed the 255 byte limit up to 1023 bytes,
by stealing some bits from the previously-unused attributes's size.
Unfortunately this limits attributes to 63 bytes in total and has a
minor code cost, but I'm not sure even 1023 bytes will be sufficient for
a lot of cases.

The littlefs will probably never be as efficient with internal flash as
other filesystems such as SPIFFS, it just wasn't designed for this sort of
limited geometry. However, this feature has been heavily requested, even
with limitations, because of the opportunity for code reuse on
microcontrollers with both internal and external flash.
2018-10-09 23:02:09 -05:00
Christopher Haster
6362afa8d0 Added disk-backed limits on the name/attrs/inline sizes
Being a portable, microcontroller-scale embedded filesystem, littlefs is
presented with a relatively unique challenge. The amount of RAM
available is on completely different scales from machine to machine, and
what is normally a reasonable RAM assumption may break completely on an
embedded system.

A great example of this is file names. On almost every PC these days, the limit
for a file name is 255 bytes. It's a very convenient limit for a number
of reasons. However, on microcontrollers, allocating 255 bytes of RAM to
do a file search can be unreasonable.

The simplest solution (and one that has existing in littlefs for a
while), is to let this limit be redefined to a smaller value on devices
that need to save RAM. However, this presents an interesting portability
issue. If these devices are plugged into a PC with relatively infinite
RAM, nothing stops the PC from writing files with full 255-byte file
names, which can't be read on the small device.

One solution here is to store this limit on the superblock during format
time. When mounting a disk, the filesystem implementation is responsible for
checking this limit in the superblock. If it's larger than what can be
read, raise an error. If it's smaller, respect the limit on the
superblock and raise an error if the user attempts to exceed it.

In this commit, this strategy is adopted for file names, inline files,
and the size of all attributes, since these could impact the memory
consumption of the filesystem. (Recording the attribute's limit is
iffy, but is the only other arbitrary limit and could be used for disabling
support of custom attributes).

Note! This changes makes it very important to configure littlefs
correctly at format time. If littlefs is formatted on a PC without
changing the limits appropriately, it will be rejected by a smaller
device.
2018-10-09 23:02:09 -05:00
Christopher Haster
d0e0453651 Changed how we write out superblock to use append
Making the superblock look like "just another entry" allows us to treat
the superblock like "just another entry" and reuse a decent amount of
logic that would otherwise only be used a format and mount time. In this
case we can use append to write out the superblock like it was creating
a new entry on the filesystem.
2018-10-09 23:02:09 -05:00
Christopher Haster
836e23895a Shoehorned in hacky implementation of inline files
Proof-of-concept implementation of inline files that stores the file's
content directly in its parent's directory pair.

Inline files are indicated by a different type stored in an entry's
struct field, and take advantage of resizable entries. Where a normal
file's entry would normally hold the reference to the CTZ skip-list, an
inline file's entry contains the contents of the actual file.

Unfortunately, storing the inline file on disk is the easy part. We also
need to manage inline files in the internals of littlefs and provide the
same operations that we do on normal files, all while reusing as much
code as possible to avoid a significant increase in code cost.

There is a relatively simple, though maybe a bit hacky, solution here. If a
file fits entirely in a cache line, the file logic never actually has to go to
disk. This means we can just give the file a "pretend" block (hopefully
one that would assert if ever written to), and carry out file operations
as normal, as long as we catch the file before it exceeds the cache line
and write out the file to an actual disk.
2018-10-09 23:02:09 -05:00
Christopher Haster
9273ac708b Added size field to entry structure
The size field is redundant, since an entry's size can be determined
from the nlen+elen+alen+4. However, as you may have guessed from that
expression, calculating the size this way is a bit roundabout and
inefficient. Despite its redundancy, it's cheaper to store the size in the
entry, though with a minor RAM cost.

Note, extra care must now be taken to make sure these size and len fields
don't fall out of sync.
2018-10-09 23:02:09 -05:00
Christopher Haster
49698e431f 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.

Explicitely separating the data-struct and entry types allows us to
introduce new data structures (inlined files).
2018-10-09 23:02:01 -05:00
Christopher Haster
9637b96069 Fixed lookahead overflow and removed unbounded lookahead pointers
As pointed out by davidefer, the lookahead pointer modular arithmetic
does not work around integer overflow when the pointer size is not a
multiple of the block count.

To avoid overflow problems, the easy solution is to stop trying to
work around integer overflows and keep the lookahead offset inside the
block device. To make this work, the ack was modified into a resetable
counter that is decremented every block allocation.

As a plus, quite a bit of the allocation logic ended up simplified.
2018-04-11 14:38:25 -05:00
Christopher Haster
93ece2e87a Removed outdated note about moves and powerloss 2018-04-08 17:31:05 -05:00
Christopher Haster
a3fd2d4d6d Added more configurable utils
Note: It's still expected to modify lfs_utils.h when porting littlefs
to a new target/system. There's just too much room for system-specific
improvements, such as taking advantage of CRC hardware.

Rather, encouraging modification of lfs_util.h and making it easy to
modify and debug should result in better integration with the consuming
systems.

This just adds a bunch of quality-of-life improvements that should help
development and integration in littlefs.

- Macros that require no side-effects are all-caps
- System includes are only brought in when needed
- Malloc/free wrappers
- LFS_NO_* checks for quickly disabling things at the command line
- At least a little-bit more docs
2018-02-19 01:40:23 -06:00
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
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
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
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
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
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
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
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
Christopher Haster
273cb7c9c8 Fixed problem with lookaheads larger than block device
Simply limiting the lookahead region to the size of
the block device fixes the problem.

Also added logic to limit the allocated region and
floor to nearest word, since the additional memory
couldn't really be used effectively.
2017-09-18 21:20:33 -05:00
Christopher Haster
c2283a2753 Extended entry tag to support attributes
Zero attributes are actually supported at the moment, but this change
will allow entry attribute to be added in a backwards compatible manner.

Each dir entry is now prefixed with a 32 bit tag:
4b - entry type
4b - data structure
8b - entry len
8b - attribute len
8b - name len

A full entry on disk looks a bit like this:
[-  8 -|-  8 -|-  8 -|-  8 -|-- elen --|-- alen --|-- nlen --]
[ type | elen | alen | nlen |  entry   |  attrs   |   name   ]

The actually contents of the attributes section is a bit handwavey
until the first attributes are implemented, but to put plans in place:
Each attribute will be prefixed with only a byte that indicates the type
of attribute. Attributes should be sorted based on portability, since
unknown attributes will force attribute parsing to stop.
2017-07-18 02:09:35 -05:00
Christopher Haster
663e953a50 Adopted the Apache 2.0 license 2017-07-08 11:49:40 -05:00
Christopher Haster
8a9b9baa12 Modified entry head to include name length
This provides a path for adding inlined files in the future, which
requires multiple lengths to distinguish between the file data and name.

As an extra bonus, the directory can now be iterated over even if the
types are unknown, since the name's representation is consistent on all
entry types.

This does come at the cost of reducing types from 16-bits to 8-bits, but
I doubt this will become a problem.
2017-06-28 21:46:34 -05:00
Christopher Haster
fe28ea0f93 Added internal check of data written to disk
Before, the littlefs relied on the underlying block device
to report corruption that occurs when writing data to disk.
This requirement is easy to miss or implement incorrectly, since
the error detection is only required when a block becomes corrupted,
which is very unlikely to happen until late in the block device's
lifetime.

The littlefs can detect corruption itself by reading back written data.
This requires a bit of care to reuse the available buffers, and may rely
on checksums to avoid additional RAM requirements.

This does have a runtime penalty with the extra read operations, but
should make the littlefs much more robust to different implementations.
2017-06-28 15:50:47 -05:00
Christopher Haster
69294ac418 Added better documentation
More documentation may still by worthwhile (design documentation?),
but for now this provides a reasonable baseline.
- readme
- license
- header documentation
2017-05-15 02:32:49 -05:00
Christopher Haster
fd1da602d7 Added support for handling corrupted blocks
This provides a limited form of wear leveling. While wear is
not actually balanced across blocks, the filesystem can recover
from corrupted blocks and extend the lifetime of a device nearly
as much as dynamic wear leveling.

For use-cases where wear is important, it would be better to use
a full form of dynamic wear-leveling at the block level. (or
consider a logging filesystem).

Corrupted block handling was simply added on top of the existing
logic in place for the filesystem, so it's a bit more noodly than
it may have to be, but it gets the work done.
2017-05-15 00:40:56 -05:00
Christopher Haster
b35d761196 Removed words variable from lfs struct 2017-05-08 00:53:08 -05:00
Christopher Haster
4808e9ae26 Added caching with managed caches at the file level
This adds a fully independent layer between the rest of the filesystem
and the block device. This requires some additionally logic around cache
invalidation and flushing, but removes the need for any higher layer to
consider read/write sizes less than what is supported by the hardware.

Additionally, these caches can be used for possible speed improvements.
This is left up to the user to optimize for their use cases. For very
limited embedded systems with byte-level read/writes, the caches could
be omitted completely, or they could even be the size of a full block
for minimizing storage access.

(A full block may not be the best for speed, consider if only a small
portion of the read block is used, but I'll leave that evaluation as an
exercise for any consumers of this library)
2017-05-08 00:44:54 -05:00
Christopher Haster
a30142e0e1 Fixed allocation bugs near the end of storage
Also added better testing specifically for these corner cases
around the end of storage
2017-05-08 00:37:58 -05:00
Christopher Haster
210b487325 Added file list for tracking in flight allocations
Needed primarily for tracking block allocations, unfortunately
this prevents the freedom for the user to bitwise copy files.
2017-05-08 00:32:32 -05:00
Christopher Haster
b55719bab1 Adopted more conventional buffer parameter ordering
Adopted buffer followed by size. The other order was original
chosen due to some other functions with a more complicated
parameter list.

This convention is important, as the bd api is one of the main
apis facing porting efforts.
2017-04-23 23:58:43 -05:00
Christopher Haster
0406442253 Fixed non-standard behaviour of rdwr streams
Originally had two seperate positions for reading/writing,
but this is inconsistent with the the posix standard, which
has a single position for reading and writing.

Also added proper handling of when the file is dirty, just
added an internal flag for this state.

Also moved the entry out of the file struct, and rearranged
some members to clean things up.
2017-04-23 23:39:50 -05:00