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
4 changed files with 99 additions and 86 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

22
lfs.c
View File

@@ -2068,14 +2068,10 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
dir->pos = lfs_min(2, off);
off -= dir->pos;
// skip superblock entry
dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0);
while (off > 0) {
int diff = lfs_min(dir->m.count - dir->id, off);
dir->id += diff;
dir->pos += diff;
off -= diff;
while (off != 0) {
dir->id = lfs_min(dir->m.count, off);
dir->pos += dir->id;
off -= dir->id;
if (dir->id == dir->m.count) {
if (!dir->m.split) {
@@ -2088,8 +2084,6 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
LFS_TRACE("lfs_dir_seek -> %d", err);
return err;
}
dir->id = 0;
}
}
@@ -2113,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);
@@ -3897,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

View File

@@ -428,78 +428,4 @@ scripts/test.py << TEST
TEST
done
echo "--- Root seek test ---"
./scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
for (int i = 3; i < $MEDIUMSIZE; i++) {
sprintf(path, "hi%03d", i);
lfs_mkdir(&lfs, path) => 0;
}
lfs_dir_open(&lfs, &dir, "/") => 0;
for (int i = 0; i < $MEDIUMSIZE; i++) {
if (i == 0) {
sprintf(path, ".");
} else if (i == 1) {
sprintf(path, "..");
} else if (i == 2) {
sprintf(path, "hello");
} else {
sprintf(path, "hi%03d", i);
}
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(path, info.name) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int j = 0; j < $MEDIUMSIZE; j++) {
lfs_soff_t off = -1;
lfs_dir_open(&lfs, &dir, "/") => 0;
for (int i = 0; i < $MEDIUMSIZE; i++) {
if (i == 0) {
sprintf(path, ".");
} else if (i == 1) {
sprintf(path, "..");
} else if (i == 2) {
sprintf(path, "hello");
} else {
sprintf(path, "hi%03d", i);
}
if (i == j) {
off = lfs_dir_tell(&lfs, &dir);
off >= 0 => true;
}
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(path, info.name) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_seek(&lfs, &dir, off) => 0;
for (int i = j; i < $MEDIUMSIZE; i++) {
if (i == 0) {
sprintf(path, ".");
} else if (i == 1) {
sprintf(path, "..");
} else if (i == 2) {
sprintf(path, "hello");
} else {
sprintf(path, "hi%03d", i);
}
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(path, info.name) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
lfs_unmount(&lfs) => 0;
TEST
scripts/results.py