Giter VIP home page Giter VIP logo

littlefs's Introduction

littlefs

A little fail-safe filesystem designed for microcontrollers.

   | | |     .---._____
  .-----.   |          |
--|o    |---| littlefs |
--|     |---|          |
  '-----'   '----------'
   | | |

Power-loss resilience - littlefs is designed to handle random power failures. All file operations have strong copy-on-write guarantees and if power is lost the filesystem will fall back to the last known good state.

Dynamic wear leveling - littlefs is designed with flash in mind, and provides wear leveling over dynamic blocks. Additionally, littlefs can detect bad blocks and work around them.

Bounded RAM/ROM - littlefs is designed to work with a small amount of memory. RAM usage is strictly bounded, which means RAM consumption does not change as the filesystem grows. The filesystem contains no unbounded recursion and dynamic memory is limited to configurable buffers that can be provided statically.

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:

#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,
    .cache_size = 16,
    .lookahead_size = 16,
    .block_cycles = 500,
};

// 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.

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, file updates are not actually committed to the filesystem until sync or close is called on the file.

Other notes

Littlefs is written in C, and specifically should compile with any compiler that conforms to the C99 standard.

All littlefs calls 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, 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.

Design

At a high level, littlefs is a block based filesystem that uses small logs to store metadata and larger copy-on-write (COW) structures to store file data.

In littlefs, these ingredients form a sort of two-layered cake, with the small logs (called metadata pairs) providing fast updates to metadata anywhere on storage, while the COW structures store file data compactly and without any wear amplification cost.

Both of these data structures are built out of blocks, which are fed by a common block allocator. By limiting the number of erases allowed on a block per allocation, the allocator provides dynamic wear leveling over the entire filesystem.

                    root
                   .--------.--------.
                   | A'| B'|         |
                   |   |   |->       |
                   |   |   |         |
                   '--------'--------'
                .----'   '--------------.
       A       v                 B       v
      .--------.--------.       .--------.--------.
      | C'| D'|         |       | E'|new|         |
      |   |   |->       |       |   | E'|->       |
      |   |   |         |       |   |   |         |
      '--------'--------'       '--------'--------'
      .-'   '--.                  |   '------------------.
     v          v              .-'                        v
.--------.  .--------.        v                       .--------.
|   C    |  |   D    |   .--------.       write       | new E  |
|        |  |        |   |   E    |        ==>        |        |
|        |  |        |   |        |                   |        |
'--------'  '--------'   |        |                   '--------'
                         '--------'                   .-'    |
                         .-'    '-.    .-------------|------'
                        v          v  v              v
                   .--------.  .--------.       .--------.
                   |   F    |  |   G    |       | new F  |
                   |        |  |        |       |        |
                   |        |  |        |       |        |
                   '--------'  '--------'       '--------'

More details on how littlefs works can be found in DESIGN.md and SPEC.md.

  • DESIGN.md - A fully detailed dive into how littlefs works. I would suggest reading it as the tradeoffs at work are quite interesting.

  • SPEC.md - The on-disk specification of littlefs with all the nitty-gritty details. May be useful for tooling development.

Testing

The littlefs comes with a test suite designed to run on a PC using the emulated block device found in the bd directory. The tests assume a Linux environment and can be started with make:

make test

License

The littlefs is provided under the BSD-3-Clause license. See 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

  • littlefs-fuse - A FUSE 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 - 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.

  • littlefs-python - A Python wrapper for littlefs. The project allows you to create images of the filesystem on your PC. Check if littlefs will fit your needs, create images for a later download to the target memory or inspect the content of a binary image of the target memory.

  • littlefs2-rust - A Rust wrapper for littlefs. This project allows you to use littlefs in a Rust-friendly API, reaping the benefits of Rust's memory safety and other guarantees.

  • nim-littlefs - A Nim wrapper and API for littlefs. Includes a fuse implementation based on littlefs-fuse

  • chamelon - A pure-OCaml implementation of (most of) littlefs, designed for use with the MirageOS library operating system project. It is interoperable with the reference implementation, with some caveats.

  • littlefs-disk-img-viewer - A memory-efficient web application for viewing littlefs disk images in your web browser.

  • mklfs - A command line tool for creating littlefs images. Used in the Lua RTOS ecosystem.

  • mklittlefs - A command line tool for creating littlefs images. Used in the ESP8266 and RP2040 ecosystem.

  • pico-littlefs-usb - An interface for littlefs that emulates a FAT12 filesystem over USB. Allows mounting littlefs on a host PC without additional drivers.

  • Mbed OS - The easiest way to get started with littlefs is to jump into Mbed which already has block device drivers for most forms of embedded storage. littlefs is available in Mbed OS as the LittleFileSystem class.

  • 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 - 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.

  • ChaN's FatFs - A lightweight reimplementation of the infamous FAT filesystem for microcontroller-scale devices. Due to limitations of FAT it can't provide power-loss resilience, but it does allow easy interop with PCs.

