Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorChristopher Haster <chaster@utexas.edu>2019-11-26 10:21:42 +0300
committerChristopher Haster <chaster@utexas.edu>2019-12-02 01:32:01 +0300
commitce2c01f098f4d2b9479de5a796c3bb531f1fe14c (patch)
treeed80eab42acfc1817ebd1d6324dcb7d7f3ea26f7 /tests
parent0197b1810071d0d144a2350df9c0acee5393221e (diff)
Fixed lfs_dir_fetchmatch not understanding overwritten tagsv2.1.4
Sometimes small, single line code change hides behind it a complicated story. This is one of those times. If you look at this diff, you may note that this is a case of lfs_dir_fetchmatch not correctly handling a tag that invalidates a callback used to search for some condition, in this case a search for a parent, which is invalidated by a later dir tag overwritting the previous dir pair. But how can this happen? Dir-pair-tags are only overwritten during relocations (when a block goes bad or exceeds the block_cycles config option for dynamic wear-leveling). Other dir operations create new directory entries. And the only lfs_dir_fetchmatch condition that relies on overwrites (as opposed to proper deletes) is when we need to find a directory's parent, an operation that only occurs during a _different_ relocation. And a false _positive_, can only happen if we don't have a parent. Which is really unlikely when we search for directory parents! This bug and minimal test case was found by Matthew Renzelmann. In a unfortunate series of events, first a file creation causes a directory split to occur. This creates a new, orphaned metadata-pair containing our new file. However, the revision count on this metadata-pair indicates the pair is due for relocation as a part of wear-leveling. Normally, this is fine, even though this metadata-pair has no parent, the lfs_dir_find should return ENOENT and continue without error. However, here we get hit by our fetchmatch bug. A previous, unrelated relocation overwrites a pair which just happens to contain the block allocated for a new metadata-pair. When we search for a parent, lfs_dir_fetchmatch incorrectly finds this old, outdated metadata pair and incorrectly tells our orphan it's found its parent. As you can imagine the orphan's dissapointment must be immense. So an unfortunately timed dir split triggers a relocation which incorrectly finds a previously written parent that has been outdated by another relocation. As a solution we can outdate our found tag if it is overwritten by an exact match during lfs_dir_fetchmatch. As a part of this I started adding a new set of tests: tests/test_relocations, for aggressive relocations tests. This is already by appended to by another PR. I suspect relocations is relatively under-tested and is becoming more important due to recent improvements in wear-leveling.
Diffstat (limited to 'tests')
-rwxr-xr-xtests/test_alloc.sh10
-rwxr-xr-xtests/test_relocations.sh51
2 files changed, 61 insertions, 0 deletions
diff --git a/tests/test_alloc.sh b/tests/test_alloc.sh
index d9f233b..0669850 100755
--- a/tests/test_alloc.sh
+++ b/tests/test_alloc.sh
@@ -250,6 +250,14 @@ scripts/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
+## Below, these tests depend _very_ heavily on the geometry of the
+## block device being tested, they should be removed and replaced
+## by generalized tests. For now we'll just skip if the geometry
+## is customized.
+
+if [[ ! $MAKEFLAGS =~ "LFS_BLOCK_CYCLES" ]]
+then
+
echo "--- Chained dir exhaustion test ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
@@ -481,4 +489,6 @@ scripts/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
+fi
+
scripts/results.py
diff --git a/tests/test_relocations.sh b/tests/test_relocations.sh
index 4a371ea..5244e5e 100755
--- a/tests/test_relocations.sh
+++ b/tests/test_relocations.sh
@@ -23,6 +23,57 @@ scripts/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
+echo "--- Dangling split dir 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;
+ }
+ lfs_dir_read(&lfs, &dir, &info) => 0;
+ lfs_dir_close(&lfs, &dir) => 0;
+
+ if (j == $ITERATIONS-1) {
+ break;
+ }
+
+ 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/test.py << TEST
+ lfs_mount(&lfs, &cfg) => 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;
+ }
+ 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
+
echo "--- Outdated head test ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;