From fe0ba38580aad3abbe52dd698bd14f1f7606b308 Mon Sep 17 00:00:00 2001 From: liuhaitao Date: Tue, 21 Apr 2020 15:22:31 +0800 Subject: [PATCH] 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 --- 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(-) create mode 100644 fs/littlefs/.gitignore delete mode 100644 fs/littlefs/DESIGN.md delete mode 100644 fs/littlefs/README.md delete mode 100644 fs/littlefs/SPEC.md delete mode 100644 fs/littlefs/lfs.c delete mode 100644 fs/littlefs/lfs.h delete mode 100644 fs/littlefs/lfs_util.c delete mode 100644 fs/littlefs/lfs_util.h diff --git a/fs/Makefile b/fs/Makefile index 2218aace6c..6989a74573 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 0000000000..90a5e6edb7 --- /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 125a1b036a..0000000000 --- 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 d02e529022..fb21f0629f 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 -# -# 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 32d3f6e639..0000000000 --- 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 202f50cf27..0000000000 --- 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 77d06afc8b..0000000000 --- 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 - * - * 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 - -#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 a597593d22..0000000000 --- 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 - * - * 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 -#include - -/**************************************************************************** - * 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 b558ff66aa..0000000000 --- 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 - * - * 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 46c72bec61..0000000000 --- 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 - * - * 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 -#include -#include - -#ifndef LFS_NO_MALLOC -# include -#endif -#ifndef LFS_NO_ASSERT -# include -#endif -#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) -# include -#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 eeea1e2be5..85ff7b47ff 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 - * - * 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 #include +#include #include #include #include #include -#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. @@ -1116,20 +1089,6 @@ static int littlefs_unbind(FAR void *handle, FAR struct inode **driver, return ret; } -/**************************************************************************** - * 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 * @@ -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 9f6a437144..70a07ab70f 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)