[RFC PATCH mtd-utils 086/110] fsck.ubifs: Check and handle unreachable files

From: Zhihao Cheng
Date: Fri Jun 07 2024 - 00:46:22 EST


This is the 9/18 step of fsck. Check and handle unreachable files, the
checking rule is same as rebuild mode which has been implemented in
file_is_reachable, but the methods of handling are different:
1. Move unreachable regular file into disconnected list, let subsequent
steps to handle them with lost+found.
2. Delete unreachable non-regular file.
3. Delete unreachable directory entries.

Signed-off-by: Zhihao Cheng <chengzhihao1@xxxxxxxxxx>
---
ubifs-utils/fsck.ubifs/check_files.c | 57 ++++++++++++++++++++++++++++++++++
ubifs-utils/fsck.ubifs/extract_files.c | 13 ++++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.c | 9 ++++++
ubifs-utils/fsck.ubifs/fsck.ubifs.h | 3 +-
ubifs-utils/fsck.ubifs/problem.c | 14 +++++++++
5 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/ubifs-utils/fsck.ubifs/check_files.c b/ubifs-utils/fsck.ubifs/check_files.c
index c5c606e1..2be96193 100644
--- a/ubifs-utils/fsck.ubifs/check_files.c
+++ b/ubifs-utils/fsck.ubifs/check_files.c
@@ -7,6 +7,7 @@

#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>

#include "linux_err.h"
#include "bitops.h"
@@ -442,3 +443,59 @@ int handle_invalid_files(struct ubifs_info *c)

return 0;
}
+
+/**
+ * handle_dentry_tree - Handle unreachable dentries and files.
+ * @c: UBIFS file-system description object
+ *
+ * This function iterates all directory entries and remove those unreachable
+ * ones. If file has no directory entries, it becomes unreachable:
+ * 1. If the unreachable file has non-regular type, delete it;
+ * 2. If the unreachable file has regular type, move it into the
+ * @FSCK(c)->disconnected_files.
+ * 'Unreachable' means that a directory entry can not be searched from '/'.
+ *
+ * Returns zero in case of success, a negative error code in case of failure.
+ */
+int handle_dentry_tree(struct ubifs_info *c)
+{
+ struct rb_node *node;
+ struct scanned_file *file;
+ struct rb_root *tree = &FSCK(c)->scanned_files;
+ LIST_HEAD(unreachable);
+
+ for (node = rb_first(tree); node; node = rb_next(node)) {
+ file = rb_entry(node, struct scanned_file, rb);
+
+ /*
+ * Since all xattr files are already attached to corresponding
+ * host file, there are only non-xattr files in the file tree.
+ */
+ ubifs_assert(c, !file->ino.is_xattr);
+ if (!file_is_reachable(c, file, tree))
+ list_add(&file->list, &unreachable);
+ }
+
+ while (!list_empty(&unreachable)) {
+ file = list_entry(unreachable.next, struct scanned_file, list);
+
+ list_del(&file->list);
+ if (S_ISREG(file->ino.mode)) {
+ /*
+ * Move regular type unreachable file into the
+ * @FSCK(c)->disconnected_files.
+ */
+ list_add(&file->list, &FSCK(c)->disconnected_files);
+ rb_erase(&file->rb, tree);
+ } else {
+ /* Delete non-regular type unreachable file. */
+ int err = delete_file(c, file);
+ if (err)
+ return err;
+ rb_erase(&file->rb, tree);
+ kfree(file);
+ }
+ }
+
+ return 0;
+}
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index 51b83b82..b24445be 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -1247,6 +1247,15 @@ retry:
dent_node = list_entry(path_list.next,
struct scanned_dent_node, list);

+ handle_invalid_file(c, DENTRY_IS_UNREACHABLE,
+ dent_node->file, dent_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum,
+ dent_node->header.offs);
+ if (err)
+ return err;
+ }
dbg_fsck("remove unreachable dentry %s, in %s",
c->encrypted && !file->ino.is_xattr ?
"<encrypted>" : dent_node->name, c->dev_name);
@@ -1260,6 +1269,10 @@ retry:
}

if (!rb_first(&file->dent_nodes)) {
+ if (S_ISREG(file->ino.mode))
+ handle_invalid_file(c, FILE_IS_DISCONNECTED, file, NULL);
+ else
+ handle_invalid_file(c, FILE_HAS_NO_DENT, file, NULL);
dbg_fsck("file %lu is unreachable, in %s", file->inum, c->dev_name);
return false;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 4b1b35b0..fd4890a0 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -453,6 +453,14 @@ static int do_fsck(void)
goto free_used_lebs;
}

+ log_out(c, "Check and handle unreachable files");
+ err = handle_dentry_tree(c);
+ if (err) {
+ exit_code |= FSCK_ERROR;
+ goto free_disconnected_files;
+ }
+
+free_disconnected_files:
destroy_file_list(c, &FSCK(c)->disconnected_files);
free_used_lebs:
kfree(FSCK(c)->used_lebs);
@@ -495,6 +503,7 @@ int main(int argc, char *argv[])
* Step 6: Traverse tnc and construct files
* Step 7: Update files' size
* Step 8: Check and handle invalid files
+ * Step 9: Check and handle unreachable files
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 34d300b2..ba4771a3 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -43,7 +43,7 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
FILE_HAS_0_NLINK_INODE, FILE_HAS_INCONSIST_TYPE, FILE_HAS_TOO_MANY_DENT,
FILE_SHOULDNT_HAVE_DATA, FILE_HAS_NO_DENT, XATTR_HAS_NO_HOST,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
- FILE_ROOT_HAS_DENT };
+ FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE };

enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };

@@ -318,5 +318,6 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c);
int traverse_tnc_and_construct_files(struct ubifs_info *c);
void update_files_size(struct ubifs_info *c);
int handle_invalid_files(struct ubifs_info *c);
+int handle_dentry_tree(struct ubifs_info *c);

#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 9222cba4..0395a34f 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -58,6 +58,7 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Encrypted file has no encryption information"}, // FILE_HAS_NO_ENCRYPT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is disconnected(regular file without dentries)"}, // FILE_IS_DISCONNECTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Root dir should not have a dentry"}, // FILE_ROOT_HAS_DENT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"}, // DENTRY_IS_UNREACHABLE
};

static const char *get_question(const struct fsck_problem *problem,
@@ -84,6 +85,7 @@ static const char *get_question(const struct fsck_problem *problem,
case XATTR_HAS_WRONG_HOST:
case FILE_HAS_NO_ENCRYPT:
case FILE_ROOT_HAS_DENT:
+ case DENTRY_IS_UNREACHABLE:
return "Delete it?";
case FILE_HAS_INCONSIST_TYPE:
case FILE_HAS_TOO_MANY_DENT:
@@ -198,6 +200,18 @@ static void print_problem(const struct ubifs_info *c,
host->ino.is_xattr ? "(xattr)" : "");
break;
}
+ case DENTRY_IS_UNREACHABLE:
+ {
+ const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+ const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;
+
+ log_out(c, "problem: %s, ino %lu, unreachable dentry %s, type %s%s",
+ problem->desc, ifp->file->inum,
+ c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name,
+ ubifs_get_type_name(dent_node->type),
+ key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
--
2.13.6