Compare commits

...

2 Commits

Author SHA1 Message Date
Christopher Haster
9acf6a08b1 Stop wear-leveling during migration
Stop proactively relocate blocks during migrations, this can cause a number of
failure states such: clobbering the v1 superblock if we relocate root, and
invalidating directory pointers if we relocate the head of a directory. On top
of this, relocations increase the overall complexity of lfs_migration, which is
already a delicate operation.
2019-11-26 17:47:06 -06:00
Christopher Haster
88ed8623be Fixed issue with directories falling out of date after block relocation
This is caused by dir->head not being updated when dir->m.pair may be.
This causes the two to fall out of sync and later dir rewinds to fail.

This bug stems all the way back from the first commits of littlefs, so
it's surprising it has avoided detection for this long. Perhaps because
lfs_dir_rewind is not used often.
2019-11-26 10:57:15 -06:00
3 changed files with 102 additions and 5 deletions

View File

@@ -55,6 +55,7 @@ test: \
test_attrs \
test_move \
test_orphan \
test_relocations \
test_corrupt
@rm test.c
test_%: tests/test_%.sh

18
lfs.c
View File

@@ -1503,9 +1503,13 @@ static int lfs_dir_compact(lfs_t *lfs,
}
}
#ifdef LFS_MIGRATE
} else if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && lfs->lfs1) {
// we can't relocate our root during migrations, as this would
// cause the superblock to get updated, which would clobber v1
} else if (lfs->lfs1) {
// do not proactively relocate blocks during migrations, this
// can cause a number of failure states such: clobbering the
// v1 superblock if we relocate root, and invalidating directory
// pointers if we relocate the head of a directory. On top of
// this, relocations increase the overall complexity of
// lfs_migration, which is already a delicate operation.
#endif
} else {
// we're writing too much, time to relocate
@@ -2103,8 +2107,6 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
return err;
}
dir->m.pair[0] = dir->head[0];
dir->m.pair[1] = dir->head[1];
dir->id = 0;
dir->pos = 0;
LFS_TRACE("lfs_dir_rewind -> %d", 0);
@@ -3887,6 +3889,12 @@ static int lfs_fs_relocate(lfs_t *lfs,
d->m.pair[0] = newpair[0];
d->m.pair[1] = newpair[1];
}
if (d->type == LFS_TYPE_DIR &&
lfs_pair_cmp(oldpair, ((lfs_dir_t*)d)->head) == 0) {
((lfs_dir_t*)d)->head[0] = newpair[0];
((lfs_dir_t*)d)->head[1] = newpair[1];
}
}
// find parent

88
tests/test_relocations.sh Executable file
View File

@@ -0,0 +1,88 @@
#!/bin/bash
set -eu
export TEST_FILE=$0
trap 'export TEST_LINE=$LINENO' DEBUG
ITERATIONS=20
COUNT=10
echo "=== Relocation tests ==="
rm -rf blocks
scripts/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
// fill up filesystem so only ~16 blocks are left
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
memset(buffer, 0, 512);
while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
lfs_file_write(&lfs, &file, buffer, 512) => 512;
}
lfs_file_close(&lfs, &file) => 0;
// make a child dir to use in bounded space
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Outdated head test ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
for (int j = 0; j < $ITERATIONS; j++) {
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 0;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_remove(&lfs, path) => 0;
}
}
lfs_unmount(&lfs) => 0;
TEST
scripts/results.py