]> www.infradead.org Git - mtd-utils.git/commitdiff
fsck.ubifs: Handle disconnected files
authorZhihao Cheng <chengzhihao1@huawei.com>
Mon, 11 Nov 2024 09:08:15 +0000 (17:08 +0800)
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Mon, 11 Nov 2024 09:32:46 +0000 (10:32 +0100)
This is the 17/18 step of fsck. Recover disconnected files into
lost+found. If there is no free space left to recover the disconnected
files, fsck may delete the files to make filesystem be consistent.

Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
ubifs-utils/fsck.ubifs/fsck.ubifs.c
ubifs-utils/fsck.ubifs/fsck.ubifs.h
ubifs-utils/fsck.ubifs/handle_disconnected.c
ubifs-utils/fsck.ubifs/problem.c

index 550d3f98e764941cdbdd34696c19b26a5d667794..423a753fdf08bd44a4176eefd92f24db1bc05edb 100644 (file)
@@ -541,6 +541,13 @@ static int do_fsck(void)
                goto free_disconnected_files_2;
        }
 
+       log_out(c, "Handle disconnected files");
+       err = handle_disonnected_files(c);
+       if (err) {
+               exit_code |= FSCK_ERROR;
+               goto free_disconnected_files_2;
+       }
+
 free_disconnected_files_2:
        destroy_file_list(c, &FSCK(c)->disconnected_files);
        return err;
@@ -596,6 +603,7 @@ int main(int argc, char *argv[])
         * Step 14: Check and correct the index size
         * Step 15: Check and create root dir
         * Step 16: Check and create lost+found
+        * Step 17: Handle disconnected files
         */
        err = do_fsck();
        if (err && FSCK(c)->try_rebuild) {
index 0e2a0811a8e1cd6314fe227e9e6702ed7df4c6e0..652993264242e0d23f73da965d003df21280ec7c 100644 (file)
@@ -46,7 +46,7 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
        FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT,
        EMPTY_TNC, LPT_CORRUPTED, NNODE_INCORRECT, PNODE_INCORRECT,
        LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT, INCORRECT_IDX_SZ,
-       ROOT_DIR_NOT_FOUND };
+       ROOT_DIR_NOT_FOUND, DISCONNECTED_FILE_CANNOT_BE_RECOVERED };
 
 enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
 
@@ -387,5 +387,6 @@ int check_and_correct_index_size(struct ubifs_info *c);
 
 /* handle_disconnected.c */
 int check_and_create_lost_found(struct ubifs_info *c);
+int handle_disonnected_files(struct ubifs_info *c);
 
 #endif
index b9a380f9994270923f2e0fc27db99112a54040df..be62522ad12631bfdda27f506aa8814a70d340bf 100644 (file)
@@ -19,6 +19,7 @@
 #include "fsck.ubifs.h"
 
 #define LOST_FOUND_DIR_NAME "lost+found"
