[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