You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/04/23 15:31:32 UTC
[incubator-nuttx] branch master updated: fs/littlefs: upgrade
littlefs to v2.2.1
This is an automated email from the ASF dual-hosted git repository.
gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new fe0ba38 fs/littlefs: upgrade littlefs to v2.2.1
fe0ba38 is described below
commit fe0ba38580aad3abbe52dd698bd14f1f7606b308
Author: liuhaitao <li...@xiaomi.com>
AuthorDate: Tue Apr 21 15:22:31 2020 +0800
fs/littlefs: upgrade littlefs to v2.2.1
Since littlefs is in active development, it's not a good idea to use its
source code files directly. So upgrade littlefs v2.2.1 by using the littlefs
tar.gz release package instead.
Change-Id: I16d9cf0b6bca700a54ca86ed11d7c8c7f27a898f
Signed-off-by: liuhaitao <li...@xiaomi.com>
---
fs/Makefile | 4 +-
fs/littlefs/.gitignore | 4 +
fs/littlefs/DESIGN.md | 1225 -----------------
fs/littlefs/Make.defs | 79 +-
fs/littlefs/README.md | 220 ---
fs/littlefs/SPEC.md | 370 ------
fs/littlefs/lfs.c | 3464 ------------------------------------------------
fs/littlefs/lfs.h | 673 ----------
fs/littlefs/lfs_util.c | 83 --
fs/littlefs/lfs_util.h | 275 ----
fs/littlefs/lfs_vfs.c | 145 +-
tools/Directories.mk | 2 +-
12 files changed, 102 insertions(+), 6442 deletions(-)
diff --git a/fs/Makefile b/fs/Makefile
index 2218aac..6989a74 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -99,6 +99,8 @@ $(COBJS): %$(OBJEXT): %.c
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
+context::
+
.depend: Makefile $(SRCS)
$(Q) $(MKDEP) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep
$(Q) touch $@
@@ -109,7 +111,7 @@ clean:
$(call DELFILE, $(BIN))
$(call CLEAN)
-distclean: clean
+distclean:: clean
$(call DELFILE, Make.dep)
$(call DELFILE, .depend)
diff --git a/fs/littlefs/.gitignore b/fs/littlefs/.gitignore
new file mode 100644
index 0000000..90a5e6e
--- /dev/null
+++ b/fs/littlefs/.gitignore
@@ -0,0 +1,4 @@
+/.littlefsunpack
+/littlefs
+/*tar.gz
+/*.zip
diff --git a/fs/littlefs/DESIGN.md b/fs/littlefs/DESIGN.md
deleted file mode 100644
index 125a1b0..0000000
--- a/fs/littlefs/DESIGN.md
+++ /dev/null
@@ -1,1225 +0,0 @@
-## The design of the little filesystem
-
-A little fail-safe filesystem designed for embedded systems.
-
-```
- | | | .---._____
- .-----. | |
---|o |---| littlefs |
---| |---| |
- '-----' '----------'
- | | |
-```
-
-For a bit of backstory, the littlefs was developed with the goal of learning
-more about filesystem design by tackling the relative unsolved problem of
-managing a robust filesystem resilient to power loss on devices
-with limited RAM and ROM.
-
-The embedded systems the littlefs is targeting are usually 32 bit
-microcontrollers with around 32KB of RAM and 512KB of ROM. These are
-often paired with SPI NOR flash chips with about 4MB of flash storage.
-
-Flash itself is a very interesting piece of technology with quite a bit of
-nuance. Unlike most other forms of storage, writing to flash requires two
-operations: erasing and programming. The programming operation is relatively
-cheap, and can be very granular. For NOR flash specifically, byte-level
-programs are quite common. Erasing, however, requires an expensive operation
-that forces the state of large blocks of memory to reset in a destructive
-reaction that gives flash its name. The [Wikipedia entry](https://en.wikipedia.org/wiki/Flash_memory)
-has more information if you are interested in how this works.
-
-This leaves us with an interesting set of limitations that can be simplified
-to three strong requirements:
-
-1. **Power-loss resilient** - This is the main goal of the littlefs and the
- focus of this project.
-
- Embedded systems are usually designed without a shutdown routine and a
- notable lack of user interface for recovery, so filesystems targeting
- embedded systems must be prepared to lose power at any given time.
-
- Despite this state of things, there are very few embedded filesystems that
- handle power loss in a reasonable manner, and most can become corrupted if
- the user is unlucky enough.
-
-2. **Wear leveling** - Due to the destructive nature of flash, most flash
- chips have a limited number of erase cycles, usually in the order of around
- 100,000 erases per block for NOR flash. Filesystems that don't take wear
- into account can easily burn through blocks used to store frequently updated
- metadata.
-
- Consider the [FAT filesystem](https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system),
- which stores a file allocation table (FAT) at a specific offset from the
- beginning of disk. Every block allocation will update this table, and after
- 100,000 updates, the block will likely go bad, rendering the filesystem
- unusable even if there are many more erase cycles available on the storage
- as a whole.
-
-3. **Bounded RAM/ROM** - Even with the design difficulties presented by the
- previous two limitations, we have already seen several flash filesystems
- developed on PCs that handle power loss just fine, such as the
- logging filesystems. However, these filesystems take advantage of the
- relatively cheap access to RAM, and use some rather... opportunistic...
- techniques, such as reconstructing the entire directory structure in RAM.
- These operations make perfect sense when the filesystem's only concern is
- erase cycles, but the idea is a bit silly on embedded systems.
-
- To cater to embedded systems, the littlefs has the simple limitation of
- using only a bounded amount of RAM and ROM. That is, no matter what is
- written to the filesystem, and no matter how large the underlying storage
- is, the littlefs will always use the same amount of RAM and ROM. This
- presents a very unique challenge, and makes presumably simple operations,
- such as iterating through the directory tree, surprisingly difficult.
-
-## Existing designs?
-
-There are of course, many different existing filesystem. Here is a very rough
-summary of the general ideas behind some of them.
-
-Most of the existing filesystems fall into the one big category of filesystem
-designed in the early days of spinny magnet disks. While there is a vast amount
-of interesting technology and ideas in this area, the nature of spinny magnet
-disks encourage properties, such as grouping writes near each other, that don't
-make as much sense on recent storage types. For instance, on flash, write
-locality is not important and can actually increase wear.
-
-One of the most popular designs for flash filesystems is called the
-[logging filesystem](https://en.wikipedia.org/wiki/Log-structured_file_system).
-The flash filesystems [jffs](https://en.wikipedia.org/wiki/JFFS)
-and [yaffs](https://en.wikipedia.org/wiki/YAFFS) are good examples. In a
-logging filesystem, data is not stored in a data structure on disk, but instead
-the changes to the files are stored on disk. This has several neat advantages,
-such as the fact that the data is written in a cyclic log format and naturally
-wear levels as a side effect. And, with a bit of error detection, the entire
-filesystem can easily be designed to be resilient to power loss. The
-journaling component of most modern day filesystems is actually a reduced
-form of a logging filesystem. However, logging filesystems have a difficulty
-scaling as the size of storage increases. And most filesystems compensate by
-caching large parts of the filesystem in RAM, a strategy that is inappropriate
-for embedded systems.
-
-Another interesting filesystem design technique is that of [copy-on-write (COW)](https://en.wikipedia.org/wiki/Copy-on-write).
-A good example of this is the [btrfs](https://en.wikipedia.org/wiki/Btrfs)
-filesystem. COW filesystems can easily recover from corrupted blocks and have
-natural protection against power loss. However, if they are not designed with
-wear in mind, a COW filesystem could unintentionally wear down the root block
-where the COW data structures are synchronized.
-
-## Metadata pairs
-
-The core piece of technology that provides the backbone for the littlefs is
-the concept of metadata pairs. The key idea here is that any metadata that
-needs to be updated atomically is stored on a pair of blocks tagged with
-a revision count and checksum. Every update alternates between these two
-pairs, so that at any time there is always a backup containing the previous
-state of the metadata.
-
-Consider a small example where each metadata pair has a revision count,
-a number as data, and the XOR of the block as a quick checksum. If
-we update the data to a value of 9, and then to a value of 5, here is
-what the pair of blocks may look like after each update:
-```
- block 1 block 2 block 1 block 2 block 1 block 2
-.---------.---------. .---------.---------. .---------.---------.
-| rev: 1 | rev: 0 | | rev: 1 | rev: 2 | | rev: 3 | rev: 2 |
-| data: 3 | data: 0 | -> | data: 3 | data: 9 | -> | data: 5 | data: 9 |
-| xor: 2 | xor: 0 | | xor: 2 | xor: 11 | | xor: 6 | xor: 11 |
-'---------'---------' '---------'---------' '---------'---------'
- let data = 9 let data = 5
-```
-
-After each update, we can find the most up to date value of data by looking
-at the revision count.
-
-Now consider what the blocks may look like if we suddenly lose power while
-changing the value of data to 5:
-```
- block 1 block 2 block 1 block 2 block 1 block 2
-.---------.---------. .---------.---------. .---------.---------.
-| rev: 1 | rev: 0 | | rev: 1 | rev: 2 | | rev: 3 | rev: 2 |
-| data: 3 | data: 0 | -> | data: 3 | data: 9 | -x | data: 3 | data: 9 |
-| xor: 2 | xor: 0 | | xor: 2 | xor: 11 | | xor: 2 | xor: 11 |
-'---------'---------' '---------'---------' '---------'---------'
- let data = 9 let data = 5
- powerloss!!!
-```
-
-In this case, block 1 was partially written with a new revision count, but
-the littlefs hadn't made it to updating the value of data. However, if we
-check our checksum we notice that block 1 was corrupted. So we fall back to
-block 2 and use the value 9.
-
-Using this concept, the littlefs is able to update metadata blocks atomically.
-There are a few other tweaks, such as using a 32 bit CRC and using sequence
-arithmetic to handle revision count overflow, but the basic concept
-is the same. These metadata pairs define the backbone of the littlefs, and the
-rest of the filesystem is built on top of these atomic updates.
-
-## Non-meta data
-
-Now, the metadata pairs do come with some drawbacks. Most notably, each pair
-requires two blocks for each block of data. I'm sure users would be very
-unhappy if their storage was suddenly cut in half! Instead of storing
-everything in these metadata blocks, the littlefs uses a COW data structure
-for files which is in turn pointed to by a metadata block. When
-we update a file, we create copies of any blocks that are modified until
-the metadata blocks are updated with the new copy. Once the metadata block
-points to the new copy, we deallocate the old blocks that are no longer in use.
-
-Here is what updating a one-block file may look like:
-```
- block 1 block 2 block 1 block 2 block 1 block 2
-.---------.---------. .---------.---------. .---------.---------.
-| rev: 1 | rev: 0 | | rev: 1 | rev: 0 | | rev: 1 | rev: 2 |
-| file: 4 | file: 0 | -> | file: 4 | file: 0 | -> | file: 4 | file: 5 |
-| xor: 5 | xor: 0 | | xor: 5 | xor: 0 | | xor: 5 | xor: 7 |
-'---------'---------' '---------'---------' '---------'---------'
- | | |
- v v v
- block 4 block 4 block 5 block 4 block 5
-.--------. .--------. .--------. .--------. .--------.
-| old | | old | | new | | old | | new |
-| data | | data | | data | | data | | data |
-| | | | | | | | | |
-'--------' '--------' '--------' '--------' '--------'
- update data in file update metadata pair
-```
-
-It doesn't matter if we lose power while writing new data to block 5,
-since the old data remains unmodified in block 4. This example also
-highlights how the atomic updates of the metadata blocks provide a
-synchronization barrier for the rest of the littlefs.
-
-At this point, it may look like we are wasting an awfully large amount
-of space on the metadata. Just looking at that example, we are using
-three blocks to represent a file that fits comfortably in one! So instead
-of giving each file a metadata pair, we actually store the metadata for
-all files contained in a single directory in a single metadata block.
-
-Now we could just leave files here, copying the entire file on write
-provides the synchronization without the duplicated memory requirements
-of the metadata blocks. However, we can do a bit better.
-
-## CTZ skip-lists
-
-There are many different data structures for representing the actual
-files in filesystems. Of these, the littlefs uses a rather unique [COW](https://upload.wikimedia.org/wikipedia/commons/0/0c/Cow_female_black_white.jpg)
-data structure that allows the filesystem to reuse unmodified parts of the
-file without additional metadata pairs.
-
-First lets consider storing files in a simple linked-list. What happens when we
-append a block? We have to change the last block in the linked-list to point
-to this new block, which means we have to copy out the last block, and change
-the second-to-last block, and then the third-to-last, and so on until we've
-copied out the entire file.
-
-```
-Exhibit A: A linked-list
-.--------. .--------. .--------. .--------. .--------. .--------.
-| data 0 |->| data 1 |->| data 2 |->| data 4 |->| data 5 |->| data 6 |
-| | | | | | | | | | | |
-| | | | | | | | | | | |
-'--------' '--------' '--------' '--------' '--------' '--------'
-```
-
-To get around this, the littlefs, at its heart, stores files backwards. Each
-block points to its predecessor, with the first block containing no pointers.
-If you think about for a while, it starts to make a bit of sense. Appending
-blocks just point to their predecessor and no other blocks need to be updated.
-If we update a block in the middle, we will need to copy out the blocks that
-follow, but can reuse the blocks before the modified block. Since most file
-operations either reset the file each write or append to files, this design
-avoids copying the file in the most common cases.
-
-```
-Exhibit B: A backwards linked-list
-.--------. .--------. .--------. .--------. .--------. .--------.
-| data 0 |<-| data 1 |<-| data 2 |<-| data 4 |<-| data 5 |<-| data 6 |
-| | | | | | | | | | | |
-| | | | | | | | | | | |
-'--------' '--------' '--------' '--------' '--------' '--------'
-```
-
-However, a backwards linked-list does come with a rather glaring problem.
-Iterating over a file _in order_ has a runtime cost of O(n^2). Gah! A quadratic
-runtime to just _read_ a file? That's awful. Keep in mind reading files is
-usually the most common filesystem operation.
-
-To avoid this problem, the littlefs uses a multilayered linked-list. For
-every nth block where n is divisible by 2^x, the block contains a pointer
-to block n-2^x. So each block contains anywhere from 1 to log2(n) pointers
-that skip to various sections of the preceding list. If you're familiar with
-data-structures, you may have recognized that this is a type of deterministic
-skip-list.
-
-The name comes from the use of the
-[count trailing zeros (CTZ)](https://en.wikipedia.org/wiki/Count_trailing_zeros)
-instruction, which allows us to calculate the power-of-two factors efficiently.
-For a given block n, the block contains ctz(n)+1 pointers.
-
-```
-Exhibit C: A backwards CTZ skip-list
-.--------. .--------. .--------. .--------. .--------. .--------.
-| data 0 |<-| data 1 |<-| data 2 |<-| data 3 |<-| data 4 |<-| data 5 |
-| |<-| |--| |<-| |--| | | |
-| |<-| |--| |--| |--| | | |
-'--------' '--------' '--------' '--------' '--------' '--------'
-```
-
-The additional pointers allow us to navigate the data-structure on disk
-much more efficiently than in a singly linked-list.
-
-Taking exhibit C for example, here is the path from data block 5 to data
-block 1. You can see how data block 3 was completely skipped:
-```
-.--------. .--------. .--------. .--------. .--------. .--------.
-| data 0 | | data 1 |<-| data 2 | | data 3 | | data 4 |<-| data 5 |
-| | | | | |<-| |--| | | |
-| | | | | | | | | | | |
-'--------' '--------' '--------' '--------' '--------' '--------'
-```
-
-The path to data block 0 is even more quick, requiring only two jumps:
-```
-.--------. .--------. .--------. .--------. .--------. .--------.
-| data 0 | | data 1 | | data 2 | | data 3 | | data 4 |<-| data 5 |
-| | | | | | | | | | | |
-| |<-| |--| |--| |--| | | |
-'--------' '--------' '--------' '--------' '--------' '--------'
-```
-
-We can find the runtime complexity by looking at the path to any block from
-the block containing the most pointers. Every step along the path divides
-the search space for the block in half. This gives us a runtime of O(log n).
-To get to the block with the most pointers, we can perform the same steps
-backwards, which puts the runtime at O(2 log n) = O(log n). The interesting
-part about this data structure is that this optimal path occurs naturally
-if we greedily choose the pointer that covers the most distance without passing
-our target block.
-
-So now we have a representation of files that can be appended trivially with
-a runtime of O(1), and can be read with a worst case runtime of O(n log n).
-Given that the the runtime is also divided by the amount of data we can store
-in a block, this is pretty reasonable.
-
-Unfortunately, the CTZ skip-list comes with a few questions that aren't
-straightforward to answer. What is the overhead? How do we handle more
-pointers than we can store in a block? How do we store the skip-list in
-a directory entry?
-
-One way to find the overhead per block is to look at the data structure as
-multiple layers of linked-lists. Each linked-list skips twice as many blocks
-as the previous linked-list. Another way of looking at it is that each
-linked-list uses half as much storage per block as the previous linked-list.
-As we approach infinity, the number of pointers per block forms a geometric
-series. Solving this geometric series gives us an average of only 2 pointers
-per block.
-
-![overhead_per_block](https://latex.codecogs.com/svg.latex?%5Clim_%7Bn%5Cto%5Cinfty%7D%5Cfrac%7B1%7D%7Bn%7D%5Csum_%7Bi%3D0%7D%5E%7Bn%7D%5Cleft%28%5Ctext%7Bctz%7D%28i%29+1%5Cright%29%20%3D%20%5Csum_%7Bi%3D0%7D%5Cfrac%7B1%7D%7B2%5Ei%7D%20%3D%202)
-
-Finding the maximum number of pointers in a block is a bit more complicated,
-but since our file size is limited by the integer width we use to store the
-size, we can solve for it. Setting the overhead of the maximum pointers equal
-to the block size we get the following equation. Note that a smaller block size
-results in more pointers, and a larger word width results in larger pointers.
-
-![maximum overhead](https://latex.codecogs.com/svg.latex?B%20%3D%20%5Cfrac%7Bw%7D%7B8%7D%5Cleft%5Clceil%5Clog_2%5Cleft%28%5Cfrac%7B2%5Ew%7D%7BB-2%5Cfrac%7Bw%7D%7B8%7D%7D%5Cright%29%5Cright%5Crceil)
-
-where:
-B = block size in bytes
-w = word width in bits
-
-Solving the equation for B gives us the minimum block size for various word
-widths:
-32 bit CTZ skip-list = minimum block size of 104 bytes
-64 bit CTZ skip-list = minimum block size of 448 bytes
-
-Since littlefs uses a 32 bit word size, we are limited to a minimum block
-size of 104 bytes. This is a perfectly reasonable minimum block size, with most
-block sizes starting around 512 bytes. So we can avoid additional logic to
-avoid overflowing our block's capacity in the CTZ skip-list.
-
-So, how do we store the skip-list in a directory entry? A naive approach would
-be to store a pointer to the head of the skip-list, the length of the file
-in bytes, the index of the head block in the skip-list, and the offset in the
-head block in bytes. However this is a lot of information, and we can observe
-that a file size maps to only one block index + offset pair. So it should be
-sufficient to store only the pointer and file size.
-
-But there is one problem, calculating the block index + offset pair from a
-file size doesn't have an obvious implementation.
-
-We can start by just writing down an equation. The first idea that comes to
-mind is to just use a for loop to sum together blocks until we reach our
-file size. We can write this equation as a summation:
-
-![summation1](https://latex.codecogs.com/svg.latex?N%20%3D%20%5Csum_i%5En%5Cleft%5BB-%5Cfrac%7Bw%7D%7B8%7D%5Cleft%28%5Ctext%7Bctz%7D%28i%29+1%5Cright%29%5Cright%5D)
-
-where:
-B = block size in bytes
-w = word width in bits
-n = block index in skip-list
-N = file size in bytes
-
-And this works quite well, but is not trivial to calculate. This equation
-requires O(n) to compute, which brings the entire runtime of reading a file
-to O(n^2 log n). Fortunately, the additional O(n) does not need to touch disk,
-so it is not completely unreasonable. But if we could solve this equation into
-a form that is easily computable, we can avoid a big slowdown.
-
-Unfortunately, the summation of the CTZ instruction presents a big challenge.
-How would you even begin to reason about integrating a bitwise instruction?
-Fortunately, there is a powerful tool I've found useful in these situations:
-The [On-Line Encyclopedia of Integer Sequences (OEIS)](https://oeis.org/).
-If we work out the first couple of values in our summation, we find that CTZ
-maps to [A001511](https://oeis.org/A001511), and its partial summation maps
-to [A005187](https://oeis.org/A005187), and surprisingly, both of these
-sequences have relatively trivial equations! This leads us to a rather
-unintuitive property:
-
-![mindblown](https://latex.codecogs.com/svg.latex?%5Csum_i%5En%5Cleft%28%5Ctext%7Bctz%7D%28i%29+1%5Cright%29%20%3D%202n-%5Ctext%7Bpopcount%7D%28n%29)
-
-where:
-ctz(x) = the number of trailing bits that are 0 in x
-popcount(x) = the number of bits that are 1 in x
-
-It's a bit bewildering that these two seemingly unrelated bitwise instructions
-are related by this property. But if we start to dissect this equation we can
-see that it does hold. As n approaches infinity, we do end up with an average
-overhead of 2 pointers as we find earlier. And popcount seems to handle the
-error from this average as it accumulates in the CTZ skip-list.
-
-Now we can substitute into the original equation to get a trivial equation
-for a file size:
-
-![summation2](https://latex.codecogs.com/svg.latex?N%20%3D%20Bn%20-%20%5Cfrac%7Bw%7D%7B8%7D%5Cleft%282n-%5Ctext%7Bpopcount%7D%28n%29%5Cright%29)
-
-Unfortunately, we're not quite done. The popcount function is non-injective,
-so we can only find the file size from the block index, not the other way
-around. However, we can solve for an n' block index that is greater than n
-with an error bounded by the range of the popcount function. We can then
-repeatedly substitute this n' into the original equation until the error
-is smaller than the integer division. As it turns out, we only need to
-perform this substitution once. Now we directly calculate our block index:
-
-![formulaforn](https://latex.codecogs.com/svg.latex?n%20%3D%20%5Cleft%5Clfloor%5Cfrac%7BN-%5Cfrac%7Bw%7D%7B8%7D%5Cleft%28%5Ctext%7Bpopcount%7D%5Cleft%28%5Cfrac%7BN%7D%7BB-2%5Cfrac%7Bw%7D%7B8%7D%7D-1%5Cright%29+2%5Cright%29%7D%7BB-2%5Cfrac%7Bw%7D%7B8%7D%7D%5Cright%5Crfloor)
-
-Now that we have our block index n, we can just plug it back into the above
-equation to find the offset. However, we do need to rearrange the equation
-a bit to avoid integer overflow:
-
-![formulaforoff](https://latex.codecogs.com/svg.latex?%5Cmathit%7Boff%7D%20%3D%20N%20-%20%5Cleft%28B-2%5Cfrac%7Bw%7D%7B8%7D%5Cright%29n%20-%20%5Cfrac%7Bw%7D%7B8%7D%5Ctext%7Bpopcount%7D%28n%29)
-
-The solution involves quite a bit of math, but computers are very good at math.
-Now we can solve for both the block index and offset from the file size in O(1).
-
-Here is what it might look like to update a file stored with a CTZ skip-list:
-```
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 0 |
- | file: 6 | file: 0 |
- | size: 4 | size: 0 |
- | xor: 3 | xor: 0 |
- '---------'---------'
- |
- v
- block 3 block 4 block 5 block 6
-.--------. .--------. .--------. .--------.
-| data 0 |<-| data 1 |<-| data 2 |<-| data 3 |
-| |<-| |--| | | |
-| | | | | | | |
-'--------' '--------' '--------' '--------'
-
-| update data in file
-v
-
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 0 |
- | file: 6 | file: 0 |
- | size: 4 | size: 0 |
- | xor: 3 | xor: 0 |
- '---------'---------'
- |
- v
- block 3 block 4 block 5 block 6
-.--------. .--------. .--------. .--------.
-| data 0 |<-| data 1 |<-| old |<-| old |
-| |<-| |--| data 2 | | data 3 |
-| | | | | | | |
-'--------' '--------' '--------' '--------'
- ^ ^ ^
- | | | block 7 block 8 block 9 block 10
- | | | .--------. .--------. .--------. .--------.
- | | '----| new |<-| new |<-| new |<-| new |
- | '----------------| data 2 |<-| data 3 |--| data 4 | | data 5 |
- '------------------| |--| |--| | | |
- '--------' '--------' '--------' '--------'
-
-| update metadata pair
-v
-
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 2 |
- | file: 6 | file: 10|
- | size: 4 | size: 6 |
- | xor: 3 | xor: 14 |
- '---------'---------'
- |
- |
- block 3 block 4 block 5 block 6 |
-.--------. .--------. .--------. .--------. |
-| data 0 |<-| data 1 |<-| old |<-| old | |
-| |<-| |--| data 2 | | data 3 | |
-| | | | | | | | |
-'--------' '--------' '--------' '--------' |
- ^ ^ ^ v
- | | | block 7 block 8 block 9 block 10
- | | | .--------. .--------. .--------. .--------.
- | | '----| new |<-| new |<-| new |<-| new |
- | '----------------| data 2 |<-| data 3 |--| data 4 | | data 5 |
- '------------------| |--| |--| | | |
- '--------' '--------' '--------' '--------'
-```
-
-## Block allocation
-
-So those two ideas provide the grounds for the filesystem. The metadata pairs
-give us directories, and the CTZ skip-lists give us files. But this leaves
-one big [elephant](https://upload.wikimedia.org/wikipedia/commons/3/37/African_Bush_Elephant.jpg)
-of a question. How do we get those blocks in the first place?
-
-One common strategy is to store unallocated blocks in a big free list, and
-initially the littlefs was designed with this in mind. By storing a reference
-to the free list in every single metadata pair, additions to the free list
-could be updated atomically at the same time the replacement blocks were
-stored in the metadata pair. During boot, every metadata pair had to be
-scanned to find the most recent free list, but once the list was found the
-state of all free blocks becomes known.
-
-However, this approach had several issues:
-
-- There was a lot of nuanced logic for adding blocks to the free list without
- modifying the blocks, since the blocks remain active until the metadata is
- updated.
-- The free list had to support both additions and removals in FIFO order while
- minimizing block erases.
-- The free list had to handle the case where the file system completely ran
- out of blocks and may no longer be able to add blocks to the free list.
-- If we used a revision count to track the most recently updated free list,
- metadata blocks that were left unmodified were ticking time bombs that would
- cause the system to go haywire if the revision count overflowed.
-- Every single metadata block wasted space to store these free list references.
-
-Actually, to simplify, this approach had one massive glaring issue: complexity.
-
-> Complexity leads to fallibility.
-> Fallibility leads to unmaintainability.
-> Unmaintainability leads to suffering.
-
-Or at least, complexity leads to increased code size, which is a problem
-for embedded systems.
-
-In the end, the littlefs adopted more of a "drop it on the floor" strategy.
-That is, the littlefs doesn't actually store information about which blocks
-are free on the storage. The littlefs already stores which files _are_ in
-use, so to find a free block, the littlefs just takes all of the blocks that
-exist and subtract the blocks that are in use.
-
-Of course, it's not quite that simple. Most filesystems that adopt this "drop
-it on the floor" strategy either rely on some properties inherent to the
-filesystem, such as the cyclic-buffer structure of logging filesystems,
-or use a bitmap or table stored in RAM to track free blocks, which scales
-with the size of storage and is problematic when you have limited RAM. You
-could iterate through every single block in storage and check it against
-every single block in the filesystem on every single allocation, but that
-would have an abhorrent runtime.
-
-So the littlefs compromises. It doesn't store a bitmap the size of the storage,
-but it does store a little bit-vector that contains a fixed set lookahead
-for block allocations. During a block allocation, the lookahead vector is
-checked for any free blocks. If there are none, the lookahead region jumps
-forward and the entire filesystem is scanned for free blocks.
-
-Here's what it might look like to allocate 4 blocks on a decently busy
-filesystem with a 32bit lookahead and a total of
-128 blocks (512Kbytes of storage if blocks are 4Kbyte):
-```
-boot... lookahead:
- fs blocks: fffff9fffffffffeffffffffffff0000
-scanning... lookahead: fffff9ff
- fs blocks: fffff9fffffffffeffffffffffff0000
-alloc = 21 lookahead: fffffdff
- fs blocks: fffffdfffffffffeffffffffffff0000
-alloc = 22 lookahead: ffffffff
- fs blocks: fffffffffffffffeffffffffffff0000
-scanning... lookahead: fffffffe
- fs blocks: fffffffffffffffeffffffffffff0000
-alloc = 63 lookahead: ffffffff
- fs blocks: ffffffffffffffffffffffffffff0000
-scanning... lookahead: ffffffff
- fs blocks: ffffffffffffffffffffffffffff0000
-scanning... lookahead: ffffffff
- fs blocks: ffffffffffffffffffffffffffff0000
-scanning... lookahead: ffff0000
- fs blocks: ffffffffffffffffffffffffffff0000
-alloc = 112 lookahead: ffff8000
- fs blocks: ffffffffffffffffffffffffffff8000
-```
-
-While this lookahead approach still has an asymptotic runtime of O(n^2) to
-scan all of storage, the lookahead reduces the practical runtime to a
-reasonable amount. Bit-vectors are surprisingly compact, given only 16 bytes,
-the lookahead could track 128 blocks. For a 4Mbyte flash chip with 4Kbyte
-blocks, the littlefs would only need 8 passes to scan the entire storage.
-
-The real benefit of this approach is just how much it simplified the design
-of the littlefs. Deallocating blocks is as simple as simply forgetting they
-exist, and there is absolutely no concern of bugs in the deallocation code
-causing difficult to detect memory leaks.
-
-## Directories
-
-Now we just need directories to store our files. Since we already have
-metadata blocks that store information about files, lets just use these
-metadata blocks as the directories. Maybe turn the directories into linked
-lists of metadata blocks so it isn't limited by the number of files that fit
-in a single block. Add entries that represent other nested directories.
-Drop "." and ".." entries, cause who needs them. Dust off our hands and
-we now have a directory tree.
-
-```
- .--------.
- |root dir|
- | pair 0 |
- | |
- '--------'
- .-' '-------------------------.
- v v
- .--------. .--------. .--------.
- | dir A |------->| dir A | | dir B |
- | pair 0 | | pair 1 | | pair 0 |
- | | | | | |
- '--------' '--------' '--------'
- .-' '-. | .-' '-.
- v v v v v
-.--------. .--------. .--------. .--------. .--------.
-| file C | | file D | | file E | | file F | | file G |
-| | | | | | | | | |
-| | | | | | | | | |
-'--------' '--------' '--------' '--------' '--------'
-```
-
-Unfortunately it turns out it's not that simple. See, iterating over a
-directory tree isn't actually all that easy, especially when you're trying
-to fit in a bounded amount of RAM, which rules out any recursive solution.
-And since our block allocator involves iterating over the entire filesystem
-tree, possibly multiple times in a single allocation, iteration needs to be
-efficient.
-
-So, as a solution, the littlefs adopted a sort of threaded tree. Each
-directory not only contains pointers to all of its children, but also a
-pointer to the next directory. These pointers create a linked-list that
-is threaded through all of the directories in the filesystem. Since we
-only use this linked list to check for existence, the order doesn't actually
-matter. As an added plus, we can repurpose the pointer for the individual
-directory linked-lists and avoid using any additional space.
-
-```
- .--------.
- |root dir|-.
- | pair 0 | |
- .--------| |-'
- | '--------'
- | .-' '-------------------------.
- | v v
- | .--------. .--------. .--------.
- '->| dir A |------->| dir A |------->| dir B |
- | pair 0 | | pair 1 | | pair 0 |
- | | | | | |
- '--------' '--------' '--------'
- .-' '-. | .-' '-.
- v v v v v
-.--------. .--------. .--------. .--------. .--------.
-| file C | | file D | | file E | | file F | | file G |
-| | | | | | | | | |
-| | | | | | | | | |
-'--------' '--------' '--------' '--------' '--------'
-```
-
-This threaded tree approach does come with a few tradeoffs. Now, anytime we
-want to manipulate the directory tree, we find ourselves having to update two
-pointers instead of one. For anyone familiar with creating atomic data
-structures this should set off a whole bunch of red flags.
-
-But unlike the data structure guys, we can update a whole block atomically! So
-as long as we're really careful (and cheat a little bit), we can still
-manipulate the directory tree in a way that is resilient to power loss.
-
-Consider how we might add a new directory. Since both pointers that reference
-it can come from the same directory, we only need a single atomic update to
-finagle the directory into the filesystem:
-```
- .--------.
- |root dir|-.
- | pair 0 | |
-.--| |-'
-| '--------'
-| |
-| v
-| .--------.
-'->| dir A |
- | pair 0 |
- | |
- '--------'
-
-| create the new directory block
-v
-
- .--------.
- |root dir|-.
- | pair 0 | |
- .--| |-'
- | '--------'
- | |
- | v
- | .--------.
-.--------. '->| dir A |
-| dir B |---->| pair 0 |
-| pair 0 | | |
-| | '--------'
-'--------'
-
-| update root to point to directory B
-v
-
- .--------.
- |root dir|-.
- | pair 0 | |
-.--------| |-'
-| '--------'
-| .-' '-.
-| v v
-| .--------. .--------.
-'->| dir B |->| dir A |
- | pair 0 | | pair 0 |
- | | | |
- '--------' '--------'
-```
-
-Note that even though directory B was added after directory A, we insert
-directory B before directory A in the linked-list because it is convenient.
-
-Now how about removal:
-```
- .--------. .--------.
- |root dir|------->|root dir|-.
- | pair 0 | | pair 1 | |
-.--------| |--------| |-'
-| '--------' '--------'
-| .-' '-. |
-| v v v
-| .--------. .--------. .--------.
-'->| dir A |->| dir B |->| dir C |
- | pair 0 | | pair 0 | | pair 0 |
- | | | | | |
- '--------' '--------' '--------'
-
-| update root to no longer contain directory B
-v
-
- .--------. .--------.
- |root dir|------------->|root dir|-.
- | pair 0 | | pair 1 | |
-.--| |--------------| |-'
-| '--------' '--------'
-| | |
-| v v
-| .--------. .--------. .--------.
-'->| dir A |->| dir B |->| dir C |
- | pair 0 | | pair 0 | | pair 0 |
- | | | | | |
- '--------' '--------' '--------'
-
-| remove directory B from the linked-list
-v
-
- .--------. .--------.
- |root dir|->|root dir|-.
- | pair 0 | | pair 1 | |
-.--| |--| |-'
-| '--------' '--------'
-| | |
-| v v
-| .--------. .--------.
-'->| dir A |->| dir C |
- | pair 0 | | pair 0 |
- | | | |
- '--------' '--------'
-```
-
-Wait, wait, wait, that's not atomic at all! If power is lost after removing
-directory B from the root, directory B is still in the linked-list. We've
-just created a memory leak!
-
-And to be honest, I don't have a clever solution for this case. As a
-side-effect of using multiple pointers in the threaded tree, the littlefs
-can end up with orphan blocks that have no parents and should have been
-removed.
-
-To keep these orphan blocks from becoming a problem, the littlefs has a
-deorphan step that simply iterates through every directory in the linked-list
-and checks it against every directory entry in the filesystem to see if it
-has a parent. The deorphan step occurs on the first block allocation after
-boot, so orphans should never cause the littlefs to run out of storage
-prematurely. Note that the deorphan step never needs to run in a read-only
-filesystem.
-
-## The move problem
-
-Now we have a real problem. How do we move things between directories while
-remaining power resilient? Even looking at the problem from a high level,
-it seems impossible. We can update directory blocks atomically, but atomically
-updating two independent directory blocks is not an atomic operation.
-
-Here's the steps the filesystem may go through to move a directory:
-```
- .--------.
- |root dir|-.
- | pair 0 | |
-.--------| |-'
-| '--------'
-| .-' '-.
-| v v
-| .--------. .--------.
-'->| dir A |->| dir B |
- | pair 0 | | pair 0 |
- | | | |
- '--------' '--------'
-
-| update directory B to point to directory A
-v
-
- .--------.
- |root dir|-.
- | pair 0 | |
-.--------| |-'
-| '--------'
-| .-----' '-.
-| | v
-| | .--------.
-| | .->| dir B |
-| | | | pair 0 |
-| | | | |
-| | | '--------'
-| | .-------'
-| v v |
-| .--------. |
-'->| dir A |-'
- | pair 0 |
- | |
- '--------'
-
-| update root to no longer contain directory A
-v
- .--------.
- |root dir|-.
- | pair 0 | |
-.----| |-'
-| '--------'
-| |
-| v
-| .--------.
-| .->| dir B |
-| | | pair 0 |
-| '--| |-.
-| '--------' |
-| | |
-| v |
-| .--------. |
-'--->| dir A |-'
- | pair 0 |
- | |
- '--------'
-```
-
-We can leave any orphans up to the deorphan step to collect, but that doesn't
-help the case where dir A has both dir B and the root dir as parents if we
-lose power inconveniently.
-
-Initially, you might think this is fine. Dir A _might_ end up with two parents,
-but the filesystem will still work as intended. But then this raises the
-question of what do we do when the dir A wears out? For other directory blocks
-we can update the parent pointer, but for a dir with two parents we would need
-work out how to update both parents. And the check for multiple parents would
-need to be carried out for every directory, even if the directory has never
-been moved.
-
-It also presents a bad user-experience, since the condition of ending up with
-two parents is rare, it's unlikely user-level code will be prepared. Just think
-about how a user would recover from a multi-parented directory. They can't just
-remove one directory, since remove would report the directory as "not empty".
-
-Other atomic filesystems simple COW the entire directory tree. But this
-introduces a significant bit of complexity, which leads to code size, along
-with a surprisingly expensive runtime cost during what most users assume is
-a single pointer update.
-
-Another option is to update the directory block we're moving from to point
-to the destination with a sort of predicate that we have moved if the
-destination exists. Unfortunately, the omnipresent concern of wear could
-cause any of these directory entries to change blocks, and changing the
-entry size before a move introduces complications if it spills out of
-the current directory block.
-
-So how do we go about moving a directory atomically?
-
-We rely on the improbableness of power loss.
-
-Power loss during a move is certainly possible, but it's actually relatively
-rare. Unless a device is writing to a filesystem constantly, it's unlikely that
-a power loss will occur during filesystem activity. We still need to handle
-the condition, but runtime during a power loss takes a back seat to the runtime
-during normal operations.
-
-So what littlefs does is inelegantly simple. When littlefs moves a file, it
-marks the file as "moving". This is stored as a single bit in the directory
-entry and doesn't take up much space. Then littlefs moves the directory,
-finishing with the complete remove of the "moving" directory entry.
-
-```
- .--------.
- |root dir|-.
- | pair 0 | |
-.--------| |-'
-| '--------'
-| .-' '-.
-| v v
-| .--------. .--------.
-'->| dir A |->| dir B |
- | pair 0 | | pair 0 |
- | | | |
- '--------' '--------'
-
-| update root directory to mark directory A as moving
-v
-
- .----------.
- |root dir |-.
- | pair 0 | |
-.-------| moving A!|-'
-| '----------'
-| .-' '-.
-| v v
-| .--------. .--------.
-'->| dir A |->| dir B |
- | pair 0 | | pair 0 |
- | | | |
- '--------' '--------'
-
-| update directory B to point to directory A
-v
-
- .----------.
- |root dir |-.
- | pair 0 | |
-.-------| moving A!|-'
-| '----------'
-| .-----' '-.
-| | v
-| | .--------.
-| | .->| dir B |
-| | | | pair 0 |
-| | | | |
-| | | '--------'
-| | .-------'
-| v v |
-| .--------. |
-'->| dir A |-'
- | pair 0 |
- | |
- '--------'
-
-| update root to no longer contain directory A
-v
- .--------.
- |root dir|-.
- | pair 0 | |
-.----| |-'
-| '--------'
-| |
-| v
-| .--------.
-| .->| dir B |
-| | | pair 0 |
-| '--| |-.
-| '--------' |
-| | |
-| v |
-| .--------. |
-'--->| dir A |-'
- | pair 0 |
- | |
- '--------'
-```
-
-Now, if we run into a directory entry that has been marked as "moved", one
-of two things is possible. Either the directory entry exists elsewhere in the
-filesystem, or it doesn't. This is a O(n) operation, but only occurs in the
-unlikely case we lost power during a move.
-
-And we can easily fix the "moved" directory entry. Since we're already scanning
-the filesystem during the deorphan step, we can also check for moved entries.
-If we find one, we either remove the "moved" marking or remove the whole entry
-if it exists elsewhere in the filesystem.
-
-## Wear awareness
-
-So now that we have all of the pieces of a filesystem, we can look at a more
-subtle attribute of embedded storage: The wear down of flash blocks.
-
-The first concern for the littlefs, is that perfectly valid blocks can suddenly
-become unusable. As a nice side-effect of using a COW data-structure for files,
-we can simply move on to a different block when a file write fails. All
-modifications to files are performed in copies, so we will only replace the
-old file when we are sure none of the new file has errors. Directories, on
-the other hand, need a different strategy.
-
-The solution to directory corruption in the littlefs relies on the redundant
-nature of the metadata pairs. If an error is detected during a write to one
-of the metadata pairs, we seek out a new block to take its place. Once we find
-a block without errors, we iterate through the directory tree, updating any
-references to the corrupted metadata pair to point to the new metadata block.
-Just like when we remove directories, we can lose power during this operation
-and end up with a desynchronized metadata pair in our filesystem. And just like
-when we remove directories, we leave the possibility of a desynchronized
-metadata pair up to the deorphan step to clean up.
-
-Here's what encountering a directory error may look like with all of
-the directories and directory pointers fully expanded:
-```
- root dir
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 0 |--.
- | | |-.|
-.------| | |-|'
-|.-----| | |-'
-|| '---------'---------'
-|| |||||'--------------------------------------------------.
-|| ||||'-----------------------------------------. |
-|| |||'-----------------------------. | |
-|| ||'--------------------. | | |
-|| |'-------. | | | |
-|| v v v v v v
-|| dir A dir B dir C
-|| block 3 block 4 block 5 block 6 block 7 block 8
-|| .---------.---------. .---------.---------. .---------.---------.
-|'->| rev: 1 | rev: 0 |->| rev: 1 | rev: 0 |->| rev: 1 | rev: 0 |
-'-->| | |->| | |->| | |
- | | | | | | |
- | | | | | | | | |
- '---------'---------' '---------'---------' '---------'---------'
-
-| update directory B
-v
-
- root dir
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 0 |--.
- | | |-.|
-.------| | |-|'
-|.-----| | |-'
-|| '---------'---------'
-|| |||||'--------------------------------------------------.
-|| ||||'-----------------------------------------. |
-|| |||'-----------------------------. | |
-|| ||'--------------------. | | |
-|| |'-------. | | | |
-|| v v v v v v
-|| dir A dir B dir C
-|| block 3 block 4 block 5 block 6 block 7 block 8
-|| .---------.---------. .---------.---------. .---------.---------.
-|'->| rev: 1 | rev: 0 |->| rev: 1 | rev: 2 |->| rev: 1 | rev: 0 |
-'-->| | |->| | corrupt!|->| | |
- | | | | | corrupt!| | | |
- | | | | | corrupt!| | | |
- '---------'---------' '---------'---------' '---------'---------'
-
-| oh no! corruption detected
-v allocate a replacement block
-
- root dir
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 0 |--.
- | | |-.|
-.------| | |-|'
-|.-----| | |-'
-|| '---------'---------'
-|| |||||'----------------------------------------------------.
-|| ||||'-------------------------------------------. |
-|| |||'-----------------------------. | |
-|| ||'--------------------. | | |
-|| |'-------. | | | |
-|| v v v v v v
-|| dir A dir B dir C
-|| block 3 block 4 block 5 block 6 block 7 block 8
-|| .---------.---------. .---------.---------. .---------.---------.
-|'->| rev: 1 | rev: 0 |->| rev: 1 | rev: 2 |--->| rev: 1 | rev: 0 |
-'-->| | |->| | corrupt!|--->| | |
- | | | | | corrupt!| .->| | |
- | | | | | corrupt!| | | | |
- '---------'---------' '---------'---------' | '---------'---------'
- block 9 |
- .---------. |
- | rev: 2 |-'
- | |
- | |
- | |
- '---------'
-
-| update root directory to contain block 9
-v
-
- root dir
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 2 |--.
- | | |-.|
-.-----| | |-|'
-|.----| | |-'
-|| '---------'---------'
-|| .--------'||||'----------------------------------------------.
-|| | |||'-------------------------------------. |
-|| | ||'-----------------------. | |
-|| | |'------------. | | |
-|| | | | | | |
-|| v v v v v v
-|| dir A dir B dir C
-|| block 3 block 4 block 5 block 9 block 7 block 8
-|| .---------.---------. .---------. .---------. .---------.---------.
-|'->| rev: 1 | rev: 0 |-->| rev: 1 |-| rev: 2 |--->| rev: 1 | rev: 0 |
-'-->| | |-. | | | |--->| | |
- | | | | | | | | .->| | |
- | | | | | | | | | | | |
- '---------'---------' | '---------' '---------' | '---------'---------'
- | block 6 |
- | .---------. |
- '------------>| rev: 2 |-'
- | corrupt!|
- | corrupt!|
- | corrupt!|
- '---------'
-
-| remove corrupted block from linked-list
-v
-
- root dir
- block 1 block 2
- .---------.---------.
- | rev: 1 | rev: 2 |--.
- | | |-.|
-.-----| | |-|'
-|.----| | |-'
-|| '---------'---------'
-|| .--------'||||'-----------------------------------------.
-|| | |||'--------------------------------. |
-|| | ||'--------------------. | |
-|| | |'-----------. | | |
-|| | | | | | |
-|| v v v v v v
-|| dir A dir B dir C
-|| block 3 block 4 block 5 block 9 block 7 block 8
-|| .---------.---------. .---------.---------. .---------.---------.
-|'->| rev: 1 | rev: 2 |->| rev: 1 | rev: 2 |->| rev: 1 | rev: 0 |
-'-->| | |->| | |->| | |
- | | | | | | | | |
- | | | | | | | | |
- '---------'---------' '---------'---------' '---------'---------'
-```
-
-Also one question I've been getting is, what about the root directory?
-It can't move so wouldn't the filesystem die as soon as the root blocks
-develop errors? And you would be correct. So instead of storing the root
-in the first few blocks of the storage, the root is actually pointed to
-by the superblock. The superblock contains a few bits of static data, but
-outside of when the filesystem is formatted, it is only updated when the root
-develops errors and needs to be moved.
-
-## Wear leveling
-
-The second concern for the littlefs is that blocks in the filesystem may wear
-unevenly. In this situation, a filesystem may meet an early demise where
-there are no more non-corrupted blocks that aren't in use. It's common to
-have files that were written once and left unmodified, wasting the potential
-erase cycles of the blocks it sits on.
-
-Wear leveling is a term that describes distributing block writes evenly to
-avoid the early termination of a flash part. There are typically two levels
-of wear leveling:
-1. Dynamic wear leveling - Wear is distributed evenly across all **dynamic**
- blocks. Usually this is accomplished by simply choosing the unused block
- with the lowest amount of wear. Note this does not solve the problem of
- static data.
-2. Static wear leveling - Wear is distributed evenly across all **dynamic**
- and **static** blocks. Unmodified blocks may be evicted for new block
- writes. This does handle the problem of static data but may lead to
- wear amplification.
-
-In littlefs's case, it's possible to use the revision count on metadata pairs
-to approximate the wear of a metadata block. And combined with the COW nature
-of files, littlefs could provide your usual implementation of dynamic wear
-leveling.
-
-However, the littlefs does not. This is for a few reasons. Most notably, even
-if the littlefs did implement dynamic wear leveling, this would still not
-handle the case of write-once files, and near the end of the lifetime of a
-flash device, you would likely end up with uneven wear on the blocks anyways.
-
-As a flash device reaches the end of its life, the metadata blocks will
-naturally be the first to go since they are updated most often. In this
-situation, the littlefs is designed to simply move on to another set of
-metadata blocks. This travelling means that at the end of a flash device's
-life, the filesystem will have worn the device down nearly as evenly as the
-usual dynamic wear leveling could. More aggressive wear leveling would come
-with a code-size cost for marginal benefit.
-
-
-One important takeaway to note, if your storage stack uses highly sensitive
-storage such as NAND flash, static wear leveling is the only valid solution.
-In most cases you are going to be better off using a full [flash translation layer (FTL)](https://en.wikipedia.org/wiki/Flash_translation_layer).
-NAND flash already has many limitations that make it poorly suited for an
-embedded system: low erase cycles, very large blocks, errors that can develop
-even during reads, errors that can develop during writes of neighboring blocks.
-Managing sensitive storage such as NAND flash is out of scope for the littlefs.
-The littlefs does have some properties that may be beneficial on top of a FTL,
-such as limiting the number of writes where possible, but if you have the
-storage requirements that necessitate the need of NAND flash, you should have
-the RAM to match and just use an FTL or flash filesystem.
-
-## Summary
-
-So, to summarize:
-
-1. The littlefs is composed of directory blocks
-2. Each directory is a linked-list of metadata pairs
-3. These metadata pairs can be updated atomically by alternating which
- metadata block is active
-4. Directory blocks contain either references to other directories or files
-5. Files are represented by copy-on-write CTZ skip-lists which support O(1)
- append and O(n log n) reading
-6. Blocks are allocated by scanning the filesystem for used blocks in a
- fixed-size lookahead region that is stored in a bit-vector
-7. To facilitate scanning the filesystem, all directories are part of a
- linked-list that is threaded through the entire filesystem
-8. If a block develops an error, the littlefs allocates a new block, and
- moves the data and references of the old block to the new.
-9. Any case where an atomic operation is not possible, mistakes are resolved
- by a deorphan step that occurs on the first allocation after boot
-
-That's the little filesystem. Thanks for reading!
diff --git a/fs/littlefs/Make.defs b/fs/littlefs/Make.defs
index d02e529..fb21f06 100644
--- a/fs/littlefs/Make.defs
+++ b/fs/littlefs/Make.defs
@@ -1,55 +1,52 @@
-#############################################################################
+############################################################################
# fs/littlefs/Make.defs
#
-# This file is a part of NuttX:
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
#
-# Copyright (C) 2019 Gregory Nutt. All rights reserved.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# Ported by:
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
#
-# Copyright (C) 2019 Pinecone Inc. All rights reserved.
-# Author: lihaichen <li...@163.com>
-#
-# This port derives from ARM mbed logic which has a compatible 3-clause
-# BSD license:
-#
-# Copyright (c) 2017, Arm Limited. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in
-# the documentation and/or other materials provided with the
-# distribution.
-# 3. Neither the names ARM, NuttX nor the names of its contributors may be
-# used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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.
-#
-#############################################################################
+############################################################################
ifeq ($(CONFIG_FS_LITTLEFS),y)
# Files required for littlefs file system support
-CSRCS += lfs.c lfs_util.c lfs_vfs.c
+CSRCS += lfs_vfs.c
DEPPATH += --dep-path littlefs
VPATH += :littlefs
+CSRCS += lfs.c lfs_util.c
+
+DEPPATH += --dep-path littlefs/littlefs
+VPATH += :littlefs/littlefs
+
+LITTLEFS_VERSION ?= 2.2.1
+LITTLEFS_TARBALL = v$(LITTLEFS_VERSION).tar.gz
+
+$(LITTLEFS_TARBALL):
+ $(Q) wget -P littlefs https://github.com/ARMmbed/littlefs/archive/$(LITTLEFS_TARBALL)
+
+.littlefsunpack: $(LITTLEFS_TARBALL)
+ $(Q) tar zxf littlefs/$(LITTLEFS_TARBALL) -C littlefs
+ $(Q) mv littlefs/littlefs-$(LITTLEFS_VERSION) littlefs/littlefs
+ $(Q) touch littlefs/.littlefsunpack
+
+context:: .littlefsunpack
+
+distclean::
+ $(call DELFILE, littlefs/.littlefsunpack)
+ $(call DELFILE, littlefs/$(LITTLEFS_TARBALL))
+ $(call DELDIR, littlefs/littlefs)
+
endif
diff --git a/fs/littlefs/README.md b/fs/littlefs/README.md
deleted file mode 100644
index 32d3f6e..0000000
--- a/fs/littlefs/README.md
+++ /dev/null
@@ -1,220 +0,0 @@
-## usage
-
-depends on !DISABLE_MOUNTPOINT
-
-1. register_mtddriver("/dev/w25", mtd, 0755, NULL);
-2. mount("/dev/w25", "/w25", "littlefs", 0, NULL);
-
-## need to do
-
-1. no format tool, mount auto format.
-
-
-## The little filesystem
-
-A little fail-safe filesystem designed for embedded systems.
-
-```
- | | | .---._____
- .-----. | |
---|o |---| littlefs |
---| |---| |
- '-----' '----------'
- | | |
-```
-
-**Bounded RAM/ROM** - The littlefs is designed to work with a limited amount
-of memory. Recursion is avoided and dynamic memory is limited to configurable
-buffers that can be provided statically.
-
-**Power-loss resilient** - The littlefs is designed for systems that may have
-random power failures. The littlefs has strong copy-on-write guarantees and
-storage on disk is always kept in a valid state.
-
-**Wear leveling** - Since the most common form of embedded storage is erodible
-flash memories, littlefs provides a form of dynamic wear leveling for systems
-that can not fit a full flash translation layer.
-
-## Example
-
-Here's a simple example that updates a file named `boot_count` every time
-main runs. The program can be interrupted at any time without losing track
-of how many times it has been booted and without corrupting the filesystem:
-
-``` c
-#include "lfs.h"
-
-/* variables used by the filesystem */
-
-lfs_t lfs;
-lfs_file_t file;
-
-/* configuration of the filesystem is provided by this struct */
-
-const struct lfs_config cfg =
-{
- /* block device operations */
-
- .read = user_provided_block_device_read,
- .prog = user_provided_block_device_prog,
- .erase = user_provided_block_device_erase,
- .sync = user_provided_block_device_sync,
-
- /* block device configuration */
-
- .read_size = 16,
- .prog_size = 16,
- .block_size = 4096,
- .block_count = 128,
- .lookahead = 128,
-};
-
-/* entry point */
-
-int main(void)
-{
- /* mount the filesystem */
-
- int err = lfs_mount(&lfs, &cfg);
-
- /* reformat if we can't mount the filesystem
- * this should only happen on the first boot
- */
-
- if (err)
- {
- lfs_format(&lfs, &cfg);
- lfs_mount(&lfs, &cfg);
- }
-
- /* read current count */
-
- uint32_t boot_count = 0;
- lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
- lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
-
- /* update boot count */
-
- boot_count += 1;
- lfs_file_rewind(&lfs, &file);
- lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
-
- /* remember the storage is not updated until the file is closed successfully */
-
- lfs_file_close(&lfs, &file);
-
- /* release any resources we were using */
-
- lfs_unmount(&lfs);
-
- /* print the boot count */
-
- printf("boot_count: %d\n", boot_count);
-}
-```
-
-## Usage
-
-Detailed documentation (or at least as much detail as is currently available)
-can be found in the comments in [lfs.h](lfs.h).
-
-As you may have noticed, littlefs takes in a configuration structure that
-defines how the filesystem operates. The configuration struct provides the
-filesystem with the block device operations and dimensions, tweakable
-parameters that tradeoff memory usage for performance, and optional
-static buffers if the user wants to avoid dynamic memory.
-
-The state of the littlefs is stored in the `lfs_t` type which is left up
-to the user to allocate, allowing multiple filesystems to be in use
-simultaneously. With the `lfs_t` and configuration struct, a user can
-format a block device or mount the filesystem.
-
-Once mounted, the littlefs provides a full set of POSIX-like file and
-directory functions, with the deviation that the allocation of filesystem
-structures must be provided by the user.
-
-All POSIX operations, such as remove and rename, are atomic, even in event
-of power-loss. Additionally, no file updates are actually committed to the
-filesystem until sync or close is called on the file.
-
-## Other notes
-
-All littlefs have the potential to return a negative error code. The errors
-can be either one of those found in the `enum lfs_error` in [lfs.h](lfs.h),
-or an error returned by the user's block device operations.
-
-In the configuration struct, the `prog` and `erase` function provided by the
-user may return a `LFS_ERR_CORRUPT` error if the implementation already can
-detect corrupt blocks. However, the wear leveling does not depend on the return
-code of these functions, instead all data is read back and checked for
-integrity.
-
-If your storage caches writes, make sure that the provided `sync` function
-flushes all the data to memory and ensures that the next read fetches the data
-from memory, otherwise data integrity can not be guaranteed. If the `write`
-function does not perform caching, and therefore each `read` or `write` call
-hits the memory, the `sync` function can simply return 0.
-
-## Reference material
-
-[DESIGN.md](DESIGN.md) - DESIGN.md contains a fully detailed dive into how
-littlefs actually works. I would encourage you to read it since the
-solutions and tradeoffs at work here are quite interesting.
-
-[SPEC.md](SPEC.md) - SPEC.md contains the on-disk specification of littlefs
-with all the nitty-gritty details. Can be useful for developing tooling.
-
-## Testing
-
-The littlefs comes with a test suite designed to run on a PC using the
-[emulated block device](emubd/lfs_emubd.h) found in the emubd directory.
-The tests assume a Linux environment and can be started with make:
-
-``` bash
-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) -
-The easiest way to get started with littlefs is to jump into [Mbed](https://os.mbed.com/),
-which already has block device drivers for most forms of embedded storage. The
-littlefs is available in Mbed OS as the [LittleFileSystem](https://os.mbed.com/docs/latest/reference/littlefilesystem.html)
-class.
-
-[littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse)
-wrapper for littlefs. The project allows you to mount littlefs directly on a
-Linux machine. Can be useful for debugging littlefs if you have an SD card
-handy.
-
-[littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for
-littlefs. I'm not sure why you would want this, but it is handy for demos.
-You can see it in action [here](http://littlefs.geky.net/demo.html).
-
-[mklfs](https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src) -
-A command line tool built by the [Lua RTOS](https://github.com/whitecatboard/Lua-RTOS-ESP32)
-guys for making littlefs images from a host PC. Supports Windows, Mac OS,
-and Linux.
-
-[SPIFFS](https://github.com/pellepl/spiffs) - Another excellent embedded
-filesystem for NOR flash. As a more traditional logging filesystem with full
-static wear-leveling, SPIFFS will likely outperform littlefs on small
-memories such as the internal flash on microcontrollers.
-
-[Dhara](https://github.com/dlbeer/dhara) - An interesting NAND flash
-translation layer designed for small MCUs. It offers static wear-leveling and
-power-resilience with only a fixed O(|address|) pointer structure stored on
-each block and in RAM.
diff --git a/fs/littlefs/SPEC.md b/fs/littlefs/SPEC.md
deleted file mode 100644
index 202f50c..0000000
--- a/fs/littlefs/SPEC.md
+++ /dev/null
@@ -1,370 +0,0 @@
-## The little filesystem 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 development. 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).
-
-```
- | | | .---._____
- .-----. | |
---|o |---| littlefs |
---| |---| |
- '-----' '----------'
- | | |
-```
-
-## Some important details
-
-- The littlefs is a block-based filesystem. This is, the disk is divided into
- an array of evenly sized blocks that are used as the logical unit of storage
- in littlefs. Block pointers are stored in 32 bits.
-
-- There is no explicit free-list stored on disk, the littlefs only knows what
- is in use in the filesystem.
-
-- The littlefs uses the value of 0xffffffff to represent a null block-pointer.
-
-- All values in littlefs are stored in little-endian byte order.
-
-## Directories / Metadata pairs
-
-Metadata pairs form the backbone of the littlefs and provide a system for
-atomic updates. Even the superblock is stored in a metadata pair.
-
-As their name suggests, a metadata pair is stored in two blocks, with one block
-acting as a redundant backup in case the other is corrupted. These two blocks
-could be anywhere in the disk and may not be next to each other, so any
-pointers to directory pairs need to be stored as two block pointers.
-
-Here's the layout of metadata blocks on disk:
-
-| offset | size | description |
-|--------|---------------|----------------|
-| 0x00 | 32 bits | revision count |
-| 0x04 | 32 bits | dir size |
-| 0x08 | 64 bits | tail pointer |
-| 0x10 | size-16 bytes | dir entries |
-| 0x00+s | 32 bits | CRC |
-
-**Revision count** - Incremented every update, only the uncorrupted
-metadata-block with the most recent revision count contains the valid metadata.
-Comparison between revision counts must use sequence comparison since the
-revision counts may overflow.
-
-**Dir size** - Size in bytes of the contents in the current metadata block,
-including the metadata-pair metadata. Additionally, the highest bit of the
-dir size may be set to indicate that the directory's contents continue on the
-next metadata-pair pointed to by the tail pointer.
-
-**Tail pointer** - Pointer to the next metadata-pair in the filesystem.
-A null pair-pointer (0xffffffff, 0xffffffff) indicates the end of the list.
-If the highest bit in the dir size is set, this points to the next
-metadata-pair in the current directory, otherwise it points to an arbitrary
-metadata-pair. Starting with the superblock, the tail-pointers form a
-linked-list containing all metadata-pairs in the filesystem.
-
-**CRC** - 32 bit CRC used to detect corruption from power-lost, from block
-end-of-life, or just from noise on the storage bus. The CRC is appended to
-the end of each metadata-block. The littlefs uses the standard CRC-32, which
-uses a polynomial of 0x04c11db7, initialized with 0xffffffff.
-
-Here's an example of a simple directory stored on disk:
-```
-(32 bits) revision count = 10 (0x0000000a)
-(32 bits) dir size = 154 bytes, end of dir (0x0000009a)
-(64 bits) tail pointer = 37, 36 (0x00000025, 0x00000024)
-(32 bits) CRC = 0xc86e3106
-
-00000000: 0a 00 00 00 9a 00 00 00 25 00 00 00 24 00 00 00 ........%...$...
-00000010: 22 08 00 03 05 00 00 00 04 00 00 00 74 65 61 22 "...........tea"
-00000020: 08 00 06 07 00 00 00 06 00 00 00 63 6f 66 66 65 ...........coffe
-00000030: 65 22 08 00 04 09 00 00 00 08 00 00 00 73 6f 64 e"...........sod
-00000040: 61 22 08 00 05 1d 00 00 00 1c 00 00 00 6d 69 6c a"...........mil
-00000050: 6b 31 22 08 00 05 1f 00 00 00 1e 00 00 00 6d 69 k1"...........mi
-00000060: 6c 6b 32 22 08 00 05 21 00 00 00 20 00 00 00 6d lk2"...!... ...m
-00000070: 69 6c 6b 33 22 08 00 05 23 00 00 00 22 00 00 00 ilk3"...#..."...
-00000080: 6d 69 6c 6b 34 22 08 00 05 25 00 00 00 24 00 00 milk4"...%...$..
-00000090: 00 6d 69 6c 6b 35 06 31 6e c8 .milk5.1n.
-```
-
-A note about the tail pointer linked-list: Normally, this linked-list is
-threaded through the entire filesystem. However, after power-loss this
-linked-list may become out of sync with the rest of the filesystem.
-- The linked-list may contain a directory that has actually been removed
-- The linked-list may contain a metadata pair that has not been updated after
- a block in the pair has gone bad.
-
-The threaded linked-list must be checked for these errors before it can be
-used reliably. Fortunately, the threaded linked-list can simply be ignored
-if littlefs is mounted read-only.
-
-## Entries
-
-Each metadata block contains a series of entries that follow a standard
-layout. An entry contains the type of the entry, along with a section for
-entry-specific data, attributes, and a name.
-
-Here's the layout of entries on disk:
-
-| offset | size | description |
-|---------|------------------------|----------------------------|
-| 0x0 | 8 bits | entry type |
-| 0x1 | 8 bits | entry length |
-| 0x2 | 8 bits | attribute length |
-| 0x3 | 8 bits | name length |
-| 0x4 | entry length bytes | entry-specific data |
-| 0x4+e | attribute length bytes | system-specific attributes |
-| 0x4+e+a | name length bytes | entry name |
-
-**Entry type** - Type of the entry, currently this is limited to the following:
-- 0x11 - file entry
-- 0x22 - directory entry
-- 0x2e - superblock entry
-
-Additionally, the type is broken into two 4 bit nibbles, with the upper nibble
-specifying the type's data structure used when scanning the filesystem. The
-lower nibble clarifies the type further when multiple entries share the same
-data structure.
-
-The highest bit is reserved for marking the entry as "moved". If an entry
-is marked as "moved", the entry may also exist somewhere else in the
-filesystem. If the entry exists elsewhere, this entry must be treated as
-though it does not exist.
-
-**Entry length** - Length in bytes of the entry-specific data. This does
-not include the entry type size, attributes, or name. The full size in bytes
-of the entry is 4 + entry length + attribute length + name length.
-
-**Attribute length** - Length of system-specific attributes in bytes. Since
-attributes are system specific, there is not much guarantee on the values in
-this section, and systems are expected to work even when it is empty. See the
-[attributes](#entry-attributes) section for more details.
-
-**Name length** - Length of the entry name. Entry names are stored as UTF8,
-although most systems will probably only support ASCII. Entry names can not
-contain '/' and can not be '.' or '..' as these are a part of the syntax of
-filesystem paths.
-
-Here's an example of a simple entry stored on disk:
-```
-(8 bits) entry type = file (0x11)
-(8 bits) entry length = 8 bytes (0x08)
-(8 bits) attribute length = 0 bytes (0x00)
-(8 bits) name length = 12 bytes (0x0c)
-(8 bytes) entry data = 05 00 00 00 20 00 00 00
-(12 bytes) entry name = smallavacado
-
-00000000: 11 08 00 0c 05 00 00 00 20 00 00 00 73 6d 61 6c ........ ...smal
-00000010: 6c 61 76 61 63 61 64 6f lavacado
-```
-
-## Superblock
-
-The superblock is the anchor for the littlefs. The superblock is stored as
-a metadata pair containing a single superblock entry. It is through the
-superblock that littlefs can access the rest of the filesystem.
-
-The superblock can always be found in blocks 0 and 1, however fetching the
-superblock requires knowing the block size. The block size can be guessed by
-searching the beginning of disk for the string "littlefs", although currently
-the filesystems relies on the user providing the correct block size.
-
-The superblock is the most valuable block in the filesystem. It is updated
-very rarely, only during format or when the root directory must be moved. It
-is encouraged to always write out both superblock pairs even though it is not
-required.
-
-Here's the layout of the superblock entry:
-
-| offset | size | description |
-|--------|------------------------|----------------------------------------|
-| 0x00 | 8 bits | entry type (0x2e for superblock entry) |
-| 0x01 | 8 bits | entry length (20 bytes) |
-| 0x02 | 8 bits | attribute length |
-| 0x03 | 8 bits | name length (8 bytes) |
-| 0x04 | 64 bits | root directory |
-| 0x0c | 32 bits | block size |
-| 0x10 | 32 bits | block count |
-| 0x14 | 32 bits | version |
-| 0x18 | attribute length bytes | system-specific attributes |
-| 0x18+a | 8 bytes | magic string ("littlefs") |
-
-**Root directory** - Pointer to the root directory's metadata pair.
-
-**Block size** - Size of the logical block size used by the filesystem.
-
-**Block count** - Number of blocks in the filesystem.
-
-**Version** - The littlefs version encoded as a 32 bit value. The upper 16 bits
-encodes the major version, which is incremented when a breaking-change is
-introduced in the filesystem specification. The lower 16 bits encodes the
-minor version, which is incremented when a backwards-compatible change is
-introduced. Non-standard Attribute changes do not change the version. This
-specification describes version 1.1 (0x00010001), which is the first version
-of littlefs.
-
-**Magic string** - The magic string "littlefs" takes the place of an entry
-name.
-
-Here's an example of a complete superblock:
-```
-(32 bits) revision count = 3 (0x00000003)
-(32 bits) dir size = 52 bytes, end of dir (0x00000034)
-(64 bits) tail pointer = 3, 2 (0x00000003, 0x00000002)
-(8 bits) entry type = superblock (0x2e)
-(8 bits) entry length = 20 bytes (0x14)
-(8 bits) attribute length = 0 bytes (0x00)
-(8 bits) name length = 8 bytes (0x08)
-(64 bits) root directory = 3, 2 (0x00000003, 0x00000002)
-(32 bits) block size = 512 bytes (0x00000200)
-(32 bits) block count = 1024 blocks (0x00000400)
-(32 bits) version = 1.1 (0x00010001)
-(8 bytes) magic string = littlefs
-(32 bits) CRC = 0xc50b74fa
-
-00000000: 03 00 00 00 34 00 00 00 03 00 00 00 02 00 00 00 ....4...........
-00000010: 2e 14 00 08 03 00 00 00 02 00 00 00 00 02 00 00 ................
-00000020: 00 04 00 00 01 00 01 00 6c 69 74 74 6c 65 66 73 ........littlefs
-00000030: fa 74 0b c5 .t..
-```
-
-## Directory entries
-
-Directories are stored in entries with a pointer to the first metadata pair
-in the directory. Keep in mind that a directory may be composed of multiple
-metadata pairs connected by the tail pointer when the highest bit in the dir
-size is set.
-
-Here's the layout of a directory entry:
-
-| offset | size | description |
-|--------|------------------------|-----------------------------------------|
-| 0x0 | 8 bits | entry type (0x22 for directory entries) |
-| 0x1 | 8 bits | entry length (8 bytes) |
-| 0x2 | 8 bits | attribute length |
-| 0x3 | 8 bits | name length |
-| 0x4 | 64 bits | directory pointer |
-| 0xc | attribute length bytes | system-specific attributes |
-| 0xc+a | name length bytes | directory name |
-
-**Directory pointer** - Pointer to the first metadata pair in the directory.
-
-Here's an example of a directory entry:
-```
-(8 bits) entry type = directory (0x22)
-(8 bits) entry length = 8 bytes (0x08)
-(8 bits) attribute length = 0 bytes (0x00)
-(8 bits) name length = 3 bytes (0x03)
-(64 bits) directory pointer = 5, 4 (0x00000005, 0x00000004)
-(3 bytes) name = tea
-
-00000000: 22 08 00 03 05 00 00 00 04 00 00 00 74 65 61 "...........tea
-```
-
-## File entries
-
-Files are stored in entries with a pointer to the head of the file and the
-size of the file. This is enough information to determine the state of the
-CTZ skip-list that is being referenced.
-
-How files are actually stored on disk is a bit complicated. The full
-explanation of CTZ skip-lists can be found in [DESIGN.md](DESIGN.md#ctz-skip-lists).
-
-A terribly quick summary: For every nth block where n is divisible by 2^x,
-the block contains a pointer to block n-2^x. These pointers are stored in
-increasing order of x in each block of the file preceding the data in the
-block.
-
-The maximum number of pointers in a block is bounded by the maximum file size
-divided by the block size. With 32 bits for file size, this results in a
-minimum block size of 104 bytes.
-
-Here's the layout of a file entry:
-
-| offset | size | description |
-|--------|------------------------|------------------------------------|
-| 0x0 | 8 bits | entry type (0x11 for file entries) |
-| 0x1 | 8 bits | entry length (8 bytes) |
-| 0x2 | 8 bits | attribute length |
-| 0x3 | 8 bits | name length |
-| 0x4 | 32 bits | file head |
-| 0x8 | 32 bits | file size |
-| 0xc | attribute length bytes | system-specific attributes |
-| 0xc+a | name length bytes | directory name |
-
-**File head** - Pointer to the block that is the head of the file's CTZ
-skip-list.
-
-**File size** - Size of file in bytes.
-
-Here's an example of a file entry:
-```
-(8 bits) entry type = file (0x11)
-(8 bits) entry length = 8 bytes (0x08)
-(8 bits) attribute length = 0 bytes (0x00)
-(8 bits) name length = 12 bytes (0x03)
-(32 bits) file head = 543 (0x0000021f)
-(32 bits) file size = 256 KB (0x00040000)
-(12 bytes) name = largeavacado
-
-00000000: 11 08 00 0c 1f 02 00 00 00 00 04 00 6c 61 72 67 ............large
-00000010: 65 61 76 61 63 61 64 6f eavacado
-```
-
-## Entry attributes
-
-Each dir entry can have up to 256 bytes of system-specific attributes. Since
-these attributes are system-specific, they may not be portable between
-different systems. For this reason, all attributes must be optional. A minimal
-littlefs driver must be able to get away with supporting no attributes at all.
-
-For some level of portability, littlefs has a simple scheme for attributes.
-Each attribute is prefixes with an 8-bit type that indicates what the attribute
-is. The length of attributes may also be determined from this type. Attributes
-in an entry should be sorted based on portability, since attribute parsing
-will end when it hits the first attribute it does not understand.
-
-Each system should choose a 4-bit value to prefix all attribute types with to
-avoid conflicts with other systems. Additionally, littlefs drivers that support
-attributes should provide a "ignore attributes" flag to users in case attribute
-conflicts do occur.
-
-Attribute types prefixes with 0x0 and 0xf are currently reserved for future
-standard attributes. Standard attributes will be added to this document in
-that case.
-
-Here's an example of non-standard time attribute:
-```
-(8 bits) attribute type = time (0xc1)
-(72 bits) time in seconds = 1506286115 (0x0059c81a23)
-
-00000000: c1 23 1a c8 59 00 .#..Y.
-```
-
-Here's an example of non-standard permissions attribute:
-```
-(8 bits) attribute type = permissions (0xc2)
-(16 bits) permission bits = rw-rw-r-- (0x01b4)
-
-00000000: c2 b4 01 ...
-```
-
-Here's what a dir entry may look like with these attributes:
-```
-(8 bits) entry type = file (0x11)
-(8 bits) entry length = 8 bytes (0x08)
-(8 bits) attribute length = 9 bytes (0x09)
-(8 bits) name length = 12 bytes (0x0c)
-(8 bytes) entry data = 05 00 00 00 20 00 00 00
-(8 bits) attribute type = time (0xc1)
-(72 bits) time in seconds = 1506286115 (0x0059c81a23)
-(8 bits) attribute type = permissions (0xc2)
-(16 bits) permission bits = rw-rw-r-- (0x01b4)
-(12 bytes) entry name = smallavacado
-
-00000000: 11 08 09 0c 05 00 00 00 20 00 00 00 c1 23 1a c8 ........ ....#..
-00000010: 59 00 c2 b4 01 73 6d 61 6c 6c 61 76 61 63 61 64 Y....smallavacad
-00000020: 6f o
-```
diff --git a/fs/littlefs/lfs.c b/fs/littlefs/lfs.c
deleted file mode 100644
index 77d06af..0000000
--- a/fs/littlefs/lfs.c
+++ /dev/null
@@ -1,3464 +0,0 @@
-/****************************************************************************
- * fs/littlefs/lfs.c
- *
- * This file is a part of NuttX:
- *
- * Copyright (C) 2019 Gregory Nutt. All rights reserved.
- *
- * Ported by:
- *
- * Copyright (C) 2019 Pinecone Inc. All rights reserved.
- * Author: lihaichen <li...@163.com>
- *
- * This port derives from ARM mbed logic which has a compatible 3-clause
- * BSD license:
- *
- * Copyright (c) 2017, Arm Limited. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the names ARM, NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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.
- *
- ****************************************************************************/
-
-/****************************************************************************
- * Included Files
- ****************************************************************************/
-
-#include <inttypes.h>
-
-#include "lfs.h"
-#include "lfs_util.h"
-
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/****************************************************************************
- * Private Types
- ****************************************************************************/
-
-struct lfs_region_s
-{
- lfs_off_t oldoff;
- lfs_size_t oldlen;
- FAR const void *newdata;
- lfs_size_t newlen;
-};
-
-/****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
-
-static int lfs_pred(FAR lfs_t *lfs, FAR const lfs_block_t dir[2],
- FAR lfs_dir_t *pdir);
-static int lfs_parent(FAR lfs_t *lfs, FAR const lfs_block_t dir[2],
- FAR lfs_dir_t *parent, FAR lfs_entry_t *entry);
-static int lfs_moved(FAR lfs_t *lfs, FAR const void *e);
-static int lfs_relocate(FAR lfs_t *lfs, FAR const lfs_block_t oldpair[2],
- FAR const lfs_block_t newpair[2]);
-
-/****************************************************************************
- * Private Functions
- ****************************************************************************/
-
-/* Caching block device operations */
-
-static int lfs_cache_read(FAR lfs_t *lfs, FAR lfs_cache_t *rcache,
- FAR const lfs_cache_t *pcache, lfs_block_t block,
- lfs_off_t off, FAR void *buffer, lfs_size_t size)
-{
- FAR uint8_t *data = buffer;
- LFS_ASSERT(block < lfs->cfg->block_count);
-
- while (size > 0)
- {
- if (pcache && block == pcache->block && off >= pcache->off &&
- off < pcache->off + lfs->cfg->prog_size)
- {
- /* is already in pcache? */
-
- lfs_size_t diff =
- lfs_min(size, lfs->cfg->prog_size - (off - pcache->off));
- memcpy(data, &pcache->buffer[off - pcache->off], diff);
-
- data += diff;
- off += diff;
- size -= diff;
- continue;
- }
-
- if (block == rcache->block && off >= rcache->off &&
- off < rcache->off + lfs->cfg->read_size)
- {
- /* is already in rcache? */
-
- lfs_size_t diff =
- lfs_min(size, lfs->cfg->read_size - (off - rcache->off));
- memcpy(data, &rcache->buffer[off - rcache->off], diff);
-
- data += diff;
- off += diff;
- size -= diff;
- continue;
- }
-
- if (off % lfs->cfg->read_size == 0 && size >= lfs->cfg->read_size)
- {
- /* bypass cache? */
-
- lfs_size_t diff = size - (size % lfs->cfg->read_size);
- int err = lfs->cfg->read(lfs->cfg, block, off, data, diff);
- if (err)
- {
- return err;
- }
-
- data += diff;
- off += diff;
- size -= diff;
- continue;
- }
-
- /* load to cache, first condition can no longer fail */
-
- rcache->block = block;
- rcache->off = off - (off % lfs->cfg->read_size);
- int err = lfs->cfg->read(lfs->cfg, rcache->block, rcache->off,
- rcache->buffer, lfs->cfg->read_size);
- if (err)
- {
- return err;
- }
- }
-
- return 0;
-}
-
-static int lfs_cache_cmp(FAR lfs_t *lfs, FAR lfs_cache_t *rcache,
- FAR const lfs_cache_t *pcache, lfs_block_t block,
- lfs_off_t off, FAR const void *buffer,
- lfs_size_t size)
-{
- FAR const uint8_t *data = buffer;
- lfs_off_t i;
-
- for (i = 0; i < size; i++)
- {
- uint8_t c;
- int err = lfs_cache_read(lfs, rcache, pcache, block, off + i, &c, 1);
- if (err)
- {
- return err;
- }
-
- if (c != data[i])
- {
- return false;
- }
- }
-
- return true;
-}
-
-static int lfs_cache_crc(FAR lfs_t *lfs, FAR lfs_cache_t *rcache,
- FAR const lfs_cache_t *pcache, lfs_block_t block,
- lfs_off_t off, lfs_size_t size, FAR uint32_t *crc)
-{
- lfs_off_t i;
-
- for (i = 0; i < size; i++)
- {
- uint8_t c;
- int err = lfs_cache_read(lfs, rcache, pcache, block, off + i, &c, 1);
- if (err)
- {
- return err;
- }
-
- lfs_crc(crc, &c, 1);
- }
-
- return 0;
-}
-
-static inline void lfs_cache_drop(FAR lfs_t *lfs, FAR lfs_cache_t *rcache)
-{
- /* do not zero, cheaper if cache is readonly or only going to be
- * written with identical data (during relocates)
- */
-
- rcache->block = 0xffffffff;
-}
-
-static inline void lfs_cache_zero(FAR lfs_t *lfs, FAR lfs_cache_t *pcache)
-{
- /* zero to avoid information leak */
-
- memset(pcache->buffer, 0xff, lfs->cfg->prog_size);
- pcache->block = 0xffffffff;
-}
-
-static int lfs_cache_flush(FAR lfs_t *lfs, FAR lfs_cache_t *pcache,
- FAR lfs_cache_t *rcache)
-{
- if (pcache->block != 0xffffffff)
- {
- int err = lfs->cfg->prog(lfs->cfg, pcache->block, pcache->off,
- pcache->buffer, lfs->cfg->prog_size);
- if (err)
- {
- return err;
- }
-
- if (rcache)
- {
- int res = lfs_cache_cmp(lfs, rcache, NULL, pcache->block,
- pcache->off, pcache->buffer,
- lfs->cfg->prog_size);
- if (res < 0)
- {
- return res;
- }
-
- if (!res)
- {
- return LFS_ERR_CORRUPT;
- }
- }
-
- lfs_cache_zero(lfs, pcache);
- }
-
- return 0;
-}
-
-static int lfs_cache_prog(FAR lfs_t *lfs, FAR lfs_cache_t *pcache,
- FAR lfs_cache_t *rcache, lfs_block_t block,
- lfs_off_t off, FAR const void *buffer,
- lfs_size_t size)
-{
- FAR const uint8_t *data = buffer;
- LFS_ASSERT(block < lfs->cfg->block_count);
-
- while (size > 0)
- {
- if (block == pcache->block && off >= pcache->off &&
- off < pcache->off + lfs->cfg->prog_size)
- {
- /* is already in pcache? */
-
- lfs_size_t diff =
- lfs_min(size, lfs->cfg->prog_size - (off - pcache->off));
- memcpy(&pcache->buffer[off - pcache->off], data, diff);
-
- data += diff;
- off += diff;
- size -= diff;
-
- if (off % lfs->cfg->prog_size == 0)
- {
- /* eagerly flush out pcache if we fill up */
-
- int err = lfs_cache_flush(lfs, pcache, rcache);
- if (err)
- {
- return err;
- }
- }
-
- continue;
- }
-
- /* pcache must have been flushed, either by programming and
- * entire block or manually flushing the pcache
- */
-
- LFS_ASSERT(pcache->block == 0xffffffff);
-
- if (off % lfs->cfg->prog_size == 0 && size >= lfs->cfg->prog_size)
- {
- /* bypass pcache? */
-
- lfs_size_t diff = size - (size % lfs->cfg->prog_size);
- int err = lfs->cfg->prog(lfs->cfg, block, off, data, diff);
- if (err)
- {
- return err;
- }
-
- if (rcache)
- {
- int res =
- lfs_cache_cmp(lfs, rcache, NULL, block, off, data, diff);
- if (res < 0)
- {
- return res;
- }
-
- if (!res)
- {
- return LFS_ERR_CORRUPT;
- }
- }
-
- data += diff;
- off += diff;
- size -= diff;
- continue;
- }
-
- /* prepare pcache, first condition can no longer fail */
-
- pcache->block = block;
- pcache->off = off - (off % lfs->cfg->prog_size);
- }
-
- return 0;
-}
-
-/* General lfs block device operations */
-
-static int lfs_bd_read(FAR lfs_t *lfs, lfs_block_t block, lfs_off_t off,
- FAR void *buffer, lfs_size_t size)
-{
- /* if we ever do more than writes to alternating pairs,
- * this may need to consider pcache
- */
-
- return lfs_cache_read(lfs, &lfs->rcache, NULL, block, off, buffer, size);
-}
-
-static int lfs_bd_prog(FAR lfs_t *lfs, lfs_block_t block, lfs_off_t off,
- FAR const void *buffer, lfs_size_t size)
-{
- return lfs_cache_prog(lfs, &lfs->pcache, NULL, block, off, buffer, size);
-}
-
-static int lfs_bd_cmp(FAR lfs_t *lfs, lfs_block_t block, lfs_off_t off,
- FAR const void *buffer, lfs_size_t size)
-{
- return lfs_cache_cmp(lfs, &lfs->rcache, NULL, block, off, buffer, size);
-}
-
-static int lfs_bd_crc(FAR lfs_t *lfs, lfs_block_t block, lfs_off_t off,
- lfs_size_t size, FAR uint32_t *crc)
-{
- return lfs_cache_crc(lfs, &lfs->rcache, NULL, block, off, size, crc);
-}
-
-static int lfs_bd_erase(FAR lfs_t *lfs, lfs_block_t block)
-{
- return lfs->cfg->erase(lfs->cfg, block);
-}
-
-static int lfs_bd_sync(FAR lfs_t *lfs)
-{
- lfs_cache_drop(lfs, &lfs->rcache);
-
- int err = lfs_cache_flush(lfs, &lfs->pcache, NULL);
- if (err)
- {
- return err;
- }
-
- return lfs->cfg->sync(lfs->cfg);
-}
-
-/* Block allocator */
-
-static int lfs_alloc_lookahead(FAR void *p, lfs_block_t block)
-{
- FAR lfs_t *lfs = p;
-
- lfs_block_t off =
- ((block - lfs->free.off) + lfs->cfg->block_count) %
- lfs->cfg->block_count;
-
- if (off < lfs->free.size)
- {
- lfs->free.buffer[off / 32] |= 1U << (off % 32);
- }
-
- return 0;
-}
-
-static int lfs_alloc(FAR lfs_t *lfs, FAR lfs_block_t *block)
-{
- while (true)
- {
- while (lfs->free.i != lfs->free.size)
- {
- lfs_block_t off = lfs->free.i;
- lfs->free.i += 1;
- lfs->free.ack -= 1;
-
- if (!(lfs->free.buffer[off / 32] & (1U << (off % 32))))
- {
- /* found a free block */
-
- *block = (lfs->free.off + off) % lfs->cfg->block_count;
-
- /* eagerly find next off so an alloc ack can
- * discredit old lookahead blocks
- */
-
- while (lfs->free.i != lfs->free.size &&
- (lfs->free.buffer[lfs->free.i / 32] &
- (1U << (lfs->free.i % 32))))
- {
- lfs->free.i += 1;
- lfs->free.ack -= 1;
- }
-
- return 0;
- }
- }
-
- /* check if we have looked at all blocks since last ack */
-
- if (lfs->free.ack == 0)
- {
- LFS_WARN("No more free space %" PRIu32,
- lfs->free.i + lfs->free.off);
- return LFS_ERR_NOSPC;
- }
-
- lfs->free.off = (lfs->free.off + lfs->free.size) %
- lfs->cfg->block_count;
- lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->free.ack);
- lfs->free.i = 0;
-
- /* find mask of free blocks from tree */
-
- memset(lfs->free.buffer, 0, lfs->cfg->lookahead / 8);
- int err = lfs_traverse(lfs, lfs_alloc_lookahead, lfs);
- if (err)
- {
- return err;
- }
- }
-}
-
-static void lfs_alloc_ack(FAR lfs_t *lfs)
-{
- lfs->free.ack = lfs->cfg->block_count;
-}
-
-/* Endian swapping functions */
-
-static void lfs_dir_fromle32(FAR struct lfs_disk_dir_s *d)
-{
- d->rev = lfs_fromle32(d->rev);
- d->size = lfs_fromle32(d->size);
- d->tail[0] = lfs_fromle32(d->tail[0]);
- d->tail[1] = lfs_fromle32(d->tail[1]);
-}
-
-static void lfs_dir_tole32(FAR struct lfs_disk_dir_s *d)
-{
- d->rev = lfs_tole32(d->rev);
- d->size = lfs_tole32(d->size);
- d->tail[0] = lfs_tole32(d->tail[0]);
- d->tail[1] = lfs_tole32(d->tail[1]);
-}
-
-static void lfs_entry_fromle32(FAR struct lfs_disk_entry_s *d)
-{
- d->u.dir[0] = lfs_fromle32(d->u.dir[0]);
- d->u.dir[1] = lfs_fromle32(d->u.dir[1]);
-}
-
-static void lfs_entry_tole32(FAR struct lfs_disk_entry_s *d)
-{
- d->u.dir[0] = lfs_tole32(d->u.dir[0]);
- d->u.dir[1] = lfs_tole32(d->u.dir[1]);
-}
-
-static void lfs_superblock_fromle32(FAR struct lfs_disk_superblock_s *d)
-{
- d->root[0] = lfs_fromle32(d->root[0]);
- d->root[1] = lfs_fromle32(d->root[1]);
- d->block_size = lfs_fromle32(d->block_size);
- d->block_count = lfs_fromle32(d->block_count);
- d->version = lfs_fromle32(d->version);
-}
-
-static void lfs_superblock_tole32(FAR struct lfs_disk_superblock_s *d)
-{
- d->root[0] = lfs_tole32(d->root[0]);
- d->root[1] = lfs_tole32(d->root[1]);
- d->block_size = lfs_tole32(d->block_size);
- d->block_count = lfs_tole32(d->block_count);
- d->version = lfs_tole32(d->version);
-}
-
-/* Metadata pair and directory operations */
-
-static inline void lfs_pairswap(FAR lfs_block_t pair[2])
-{
- lfs_block_t t = pair[0];
- pair[0] = pair[1];
- pair[1] = t;
-}
-
-static inline bool lfs_pairisnull(FAR const lfs_block_t pair[2])
-{
- return pair[0] == 0xffffffff || pair[1] == 0xffffffff;
-}
-
-static inline int lfs_paircmp(FAR const lfs_block_t paira[2],
- FAR const lfs_block_t pairb[2])
-{
- return !(paira[0] == pairb[0] || paira[1] == pairb[1] ||
- paira[0] == pairb[1] || paira[1] == pairb[0]);
-}
-
-static inline bool lfs_pairsync(FAR const lfs_block_t paira[2],
- FAR const lfs_block_t pairb[2])
-{
- return (paira[0] == pairb[0] && paira[1] == pairb[1]) ||
- (paira[0] == pairb[1] && paira[1] == pairb[0]);
-}
-
-static inline lfs_size_t lfs_entry_size(FAR const lfs_entry_t *entry)
-{
- return 4 + entry->d.elen + entry->d.alen + entry->d.nlen;
-}
-
-static int lfs_dir_alloc(FAR lfs_t *lfs, FAR lfs_dir_t *dir)
-{
- int i;
-
- /* allocate pair of dir blocks */
-
- for (i = 0; i < 2; i++)
- {
- int err = lfs_alloc(lfs, &dir->pair[i]);
- if (err)
- {
- return err;
- }
- }
-
- /* rather than clobbering one of the blocks we just pretend
- * the revision may be valid
- */
-
- int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4);
- if (err && err != LFS_ERR_CORRUPT)
- {
- return err;
- }
-
- if (err != LFS_ERR_CORRUPT)
- {
- dir->d.rev = lfs_fromle32(dir->d.rev);
- }
-
- /* set defaults */
-
- dir->d.rev += 1;
- dir->d.size = sizeof(dir->d) + 4;
- dir->d.tail[0] = 0xffffffff;
- dir->d.tail[1] = 0xffffffff;
- dir->off = sizeof(dir->d);
-
- /* don't write out yet, let caller take care of that */
-
- return 0;
-}
-
-static int lfs_dir_fetch(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR const lfs_block_t pair[2])
-{
- /* copy out pair, otherwise may be aliasing dir */
-
- FAR const lfs_block_t tpair[2] =
- {
- pair[0], pair[1]
- };
-
- bool valid = false;
- int i;
-
- /* check both blocks for the most recent revision */
-
- for (i = 0; i < 2; i++)
- {
- struct lfs_disk_dir_s test;
- uint32_t crc;
- int err = lfs_bd_read(lfs, tpair[i], 0, &test, sizeof(test));
- lfs_dir_fromle32(&test);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- continue;
- }
-
- return err;
- }
-
- if (valid && lfs_scmp(test.rev, dir->d.rev) < 0)
- {
- continue;
- }
-
- if ((0x7fffffff & test.size) < sizeof(test) + 4 ||
- (0x7fffffff & test.size) > lfs->cfg->block_size)
- {
- continue;
- }
-
- crc = 0xffffffff;
- lfs_dir_tole32(&test);
- lfs_crc(&crc, &test, sizeof(test));
- lfs_dir_fromle32(&test);
- err = lfs_bd_crc(lfs, tpair[i], sizeof(test),
- (0x7fffffff & test.size) - sizeof(test), &crc);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- continue;
- }
-
- return err;
- }
-
- if (crc != 0)
- {
- continue;
- }
-
- valid = true;
-
- /* setup dir in case it's valid */
-
- dir->pair[0] = tpair[(i + 0) % 2];
- dir->pair[1] = tpair[(i + 1) % 2];
- dir->off = sizeof(dir->d);
- dir->d = test;
- }
-
- if (!valid)
- {
- LFS_ERROR("Corrupted dir pair at %" PRIu32 " %" PRIu32, tpair[0],
- tpair[1]);
- return LFS_ERR_CORRUPT;
- }
-
- return 0;
-}
-
-static int lfs_dir_commit(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR const struct lfs_region_s *regions, int count)
-{
- FAR lfs_dir_t *d;
- lfs_block_t oldpair[2];
- bool relocated;
- int err;
- int i;
-
- /* increment revision count */
-
- dir->d.rev += 1;
-
- /* keep pairs in order such that pair[0] is most recent */
-
- lfs_pairswap(dir->pair);
- for (i = 0; i < count; i++)
- {
- dir->d.size += regions[i].newlen - regions[i].oldlen;
- }
-
- oldpair[0] = dir->pair[0];
- oldpair[1] = dir->pair[1];
- relocated = false;
-
- while (true)
- {
- if (true)
- {
- uint32_t crc;
- err = lfs_bd_erase(lfs, dir->pair[0]);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- crc = 0xffffffff;
- lfs_dir_tole32(&dir->d);
- lfs_crc(&crc, &dir->d, sizeof(dir->d));
- err = lfs_bd_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d));
- lfs_dir_fromle32(&dir->d);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- i = 0;
- lfs_off_t oldoff = sizeof(dir->d);
- lfs_off_t newoff = sizeof(dir->d);
- while (newoff < (0x7fffffff & dir->d.size) - 4)
- {
- if (i < count && regions[i].oldoff == oldoff)
- {
- lfs_crc(&crc, regions[i].newdata, regions[i].newlen);
- err = lfs_bd_prog(lfs, dir->pair[0], newoff,
- regions[i].newdata, regions[i].newlen);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- oldoff += regions[i].oldlen;
- newoff += regions[i].newlen;
- i += 1;
- }
- else
- {
- uint8_t data;
- err = lfs_bd_read(lfs, oldpair[1], oldoff, &data, 1);
- if (err)
- {
- return err;
- }
-
- lfs_crc(&crc, &data, 1);
- err = lfs_bd_prog(lfs, dir->pair[0], newoff, &data, 1);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- oldoff += 1;
- newoff += 1;
- }
- }
-
- crc = lfs_tole32(crc);
- err = lfs_bd_prog(lfs, dir->pair[0], newoff, &crc, 4);
- crc = lfs_fromle32(crc);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- err = lfs_bd_sync(lfs);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- /* successful commit, check checksum to make sure */
-
- uint32_t ncrc = 0xffffffff;
- err = lfs_bd_crc(lfs, dir->pair[0], 0,
- (0x7fffffff & dir->d.size) - 4, &ncrc);
- if (err)
- {
- return err;
- }
-
- if (ncrc != crc)
- {
- goto relocate;
- }
- }
-
- break;
-
-relocate:
-
- /* Commit was corrupted */
-
- LFS_DEBUG("Bad block at %" PRIu32, dir->pair[0]);
-
- /* Drop caches and prepare to relocate block */
-
- relocated = true;
- lfs_cache_drop(lfs, &lfs->pcache);
-
- /* Can't relocate superblock, filesystem is now frozen */
-
- if (lfs_paircmp(oldpair, (const lfs_block_t[2]){ 0, 1 }) == 0)
- {
- LFS_WARN("Superblock %" PRIu32 " has become unwritable",
- oldpair[0]);
- return LFS_ERR_CORRUPT;
- }
-
- /* Relocate half of pair */
-
- err = lfs_alloc(lfs, &dir->pair[0]);
- if (err)
- {
- return err;
- }
- }
-
- if (relocated)
- {
- /* update references if we relocated */
-
- LFS_DEBUG("Relocating %" PRIu32 " %" PRIu32 " to %" PRIu32 " %" PRIu32,
- oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
- err = lfs_relocate(lfs, oldpair, dir->pair);
- if (err)
- {
- return err;
- }
- }
-
- /* shift over any directories that are affected */
-
- for (d = lfs->dirs; d; d = d->next)
- {
- if (lfs_paircmp(d->pair, dir->pair) == 0)
- {
- d->pair[0] = dir->pair[0];
- d->pair[1] = dir->pair[1];
- }
- }
-
- return 0;
-}
-
-static int lfs_dir_update(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR lfs_entry_t *entry, FAR const void *data)
-{
- int err;
-
- lfs_entry_tole32(&entry->d);
- err = lfs_dir_commit(
- lfs, dir,
- (struct lfs_region_s[])
- {
- {
- entry->off, sizeof(entry->d), &entry->d, sizeof(entry->d)
- },
- {
- entry->off + sizeof(entry->d), entry->d.nlen, data, entry->d.nlen
- }
- },
- data ? 2 : 1);
- lfs_entry_fromle32(&entry->d);
- return err;
-}
-
-static int lfs_dir_append(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR lfs_entry_t *entry, FAR const void *data)
-{
- /* check if we fit, if top bit is set we do not and move on */
-
- while (true)
- {
- if (dir->d.size + lfs_entry_size(entry) <= lfs->cfg->block_size)
- {
- entry->off = dir->d.size - 4;
-
- lfs_entry_tole32(&entry->d);
- int err =
- lfs_dir_commit(lfs, dir,
- (struct lfs_region_s[])
- {
- {
- entry->off, 0, &entry->d, sizeof(entry->d)
- },
- {
- entry->off, 0, data, entry->d.nlen
- }
- },
- 2);
- lfs_entry_fromle32(&entry->d);
- return err;
- }
-
- /* we need to allocate a new dir block */
-
- if (!(0x80000000 & dir->d.size))
- {
- lfs_dir_t olddir = *dir;
- int err = lfs_dir_alloc(lfs, dir);
- if (err)
- {
- return err;
- }
-
- dir->d.tail[0] = olddir.d.tail[0];
- dir->d.tail[1] = olddir.d.tail[1];
- entry->off = dir->d.size - 4;
- lfs_entry_tole32(&entry->d);
- err = lfs_dir_commit(lfs, dir,
- (struct lfs_region_s[])
- {
- {
- entry->off, 0, &entry->d, sizeof(entry->d)
- },
- {
- entry->off, 0, data, entry->d.nlen
- }
- },
- 2);
- lfs_entry_fromle32(&entry->d);
- if (err)
- {
- return err;
- }
-
- olddir.d.size |= 0x80000000;
- olddir.d.tail[0] = dir->pair[0];
- olddir.d.tail[1] = dir->pair[1];
- return lfs_dir_commit(lfs, &olddir, NULL, 0);
- }
-
- int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
- if (err)
- {
- return err;
- }
- }
-}
-
-static int lfs_dir_remove(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR lfs_entry_t *entry)
-{
- FAR lfs_file_t *f;
- FAR lfs_dir_t *d;
- int err;
-
- /* check if we should just drop the directory block */
-
- if ((dir->d.size & 0x7fffffff) ==
- sizeof(dir->d) + 4 + lfs_entry_size(entry))
- {
- lfs_dir_t pdir;
- int res = lfs_pred(lfs, dir->pair, &pdir);
- if (res < 0)
- {
- return res;
- }
-
- if (pdir.d.size & 0x80000000)
- {
- pdir.d.size &= dir->d.size | 0x7fffffff;
- pdir.d.tail[0] = dir->d.tail[0];
- pdir.d.tail[1] = dir->d.tail[1];
- return lfs_dir_commit(lfs, &pdir, NULL, 0);
- }
- }
-
- /* shift out the entry */
-
- err = lfs_dir_commit(lfs, dir,
- (struct lfs_region_s[])
- {
- {
- entry->off, lfs_entry_size(entry), NULL, 0
- },
- },
- 1);
- if (err)
- {
- return err;
- }
-
- /* shift over any files/directories that are affected */
-
- for (f = lfs->files; f; f = f->next)
- {
- if (lfs_paircmp(f->pair, dir->pair) == 0)
- {
- if (f->poff == entry->off)
- {
- f->pair[0] = 0xffffffff;
- f->pair[1] = 0xffffffff;
- }
- else if (f->poff > entry->off)
- {
- f->poff -= lfs_entry_size(entry);
- }
- }
- }
-
- for (d = lfs->dirs; d; d = d->next)
- {
- if (lfs_paircmp(d->pair, dir->pair) == 0)
- {
- if (d->off > entry->off)
- {
- d->off -= lfs_entry_size(entry);
- d->pos -= lfs_entry_size(entry);
- }
- }
- }
-
- return 0;
-}
-
-static int lfs_dir_next(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR lfs_entry_t *entry)
-{
- while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size) - 4)
- {
- if (!(0x80000000 & dir->d.size))
- {
- entry->off = dir->off;
- return LFS_ERR_NOENT;
- }
-
- int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
- if (err)
- {
- return err;
- }
-
- dir->off = sizeof(dir->d);
- dir->pos += sizeof(dir->d) + 4;
- }
-
- int err = lfs_bd_read(lfs, dir->pair[0], dir->off, &entry->d,
- sizeof(entry->d));
- lfs_entry_fromle32(&entry->d);
- if (err)
- {
- return err;
- }
-
- entry->off = dir->off;
- dir->off += lfs_entry_size(entry);
- dir->pos += lfs_entry_size(entry);
- return 0;
-}
-
-static int lfs_dir_find(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR lfs_entry_t *entry,
- FAR const char **path)
-{
- FAR const char *pathname = *path;
- size_t pathlen;
- entry->d.type = LFS_TYPE_DIR;
- entry->d.elen = sizeof(entry->d) - 4;
- entry->d.alen = 0;
- entry->d.nlen = 0;
- entry->d.u.dir[0] = lfs->root[0];
- entry->d.u.dir[1] = lfs->root[1];
-
- while (true)
- {
- FAR const char *suffix;
- size_t sufflen;
- int depth;
-
-nextname:
-
- /* Skip slashes */
-
- pathname += strspn(pathname, "/");
- pathlen = strcspn(pathname, "/");
-
- /* Skip '.' and root '..' */
-
- if ((pathlen == 1 && memcmp(pathname, ".", 1) == 0) ||
- (pathlen == 2 && memcmp(pathname, "..", 2) == 0))
- {
- pathname += pathlen;
- goto nextname;
- }
-
- /* Skip if matched by '..' in name */
-
- suffix = pathname + pathlen;
- depth = 1;
- while (true)
- {
- suffix += strspn(suffix, "/");
- sufflen = strcspn(suffix, "/");
- if (sufflen == 0)
- {
- break;
- }
-
- if (sufflen == 2 && memcmp(suffix, "..", 2) == 0)
- {
- depth -= 1;
- if (depth == 0)
- {
- pathname = suffix + sufflen;
- goto nextname;
- }
- }
- else
- {
- depth += 1;
- }
-
- suffix += sufflen;
- }
-
- /* found path */
-
- if (pathname[0] == '\0')
- {
- return 0;
- }
-
- /* update what we've found */
-
- *path = pathname;
-
- /* continue on if we hit a directory */
-
- if (entry->d.type != LFS_TYPE_DIR)
- {
- return LFS_ERR_NOTDIR;
- }
-
- int err = lfs_dir_fetch(lfs, dir, entry->d.u.dir);
- if (err)
- {
- return err;
- }
-
- /* find entry matching name */
-
- while (true)
- {
- err = lfs_dir_next(lfs, dir, entry);
- if (err)
- {
- return err;
- }
-
- if (((0x7f & entry->d.type) != LFS_TYPE_REG &&
- (0x7f & entry->d.type) != LFS_TYPE_DIR) ||
- entry->d.nlen != pathlen)
- {
- continue;
- }
-
- int res = lfs_bd_cmp(lfs, dir->pair[0],
- entry->off + 4 + entry->d.elen + entry->d.alen,
- pathname, pathlen);
- if (res < 0)
- {
- return res;
- }
-
- /* found match */
-
- if (res)
- {
- break;
- }
- }
-
- /* check that entry has not been moved */
-
- if (!lfs->moving && entry->d.type & 0x80)
- {
- int moved = lfs_moved(lfs, &entry->d.u);
- if (moved < 0 || moved)
- {
- return (moved < 0) ? moved : LFS_ERR_NOENT;
- }
-
- entry->d.type &= ~0x80;
- }
-
- /* to next name */
-
- pathname += pathlen;
- }
-}
-
-/* File index list operations */
-
-static int lfs_ctz_index(FAR lfs_t *lfs, FAR lfs_off_t *off)
-{
- lfs_off_t size = *off;
- lfs_off_t b = lfs->cfg->block_size - 2 * 4;
- lfs_off_t i = size / b;
- if (i == 0)
- {
- return 0;
- }
-
- i = (size - 4 * (lfs_popc(i - 1) + 2)) / b;
- *off = size - b * i - 4 * lfs_popc(i);
- return i;
-}
-
-static int lfs_ctz_find(FAR lfs_t *lfs, FAR lfs_cache_t *rcache,
- FAR const lfs_cache_t *pcache, lfs_block_t head,
- lfs_size_t size, lfs_size_t pos,
- FAR lfs_block_t *block, FAR lfs_off_t *off)
-{
- if (size == 0)
- {
- *block = 0xffffffff;
- *off = 0;
- return 0;
- }
-
- lfs_off_t current = lfs_ctz_index(lfs, &(lfs_off_t){ size - 1 });
- lfs_off_t target = lfs_ctz_index(lfs, &pos);
-
- while (current > target)
- {
- int err;
-
- lfs_size_t skip =
- lfs_min(lfs_npw2(current - target + 1) - 1, lfs_ctz(current));
-
- err = lfs_cache_read(lfs, rcache, pcache, head, 4 * skip, &head, 4);
- head = lfs_fromle32(head);
- if (err)
- {
- return err;
- }
-
- LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
- current -= 1 << skip;
- }
-
- *block = head;
- *off = pos;
- return 0;
-}
-
-static int lfs_ctz_extend(FAR lfs_t *lfs, FAR lfs_cache_t *rcache,
- FAR lfs_cache_t *pcache, lfs_block_t head,
- lfs_size_t size, FAR lfs_block_t *block,
- FAR lfs_off_t *off)
-{
- while (true)
- {
- lfs_block_t nblock;
- int err;
-
- /* go ahead and grab a block */
-
- err = lfs_alloc(lfs, &nblock);
- if (err)
- {
- return err;
- }
-
- LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count);
-
- if (true)
- {
- lfs_off_t index;
- lfs_size_t skips;
- lfs_off_t i;
-
- err = lfs_bd_erase(lfs, nblock);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- if (size == 0)
- {
- *block = nblock;
- *off = 0;
- return 0;
- }
-
- size -= 1;
- index = lfs_ctz_index(lfs, &size);
- size += 1;
-
- /* just copy out the last block if it is incomplete */
-
- if (size != lfs->cfg->block_size)
- {
- for (i = 0; i < size; i++)
- {
- uint8_t data;
- err = lfs_cache_read(lfs, rcache, NULL, head, i,
- &data, 1);
- if (err)
- {
- return err;
- }
-
- err = lfs_cache_prog(lfs, pcache, rcache, nblock, i,
- &data, 1);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
- }
-
- *block = nblock;
- *off = size;
- return 0;
- }
-
- /* append block */
-
- index += 1;
- skips = lfs_ctz(index) + 1;
-
- for (i = 0; i < skips; i++)
- {
- head = lfs_tole32(head);
- err = lfs_cache_prog(lfs, pcache, rcache, nblock, 4 * i,
- &head, 4);
- head = lfs_fromle32(head);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- if (i != skips - 1)
- {
- err = lfs_cache_read(lfs, rcache, NULL, head, 4 * i,
- &head, 4);
- head = lfs_fromle32(head);
- if (err)
- {
- return err;
- }
- }
-
- LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
- }
-
- *block = nblock;
- *off = 4 * skips;
- return 0;
- }
-
-relocate:
-
- LFS_DEBUG("Bad block at %" PRIu32, nblock);
-
- /* Just clear cache and try a new block */
-
- lfs_cache_drop(lfs, &lfs->pcache);
- }
-}
-
-static int lfs_ctz_traverse(FAR lfs_t *lfs, FAR lfs_cache_t *rcache,
- FAR const lfs_cache_t *pcache, lfs_block_t head,
- FAR lfs_size_t size,
- CODE int (*cb)(FAR void *, lfs_block_t),
- FAR void *data)
-{
- lfs_off_t index;
-
- if (size == 0)
- {
- return 0;
- }
-
- index = lfs_ctz_index(lfs, &(lfs_off_t){ size - 1 });
-
- while (true)
- {
- lfs_block_t heads[2];
- int count;
- int err;
- int i;
-
- err = cb(data, head);
- if (err)
- {
- return err;
- }
-
- if (index == 0)
- {
- return 0;
- }
-
- count = 2 - (index & 1);
- err = lfs_cache_read(lfs, rcache, pcache, head, 0, &heads, count * 4);
- heads[0] = lfs_fromle32(heads[0]);
- heads[1] = lfs_fromle32(heads[1]);
- if (err)
- {
- return err;
- }
-
- for (i = 0; i < count - 1; i++)
- {
- err = cb(data, heads[i]);
- if (err)
- {
- return err;
- }
- }
-
- head = heads[count - 1];
- index -= count;
- }
-}
-
-static int lfs_file_relocate(FAR lfs_t *lfs, FAR lfs_file_t *file)
-{
- lfs_block_t nblock;
- lfs_off_t i;
- int err;
-
-relocate:
- LFS_DEBUG("Bad block at %" PRIu32, file->block);
-
- /* just relocate what exists into new block */
-
- err = lfs_alloc(lfs, &nblock);
- if (err)
- {
- return err;
- }
-
- err = lfs_bd_erase(lfs, nblock);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- /* either read from dirty cache or disk */
-
- for (i = 0; i < file->off; i++)
- {
- uint8_t data;
- err = lfs_cache_read(lfs, &lfs->rcache, &file->cache, file->block, i,
- &data, 1);
- if (err)
- {
- return err;
- }
-
- err = lfs_cache_prog(lfs, &lfs->pcache, &lfs->rcache, nblock, i,
- &data, 1);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
- }
-
- /* copy over new state of file */
-
- memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->prog_size);
- file->cache.block = lfs->pcache.block;
- file->cache.off = lfs->pcache.off;
- lfs_cache_zero(lfs, &lfs->pcache);
-
- file->block = nblock;
- return 0;
-}
-
-static int lfs_file_flush(FAR lfs_t *lfs, FAR lfs_file_t *file)
-{
- if (file->flags & LFS_F_READING)
- {
- /* just drop read cache */
-
- lfs_cache_drop(lfs, &file->cache);
- file->flags &= ~LFS_F_READING;
- }
-
- if (file->flags & LFS_F_WRITING)
- {
- lfs_off_t pos = file->pos;
-
- /* copy over anything after current branch */
-
- lfs_file_t orig =
- {
- .head = file->head,
- .size = file->size,
- .flags = LFS_O_RDONLY,
- .pos = file->pos,
- .cache = lfs->rcache,
- };
-
- lfs_cache_drop(lfs, &lfs->rcache);
-
- while (file->pos < file->size)
- {
- /* copy over a byte at a time, leave it up to caching
- * to make this efficient
- */
-
- uint8_t data;
- lfs_ssize_t res = lfs_file_read(lfs, &orig, &data, 1);
- if (res < 0)
- {
- return res;
- }
-
- res = lfs_file_write(lfs, file, &data, 1);
- if (res < 0)
- {
- return res;
- }
-
- /* keep our reference to the rcache in sync */
-
- if (lfs->rcache.block != 0xffffffff)
- {
- lfs_cache_drop(lfs, &orig.cache);
- lfs_cache_drop(lfs, &lfs->rcache);
- }
- }
-
- /* Write out what we have */
-
- while (true)
- {
- int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- return err;
- }
-
- break;
-
-relocate:
-
- err = lfs_file_relocate(lfs, file);
- if (err)
- {
- return err;
- }
- }
-
- /* Actual file updates */
-
- file->head = file->block;
- file->size = file->pos;
- file->flags &= ~LFS_F_WRITING;
- file->flags |= LFS_F_DIRTY;
-
- file->pos = pos;
- }
-
- return 0;
-}
-
-/* Filesystem operations */
-
-static void lfs_deinit(FAR lfs_t *lfs)
-{
- /* free allocated memory */
-
- if (!lfs->cfg->read_buffer)
- {
- lfs_free(lfs->rcache.buffer);
- }
-
- if (!lfs->cfg->prog_buffer)
- {
- lfs_free(lfs->pcache.buffer);
- }
-
- if (!lfs->cfg->lookahead_buffer)
- {
- lfs_free(lfs->free.buffer);
- }
-}
-
-static int lfs_init(FAR lfs_t *lfs, FAR const struct lfs_config_s *cfg)
-{
- lfs->cfg = cfg;
-
- /* setup read cache */
-
- if (lfs->cfg->read_buffer)
- {
- lfs->rcache.buffer = lfs->cfg->read_buffer;
- }
- else
- {
- lfs->rcache.buffer = lfs_malloc(lfs->cfg->read_size);
- if (!lfs->rcache.buffer)
- {
- goto cleanup;
- }
- }
-
- /* setup program cache */
-
- if (lfs->cfg->prog_buffer)
- {
- lfs->pcache.buffer = lfs->cfg->prog_buffer;
- }
- else
- {
- lfs->pcache.buffer = lfs_malloc(lfs->cfg->prog_size);
- if (!lfs->pcache.buffer)
- {
- goto cleanup;
- }
- }
-
- /* zero to avoid information leaks */
-
- lfs_cache_zero(lfs, &lfs->pcache);
- lfs_cache_drop(lfs, &lfs->rcache);
-
- /* setup lookahead, round down to nearest 32-bits */
-
- LFS_ASSERT(lfs->cfg->lookahead % 32 == 0);
- LFS_ASSERT(lfs->cfg->lookahead > 0);
- if (lfs->cfg->lookahead_buffer)
- {
- lfs->free.buffer = lfs->cfg->lookahead_buffer;
- }
- else
- {
- lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead / 8);
- if (!lfs->free.buffer)
- {
- goto cleanup;
- }
- }
-
- /* check that program and read sizes are multiples of the block size */
-
- LFS_ASSERT(lfs->cfg->prog_size % lfs->cfg->read_size == 0);
- LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->prog_size == 0);
-
- /* check that the block size is large enough to fit ctz pointers */
-
- LFS_ASSERT(4 * lfs_npw2(0xffffffff / (lfs->cfg->block_size - 2 * 4)) <=
- lfs->cfg->block_size);
-
- /* setup default state */
-
- lfs->root[0] = 0xffffffff;
- lfs->root[1] = 0xffffffff;
- lfs->files = NULL;
- lfs->dirs = NULL;
- lfs->deorphaned = false;
- lfs->moving = false;
-
- return 0;
-
-cleanup:
- lfs_deinit(lfs);
- return LFS_ERR_NOMEM;
-}
-
-static int lfs_pred(FAR lfs_t *lfs, FAR const lfs_block_t dir[2],
- FAR lfs_dir_t *pdir)
-{
- int err;
-
- if (lfs_pairisnull(lfs->root))
- {
- return 0;
- }
-
- /* iterate over all directory directory entries */
-
- err = lfs_dir_fetch(lfs, pdir, (FAR const lfs_block_t[2]){ 0, 1 });
- if (err)
- {
- return err;
- }
-
- while (!lfs_pairisnull(pdir->d.tail))
- {
- if (lfs_paircmp(pdir->d.tail, dir) == 0)
- {
- return true;
- }
-
- err = lfs_dir_fetch(lfs, pdir, pdir->d.tail);
- if (err)
- {
- return err;
- }
- }
-
- return false;
-}
-
-static int lfs_parent(FAR lfs_t *lfs, FAR const lfs_block_t dir[2],
- FAR lfs_dir_t *parent, FAR lfs_entry_t *entry)
-{
- if (lfs_pairisnull(lfs->root))
- {
- return 0;
- }
-
- parent->d.tail[0] = 0;
- parent->d.tail[1] = 1;
-
- /* iterate over all directory directory entries */
-
- while (!lfs_pairisnull(parent->d.tail))
- {
- int err = lfs_dir_fetch(lfs, parent, parent->d.tail);
- if (err)
- {
- return err;
- }
-
- while (true)
- {
- err = lfs_dir_next(lfs, parent, entry);
- if (err && err != LFS_ERR_NOENT)
- {
- return err;
- }
-
- if (err == LFS_ERR_NOENT)
- {
- break;
- }
-
- if (((0x70 & entry->d.type) == (0x70 & LFS_TYPE_DIR)) &&
- lfs_paircmp(entry->d.u.dir, dir) == 0)
- {
- return true;
- }
- }
- }
-
- return false;
-}
-
-static int lfs_moved(FAR lfs_t *lfs, FAR const void *e)
-{
- lfs_dir_t cwd;
- lfs_entry_t entry;
- int err;
-
- if (lfs_pairisnull(lfs->root))
- {
- return 0;
- }
-
- /* skip superblock */
-
- err = lfs_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){ 0, 1 });
- if (err)
- {
- return err;
- }
-
- /* iterate over all directory directory entries */
-
- while (!lfs_pairisnull(cwd.d.tail))
- {
- err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail);
- if (err)
- {
- return err;
- }
-
- while (true)
- {
- err = lfs_dir_next(lfs, &cwd, &entry);
- if (err && err != LFS_ERR_NOENT)
- {
- return err;
- }
-
- if (err == LFS_ERR_NOENT)
- {
- break;
- }
-
- if (!(0x80 & entry.d.type) &&
- memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0)
- {
- return true;
- }
- }
- }
-
- return false;
-}
-
-static int lfs_relocate(FAR lfs_t *lfs, FAR const lfs_block_t oldpair[2],
- FAR const lfs_block_t newpair[2])
-{
- lfs_dir_t parent;
- lfs_entry_t entry;
- int res;
-
- /* find parent */
-
- res = lfs_parent(lfs, oldpair, &parent, &entry);
- if (res < 0)
- {
- return res;
- }
-
- if (res)
- {
- int err;
-
- /* update disk, this creates a desync */
-
- entry.d.u.dir[0] = newpair[0];
- entry.d.u.dir[1] = newpair[1];
-
- err = lfs_dir_update(lfs, &parent, &entry, NULL);
- if (err)
- {
- return err;
- }
-
- /* update internal root */
-
- if (lfs_paircmp(oldpair, lfs->root) == 0)
- {
- LFS_DEBUG("Relocating root %" PRIu32 " %" PRIu32, newpair[0],
- newpair[1]);
- lfs->root[0] = newpair[0];
- lfs->root[1] = newpair[1];
- }
-
- /* clean up bad block, which should now be a desync */
-
- return lfs_deorphan(lfs);
- }
-
- /* find pred */
-
- res = lfs_pred(lfs, oldpair, &parent);
- if (res < 0)
- {
- return res;
- }
-
- if (res)
- {
- /* just replace bad pair, no desync can occur */
-
- parent.d.tail[0] = newpair[0];
- parent.d.tail[1] = newpair[1];
-
- return lfs_dir_commit(lfs, &parent, NULL, 0);
- }
-
- /* couldn't find dir, must be new */
-
- return 0;
-}
-
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-/* Top level directory operations */
-
-int lfs_mkdir(FAR lfs_t *lfs, FAR const char *path)
-{
- lfs_dir_t cwd;
- lfs_dir_t dir;
- lfs_entry_t entry;
- int err;
-
- /* deorphan if we haven't yet, needed at most once after poweron */
-
- if (!lfs->deorphaned)
- {
- err = lfs_deorphan(lfs);
- if (err)
- {
- return err;
- }
- }
-
- /* fetch parent directory */
-
- err = lfs_dir_find(lfs, &cwd, &entry, &path);
- if (err != LFS_ERR_NOENT || strchr(path, '/') != NULL)
- {
- return err ? err : LFS_ERR_EXIST;
- }
-
- /* build up new directory */
-
- lfs_alloc_ack(lfs);
-
- err = lfs_dir_alloc(lfs, &dir);
- if (err)
- {
- return err;
- }
-
- dir.d.tail[0] = cwd.d.tail[0];
- dir.d.tail[1] = cwd.d.tail[1];
-
- err = lfs_dir_commit(lfs, &dir, NULL, 0);
- if (err)
- {
- return err;
- }
-
- entry.d.type = LFS_TYPE_DIR;
- entry.d.elen = sizeof(entry.d) - 4;
- entry.d.alen = 0;
- entry.d.nlen = strlen(path);
- entry.d.u.dir[0] = dir.pair[0];
- entry.d.u.dir[1] = dir.pair[1];
-
- cwd.d.tail[0] = dir.pair[0];
- cwd.d.tail[1] = dir.pair[1];
-
- err = lfs_dir_append(lfs, &cwd, &entry, path);
- if (err)
- {
- return err;
- }
-
- lfs_alloc_ack(lfs);
- return 0;
-}
-
-int lfs_dir_open(FAR lfs_t *lfs, FAR lfs_dir_t *dir, FAR const char *path)
-{
- lfs_entry_t entry;
- int err;
-
- dir->pair[0] = lfs->root[0];
- dir->pair[1] = lfs->root[1];
-
- err = lfs_dir_find(lfs, dir, &entry, &path);
- if (err)
- {
- return err;
- }
- else if (entry.d.type != LFS_TYPE_DIR)
- {
- return LFS_ERR_NOTDIR;
- }
-
- err = lfs_dir_fetch(lfs, dir, entry.d.u.dir);
- if (err)
- {
- return err;
- }
-
- /* setup head dir
- * special offset for '.' and '..'
- */
-
- dir->head[0] = dir->pair[0];
- dir->head[1] = dir->pair[1];
- dir->pos = sizeof(dir->d) - 2;
- dir->off = sizeof(dir->d);
-
- /* add to list of directories */
-
- dir->next = lfs->dirs;
- lfs->dirs = dir;
-
- return 0;
-}
-
-int lfs_dir_close(FAR lfs_t *lfs, FAR lfs_dir_t *dir)
-{
- FAR lfs_dir_t **p;
-
- /* remove from list of directories */
-
- for (p = &lfs->dirs; *p; p = &(*p)->next)
- {
- if (*p == dir)
- {
- *p = dir->next;
- break;
- }
- }
-
- return 0;
-}
-
-int lfs_dir_read(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR struct lfs_info_s *info)
-{
- lfs_entry_t entry;
-
- memset(info, 0, sizeof(*info));
-
- /* special offset for '.' and '..' */
-
- if (dir->pos == sizeof(dir->d) - 2)
- {
- info->type = LFS_TYPE_DIR;
- strcpy(info->name, ".");
- dir->pos += 1;
- return 1;
- }
- else if (dir->pos == sizeof(dir->d) - 1)
- {
- info->type = LFS_TYPE_DIR;
- strcpy(info->name, "..");
- dir->pos += 1;
- return 1;
- }
-
- while (true)
- {
- int err = lfs_dir_next(lfs, dir, &entry);
- if (err)
- {
- return (err == LFS_ERR_NOENT) ? 0 : err;
- }
-
- if ((0x7f & entry.d.type) != LFS_TYPE_REG &&
- (0x7f & entry.d.type) != LFS_TYPE_DIR)
- {
- continue;
- }
-
- /* check that entry has not been moved */
-
- if (entry.d.type & 0x80)
- {
- int moved = lfs_moved(lfs, &entry.d.u);
- if (moved < 0)
- {
- return moved;
- }
-
- if (moved)
- {
- continue;
- }
-
- entry.d.type &= ~0x80;
- }
-
- break;
- }
-
- info->type = entry.d.type;
- if (info->type == LFS_TYPE_REG)
- {
- info->size = entry.d.u.file.size;
- }
-
- int err = lfs_bd_read(lfs, dir->pair[0],
- entry.off + 4 + entry.d.elen + entry.d.alen,
- info->name, entry.d.nlen);
- if (err)
- {
- return err;
- }
-
- return 1;
-}
-
-int lfs_dir_seek(FAR lfs_t *lfs, FAR lfs_dir_t *dir, lfs_off_t off)
-{
- int err;
-
- /* simply walk from head dir */
-
- err = lfs_dir_rewind(lfs, dir);
- if (err)
- {
- return err;
- }
-
- dir->pos = off;
-
- while (off > (0x7fffffff & dir->d.size))
- {
- off -= 0x7fffffff & dir->d.size;
- if (!(0x80000000 & dir->d.size))
- {
- return LFS_ERR_INVAL;
- }
-
- err = lfs_dir_fetch(lfs, dir, dir->d.tail);
- if (err)
- {
- return err;
- }
- }
-
- dir->off = off;
- return 0;
-}
-
-lfs_soff_t lfs_dir_tell(FAR lfs_t *lfs, FAR lfs_dir_t *dir)
-{
- return dir->pos;
-}
-
-int lfs_dir_rewind(FAR lfs_t *lfs, FAR lfs_dir_t *dir)
-{
- int err;
-
- /* reload the head dir */
-
- err = lfs_dir_fetch(lfs, dir, dir->head);
- if (err)
- {
- return err;
- }
-
- dir->pair[0] = dir->head[0];
- dir->pair[1] = dir->head[1];
- dir->pos = sizeof(dir->d) - 2;
- dir->off = sizeof(dir->d);
- return 0;
-}
-
-/* Top level file operations */
-
-int lfs_file_opencfg(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR const char *path, int flags,
- FAR const struct lfs_file_config_s *cfg)
-{
- lfs_dir_t cwd;
- lfs_entry_t entry;
- int err;
-
- /* deorphan if we haven't yet, needed at most once after poweron */
-
- if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned)
- {
- err = lfs_deorphan(lfs);
- if (err)
- {
- return err;
- }
- }
-
- /* allocate entry for file if it doesn't exist */
-
- err = lfs_dir_find(lfs, &cwd, &entry, &path);
- if (err && (err != LFS_ERR_NOENT || strchr(path, '/') != NULL))
- {
- return err;
- }
-
- if (err == LFS_ERR_NOENT)
- {
- if (!(flags & LFS_O_CREAT))
- {
- return LFS_ERR_NOENT;
- }
-
- /* create entry to remember name */
-
- entry.d.type = LFS_TYPE_REG;
- entry.d.elen = sizeof(entry.d) - 4;
- entry.d.alen = 0;
- entry.d.nlen = strlen(path);
- entry.d.u.file.head = 0xffffffff;
- entry.d.u.file.size = 0;
- err = lfs_dir_append(lfs, &cwd, &entry, path);
- if (err)
- {
- return err;
- }
- }
- else if (entry.d.type == LFS_TYPE_DIR)
- {
- return LFS_ERR_ISDIR;
- }
- else if (flags & LFS_O_EXCL)
- {
- return LFS_ERR_EXIST;
- }
-
- /* setup file struct */
-
- file->cfg = cfg;
- file->pair[0] = cwd.pair[0];
- file->pair[1] = cwd.pair[1];
- file->poff = entry.off;
- file->head = entry.d.u.file.head;
- file->size = entry.d.u.file.size;
- file->flags = flags;
- file->pos = 0;
-
- if (flags & LFS_O_TRUNC)
- {
- if (file->size != 0)
- {
- file->flags |= LFS_F_DIRTY;
- }
-
- file->head = 0xffffffff;
- file->size = 0;
- }
-
- /* allocate buffer if needed */
-
- file->cache.block = 0xffffffff;
- if (file->cfg && file->cfg->buffer)
- {
- file->cache.buffer = file->cfg->buffer;
- }
- else if (lfs->cfg->file_buffer)
- {
- if (lfs->files)
- {
- /* already in use */
-
- return LFS_ERR_NOMEM;
- }
-
- file->cache.buffer = lfs->cfg->file_buffer;
- }
- else if ((file->flags & 3) == LFS_O_RDONLY)
- {
- file->cache.buffer = lfs_malloc(lfs->cfg->read_size);
- if (!file->cache.buffer)
- {
- return LFS_ERR_NOMEM;
- }
- }
- else
- {
- file->cache.buffer = lfs_malloc(lfs->cfg->prog_size);
- if (!file->cache.buffer)
- {
- return LFS_ERR_NOMEM;
- }
- }
-
- /* zero to avoid information leak */
-
- lfs_cache_drop(lfs, &file->cache);
- if ((file->flags & 3) != LFS_O_RDONLY)
- {
- lfs_cache_zero(lfs, &file->cache);
- }
-
- /* add to list of files */
-
- file->next = lfs->files;
- lfs->files = file;
-
- return 0;
-}
-
-int lfs_file_open(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR const char *path, int flags)
-{
- return lfs_file_opencfg(lfs, file, path, flags, NULL);
-}
-
-int lfs_file_close(FAR lfs_t *lfs, FAR lfs_file_t *file)
-{
- FAR lfs_file_t **p;
- int err;
-
- err = lfs_file_sync(lfs, file);
-
- /* remove from list of files */
-
- for (p = &lfs->files; *p; p = &(*p)->next)
- {
- if (*p == file)
- {
- *p = file->next;
- break;
- }
- }
-
- /* clean up memory */
-
- if (!(file->cfg && file->cfg->buffer) && !lfs->cfg->file_buffer)
- {
- lfs_free(file->cache.buffer);
- }
-
- return err;
-}
-
-int lfs_file_sync(FAR lfs_t *lfs, FAR lfs_file_t *file)
-{
- int err = lfs_file_flush(lfs, file);
- if (err)
- {
- return err;
- }
-
- if ((file->flags & LFS_F_DIRTY) && !(file->flags & LFS_F_ERRED) &&
- !lfs_pairisnull(file->pair))
- {
- lfs_dir_t cwd;
- lfs_entry_t entry =
- {
- .off = file->poff
- };
-
- /* update dir entry */
-
- err = lfs_dir_fetch(lfs, &cwd, file->pair);
- if (err)
- {
- return err;
- }
-
- err = lfs_bd_read(lfs, cwd.pair[0], entry.off, &entry.d,
- sizeof(entry.d));
- lfs_entry_fromle32(&entry.d);
- if (err)
- {
- return err;
- }
-
- LFS_ASSERT(entry.d.type == LFS_TYPE_REG);
- entry.d.u.file.head = file->head;
- entry.d.u.file.size = file->size;
-
- err = lfs_dir_update(lfs, &cwd, &entry, NULL);
- if (err)
- {
- return err;
- }
-
- file->flags &= ~LFS_F_DIRTY;
- }
-
- return 0;
-}
-
-lfs_ssize_t lfs_file_read(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR void *buffer, lfs_size_t size)
-{
- FAR uint8_t *data = buffer;
- lfs_size_t nsize = size;
-
- if ((file->flags & 3) == LFS_O_WRONLY)
- {
- return LFS_ERR_BADF;
- }
-
- if (file->flags & LFS_F_WRITING)
- {
- /* flush out any writes */
-
- int err = lfs_file_flush(lfs, file);
- if (err)
- {
- return err;
- }
- }
-
- if (file->pos >= file->size)
- {
- /* eof if past end */
-
- return 0;
- }
-
- size = lfs_min(size, file->size - file->pos);
- nsize = size;
-
- while (nsize > 0)
- {
- /* check if we need a new block */
-
- if (!(file->flags & LFS_F_READING) || file->off == lfs->cfg->block_size)
- {
- int err =
- lfs_ctz_find(lfs, &file->cache, NULL, file->head, file->size,
- file->pos, &file->block, &file->off);
- if (err)
- {
- return err;
- }
-
- file->flags |= LFS_F_READING;
- }
-
- /* read as much as we can in current block */
-
- lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off);
- int err = lfs_cache_read(lfs, &file->cache, NULL, file->block,
- file->off, data, diff);
- if (err)
- {
- return err;
- }
-
- file->pos += diff;
- file->off += diff;
- data += diff;
- nsize -= diff;
- }
-
- return size;
-}
-
-lfs_ssize_t lfs_file_write(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR const void *buffer, lfs_size_t size)
-{
- FAR const uint8_t *data = buffer;
- lfs_size_t nsize = size;
-
- if ((file->flags & 3) == LFS_O_RDONLY)
- {
- return LFS_ERR_BADF;
- }
-
- if (file->flags & LFS_F_READING)
- {
- /* drop any reads */
-
- int err = lfs_file_flush(lfs, file);
- if (err)
- {
- return err;
- }
- }
-
- if ((file->flags & LFS_O_APPEND) && file->pos < file->size)
- {
- file->pos = file->size;
- }
-
- if (file->pos + size > LFS_FILE_MAX)
- {
- /* larger than file limit? */
-
- return LFS_ERR_FBIG;
- }
-
- if (!(file->flags & LFS_F_WRITING) && file->pos > file->size)
- {
- /* fill with zeros */
-
- lfs_off_t pos = file->pos;
- file->pos = file->size;
-
- while (file->pos < pos)
- {
- lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){ 0 }, 1);
- if (res < 0)
- {
- return res;
- }
- }
- }
-
- while (nsize > 0)
- {
- lfs_size_t diff;
-
- /* check if we need a new block */
-
- if (!(file->flags & LFS_F_WRITING) || file->off == lfs->cfg->block_size)
- {
- int err;
-
- if (!(file->flags & LFS_F_WRITING) && file->pos > 0)
- {
- /* find out which block we're extending from */
-
- err = lfs_ctz_find(lfs, &file->cache, NULL, file->head,
- file->size, file->pos - 1, &file->block,
- &file->off);
- if (err)
- {
- file->flags |= LFS_F_ERRED;
- return err;
- }
-
- /* mark cache as dirty since we may have read data into it */
-
- lfs_cache_zero(lfs, &file->cache);
- }
-
- /* extend file with new blocks */
-
- lfs_alloc_ack(lfs);
- err = lfs_ctz_extend(lfs, &lfs->rcache, &file->cache, file->block,
- file->pos, &file->block, &file->off);
- if (err)
- {
- file->flags |= LFS_F_ERRED;
- return err;
- }
-
- file->flags |= LFS_F_WRITING;
- }
-
- /* program as much as we can in current block */
-
- diff = lfs_min(nsize, lfs->cfg->block_size - file->off);
- while (true)
- {
- int err = lfs_cache_prog(lfs, &file->cache, &lfs->rcache,
- file->block, file->off, data, diff);
- if (err)
- {
- if (err == LFS_ERR_CORRUPT)
- {
- goto relocate;
- }
-
- file->flags |= LFS_F_ERRED;
- return err;
- }
-
- break;
- relocate:
- err = lfs_file_relocate(lfs, file);
- if (err)
- {
- file->flags |= LFS_F_ERRED;
- return err;
- }
- }
-
- file->pos += diff;
- file->off += diff;
- data += diff;
- nsize -= diff;
-
- lfs_alloc_ack(lfs);
- }
-
- file->flags &= ~LFS_F_ERRED;
- return size;
-}
-
-lfs_soff_t lfs_file_seek(FAR lfs_t *lfs, FAR lfs_file_t *file,
- lfs_soff_t off, int whence)
-{
- lfs_soff_t npos;
- int err;
-
- /* write out everything beforehand, may be noop if rdonly */
-
- err = lfs_file_flush(lfs, file);
- if (err)
- {
- return err;
- }
-
- /* find new pos */
-
- npos = file->pos;
- if (whence == LFS_SEEK_SET)
- {
- npos = off;
- }
- else if (whence == LFS_SEEK_CUR)
- {
- npos = file->pos + off;
- }
- else if (whence == LFS_SEEK_END)
- {
- npos = file->size + off;
- }
-
- if (npos < 0 || npos > LFS_FILE_MAX)
- {
- /* file position out of range */
-
- return LFS_ERR_INVAL;
- }
-
- /* update pos */
-
- file->pos = npos;
- return npos;
-}
-
-int lfs_file_truncate(FAR lfs_t *lfs, FAR lfs_file_t *file, lfs_off_t size)
-{
- lfs_off_t oldsize;
-
- if ((file->flags & 3) == LFS_O_RDONLY)
- {
- return LFS_ERR_BADF;
- }
-
- oldsize = lfs_file_size(lfs, file);
- if (size < oldsize)
- {
- /* need to flush since directly changing metadata */
-
- int err = lfs_file_flush(lfs, file);
- if (err)
- {
- return err;
- }
-
- /* lookup new head in ctz skip list */
-
- err = lfs_ctz_find(lfs, &file->cache, NULL, file->head, file->size,
- size, &file->head, &(lfs_off_t){ 0 });
- if (err)
- {
- return err;
- }
-
- file->size = size;
- file->flags |= LFS_F_DIRTY;
- }
- else if (size > oldsize)
- {
- int err;
-
- lfs_off_t pos = file->pos;
-
- /* flush+seek if not already at end */
-
- if (file->pos != oldsize)
- {
- err = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
- if (err < 0)
- {
- return err;
- }
- }
-
- /* fill with zeros */
-
- while (file->pos < size)
- {
- lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){ 0 }, 1);
- if (res < 0)
- {
- return res;
- }
- }
-
- /* restore pos */
-
- err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
- if (err < 0)
- {
- return err;
- }
- }
-
- return 0;
-}
-
-lfs_soff_t lfs_file_tell(FAR lfs_t *lfs, FAR lfs_file_t *file)
-{
- return file->pos;
-}
-
-int lfs_file_rewind(FAR lfs_t *lfs, FAR lfs_file_t *file)
-{
- lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET);
- if (res < 0)
- {
- return res;
- }
-
- return 0;
-}
-
-lfs_soff_t lfs_file_size(FAR lfs_t *lfs, FAR lfs_file_t *file)
-{
- if (file->flags & LFS_F_WRITING)
- {
- return lfs_max(file->pos, file->size);
- }
- else
- {
- return file->size;
- }
-}
-
-/* General fs operations */
-
-int lfs_stat(FAR lfs_t *lfs, FAR const char *path,
- FAR struct lfs_info_s *info)
-{
- lfs_dir_t cwd;
- lfs_entry_t entry;
- int err = lfs_dir_find(lfs, &cwd, &entry, &path);
- if (err)
- {
- return err;
- }
-
- memset(info, 0, sizeof(*info));
- info->type = entry.d.type;
- if (info->type == LFS_TYPE_REG)
- {
- info->size = entry.d.u.file.size;
- }
-
- if (lfs_paircmp(entry.d.u.dir, lfs->root) == 0)
- {
- strcpy(info->name, "/");
- }
- else
- {
- err = lfs_bd_read(lfs, cwd.pair[0],
- entry.off + 4 + entry.d.elen + entry.d.alen,
- info->name, entry.d.nlen);
- if (err)
- {
- return err;
- }
- }
-
- return 0;
-}
-
-int lfs_remove(FAR lfs_t *lfs, FAR const char *path)
-{
- lfs_dir_t cwd;
- lfs_entry_t entry;
- int err;
-
- /* deorphan if we haven't yet, needed at most once after poweron */
-
- if (!lfs->deorphaned)
- {
- err = lfs_deorphan(lfs);
- if (err)
- {
- return err;
- }
- }
-
- err = lfs_dir_find(lfs, &cwd, &entry, &path);
- if (err)
- {
- return err;
- }
-
- lfs_dir_t dir;
- if (entry.d.type == LFS_TYPE_DIR)
- {
- /* must be empty before removal, checking size
- * without masking top bit checks for any case where
- * dir is not empty
- */
-
- err = lfs_dir_fetch(lfs, &dir, entry.d.u.dir);
- if (err)
- {
- return err;
- }
- else if (dir.d.size != sizeof(dir.d) + 4)
- {
- return LFS_ERR_NOTEMPTY;
- }
- }
-
- /* remove the entry */
-
- err = lfs_dir_remove(lfs, &cwd, &entry);
- if (err)
- {
- return err;
- }
-
- /* if we were a directory, find pred, replace tail */
-
- if (entry.d.type == LFS_TYPE_DIR)
- {
- int res = lfs_pred(lfs, dir.pair, &cwd);
- if (res < 0)
- {
- return res;
- }
-
- LFS_ASSERT(res); /* must have pred */
-
- cwd.d.tail[0] = dir.d.tail[0];
- cwd.d.tail[1] = dir.d.tail[1];
-
- err = lfs_dir_commit(lfs, &cwd, NULL, 0);
- if (err)
- {
- return err;
- }
- }
-
- return 0;
-}
-
-int lfs_rename(FAR lfs_t *lfs, FAR const char *oldpath,
- FAR const char *newpath)
-{
- lfs_dir_t oldcwd;
- lfs_dir_t newcwd;
- lfs_dir_t dir;
- lfs_entry_t oldentry;
- lfs_entry_t preventry;
- lfs_entry_t newentry;
- bool prevexists;
- int err;
-
- /* deorphan if we haven't yet, needed at most once after poweron */
-
- if (!lfs->deorphaned)
- {
- err = lfs_deorphan(lfs);
- if (err)
- {
- return err;
- }
- }
-
- /* find old entry */
-
- err = lfs_dir_find(lfs, &oldcwd, &oldentry,
- &(FAR const char *){ oldpath });
- if (err)
- {
- return err;
- }
-
- /* mark as moving */
-
- oldentry.d.type |= 0x80;
- err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL);
- if (err)
- {
- return err;
- }
-
- /* allocate new entry */
-
- err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath);
- if (err && (err != LFS_ERR_NOENT || strchr(newpath, '/') != NULL))
- {
- return err;
- }
-
- /* must have same type */
-
- prevexists = (err != LFS_ERR_NOENT);
- if (prevexists && preventry.d.type != (0x7f & oldentry.d.type))
- {
- return LFS_ERR_ISDIR;
- }
-
- if (prevexists && preventry.d.type == LFS_TYPE_DIR)
- {
- /* must be empty before removal, checking size
- * without masking top bit checks for any case where
- * dir is not empty
- */
-
- err = lfs_dir_fetch(lfs, &dir, preventry.d.u.dir);
- if (err)
- {
- return err;
- }
- else if (dir.d.size != sizeof(dir.d) + 4)
- {
- return LFS_ERR_NOTEMPTY;
- }
- }
-
- /* move to new location */
-
- newentry = preventry;
- newentry.d = oldentry.d;
- newentry.d.type &= ~0x80;
- newentry.d.nlen = strlen(newpath);
-
- if (prevexists)
- {
- err = lfs_dir_update(lfs, &newcwd, &newentry, newpath);
- if (err)
- {
- return err;
- }
- }
- else
- {
- err = lfs_dir_append(lfs, &newcwd, &newentry, newpath);
- if (err)
- {
- return err;
- }
- }
-
- /* fetch old pair again in case dir block changed */
-
- lfs->moving = true;
- err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
- if (err)
- {
- return err;
- }
-
- lfs->moving = false;
-
- /* remove old entry */
-
- err = lfs_dir_remove(lfs, &oldcwd, &oldentry);
- if (err)
- {
- return err;
- }
-
- /* if we were a directory, find pred, replace tail */
-
- if (prevexists && preventry.d.type == LFS_TYPE_DIR)
- {
- int res = lfs_pred(lfs, dir.pair, &newcwd);
- if (res < 0)
- {
- return res;
- }
-
- LFS_ASSERT(res); /* must have pred */
-
- newcwd.d.tail[0] = dir.d.tail[0];
- newcwd.d.tail[1] = dir.d.tail[1];
-
- err = lfs_dir_commit(lfs, &newcwd, NULL, 0);
- if (err)
- {
- return err;
- }
- }
-
- return 0;
-}
-
-int lfs_format(FAR lfs_t *lfs, FAR const struct lfs_config_s *cfg)
-{
- lfs_superblock_t superblock;
- bool valid;
- int err = 0;
-
- if (true)
- {
- lfs_dir_t superdir;
- lfs_dir_t root;
- int i;
-
- err = lfs_init(lfs, cfg);
- if (err)
- {
- return err;
- }
-
- /* create free lookahead */
-
- memset(lfs->free.buffer, 0, lfs->cfg->lookahead / 8);
- lfs->free.off = 0;
- lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
- lfs->free.i = 0;
- lfs_alloc_ack(lfs);
-
- /* create superblock dir */
-
- err = lfs_dir_alloc(lfs, &superdir);
- if (err)
- {
- goto cleanup;
- }
-
- /* write root directory */
-
- err = lfs_dir_alloc(lfs, &root);
- if (err)
- {
- goto cleanup;
- }
-
- err = lfs_dir_commit(lfs, &root, NULL, 0);
- if (err)
- {
- goto cleanup;
- }
-
- lfs->root[0] = root.pair[0];
- lfs->root[1] = root.pair[1];
-
- /* write superblocks */
-
- superblock.off = sizeof(superdir.d);
- superblock.d.type = LFS_TYPE_SUPERBLOCK;
- superblock.d.elen = sizeof(superblock.d) -
- sizeof(superblock.d.magic) - 4;
- superblock.d.nlen = sizeof(superblock.d.magic);
- superblock.d.version = LFS_DISK_VERSION;
- superblock.d.block_size = lfs->cfg->block_size;
- superblock.d.block_count = lfs->cfg->block_count;
- superblock.d.root[0] = lfs->root[0];
- superblock.d.root[1] = lfs->root[1];
-
- memcpy(superblock.d.magic, "littlefs", 8);
-
- superdir.d.tail[0] = root.pair[0];
- superdir.d.tail[1] = root.pair[1];
- superdir.d.size = sizeof(superdir.d) +
- sizeof(superblock.d) + 4;
-
- /* write both pairs to be safe */
-
- lfs_superblock_tole32(&superblock.d);
- valid = false;
- for (i = 0; i < 2; i++)
- {
- err = lfs_dir_commit(
- lfs, &superdir,
- (struct lfs_region_s[])
- {
- {
- sizeof(superdir.d), sizeof(superblock.d),
- &superblock.d, sizeof(superblock.d)
- }
- },
- 1);
-
- if (err && err != LFS_ERR_CORRUPT)
- {
- goto cleanup;
- }
-
- valid = valid || !err;
- }
-
- if (!valid)
- {
- err = LFS_ERR_CORRUPT;
- goto cleanup;
- }
-
- /* sanity check that fetch works */
-
- err = lfs_dir_fetch(lfs, &superdir,
- (FAR const lfs_block_t[2]){0, 1});
- if (err)
- {
- goto cleanup;
- }
-
- lfs_alloc_ack(lfs);
- }
-
-cleanup:
- lfs_deinit(lfs);
- return err;
-}
-
-int lfs_mount(FAR lfs_t *lfs, FAR const struct lfs_config_s *cfg)
-{
- int err = 0;
- if (true)
- {
- lfs_dir_t dir;
- lfs_superblock_t superblock;
- uint16_t major_version;
- uint16_t minor_version;
-
- err = lfs_init(lfs, cfg);
- if (err)
- {
- return err;
- }
-
- /* setup free lookahead */
-
- lfs->free.off = 0;
- lfs->free.size = 0;
- lfs->free.i = 0;
- lfs_alloc_ack(lfs);
-
- /* load superblock */
-
- err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){ 0, 1 });
- if (err && err != LFS_ERR_CORRUPT)
- {
- goto cleanup;
- }
-
- if (!err)
- {
- err = lfs_bd_read(lfs, dir.pair[0], sizeof(dir.d), &superblock.d,
- sizeof(superblock.d));
- lfs_superblock_fromle32(&superblock.d);
- if (err)
- {
- goto cleanup;
- }
-
- lfs->root[0] = superblock.d.root[0];
- lfs->root[1] = superblock.d.root[1];
- }
-
- if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0)
- {
- LFS_ERROR("Invalid superblock at %d %d", 0, 1);
- err = LFS_ERR_CORRUPT;
- goto cleanup;
- }
-
- major_version = (0xffff & (superblock.d.version >> 16));
- minor_version = (0xffff & (superblock.d.version >> 0));
- if ((major_version != LFS_DISK_VERSION_MAJOR ||
- minor_version > LFS_DISK_VERSION_MINOR))
- {
- LFS_ERROR("Invalid version %d.%d", major_version, minor_version);
- err = LFS_ERR_INVAL;
- goto cleanup;
- }
-
- return 0;
- }
-
-cleanup:
-
- lfs_deinit(lfs);
- return err;
-}
-
-int lfs_unmount(FAR lfs_t *lfs)
-{
- lfs_deinit(lfs);
- return 0;
-}
-
-/* Littlefs specific operations */
-
-int lfs_traverse(FAR lfs_t *lfs, CODE int (*cb)(void *, lfs_block_t),
- FAR void *data)
-{
- lfs_dir_t dir;
- lfs_entry_t entry;
- lfs_block_t cwd[2] =
- {
- 0, 1
- };
-
- FAR lfs_file_t *f;
-
- if (lfs_pairisnull(lfs->root))
- {
- return 0;
- }
-
- /* iterate over metadata pairs */
-
- while (true)
- {
- int err;
- int i;
-
- for (i = 0; i < 2; i++)
- {
- err = cb(data, cwd[i]);
- if (err)
- {
- return err;
- }
- }
-
- err = lfs_dir_fetch(lfs, &dir, cwd);
- if (err)
- {
- return err;
- }
-
- /* iterate over contents */
-
- while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size) - 4)
- {
- err = lfs_bd_read(lfs, dir.pair[0], dir.off, &entry.d,
- sizeof(entry.d));
- lfs_entry_fromle32(&entry.d);
- if (err)
- {
- return err;
- }
-
- dir.off += lfs_entry_size(&entry);
- if ((0x70 & entry.d.type) == (0x70 & LFS_TYPE_REG))
- {
- err = lfs_ctz_traverse(lfs, &lfs->rcache, NULL,
- entry.d.u.file.head,
- entry.d.u.file.size, cb, data);
- if (err)
- {
- return err;
- }
- }
- }
-
- cwd[0] = dir.d.tail[0];
- cwd[1] = dir.d.tail[1];
-
- if (lfs_pairisnull(cwd))
- {
- break;
- }
- }
-
- /* iterate over any open files */
-
- for (f = lfs->files; f; f = f->next)
- {
- if (f->flags & LFS_F_DIRTY)
- {
- int err = lfs_ctz_traverse(lfs, &lfs->rcache, &f->cache, f->head,
- f->size, cb, data);
- if (err)
- {
- return err;
- }
- }
-
- if (f->flags & LFS_F_WRITING)
- {
- int err = lfs_ctz_traverse(lfs, &lfs->rcache, &f->cache, f->block,
- f->pos, cb, data);
- if (err)
- {
- return err;
- }
- }
- }
-
- return 0;
-}
-
-int lfs_deorphan(FAR lfs_t *lfs)
-{
- lfs_dir_t pdir =
- {
- .d.size = 0x80000000
- };
-
- lfs_dir_t cwd =
- {
- .d.tail[0] = 0,
- .d.tail[1] = 1
- };
-
- lfs_size_t i;
- int err;
-
- lfs->deorphaned = true;
-
- if (lfs_pairisnull(lfs->root))
- {
- return 0;
- }
-
- /* iterate over all directory directory entries */
-
- for (i = 0; i < lfs->cfg->block_count; i++)
- {
- lfs_entry_t entry;
-
- if (lfs_pairisnull(cwd.d.tail))
- {
- return 0;
- }
-
- err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail);
- if (err)
- {
- return err;
- }
-
- /* check head blocks for orphans */
-
- if (!(0x80000000 & pdir.d.size))
- {
- lfs_dir_t parent;
- int res;
-
- /* check if we have a parent */
-
- res = lfs_parent(lfs, pdir.d.tail, &parent, &entry);
- if (res < 0)
- {
- return res;
- }
-
- if (!res)
- {
- /* we are an orphan */
-
- LFS_DEBUG("Found orphan %" PRIu32 " %" PRIu32, pdir.d.tail[0],
- pdir.d.tail[1]);
-
- pdir.d.tail[0] = cwd.d.tail[0];
- pdir.d.tail[1] = cwd.d.tail[1];
-
- err = lfs_dir_commit(lfs, &pdir, NULL, 0);
- if (err)
- {
- return err;
- }
-
- return 0;
- }
-
- if (!lfs_pairsync(entry.d.u.dir, pdir.d.tail))
- {
- /* we have desynced */
-
- LFS_DEBUG("Found desync %" PRIu32 " %" PRIu32, entry.d.u.dir[0],
- entry.d.u.dir[1]);
-
- pdir.d.tail[0] = entry.d.u.dir[0];
- pdir.d.tail[1] = entry.d.u.dir[1];
-
- err = lfs_dir_commit(lfs, &pdir, NULL, 0);
- if (err)
- {
- return err;
- }
-
- return 0;
- }
- }
-
- /* check entries for moves */
-
- while (true)
- {
- err = lfs_dir_next(lfs, &cwd, &entry);
- if (err && err != LFS_ERR_NOENT)
- {
- return err;
- }
-
- if (err == LFS_ERR_NOENT)
- {
- break;
- }
-
- /* found moved entry */
-
- if (entry.d.type & 0x80)
- {
- int moved = lfs_moved(lfs, &entry.d.u);
- if (moved < 0)
- {
- return moved;
- }
-
- if (moved)
- {
- LFS_DEBUG("Found move %" PRIu32 " %" PRIu32,
- entry.d.u.dir[0], entry.d.u.dir[1]);
-
- err = lfs_dir_remove(lfs, &cwd, &entry);
- if (err)
- {
- return err;
- }
- }
- else
- {
- LFS_DEBUG("Found partial move %" PRIu32 " %" PRIu32,
- entry.d.u.dir[0], entry.d.u.dir[1]);
-
- entry.d.type &= ~0x80;
- err = lfs_dir_update(lfs, &cwd, &entry, NULL);
- if (err)
- {
- return err;
- }
- }
- }
- }
-
- memcpy(&pdir, &cwd, sizeof(pdir));
- }
-
- /* If we reached here, we have more directory pairs than blocks in the
- * filesystem... So something must be horribly wrong
- */
-
- return LFS_ERR_CORRUPT;
-}
diff --git a/fs/littlefs/lfs.h b/fs/littlefs/lfs.h
deleted file mode 100644
index a597593..0000000
--- a/fs/littlefs/lfs.h
+++ /dev/null
@@ -1,673 +0,0 @@
-/****************************************************************************
- * fs/littlefs/lfs.h
- *
- * This file is a part of NuttX:
- *
- * Copyright (C) 2019 Gregory Nutt. All rights reserved.
- *
- * Ported by:
- *
- * Copyright (C) 2019 Pinecone Inc. All rights reserved.
- * Author: lihaichen <li...@163.com>
- *
- * This port derives from ARM mbed logic which has a compatible 3-clause
- * BSD license:
- *
- * Copyright (c) 2017, Arm Limited. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the names ARM, NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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.
- *
- ****************************************************************************/
-
-#ifndef __FS_LITTLEFS_LFS_H
-#define __FS_LITTLEFS_LFS_H
-
-/****************************************************************************
- * Included Files
- ****************************************************************************/
-
-#include <stdint.h>
-#include <stdbool.h>
-
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/* Version info */
-
-/* Software library version
- * Major (top-nibble), incremented on backwards incompatible changes
- * Minor (bottom-nibble), incremented on feature additions
- */
-
-#define LFS_VERSION 0x00010007
-#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_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
-#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
-
-/* Max name size in bytes */
-
-#ifndef LFS_NAME_MAX
-# define LFS_NAME_MAX NAME_MAX
-#endif
-
-/* Max file size in bytes */
-
-#ifndef LFS_FILE_MAX
-# define LFS_FILE_MAX 2147483647
-#endif
-
-/****************************************************************************
- * Public Types
- ****************************************************************************/
-
-typedef uint32_t lfs_size_t;
-typedef uint32_t lfs_off_t;
-
-typedef int32_t lfs_ssize_t;
-typedef int32_t lfs_soff_t;
-
-typedef uint32_t lfs_block_t;
-
-/* Possible error codes, these are negative to allow
- * valid positive return values
- */
-
-enum lfs_error_e
-{
- 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_FBIG = -27, /* File too large */
- LFS_ERR_INVAL = -22, /* Invalid parameter */
- LFS_ERR_NOSPC = -28, /* No space left on device */
- LFS_ERR_NOMEM = -12, /* No more memory available */
-};
-
-/* File types */
-
-enum lfs_type_e
-{
- LFS_TYPE_REG = 0x11,
- LFS_TYPE_DIR = 0x22,
- LFS_TYPE_SUPERBLOCK = 0x2e,
-};
-
-/* File open flags */
-
-enum lfs_open_flags_e
-{
- /* 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 */
-
- /* 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 occurred during write */
-};
-
-/* File seek flags */
-
-enum lfs_whence_flags_e
-{
- LFS_SEEK_SET = 0, /* Seek relative to an absolute position */
- LFS_SEEK_CUR = 1, /* Seek relative to the current file position */
- LFS_SEEK_END = 2, /* Seek relative to the end of the file */
-};
-
-/* Configuration provided during initialization of the littlefs */
-
-struct lfs_config_s
-{
- /* Opaque user provided context that can be used to pass
- * information to the block device operations
- */
-
- FAR void *context;
-
- /* Read a region in a block. Negative error codes are propagated
- * to the user.
- */
-
- CODE int (*read)(FAR const struct lfs_config_s *c, lfs_block_t block,
- lfs_off_t off, FAR void *buffer, lfs_size_t size);
-
- /* Program a region in a block. The block must have previously
- * been erased. Negative error codes are propagated to the user.
- * May return LFS_ERR_CORRUPT if the block should be considered bad.
- */
-
- CODE int (*prog)(FAR const struct lfs_config_s *c, lfs_block_t block,
- lfs_off_t off, const void *buffer, lfs_size_t size);
-
- /* Erase a block. A block must be erased before being programmed.
- * The state of an erased block is undefined. Negative error codes
- * are propagated to the user.
- * May return LFS_ERR_CORRUPT if the block should be considered bad.
- */
-
- CODE int (*erase)(FAR const struct lfs_config_s *c, lfs_block_t block);
-
- /* Sync the state of the underlying block device. Negative error codes
- * are propagated to the user.
- */
-
- CODE int (*sync)(FAR const struct lfs_config_s *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.
- */
-
- 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.
- */
-
- 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.
- */
-
- lfs_size_t block_size;
-
- /* Number of erasable blocks on the device. */
-
- lfs_size_t block_count;
-
- /* 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
- * large with little ram impact. Should be a multiple of 32.
- */
-
- lfs_size_t lookahead;
-
- /* Optional, statically allocated read buffer. Must be read sized. */
-
- FAR void *read_buffer;
-
- /* Optional, statically allocated program buffer. Must be program sized. */
-
- FAR void *prog_buffer;
-
- /* Optional, statically allocated lookahead buffer. Must be 1 bit per
- * lookahead block.
- */
-
- FAR void *lookahead_buffer;
-
- /* Optional, statically allocated buffer for files. Must be program sized.
- * If enabled, only one file may be opened at a time.
- */
-
- FAR void *file_buffer;
-};
-
-/* Optional configuration provided during lfs_file_opencfg */
-
-struct lfs_file_config_s
-{
- /* Optional, statically allocated buffer for files. Must be program sized.
- * If NULL, malloc will be used by default.
- */
-
- FAR void *buffer;
-};
-
-/* File info structure */
-
-struct lfs_info_s
-{
- /* Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR */
-
- uint8_t type;
-
- /* Size of the file, only valid for REG files */
-
- lfs_size_t size;
-
- /* Name of the file stored as a null-terminated string */
-
- char name[LFS_NAME_MAX + 1];
-};
-
-/* littlefs data structures */
-
-typedef struct lfs_entry_s
-{
- lfs_off_t off;
-
- struct lfs_disk_entry_s
- {
- 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_cache_s
-{
- lfs_block_t block;
- lfs_off_t off;
- FAR uint8_t *buffer;
-} lfs_cache_t;
-
-typedef struct lfs_file_s
-{
- FAR struct lfs_file_s *next;
- lfs_block_t pair[2];
- lfs_off_t poff;
-
- lfs_block_t head;
- lfs_size_t size;
-
- FAR const struct lfs_file_config_s *cfg;
- uint32_t flags;
- lfs_off_t pos;
- lfs_block_t block;
- lfs_off_t off;
- lfs_cache_t cache;
-} lfs_file_t;
-
-typedef struct lfs_dir_s
-{
- FAR struct lfs_dir_s *next;
- lfs_block_t pair[2];
- lfs_off_t off;
-
- lfs_block_t head[2];
- lfs_off_t pos;
-
- struct lfs_disk_dir_s
- {
- uint32_t rev;
- lfs_size_t size;
- lfs_block_t tail[2];
- } d;
-} lfs_dir_t;
-
-typedef struct lfs_superblock_s
-{
- lfs_off_t off;
-
- struct lfs_disk_superblock_s
- {
- 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_superblock_t;
-
-typedef struct lfs_free_s
-{
- lfs_block_t off;
- lfs_block_t size;
- lfs_block_t i;
- lfs_block_t ack;
- FAR uint32_t *buffer;
-} lfs_free_t;
-
-/* The littlefs type */
-
-typedef struct lfs_s
-{
- FAR const struct lfs_config_s *cfg;
-
- lfs_block_t root[2];
- FAR lfs_file_t *files;
- lfs_dir_t *dirs;
-
- lfs_cache_t rcache;
- lfs_cache_t pcache;
-
- lfs_free_t free;
- bool deorphaned;
- bool moving;
-} lfs_t;
-
-/****************************************************************************
- * Public Data
- ****************************************************************************/
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/****************************************************************************
- * Public Function Prototypes
- ****************************************************************************/
-
-/* Filesystem functions */
-
-/* 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. The config struct must
- * be zeroed for defaults and backwards compatibility.
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_format(FAR lfs_t *lfs, FAR const struct lfs_config_s *config);
-
-/* Mounts a littlefs
- *
- * 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. The config struct must
- * be zeroed for defaults and backwards compatibility.
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_mount(FAR lfs_t *lfs, FAR const struct lfs_config_s *config);
-
-/* Unmounts a littlefs
- *
- * Does nothing besides releasing any allocated resources.
- * Returns a negative error code on failure.
- */
-
-int lfs_unmount(FAR lfs_t *lfs);
-
-/* General operations */
-
-/* Removes a file or directory
- *
- * If removing a directory, the directory must be empty.
- * Returns a negative error code on failure.
- */
-
-int lfs_remove(FAR lfs_t *lfs, FAR const char *path);
-
-/* Rename or move a file or directory
- *
- * If the destination exists, it must match the source in type.
- * If the destination is a directory, the directory must be empty.
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_rename(FAR lfs_t *lfs, FAR const char *oldpath, FAR
- const char *newpath);
-
-/* Find info about a file or directory
- *
- * Fills out the info structure, based on the specified file or directory.
- * Returns a negative error code on failure.
- */
-
-int lfs_stat(FAR lfs_t *lfs, FAR const char *path,
- FAR struct lfs_info_s *info);
-
-/* 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.
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_file_open(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR 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(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR const char *path, int flags,
- FAR const struct lfs_file_config_s *config);
-
-/* Close a file
- *
- * Any pending writes are written out to storage as though
- * sync had been called and releases any allocated resources.
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_file_close(FAR lfs_t *lfs, FAR lfs_file_t *file);
-
-/* Synchronize a file on storage
- *
- * Any pending writes are written out to storage.
- * Returns a negative error code on failure.
- */
-
-int lfs_file_sync(FAR lfs_t *lfs, FAR lfs_file_t *file);
-
-/* Read data from file
- *
- * Takes a buffer and size indicating where to store the read data.
- * Returns the number of bytes read, or a negative error code on failure.
- */
-
-lfs_ssize_t lfs_file_read(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR void *buffer, lfs_size_t size);
-
-/* Write data to file
- *
- * Takes a buffer and size indicating the data to write. The file will not
- * actually be updated on the storage until either sync or close is called.
- *
- * Returns the number of bytes written, or a negative error code on failure.
- */
-
-lfs_ssize_t lfs_file_write(FAR lfs_t *lfs, FAR lfs_file_t *file,
- FAR const void *buffer, lfs_size_t size);
-
-/* Change the position of the file
- *
- * The change in position is determined by the offset and whence flag.
- * Returns the old position of the file, or a negative error code on failure.
- */
-
-lfs_soff_t lfs_file_seek(FAR lfs_t *lfs, FAR lfs_file_t *file,
- lfs_soff_t off, int whence);
-
-/* Truncates the size of the file to the specified size
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_file_truncate(FAR lfs_t *lfs, FAR lfs_file_t *file,
- lfs_off_t size);
-
-/* Return the position of the file
- *
- * Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
- * Returns the position of the file, or a negative error code on failure.
- */
-
-lfs_soff_t lfs_file_tell(FAR lfs_t *lfs, FAR lfs_file_t *file);
-
-/* Change the position of the file to the beginning of the file
- *
- * Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
- * Returns a negative error code on failure.
- */
-
-int lfs_file_rewind(FAR lfs_t *lfs, FAR lfs_file_t *file);
-
-/* Return the size of the file
- *
- * Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
- * Returns the size of the file, or a negative error code on failure.
- */
-
-lfs_soff_t lfs_file_size(FAR lfs_t *lfs, FAR lfs_file_t *file);
-
-/* Directory operations */
-
-/* Create a directory
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_mkdir(FAR lfs_t *lfs, FAR const char *path);
-
-/* Open a directory
- *
- * Once open a directory can be used with read to iterate over files.
- * Returns a negative error code on failure.
- */
-
-int lfs_dir_open(FAR lfs_t *lfs, FAR lfs_dir_t *dir, FAR const char *path);
-
-/* Close a directory
- *
- * Releases any allocated resources.
- * Returns a negative error code on failure.
- */
-
-int lfs_dir_close(FAR lfs_t *lfs, FAR lfs_dir_t *dir);
-
-/* Read an entry in the directory
- *
- * Fills out the info structure, based on the specified file or directory.
- * Returns a negative error code on failure.
- */
-
-int lfs_dir_read(FAR lfs_t *lfs, FAR lfs_dir_t *dir,
- FAR struct lfs_info_s *info);
-
-/* Change the position of the directory
- *
- * The new off must be a value previous returned from tell and specifies
- * an absolute offset in the directory seek.
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_dir_seek(FAR lfs_t *lfs, FAR lfs_dir_t *dir, lfs_off_t off);
-
-/* Return the position of the directory
- *
- * The returned offset is only meant to be consumed by seek and may not make
- * sense, but does indicate the current position in the directory iteration.
- *
- * Returns the position of the directory, or a negative error code on failure
- */
-
-lfs_soff_t lfs_dir_tell(FAR lfs_t *lfs, FAR lfs_dir_t *dir);
-
-/* Change the position of the directory to the beginning of the directory
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_dir_rewind(FAR lfs_t *lfs, FAR lfs_dir_t *dir);
-
-/* Miscellaneous littlefs specific operations */
-
-/* Traverse through all blocks in use by the filesystem
- *
- * The provided callback will be called with each block address that is
- * currently in use by the filesystem. This can be used to determine which
- * blocks are in use or how much of the storage is available.
- *
- * Returns a negative error code on failure.
- */
-
-int lfs_traverse(FAR lfs_t *lfs, CODE int (*cb)(FAR void *, lfs_block_t),
- FAR void *data);
-
-/* Prunes any recoverable errors that may have occurred 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(FAR lfs_t *lfs);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FS_LITTLEFS_LFS_H */
diff --git a/fs/littlefs/lfs_util.c b/fs/littlefs/lfs_util.c
deleted file mode 100644
index b558ff6..0000000
--- a/fs/littlefs/lfs_util.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
- * fs/littlefs/lfs_util.c
- *
- * This file is a part of NuttX:
- *
- * Copyright (C) 2019 Gregory Nutt. All rights reserved.
- *
- * Ported by:
- *
- * Copyright (C) 2019 Pinecone Inc. All rights reserved.
- * Author: lihaichen <li...@163.com>
- *
- * This port derives from ARM mbed logic which has a compatible 3-clause
- * BSD license:
- *
- * Copyright (c) 2017, Arm Limited. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the names ARM, NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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.
- *
- ****************************************************************************/
-
-/****************************************************************************
- * Included Files
- ****************************************************************************/
-
-#include "lfs.h"
-#include "lfs_util.h"
-
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-/* Only compile if user does not provide custom config */
-
-#ifndef LFS_CONFIG
-
-/* Software CRC implementation with small lookup table */
-
-void lfs_crc(FAR uint32_t *crc, FAR const void *buffer, size_t size)
-{
- static const uint32_t rtable[16] =
- {
- 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4,
- 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
- 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
- };
-
- FAR const uint8_t *data = buffer;
- size_t i;
-
- for (i = 0; i < size; i++)
- {
- *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf];
- *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf];
- }
-}
-
-#endif
diff --git a/fs/littlefs/lfs_util.h b/fs/littlefs/lfs_util.h
deleted file mode 100644
index 46c72be..0000000
--- a/fs/littlefs/lfs_util.h
+++ /dev/null
@@ -1,275 +0,0 @@
-/****************************************************************************
- * fs/littlefs/lfs_util.h
- *
- * This file is a part of NuttX:
- *
- * Copyright (C) 2019 Gregory Nutt. All rights reserved.
- *
- * Ported by:
- *
- * Copyright (C) 2019 Pinecone Inc. All rights reserved.
- * Author: lihaichen <li...@163.com>
- *
- * This port derives from ARM mbed logic which has a compatible 3-clause
- * BSD license:
- *
- * Copyright (c) 2017, Arm Limited. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the names ARM, NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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.
- *
- ****************************************************************************/
-
-#ifndef __FS_LITTLEFS_LFS_UTIL_H
-#define __FS_LITTLEFS_LFS_UTIL_H
-
-/****************************************************************************
- * Included Files
- ****************************************************************************/
-
-/* Users can override lfs_util.h with their own configuration by defining
- * 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.
- */
-
-#ifdef LFS_CONFIG
-# define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
-# define LFS_STRINGIZE2(x) #x
-# include LFS_STRINGIZE(LFS_CONFIG)
-#else
-
-/* System includes */
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-
-#ifndef LFS_NO_MALLOC
-# include <nuttx/kmalloc.h>
-#endif
-#ifndef LFS_NO_ASSERT
-# include <assert.h>
-#endif
-#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR)
-# include <debug.h>
-#endif
-
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/* 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
- * code footprint
- */
-
-/* Logging functions */
-
-#ifndef LFS_NO_DEBUG
-# define LFS_DEBUG(fmt, ...) \
- finfo("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
-#else
-# define LFS_DEBUG(fmt, ...)
-#endif
-
-#ifndef LFS_NO_WARN
-# define LFS_WARN(fmt, ...) \
- fwarn("lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
-#else
-# define LFS_WARN(fmt, ...)
-#endif
-
-#ifndef LFS_NO_ERROR
-# define LFS_ERROR(fmt, ...) \
- ferr("lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
-#else
-# define LFS_ERROR(fmt, ...)
-#endif
-
-/* Runtime assertions */
-
-#ifndef LFS_NO_ASSERT
-# define LFS_ASSERT(test) DEBUGASSERT(test)
-#else
-# define LFS_ASSERT(test)
-#endif
-
-/****************************************************************************
- * Public Types
- ****************************************************************************/
-
-/****************************************************************************
- * Public Data
- ****************************************************************************/
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/****************************************************************************
- * Inline Functions
- ****************************************************************************/
-
-/* Builtin functions, these may be replaced by more efficient
- * toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
- * expensive basic C implementation for debugging purposes
- */
-
-/* Min/max functions for unsigned 32-bit numbers */
-
-static inline uint32_t lfs_max(uint32_t a, uint32_t b)
-{
- return (a > b) ? a : b;
-}
-
-static inline uint32_t lfs_min(uint32_t a, uint32_t b)
-{
- return (a < b) ? a : b;
-}
-
-/* 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))
- return 32 - __builtin_clz(a - 1);
-#else
- uint32_t r = 0;
- uint32_t s;
- a -= 1;
- s = (a > 0xffff) << 4;
- a >>= s;
- r |= s;
- s = (a > 0xff) << 3;
- a >>= s;
- r |= s;
- s = (a > 0xf) << 2;
- a >>= s;
- r |= s;
- s = (a > 0x3) << 1;
- a >>= s;
- r |= s;
- return (r | (a >> 1)) + 1;
-#endif
-}
-
-/* Count the number of trailing binary zeros in a
- * lfs_ctz(0) may be undefined
- */
-
-static inline uint32_t lfs_ctz(uint32_t a)
-{
-#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
- return __builtin_ctz(a);
-#else
- return lfs_npw2((a & -a) + 1) - 1;
-#endif
-}
-
-/* Count the number of binary ones in a */
-
-static inline uint32_t lfs_popc(uint32_t a)
-{
-#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
- return __builtin_popcount(a);
-#else
- a = a - ((a >> 1) & 0x55555555);
- a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
- return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
-#endif
-}
-
-/* Find the sequence comparison of a and b, this is the distance
- * between a and b ignoring overflow
- */
-
-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 */
-
-static inline uint32_t lfs_fromle32(uint32_t a)
-{
-#if !defined(CONFIG_ENDIAN_BIG)
- return a;
-#elif !defined(LFS_NO_INTRINSICS)
- return __builtin_bswap32(a);
-#else
- return (((uint8_t *)&a)[0] << 0) | (((uint8_t *)&a)[1] << 8) |
- (((uint8_t *)&a)[2] << 16) | (((uint8_t *)&a)[3] << 24);
-#endif
-}
-
-/* Convert to 32-bit little-endian from native order */
-
-static inline uint32_t lfs_tole32(uint32_t a)
-{
- return lfs_fromle32(a);
-}
-
-/* Allocate memory, only used if buffers are not provided to littlefs */
-
-static inline void *lfs_malloc(size_t size)
-{
-#ifndef LFS_NO_MALLOC
- return kmm_malloc(size);
-#else
- return NULL;
-#endif
-}
-
-/* Deallocate memory, only used if buffers are not provided to littlefs */
-
-static inline void lfs_free(FAR void *p)
-{
-#ifndef LFS_NO_MALLOC
- kmm_free(p);
-#else
- (void)p;
-#endif
-}
-
-/****************************************************************************
- * Public Function Prototypes
- ****************************************************************************/
-
- /* Calculate CRC-32 with polynomial = 0x04c11db7 */
-
-void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif
-#endif /* __FS_LITTLEFS_LFS_UTIL_H */
diff --git a/fs/littlefs/lfs_vfs.c b/fs/littlefs/lfs_vfs.c
index eeea1e2..85ff7b4 100644
--- a/fs/littlefs/lfs_vfs.c
+++ b/fs/littlefs/lfs_vfs.c
@@ -1,46 +1,20 @@
/****************************************************************************
* fs/littlefs/lfs_vfs.c
*
- * This file is a part of NuttX:
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
*
- * Copyright (C) 2019 Gregory Nutt. All rights reserved.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Ported by:
- *
- * Copyright (C) 2019 Pinecone Inc. All rights reserved.
- * Author: lihaichen <li...@163.com>
- *
- * This port derives from ARM mbed logic which has a compatible 3-clause
- * BSD license:
- *
- * Copyright (c) 2017, Arm Limited. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the names ARM, NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
*
****************************************************************************/
@@ -56,14 +30,15 @@
#include <nuttx/fs/dirent.h>
#include <nuttx/fs/fs.h>
+#include <nuttx/kmalloc.h>
#include <nuttx/mtd/mtd.h>
#include <nuttx/semaphore.h>
#include <sys/stat.h>
#include <sys/statfs.h>
-#include "lfs.h"
-#include "lfs_util.h"
+#include "littlefs/lfs.h"
+#include "littlefs/lfs_util.h"
/****************************************************************************
* Private Types
@@ -79,7 +54,7 @@ struct littlefs_mountpt_s
sem_t sem;
FAR struct inode *drv;
struct mtd_geometry_s geo;
- struct lfs_config_s cfg;
+ struct lfs_config cfg;
lfs_t lfs;
};
@@ -249,7 +224,7 @@ static int littlefs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
int ret;
@@ -327,7 +302,7 @@ errsem:
static int littlefs_close(FAR struct file *filep)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
int ret;
@@ -363,7 +338,7 @@ static ssize_t littlefs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
ssize_t ret;
int semret;
@@ -401,7 +376,7 @@ static ssize_t littlefs_write(FAR struct file *filep, const char *buffer,
size_t buflen)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
ssize_t ret;
int semret;
@@ -438,7 +413,7 @@ static ssize_t littlefs_write(FAR struct file *filep, const char *buffer,
static off_t littlefs_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
off_t ret;
int semret;
@@ -505,7 +480,7 @@ static int littlefs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
static int littlefs_sync(FAR struct file *filep)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
int ret;
@@ -551,7 +526,7 @@ static int littlefs_dup(FAR const struct file *oldp, FAR struct file *newp)
static int littlefs_fstat(FAR const struct file *filep, FAR struct stat *buf)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
int ret;
@@ -598,7 +573,7 @@ static int littlefs_fstat(FAR const struct file *filep, FAR struct stat *buf)
static int littlefs_truncate(FAR struct file *filep, off_t length)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_file_s *priv;
+ FAR struct lfs_file *priv;
FAR struct inode *inode;
int ret;
@@ -634,7 +609,7 @@ static int littlefs_opendir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_dir_s *priv;
+ FAR struct lfs_dir *priv;
int ret;
/* Recover our private data from the inode instance */
@@ -689,7 +664,7 @@ static int littlefs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
struct littlefs_mountpt_s *fs;
- FAR struct lfs_dir_s *priv;
+ FAR struct lfs_dir *priv;
int ret;
/* Recover our private data from the inode instance */
@@ -724,8 +699,8 @@ static int littlefs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct littlefs_mountpt_s *fs;
- FAR struct lfs_dir_s *priv;
- struct lfs_info_s info;
+ FAR struct lfs_dir *priv;
+ struct lfs_info info;
int ret;
/* Recover our private data from the inode instance */
@@ -777,7 +752,7 @@ static int littlefs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
struct littlefs_mountpt_s *fs;
- FAR struct lfs_dir_s *priv;
+ FAR struct lfs_dir *priv;
int ret;
/* Recover our private data from the inode instance */
@@ -815,7 +790,7 @@ static int littlefs_rewinddir(FAR struct inode *mountpt,
*
****************************************************************************/
-static int littlefs_read_block(FAR const struct lfs_config_s *c,
+static int littlefs_read_block(FAR const struct lfs_config *c,
lfs_block_t block, lfs_off_t off,
FAR void *buffer, lfs_size_t size)
{
@@ -843,7 +818,7 @@ static int littlefs_read_block(FAR const struct lfs_config_s *c,
* Name: littlefs_write_block
****************************************************************************/
-static int littlefs_write_block(FAR const struct lfs_config_s *c,
+static int littlefs_write_block(FAR const struct lfs_config *c,
lfs_block_t block, lfs_off_t off,
FAR const void *buffer, lfs_size_t size)
{
@@ -871,7 +846,7 @@ static int littlefs_write_block(FAR const struct lfs_config_s *c,
* Name: littlefs_erase_block
****************************************************************************/
-static int littlefs_erase_block(FAR const struct lfs_config_s *c,
+static int littlefs_erase_block(FAR const struct lfs_config *c,
lfs_block_t block)
{
FAR struct littlefs_mountpt_s *fs = c->context;
@@ -894,7 +869,7 @@ static int littlefs_erase_block(FAR const struct lfs_config_s *c,
* Name: littlefs_sync_block
****************************************************************************/
-static int littlefs_sync_block(FAR const struct lfs_config_s *c)
+static int littlefs_sync_block(FAR const struct lfs_config *c)
{
FAR struct littlefs_mountpt_s *fs = c->context;
FAR struct inode *drv = fs->drv;
@@ -988,21 +963,19 @@ static int littlefs_bind(FAR struct inode *driver, FAR const void *data,
/* Initialize lfs_config structure */
- fs->cfg.context = fs;
- fs->cfg.read = littlefs_read_block;
- fs->cfg.prog = littlefs_write_block;
- fs->cfg.erase = littlefs_erase_block;
- fs->cfg.sync = littlefs_sync_block;
- fs->cfg.read_size = fs->geo.blocksize;
- fs->cfg.prog_size = fs->geo.blocksize;
- fs->cfg.block_size = fs->geo.erasesize;
- fs->cfg.block_count = fs->geo.neraseblocks;
- fs->cfg.lookahead = 32 * ((fs->cfg.block_count + 31) / 32);
-
- if (fs->cfg.lookahead > 32 * fs->cfg.read_size)
- {
- fs->cfg.lookahead = 32 * fs->cfg.read_size;
- }
+ fs->cfg.context = fs;
+ fs->cfg.read = littlefs_read_block;
+ fs->cfg.prog = littlefs_write_block;
+ fs->cfg.erase = littlefs_erase_block;
+ fs->cfg.sync = littlefs_sync_block;
+ fs->cfg.read_size = fs->geo.blocksize;
+ fs->cfg.prog_size = fs->geo.blocksize;
+ fs->cfg.block_size = fs->geo.erasesize;
+ fs->cfg.block_count = fs->geo.neraseblocks;
+ fs->cfg.block_cycles = 500;
+ fs->cfg.cache_size = fs->geo.blocksize;
+ fs->cfg.lookahead_size = lfs_min(lfs_alignup(fs->cfg.block_count / 8, 8),
+ fs->cfg.read_size);
/* Then get information about the littlefs filesystem on the devices
* managed by this driver.
@@ -1117,20 +1090,6 @@ static int littlefs_unbind(FAR void *handle, FAR struct inode **driver,
}
/****************************************************************************
- * Name: littlefs_used_block
- ****************************************************************************/
-
-static int littlefs_used_block(void *arg, lfs_block_t block)
-{
- FAR struct statfs *buf = arg;
-
- buf->f_bfree--;
- buf->f_bavail--;
-
- return 0;
-}
-
-/****************************************************************************
* Name: littlefs_statfs
*
* Description: Return filesystem statistics
@@ -1162,7 +1121,15 @@ static int littlefs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
return ret;
}
- ret = lfs_traverse(&fs->lfs, littlefs_used_block, buf);
+ ret = lfs_fs_size(&fs->lfs);
+ if (ret > 0)
+ {
+ buf->f_bfree -= ret;
+ buf->f_bavail -= ret;
+
+ ret = 0;
+ }
+
littlefs_semgive(fs);
return ret;
@@ -1285,7 +1252,7 @@ static int littlefs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct littlefs_mountpt_s *fs;
- struct lfs_info_s info;
+ struct lfs_info info;
int ret;
memset(buf, 0, sizeof(*buf));
diff --git a/tools/Directories.mk b/tools/Directories.mk
index 9f6a437..70a07ab 100644
--- a/tools/Directories.mk
+++ b/tools/Directories.mk
@@ -74,7 +74,7 @@ endif
KERNDEPDIRS += sched drivers boards $(ARCH_SRC)
KERNDEPDIRS += fs binfmt
-CONTEXTDIRS = boards $(APPDIR)
+CONTEXTDIRS = boards fs $(APPDIR)
CLEANDIRS += pass1
ifeq ($(CONFIG_BUILD_FLAT),y)