diff --git a/SPEC.md b/SPEC.md index 3663ea54..2370ea6d 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,10 +1,10 @@ ## littlefs technical specification -This is the technical specification of the little filesystem. This document -covers the technical details of how the littlefs is stored on disk for -introspection and tooling. This document assumes you are familiar with the -design of the littlefs, for more info on how littlefs works check -out [DESIGN.md](DESIGN.md). +This is the technical specification of the little filesystem with on-disk +version lfs2.1. This document covers the technical details of how the littlefs +is stored on disk for introspection and tooling. This document assumes you are +familiar with the design of the littlefs, for more info on how littlefs works +check out [DESIGN.md](DESIGN.md). ``` | | | .---._____ @@ -133,12 +133,6 @@ tags XORed together, starting with `0xffffffff`. '-------------------' '-------------------' ``` -One last thing to note before we get into the details around tag encoding. Each -tag contains a valid bit used to indicate if the tag and containing commit is -valid. This valid bit is the first bit found in the tag and the commit and can -be used to tell if we've attempted to write to the remaining space in the -block. - Here's a more complete example of metadata block containing 4 entries: ``` @@ -191,6 +185,53 @@ Here's a more complete example of metadata block containing 4 entries: '---- most recent D ``` +Two things to note before we get into the details around tag encoding: + +1. Each tag contains a valid bit used to indicate if the tag and containing + commit is valid. After XORing, this bit should always be zero. + + At the end of each commit, the valid bit of the previous tag is XORed + with the lowest bit in the type field of the CRC tag. This allows + the CRC tag to force the next commit to fail the valid bit test if it + has not yet been written to. + +2. The valid bit alone is not enough info to know if the next commit has been + erased. We don't know the order bits will be programmed in a program block, + so it's possible that the next commit had an attempted program that left the + valid bit unchanged. + + To ensure we only ever program erased bytes, each commit can contain an + optional forward-CRC (FCRC). An FCRC contains a checksum of some amount of + bytes in the next commit at the time it was erased. + + ``` + .-------------------. \ \ + | revision count | | | + |-------------------| | | + | metadata | | | + | | +---. +-- current commit + | | | | | + |-------------------| | | | + | FCRC ---|-. | | + |-------------------| / | | | + | CRC -----|-' / + |-------------------| | + | padding | | padding (does't need CRC) + | | | + |-------------------| \ | \ + | erased? | +-' | + | | | | +-- next commit + | v | / | + | | / + | | + '-------------------' + ``` + + If the FCRC is missing or the checksum does not match, we must assume a + commit was attempted but failed due to power-loss. + + Note that end-of-block commits do not need an FCRC. + ## Metadata tags So in littlefs, 32-bit tags describe every type of metadata. And this means @@ -785,3 +826,41 @@ CRC fields: are made about the contents. --- +#### `0x5ff` LFS_TYPE_FCRC + +Added in lfs2.1, the optional FCRC tag contains a checksum of some amount of +bytes in the next commit at the time it was erased. This allows us to ensure +that we only ever program erased bytes, even if a previous commit failed due +to power-loss. + +When programming a commit, the FCRC size must be at least as large as the +program block size. However, the program block is not saved on disk, and can +change between mounts, so the FCRC size on disk may be different than the +current program block size. + +If the FCRC is missing or the checksum does not match, we must assume a +commit was attempted but failed due to power-loss. + +Layout of the FCRC tag: + +``` + tag data +[-- 32 --][-- 32 --|-- 32 --] +[1|- 11 -| 10 | 10 ][-- 32 --|-- 32 --] + ^ ^ ^ ^ ^- fcrc size ^- fcrc + | | | '- size (8) + | | '------ id (0x3ff) + | '------------ type (0x5ff) + '----------------- valid bit +``` + +FCRC fields: + +1. **FCRC size (32-bits)** - Number of bytes after this commit's CRC tag's + padding to include in the FCRC. + +2. **FCRC (32-bits)** - CRC of the bytes after this commit's CRC tag's padding + when erased. Like the CRC tag, this uses a CRC-32 with a polynomial of + `0x04c11db7` initialized with `0xffffffff`. + +---