diff mbox series

[RFC,mtd-utils,087/110] fsck.ubifs: Check and correct files' information

Message ID 20240607042615.2069840-88-chengzhihao1@huawei.com
State New
Delegated to: David Oberhollenzer
Headers show
Series Add fsck.ubifs support | expand

Commit Message

Zhihao Cheng June 7, 2024, 4:25 a.m. UTC
From: Xiang Yang <xiangyang3@huawei.com>

This is the 10/18 step of fsck. Check and handle inconsistent files, the
checking rule is same as rebuild mode which has been implemented in
check_and_correct_files, but the methods of handling are different:
 1. Correct the file information for safe mode, danger mode and normal
    mode with 'yes' answer, other modes will exit.

Signed-off-by: Xiang Yang <xiangyang3@huawei.com>
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
---
 ubifs-utils/fsck.ubifs/extract_files.c | 76 ++++++++++++++++++++++------------
 ubifs-utils/fsck.ubifs/fsck.ubifs.c    |  8 ++++
 ubifs-utils/fsck.ubifs/fsck.ubifs.h    |  2 +-
 ubifs-utils/fsck.ubifs/problem.c       | 16 +++++++
 4 files changed, 74 insertions(+), 28 deletions(-)
diff mbox series

Patch

diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index b24445be..c83d3774 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -1292,8 +1292,8 @@  reachable:
  * data nodes and truncation node. The calculated informaion will be used
  * to correct inode node.
  */
-static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
-				struct rb_root *file_tree)
+static int calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
+			       struct rb_root *file_tree)
 {
 	int nlink = 0;
 	bool corrupted_truncation = false;
@@ -1306,15 +1306,24 @@  static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
 
 	for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) {
 		xattr_file = rb_entry(node, struct scanned_file, rb);
+		dent_node = rb_entry(rb_first(&xattr_file->dent_nodes),
+				     struct scanned_dent_node, rb);
 
+		ubifs_assert(c, xattr_file->ino.is_xattr);
 		ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
-		calculate_file_info(c, xattr_file, file_tree);
+		xattr_file->calc_nlink = 1;
+		xattr_file->calc_size = xattr_file->ino.size;
+
+		file->calc_xcnt += 1;
+		file->calc_xsz += CALC_DENT_SIZE(dent_node->nlen);
+		file->calc_xsz += CALC_XATTR_BYTES(xattr_file->ino.size);
+		file->calc_xnms += dent_node->nlen;
 	}
 
 	if (file->inum == UBIFS_ROOT_INO) {
 		file->calc_nlink += 2;
 		file->calc_size += UBIFS_INO_NODE_SZ;
-		return;
+		return 0;
 	}
 
 	if (S_ISDIR(file->ino.mode)) {
@@ -1326,29 +1335,11 @@  static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
 		parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
 		if (!parent_file) {
 			ubifs_assert(c, 0);
-			return;
+			return 0;
 		}
 		parent_file->calc_nlink += 1;
 		parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
-		return;
-	}
-
-	if (file->ino.is_xattr) {
-		file->calc_nlink = 1;
-		file->calc_size = file->ino.size;
-
-		dent_node = rb_entry(rb_first(&file->dent_nodes),
-				     struct scanned_dent_node, rb);
-		parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
-		if (!parent_file) {
-			ubifs_assert(c, 0);
-			return;
-		}
-		parent_file->calc_xcnt += 1;
-		parent_file->calc_xsz += CALC_DENT_SIZE(dent_node->nlen);
-		parent_file->calc_xsz += CALC_XATTR_BYTES(file->ino.size);
-		parent_file->calc_xnms += dent_node->nlen;
-		return;
+		return 0;
 	}
 
 	for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) {
@@ -1359,7 +1350,7 @@  static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
 		parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
 		if (!parent_file) {
 			ubifs_assert(c, 0);
-			return;
+			return 0;
 		}
 		parent_file->calc_size += CALC_DENT_SIZE(dent_node->nlen);
 	}