+#define MAX_REPEAT_NAME_RETRY_TIMES 10000000
 
 /**
  * check_and_create_lost_found - Check and create the lost+found directory.
@@ -87,3 +88,110 @@ free_root:
        kfree(root_ui);
        return err;
 }
+
+static int handle_disonnected_file(struct ubifs_info *c,
+                                  struct scanned_file *file)
+{
+       int err = 0;
+
+       if (FSCK(c)->lost_and_found) {
+               unsigned int index = 0;
+               char file_name[UBIFS_MAX_NLEN + 1];
+               struct fscrypt_name nm;
+               struct ubifs_inode *ui = NULL, *lost_found_ui = NULL;
+
+               lost_found_ui = ubifs_lookup_by_inum(c, FSCK(c)->lost_and_found);
+               if (IS_ERR(lost_found_ui)) {
+                       err = PTR_ERR(lost_found_ui);
+                       ubifs_assert(c, err != -ENOENT);
+                       return err;
+               }
+               ui = ubifs_lookup_by_inum(c, file->inum);
+               if (IS_ERR(ui)) {
+                       err = PTR_ERR(ui);
+                       ubifs_assert(c, err != -ENOENT);
+                       goto free_lost_found_ui;
+               }
+
+               while (index < MAX_REPEAT_NAME_RETRY_TIMES) {
+                       struct ubifs_inode *target_ui;
+
+                       err = snprintf(file_name, sizeof(file_name),
+                                      "INO_%lu_%u", file->inum, index);
+                       if (err < 0)
+                               goto free_ui;
+                       fname_name(&nm) = file_name;
+                       fname_len(&nm) = strlen(file_name);
+                       target_ui = ubifs_lookup(c, lost_found_ui, &nm);
+                       if (IS_ERR(target_ui)) {
+                               err = PTR_ERR(target_ui);
+                               if (err == -ENOENT)
+                                       break;
+                               goto free_ui;
+                       }
+                       kfree(target_ui);
+                       index++;
+               }
+
+               if (err != -ENOENT) {
+                       err = 0;
+                       kfree(ui);
+                       kfree(lost_found_ui);
+                       log_out(c, "Too many duplicated names(%u) in lost+found for inum %lu",
+                               index, file->inum);
+                       goto delete_file;
+               }
+
+               /* Try to recover disconnected file into lost+found. */
+               err = ubifs_link_recovery(c, lost_found_ui, ui, &nm);
+               if (err && err == -ENOSPC) {
+                       err = 0;
+                       log_out(c, "No free space to recover disconnected file");
+                       goto delete_file;
+               }
+               dbg_fsck("recover disconnected file %lu, in %s",
+                        file->inum, c->dev_name);
+
+free_ui:
+               kfree(ui);
+free_lost_found_ui:
+               kfree(lost_found_ui);
+               return err;
+       }
+
+       log_out(c, "No valid lost+found");
+
+delete_file:
+       if (fix_problem(c, DISCONNECTED_FILE_CANNOT_BE_RECOVERED, file))
+               err = delete_file(c, file);
+       return err;
+}
+
+/**
+ * handle_disonnected_files - Handle disconnected files.
+ * @c: UBIFS file-system description object
+ *
+ * This function tries to recover disonnected files into lost+found directory.
+ * If there is no free space left to recover the disconnected files, fsck may
+ * delete the files to make filesystem be consistent. Returns zero in case of
+ * success, a negative error code in case of failure.
+ */
+int handle_disonnected_files(struct ubifs_info *c)
+{
+       int err, ret = 0;
+       struct scanned_file *file;
+
+       while (!list_empty(&FSCK(c)->disconnected_files)) {
+               file = list_entry(FSCK(c)->disconnected_files.next,
+                                 struct scanned_file, list);
+
+               list_del(&file->list);
+               err = handle_disonnected_file(c, file);
+               if (err)
+                       ret = ret ? ret : err;
+               destroy_file_content(c, file);
+               kfree(file);
+       }
+
+       return ret;
+}
index 8e7e1e158757919b9cff62f79c3af3949c659986..916c97627d6a4318f954f8d32e05cbe1ac69dcd2 100644 (file)
@@ -69,6 +69,7 @@ static const struct fsck_problem problem_table[] = {
        {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"},       // LTAB_INCORRECT
        {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect index size"},   // INCORRECT_IDX_SZ
        {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Root dir is lost"},       // ROOT_DIR_NOT_FOUND
+       {PROBLEM_FIXABLE | PROBLEM_DROP_DATA, "Disconnected file cannot be recovered"}, // DISCONNECTED_FILE_CANNOT_BE_RECOVERED
 };
 
 static const char *get_question(const struct fsck_problem *problem,
@@ -96,6 +97,7 @@ static const char *get_question(const struct fsck_problem *problem,
        case FILE_HAS_NO_ENCRYPT:
        case FILE_ROOT_HAS_DENT:
        case DENTRY_IS_UNREACHABLE:
+       case DISCONNECTED_FILE_CANNOT_BE_RECOVERED:
                return "Delete it?";
        case FILE_HAS_INCONSIST_TYPE:
        case FILE_HAS_TOO_MANY_DENT:
@@ -292,6 +294,14 @@ static void print_problem(const struct ubifs_info *c,
                        problem->desc, c->calc_idx_sz, *calc_sz);
                break;
        }
+       case DISCONNECTED_FILE_CANNOT_BE_RECOVERED:
+       {
+               const struct scanned_file *file = (const struct scanned_file *)priv;
+
+               log_out(c, "problem: %s, ino %lu, size %llu", problem->desc,
+                       file->inum, file->ino.size);
+               break;
+       }
        default:
                log_out(c, "problem: %s", problem->desc);
                break;