From 88ed8623be912f1c4dfc60b77256078aa44015b2 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 a22fc22..95c26da 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 95a3b6a..e71faff 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 0000000..4a371ea --- /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