From aae22c8256b2bb580cce226d320d6c74a7d8a579 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 14 Nov 2019 14:25:42 -0600 Subject: [PATCH] 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. --- Makefile | 1 + lfs.c | 8 +++- tests/test_relocations.sh | 88 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100755 tests/test_relocations.sh diff --git a/Makefile b/Makefile index a22fc22d..95c26dac 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,7 @@ test: \ test_attrs \ test_move \ test_orphan \ + test_relocations \ test_corrupt @rm test.c test_%: tests/test_%.sh diff --git a/lfs.c b/lfs.c index 95a3b6af..e71faff8 100644 --- a/lfs.c +++ b/lfs.c @@ -2103,8 +2103,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 +3885,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 diff --git a/tests/test_relocations.sh b/tests/test_relocations.sh new file mode 100755 index 00000000..4a371eab --- /dev/null +++ b/tests/test_relocations.sh @@ -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