littlefs's People

Contributors

aldot avatar ar2rl avatar brianpugh avatar colin-foster-in-advantage avatar dpgeorge avatar embeddedt avatar freddiechopin avatar geky avatar haneefmubarak avatar johnlunney avatar jrast avatar lmapii avatar maximevince avatar mon avatar noahgorny avatar ondrapcze avatar pabigot avatar renesas-billgesner avatar rojer avatar roykuper13 avatar runderwo avatar sgupta20 avatar sipkev avatar sosthene-nitrokey avatar tannewt avatar tniessen avatar tomscii avatar xenoamor avatar yamt avatar zchen24 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

littlefs's Issues

Rewrite of file header slows down as file size increases

I'm curious if my understanding of how littlefs works when "overwriting" existing data at the start of a file. For example rewriting a header at the start of a largish file, e.g. something like the following

 elcofs_lseek(testFd, 0, SEEK_SET);
 elcofs_write(testFd, headerData, sizeof(headerData));

As currently what seems to occur is that if you rewrite the beginning of a file the entire file gets rewritten, specifically in lfs_file_flush:

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) {
                orig.cache.block = 0xffffffff;
                lfs->rcache.block = 0xffffffff;
            }
        }

Is that expected behavior due to need to rebuild the reverse linked list of blocks? I just want to be sure this is expected before I rework my code to put the header data in separate file.

V1.3 release conflict?

Hi geky
I've downloaded the V1.3 at end of March and now there is a new version 1.3 including the bugfix of the lookahead.
By the way if you look at the releases page the V1.3 is dated February the 20th.
Is there a multiple V1.3? Does it make sense to generate a V1.4?
Thanks in advance.

lfs_dir_open() does not handle "." and ".." at end of a path

Hello,

I noticed that lfs_dir_open() does not handle paths where the last "location" is given as "." or "..".
E.g.: "/test/mydirectory/.." or "/test/mydirectory/." will open directory "/" (root), but "/test/mydirectory/../mydirectory" opens "mydirectory" correctly.

Could you check this please ?

Thanks,
Geza

Is LFS_NAME_MAX 255 bytes a little larger?

Most files or directories do not have a so long name. If user define 'struct lfs_info' on the stack, it may be unsafe. I have reduced LFS_NAME_MAX to 32 and it works. But I don't want to modify โ€˜lfs.hโ€™. Should this macro appear in โ€˜lfs_util.hโ€™ ?

Available space

Hi
first of all thank you very much for having developed littlefs.

I have a question concerning the available space on the storage: how to calculate it?
Thank you in advance.

file truncation behaviour (LFS_O_TRUNC)

Hi and thanks for the great work!
I believe that LFS_O_TRUNC should work differently: if a file gets opened with LFS_O_TRUNC set and closed without writing anything, no truncation will occur. Is this the intended behaviour?

I've fixed it, quick and dirty, by forcing LFS_F_WRITING on open:

if (flags & LFS_O_TRUNC) {
    file->head = 0xffffffff;
    file->size = 0;
    file->flags |= LFS_F_WRITING;
}

Thank you,
Alberto

Problems with the size of the file to read

Hello everybody! I have a problem with the file system with the following settings:

.read_size = 256,
.prog_size = 256,
.block_size = 4096,
.block_count = 7168, // - 1024 for other needs
.lookahead = 1024

With these settings, the file system can not normally read the recorded files larger than 2 blocks. After analyzing the code, I found a problematic place in the file lfs.c:

https://github.com/geky/littlefs/blob/c5e2b335d618a78656a867c7874835db141fbad1/lfs.c#L1122

After this line of code, I have the wrong address of the block header. Further, I did not go into depth, I need more knowledge in the file system device. Please help. =)