@@ -1368,7 +1359,7 @@  static void calculate_file_info(struct ubifs_info *c, struct scanned_file *file,
 	if (!S_ISREG(file->ino.mode)) {
 		/* No need to verify i_size for symlink/sock/block/char/fifo. */
 		file->calc_size = file->ino.size;
-		return;
+		return 0;
 	}
 
 	/*
@@ -1452,10 +1443,22 @@  drop_data:
 		data_node = list_entry(drop_list.next, struct scanned_data_node,
 				       list);
 
+		if (FSCK(c)->mode != REBUILD_MODE) {
+			/*
+			 * Don't ask, inconsistent file correcting will be
+			 * asked in function correct_file_info().
+			 */
+			int err = delete_node(c, &data_node->key,
+				data_node->header.lnum, data_node->header.offs);
+			if (err)
+				return err;
+		}
 		list_del(&data_node->list);
 		rb_erase(&data_node->rb, &file->data_nodes);
 		kfree(data_node);
 	}
+
+	return 0;
 }
 
 /**
@@ -1490,6 +1493,7 @@  static int correct_file_info(struct ubifs_info *c, struct scanned_file *file)
 	    file->calc_size == file->ino.size)
 		return 0;
 
+	handle_invalid_file(c, FILE_IS_INCONSISTENT, file, NULL);
 	lnum = file->ino.header.lnum;
 	dbg_fsck("correct file(inum:%lu type:%s), nlink %u->%u, xattr cnt %u->%u, xattr size %u->%u, xattr names %u->%u, size %llu->%llu, at %d:%d, in %s",
 		 file->inum, file->ino.is_xattr ? "xattr" :
@@ -1537,7 +1541,9 @@  int check_and_correct_files(struct ubifs_info *c)
 	for (node = rb_first(tree); node; node = rb_next(node)) {
 		file = rb_entry(node, struct scanned_file, rb);
 
-		calculate_file_info(c, file, tree);
+		err = calculate_file_info(c, file, tree);
+		if (err)
+			return err;
 	}
 
 	for (node = rb_first(tree); node; node = rb_next(node)) {
@@ -1548,5 +1554,21 @@  int check_and_correct_files(struct ubifs_info *c)
 			return err;
 	}
 
+	if (list_empty(&FSCK(c)->disconnected_files))
+		return 0;
+
+	ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+	list_for_each_entry(file, &FSCK(c)->disconnected_files, list) {
+		err = calculate_file_info(c, file, tree);
+		if (err)
+			return err;
+
+		/* Reset disconnected file's nlink as one. */
+		file->calc_nlink = 1;
+		err = correct_file_info(c, file);
+		if (err)
+			return err;
+	}
+
 	return 0;
 }
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index fd4890a0..85a5baf0 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -460,6 +460,13 @@  static int do_fsck(void)
 		goto free_disconnected_files;
 	}
 
+	log_out(c, "Check and correct files");
+	err = check_and_correct_files(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:
@@ -504,6 +511,7 @@  int main(int argc, char *argv[])
 	 * Step 7: Update files' size
 	 * Step 8: Check and handle invalid files
 	 * Step 9: Check and handle unreachable files
+	 * Step 10: Check and correct 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 ba4771a3..d1423c01 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, DENTRY_IS_UNREACHABLE };
+       FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT };
 
 enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
 
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 0395a34f..e8f08606 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -59,6 +59,7 @@  static const struct fsck_problem problem_table[] = {
 	{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
+	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is inconsistent"},	// FILE_IS_INCONSISTENT
 };
 
 static const char *get_question(const struct fsck_problem *problem,
@@ -212,6 +213,21 @@  static void print_problem(const struct ubifs_info *c,
 			key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
 		break;
 	}
+	case FILE_IS_INCONSISTENT:
+	{
+		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
+		const struct scanned_file *file = ifp->file;
+
+		log_out(c, "problem: %s, ino %lu type %s, nlink %u xcnt %u xsz %u xnms %u size %llu, "
+			"should be nlink %u xcnt %u xsz %u xnms %u size %llu",
+			problem->desc, file->inum,
+			file->ino.is_xattr ? "xattr" : ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
+			file->ino.nlink, file->ino.xcnt, file->ino.xsz,
+			file->ino.xnms, file->ino.size,
+			file->calc_nlink, file->calc_xcnt, file->calc_xsz,
+			file->calc_xnms, file->calc_size);
+		break;
+	}
 	default:
 		log_out(c, "problem: %s", problem->desc);
 		break;