[Ocfs2-tools-devel] [PATCH 7/9] fsck.ocfs2: Fix feature bits, handle bad first journal.

Joel Becker joel.becker at oracle.com
Tue Sep 9 17:57:14 PDT 2008


fsck.ocfs2 needs to make the feature bits consistent across all
journals.

While we're there, we can fix the fact that fsck.ocfs2 implicitly trusts
the first journal file.  If the first journal is corrupt, it exits and
refuses to repair the filesystem.

Instead, we create a two-pass journal check scheme.  In the first pass,
all journals are checked.  If we can find a sane disposition from one of
them, we can then go about fixing all of them.

Included in this patch is fixing journal superblock feature bits.

Signed-off-by: Joel Becker <joel.becker at oracle.com>
---
 fsck.ocfs2/journal.c |  310 +++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 271 insertions(+), 39 deletions(-)

diff --git a/fsck.ocfs2/journal.c b/fsck.ocfs2/journal.c
index c285c7f..72f834d 100644
--- a/fsck.ocfs2/journal.c
+++ b/fsck.ocfs2/journal.c
@@ -724,85 +724,317 @@ out:
 	return ret;
 }
 
-static errcode_t check_journal_super(ocfs2_filesys *fs,
-				     ocfs2_cached_inode *ci)
+struct journal_check_info {
+	errcode_t		i_error;
+	uint32_t		i_clusters;
+	ocfs2_fs_options	i_features;
+};
+
+struct journal_check_context {
+	int				jc_max_slots;
+	int				jc_this_slot;
+	uint32_t			jc_max_clusters; /* Size of the
+							    largest journal
+							    found */
+	ocfs2_fs_options		jc_max_features; /* Union all
+							    features in
+							    good journals */
+	struct journal_check_info	*jc_info;	/* One per slot */
+};
+
+static errcode_t check_journals_func(o2fsck_state *ost,
+				     ocfs2_cached_inode *ci,
+				     struct journal_check_context *jc)
 {
-	errcode_t ret;
+	errcode_t err, ret;
+	ocfs2_filesys *fs = ost->ost_fs;
 	uint64_t contig;
 	uint64_t blkno;
 	char *buf = NULL;
+	journal_superblock_t *jsb;
+	struct journal_check_info *ji = &(jc->jc_info[jc->jc_this_slot]);
 
 	ret = ocfs2_malloc_blocks(fs->fs_io, 1, &buf);
 	if (ret)
 		goto out;
 
-	ret = ocfs2_extent_map_get_blocks(ci, 0, 1, &blkno, &contig, NULL);
-	if (ret)
+	err = ocfs2_extent_map_get_blocks(ci, 0, 1, &blkno, &contig, NULL);
+	if (err) {
+		ji->i_error = err;
 		goto out;
+	}
+
+	ji->i_clusters = ci->ci_inode->i_clusters;
+
+	err = ocfs2_read_journal_superblock(fs, blkno, buf);
+	if (err) {
+		ji->i_error = err;
+		goto out;
+	}
+
+	jsb = (journal_superblock_t *)buf;
+	ji->i_features.opt_compat = jsb->s_feature_compat;
+	ji->i_features.opt_ro_compat = jsb->s_feature_ro_compat;
+	ji->i_features.opt_incompat = jsb->s_feature_incompat;
+
+	if (!ji->i_clusters) {
+		ji->i_error = OCFS2_ET_JOURNAL_TOO_SMALL;
+		goto out;
+	}
+
+	jc->jc_max_clusters = ocfs2_max(jc->jc_max_clusters,
+					ci->ci_inode->i_clusters);
+	jc->jc_max_features.opt_compat |= jsb->s_feature_compat;
+	jc->jc_max_features.opt_ro_compat |= jsb->s_feature_ro_compat;
+	jc->jc_max_features.opt_incompat |= jsb->s_feature_incompat;
+	ji->i_error = 0;
 
-	ret = ocfs2_read_journal_superblock(fs, blkno, buf);
 out:
+	if (buf)
+		ocfs2_free(&buf);
+
 	return ret;
 }
 