C89: invalid use of incomplete type โ€˜struct lfs_disk_dirโ€™ and other instances.

I am attempting to utilize littlefs in my project, but it seems there are times when incomplete struct types are being used. The problem is they're being declared in a nested struct definition and apparently my flavor or version of embedded GCC tool chain isn't too happy about it. The fix is fairly simple for example, to fix the mentioned issue above we can simply change the following,

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;

to

typedef struct lfs_disk_dir {
    uint32_t rev;
    lfs_size_t size;
    lfs_block_t tail[2];
} lfs_disk_dir_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;
    
    lfs_disk_dir_t d;
} lfs_dir_t;

And all is well. I am going to be doing this myself for my project, but would prefer to see it also applied upstream as well. There are several other places that this occurs and I can't see why declaring the structs this way would be an issue, though I could be missing something here. Let me know what you think.

Purpose of lfs_config.sync

What's the purpose of the lfs_config.sync callback function?

Looking through the emubd example does not seem to do anything with the data?

Support truncate function

If possible support for truncate would be useful, e.g.

int truncate(const char *path, off_t length);

or using the file handle like ftruncate

Change in license

Interest in littlefs is growing and weโ€™d like to enable users to combine it with other open source projects, regardless of license. Unfortunately, due to the Free Software Foundation considering Apache-2.0 to be incompatible with GPL-2.0, this creates a limitation for some open source projects.

In order to continue the growth of littlefs and enable friction-less combination with other open source projects, we are seeking to change the license for littlefs from Apache-2.0 to BSD-3-Clause. We have emailed all contributors to request permission to change the license. If you have been a contributor and received an email, please help us continue the development of littlefs by responding! Future contributions will be based on inbound = outbound BSD-3-Clause and pending contributions will need to be rebased on master after the license change has taken place to enact the appropriate inboud=outbound license.

Thanks for all the support and interest in littlefs ๐Ÿ‘

Support big-endian host

Hi,

I'm currently running littlefs on a simulated PowerPC e200 VLE that is a big-endian processor.
I suppose that natural endian of littlefs file system is little-endian since most today ARM and x86 processors are little-endian.
Can you please include support for big-endian host machines?
Please find attached a patch for lfs.c (against commit commit d88f0ac) to address the problem of endian.

Regards,
Gilles Mouchard

lfs_c.patch.gz

C89: Code is written for C99

Hi,

I'm just back porting littlefs into one of our products and have hit a problem at the first hurdle, littlefs requires C99.

