[Ocfs2-tools-commits] zab commits r1159 - trunk/fsck.ocfs2
svn-commits@oss.oracle.com
svn-commits at oss.oracle.com
Tue Feb 21 17:03:17 CST 2006
Author: zab
Signed-off-by: mfasheh
Date: 2006-02-21 17:03:16 -0600 (Tue, 21 Feb 2006)
New Revision: 1159
Modified:
trunk/fsck.ocfs2/pass2.c
Log:
o more carefully fix up dirents.. this should fix up cases where fsck hung
failing to update the current dirent.
Signed-off-by: mfasheh
Modified: trunk/fsck.ocfs2/pass2.c
===================================================================
--- trunk/fsck.ocfs2/pass2.c 2006-02-21 00:53:38 UTC (rev 1158)
+++ trunk/fsck.ocfs2/pass2.c 2006-02-21 23:03:16 UTC (rev 1159)
@@ -183,16 +183,49 @@
return ret;
}
-/* we just copy ext2's fixing behaviour here. 'left' is the number of bytes
- * from the start of the dirent struct to the end of the block. */
+static int dirent_leaves_partial(struct ocfs2_dir_entry *dirent, int left)
+{
+ left -= dirent->rec_len;
+ return (left > 0 && left < OCFS2_DIR_MEMBER_LEN);
+}
+
+/*
+ * The caller has found that either of rec_len or name_len are garbage. The
+ * caller trusts us to fix them up in place and will be checking them again
+ * before proceeding. We have to update the lengths to make forward progress.
+ * 'left' is the number of bytes from the start of this dirent struct that
+ * remain in the block.
+ *
+ * We're called for invalid dirents, and having a dirent
+ * that leaves a partial dirent at the end of the block is considered invalid,
+ * and we pad out partials at the end of this call so we can't be called here
+ * with left < OCFS2_DIR_MEMBER_LEN.
+ *
+ * we're pretty limited in the repairs we can make:
+ *
+ * - We can't just set name_len if rec_len looks valid, we might guess
+ * name_len wrong and create a bogus file name.
+ * - we can't just set rec_len based on name_len. rec_len could have
+ * included an arbitrary part of the name from a previously freed dirent.
+ */
static void fix_dirent_lengths(struct ocfs2_dir_entry *dirent,
int left, struct ocfs2_dir_entry *prev,
int *flags)
{
- /* special casing an empty dirent that doesn't include the
- * extra rec_len alignment */
- if ((left >= OCFS2_DIR_MEMBER_LEN) &&
- (dirent->rec_len == OCFS2_DIR_MEMBER_LEN)) {
+ /*
+ * as described above we can't reconstruct either value if it is
+ * complete nonsense. We can only proceed if we can work off of
+ * one that is kind of valid looking.
+ * name_len could well be 0 from the dirent being cleared.
+ */
+ if (dirent->rec_len < OCFS2_DIR_MEMBER_LEN ||
+ (dirent->rec_len > left ||
+ dirent->name_len > left))
+ goto wipe;
+
+ /* if we see a dirent with no file name then we remove it by
+ * shifting the remaining dirents forward */
+ if ((dirent->rec_len == OCFS2_DIR_MEMBER_LEN)) {
char *cp = (char *)dirent;
left -= dirent->rec_len;
memmove(cp, cp + dirent->rec_len, left);
@@ -200,28 +233,51 @@
goto out;
}
- /* clamp rec_len to the remainder of the block if name_len
- * is within the block */
- if (dirent->rec_len > left && dirent->name_len <= left) {
- dirent->rec_len = left;
+ /* if rec_len just appears to be mis-rounded in a way that doesn't
+ * affect following dirents then we can probably save this dirent */
+ if (OCFS2_DIR_REC_LEN(dirent->name_len) != dirent->rec_len &&
+ OCFS2_DIR_REC_LEN(dirent->name_len) ==
+ OCFS2_DIR_REC_LEN(dirent->rec_len)) {
+ dirent->rec_len = OCFS2_DIR_REC_LEN(dirent->name_len);
+ left -= dirent->rec_len;
goto out;
}
- /* from here on in we're losing directory entries by adding their
- * space onto previous entries. If we think we can trust this
- * entry's length we leave potential later entries intact.
- * otherwise we consume all the space left in the block */
- if (prev && ((dirent->rec_len & OCFS2_DIR_ROUND) == 0) &&
- (dirent->rec_len <= left)) {
- prev->rec_len += left;
- } else {
- dirent->rec_len = left;
+ /* if name_len is too far off, however, we're going to lose this
+ * dirent.. we might be able to just lose this one dirent if rec_len
+ * appears to be intact. */
+ if ((dirent->rec_len & OCFS2_DIR_ROUND) == 0 &&
+ !dirent_leaves_partial(dirent, left)) {
+ left -= dirent->rec_len;
dirent->name_len = 0;
dirent->inode = 0;
dirent->file_type = OCFS2_FT_UNKNOWN;
+ goto out;
}
+ /*
+ * if we can't trust rec_len, however, then we don't know where the
+ * next dirent might begin. We've lost the trail of dirents created by
+ * the file system and run the risk of parsing file names as dirents.
+ * So we're forced to wipe the block and leave the rest to lost+found.
+ */
+wipe:
+ dirent->rec_len = left;
+ dirent->name_len = 0;
+ dirent->inode = 0;
+ dirent->file_type = OCFS2_FT_UNKNOWN;
+ left = 0;
out:
+
+ /*
+ * rec_len must be valid and left must reflect the space *after* the
+ * current dirent by this point. if there isn't enough room for
+ * another dirent after the one we've just repaired then we tack the
+ * remaining space onto the current dirent.
+ */
+ if (dirent_leaves_partial(dirent, left))
+ dirent->rec_len += left;
+
*flags |= OCFS2_DIRENT_CHANGED;
}
@@ -525,12 +581,13 @@
return ret;
}
-static int corrupt_dirent(struct ocfs2_dir_entry *dirent, int left)
+static int corrupt_dirent_lengths(struct ocfs2_dir_entry *dirent, int left)
{
if ((dirent->rec_len >= OCFS2_DIR_REC_LEN(1)) &&
((dirent->rec_len & OCFS2_DIR_ROUND) == 0) &&
(dirent->rec_len <= left) &&
- (OCFS2_DIR_REC_LEN(dirent->name_len) <= dirent->rec_len))
+ (OCFS2_DIR_REC_LEN(dirent->name_len) <= dirent->rec_len) &&
+ !dirent_leaves_partial(dirent, left))
return 0;
return 1;
@@ -579,7 +636,8 @@
/* if we can't trust this dirent then fix it up or skip
* the whole block */
- if (corrupt_dirent(dirent, dd->fs->fs_blocksize - offset)) {
+ if (corrupt_dirent_lengths(dirent,
+ dd->fs->fs_blocksize - offset)) {
if (!prompt(dd->ost, PY, PR_DIRENT_LENGTH,
"Directory inode %"PRIu64" "
"corrupted in logical block %"PRIu64" "
More information about the Ocfs2-tools-commits
mailing list