littlefs-project / littlefs

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

File system corruption when creating directories #979

Closed josesimoes closed 1 month ago

josesimoes commented 1 month ago

This is kind of a follow-up on #973.

Take the following code, following a successful call to lfs_format() followed by lfs_mount().

(please ignore the file system var which is coming from the rest of the test code)

char *filePath = "temp/testdir/file1.txt";
char *dir1Path = "temp";
char *dir2Path = "temp/testdir";
volatile int32_t result;
struct lfs_info info;

// check file path
result = lfs_stat(&lfs[index], filePath, &info);

// try to get file attributes
result = lfs_getattr(
        &lfs[index],
        (const char *)filePath,
        NANO_LITTLEFS_ATTRIBUTE,
        &attributes,
        NANO_LITTLEFS_ATTRIBUTE_SIZE);

// create directories in the path
result = lfs_mkdir(&lfs[index], dir1Path);
result = lfs_mkdir(&lfs[index], dir2Path);

All the above calls return successfully with LFS_ERR_OK. Now comes the unexpected situation.

Following the above, any of these calls fail with LFS_ERR_CORRUPT.

// check directory exists
result = lfs_stat(&lfs[index], dir2Path, &info);

// check file path
result = lfs_stat(&lfs[index], filePath, &info);

Restarting the application (which detects the corruption on the 1st call to lfs_mount() which requires subsequent calls to lfs_format() followed by lfs_mount() and will execute the initial code again with LFS_ERR_OK.

Interestingly, if instead of calling lfs_stat() the following code is executed, everything runs smootly.

// set file attributes
// hardcoded to 128 which is value for FileAttributes_Normal
nanoAttributes = 128;
attr = (struct lfs_attr){
    .type = NANO_LITTLEFS_ATTRIBUTE,
    .size = NANO_LITTLEFS_ATTRIBUTE_SIZE,
    .buffer = &nanoAttributes
};

fileConfig = (struct lfs_file_config) {
    .buffer = NULL,
    .attrs = &attr,
    .attr_count = 1,
};

// create file
result =  lfs_file_opencfg(&lfs[index], &file, filePath,  LFS_O_CREAT | LFS_O_RDWR, &fileConfig);

 // sync file to save attributes
result = lfs_file_sync(&lfs[index], &file);

// read file attributes
result = lfs_getattr(
        &lfs[index],
        (const char *)filePath,
        NANO_LITTLEFS_ATTRIBUTE,
        &attributes,
        NANO_LITTLEFS_ATTRIBUTE_SIZE);

// close file
result = lfs_file_close(&lfs[index], &file);

// check file existance
result = lfs_stat(&lfs[index], filePath, &info);

// read file attributes
result = lfs_getattr(
        &lfs[index],
        (const char *)filePath,
        NANO_LITTLEFS_ATTRIBUTE,
        &attributes,
        NANO_LITTLEFS_ATTRIBUTE_SIZE);

I'm I missing something after the directory creation that can "force" any sync or similar? Or is the call to lfs_stat() somehow disrrupting the file system cache/integrity?

geky commented 1 month ago

Hi @josesimoes, hmm, I have not seen something like this before.

A couple questions to try to understand the context:

// set file attributes
// hardcoded to 128 which is value for FileAttributes_Normal
nanoAttributes = 128;
attr = (struct lfs_attr){
    .type = NANO_LITTLEFS_ATTRIBUTE,
    .size = NANO_LITTLEFS_ATTRIBUTE_SIZE,
    .buffer = &nanoAttributes
};

This is a bit confusing. Maybe I am missing something, how exactly is nanoAttributes declared? Is it a uint8_t array of size 128?

Just for completeness, what is the value of NANO_LITTLEFS_ATTRIBUTE and NANO_LITTLEFS_ATTRIBUTE_SIZE? NANO_LITTLEFS_ATTRIBUTE_SIZE should be the same size as the buffer array.

I'm I missing something after the directory creation that can "force" any sync or similar? Or is the call to lfs_stat() somehow disrrupting the file system cache/integrity?

There is a special "force consistency" step that occurs on the first mutation after lfs_mount. This occurs lazily so read-only mounts of littlefs are guaranteed not to write to disk.

You can trigger this explicitly with lfs_fs_mkconsistent.

It would be interesting to know if calling lfs_fs_mkconsistent before lfs_getattr prevents the behavior you are seeing, though if there is no power-loss (or version upgrade?) I think this would be unlikely.

josesimoes commented 1 month ago

Just to close this issue, a quick follow-up: this ended being an issue in the driver. It was using the wrong command to erase the block. Great support from @geky! Definitely recommend it. 💯 👍🏻