for example, variables are declared mid block or at the start of loops:

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);
static int lfs_cache_crc(lfs_t *lfs, lfs_cache_t *rcache,
        const lfs_cache_t *pcache, lfs_block_t block,
        lfs_off_t off, lfs_size_t size, uint32_t *crc) {
    for (lfs_off_t i = 0; i < size; i++) {

should really be:

static int lfs_cache_crc(lfs_t *lfs, lfs_cache_t *rcache,
        const lfs_cache_t *pcache, lfs_block_t block,
        lfs_off_t off, lfs_size_t size, uint32_t *crc) {
    lfs_off_t i = 0;
    for (i = 0; i < size; i++) {

It's obviously easy enough to fix and doesn't do any harm, just makes it more compatible with more compilers.

Also technically comments should be in /** **/ and not // for the same reason (although I've yet to come across a compiler that doesn't like //

Supporting fstat (statting an already open file)

It is a fairly common pattern to open a file, then stat it to determine what the size of the file that i just opened.
How hard would it be to implement?
I see that size is readily available in the file_t, not sure how hard would it be to get the current name of the file.

Implementation guide, or other examples? Particular for embedded serial flash.

Are there any guidelines for how to map the LFS block/read/program sizes onto a serial flash that has well-defined underlying geometry? e.g. align blocks to the sector size (minimum erase quanta), so the erase command in the drive is very simple to implement. However, sectors are 4K, which is not resource-light to do a read-modify-rewrite.
And then the what the effective implication of the choice read size and prog size are?

I actually already have it working on a large serial NOR flash, but due to the size and the high-level SDK shims, I am not entirely sure that everything is working as expected, even if files appear to read/write/list/etc correctly.

As I raised in another repo issue, the test battery doesn't map to an embedded context (not yet, I'll work on it), so I can't be sure that all the cases for bad blocks, exhaustion, and other edges cases are really correct on the actual serial flash block-device-like implementation.

make test race + warning

I ran in to 2 issues when running make test.

The first issue is that the test.c file is generated, compiled and run so fast that the file timestamp doesn't change between the first and second test. So make doesn't rebuild and the second test is run with the first executable, and I get this:

./tests/test_format.sh
=== Formatting tests ===
--- Basic formatting ---
lfs_format: 0
--- Invalid superblocks ---
lfs debug: Bad block at 1
lfs warn: Superblock 1 has become unwritable
lfs debug: Bad block at 0
lfs warn: Superblock 0 has become unwritable
lfs_format: -52
test.c:103: assert lfs_format failed with -52, expected 0

The second issue is that my compiler gives warnings on statements like res = res;, these are turned in to errors by -Werror and some tests fail with:

--- Exhaustion test ---
test.c:120:13: error: explicitly assigning value of variable of type 'lfs_ssize_t' (aka 'int') to itself [-Werror,-Wself-assign]
        res = res;
        ~~~ ^ ~~~

After working around those issues all tests pass! :)

C++ include

Can we have a:

#ifdef __cplusplus
extern "C" {
#endif

.....

#ifdef __cplusplus
}
#endif

in the include file so it can be cleanly included by a cpp file?

How to build LittleFileSystem on Keil

When building littlefilesystem with Keil I get following error:
Error: L6218E: Undefined symbol __builtin_ctz (referred from lfs.o).

How to build littlefs on Keil?

Wear leveling vs awareness

This statement in the current README:

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.

is in direct contradiction with the statement in this paragraph of the design document:

...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.

i think readme should be corrected, stating that LFS is wear aware, and linking to the (very clear) explanation in the design doc.

assertion "block < lfs->cfg->block_count" failed

I'm running LittleFS on a 16mbyte SPI flash on ESP32 and it's been working well, but last night I ran into the above assert. Here's the stack trace:

assertion "block < lfs->cfg->block_count" failed: file "/Users/jason/Projects/x/x-esp32-firmware/components/littlefs/littlefs/lfs.c", line 27, function: lfs_cache_read
abort() was called at PC 0x400e801b on core 0
0x400e801b: __assert_func at /home/jeroen/esp8266/esp32/newlib_xtensa-2.2.0-bin/newlib_xtensa-2.2.0/xtensa-esp32-elf/newlib/libc/stdlib/../../../.././newlib/libc/stdlib/assert.c:63 (discriminator 8)


Backtrace: 0x40094614:0x3fff2950 0x4009480f:0x3fff2970 0x400e801b:0x3fff2990 0x401703f9:0x3fff29c0 0x40170daa:0x3fff29f0 0x40171472:0x3fff2a40 0x400dbb19:0x3fff2ac0 0x400dabe4:0x3fff2af0 0x401381ad:0x3fff2b70 0x400d99dd:0x3fff2b90 0x400d9a35:0x3fff2bc0
0x40094614: invoke_abort at /Users/jason/esp/esp-idf/components/esp32/./panic.c:648

0x4009480f: abort at /Users/jason/esp/esp-idf/components/esp32/./panic.c:648

0x400e801b: __assert_func at /home/jeroen/esp8266/esp32/newlib_xtensa-2.2.0-bin/newlib_xtensa-2.2.0/xtensa-esp32-elf/newlib/libc/stdlib/../../../.././newlib/libc/stdlib/assert.c:63 (discriminator 8)

0x401703f9: lfs_cache_read at /Users/jason/Projects/x/x-esp32-firmware/components/littlefs/littlefs/lfs.c:1069

0x40170daa: lfs_ctz_traverse at /Users/jason/Projects/x/x-esp32-firmware/components/littlefs/littlefs/lfs.c:1069

0x40171472: lfs_traverse at /Users/jason/Projects/x/x-esp32-firmware/components/littlefs/littlefs/lfs.c:1069

0x400dbb19: x_fs_get_usage at /Users/jason/Projects/x/x-esp32-firmware/main/./x_fs_littlefs.c:178

Note that I have changed the names of some symbols and files for privacy purposes.

This is happening when I try to use lfs_traverse to get filesystem information. Here's the code I use for that:

static int lfs_traverse_count(void *p, lfs_block_t b) {
    *(lfs_size_t *)p += 1;
    return 0;
}

esp_err_t x_fs_get_usage(
		uint64_t* bytes_total,
		uint64_t* bytes_free,
		uint64_t* bytes_used) {

	lfs_size_t in_use = 0;
	int err = lfs_traverse(&lfs, lfs_traverse_count, &in_use);
	if (err) {
		return err;
	}

	*bytes_total = cfg.block_count * cfg.block_size;
	*bytes_used = cfg.block_size * in_use;
	*bytes_free = *bytes_total - *bytes_used;

	return ESP_OK;
}

Before this happened, I saw two instances of logging of bad blocks:

lfs debug:1229: Bad block at 3252
lfs debug:1229: Bad block at 3253
lfs debug:1229: Bad block at 3254
lfs debug:1229: Bad block at 3255
lfs debug:1229: Bad block at 3256


lfs debug:1229: Bad block at 436
lfs debug:1229: Bad block at 437
lfs debug:1229: Bad block at 438
lfs debug:1229: Bad block at 439
lfs debug:1229: Bad block at 440

I have since written a simple block checker and run it against the flash, and found no bad blocks. Here's that code:

	size_t s_size = 4096;
	uint8_t* buffer_work = malloc(s_size);
	uint8_t* buffer_compare = malloc(s_size);
	for (int i = 0; i < (16 * 1024 * 1024) / s_size; i++) {
		// erase block, read it, check all values are 0xff
		memset(buffer_compare, 0xff, s_size);
		memset(buffer_work, 0x00, s_size);
		x_hal_bulk_storage_erase(i * s_size, s_size);
		x_hal_bulk_storage_read(i * s_size, s_size, buffer_work);
		if (memcmp(buffer_compare, buffer_work, s_size) != 0) {
			printf("%08x: erase failed\n", i);
			continue;
		}

		// write bit pattern, read it, compare
		memset(buffer_work, 0b10101010, s_size);
		x_hal_bulk_storage_write(i * s_size, s_size, buffer_work);
		memset(buffer_work, 0xff, s_size);
		x_hal_bulk_storage_read(i * s_size, s_size, buffer_work);
		memset(buffer_compare, 0b10101010, s_size);
		if (memcmp(buffer_compare, buffer_work, s_size) != 0) {
			printf("%08x: bit pattern 1 failed\n", i);
			continue;
		}

		// write second bit pattern, read it, compare
		memset(buffer_work, 0b01010101, s_size);
		x_hal_bulk_storage_write(i * s_size, s_size, buffer_work);
		memset(buffer_work, 0xff, s_size);
		x_hal_bulk_storage_read(i * s_size, s_size, buffer_work);
		memset(buffer_compare, 0x00, s_size);
		if (memcmp(buffer_compare, buffer_work, s_size) != 0) {
			printf("%08x: bit pattern 2 failed\n", i);
			continue;
		}
	}

Aside from not being able to call lfs_traverse, everything else seemed to be working fine. I was able to read and write files, and list directories, but any call to lfs_traverse caused the above assert, even after a reboot.

Please let me know if there's any other information I can provide that will help with this. Happy to help in any way I can.

Thanks,
Jason

Lookahead bug?

Hi
I'm working with an SPI flash with 126 usable/erasable blocks of 65536 bytes each, The page write size is 256. littlefs is V1.3.
I'm experiencing a problem while writing big files.

When I format the storage, I get the following:
Total space 8257536
Used space 262144
Free space 7995392
Where Used space = number of blocks(126) * blocks size(64KB) - free space (calculated as suggested here #45)

Now I write 4 files, 1MB each, in the sequence: file3, file2, file1, file0 (opened with LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC)

I get the following after writing:
Total space 8257536
Used space 4718592
Free space 3538944

So far so good.

Now I write 5 files, again 1MB each, in the sequence: file4,file3,file2,file1,file0 ( file4 at the beginning and then the same as before).

Now I get the following LFS_WARN while writing file1:
// check if we have looked at all blocks since last ack
if (lfs->free.off == lfs->free.ack - lfs->free.begin) {
LFS_WARN("No more free space %d", lfs->free.off + lfs->free.begin);
return LFS_ERR_NOSPC;
}
lfs warn:299: No more free space 251

The call stack is the following:
lfs_alloc line 299
lfs_ctz_extend line 1149
lfs_file_write line 1655

lfs->free.begin = 192
lfs->free.size=59
lfs->free.off=59
lfs->free.ack=251

If I force the file1 to be closed, then I get the following:
Total space 8257536
Used space 5832704
Free space 2424832

Please note that the files are re-written without being deleted, but since I open them again with the truncate flag, I would expect that their space is immediately freed for writing.

I've seen that, when I reformat, the issue happens always after writing the block 125 (the last one). If I perform a different sequence, the block is different.

Could you please help? Thank you

Request for mkfs.lfs tool

Could you provide a mkfs.lfs like tool to build my own fs image from Windows or Linux command line?

Current status

Hi,

Whats is the current status of LittleFS ? Could it be already used in production environment ?

Regards,

Yann

littlefs versioning

Hi,

I propose to add a version number to the littlefs lib, for future improvements and new versions.

What about:

#define LFS_VERSION     20180119

int lfs_get_version(void){
    return LFS_VERSION;
}

It will be easier to follow features and issues

Manage generic attributes

As the attributes are not used by default in littlefs but it is intended to be used depending on the implementation.

In my project I use the attributes to store the modification date of an entry.

Here is my modification I have made on lfs to implement it:

static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
        const lfs_entry_t *entry,
                          const void *attribute, const void *name) {
    return lfs_dir_commit(lfs, dir, (struct lfs_region[]){
            {entry->off, sizeof(entry->d), &entry->d, sizeof(entry->d)},
            {entry->off+sizeof(entry->d), entry->d.alen, attribute, entry->d.alen},
            {entry->off+sizeof(entry->d)+entry->d.alen, entry->d.nlen, name, entry->d.nlen}
        }, name ? 3 : attribute ? 2 : 1);
}

static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
        lfs_entry_t *entry, const void *attribute, const void *name) {
    // 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;
            return lfs_dir_commit(lfs, dir, (struct lfs_region[]){
                    {entry->off, 0, &entry->d, sizeof(entry->d)},
                    {entry->off, 0, attribute, entry->d.alen},
                    {entry->off, 0, name, entry->d.nlen}
                }, 3);
        }

        // we need to allocate a new dir block
        if (!(0x80000000 & dir->d.size)) {
            lfs_dir_t newdir;
            int err = lfs_dir_alloc(lfs, &newdir);
            if (err) {
                return err;
            }

            newdir.d.tail[0] = dir->d.tail[0];
            newdir.d.tail[1] = dir->d.tail[1];
            entry->off = newdir.d.size - 4;
            err = lfs_dir_commit(lfs, &newdir, (struct lfs_region[]){
		            {entry->off, 0, &entry->d, sizeof(entry->d)},
		            {entry->off, 0, attribute, entry->d.alen},
		            {entry->off, 0, name, entry->d.nlen}
                }, 3);
            if (err) {
                return err;
            }

            dir->d.size |= 0x80000000;
            dir->d.tail[0] = newdir.pair[0];
            dir->d.tail[1] = newdir.pair[1];
            return lfs_dir_commit(lfs, dir, NULL, 0);
        }

        int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
        if (err) {
            return err;
        }
    }
}

