Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=6a7... Commit: 6a73e92beebf62fe1a1da1284dcd56a028e4662f Parent: ba3ddbcd0a1123350774e75ac1845c4fae61f697 Author: Bob Peterson rpeterso@redhat.com AuthorDate: Tue Aug 17 12:26:47 2010 -0500 Committer: Bob Peterson rpeterso@redhat.com CommitterDate: Tue Aug 17 12:40:38 2010 -0500
fsck.gfs2 deletes directories if they get too big
This patch fixes a problem whereby too many levels of indirection caused fsck.gfs2 to get confused and delete directories. The problem was that gfs2.fsck only understood three levels of data for directories: (1) dinode, (2) block pointers to leaf blocks, (3) leaf blocks. If a directory gets sufficiently big, it goes into four or more levels of indirection, with level 2 (and possibly more) being indirect blocks leading to the leaf blocks.
rhbz#624691 --- gfs2/fsck/metawalk.c | 45 ++++++++++++++++++++++----------------------- gfs2/fsck/metawalk.h | 5 +++-- gfs2/fsck/pass1.c | 40 +++++++++++++++++++++++----------------- gfs2/fsck/pass1b.c | 15 ++++++++------- 4 files changed, 56 insertions(+), 49 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c index 0ab8d7e..ef1b6b5 100644 --- a/gfs2/fsck/metawalk.c +++ b/gfs2/fsck/metawalk.c @@ -1125,7 +1125,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp, uint32_t height = ip->i_di.di_height; struct gfs2_buffer_head *bh, *nbh, *metabh = ip->i_bh; osi_list_t *prev_list, *cur_list, *tmp; - int i, head_size, iblk_type; + int h, head_size, iblk_type; uint64_t *ptr, block; int error = 0, err;
@@ -1136,35 +1136,34 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp, because it checks everything through the hash table using "depth" field calculations. However, we still have to check the indirect blocks, even if the height == 1. */ - if (S_ISDIR(ip->i_di.di_mode)) { + if (S_ISDIR(ip->i_di.di_mode)) height++; - iblk_type = GFS2_METATYPE_JD; - } else { - iblk_type = GFS2_METATYPE_IN; - }
/* if(<there are no indirect blocks to check>) */ if (height < 2) return 0; - for (i = 1; i < height; i++) { - prev_list = &mlp[i - 1]; - cur_list = &mlp[i]; + for (h = 1; h < height; h++) { + if (h > 1) { + if (S_ISDIR(ip->i_di.di_mode) && + h == ip->i_di.di_height + 1) + iblk_type = GFS2_METATYPE_JD; + else + iblk_type = GFS2_METATYPE_IN; + head_size = sizeof(struct gfs2_meta_header); + } else { + iblk_type = GFS2_METATYPE_DI; + head_size = sizeof(struct gfs2_dinode); + } + prev_list = &mlp[h - 1]; + cur_list = &mlp[h];
for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){ bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist);
- if (i > 1) { - /* if this isn't really a block list skip it */ - if (gfs2_check_meta(bh, iblk_type)) - continue; - head_size = sizeof(struct gfs2_meta_header); - } else { - /* if this isn't really a dinode, skip it */ - if (gfs2_check_meta(bh, GFS2_METATYPE_DI)) - continue; - head_size = sizeof(struct gfs2_dinode); - } + if (gfs2_check_meta(bh, iblk_type)) + continue; + /* Now check the metadata itself */ for (ptr = (uint64_t *)(bh->b_data + head_size); (char *)ptr < (bh->b_data + ip->i_sbd->bsize); @@ -1177,7 +1176,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp, continue;
block = be64_to_cpu(*ptr); - err = pass->check_metalist(ip, block, &nbh, + err = pass->check_metalist(ip, block, &nbh, h, pass->private); /* check_metalist should hold any buffers it gets with "bread". */ @@ -1459,7 +1458,7 @@ int remove_dentry_from_dir(struct gfs2_sbd *sbp, uint64_t dir, }
int delete_metadata(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, void *private) { return delete_block_if_notdup(ip, block, bh, _("metadata"), private); } @@ -1491,7 +1490,7 @@ int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent, }
static int alloc_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, void *private) { uint8_t q; const char *desc = (const char *)private; diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h index c602492..7aae9f2 100644 --- a/gfs2/fsck/metawalk.h +++ b/gfs2/fsck/metawalk.h @@ -19,7 +19,7 @@ extern int delete_block(struct gfs2_inode *ip, uint64_t block, struct gfs2_buffer_head **bh, const char *btype, void *private); extern int delete_metadata(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private); + struct gfs2_buffer_head **bh, int h, void *private); extern int delete_leaf(struct gfs2_inode *ip, uint64_t block, struct gfs2_buffer_head *bh, void *private); extern int delete_data(struct gfs2_inode *ip, uint64_t block, void *private); @@ -62,7 +62,8 @@ struct metawalk_fxns { int (*check_leaf) (struct gfs2_inode *ip, uint64_t block, struct gfs2_buffer_head *bh, void *private); int (*check_metalist) (struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private); + struct gfs2_buffer_head **bh, int h, + void *private); int (*check_data) (struct gfs2_inode *ip, uint64_t block, void *private); int (*check_eattr_indir) (struct gfs2_inode *ip, uint64_t block, diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c index 6c4762d..4fb7abf 100644 --- a/gfs2/fsck/pass1.c +++ b/gfs2/fsck/pass1.c @@ -35,9 +35,10 @@ struct block_count { static int leaf(struct gfs2_inode *ip, uint64_t block, struct gfs2_buffer_head *bh, void *private); static int check_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private); + struct gfs2_buffer_head **bh, int h, void *private); static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private); + struct gfs2_buffer_head **bh, int h, + void *private); static int check_data(struct gfs2_inode *ip, uint64_t block, void *private); static int undo_check_data(struct gfs2_inode *ip, uint64_t block, void *private); @@ -60,7 +61,8 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers, int leaf_pointer_errors, void *private); static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private); + struct gfs2_buffer_head **bh, int h, + void *private); static int invalidate_leaf(struct gfs2_inode *ip, uint64_t block, struct gfs2_buffer_head *bh, void *private); static int invalidate_data(struct gfs2_inode *ip, uint64_t block, @@ -111,7 +113,8 @@ struct metawalk_fxns invalidate_fxns = { * deleted, do you? Or worse, reused for lost+found. */ static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, + void *private) { struct block_count *bc = (struct block_count *)private;
@@ -205,7 +208,7 @@ static int leaf(struct gfs2_inode *ip, uint64_t block, }
static int check_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, void *private) { uint8_t q; int found_dup = 0, iblk_type; @@ -225,7 +228,7 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
return 1; } - if (S_ISDIR(ip->i_di.di_mode)) { + if (S_ISDIR(ip->i_di.di_mode) && h == ip->i_di.di_height) { iblk_type = GFS2_METATYPE_JD; blktypedesc = _("a directory hash table block"); } else { @@ -248,13 +251,13 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block, nbh = bread(ip->i_sbd, block);
if (gfs2_check_meta(nbh, iblk_type)){ - log_debug( _("Inode %lld (0x%llx) has a bad indirect block " - "pointer %lld (0x%llx) (points to something " - "that is not %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)block, - (unsigned long long)block, blktypedesc); + log_err( _("Inode %lld (0x%llx) has a bad indirect block " + "pointer %lld (0x%llx) (points to something " + "that is not %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)block, + (unsigned long long)block, blktypedesc); if(!found_dup) { fsck_blockmap_set(ip, block, _("bad indirect"), gfs2_meta_inval); @@ -276,7 +279,8 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block, }
static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, + void *private) { struct duptree *d; int found_dup = 0, iblk_type; @@ -290,7 +294,7 @@ static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block, _("itself"), gfs2_block_free); return 1; } - if (S_ISDIR(ip->i_di.di_mode)) + if (S_ISDIR(ip->i_di.di_mode) && h == ip->i_di.di_height) iblk_type = GFS2_METATYPE_JD; else iblk_type = GFS2_METATYPE_IN; @@ -834,7 +838,8 @@ static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block, }
static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, + void *private) { return mark_block_invalid(ip, block, ref_as_meta, _("metadata")); } @@ -915,7 +920,8 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block, }
static int rangecheck_metadata(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, + void *private) { return rangecheck_block(ip, block, bh, _("metadata"), private); } diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c index 680eb38..4d97879 100644 --- a/gfs2/fsck/pass1b.c +++ b/gfs2/fsck/pass1b.c @@ -27,7 +27,7 @@ struct dup_handler { };
static int check_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private); + struct gfs2_buffer_head **bh, int h, void *private); static int check_data(struct gfs2_inode *ip, uint64_t block, void *private); static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, @@ -74,7 +74,7 @@ struct metawalk_fxns find_dirents = { };
static int check_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, void *private) { return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID); } @@ -197,7 +197,8 @@ static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de, }
static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block, - struct gfs2_buffer_head **bh, void *private) + struct gfs2_buffer_head **bh, int h, + void *private) { struct dup_handler *dh = (struct dup_handler *) private; struct duptree *d; @@ -246,21 +247,21 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
static int clear_dup_data(struct gfs2_inode *ip, uint64_t block, void *private) { - return clear_dup_metalist(ip, block, NULL, private); + return clear_dup_metalist(ip, block, NULL, 0, private); }
static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, void *private) { - return clear_dup_metalist(ip, block, NULL, private); + return clear_dup_metalist(ip, block, NULL, 0, private); }
static int clear_dup_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, void *private) { - return clear_dup_metalist(ip, block, NULL, private); + return clear_dup_metalist(ip, block, NULL, 0, private); }
static int clear_eattr_entry (struct gfs2_inode *ip, @@ -313,7 +314,7 @@ static int clear_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr, { uint64_t block = be64_to_cpu(*ea_data_ptr);
- return clear_dup_metalist(ip, block, NULL, private); + return clear_dup_metalist(ip, block, NULL, 0, private); }
/* Finds all references to duplicate blocks in the metadata */