Jelajahi Sumber

Fixed issue where deorphan could get stuck circling between two half-orphans

This of course should never happen normally, two half-orphans requires
two parents, which is disallowed in littlefs for this reason. But it can
happen if there is an outdated half-orphan later in the metadata
linked-list. The two half-orphans can cause the deorphan step to get
stuck, constantly "fixing" the first half-orphan before it has a chance
to remove the problematic, outdated half-orphan later in the list.

The solution here is to do a full check for half-orphans before
restarting the half-orphan loop. This strategy has the potential to
visit more metadata blocks unnecessarily, but avoids situations where
removing a later half-orphan will eventually cause an earlier
half-orphan to resolve itself.

Found with heuristic powerloss testing with test_relocations_reentrant_renames
after 192 nested powerlosses.
Christopher Haster 3 tahun lalu
induk
melakukan
ba1c76435a
1 mengubah file dengan 7 tambahan dan 4 penghapusan
  1. 7 4
      lfs.c

+ 7 - 4
lfs.c

@@ -4603,7 +4603,6 @@ static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) {
 
     int8_t found = 0;
 
-restart:
     // Check for orphans in two separate passes:
     // - 1 for half-orphans (relocations)
     // - 2 for full-orphans (removes/renames)
@@ -4612,10 +4611,12 @@ restart:
     // references to full-orphans, effectively hiding them from the deorphan
     // search.
     //
-    for (int pass = 0; pass < 2; pass++) {
+    int pass = 0;
+    while (pass < 2) {
         // Fix any orphans
         lfs_mdir_t pdir = {.split = true, .tail = {0, 1}};
         lfs_mdir_t dir;
+        bool moreorphans = false;
 
         // iterate over all directory directory entries
         while (!lfs_pair_isnull(pdir.tail)) {
@@ -4676,7 +4677,7 @@ restart:
 
                         // did our commit create more orphans?
                         if (state == LFS_OK_ORPHANED) {
-                            goto restart;
+                            moreorphans = true;
                         }
 
                         // refetch tail
@@ -4712,7 +4713,7 @@ restart:
 
                     // did our commit create more orphans?
                     if (state == LFS_OK_ORPHANED) {
-                        goto restart;
+                        moreorphans = true;
                     }
 
                     // refetch tail
@@ -4722,6 +4723,8 @@ restart:
 
             pdir = dir;
         }
+
+        pass = moreorphans ? 0 : pass+1;
     }
 
     // mark orphans as fixed