[Ocfs2-tools-devel] [PATCH 04/12] fsck.ocfs2: Add checking of directory trailers.

Joel Becker joel.becker at oracle.com
Mon Dec 29 19:23:40 PST 2008


If we have trailers, fsck needs to know about them.

In pass2, we don't treat the trailer of a directory block as a regular
dirent.  We fix the trailer's compatibility fields if they are broken.

Signed-off-by: Joel Becker <joel.becker at oracle.com>
---
 fsck.ocfs2/fsck.ocfs2.checks.8.in |   36 ++++++++++++++++
 fsck.ocfs2/pass2.c                |   83 +++++++++++++++++++++++++++++++++---
 fsck.ocfs2/pass3.c                |    1 -
 3 files changed, 112 insertions(+), 8 deletions(-)

diff --git a/fsck.ocfs2/fsck.ocfs2.checks.8.in b/fsck.ocfs2/fsck.ocfs2.checks.8.in
index 725b90c..22982c0 100644
--- a/fsck.ocfs2/fsck.ocfs2.checks.8.in
+++ b/fsck.ocfs2/fsck.ocfs2.checks.8.in
@@ -598,6 +598,42 @@ Answering yes will try to repair the directory entry.  This runs a very good
 chance of invalidating all the entries in the directory block.  Orphaned
 inodes may appear in lost+found.
 
+.SS "DIR_TRAILER_INODE"
+A directory block trailer is a fake directory entry at the end of the
+block.  The trailer has compatibility fields for when it is viewed as a
+directory entry.  The inode field must be zero.
+
+Answering yes will set the inode field to zero.
+
+.SS "DIR_TRAILER_NAME_LEN"
+A directory block trailer is a fake directory entry at the end of the
+block.  The trailer has compatibility fields for when it is viewed as a
+directory entry.  The name length field must be zero.
+
+Answering yes will set the name length field to zero.
+
+.SS "DIR_TRAILER_REC_LEN"
+A directory block trailer is a fake directory entry at the end of the
+block.  The trailer has compatibility fields for when it is viewed as a
+directory entry.  The record length field must be equal to the size of
+the trailer.
+
+Answering yes will set the record length field to the size of the trailer.
+
+.SS "DIR_TRAILER_BLKNO"
+A directory block trailer is a fake directory entry at the end of the
+block.  The self-referential block number is incorrect.
+
+Answering yes will set the block number to the correct block on disk.
+
+.SS "DIR_TRAILER_PARENT_INODE"
+A directory block trailer is a fake directory entry at the end of the
+block.  It has a pointer to the directory inode it belongs to.  This
+pointer is incorrect.
+
+Answering yes will set the parent inode pointer to the inode referencing
+this directory block.
+
 \" pass3.c
 
 .SS "ROOT_DIR_MISSING"
diff --git a/fsck.ocfs2/pass2.c b/fsck.ocfs2/pass2.c
index 8239c10..b3a522b 100644
--- a/fsck.ocfs2/pass2.c
+++ b/fsck.ocfs2/pass2.c
@@ -193,6 +193,68 @@ out:
 	return ret;
 }
 
