Addes the ARM mbed littlefs to NuttX

Squashed commit of the following:

Author: Gregory Nutt <gnutt@nuttx.org>
    fs/littlefs:  Fix some compilation errors.
    fs/littlefs:  Brings code a little closer to NuttX coding standard.  Not 100%, but closer.
    fs/littlefs:  Convert all C++ style comments to C comments.

Author: lihaichen <li8303@163.com>

    fs/littlefs:  Adds port of the mbed littlefs.

    depends on !DISABLE_MOUNTPOINT && MTD_BYTE_WRITE

        register_mtddriver("/dev/w25", mtd, 0755, NULL);
        mount("/dev/w25", "/w25", "littlefs", 0, NULL);
This commit is contained in:
lihaichen 2019-01-09 14:26:51 -06:00 committed by Gregory Nutt
parent e74cabf923
commit 87a5e4cd2d
16 changed files with 7235 additions and 1 deletions

View File

@ -88,6 +88,7 @@ source fs/smartfs/Kconfig
source fs/binfs/Kconfig
source fs/procfs/Kconfig
source fs/spiffs/Kconfig
source fs/littlefs/Kconfig
source fs/unionfs/Kconfig
source fs/userfs/Kconfig
source fs/hostfs/Kconfig

View File

@ -77,6 +77,7 @@ include spiffs/Make.defs
include unionfs/Make.defs
include userfs/Make.defs
include hostfs/Make.defs
include littlefs/Make.defs
endif
endif

1226
fs/littlefs/DESIGN.md Normal file

File diff suppressed because it is too large Load Diff

9
fs/littlefs/Kconfig Normal file
View File

@ -0,0 +1,9 @@
config FS_LITTLEFS
bool "LITTLEFS File System"
default n
select FS_READABLE
select FS_WRITABLE
depends on !DISABLE_MOUNTPOINT && MTD_BYTE_WRITE && EXPERIMENTAL
---help---
Build the LITTLEFS file system. https://github.com/ARMmbed/littlefs.

55
fs/littlefs/Make.defs Normal file
View File

@ -0,0 +1,55 @@
#############################################################################
# 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 <li8303@163.com>
#
# This port derives from ARM mbed logic which has a compatible 3-clause
# BSD license:
#
# Copyright (c) 2017, Arm Limited. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name 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
DEPPATH += --dep-path littlefs
VPATH += :littlefs
endif

220
fs/littlefs/README.md Normal file
View File

@ -0,0 +1,220 @@
## usage
depends on !DISABLE_MOUNTPOINT && MTD_BYTE_WRITE
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.

370
fs/littlefs/SPEC.md Normal file
View File