In lfs_mkdir:

...
	uint8_t attribute[ATTRIBUTE_LENGTH];
    entry.d.type = LFS_TYPE_DIR;
    entry.d.elen = sizeof(entry.d) - 4;
	entry.d.alen = lfs_dir_compute_attribute(attribute);
	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, attribute, path);
...

In lfs_dir_read

...
	if(entry.d.alen > 0){
		lfs_dir_extract_attribute(lfs, dir, &entry, info);
	}
...

In lfs_file_open

...
    if (err == LFS_ERR_NOENT) {
        if (!(flags & LFS_O_CREAT)) {
            return LFS_ERR_NOENT;
        }

	    uint8_t attribute[ATTRIBUTE_LENGTH];

        // create entry to remember name
        entry.d.type = LFS_TYPE_REG;
        entry.d.elen = sizeof(entry.d) - 4;
        entry.d.alen = lfs_dir_compute_attribute(attribute);
        entry.d.nlen = strlen(path);
        entry.d.u.file.head = 0xffffffff;
        entry.d.u.file.size = 0;
	    err = lfs_dir_append(lfs, &cwd, &entry, attribute, 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;
    }
...

In lfs_file_sync

...
	    uint8_t attribute[ATTRIBUTE_LENGTH];
        entry.d.u.file.head = file->head;
        entry.d.u.file.size = file->size;
		entry.d.alen = lfs_dir_compute_attribute(attribute);
        err = lfs_dir_update(lfs, &cwd, &entry, attribute, NULL);
...

And then the 2 function to generate and extract the attributes

static uint8_t lfs_dir_compute_attribute(uint8_t *buf) {
	uint32_t datetime = now(NULL);
	buf[0] = LFS_ATTRIBUTE_TIME;
	buf[1] = datetime & 0xFF;
	buf[2] = (datetime >> 8) & 0xFF;
	buf[3] = (datetime >> 16) & 0xFF;
	buf[4] = (datetime >> 24) & 0xFF;

	return ATTRIBUTE_LENGTH;
}
static int lfs_dir_extract_attribute(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, struct lfs_info *info) {
	uint8_t *attribute = malloc(entry->d.alen);
	ASSERT(attribute);
	if (attribute) {
		int err = lfs_bd_read(lfs, dir->pair[0],
		                      entry->off + 4 + entry->d.elen,
		                      attribute, entry->d.alen);
		if (err) {
			free(attribute);
			return err;
		}

		if (entry->d.alen >= ATTRIBUTE_LENGTH) {
			if (attribute[0] == LFS_ATTRIBUTE_TIME) {
				info->time =
						attribute[1] + attribute[2] * 256 + attribute[3] * 256 * 256 + attribute[4] * 256 * 256 * 256;
			}
		}

		free(attribute);
	}

	return 0;
}

What do you think to include this in littlefs by default, to manage the attributes and let the developer to implement its custom attributes ?

Wrong pos cursor in append mode

Hi,

When opening a file with append flags, the pos cursor at beginning is 0 but should be the size of the file

I propose to add

if (flags & LFS_O_APPEND && file->pos < file->size) {
        file->pos = file->size;
    }

limitation when no dynamic memory allocation is used

Hello,

Could you help me regarding what limitations exist when statically allocated buffers are passed to the configuration structure instead of using dynamic memory allocation ?

Above the "file_buffer" it writes: "If enabled, only one file may be opened at a time." -> I could verify the problem here (when writing both files, the content of the second writing will be written into the first file too), however I would have expected that the second file open returns a negative error code.

Thanks,
Geza

User provided block device read etc.

Hi. First of all, great work! I really enjoy the web simulator.

To implement the user needs to supply these functions:

.read  = user_provided_block_device_read,
.prog  = user_provided_block_device_prog,
.erase = user_provided_block_device_erase,
.sync  = user_provided_block_device_sync,

Do you have an example of how these should look?
E.g. like the examples here: https://github.com/pellepl/spiffs/wiki/Integrate-spiffs

I'm asking because it seems the function arguments required are at a layer of abstraction higher than a driver, which usually just has: (SPI handle, address, size, *buffer).

Best regards
Frederik

"port" headers

I see that in the past day or so you've moved quite a few functions out into lfs_util.h, although this is a good move, that file should only contain defaults and should include say another file called lfs_config.h which the user can use to define functions, if they're not defined in the lfs_config file then they're defaulted to whatever is the default state by the lfs_util.h file.

lfs_config.h never appears in the repository, a version say "lfs_config_default.h" could be provided to show how to override functions.

This means we can safely pull changes without having to patch in our changes from our previous lfs_util.h file and also any new additions will be defaulted to their state as normal.

FreeRTOS and mbed both use this kind of system.

Great project btw, already building the FS into our next project, have it up and running on our eMMC module.

Deleting folder that is not empty

When using lfs_remove to remove a directory there is a check to see if the directory is not empty:

        } else if (dir.d.size != sizeof(dir.d)+4) {
            return LFS_ERR_NOTEMPTY;
        }

