Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=1b…
Commit: 1bb481b4baea438c86df58584c825ba735aef3d1
Parent: 1f8086a6382a337bc4febab691b83a7ce673e266
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Thu Jan 21 23:10:23 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:30 2010 -0600
fsck.gfs2: fix directories that have odd number of pointers.
Directory dinodes in gfs2 use the "depth" field to determine the
number of pointers in the hash table. The depth must be a
factor of 2. So 2, 4, 8, 16, etc. That's because the hash table
is doubled every time it gets full. The problem is, fsck.gfs2
previously had no checks for the depth being correct and/or
the hash table pointers being multiples of two. Needless to say,
the directory code gets very confused if this condition is not
met due to corruption. This patch introduces new code to
validate the depth is correct and fix the depth and/or pointers
if they're not.
rhbz#455300
---
gfs2/fsck/metawalk.c | 133 ++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 113 insertions(+), 20 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 7ba8e39..8b75311 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -332,6 +332,61 @@ static void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
*leaf_no = old_leaf;
}
+/*
+ * fix_leaf_pointers - fix a directory dinode that has a number of pointers
+ * that is not a multiple of 2.
+ * dip - the directory inode having the problem
+ * lindex - the index of the leaf right after the problem (need to back up)
+ * cur_numleafs - current (incorrect) number of instances of the leaf block
+ * correct_numleafs - the correct number instances of the leaf block
+ */
+static int fix_leaf_pointers(struct gfs2_inode *dip, int *lindex,
+ int cur_numleafs, int correct_numleafs)
+{
+ int count;
+ char *ptrbuf;
+ int start_lindex = *lindex - cur_numleafs; /* start of bad ptrs */
+ int tot_num_ptrs = (1 << dip->i_di.di_depth) - start_lindex;
+ int bufsize = tot_num_ptrs * sizeof(uint64_t);
+ int off_by = cur_numleafs - correct_numleafs;
+
+ ptrbuf = malloc(bufsize);
+ if (!ptrbuf) {
+ log_err( _("Error: Cannot allocate memory to fix the leaf "
+ "pointers.\n"));
+ return -1;
+ }
+ /* Read all the pointers, starting with the first bad one */
+ count = gfs2_readi(dip, ptrbuf, start_lindex * sizeof(uint64_t),
+ bufsize);
+ if (count != bufsize) {
+ log_err( _("Error: bad read while fixing leaf pointers.\n"));
+ free(ptrbuf);
+ return -1;
+ }
+
+ bufsize -= off_by * sizeof(uint64_t); /* We need to write fewer */
+ /* Write the same pointers, but offset them so they fit within the
+ smaller factor of 2. So if we have 12 pointers, write out only
+ the last 8 of them. If we have 7, write the last 4, etc.
+ We need to write these starting at the current lindex and adjust
+ lindex accordingly. */
+ count = gfs2_writei(dip, ptrbuf + (off_by * sizeof(uint64_t)),
+ start_lindex * sizeof(uint64_t), bufsize);
+ if (count != bufsize) {
+ log_err( _("Error: bad read while fixing leaf pointers.\n"));
+ free(ptrbuf);
+ return -1;
+ }
+ /* Now zero out the hole left at the end */
+ memset(ptrbuf, 0, off_by * sizeof(uint64_t));
+ gfs2_writei(dip, ptrbuf, (start_lindex * sizeof(uint64_t)) +
+ bufsize, off_by * sizeof(uint64_t));
+ free(ptrbuf);
+ *lindex -= off_by; /* adjust leaf index to account for the change */
+ return 0;
+}
+
/* Checks exhash directory entries */
static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
{
@@ -382,33 +437,71 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
ref_count++;
continue;
}
- if (gfs2_check_range(ip->i_sbd, old_leaf) == 0 &&
- ref_count != exp_count) {
- log_err( _("Dir #%llu (0x%llx) has an incorrect "
- "number of pointers to leaf #%llu "
- " (0x%llx)\n\tFound: %u, Expected: "
- "%u\n"), (unsigned long long)
- ip->i_di.di_num.no_addr,
- (unsigned long long)
- ip->i_di.di_num.no_addr,
- (unsigned long long)old_leaf,
- (unsigned long long)old_leaf,
- ref_count, exp_count);
- if (query( _("Attempt to fix it? (y/n) "))) {
- int factor = 0, divisor = ref_count;
-
- lbh = bread(sbp, old_leaf);
- while (divisor > 1) {
- factor++;
- divisor /= 2;
+ if (gfs2_check_range(ip->i_sbd, old_leaf) == 0) {
+ int factor = 0, divisor = ref_count, multiple = 1;
+
+ /* Check to see if the number of pointers we found is
+ a power of 2. It needs to be and if it's not we
+ need to fix it.*/
+ while (divisor > 1) {
+ factor++;
+ divisor /= 2;
+ multiple = multiple << 1;
+ }
+ if (ref_count != multiple) {
+ log_err( _("Directory #%llu (0x%llx) has an "
+ "invalid number of pointers to "
+ "leaf #%llu (0x%llx)\n\tFound: %u, "
+ "which is not a factor of 2.\n"),
+ (unsigned long long)
+ ip->i_di.di_num.no_addr,
+ (unsigned long long)
+ ip->i_di.di_num.no_addr,
+ (unsigned long long)old_leaf,
+ (unsigned long long)old_leaf,
+ ref_count);
+ if (!query( _("Attempt to fix it? (y/n) "))) {
+ log_err( _("Directory inode was not "
+ "fixed.\n"));
+ return 1;
}
+ error = fix_leaf_pointers(ip, &lindex,
+ ref_count, multiple);
+ if (error)
+ return error;
+ ref_count = multiple;
+ log_err( _("Directory inode was fixed.\n"));
+ }
+ /* Check to see if the counted number of leaf pointers
+ is what we expect. */
+ if (ref_count != exp_count) {
+ log_err( _("Directory #%llu (0x%llx) has an "
+ "incorrect number of pointers to "
+ "leaf #%llu (0x%llx)\n\tFound: "
+ "%u, Expected: %u\n"),
+ (unsigned long long)
+ ip->i_di.di_num.no_addr,
+ (unsigned long long)
+ ip->i_di.di_num.no_addr,
+ (unsigned long long)old_leaf,
+ (unsigned long long)old_leaf,
+ ref_count, exp_count);
+ if (!query( _("Attempt to fix it? (y/n) "))) {
+ log_err( _("Directory leaf was not "
+ "fixed.\n"));
+ return 1;
+ }
+ lbh = bread(sbp, old_leaf);
gfs2_leaf_in(&oldleaf, lbh);
+ log_err( _("Leaf depth was %d, changed to "
+ "%d\n"), oldleaf.lf_depth,
+ ip->i_di.di_depth - factor);
oldleaf.lf_depth = ip->i_di.di_depth - factor;
gfs2_leaf_out(&oldleaf, lbh);
brelse(lbh);
+ exp_count = ref_count;
+ log_err( _("Directory leaf was fixed.\n"));
}
- else
- return 1;
}
ref_count = 1;
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=6f…
Commit: 6f711c7174cdf863bc5a5c9f497270114dab7413
Parent: 3e89cfb766711d210e23284206b791eee71273f7
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Thu Jan 21 17:35:33 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:29 2010 -0600
libgfs2: Set block range based on rgrps, not device size
The functions that read in the rindex need to set the block
range properly, rather than basing it on the device size.
If not, bad block pointers might reference blocks beyond the
end of the file system (as can happen if a device is extended
before gfs2_grow is run), or if a different metadata set is
restored with gfs2_edit restoremeta.
rhbz#455300
---
gfs2/libgfs2/gfs1.c | 1 +
gfs2/libgfs2/super.c | 4 ++++
2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/gfs2/libgfs2/gfs1.c b/gfs2/libgfs2/gfs1.c
index 5cced66..5018334 100644
--- a/gfs2/libgfs2/gfs1.c
+++ b/gfs2/libgfs2/gfs1.c
@@ -315,6 +315,7 @@ int gfs1_ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount, int quiet)
rmax = ri->ri_data0 + ri->ri_data - 1;
}
+ sdp->fssize = rmax;
*rgcount = count1;
if (count1 != count2)
goto fail;
diff --git a/gfs2/libgfs2/super.c b/gfs2/libgfs2/super.c
index ca7bf09..1fa18ce 100644
--- a/gfs2/libgfs2/super.c
+++ b/gfs2/libgfs2/super.c
@@ -229,6 +229,7 @@ int ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount)
osi_list_t *tmp;
int count1 = 0, count2 = 0;
uint64_t errblock = 0;
+ uint64_t rmax = 0;
if (rindex_read(sdp, fd, &count1))
goto fail;
@@ -238,9 +239,12 @@ int ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount)
if (errblock)
return errblock;
ri = &rgd->ri;
+ if (ri->ri_data0 + ri->ri_data - 1 > rmax)
+ rmax = ri->ri_data0 + ri->ri_data - 1;
count2++;
}
+ sdp->fssize = rmax;
*rgcount = count1;
if (count1 != count2)
goto fail;
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=53…
Commit: 53028729cd3b8987ee498d486c6cae37c60b808b
Parent: 7fd936c0f96689f53c58341bf5ea20a37a119911
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Thu Jan 21 17:15:24 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:29 2010 -0600
libgfs2: dir_split_leaf needs to zero out the new leaf
The dir_split_leaf function was not clearing out the new leaf
block it had allocated, so there was likely to be trash left
on the block. Granted, this is pretty rare, but I actually
saw it in a situation where lots of orphaned dinodes were
tossed into lost+found.
rhbz#455300
---
gfs2/libgfs2/fs_ops.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
index bc8319e..3fc80ce 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -856,6 +856,8 @@ static void dir_split_leaf(struct gfs2_inode *dip, uint32_t lindex,
mh.mh_type = GFS2_METATYPE_LF;
mh.mh_format = GFS2_FORMAT_LF;
gfs2_meta_header_out(&mh, nbh);
+ buffer_clear_tail(dip->i_sbd, nbh,
+ sizeof(struct gfs2_meta_header));
}
nleaf = (struct gfs2_leaf *)nbh->b_data;
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=25…
Commit: 25541883d8844c4cfcac59346a211c897a84bbb6
Parent: 7471efad8ae060d70803bbf66d21e9a36abdd3a5
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Thu Jan 21 14:26:17 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:29 2010 -0600
fsck.gfs2: pass1 should use gfs2_special_add not _set
Since pass1 traverses the file system sequentially by block,
it can safely assume blocks it adds to the special_blocks list
are not already on it. Therefore, it can safely add those
blocks to the list and avoid checking for it already being
there. This saves a tiny bit of time, which may seem
trivial but when there are a million inodes on the list, the
time saved is worth it.
rhbz#455300
---
gfs2/fsck/pass1.c | 4 ++--
gfs2/libgfs2/block_list.c | 11 ++++++++---
gfs2/libgfs2/libgfs2.h | 3 ++-
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 0070886..ba5206d 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -364,7 +364,7 @@ static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
(unsigned long long)ip->i_di.di_num.no_addr);
/* Mark the inode as having an eattr in the block map
so pass1c can check it. */
- gfs2_special_set(&ip->i_sbd->eattr_blocks, ip->i_di.di_num.no_addr);
+ gfs2_special_add(&ip->i_sbd->eattr_blocks, ip->i_di.di_num.no_addr);
if (!leaf_pointer_errors)
return 0;
log_err( _("Inode %lld (0x%llx) has recoverable indirect "
@@ -494,7 +494,7 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
"block(s) attached.\n"),
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
- gfs2_special_set(&sdp->eattr_blocks, ip->i_di.di_num.no_addr);
+ gfs2_special_add(&sdp->eattr_blocks, ip->i_di.di_num.no_addr);
if(gfs2_check_range(sdp, block)) {
log_warn( _("Inode #%llu (0x%llx): Extended Attribute leaf "
"block #%llu (0x%llx) is out of range.\n"),
diff --git a/gfs2/libgfs2/block_list.c b/gfs2/libgfs2/block_list.c
index bc8babc..b671a4b 100644
--- a/gfs2/libgfs2/block_list.c
+++ b/gfs2/libgfs2/block_list.c
@@ -79,12 +79,10 @@ struct special_blocks *blockfind(struct special_blocks *blist, uint64_t num)
return NULL;
}
-void gfs2_special_set(struct special_blocks *blocklist, uint64_t block)
+void gfs2_special_add(struct special_blocks *blocklist, uint64_t block)
{
struct special_blocks *b;
- if (blockfind(blocklist, block))
- return;
b = malloc(sizeof(struct special_blocks));
if (b) {
memset(b, 0, sizeof(*b));
@@ -93,6 +91,13 @@ void gfs2_special_set(struct special_blocks *blocklist, uint64_t block)
}
}
+void gfs2_special_set(struct special_blocks *blocklist, uint64_t block)
+{
+ if (blockfind(blocklist, block))
+ return;
+ gfs2_special_add(blocklist, block);
+}
+
void gfs2_special_clear(struct special_blocks *blocklist, uint64_t block)
{
struct special_blocks *b;
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 86db414..0452e47 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -303,10 +303,11 @@ enum gfs2_mark_block {
extern struct gfs2_bmap *gfs2_bmap_create(struct gfs2_sbd *sdp, uint64_t size,
uint64_t *addl_mem_needed);
extern struct special_blocks *blockfind(struct special_blocks *blist, uint64_t num);
+extern void gfs2_special_add(struct special_blocks *blocklist, uint64_t block);
extern void gfs2_special_set(struct special_blocks *blocklist, uint64_t block);
extern void gfs2_special_free(struct special_blocks *blist);
extern int gfs2_blockmap_set(struct gfs2_sbd *sdp, struct gfs2_bmap *il,
- uint64_t block, enum gfs2_mark_block mark);
+ uint64_t block, enum gfs2_mark_block mark);
extern void gfs2_special_clear(struct special_blocks *blocklist,
uint64_t block);
/* gfs2_block_unmark clears ONE mark for the given block */