@ -0,0 +1,370 @@
## 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 ............larg
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
```

3367
fs/littlefs/lfs.c Normal file

File diff suppressed because it is too large Load Diff

673
fs/littlefs/lfs.h Normal file
View File

@ -0,0 +1,673 @@
/****************************************************************************
* 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 <li8303@163.com>
*
* This port derives from ARM mbed logic which has a compatible 3-clause
* BSD license:
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#ifndef __FS_LITTLEFS_LFS_H
#define __FS_LITTLEFS_LFS_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdint.h>
#include <stdbool.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Version info */
/* Software library version
* Major (top-nibble), incremented on backwards incompatible changes
* Minor (bottom-nibble), incremented on feature additions
*/
#define LFS_VERSION 0x00010007
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
/* Version of On-disk data structures
* Major (top-nibble), incremented on backwards incompatible changes
* Minor (bottom-nibble), incremented on feature additions
*/
#define LFS_DISK_VERSION 0x00010001
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
/* Max name size in bytes */
#ifndef LFS_NAME_MAX
# define LFS_NAME_MAX 255
#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
{
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
{
LFS_TYPE_REG = 0x11,
LFS_TYPE_DIR = 0x22,
LFS_TYPE_SUPERBLOCK = 0x2e,
};
/* File open flags */
enum lfs_open_flags
{
/* 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 occured during write */
};
/* File seek flags */
enum lfs_whence_flags
{
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
{
/* 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)(const struct lfs_config *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 *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 *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 *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
{
/* 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
{
/* 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
{
lfs_off_t off;
struct lfs_disk_entry
{
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
{
lfs_block_t block;
lfs_off_t off;
FAR uint8_t *buffer;
} lfs_cache_t;
typedef struct lfs_file
{
struct lfs_file *next;
lfs_block_t pair[2];
lfs_off_t poff;
lfs_block_t head;
lfs_size_t size;
FAR const struct lfs_file_config *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
{
struct lfs_dir *next;
lfs_block_t pair[2];
lfs_off_t off;
lfs_block_t head[2];
lfs_off_t pos;
struct lfs_disk_dir
{
uint32_t rev;
lfs_size_t size;
lfs_block_t tail[2];
} d;
} lfs_dir_t;
typedef struct lfs_superblock
{
lfs_off_t off;
struct lfs_disk_superblock
{
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
{
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
{
FAR const struct lfs_config *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 *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 *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 *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 *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 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 *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 occured 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
} /* extern "C" */
#endif
#endif /* __FS_LITTLEFS_LFS_H */

83
fs/littlefs/lfs_util.c Normal file
View File

@ -0,0 +1,83 @@
/****************************************************************************
* 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 <li8303@163.com>
*
* This port derives from ARM mbed logic which has a compatible 3-clause
* BSD license:
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name 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 *restrict 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

283
fs/littlefs/lfs_util.h Normal file
View File

@ -0,0 +1,283 @@
/****************************************************************************
* 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 <li8303@163.com>
*
* This port derives from ARM mbed logic which has a compatible 3-clause
* BSD license:
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#ifndef __FS_LITTLEFS_LFS_UTIL_H
#define __FS_LITTLEFS_LFS_UTIL_H
/****************************************************************************
* Included Files
****************************************************************************/
/* Users can override lfs_util.h with their own configuration by defining
* LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
*
* If LFS_CONFIG is used, none of the default utils will be emitted and must be
* provided by the config file. To start I would suggest copying lfs_util.h and
* modifying as needed.
*/
#ifdef LFS_CONFIG
# define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
# define LFS_STRINGIZE2(x) #x
# include LFS_STRINGIZE(LFS_CONFIG)
#else
/* System includes */
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define LFS_NO_DEBUG
#ifndef LFS_NO_MALLOC
# include <stdlib.h>
#endif
#ifndef LFS_NO_ASSERT
# include <assert.h>
#endif
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR)
# include <stdio.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Macros, may be replaced by system specific wrappers. Arguments to these
* macros must not have side-effects as the macros can be removed for a smaller
* code footprint
*/
/* Logging functions */
#ifndef LFS_NO_DEBUG
# define LFS_DEBUG(fmt, ...) \
printf("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
# define LFS_DEBUG(fmt, ...)
#endif
#ifndef LFS_NO_WARN
# define LFS_WARN(fmt, ...) \
printf("lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
# define LFS_WARN(fmt, ...)
#endif
#ifndef LFS_NO_ERROR
# define LFS_ERROR(fmt, ...) \
printf("lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
# define LFS_ERROR(fmt, ...)
#endif
/* Runtime assertions */
#ifndef LFS_NO_ASSERT
# define LFS_ASSERT(test) assert(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(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
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 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
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 */

906
fs/littlefs/lfs_vfs.c Normal file
View File

@ -0,0 +1,906 @@
/****************************************************************************
* 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 <li8303@163.com>
*
* This port derives from ARM mbed logic which has a compatible 3-clause
* BSD license:
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name 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 <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <nuttx/kmalloc.h>
#include <queue.h>
#include <semaphore.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include "lfs.h"
#include "lfs_util.h"
#include <nuttx/fs/dirent.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mtd/mtd.h>
/****************************************************************************
* Private Types
****************************************************************************/
struct littefs_s
{
struct lfs lfs;
struct lfs_config cfg;
FAR struct mtd_dev_s *mtd;
struct mtd_geometry_s geo;
sem_t fs_sem;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void littlefs_semgive(FAR struct littefs_s *fs);
static void littlefs_semtake(FAR struct littefs_s *fs);
static void littlefs_tostat(FAR struct stat *st,
FAR struct lfs_info *info);
static int littlefs_open(FAR struct file *filep,
FAR const char *relpath, int oflags,
mode_t mode);
static int littlefs_close(FAR struct file *filep);
static ssize_t littlefs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t littlefs_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static off_t littlefs_seek(FAR struct file *filep, off_t offset,
int whence);
static int littlefs_bind(FAR struct inode *mtdinode,
FAR const void *data, FAR void **handle);
static int littlefs_unbind(FAR void *handle,
FAR struct inode **mtdinode, unsigned int flags);
static int littlefs_statfs(FAR struct inode *mountpt,
FAR struct statfs *buf);
static int littlefs_opendir(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct fs_dirent_s *dir);
static int littlefs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int littlefs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int littlefs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int littlefs_fstat(FAR const struct file *filep,
FAR struct stat *buf);
static int littlefs_truncate(FAR struct file *filep, off_t length);
static int littlefs_mkdir(FAR struct inode *mountpt,
FAR const char *relpath, mode_t mode);
static int littlefs_rmdir(FAR struct inode *mountpt,
FAR const char *relpath);
static int littlefs_unlink(FAR struct inode *mountpt,
FAR const char *relpath);
static int littlefs_rename(FAR struct inode *mountpt,
FAR const char *oldrelpath,
FAR const char *newrelpath);
static int littlefs_stat(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct stat *buf);
/****************************************************************************
* Public Data
****************************************************************************/
const struct mountpt_operations littlefs_operations =
{
littlefs_open, /* open */
littlefs_close, /* close */
littlefs_read, /* read */
littlefs_write, /* write */
littlefs_seek, /* seek */
NULL, /* ioctl */
NULL, /* sync */
NULL, /* dup */
littlefs_fstat, /* fstat */
littlefs_truncate, /* truncate */
littlefs_opendir, /* opendir */
littlefs_closedir, /* closedir */
littlefs_readdir, /* readdir */
littlefs_rewinddir, /* rewinddir */
littlefs_bind, /* bind */
littlefs_unbind, /* unbind */
littlefs_statfs, /* statfs */
littlefs_unlink, /* unlink */
littlefs_mkdir, /* mkdir */
littlefs_rmdir, /* rmdir */
littlefs_rename, /* rename */
littlefs_stat, /* stat */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
static void littlefs_semtake(FAR struct littefs_s *fs)
{
int ret;
do
{
ret = nxsem_wait(&fs->fs_sem);
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
}
static void littlefs_semgive(FAR struct littefs_s *fs)
{
nxsem_post(&fs->fs_sem);
}
static int vfs_flag_to_lfs(int flags)
{
int res = 0;
if ((flags & 3) == O_RDONLY)
{
res |= LFS_O_RDONLY;
}
if ((flags & 3) == O_WRONLY)
{
res |= LFS_O_WRONLY;
}
if ((flags & 3) == O_RDWR)
{
res |= LFS_O_RDWR;
}
if (flags & O_CREAT)
{
res |= LFS_O_CREAT;
}
if (flags & O_EXCL)
{
res |= LFS_O_EXCL;
}
if (flags & O_TRUNC)
{
res |= LFS_O_TRUNC;
}
if (flags & O_APPEND)
{
res |= LFS_O_APPEND;
}
return res;
}
/* Conversion error code */
static int lfs_result_to_vfs(int result)
{
int status = 0;
switch (result)
{
case LFS_ERR_OK:
status = OK;
break;
case LFS_ERR_IO:
status = -EIO;
break; /* Error during device operation */
case LFS_ERR_NOENT:
status = -ENOENT;
break; /* No directory entry */
case LFS_ERR_EXIST:
status = -EEXIST;
break; /* Entry already exists */
case LFS_ERR_NOTDIR:
status = -ENOTDIR;
break; /* Entry is not a dir */
case LFS_ERR_ISDIR:
status = -EISDIR;
break; /* Entry is a dir */
case LFS_ERR_NOTEMPTY:
status = -ENOTEMPTY;
break; /* Dir is not empty */
case LFS_ERR_BADF:
status = -EBADF;
break; /* Bad file number */
case LFS_ERR_INVAL:
status = -EINVAL;
break; /* Invalid parameter */
case LFS_ERR_NOSPC:
status = -ENOSPC;
break; /* No space left on device */
case LFS_ERR_NOMEM:
status = -ENOMEM;
break; /* No more memory available */
case LFS_ERR_CORRUPT:
status = LFS_ERR_CORRUPT;
break; /* Corrupted */
default:
status = -EIO;
break;
}
return status;
}
static int _lfs_flash_read(FAR const struct lfs_config *cfg,
lfs_block_t block, lfs_off_t off,
FAR void *buffer, lfs_size_t size)
{
FAR struct mtd_dev_s *mtd;
DEBUGASSERT(cfg != NULL);
DEBUGASSERT(cfg->context != NULL);
mtd = (FAR struct mtd_dev_s *)cfg->context;
if (mtd->read(mtd, block * cfg->block_size + off, size, buffer) != size)
{
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
static int _lfs_flash_prog(FAR const struct lfs_config *cfg,
lfs_block_t block, lfs_off_t off,
FAR const void *buffer, lfs_size_t size)
{
FAR struct mtd_dev_s *mtd;
DEBUGASSERT(cfg != NULL);
DEBUGASSERT(cfg->context != NULL);
mtd = (FAR struct mtd_dev_s *)cfg->context;
if (mtd->write(mtd, block * cfg->block_size + off, size, buffer) != size)
{
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
static int _lfs_flash_erase(FAR const struct lfs_config *cfg,
lfs_block_t block)
{
FAR struct mtd_dev_s *mtd;
DEBUGASSERT(cfg != NULL);
DEBUGASSERT(cfg->context != NULL);
mtd = (struct mtd_dev_s *)cfg->context;
if (mtd->erase(mtd, block, 1) != 1)
{
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
static int _lfs_flash_sync(FAR const struct lfs_config *cfg)
{
return LFS_ERR_OK;
}
static int littlefs_bind(FAR struct inode *mtdinode,
FAR const void *data, FAR void **handle)
{
FAR struct mtd_dev_s *mtd;
FAR struct littefs_s *fs;
int ret;
finfo("mtdinode=%p data=%p handle=%p\n", mtdinode, data, handle);
DEBUGASSERT(mtdinode != NULL && handle != NULL);
/* Extract the MTD interface reference */
DEBUGASSERT(INODE_IS_MTD(mtdinode) && mtdinode->u.i_mtd != NULL);
mtd = mtdinode->u.i_mtd;
fs = (FAR struct littefs_s *)kmm_zalloc(sizeof(struct littefs_s));
if (fs == NULL)
{
ferr("ERROR: Failed to allocate volume structure\n");
return -ENOMEM;
}
(void)nxsem_init(&fs->fs_sem, 0, 0);
fs->mtd = mtd;
ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY,
(unsigned long)((uintptr_t)&fs->geo));
if (ret < 0)
{
ret = -ENODEV;
ferr("ERROR: MTD_IOCTL(MTDIOC_GEOMETRY) failed: %d\n", ret);
goto errout_with_volume;
}
fs->cfg.context = mtd;
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 > 512)
{
fs->cfg.lookahead = 512;
}
fs->cfg.read = _lfs_flash_read;
fs->cfg.prog = _lfs_flash_prog;
fs->cfg.erase = _lfs_flash_erase;
fs->cfg.sync = _lfs_flash_sync;
ret = lfs_mount(&fs->lfs, &fs->cfg);
if (ret != LFS_ERR_OK)
{
finfo("mount error\n");
ret = lfs_format(&fs->lfs, &fs->cfg);
if (ret != LFS_ERR_OK)
{
ret = -EINVAL;
ferr("ERROR: Invalid lfs format failed: %d\n", ret);
goto errout_with_volume;
}
ret = lfs_mount(&fs->lfs, &fs->cfg);
if (ret != LFS_ERR_OK)
{
ret = -EINVAL;
ferr("ERROR: Invalid lfs mount failed: %d\n", ret);
goto errout_with_volume;
}
}
*handle = (FAR void *)fs;
littlefs_semgive(fs);
finfo("mount ok\n");
return OK;
errout_with_volume:
nxsem_destroy(&fs->fs_sem);
kmm_free(fs);
return ret;
}
static int littlefs_unbind(FAR void *handle, FAR struct inode **mtdinode,
unsigned int flags)
{
FAR struct littefs_s *fs = (FAR struct littefs_s *)handle;
int ret = OK;
if (!fs)
{
return -EINVAL;
}
littlefs_semtake(fs);
ret = lfs_unmount(&fs->lfs);
nxsem_destroy(&fs->fs_sem);
kmm_free(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct fs_dirent_s *dir)
{
FAR struct littefs_s *fs = mountpt->i_private;
int ret = OK;
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_dir_open(&fs->lfs, (lfs_dir_t *)&dir->u.littlefs, relpath);
littlefs_semgive(fs);
finfo("open dir\n");
return lfs_result_to_vfs(ret);
}
static int littlefs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct littefs_s *fs = mountpt->i_private;
int ret = OK;
finfo("close dir\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_dir_close(&fs->lfs, (lfs_dir_t *)&dir->u.littlefs);
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct littefs_s *fs = mountpt->i_private;
struct lfs_info info;
int ret = OK;
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_dir_read(&fs->lfs, (lfs_dir_t *)&dir->u.littlefs, &info);
if (ret == 1)
{
memset(dir->fd_dir.d_name, 0, NAME_MAX);
strncpy(dir->fd_dir.d_name, info.name, NAME_MAX);
dir->fd_dir.d_type = 0;
switch (info.type)
{
case LFS_TYPE_DIR:
dir->fd_dir.d_type |= DTYPE_DIRECTORY;
break;
case LFS_TYPE_REG:
dir->fd_dir.d_type |= DTYPE_FILE;
break;
}
}
else
{
goto breakout;
}
littlefs_semgive(fs);
return OK;
breakout:
ret = -ENOENT;
littlefs_semgive(fs);
return ret;
}
static int littlefs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct littefs_s *fs = mountpt->i_private;
int ret = OK;
finfo("littlefs_rewinddir\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_dir_rewind(&fs->lfs, (lfs_dir_t *)&dir->u.littlefs);
if (ret != LFS_ERR_OK)
{
goto breakout;
}
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
breakout:
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_mkdir(FAR struct inode *mountpt,
FAR const char *relpath, mode_t mode)
{
FAR struct littefs_s *fs = mountpt->i_private;
int ret = OK;
finfo("littlefs_mkdir\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_mkdir(&fs->lfs, relpath);
if (ret != LFS_ERR_OK)
{
goto breakout;
}
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
breakout:
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_rmdir(FAR struct inode *mountpt,
FAR const char *relpath)
{
FAR struct littefs_s *fs = mountpt->i_private;
int ret = OK;
finfo("littlefs_rmdir\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_remove(&fs->lfs, relpath);
if (ret != LFS_ERR_OK)
{
goto breakout;
}
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
breakout:
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
FAR struct inode *inode;
FAR struct littefs_s *fs;
FAR lfs_file_t *lf = NULL;
int ret = OK;
finfo("littlefs_open [%s]\n", relpath);
DEBUGASSERT(filep != NULL && filep->f_inode != NULL &&
filep->f_inode->i_private);
inode = filep->f_inode;
fs = inode->i_private;
littlefs_semtake(fs);
lf = (lfs_file_t *)kmm_zalloc(sizeof(lfs_file_t));
if (!lf)
{
ret = LFS_ERR_NOMEM;
goto breakout;
}
ret = lfs_file_open(&fs->lfs, lf, relpath, vfs_flag_to_lfs(oflags));
if (ret != LFS_ERR_OK)
{
ret = lfs_result_to_vfs(ret);
goto breakout;
}
filep->f_priv = lf;
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
breakout:
if (lf != NULL)
{
kmm_free(lf);
}
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct littefs_s *fs;
FAR lfs_file_t *lf = NULL;
int ret = OK;
finfo("littlefs_close\n");
DEBUGASSERT(filep != NULL && filep->f_inode != NULL &&
filep->f_inode->i_private);
inode = filep->f_inode;
fs = inode->i_private;
lf = filep->f_priv;
littlefs_semtake(fs);
ret = lfs_file_close(&fs->lfs, lf);
kmm_free(lf);
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static ssize_t littlefs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode;
FAR struct littefs_s *fs;
FAR lfs_file_t *lf = NULL;
ssize_t ret = 0;
finfo("littlefs_read\n");
DEBUGASSERT(filep != NULL && filep->f_inode != NULL &&
filep->f_inode->i_private);
inode = filep->f_inode;
fs = inode->i_private;
lf = filep->f_priv;
littlefs_semtake(fs);
ret = lfs_file_read(&fs->lfs, lf, buffer, buflen);
littlefs_semgive(fs);
if (ret < 0)
{
ret = lfs_result_to_vfs(ret);
}
return ret;
}
static ssize_t littlefs_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct inode *inode;
FAR struct littefs_s *fs;
FAR lfs_file_t *lf = NULL;
ssize_t ret = 0;
finfo("littlefs_write %d\n", buflen);
DEBUGASSERT(filep != NULL && filep->f_inode != NULL &&
filep->f_inode->i_private);
inode = filep->f_inode;
fs = inode->i_private;
lf = filep->f_priv;
littlefs_semtake(fs);
ret = lfs_file_write(&fs->lfs, lf, buffer, buflen);
littlefs_semgive(fs);
if (ret < 0)
{
ret = lfs_result_to_vfs(ret);
}
return ret;
}
static off_t littlefs_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct inode *inode;
FAR struct littefs_s *fs;
FAR lfs_file_t *lf = NULL;
off_t ret = 0;
finfo("littlefs_seek\n");
DEBUGASSERT(filep != NULL && filep->f_inode != NULL &&
filep->f_inode->i_private);
inode = filep->f_inode;
fs = inode->i_private;
lf = filep->f_priv;
littlefs_semtake(fs);
ret = lfs_file_seek(&fs->lfs, lf, offset, whence);
littlefs_semgive(fs);
if (ret < 0)
{
ret = lfs_result_to_vfs(ret);
}
return ret;
}
static int littlefs_fstat(FAR const struct file *filep, FAR struct stat *buf)
{
finfo("littlefs_fstat\n");
return OK;
}
static int littlefs_truncate(FAR struct file *filep, off_t length)
{
FAR struct inode *inode;
FAR struct littefs_s *fs;
FAR lfs_file_t *lf = NULL;
int ret = 0;
finfo("littlefs_truncate\n");
DEBUGASSERT(filep != NULL && filep->f_inode != NULL &&
filep->f_inode->i_private);
inode = filep->f_inode;
fs = inode->i_private;
lf = filep->f_priv;
littlefs_semtake(fs);
ret = lfs_file_truncate(&fs->lfs, lf, length);
littlefs_semgive(fs);
if (ret < 0)
{
ret = lfs_result_to_vfs(ret);
}
return ret;
}
static int littlefs_statfs_count(FAR void *p, lfs_block_t b)
{
*(FAR lfs_size_t *)p += 1;
return 0;
}
static int littlefs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
{
FAR struct littefs_s *fs = mountpt->i_private;
lfs_size_t in_use = 0;
int ret = OK;
finfo("littlefs_statfs\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_traverse(&fs->lfs, littlefs_statfs_count, &in_use);
if (ret != LFS_ERR_OK)
{
goto breakout;
}
memset(buf, 0, sizeof(struct statfs));
buf->f_type = LITTLEFS_SUPER_MAGIC;
buf->f_bsize = fs->cfg.block_size;
buf->f_blocks = fs->cfg.block_count;
buf->f_bfree = fs->cfg.block_count - in_use;
buf->f_bavail = buf->f_bfree;
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
breakout:
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
{
FAR struct littefs_s *fs = mountpt->i_private;
int ret = OK;
finfo("littlefs_unlink\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_remove(&fs->lfs, relpath);
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_rename(FAR struct inode *mountpt,
FAR const char *oldrelpath,
FAR const char *newrelpath)
{
FAR struct littefs_s *fs = mountpt->i_private;
int ret = OK;
finfo("littlefs_rename\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
littlefs_semtake(fs);
ret = lfs_rename(&fs->lfs, oldrelpath, newrelpath);
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static int littlefs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct littefs_s *fs = mountpt->i_private;
struct lfs_info info;
int ret = OK;
finfo("littlefs_stat\n");
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
memset(buf, 0, sizeof(struct stat));
littlefs_semtake(fs);
ret = lfs_stat(&fs->lfs, relpath, &info);
if (ret != LFS_ERR_OK)
{
goto breakout;
}
littlefs_tostat(buf, &info);
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
breakout:
littlefs_semgive(fs);
return lfs_result_to_vfs(ret);
}
static void littlefs_tostat(struct stat *st, struct lfs_info *info)
{
memset(st, 0, sizeof(struct stat));
st->st_size = info->size;
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
switch (info->type)
{
case LFS_TYPE_DIR:
st->st_mode |= S_IFDIR;
break;
case LFS_TYPE_REG:
st->st_mode |= S_IFREG;
break;
}
}

View File

@ -114,6 +114,12 @@ FAR const char *fs_gettype(FAR struct statfs *statbuf)
break;
#endif
#ifdef CONFIG_FS_LITTLEFS
case LITTLEFS_SUPER_MAGIC:
fstype = "littlefs";
break;
#endif
#ifdef CONFIG_NFS
case NFS_SUPER_MAGIC:
fstype = "nfs";

View File

@ -78,7 +78,7 @@
/* These file systems require MTD drivers */
#ifdef CONFIG_FS_SPIFFS
#if defined(CONFIG_FS_SPIFFS) || defined(CONFIG_FS_LITTLEFS)
# define MDFS_SUPPORT 1
#endif
@ -140,11 +140,18 @@ static const struct fsmap_t g_bdfsmap[] =
extern const struct mountpt_operations spiffs_operations;
#endif
#ifdef CONFIG_FS_LITTLEFS
extern const struct mountpt_operations littlefs_operations;
#endif
static const struct fsmap_t g_mdfsmap[] =
{
#ifdef CONFIG_FS_SPIFFS
{ "spiffs", &spiffs_operations },
#endif
#ifdef CONFIG_FS_LITTLEFS
{ "littlefs", &littlefs_operations },
#endif
};
#endif /* MDFS_SUPPORT */

View File

@ -222,6 +222,29 @@ struct fs_hostfsdir_s
#endif /* CONFIG_DISABLE_MOUNTPOINT */
#ifdef CONFIG_FS_LITTLEFS
typedef uint32_t _lfs_block_t;
typedef uint32_t _lfs_size_t;
typedef uint32_t _lfs_off_t;
typedef struct _lfs_dir
{
struct _lfs_dir *next;
_lfs_block_t pair[2];
_lfs_off_t off;
_lfs_block_t head[2];
_lfs_off_t pos;
struct _lfs_disk_dir
{
uint32_t rev;
_lfs_size_t size;
_lfs_block_t tail[2];
} d;
} _lfs_dir_t;
#endif
struct fs_dirent_s
{
/* This is the node that was opened by opendir. The type of the inode
@ -289,6 +312,9 @@ struct fs_dirent_s
#ifdef CONFIG_FS_SPIFFS
struct fs_spiffsdir_s spiffs;
#endif
#ifdef CONFIG_FS_LITTLEFS
_lfs_dir_t littlefs;
#endif
#ifdef CONFIG_FS_UNIONFS
struct fs_unionfsdir_s unionfs;
#endif

View File

@ -96,6 +96,7 @@
#define XFS_SUPER_MAGIC 0x58465342
#define _XIAFS_SUPER_MAGIC 0x012fd16d
#define SPIFFS_SUPER_MAGIC 0x20090315
#define LITTLEFS_SUPER_MAGIC 0x0a732923
/* NuttX specific file-systems */