Am I right in thinking this check is purely there to give "expected behavior" for a filesystem and if these two lines were commented out the filesystem would be still ok (not corrupted) if a directory that is not empty is deleted?

Reason why is that it would be useful if a folder contained a lot of files to be able to quickly delete them all at once rather than use lfs_dir_open lfs_dir_read to delete each file in the folder individually (which takes a lot of unnecessary writes).

C++ support in .H files

Hi geky
I would like the .h files to have the C++ support directives, like this:

#ifdef __cplusplus
extern "C"
{
#endif

....

#ifdef __cplusplus
}
#endif

Cheers

Bad Block detection & Wear Leveling

How is the wear leveling and bad block detection done?

Is it up to the lfs_conifg.prog / lfs_config.erase function to return a LFS_ERR_CORRUPT error to inform littlefs of a bad block or does littlefs the check by itself by reading back the data for verification?

Linking failed

gcc -Os -I. -std=c99 -Wall -pedantic lfs_util.o lfs.o emubd/lfs_emubd.o  -o lfs
/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Makefile:48: recipe for target 'lfs' failed
make: *** [lfs] Error 1

I got this kind of strange error

checking block sizes

In lfs.c, line 2029, the comment says "check that program and read sizes are multiples of the block size" but it looks like the code is checking the if the ->prog_size is a multiple of the ->read_size. Is this intentional?