-/* When we remove slot in tunefs.ocfs2, there may be some panic and
- * we may corrupt some journal files, so we have to check whether the
- * journal file is corrupted and recreate it.
- */
-errcode_t o2fsck_check_journals(o2fsck_state *ost)
+static errcode_t fix_journals_func(o2fsck_state *ost,
+				   ocfs2_cached_inode *ci,
+				   struct journal_check_context *jc)
+{
+	errcode_t err, ret = 0;
+	ocfs2_filesys *fs = ost->ost_fs;
+	char fname[OCFS2_MAX_FILENAME_LEN];
+	struct journal_check_info *ji = &(jc->jc_info[jc->jc_this_slot]);
+	uint32_t min_clusters =
+			ocfs2_clusters_in_bytes(fs,
+						OCFS2_MIN_JOURNAL_SIZE);
+
+	ocfs2_sprintf_system_inode_name(fname, OCFS2_MAX_FILENAME_LEN,
+					JOURNAL_SYSTEM_INODE,
+					jc->jc_this_slot);
+
+	if (ji->i_error &&
+	    (ji->i_error != OCFS2_ET_JOURNAL_TOO_SMALL) &&
+	    (ji->i_error != OCFS2_ET_UNSUPP_FEATURE) &&
+	    (ji->i_error != OCFS2_ET_RO_UNSUPP_FEATURE)) {
+		if (prompt(ost, PY, PR_JOURNAL_FILE_INVALID,
+			    "journal file %s is invalid, regenerate it?",
+			    fname)) {
+			err = ocfs2_make_journal(fs, ci->ci_blkno,
+						 jc->jc_max_clusters,
+						 &jc->jc_max_features);
+		}
+
+		ji->i_error = err;
+		goto out;
+	}
+
+	if ((ji->i_error == OCFS2_ET_UNSUPP_FEATURE) ||
+	    (ji->i_error == OCFS2_ET_RO_UNSUPP_FEATURE)) {
+		if (prompt(ost, PN, PR_JOURNAL_FILE_INVALID,
+			   "journal file %s has unknown features.  "
+			   "However, other journals have only known "
+			   "features, so this is likely a corruption.  "
+			   "If you think your filesystem may be newer "
+			   "than this version of fsck.ocfs2, say N here "
+			   "and grab the latest version of fsck.ocfs2.  "
+			   "Reset the journal features to match other "
+			   "journals?",
+			   fname)) {
+			err = ocfs2_make_journal(fs, ci->ci_blkno,
+						 ocfs2_max(ji->i_clusters,
+							   min_clusters),
+						 &jc->jc_max_features);
+			if (!err) {
+				ji->i_features.opt_compat =
+					jc->jc_max_features.opt_compat;
+				ji->i_features.opt_ro_compat =
+					jc->jc_max_features.opt_ro_compat;
+				ji->i_features.opt_incompat =
+					jc->jc_max_features.opt_incompat;
+			}
+			ji->i_error = err;
+		}
+	} else if ((ji->i_features.opt_compat !=
+		    jc->jc_max_features.opt_compat) ||
+		   (ji->i_features.opt_ro_compat !=
+		    jc->jc_max_features.opt_ro_compat) ||
+		   (ji->i_features.opt_incompat !=
+		    jc->jc_max_features.opt_incompat)) {
+		if (prompt(ost, PY, PR_JOURNAL_FILE_INVALID,
+			   "journal file %s is missing features that "
+			   "are set on other journal files.  Set these "
+			   "features?",
+			   fname)) {
+			err = ocfs2_make_journal(fs, ci->ci_blkno,
+						 ocfs2_max(ji->i_clusters,
+							   min_clusters),
+						 &jc->jc_max_features);
+			if (!err) {
+				ji->i_features.opt_compat =
+					jc->jc_max_features.opt_compat;
+				ji->i_features.opt_ro_compat =
+					jc->jc_max_features.opt_ro_compat;
+				ji->i_features.opt_incompat =
+					jc->jc_max_features.opt_incompat;
+			}
+			ji->i_error = err;
+		}
+	}
+
+	if (ji->i_clusters != jc->jc_max_clusters) {
+		if (prompt(ost, PY, PR_JOURNAL_FILE_INVALID,
+			   "journal file %s is too small, extend it?",
+			   fname)) {
+			err = ocfs2_make_journal(fs, ci->ci_blkno,
+						 jc->jc_max_clusters,
+						 &ji->i_features);
+			ji->i_error = err;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static errcode_t check_journal_walk(o2fsck_state *ost,
+				    errcode_t (*func)(o2fsck_state *ost,
+						      ocfs2_cached_inode *ci,
+						      struct journal_check_context *jc),
+				    struct journal_check_context *jc)
 {
 	errcode_t ret = 0;
 	uint64_t blkno;
-	uint32_t num_clusters = 0;
 	ocfs2_filesys *fs = ost->ost_fs;
-	char fname[OCFS2_MAX_FILENAME_LEN];
 	uint16_t i, max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
 	ocfs2_cached_inode *ci = NULL;
-	ocfs2_fs_options features = {
-		.opt_incompat = 0,
-	};
 
 	for (i = 0; i < max_slots; i++) {
 		ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, i,
 						&blkno);
 		if (ret)
-			goto out;
+			break;
 
 		ret = ocfs2_read_cached_inode(fs, blkno, &ci);
 		if (ret)
-			goto out;
+			break;
 
-		if (ci->ci_inode->i_clusters > 0) {
-			/* check whether the file contains valid super block. */
-			ret = check_journal_super(fs, ci);
-			if (!ret) {
-				/* record the valid cluster size. */
-				num_clusters = ci->ci_inode->i_clusters;
-				continue;
-			}
+		jc->jc_this_slot = i;
+		ret = func(ost, ci, jc);
+		if (ret)
+			break;
+	}
+
+	if (ci)
+		ocfs2_free_cached_inode(fs, ci);
+
+	return ret;
+}
+
+errcode_t o2fsck_check_journals(o2fsck_state *ost)
+{
+	errcode_t ret;
+	int i;
+	ocfs2_filesys *fs = ost->ost_fs;
+	int have_one_good_journal = 0, problem_is_consistent = 1;
+	errcode_t known_problem = 0;
+	struct journal_check_context jc = {
+		.jc_max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots,
+	};
+	struct journal_check_info *ji;
+
+	ret = ocfs2_malloc0(sizeof(struct journal_check_info) *
+			    jc.jc_max_slots,
+			    &jc.jc_info);
+	if (ret) {
+		com_err(whoami, ret, "while checking journals");
+		goto out;
+	}
+
+	ret = check_journal_walk(ost, check_journals_func, &jc);
+	if (ret) {
+		com_err(whoami, ret, "while checking journals");
+		goto out;
+	}
+
+	/*
+	 * We now know the state of all our journals.  If we have at least
+	 * one good journal, we have a sane state to fix the others from.
+	 * We require all our journals to have identical configuration.
+	 * Any inconsistencies (invalid size, bad feature flags) are
+	 * probably corruption or a failed tunefs.
+	 *
+	 * If we don't have a good journal, but all the journals have the
+	 * exact same problem, we may be able to handle it as well.  We
+	 * currently know how to handle these problems:
+	 *
+	 * JOURNAL_TOO_SMALL
+	 *
+	 * We simply allocate a default journal size.
+	 *
+	 * UNSUPP_FEATURE & RO_UNSUPP_FEATURE
+	 *
+	 * If one journal has an unsupported feature bit set, it's probably
+	 * corruption.  If all the journals have the exact same feature
+	 * bit set, it's certainly a feature we don't understand, and we
+	 * want the user to upgrade their fsck.
+	 */
+	for (i = 0; i < jc.jc_max_slots; i++) {
+		ji = &jc.jc_info[i];
+
+		if (!ji->i_error) {
+			have_one_good_journal = 1;
+			continue;
 		}
 
-		if (num_clusters == 0) {
-			/* none of the journal is valid, servere errors. */
-			ret = OCFS2_ET_JOURNAL_TOO_SMALL;
-			goto out;
+		if ((ji->i_error != OCFS2_ET_JOURNAL_TOO_SMALL) &&
+		    (ji->i_error != OCFS2_ET_UNSUPP_FEATURE) &&
+		    (ji->i_error != OCFS2_ET_RO_UNSUPP_FEATURE)) {
+			problem_is_consistent = 0;
+			continue;
 		}
 
-		sprintf(fname,
-			ocfs2_system_inodes[JOURNAL_SYSTEM_INODE].si_name, i);
-		if (!prompt(ost, PY, PR_JOURNAL_FILE_INVALID,
-			    "journal file %s is invalid, regenerate it?",
-			    fname))
+		if (known_problem) {
+			if (known_problem != ji->i_error)
+				problem_is_consistent = 0;
 			continue;
+		}
 
-		ret = ocfs2_make_journal(fs, blkno, num_clusters, &features);
-		if (ret)
+		known_problem = ji->i_error;
+	}
+
+	if (!have_one_good_journal) {
+		if (!problem_is_consistent || !known_problem) {
+			ret = jc.jc_info[0].i_error;
+			com_err(whoami, ret, "while checking journals");
+			goto out;
+		}
+		if ((known_problem == OCFS2_ET_UNSUPP_FEATURE) ||
+		    (known_problem == OCFS2_ET_RO_UNSUPP_FEATURE)) {
+			com_err(whoami, known_problem,
+				"on all journals.  Please upgrade to the "
+				"latest version of fsck.ocfs2");
+			ret = known_problem;
+			goto out;
+		}
+
+		if (known_problem != OCFS2_ET_JOURNAL_TOO_SMALL) {
+			ret = known_problem;
+			com_err(whoami, ret, "for all journals");
 			goto out;
+		}
+
+		/* Force a valid cluster count for the journals */
+		jc.jc_max_clusters =
+			ocfs2_clusters_in_bytes(fs,
+						OCFS2_MIN_JOURNAL_SIZE);
 	}
 
+	ret = check_journal_walk(ost, fix_journals_func, &jc);
+
 out:
-	if (ci)
-		ocfs2_free_cached_inode(fs, ci);
+	if (jc.jc_info)
+		ocfs2_free(&jc.jc_info);
+
 	return ret;
 }
 
-- 
1.5.6.3




More information about the Ocfs2-tools-devel mailing list