[Cluster-devel] [gfs2-utils PATCH 16/47] fsck.gfs2: fix leaf blocks, don't try to patch the hash table
Bob Peterson
rpeterso at redhat.com
Tue May 14 16:21:39 UTC 2013
Before this patch, when we detected a bad leaf block, fsck.gfs2 would
try to patch the hash table. That's very wrong, because the hash table
needs to be on nice power-of-two boundaries. This patch changes the
code so that the hash table is actually repaired.
rhbz#902920
---
gfs2/fsck/metawalk.c | 135 ++++++++++++++++++---------------------------------
gfs2/fsck/metawalk.h | 3 +-
gfs2/fsck/pass1.c | 14 ++++++
gfs2/fsck/pass2.c | 9 +++-
4 files changed, 72 insertions(+), 89 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 73bdba0..4cd712e 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -480,52 +480,14 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
return 0;
}
-/* warn_and_patch - Warn the user of an error and ask permission to fix it
- * Process a bad leaf pointer and ask to repair the first time.
- * The repair process involves extending the previous leaf's entries
- * so that they replace the bad ones. We have to hack up the old
- * leaf a bit, but it's better than deleting the whole directory,
- * which is what used to happen before. */
-static int warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
- uint64_t *bad_leaf, uint64_t old_leaf,
- uint64_t first_ok_leaf, int pindex, const char *msg)
-{
- int okay_to_fix = 0;
-
- if (*bad_leaf != *leaf_no) {
- log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
- " (0x%llx) %s.\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)*leaf_no,
- (unsigned long long)*leaf_no, msg);
- }
- if (*leaf_no == *bad_leaf ||
- (okay_to_fix = query( _("Attempt to patch around it? (y/n) ")))) {
- if (valid_block(ip->i_sbd, old_leaf))
- gfs2_put_leaf_nr(ip, pindex, old_leaf);
- else
- gfs2_put_leaf_nr(ip, pindex, first_ok_leaf);
- log_err( _("Directory Inode %llu (0x%llx) repaired.\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr);
- } else
- log_err( _("Bad leaf left in place.\n"));
- *bad_leaf = *leaf_no;
- *leaf_no = old_leaf;
- return okay_to_fix;
-}
-
/**
* check_leaf - check a leaf block for errors
* Reads in the leaf block
* Leaves the buffer around for further analysis (caller must brelse)
*/
static int check_leaf(struct gfs2_inode *ip, int lindex,
- struct metawalk_fxns *pass, int *ref_count,
- uint64_t *leaf_no, uint64_t old_leaf, uint64_t *bad_leaf,
- uint64_t first_ok_leaf, struct gfs2_leaf *leaf,
- struct gfs2_leaf *oldleaf)
+ struct metawalk_fxns *pass,
+ uint64_t *leaf_no, struct gfs2_leaf *leaf, int *ref_count)
{
int error = 0, fix;
struct gfs2_buffer_head *lbh = NULL;
@@ -533,7 +495,6 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
struct gfs2_sbd *sdp = ip->i_sbd;
const char *msg;
- *ref_count = 1;
/* Make sure the block number is in range. */
if (!valid_block(ip->i_sbd, *leaf_no)) {
log_err( _("Leaf block #%llu (0x%llx) is out of range for "
@@ -543,7 +504,7 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
msg = _("that is out of range");
- goto out_copy_old_leaf;
+ goto bad_leaf;
}
/* Try to read in the leaf block. */
@@ -551,7 +512,7 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
/* Make sure it's really a valid leaf block. */
if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) {
msg = _("that is not really a leaf");
- goto out_copy_old_leaf;
+ goto bad_leaf;
}
if (pass->check_leaf) {
error = pass->check_leaf(ip, *leaf_no, pass->private);
@@ -587,7 +548,7 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
(unsigned long long)*leaf_no,
(unsigned long long)*leaf_no);
msg = _("that is not a leaf");
- goto out_copy_old_leaf;
+ goto bad_leaf;
}
if (pass->check_dentry && is_dir(&ip->i_di, sdp->gfs1)) {
@@ -599,16 +560,18 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
if (error < 0) {
stack;
- goto out;
+ goto out; /* This seems wrong: needs investigation */
}
- if (count != leaf->lf_entries) {
- /* release and re-read the leaf in case check_entries
- changed it. */
- brelse(lbh);
- lbh = bread(sdp, *leaf_no);
- gfs2_leaf_in(leaf, lbh);
+ if (count == leaf->lf_entries)
+ goto out;
+ /* release and re-read the leaf in case check_entries
+ changed it. */
+ brelse(lbh);
+ lbh = bread(sdp, *leaf_no);
+ gfs2_leaf_in(leaf, lbh);
+ if (count != leaf->lf_entries) {
log_err( _("Leaf %llu (0x%llx) entry count in "
"directory %llu (0x%llx) does not match "
"number of entries found - is %u, found %u\n"),
@@ -630,17 +593,16 @@ out:
brelse(lbh);
return 0;
-out_copy_old_leaf:
- /* The leaf we read in is bad. So we'll copy the old leaf into the
- * new one. However, that will make us shift our ref count. */
- fix = warn_and_patch(ip, leaf_no, bad_leaf, old_leaf,
- first_ok_leaf, lindex, msg);
- (*ref_count)++;
- memcpy(leaf, oldleaf, sizeof(struct gfs2_leaf));
- if (lbh) {
- if (fix)
- bmodified(lbh);
+bad_leaf:
+ if (lbh)
brelse(lbh);
+ if (pass->repair_leaf) {
+ /* The leaf we read in is bad so we need to repair it. */
+ fix = pass->repair_leaf(ip, leaf_no, lindex, *ref_count, msg,
+ pass->private);
+ if (fix < 0)
+ return fix;
+
}
return 1;
}
@@ -679,17 +641,17 @@ static void dir_leaf_reada(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize)
/* Checks exhash directory entries */
static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
{
- int error;
- struct gfs2_leaf leaf, oldleaf;
+ int error = 0;
+ struct gfs2_leaf leaf;
unsigned hsize = (1 << ip->i_di.di_depth);
- uint64_t leaf_no, old_leaf, bad_leaf = -1;
+ uint64_t leaf_no, leaf_next;
uint64_t first_ok_leaf, orig_di_blocks;
struct gfs2_buffer_head *lbh;
int lindex;
struct gfs2_sbd *sdp = ip->i_sbd;
- int ref_count = 0, orig_ref_count, orig_di_depth, orig_di_height, old_was_dup;
+ int ref_count, orig_ref_count, orig_di_depth, orig_di_height;
uint64_t *tbl;
- int tbl_valid;
+ int chained_leaf, tbl_valid;
tbl = get_dir_hash(ip);
if (tbl == NULL) {
@@ -751,10 +713,11 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
return 1;
}
- old_leaf = -1;
- memset(&oldleaf, 0, sizeof(oldleaf));
- old_was_dup = 0;
- for (lindex = 0; lindex < hsize; lindex++) {
+ lindex = 0;
+ leaf_next = -1;
+ while (lindex < hsize) {
+ int l;
+
if (fsck_abort)
break;
@@ -774,38 +737,36 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
}
leaf_no = be64_to_cpu(tbl[lindex]);
- /* GFS has multiple indirect pointers to the same leaf
- * until those extra pointers are needed, so skip the dups */
- if (leaf_no == bad_leaf) {
- tbl[lindex] = cpu_to_be64(old_leaf);
- gfs2_put_leaf_nr(ip, lindex, old_leaf);
- ref_count++;
- continue;
- } else if (old_leaf == leaf_no) {
+ /* count the number of block pointers to this leaf. We don't
+ need to count the current lindex, because we already know
+ it's a reference */
+ ref_count = 1;
+
+ for (l = lindex + 1; l < hsize; l++) {
+ leaf_next = be64_to_cpu(tbl[l]);
+ if (leaf_next != leaf_no)
+ break;
ref_count++;
- continue;
}
orig_ref_count = ref_count;
+ chained_leaf = 0;
do {
if (fsck_abort) {
free(tbl);
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
return 0;
}
- error = check_leaf(ip, lindex, pass, &ref_count,
- &leaf_no, old_leaf, &bad_leaf,
- first_ok_leaf, &leaf, &oldleaf);
+ error = check_leaf(ip, lindex, pass, &leaf_no, &leaf,
+ &ref_count);
if (ref_count != orig_ref_count)
tbl_valid = 0;
- old_was_dup = (error == -EEXIST);
- old_leaf = leaf_no;
- memcpy(&oldleaf, &leaf, sizeof(oldleaf));
if (!leaf.lf_next || error)
break;
leaf_no = leaf.lf_next;
- log_debug( _("Leaf chain (0x%llx) detected.\n"),
- (unsigned long long)leaf_no);
+ chained_leaf++;
+ log_debug( _("Leaf chain #%d (0x%llx) detected.\n"),
+ chained_leaf, (unsigned long long)leaf_no);
} while (1); /* while we have chained leaf blocks */
if (orig_di_depth != ip->i_di.di_depth) {
log_debug(_("Depth of 0x%llx changed from %d to %d\n"),
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index bef99ae..e11b5e0 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -104,7 +104,8 @@ struct metawalk_fxns {
int (*check_hash_tbl) (struct gfs2_inode *ip, uint64_t *tbl,
unsigned hsize, void *private);
int (*repair_leaf) (struct gfs2_inode *ip, uint64_t *leaf_no,
- int lindex, int ref_count, const char *msg);
+ int lindex, int ref_count, const char *msg,
+ void *private);
};
#endif /* _METAWALK_H */
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index dd6b958..e827a55 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -78,6 +78,19 @@ static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
void *private);
static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip);
+static int pass1_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
+ int lindex, int ref_count, const char *msg,
+ void *private)
+{
+ struct block_count *bc = (struct block_count *)private;
+ int new_leaf_blks;
+
+ new_leaf_blks = repair_leaf(ip, leaf_no, lindex, ref_count, msg);
+ bc->indir_count += new_leaf_blks;
+
+ return new_leaf_blks;
+}
+
struct metawalk_fxns pass1_fxns = {
.private = NULL,
.check_leaf = check_leaf,
@@ -90,6 +103,7 @@ struct metawalk_fxns pass1_fxns = {
.check_eattr_extentry = check_extended_leaf_eattr,
.finish_eattr_indir = finish_eattr_indir,
.big_file_msg = big_file_comfort,
+ .repair_leaf = pass1_repair_leaf,
};
struct metawalk_fxns undo_fxns = {
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 527071c..e0b1350 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1422,6 +1422,13 @@ static int check_hash_tbl(struct gfs2_inode *ip, uint64_t *tbl,
return error;
}
+static int pass2_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
+ int lindex, int ref_count, const char *msg,
+ void *private)
+{
+ return repair_leaf(ip, leaf_no, lindex, ref_count, msg);
+}
+
struct metawalk_fxns pass2_fxns = {
.private = NULL,
.check_leaf = NULL,
@@ -1432,7 +1439,7 @@ struct metawalk_fxns pass2_fxns = {
.check_dentry = check_dentry,
.check_eattr_entry = NULL,
.check_hash_tbl = check_hash_tbl,
- .repair_leaf = repair_leaf,
+ .repair_leaf = pass2_repair_leaf,
};
/* Check system directory inode */
--
1.7.11.7
More information about the Cluster-devel
mailing list