Read cache internal usage

For one of our use cases we have a file that we only read from in a very random manner (its graphical data), typically we have a table at the start with information on width, height of icons and offset into the file of the icon. For best performance we have found that setting a read cache of 1 (i.e. disabled) gives the best performance.

However we have found that some internal aspects of littlefs slow down significantly when using a cache of this size (e.g. opening and closing files). Specifically the lfs_cache_cmp and lfs_cache_crc functions slow down without a read cache. If we tweak these functions to use a separate cache we get good performance for our data, and good open and close performance (e.g. 250ms to open and close 100 files goes down to 25ms).

It might be useful if this internal read cache size could be configurable in the standard release if you feel it doesn't over complicate things.

Enhancement: List a directory content

I just implemented the littlefs and it would be great to have a function to list a directory with the following result:

  • type (file, dir)
  • name
  • size

In-situ testing method?

Great project. I am looking forward to using it extensively once I get it working.

Can you recommend a method by which I can do an exhaustive in-situ test on the hardware target for my implementation?
I am still in the debugging phase (getting extremely large block numbers), but I have to manually construct cases to comb for errors.

I don't quite see how to easily map the test suite into a single monolithic embedded application that I can run to do all the same exercises. But that would be a pretty useful thing to have ...

Discussion: littlefs on SPI NAND

