Merge pull request #1068 from littlefs-project/fix-dir-remove-read

Fix dir iteration being broken by concurrent removes
This commit is contained in:
Christopher Haster
2025-03-20 01:24:04 -05:00
committed by GitHub
2 changed files with 78 additions and 1 deletions

3
lfs.c
View File

@@ -2369,7 +2369,8 @@ fixmlist:;
if (d->m.pair != pair) {
for (int i = 0; i < attrcount; i++) {
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
d->id == lfs_tag_id(attrs[i].tag)) {
d->id == lfs_tag_id(attrs[i].tag) &&
d->type != LFS_TYPE_DIR) {
d->m.pair[0] = LFS_BLOCK_NULL;
d->m.pair[1] = LFS_BLOCK_NULL;
} else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&

View File

@@ -725,6 +725,82 @@ code = '''
lfs_unmount(&lfs) => 0;
'''
[cases.test_dirs_remove_read]
defines.N = 10
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 < N; i++) {
char path[1024];
sprintf(path, "prickly-pear/cactus%03d", i);
lfs_mkdir(&lfs, path) => 0;
}
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
struct lfs_info info;
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);
for (int i = 0; i < N; i++) {
char path[1024];
sprintf(path, "cactus%03d", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, path) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs);
for (lfs_size_t k = 0; k < N; k++) {
for (lfs_size_t j = 0; j < N; j++) {
lfs_mount(&lfs, cfg) => 0;
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);
// iterate over dirs < j
for (unsigned i = 0; i < j; i++) {
char path[1024];
sprintf(path, "cactus%03d", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, path) == 0);
}
// remove k while iterating
char path[1024];
sprintf(path, "prickly-pear/cactus%03d", k);
lfs_remove(&lfs, path) => 0;
// iterate over dirs >= j
for (unsigned i = j; i < ((k >= j) ? N-1 : N); i++) {
char path[1024];
sprintf(path, "cactus%03d", (k >= j && i >= k) ? i+1 : i);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, path) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
// recreate k
sprintf(path, "prickly-pear/cactus%03d", k);
lfs_mkdir(&lfs, path) => 0;
lfs_unmount(&lfs) => 0;
}
}
'''
[cases.test_dirs_other_errors]
code = '''
lfs_t lfs;