Gitweb:
http://git.fedorahosted.org/git/gfs2-utils.git?p=gfs2-utils.git;a=commitd...
Commit: fb5bd562b6768e9110357434f3441e006a795d96
Parent: d3d8ba6be4786077cb5a9fea13e035a51ef92022
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Aug 17 12:26:47 2010 -0500
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Aug 17 12:32:05 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 7ea4b59..5cb531d 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1127,7 +1127,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;
@@ -1138,35 +1138,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);
@@ -1179,7 +1178,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". */
@@ -1461,7 +1460,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);
}
@@ -1493,7 +1492,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 a335d28..72d35ad 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -37,9 +37,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);
@@ -62,7 +63,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,
@@ -113,7 +115,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;
@@ -207,7 +210,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;
@@ -227,7 +230,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 {
@@ -250,13 +253,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);
@@ -278,7 +281,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;
@@ -292,7 +296,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;
@@ -836,7 +840,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"));
}
@@ -917,7 +922,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 c3290c2..12b6a3b 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -29,7 +29,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,
@@ -76,7 +76,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);
}
@@ -199,7 +199,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;
@@ -248,21 +249,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,
@@ -315,7 +316,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 */