+/*
+ * The directory trailer has compatibility fields so it can be treated
+ * as an empty (deleted) dirent.  We need to make sure those are correct.
+ */
+static void fix_dir_trailer(o2fsck_state *ost, o2fsck_dirblock_entry *dbe,
+			    struct ocfs2_dir_block_trailer *trailer,
+			    unsigned int *flags)
+{
+	if (trailer->db_compat_inode &&
+	    prompt(ost, PY, PR_DIR_TRAILER_INODE,
+		   "Directory block trailer for logical block %"PRIu64" "
+		   "physcal block %"PRIu64" in directory inode %"PRIu64" "
+		   "has a non-zero inode number.  Clear it?",
+		   dbe->e_blkcount, dbe->e_blkno, dbe->e_ino)) {
+		trailer->db_compat_inode = 0;
+		*flags |= OCFS2_DIRENT_CHANGED;
+	}
+
+	if (trailer->db_compat_name_len &&
+	    prompt(ost, PY, PR_DIR_TRAILER_NAME_LEN,
+		   "Directory block trailer for logical block %"PRIu64" "
+		   "physcal block %"PRIu64" in directory inode %"PRIu64" "
+		   "has a non-zero name_len.  Clear it?",
+		   dbe->e_blkcount, dbe->e_blkno, dbe->e_ino)) {
+		trailer->db_compat_name_len = 0;
+		*flags |= OCFS2_DIRENT_CHANGED;
+	}
+
+	if ((trailer->db_compat_rec_len !=
+	     sizeof(struct ocfs2_dir_block_trailer)) &&
+	    prompt(ost, PY, PR_DIR_TRAILER_REC_LEN,
+		   "Directory block trailer for logical block %"PRIu64" "
+		   "physcal block %"PRIu64" in directory inode %"PRIu64" "
+		   "has an invalid rec_len.  Fix it?",
+		   dbe->e_blkcount, dbe->e_blkno, dbe->e_ino)) {
+		trailer->db_compat_rec_len = 0;
+		*flags |= OCFS2_DIRENT_CHANGED;
+	}
+
+	if ((trailer->db_blkno != dbe->e_blkno) &&
+	    prompt(ost, PY, PR_DIR_TRAILER_BLKNO,
+		   "Directory block trailer for logical block %"PRIu64" "
+		   "physcal block %"PRIu64" in directory inode %"PRIu64" "
+		   "has an invalid db_blkno of %"PRIu64".  Fix it?",
+		   dbe->e_blkcount, dbe->e_blkno, dbe->e_ino,
+		   trailer->db_blkno)) {
+		trailer->db_blkno = dbe->e_blkno;
+		*flags |= OCFS2_DIRENT_CHANGED;
+	}
+
+	if ((trailer->db_parent_dinode != dbe->e_ino) &&
+	    prompt(ost, PY, PR_DIR_TRAILER_PARENT_INODE,
+		   "Directory block trailer for logical block %"PRIu64" "
+		   "physcal block %"PRIu64" in directory inode %"PRIu64" "
+		   "claims it belongs to inoe %"PRIu64".  Fix it?",
+		   dbe->e_blkcount, dbe->e_blkno, dbe->e_ino,
+		   trailer->db_parent_dinode)) {
+		trailer->db_parent_dinode = dbe->e_ino;
+		*flags |= OCFS2_DIRENT_CHANGED;
+	}
+}
+
 static int dirent_leaves_partial(struct ocfs2_dir_entry *dirent, int left)
 {
 	left -= dirent->rec_len;
@@ -612,7 +674,7 @@ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe,
 {
 	struct dirblock_data *dd = priv_data;
 	struct ocfs2_dir_entry *dirent, *prev = NULL;
-	unsigned int offset = 0, ret_flags = 0;
+	unsigned int offset = 0, ret_flags = 0, end = dd->fs->fs_blocksize;
 	struct ocfs2_dinode *di = (struct ocfs2_dinode *)dd->inoblock_buf; 
 	errcode_t ret = 0;
 
@@ -663,9 +725,12 @@ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe,
 				dbe->e_blkno);
 			goto out;
 		}
+
+		if (ocfs2_dir_has_trailer(dd->fs, di))
+			end = ocfs2_dir_trailer_blk_off(dd->fs);
 	}
 
-	while (offset < dd->fs->fs_blocksize) {
+	while (offset < end) {
 		dirent = (struct ocfs2_dir_entry *)(dd->dirblock_buf + offset);
 
 		verbosef("checking dirent offset %d, rec_len %"PRIu16" "
@@ -679,7 +744,7 @@ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe,
 		/* if we can't trust this dirent then fix it up or skip
 		 * the whole block */
 		if (corrupt_dirent_lengths(dirent,
-					   dd->fs->fs_blocksize - offset)) {
+					   end - offset)) {
 			if (!prompt(dd->ost, PY, PR_DIRENT_LENGTH,
 				    "Directory inode %"PRIu64" "
 				    "corrupted in logical block %"PRIu64" "
@@ -691,8 +756,7 @@ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe,
 
 			/* we edit the dirent in place so we try to parse
 			 * it again after fixing it */
-			fix_dirent_lengths(dirent,
-					   dd->fs->fs_blocksize - offset,
+			fix_dirent_lengths(dirent, end - offset,
 					   prev, &ret_flags);
 			continue;
 
@@ -708,8 +772,7 @@ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe,
 		 * XXX should verify that ocfs2 reclaims entries like that.
 		 */
 		ret = fix_dirent_dots(dd->ost, dbe, dirent, offset, 
-					     dd->fs->fs_blocksize - offset,
-					     &ret_flags);
+				      end - offset, &ret_flags);
 		if (ret)
 			goto out;
 		if (dirent->inode == 0)
@@ -752,6 +815,12 @@ next:
 		prev = dirent;
 	}
 
+	if (ocfs2_dir_has_trailer(dd->fs, di))
+		fix_dir_trailer(dd->ost, dbe,
+				ocfs2_dir_trailer_from_block(dd->fs,
+							     dd->dirblock_buf),
+				&ret_flags);
+
 	if (ret_flags & OCFS2_DIRENT_CHANGED) {
 		if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) {
 			memcpy(dd->inoblock_buf, dd->dirblock_buf,
diff --git a/fsck.ocfs2/pass3.c b/fsck.ocfs2/pass3.c
index 7c9d2bd..457f312 100644
--- a/fsck.ocfs2/pass3.c
+++ b/fsck.ocfs2/pass3.c
@@ -280,7 +280,6 @@ void o2fsck_reconnect_file(o2fsck_state *ost, uint64_t inode)
 	if (ret)
 		goto out;
 
-	/* XXX I gotta say, ocfs2_link_and_expand() seems pretty reasonable */
 	ret = ocfs2_link(ost->ost_fs, ost->ost_lostfound_ino, iname, inode,
 			 type);
 	if (ret) {
-- 
1.5.6.5




More information about the Ocfs2-tools-devel mailing list