Skip to content

Corruption reported upon concurrent directory modification #1061

Open
@tpwrules

Description

@tpwrules

I have an embedded application where one part of the application might be iterating a directory while another deletes files from it. The outcome of the iteration isn't meaningful to the application in this case, but sometimes lfs_dir_read returns LFS_ERR_CORRUPT. I would expect it to return some subset of the files in the directory (possibly none at all) but complete without any errors.

POSIX says that a directory iteration is unspecified to report an entry if it's added or removed while the iteration is in progress, but otherwise I believe it's expected to. I'm not sure LittleFS needs to go that far and guarantee entries that aren't touched will be returned (it would be nice if it did), but it at least should not claim corruption.

Is there any actual corruption occurring? Can file operations safely be continued as long as the directory is closed after the error is reported? Can LittleFS be easily fixed to not claim corruption?

To put it another way, I would not expect the below tests to fail, but they do on v2.10.1 at least. Iteration shouldn't give an error if a file is removed before reads start, nor if multiple files are removed between reads.

[cases.test_dirs_read_remove_multi]
defines.K = [0, 5]
if = 'N < BLOCK_COUNT/2'
code = '''
    lfs_t lfs;
    lfs_format(&lfs, cfg) => 0;
    lfs_mount(&lfs, cfg) => 0;
    lfs_mkdir(&lfs, "prickly-pear") => 0;
    for (int i = 0; i < 10; i++) {
        char path[1024];
        sprintf(path, "prickly-pear/cactus%03d", i);
        lfs_file_t file;
        lfs_file_open(&lfs, &file, path,
                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
        lfs_file_close(&lfs, &file) => 0;
    }

    lfs_dir_t dir;
    struct lfs_info info;
    char path[1024];

    lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
    lfs_dir_read(&lfs, &dir, &info) => 1;
    assert(info.type == LFS_TYPE_DIR);
    assert(strcmp(info.name, ".") == 0);
    lfs_dir_read(&lfs, &dir, &info) => 1;
    assert(info.type == LFS_TYPE_DIR);
    assert(strcmp(info.name, "..") == 0);

    lfs_dir_read(&lfs, &dir, &info) => 1;
    sprintf(path, "prickly-pear/cactus%03d", (int)F);
    lfs_remove(&lfs, path) => 0;
    sprintf(path, "prickly-pear/cactus%03d", (int)F+1);
    lfs_remove(&lfs, path) => 0;
    assert(lfs_dir_read(&lfs, &dir, &info) >= 0);

    lfs_dir_close(&lfs, &dir) => 0;
    lfs_unmount(&lfs) => 0;
'''

[cases.test_dirs_remove_read]
defines.F = [0, 5]
if = 'N < BLOCK_COUNT/2'
code = '''
    lfs_t lfs;
    lfs_format(&lfs, cfg) => 0;
    lfs_mount(&lfs, cfg) => 0;
    lfs_mkdir(&lfs, "prickly-pear") => 0;
    for (int i = 0; i < 10; i++) {
        char path[1024];
        sprintf(path, "prickly-pear/cactus%03d", i);
        lfs_file_t file;
        lfs_file_open(&lfs, &file, path,
                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
        lfs_file_close(&lfs, &file) => 0;
    }

    lfs_dir_t dir;
    struct lfs_info info;
    char path[1024];

    lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
    lfs_dir_read(&lfs, &dir, &info) => 1;
    assert(info.type == LFS_TYPE_DIR);
    assert(strcmp(info.name, ".") == 0);
    lfs_dir_read(&lfs, &dir, &info) => 1;
    assert(info.type == LFS_TYPE_DIR);
    assert(strcmp(info.name, "..") == 0);

    sprintf(path, "prickly-pear/cactus%03d", (int)F);
    lfs_remove(&lfs, path) => 0;
    assert(lfs_dir_read(&lfs, &dir, &info) >= 0);

    lfs_dir_close(&lfs, &dir) => 0;
    lfs_unmount(&lfs) => 0;
'''

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs fixwe know what is wrong

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions