Gitweb:
http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=...
Commit: 52b6962e42c46f4344bd94dbd7911a73e6005246
Parent: e8cc459b6ac847c1dd92a17ce833b74e46228ab0
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Aug 17 13:55:49 2010 -0500
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Wed Aug 18 09:20:08 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#624689
---
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 09305da..330dace 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1138,7 +1138,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;
@@ -1149,35 +1149,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);
@@ -1190,7 +1189,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". */
@@ -1472,7 +1471,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);
}
@@ -1504,7 +1503,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 3ea2991..4489c60 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -31,7 +31,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);
@@ -74,7 +74,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 bea0ef5..5f18818 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -50,9 +50,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);
@@ -75,7 +76,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,
@@ -126,7 +128,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;
@@ -220,7 +223,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;
@@ -240,7 +243,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 {
@@ -263,13 +266,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);
@@ -291,7 +294,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;
@@ -305,7 +309,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;
@@ -849,7 +853,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"));
}
@@ -930,7 +935,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 8b67573..a0c5c88 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -42,7 +42,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,
@@ -89,7 +89,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);
}
@@ -213,7 +213,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;
@@ -262,21 +263,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,
@@ -329,7 +330,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 */