Hi I would like to use LittleFS directly on a NAND chip, such as Micron MT29F.

The minimum erasable unit on the MT29F is 128KiB
The smallest writable unit is 2KiB (random write)
The smallest writable unit is 1 byte (sequential write)

Thanks

lfs_disk_entry

alen and elen seem to be a bit superfluous.
I'd fold them in some spare bits in type
or trim nlen (and thus LFS_NAME_MAX) to put them there.
Eventually.

Hardfault in lfs_remove / lfs_dir_remove

I am hitting a hardfault inside lfs_paircmp, from lfs_dir_remove, from lfs_remove, specifically on line 734.

How can I go about debugging the root cause here? The two obvious causes are either an incorrect implementation of the driver by myself, or an incorrect usage of lfs_remove.

Unique file identifier

What can be used as a unique file identifier of a file on a filesystem? Basically, to set the st_ino field of the struct stat.

C89: lfs.c:638:24: error: taking address of temporary array and other instances

To resolve this problem for my project I simply moved the declaration of the temporary struct out of the function call and then used it like so,

    struct lfs_region region[] = {
        {entry->off, sizeof(entry->d), &entry->d, sizeof(entry->d)},
        {entry->off+sizeof(entry->d), entry->d.nlen, data, entry->d.nlen}
    };
    
    int err = lfs_dir_commit(lfs, dir, region, data ? 2 : 1);

This happens in several other places

lfs.c:654:21: error: taking address of temporary array
lfs.c:674:21: error: taking address of temporary array
lfs.c:714:13: error: taking address of temporary array
lfs.c:1119:63: warning: taking address of temporary

What's interesting is sometimes I just get a warning for it and others it's an error even though it all gets the same flags.

Enumerating directory and opening files

In another use case, we want to open every file in the directory (to read some info) and print this out. There's quite a lot of files (100s) and this can take a long time - due to the fact that lfs_file_open will cause another directory traverse to find the top block of the file.

Again its adding to complexity but it might be useful to have an open that you could pass an lfs_info structure that is returned from lfs_dir_read and have it open it without the search overhead (e.g. lfs_file_open_direct).

The lfs_info struct would have to be modified to store the block info I guess.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.