littlefs-project / littlefs

A little fail-safe filesystem designed for microcontrollers
BSD 3-Clause "New" or "Revised" License
5.16k stars 793 forks source link

LFSv1: create more files than physically possible #202

Open pasi-ojala-tridonic opened 5 years ago

pasi-ojala-tridonic commented 5 years ago

I have a very small ChainingBlockDevice, 10 blocks total. LFS seems to allow me to create 8 (or more files). Obviously they don't work very well. Test snippet and a log attached. In the snippet, there's a call to mountLittleFS, which does the mount and prints out root directory, and reports the size of the blockdevice.

On the first loop, files get created and initialized (integer, same as filename or 0 for bootcount). Things work reasonably till file 7, where I do get a warning about no more free space. Creating a new file returns a valid handle though, and fwrite() returns a value indicating that the write was fine. Same for file 8.

On second run, I see 8 files in root directory, which should not be possible with the 10 block system. Reading the previous data till file 6 works fine, 7 and 8 return no data. Updating files up to 6 also seems to work according to return values, but

on third run, bootcount is still read as 1, file 2 still has contents of 2, though it should have been updated on previous run. Same for others.

If I use maximum of 5 files total, the system seems to work as intended (the values keep increasing), 6 already show this behavior, in the sense that updating the values will not work. Am I guessing right that creating file 6 already reserves too many blocks for all of the updating and directory maintenance to work properly?

lfstest.txt

LFSv1_10blocks_8files.txt

lsilvaalmeida commented 5 years ago

Hello @pasi-ojala-tridonic

If I'm not wrong, lfs v1 uses at least 4 blocks (2 for the superblock and 2 others for the root directory). Also each file consumes 1 or more blocks. So, as you have just 10 blocks, you could create up to 6 files.

But, thanks to the COW spirit of lfs, a block with old data will not be released until a succesful write of the new data. It means that if you open a file that already exists, lfs will alloc another block to write to and just after this write the old block where was this file will be free. So, you need at least 1 free block to handle with and this is the source of the "strange behavior".

Resuming, 10 blocks - (4 blocks to metadata (superblock + root directory) + 1 free block to write new data until close the file) = 5 blocks to real data = 5 files max.

If you try to create more than 5 files, you will not respect the lfs spec and the behavior can not be guaranted.

(In v2 all this was changed)

geky commented 5 years ago

Hi, sorry I haven't been able to dig into this issue yet. I should get better at noting the issues I've written down for later investigation.

Am I guessing right that creating file 6 already reserves too many blocks for all of the updating and directory maintenance to work properly?

Yes, that is likely what's happening, @lsilvaalmeida is right that this is too many files for v1.

However, littlefs should return an error (-ENOSPC) instead of crashing uncontrollably. The fact that it doesn't is a bug.

Unfortunately I'm currently prioritizing v2 issues (and pushing v2 update into Mbed OS), so this issue won't be able to leave my TODO list for now.

pasi-ojala-tridonic commented 5 years ago

@geky, ran into this again... I think I figured out what's going on. When running the following code in a full filesystem, fwrite() happily returns the number of characters I wanted to write. But fclose() returns -1 (after trying to flush to flash, looks like), and errno is set to -ENOSPC. Not sure if this is a flaw or a fact of life, but a good reminder to check return value of fclose(), anyway. `

    size_t wrotetotal = fwrite(contents, 1, len, fp);

    int err = fclose(fp);

    if (err){

        tr_error("error closing file, errno %d",errno);

    }

`