gfs2-utils: RHEL7 - fsck.gfs2: addendum to fix broken i_goal values in inodes - addendum 2 of 2
by Abhijith Das
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=6d89e9df...
Commit: 6d89e9dfe83f68ebb05bfb2ef4715cc5fb1aa630
Parent: 5eccbb33c28604436d38f4148fbb651151af1aef
Author: Abhi Das <adas(a)redhat.com>
AuthorDate: Thu Jan 15 12:49:48 2015 -0600
Committer: Abhi Das <adas(a)redhat.com>
CommitterDate: Thu Jan 15 13:54:33 2015 -0600
fsck.gfs2: addendum to fix broken i_goal values in inodes - addendum 2 of 2
This patch moves some code around and fixes some corner cases that
the previous patches did not address.
This patch also fixes some trailing whitespace and removes a test
that is no longer valid from test/fsck.at
This patch is part 2 of 2 addendum fixes to make fsck fix i_goal
values correctly. The original three patches to fix this problem were
checked in against a wrong bz 1149516 (which is the rhel6 version
of the bug). The complete fix contains those three patches (b09dae8,
d79246d, c5b09c4) and this two-patch addendum for a total of 5
patches.
Resolves: rhbz#1178604
Signed-off-by: Abhi Das <adas(a)redhat.com>
---
gfs2/fsck/metawalk.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
gfs2/fsck/metawalk.h | 2 +
gfs2/fsck/pass1.c | 54 +++---------------------------------
gfs2/libgfs2/libgfs2.h | 1 +
tests/fsck.at | 1 -
5 files changed, 78 insertions(+), 50 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 82abb40..3460141 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1497,6 +1497,9 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
uint64_t error_blk = 0;
int hit_error_blk = 0;
+ if (!height && pass->check_i_goal)
+ pass->check_i_goal(ip, ip->i_di.di_num.no_addr,
+ pass->private);
if (!height && !is_dir(&ip->i_di, ip->i_sbd->gfs1))
return 0;
@@ -1891,6 +1894,72 @@ static int alloc_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
return 0;
}
+/**
+ * rgrp_contains_block - Check if the rgrp provided contains the
+ * given block. Taken directly from the gfs2 kernel code
+ * @rgd: The rgrp to search within
+ * @block: The block to search for
+ *
+ * Returns: 1 if present, 0 if not.
+ */
+static inline int rgrp_contains_block(struct rgrp_tree *rgd, uint64_t block)
+{
+ uint64_t first = rgd->ri.ri_data0;
+ uint64_t last = first + rgd->ri.ri_data;
+ return first <= block && block < last;
+}
+
+/**
+ * check_i_goal
+ * @ip
+ * @goal_blk: What the goal block should be for this inode
+ *
+ * The goal block for a regular file is typically the last
+ * data block of the file. If we can't get the right value,
+ * the inode metadata block is the next best thing.
+ *
+ * Returns: 0 if corrected, 1 if not corrected
+ */
+int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk,
+ void *private)
+{
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ uint64_t i_block = ip->i_di.di_num.no_addr;
+
+ /* Don't fix gfs1 inodes, system inodes or inodes whose goal blocks are
+ * set to the inode blocks themselves. */
+ if (sdp->gfs1 || ip->i_di.di_flags & GFS2_DIF_SYSTEM ||
+ ip->i_di.di_goal_meta == i_block)
+ return 0;
+ /* We default to the inode block */
+ if (!goal_blk)
+ goal_blk = i_block;
+
+ if (ip->i_di.di_goal_meta != goal_blk) {
+ /* If the existing goal block is in the same rgrp as the inode,
+ * we give the benefit of doubt and assume the value is correct */
+ if (ip->i_rgd &&
+ rgrp_contains_block(ip->i_rgd, ip->i_di.di_goal_meta))
+ goto skip;
+ log_err( _("Error: inode %llu (0x%llx) has invalid "
+ "allocation goal block %llu (0x%llx). Should"
+ " be %llu (0x%llx)\n"),
+ (unsigned long long)i_block, (unsigned long long)i_block,
+ (unsigned long long)ip->i_di.di_goal_meta,
+ (unsigned long long)ip->i_di.di_goal_meta,
+ (unsigned long long)goal_blk, (unsigned long long)goal_blk);
+ if (query( _("Fix the invalid goal block? (y/n) "))) {
+ ip->i_di.di_goal_meta = ip->i_di.di_goal_data = goal_blk;
+ bmodified(ip->i_bh);
+ } else {
+ log_err(_("Invalid goal block not fixed.\n"));
+ return 1;
+ }
+ }
+skip:
+ return 0;
+}
+
struct metawalk_fxns alloc_fxns = {
.private = NULL,
.check_leaf = alloc_leaf,
@@ -1901,6 +1970,7 @@ struct metawalk_fxns alloc_fxns = {
.check_dentry = NULL,
.check_eattr_entry = NULL,
.check_eattr_extentry = NULL,
+ .check_i_goal = check_i_goal,
.finish_eattr_indir = NULL,
};
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index b3c1299..482016a 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -51,6 +51,8 @@ extern int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
extern int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
int error_on_dinode,
enum gfs2_mark_block new_blockmap_state);
+extern int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk,
+ void *private);
extern void reprocess_inode(struct gfs2_inode *ip, const char *desc);
extern struct duptree *dupfind(uint64_t block);
extern struct gfs2_inode *fsck_system_inode(struct gfs2_sbd *sdp,
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 60f0356..dbe60bd 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -62,7 +62,6 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
struct gfs2_ea_header *ea_hdr,
struct gfs2_ea_header *ea_hdr_prev,
void *private);
-static int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk, void *private);
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,
@@ -100,7 +99,7 @@ struct metawalk_fxns pass1_fxns = {
.check_dentry = NULL,
.check_eattr_entry = check_eattr_entries,
.check_eattr_extentry = check_extended_leaf_eattr,
- .check_i_goal = NULL,
+ .check_i_goal = check_i_goal,
.finish_eattr_indir = finish_eattr_indir,
.big_file_msg = big_file_comfort,
.repair_leaf = pass1_repair_leaf,
@@ -794,48 +793,6 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
return error;
}
-/**
- * check_i_goal
- * @ip
- * @goal_blk: What the goal block should be for this inode
- *
- * The goal block for a regular file is typically the last
- * data block of the file. If we can't get the right value,
- * the inode metadata block is the next best thing.
- *
- * Returns: 0 if corrected, 1 if not corrected
- */
-static int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk,
- void *private)
-{
- struct gfs2_sbd *sdp = ip->i_sbd;
-
- if (fsck_system_inode(sdp, ip->i_di.di_num.no_addr))
- return 0;
- if (!goal_blk)
- goal_blk = ip->i_di.di_num.no_addr;
- if (ip->i_di.di_goal_meta != goal_blk ||
- ip->i_di.di_goal_data != goal_blk) {
- log_err( _("Error: inode %llu (0x%llx) has invalid "
- "allocation goal block %llu (0x%llx). Should"
- " be %llu (0x%llx)\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_goal_meta,
- (unsigned long long)ip->i_di.di_goal_meta,
- (unsigned long long)goal_blk,
- (unsigned long long)goal_blk);
- if (query( _("Fix the invalid goal block? (y/n) "))) {
- ip->i_di.di_goal_meta = ip->i_di.di_goal_data = goal_blk;
- bmodified(ip->i_bh);
- } else {
- log_err(_("Invalid goal block not fixed.\n"));
- return 1;
- }
- }
- return 0;
-}
-
static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
uint64_t parent, struct gfs2_buffer_head **bh,
void *private)
@@ -1217,7 +1174,8 @@ bad_dinode:
* handle_di - This is now a wrapper function that takes a gfs2_buffer_head
* and calls handle_ip, which takes an in-code dinode structure.
*/
-static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
+static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
+ struct rgrp_tree *rgd)
{
int error = 0;
uint64_t block = bh->b_blocknr;
@@ -1259,6 +1217,7 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
(unsigned long long)block,
(unsigned long long)block);
}
+ ip->i_rgd = rgd;
error = handle_ip(sdp, ip);
fsck_inode_put(&ip);
return error;
@@ -1585,7 +1544,7 @@ static int pass1_process_bitmap(struct gfs2_sbd *sdp, struct rgrp_tree *rgd, uin
(unsigned long long)block,
(unsigned long long)block);
check_n_fix_bitmap(sdp, block, 0, gfs2_block_free);
- } else if (handle_di(sdp, bh) < 0) {
+ } else if (handle_di(sdp, bh, rgd) < 0) {
stack;
brelse(bh);
gfs2_special_free(&gfs1_rindex_blks);
@@ -1668,9 +1627,6 @@ int pass1(struct gfs2_sbd *sdp)
/* Make sure the system inodes are okay & represented in the bitmap. */
check_system_inodes(sdp);
- /* Turn the check_i_goal function ON for the non-system inodes */
- pass1_fxns.check_i_goal = check_i_goal;
-
/* So, do we do a depth first search starting at the root
* inode, or use the rg bitmaps, or just read every fs block
* to find the inodes? If we use the depth first search, why
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 10df2d4..d024e56 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -233,6 +233,7 @@ struct gfs2_inode {
struct gfs2_dinode i_di;
struct gfs2_buffer_head *i_bh;
struct gfs2_sbd *i_sbd;
+ struct rgrp_tree *i_rgd; /* The rgrp this inode is in */
};
/* FIXME not sure that i want to keep a record of the inodes or the
diff --git a/tests/fsck.at b/tests/fsck.at
index afe26db..e3b82bd 100644
--- a/tests/fsck.at
+++ b/tests/fsck.at
@@ -11,5 +11,4 @@ AT_CLEANUP
AT_SETUP([Fix invalid goal blocks])
GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set '/' { di_goal_meta: 0 }])
-GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set '/' { di_goal_data: 0 }])
AT_CLEANUP
9 years, 3 months
gfs2-utils: RHEL7 - fsck.gfs2: Reprocess nodes if anything changed - addendum 1 of 2
by Abhijith Das
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=5eccbb33...
Commit: 5eccbb33c28604436d38f4148fbb651151af1aef
Parent: c5b09c4f957d9690807610c8b5e789a280132d2e
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Jan 14 10:54:28 2015 -0600
Committer: Abhi Das <adas(a)redhat.com>
CommitterDate: Thu Jan 15 13:53:21 2015 -0600
fsck.gfs2: Reprocess nodes if anything changed - addendum 1 of 2
Before this patch, fsck.gfs2 called "reprocess_inode" in several
places, especially when the number of blocks had changed from the
original. That works in almost all cases. However, there's a corner
case where a block may be deleted, due to errors (for example, a
bad directory leaf), and another block takes its place. In that
case the count of the number of blocks is still the same, but the
inode should be reprocessed anyway, because the deleted blocks
and replacement blocks may be at different locations or maybe of
different types. A hash table block may be "data" when it's freed,
but if the block is reused, it may be repurposed as a "leaf" block.
That may confuse subsequent processing. Another reason to call
reprocess_inode is when the goal block changes to a value outside
the resource group, due to block allocations for repairs.
This patch is part 1 of 2 addendum fixes to make fsck fix i_goal
values correctly. The original three patches to fix this problem were
checked in against a wrong bz 1149516 (which is the rhel6 version
of the bug). The complete fix contains those three patches (b09dae8,
d79246d, c5b09c4) and this two-patch addendum for a total of 5
patches.
Resolves: rhbz#1178604
Signed-off-by: Bob Peterson <rpeterso(a)redhat.com>
---
gfs2/fsck/lost_n_found.c | 6 ------
gfs2/fsck/metawalk.c | 6 +++---
gfs2/fsck/pass2.c | 22 ++++++++++++----------
gfs2/fsck/pass3.c | 17 +++++++++++++----
gfs2/fsck/pass4.c | 6 ++++++
gfs2/fsck/util.h | 20 ++++++++++++++++++++
6 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 9672c7a..b2e35e2 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -174,7 +174,6 @@ void make_sure_lf_exists(struct gfs2_inode *ip)
int add_inode_to_lf(struct gfs2_inode *ip){
char tmp_name[256];
__be32 inode_type;
- uint64_t lf_blocks;
struct gfs2_sbd *sdp = ip->i_sbd;
int err = 0;
uint32_t mode;
@@ -184,7 +183,6 @@ int add_inode_to_lf(struct gfs2_inode *ip){
log_err( _("Trying to add lost+found to itself...skipping"));
return 0;
}
- lf_blocks = lf_dip->i_di.di_blocks;
if (sdp->gfs1)
mode = gfs_to_gfs2_mode(ip);
@@ -242,10 +240,6 @@ int add_inode_to_lf(struct gfs2_inode *ip){
tmp_name, strerror(errno));
exit(FSCK_ERROR);
}
- /* If the lf directory had new blocks added we have to mark them
- properly in the bitmap so they're not freed. */
- if (lf_dip->i_di.di_blocks != lf_blocks)
- reprocess_inode(lf_dip, "lost+found");
/* This inode is linked from lost+found */
incr_link_count(ip->i_di.di_num, lf_dip, _("from lost+found"));
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 9aa9fb8..82abb40 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1639,11 +1639,11 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
{
struct gfs2_inode *ip;
int error = 0;
- uint64_t cur_blks;
+ struct alloc_state as;
ip = fsck_load_inode(sdp, block);
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
if (ip->i_di.di_flags & GFS2_DIF_EXHASH)
error = check_leaf_blks(ip, pass);
@@ -1653,7 +1653,7 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
if (error < 0)
stack;
- if (ip->i_di.di_blocks != cur_blks)
+ if (astate_changed(ip, &as))
reprocess_inode(ip, _("Current"));
fsck_inode_put(&ip); /* does a brelse */
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 2971b8c..40c80b9 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1712,7 +1712,8 @@ build_it:
static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
int builder(struct gfs2_sbd *sdp))
{
- uint64_t iblock = 0, cur_blks;
+ uint64_t iblock = 0;
+ struct alloc_state as;
struct dir_status ds = {0};
int error = 0;
@@ -1729,14 +1730,14 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
pass2_fxns.private = (void *) &ds;
if (ds.q == gfs2_bad_block) {
- cur_blks = sysinode->i_di.di_blocks;
+ astate_save(sysinode, &as);
/* First check that the directory's metatree is valid */
error = check_metatree(sysinode, &pass2_fxns);
if (error < 0) {
stack;
return error;
}
- if (sysinode->i_di.di_blocks != cur_blks)
+ if (astate_changed(sysinode, &as))
reprocess_inode(sysinode, _("System inode"));
}
error = check_dir(sysinode->i_sbd, iblock, &pass2_fxns);
@@ -1757,7 +1758,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
if (!ds.dotdir) {
log_err( _("No '.' entry found for %s directory.\n"), dirname);
if (query( _("Is it okay to add '.' entry? (y/n) "))) {
- cur_blks = sysinode->i_di.di_blocks;
+ astate_save(sysinode, &as);
log_warn( _("Adding '.' entry\n"));
error = dir_add(sysinode, ".", 1, &(sysinode->i_di.di_num),
(sysinode->i_sbd->gfs1 ? GFS_FILE_DIR : DT_DIR));
@@ -1766,7 +1767,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
strerror(errno));
return -errno;
}
- if (cur_blks != sysinode->i_di.di_blocks)
+ if (astate_changed(sysinode, &as))
reprocess_inode(sysinode, dirname);
/* This system inode is linked to itself via '.' */
incr_link_count(sysinode->i_di.di_num, sysinode,
@@ -1848,7 +1849,8 @@ static inline int is_system_dir(struct gfs2_sbd *sdp, uint64_t block)
*/
int pass2(struct gfs2_sbd *sdp)
{
- uint64_t dirblk, cur_blks;
+ uint64_t dirblk;
+ struct alloc_state as;
uint8_t q;
struct dir_status ds = {0};
struct gfs2_inode *ip;
@@ -1915,14 +1917,14 @@ int pass2(struct gfs2_sbd *sdp)
/* First check that the directory's metatree
* is valid */
ip = fsck_load_inode(sdp, dirblk);
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
error = check_metatree(ip, &pass2_fxns);
fsck_inode_put(&ip);
if (error < 0) {
stack;
return error;
}
- if (ip->i_di.di_blocks != cur_blks)
+ if (astate_changed(ip, &as))
reprocess_inode(ip, "current");
}
error = check_dir(sdp, dirblk, &pass2_fxns);
@@ -1982,7 +1984,7 @@ int pass2(struct gfs2_sbd *sdp)
(unsigned long long)dirblk);
if (query( _("Is it okay to add '.' entry? (y/n) "))) {
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
error = dir_add(ip, ".", 1, &(ip->i_di.di_num),
(sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
if (error) {
@@ -1990,7 +1992,7 @@ int pass2(struct gfs2_sbd *sdp)
strerror(errno));
return -errno;
}
- if (cur_blks != ip->i_di.di_blocks) {
+ if (astate_changed(ip, &as)) {
char dirname[80];
sprintf(dirname, _("Directory at %lld "
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index 9582b5b..167dac0 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -24,7 +24,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
int filename_len = 2;
int err;
struct gfs2_inode *ip, *pip;
- uint64_t cur_blks;
+ struct alloc_state as;
ip = fsck_load_inode(sdp, block);
pip = fsck_load_inode(sdp, newdotdot);
@@ -39,7 +39,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
log_warn( _("Unable to remove \"..\" directory entry.\n"));
else
decr_link_count(olddotdot, block, _("old \"..\""));
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
err = dir_add(ip, filename, filename_len, &pip->i_di.di_num,
(sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
if (err) {
@@ -47,7 +47,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
filename, strerror(errno));
exit(FSCK_ERROR);
}
- if (cur_blks != ip->i_di.di_blocks) {
+ if (astate_changed(ip, &as)) {
char dirname[80];
sprintf(dirname, _("Directory at %lld (0x%llx)"),
@@ -179,6 +179,7 @@ int pass3(struct gfs2_sbd *sdp)
struct dir_info *di, *tdi;
struct gfs2_inode *ip;
uint8_t q;
+ struct alloc_state lf_as = {.as_blocks = 0, .as_meta_goal = 0};
di = dirtree_find(sdp->md.rooti->i_di.di_num.no_addr);
if (di) {
@@ -225,6 +226,8 @@ int pass3(struct gfs2_sbd *sdp)
*/
log_info( _("Checking directory linkage.\n"));
for (tmp = osi_first(&dirtree); tmp; tmp = next) {
+ if (lf_dip && lf_as.as_blocks == 0)
+ astate_save(lf_dip, &lf_as);
next = osi_next(tmp);
di = (struct dir_info *)tmp;
while (!di->checked) {
@@ -327,8 +330,14 @@ int pass3(struct gfs2_sbd *sdp)
break;
}
}
- if (lf_dip)
+ if (lf_dip) {
+ /* If the lf directory had new blocks added we have to mark
+ them properly in the blockmap so they're not freed. */
+ if (astate_changed(lf_dip, &lf_as))
+ reprocess_inode(lf_dip, "lost+found");
+
log_debug( _("At end of pass3, lost+found entries is %u\n"),
lf_dip->i_di.di_entries);
+ }
return FSCK_OK;
}
diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c
index 7b3cb87..f0c13fc 100644
--- a/gfs2/fsck/pass4.c
+++ b/gfs2/fsck/pass4.c
@@ -48,12 +48,15 @@ static int scan_inode_list(struct gfs2_sbd *sdp) {
struct gfs2_inode *ip;
int lf_addition = 0;
uint8_t q;
+ struct alloc_state lf_as = {.as_blocks = 0, .as_meta_goal = 0};
/* FIXME: should probably factor this out into a generic
* scanning fxn */
for (tmp = osi_first(&inodetree); tmp; tmp = next) {
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return 0;
+ if (lf_dip && lf_as.as_blocks == 0)
+ astate_save(lf_dip, &lf_as);
next = osi_next(tmp);
if (!(ii = (struct inode_info *)tmp)) {
log_crit( _("osi_tree broken in scan_info_list!!\n"));
@@ -182,6 +185,9 @@ static int scan_inode_list(struct gfs2_sbd *sdp) {
(unsigned long long)ii->di_num.no_addr, ii->di_nlink);
} /* osi_list_foreach(tmp, list) */
+ if (lf_dip && astate_changed(lf_dip, &lf_as))
+ reprocess_inode(lf_dip, "lost+found");
+
if (lf_addition) {
if (!(ii = inodetree_find(lf_dip->i_di.di_num.no_addr))) {
log_crit( _("Unable to find lost+found inode in inode_hash!!\n"));
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
index 276c726..66b9c7a 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -12,6 +12,11 @@
#define INODE_VALID 1
#define INODE_INVALID 0
+struct alloc_state {
+ uint64_t as_blocks;
+ uint64_t as_meta_goal;
+};
+
struct di_info *search_list(osi_list_t *list, uint64_t addr);
void big_file_comfort(struct gfs2_inode *ip, uint64_t blks_checked);
void warm_fuzzy_stuff(uint64_t block);
@@ -27,6 +32,21 @@ extern const char *reftypes[ref_types + 1];
#define BLOCKMAP_BYTE_OFFSET4(x) (((x) & 0x0000000000000001) << 2)
#define BLOCKMAP_MASK4 (0xf)
+static inline void astate_save(struct gfs2_inode *ip, struct alloc_state *as)
+{
+ as->as_blocks = ip->i_di.di_blocks;
+ as->as_meta_goal = ip->i_di.di_goal_meta;
+}
+
+static inline int astate_changed(struct gfs2_inode *ip, struct alloc_state *as)
+{
+ if (as->as_blocks != ip->i_di.di_blocks)
+ return 1;
+ if (as->as_meta_goal != ip->i_di.di_goal_meta)
+ return 1;
+ return 0;
+}
+
static inline uint8_t block_type(uint64_t bblock)
{
static unsigned char *byte;
9 years, 3 months
gfs2-utils: master - mkfs.gfs2: addendum to fix broken i_goal values in inodes
by Abhijith Das
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=d07e7274...
Commit: d07e72747f3f832d2f68f1a9788358fe4a4739b0
Parent: 601163a19e3cdf7f2b85afe0e2eb68fe132eb3d9
Author: Abhi Das <adas(a)redhat.com>
AuthorDate: Thu Jan 15 12:49:48 2015 -0600
Committer: Abhi Das <adas(a)redhat.com>
CommitterDate: Thu Jan 15 12:49:48 2015 -0600
mkfs.gfs2: addendum to fix broken i_goal values in inodes
This patch moves some code around and fixes some corner cases that
the previous patches did not address.
This patch also fixes some trailing whitespace and removes a test
that is no longer valid from test/fsck.at
Resolves: rhbz#1149516
Signed-off-by: Abhi Das <adas(a)redhat.com>
---
gfs2/fsck/metawalk.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
gfs2/fsck/metawalk.h | 2 +
gfs2/fsck/pass1.c | 54 +++---------------------------------
gfs2/libgfs2/libgfs2.h | 1 +
tests/fsck.at | 1 -
5 files changed, 78 insertions(+), 50 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 217bb07..5f432d6 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1549,6 +1549,9 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
uint64_t error_blk = 0;
int hit_error_blk = 0;
+ if (!height && pass->check_i_goal)
+ pass->check_i_goal(ip, ip->i_di.di_num.no_addr,
+ pass->private);
if (!height && !is_dir(&ip->i_di, ip->i_sbd->gfs1))
return 0;
@@ -1945,6 +1948,72 @@ static int alloc_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
return 0;
}
+/**
+ * rgrp_contains_block - Check if the rgrp provided contains the
+ * given block. Taken directly from the gfs2 kernel code
+ * @rgd: The rgrp to search within
+ * @block: The block to search for
+ *
+ * Returns: 1 if present, 0 if not.
+ */
+static inline int rgrp_contains_block(struct rgrp_tree *rgd, uint64_t block)
+{
+ uint64_t first = rgd->ri.ri_data0;
+ uint64_t last = first + rgd->ri.ri_data;
+ return first <= block && block < last;
+}
+
+/**
+ * check_i_goal
+ * @ip
+ * @goal_blk: What the goal block should be for this inode
+ *
+ * The goal block for a regular file is typically the last
+ * data block of the file. If we can't get the right value,
+ * the inode metadata block is the next best thing.
+ *
+ * Returns: 0 if corrected, 1 if not corrected
+ */
+int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk,
+ void *private)
+{
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ uint64_t i_block = ip->i_di.di_num.no_addr;
+
+ /* Don't fix gfs1 inodes, system inodes or inodes whose goal blocks are
+ * set to the inode blocks themselves. */
+ if (sdp->gfs1 || ip->i_di.di_flags & GFS2_DIF_SYSTEM ||
+ ip->i_di.di_goal_meta == i_block)
+ return 0;
+ /* We default to the inode block */
+ if (!goal_blk)
+ goal_blk = i_block;
+
+ if (ip->i_di.di_goal_meta != goal_blk) {
+ /* If the existing goal block is in the same rgrp as the inode,
+ * we give the benefit of doubt and assume the value is correct */
+ if (ip->i_rgd &&
+ rgrp_contains_block(ip->i_rgd, ip->i_di.di_goal_meta))
+ goto skip;
+ log_err( _("Error: inode %llu (0x%llx) has invalid "
+ "allocation goal block %llu (0x%llx). Should"
+ " be %llu (0x%llx)\n"),
+ (unsigned long long)i_block, (unsigned long long)i_block,
+ (unsigned long long)ip->i_di.di_goal_meta,
+ (unsigned long long)ip->i_di.di_goal_meta,
+ (unsigned long long)goal_blk, (unsigned long long)goal_blk);
+ if (query( _("Fix the invalid goal block? (y/n) "))) {
+ ip->i_di.di_goal_meta = ip->i_di.di_goal_data = goal_blk;
+ bmodified(ip->i_bh);
+ } else {
+ log_err(_("Invalid goal block not fixed.\n"));
+ return 1;
+ }
+ }
+skip:
+ return 0;
+}
+
struct metawalk_fxns alloc_fxns = {
.private = NULL,
.check_leaf = alloc_leaf,
@@ -1955,6 +2024,7 @@ struct metawalk_fxns alloc_fxns = {
.check_dentry = NULL,
.check_eattr_entry = NULL,
.check_eattr_extentry = NULL,
+ .check_i_goal = check_i_goal,
.finish_eattr_indir = NULL,
};
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 0d9de3f..aae9121 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -51,6 +51,8 @@ extern int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
extern int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
int error_on_dinode,
enum gfs2_mark_block new_blockmap_state);
+extern int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk,
+ void *private);
extern void reprocess_inode(struct gfs2_inode *ip, const char *desc);
extern struct duptree *dupfind(uint64_t block);
extern struct gfs2_inode *fsck_system_inode(struct gfs2_sbd *sdp,
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 73b054c..2841b8c 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -62,7 +62,6 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
struct gfs2_ea_header *ea_hdr,
struct gfs2_ea_header *ea_hdr_prev,
void *private);
-static int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk, void *private);
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,
@@ -100,7 +99,7 @@ struct metawalk_fxns pass1_fxns = {
.check_dentry = NULL,
.check_eattr_entry = check_eattr_entries,
.check_eattr_extentry = check_extended_leaf_eattr,
- .check_i_goal = NULL,
+ .check_i_goal = check_i_goal,
.finish_eattr_indir = finish_eattr_indir,
.big_file_msg = big_file_comfort,
.repair_leaf = pass1_repair_leaf,
@@ -799,48 +798,6 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
return error;
}
-/**
- * check_i_goal
- * @ip
- * @goal_blk: What the goal block should be for this inode
- *
- * The goal block for a regular file is typically the last
- * data block of the file. If we can't get the right value,
- * the inode metadata block is the next best thing.
- *
- * Returns: 0 if corrected, 1 if not corrected
- */
-static int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk,
- void *private)
-{
- struct gfs2_sbd *sdp = ip->i_sbd;
-
- if (fsck_system_inode(sdp, ip->i_di.di_num.no_addr))
- return 0;
- if (!goal_blk)
- goal_blk = ip->i_di.di_num.no_addr;
- if (ip->i_di.di_goal_meta != goal_blk ||
- ip->i_di.di_goal_data != goal_blk) {
- log_err( _("Error: inode %llu (0x%llx) has invalid "
- "allocation goal block %llu (0x%llx). Should"
- " be %llu (0x%llx)\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_goal_meta,
- (unsigned long long)ip->i_di.di_goal_meta,
- (unsigned long long)goal_blk,
- (unsigned long long)goal_blk);
- if (query( _("Fix the invalid goal block? (y/n) "))) {
- ip->i_di.di_goal_meta = ip->i_di.di_goal_data = goal_blk;
- bmodified(ip->i_bh);
- } else {
- log_err(_("Invalid goal block not fixed.\n"));
- return 1;
- }
- }
- return 0;
-}
-
static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
uint64_t parent, struct gfs2_buffer_head **bh,
void *private)
@@ -1227,7 +1184,8 @@ bad_dinode:
* handle_di - This is now a wrapper function that takes a gfs2_buffer_head
* and calls handle_ip, which takes an in-code dinode structure.
*/
-static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
+static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
+ struct rgrp_tree *rgd)
{
int error = 0;
uint64_t block = bh->b_blocknr;
@@ -1269,6 +1227,7 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
(unsigned long long)block,
(unsigned long long)block);
}
+ ip->i_rgd = rgd;
error = handle_ip(sdp, ip);
fsck_inode_put(&ip);
return error;
@@ -1595,7 +1554,7 @@ static int pass1_process_bitmap(struct gfs2_sbd *sdp, struct rgrp_tree *rgd, uin
(unsigned long long)block,
(unsigned long long)block);
check_n_fix_bitmap(sdp, block, 0, gfs2_block_free);
- } else if (handle_di(sdp, bh) < 0) {
+ } else if (handle_di(sdp, bh, rgd) < 0) {
stack;
brelse(bh);
gfs2_special_free(&gfs1_rindex_blks);
@@ -1678,9 +1637,6 @@ int pass1(struct gfs2_sbd *sdp)
/* Make sure the system inodes are okay & represented in the bitmap. */
check_system_inodes(sdp);
- /* Turn the check_i_goal function ON for the non-system inodes */
- pass1_fxns.check_i_goal = check_i_goal;
-
/* So, do we do a depth first search starting at the root
* inode, or use the rg bitmaps, or just read every fs block
* to find the inodes? If we use the depth first search, why
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 5bffaed..2907e8c 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -233,6 +233,7 @@ struct gfs2_inode {
struct gfs2_dinode i_di;
struct gfs2_buffer_head *i_bh;
struct gfs2_sbd *i_sbd;
+ struct rgrp_tree *i_rgd; /* The rgrp this inode is in */
};
/* FIXME not sure that i want to keep a record of the inodes or the
diff --git a/tests/fsck.at b/tests/fsck.at
index afe26db..e3b82bd 100644
--- a/tests/fsck.at
+++ b/tests/fsck.at
@@ -11,5 +11,4 @@ AT_CLEANUP
AT_SETUP([Fix invalid goal blocks])
GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set '/' { di_goal_meta: 0 }])
-GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set '/' { di_goal_data: 0 }])
AT_CLEANUP
9 years, 3 months
gfs2-utils: master - fsck.gfs2: Reprocess nodes if anything changed
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=601163a1...
Commit: 601163a19e3cdf7f2b85afe0e2eb68fe132eb3d9
Parent: 818232fbf5b4adb48488877abc7c022894372cd8
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Jan 14 10:54:28 2015 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Thu Jan 15 10:51:35 2015 -0600
fsck.gfs2: Reprocess nodes if anything changed
Before this patch, fsck.gfs2 called "reprocess_inode" in several
places, especially when the number of blocks had changed from the
original. That works in almost all cases. However, there's a corner
case where a block may be deleted, due to errors (for example, a
bad directory leaf), and another block takes its place. In that
case the count of the number of blocks is still the same, but the
inode should be reprocessed anyway, because the deleted blocks
and replacement blocks may be at different locations or maybe of
different types. A hash table block may be "data" when it's freed,
but if the block is reused, it may be repurposed as a "leaf" block.
That may confuse subsequent processing. Another reason to call
reprocess_inode is when the goal block changes to a value outside
the resource group, due to block allocations for repairs.
---
gfs2/fsck/lost_n_found.c | 6 ------
gfs2/fsck/metawalk.c | 6 +++---
gfs2/fsck/pass2.c | 22 ++++++++++++----------
gfs2/fsck/pass3.c | 17 +++++++++++++----
gfs2/fsck/pass4.c | 6 ++++++
gfs2/fsck/util.h | 20 ++++++++++++++++++++
6 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 9672c7a..b2e35e2 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -174,7 +174,6 @@ void make_sure_lf_exists(struct gfs2_inode *ip)
int add_inode_to_lf(struct gfs2_inode *ip){
char tmp_name[256];
__be32 inode_type;
- uint64_t lf_blocks;
struct gfs2_sbd *sdp = ip->i_sbd;
int err = 0;
uint32_t mode;
@@ -184,7 +183,6 @@ int add_inode_to_lf(struct gfs2_inode *ip){
log_err( _("Trying to add lost+found to itself...skipping"));
return 0;
}
- lf_blocks = lf_dip->i_di.di_blocks;
if (sdp->gfs1)
mode = gfs_to_gfs2_mode(ip);
@@ -242,10 +240,6 @@ int add_inode_to_lf(struct gfs2_inode *ip){
tmp_name, strerror(errno));
exit(FSCK_ERROR);
}
- /* If the lf directory had new blocks added we have to mark them
- properly in the bitmap so they're not freed. */
- if (lf_dip->i_di.di_blocks != lf_blocks)
- reprocess_inode(lf_dip, "lost+found");
/* This inode is linked from lost+found */
incr_link_count(ip->i_di.di_num, lf_dip, _("from lost+found"));
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 16faafb..217bb07 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1693,11 +1693,11 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
{
struct gfs2_inode *ip;
int error = 0;
- uint64_t cur_blks;
+ struct alloc_state as;
ip = fsck_load_inode(sdp, block);
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
if (ip->i_di.di_flags & GFS2_DIF_EXHASH)
error = check_leaf_blks(ip, pass);
@@ -1707,7 +1707,7 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
if (error < 0)
stack;
- if (ip->i_di.di_blocks != cur_blks)
+ if (astate_changed(ip, &as))
reprocess_inode(ip, _("Current"));
fsck_inode_put(&ip); /* does a brelse */
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 4ea322a..74bcc26 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1723,7 +1723,8 @@ build_it:
static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
int builder(struct gfs2_sbd *sdp))
{
- uint64_t iblock = 0, cur_blks;
+ uint64_t iblock = 0;
+ struct alloc_state as;
struct dir_status ds = {0};
int error = 0;
@@ -1740,14 +1741,14 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
pass2_fxns.private = (void *) &ds;
if (ds.q == gfs2_bad_block) {
- cur_blks = sysinode->i_di.di_blocks;
+ astate_save(sysinode, &as);
/* First check that the directory's metatree is valid */
error = check_metatree(sysinode, &pass2_fxns);
if (error < 0) {
stack;
return error;
}
- if (sysinode->i_di.di_blocks != cur_blks)
+ if (astate_changed(sysinode, &as))
reprocess_inode(sysinode, _("System inode"));
}
error = check_dir(sysinode->i_sbd, iblock, &pass2_fxns);
@@ -1768,7 +1769,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
if (!ds.dotdir) {
log_err( _("No '.' entry found for %s directory.\n"), dirname);
if (query( _("Is it okay to add '.' entry? (y/n) "))) {
- cur_blks = sysinode->i_di.di_blocks;
+ astate_save(sysinode, &as);
log_warn( _("Adding '.' entry\n"));
error = dir_add(sysinode, ".", 1, &(sysinode->i_di.di_num),
(sysinode->i_sbd->gfs1 ? GFS_FILE_DIR : DT_DIR));
@@ -1777,7 +1778,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
strerror(errno));
return -errno;
}
- if (cur_blks != sysinode->i_di.di_blocks)
+ if (astate_changed(sysinode, &as))
reprocess_inode(sysinode, dirname);
/* This system inode is linked to itself via '.' */
incr_link_count(sysinode->i_di.di_num, sysinode,
@@ -1859,7 +1860,8 @@ static inline int is_system_dir(struct gfs2_sbd *sdp, uint64_t block)
*/
int pass2(struct gfs2_sbd *sdp)
{
- uint64_t dirblk, cur_blks;
+ uint64_t dirblk;
+ struct alloc_state as;
uint8_t q;
struct dir_status ds = {0};
struct gfs2_inode *ip;
@@ -1926,14 +1928,14 @@ int pass2(struct gfs2_sbd *sdp)
/* First check that the directory's metatree
* is valid */
ip = fsck_load_inode(sdp, dirblk);
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
error = check_metatree(ip, &pass2_fxns);
if (error < 0) {
stack;
fsck_inode_put(&ip);
return error;
}
- if (ip->i_di.di_blocks != cur_blks)
+ if (astate_changed(ip, &as))
reprocess_inode(ip, "current");
fsck_inode_put(&ip);
}
@@ -1994,7 +1996,7 @@ int pass2(struct gfs2_sbd *sdp)
(unsigned long long)dirblk);
if (query( _("Is it okay to add '.' entry? (y/n) "))) {
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
error = dir_add(ip, ".", 1, &(ip->i_di.di_num),
(sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
if (error) {
@@ -2003,7 +2005,7 @@ int pass2(struct gfs2_sbd *sdp)
fsck_inode_put(&ip);
return -errno;
}
- if (cur_blks != ip->i_di.di_blocks) {
+ if (astate_changed(ip, &as)) {
char dirname[80];
sprintf(dirname, _("Directory at %lld "
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index 33865df..3e183e5 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -24,7 +24,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
int filename_len = 2;
int err;
struct gfs2_inode *ip, *pip;
- uint64_t cur_blks;
+ struct alloc_state as;
ip = fsck_load_inode(sdp, block);
pip = fsck_load_inode(sdp, newdotdot);
@@ -39,7 +39,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
log_warn( _("Unable to remove \"..\" directory entry.\n"));
else
decr_link_count(olddotdot, block, _("old \"..\""));
- cur_blks = ip->i_di.di_blocks;
+ astate_save(ip, &as);
err = dir_add(ip, filename, filename_len, &pip->i_di.di_num,
(sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
if (err) {
@@ -47,7 +47,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
filename, strerror(errno));
exit(FSCK_ERROR);
}
- if (cur_blks != ip->i_di.di_blocks) {
+ if (astate_changed(ip, &as)) {
char dirname[80];
sprintf(dirname, _("Directory at %lld (0x%llx)"),
@@ -179,6 +179,7 @@ int pass3(struct gfs2_sbd *sdp)
struct dir_info *di, *tdi;
struct gfs2_inode *ip;
uint8_t q;
+ struct alloc_state lf_as = {.as_blocks = 0, .as_meta_goal = 0};
di = dirtree_find(sdp->md.rooti->i_di.di_num.no_addr);
if (di) {
@@ -225,6 +226,8 @@ int pass3(struct gfs2_sbd *sdp)
*/
log_info( _("Checking directory linkage.\n"));
for (tmp = osi_first(&dirtree); tmp; tmp = next) {
+ if (lf_dip && lf_as.as_blocks == 0)
+ astate_save(lf_dip, &lf_as);
next = osi_next(tmp);
di = (struct dir_info *)tmp;
while (!di->checked) {
@@ -330,8 +333,14 @@ int pass3(struct gfs2_sbd *sdp)
break;
}
}
- if (lf_dip)
+ if (lf_dip) {
+ /* If the lf directory had new blocks added we have to mark
+ them properly in the blockmap so they're not freed. */
+ if (astate_changed(lf_dip, &lf_as))
+ reprocess_inode(lf_dip, "lost+found");
+
log_debug( _("At end of pass3, lost+found entries is %u\n"),
lf_dip->i_di.di_entries);
+ }
return FSCK_OK;
}
diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c
index f9d08dc..324ea9f 100644
--- a/gfs2/fsck/pass4.c
+++ b/gfs2/fsck/pass4.c
@@ -48,12 +48,15 @@ static int scan_inode_list(struct gfs2_sbd *sdp) {
struct gfs2_inode *ip;
int lf_addition = 0;
uint8_t q;
+ struct alloc_state lf_as = {.as_blocks = 0, .as_meta_goal = 0};
/* FIXME: should probably factor this out into a generic
* scanning fxn */
for (tmp = osi_first(&inodetree); tmp; tmp = next) {
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return 0;
+ if (lf_dip && lf_as.as_blocks == 0)
+ astate_save(lf_dip, &lf_as);
next = osi_next(tmp);
ii = (struct inode_info *)tmp;
/* Don't check reference counts on the special gfs files */
@@ -179,6 +182,9 @@ static int scan_inode_list(struct gfs2_sbd *sdp) {
(unsigned long long)ii->di_num.no_addr, ii->di_nlink);
} /* osi_list_foreach(tmp, list) */
+ if (lf_dip && astate_changed(lf_dip, &lf_as))
+ reprocess_inode(lf_dip, "lost+found");
+
if (lf_addition) {
if (!(ii = inodetree_find(lf_dip->i_di.di_num.no_addr))) {
log_crit( _("Unable to find lost+found inode in inode_hash!!\n"));
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
index 276c726..66b9c7a 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -12,6 +12,11 @@
#define INODE_VALID 1
#define INODE_INVALID 0
+struct alloc_state {
+ uint64_t as_blocks;
+ uint64_t as_meta_goal;
+};
+
struct di_info *search_list(osi_list_t *list, uint64_t addr);
void big_file_comfort(struct gfs2_inode *ip, uint64_t blks_checked);
void warm_fuzzy_stuff(uint64_t block);
@@ -27,6 +32,21 @@ extern const char *reftypes[ref_types + 1];
#define BLOCKMAP_BYTE_OFFSET4(x) (((x) & 0x0000000000000001) << 2)
#define BLOCKMAP_MASK4 (0xf)
+static inline void astate_save(struct gfs2_inode *ip, struct alloc_state *as)
+{
+ as->as_blocks = ip->i_di.di_blocks;
+ as->as_meta_goal = ip->i_di.di_goal_meta;
+}
+
+static inline int astate_changed(struct gfs2_inode *ip, struct alloc_state *as)
+{
+ if (as->as_blocks != ip->i_di.di_blocks)
+ return 1;
+ if (as->as_meta_goal != ip->i_di.di_goal_meta)
+ return 1;
+ return 0;
+}
+
static inline uint8_t block_type(uint64_t bblock)
{
static unsigned char *byte;
9 years, 3 months
gfs2-utils: master - fsck.gfs2: fix double-free bug
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=818232fb...
Commit: 818232fbf5b4adb48488877abc7c022894372cd8
Parent: 9c750fe0f06a9ab1b7d36c68041d0a61d8cdcede
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Thu Jan 15 09:49:12 2015 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Thu Jan 15 10:39:17 2015 -0600
fsck.gfs2: fix double-free bug
This patch fixes a double-free bug that can occur when errors are found
in an inode and later, it's freed. The problem is that function
free_metalist was using osi_list_del when removing buffer_heads from the
inode in memory. It should be using osi_list_del_init so that the list
head is reinitialized. Otherwise the list head is left with an unknown
value which gets interpreted later on as !list_empty(), so it tries to
free the list a second time.
This also fixes a minor issue with function rangecheck_jmeta, which
frees the buffer_head when it hits an error. The patch sets the returned
*bh value back to NULL to ensure it's not reused. This is just a
precaution I spotted while debugging, and it's worth doing.
---
gfs2/fsck/fs_recovery.c | 1 +
gfs2/fsck/metawalk.c | 2 +-
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/fs_recovery.c b/gfs2/fsck/fs_recovery.c
index 3d8fe98..095d118 100644
--- a/gfs2/fsck/fs_recovery.c
+++ b/gfs2/fsck/fs_recovery.c
@@ -631,6 +631,7 @@ static int rangecheck_jmeta(struct gfs2_inode *ip, uint64_t block,
(unsigned long long)block,
(unsigned long long)block);
brelse(*bh);
+ *bh = NULL;
return meta_skip_further;
}
}
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 1875b24..16faafb 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1203,7 +1203,7 @@ static void free_metalist(struct gfs2_inode *ip, osi_list_t *mlp)
nbh = osi_list_entry(list->next,
struct gfs2_buffer_head, b_altlist);
if (nbh == ip->i_bh)
- osi_list_del(&nbh->b_altlist);
+ osi_list_del_init(&nbh->b_altlist);
else
brelse(nbh);
}
9 years, 3 months
gfs2-utils: master - fsck.gfs2: Fix a use-after-free in pass2
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=9c750fe0...
Commit: 9c750fe0f06a9ab1b7d36c68041d0a61d8cdcede
Parent: 9aedcc0e2e1d1b801bcb0b7d33d1f6e104fd400a
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Jan 14 12:22:23 2015 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Wed Jan 14 14:35:20 2015 -0600
fsck.gfs2: Fix a use-after-free in pass2
This patch fixes a rare code path that's doing use-after-free
in pass2.
---
gfs2/fsck/pass2.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 27b7336..4ea322a 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1928,13 +1928,14 @@ int pass2(struct gfs2_sbd *sdp)
ip = fsck_load_inode(sdp, dirblk);
cur_blks = ip->i_di.di_blocks;
error = check_metatree(ip, &pass2_fxns);
- fsck_inode_put(&ip);
if (error < 0) {
stack;
+ fsck_inode_put(&ip);
return error;
}
if (ip->i_di.di_blocks != cur_blks)
reprocess_inode(ip, "current");
+ fsck_inode_put(&ip);
}
error = check_dir(sdp, dirblk, &pass2_fxns);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
9 years, 3 months
gfs2-utils: master - fsck.gfs2: remove duplicate designation during undo
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=9aedcc0e...
Commit: 9aedcc0e2e1d1b801bcb0b7d33d1f6e104fd400a
Parent: 3a629eccf04f4f7ee960c3ed980352a3d022f8cd
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Fri Jan 9 07:35:46 2015 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri Jan 9 13:31:45 2015 -0600
fsck.gfs2: remove duplicate designation during undo
This patch fixes a problem whereby fsck.gfs2's pass1 would perform this
sequence of events:
1. Metadata block X is identified as being referenced from dinode D1.
2. Metadata block X is identified as being referenced from another dinode, D2,
which makes it a duplicate reference, but so far, no serious errors were
found for that dinode.
3. Dinode D2 is found later to be irreparably damaged, and needs to be removed.
When D2 is deleted, the duplicate reference from D2 is removed and block X is
not freed because D1 still references it. However, it's still marked as a
duplicate and requires processing in pass1b.
Later, pass1b resolves the duplicate and determine's there is really only one
reference remaining, so it makes the correct decision. However, it should not
be necessary. The "undo" functions should remove the duplicate reference if
(and only if) the only reference was from D2. Note, though, that if the
corruption is found later in the cycle (after "undo" is possible) the duplicate
reference MUST remain and be resolved by pass1b.
---
gfs2/fsck/pass1.c | 12 +++++++++++-
1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index a4ba04c..73b054c 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -364,6 +364,11 @@ static int undo_reference(struct gfs2_inode *ip, uint64_t block, int meta,
"from another inode; not freeing.\n"),
(unsigned long long)block,
(unsigned long long)block);
+ if (dt->refs == 1) {
+ log_err(_("This was the only duplicate "
+ "reference so far; removing it.\n"));
+ dup_delete(dt);
+ }
return 1;
}
}
@@ -1055,8 +1060,13 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
(unsigned long long)ip->i_di.di_num.no_addr);
if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
return meta_is_good;
- else
+ else {
+ log_debug(_("Inode 0x%llx bad pointer tolerance "
+ "exceeded: block 0x%llx.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)block);
return meta_error; /* Exits check_metatree quicker */
+ }
}
return meta_is_good;
}
9 years, 3 months
gfs2-utils: master - fsck.gfs2: Revise "undo" processing
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=3a629ecc...
Commit: 3a629eccf04f4f7ee960c3ed980352a3d022f8cd
Parent: 060e7a17bc411d7e44e6e433ed7e8220c088f839
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Jan 7 08:18:44 2015 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri Jan 9 13:27:11 2015 -0600
fsck.gfs2: Revise "undo" processing
This patch has to do with "undo" processing. This is where pass1 has
processed a bunch of data blocks before encountering an error. If the
error is "fatal" processing is stopped immediately and the work done
to that point (up to the fatal error block) is backed out. If the
error is not fatal, processing may continue for the rest of the
indirect block it's processing. The problem occurs when it finds a
non-fatal error block, then later it decides it cannot continue. In
that case, the "undo" function needs to back out changes, but it
needs to finish working through the data blocks on the rest of that
indirect block. This patch allows it to finish out the rest of the
"undo" work properly.
---
gfs2/fsck/metawalk.c | 46 ++++++++++++++++++++++++++++++++++++----------
1 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index cd224fe..1875b24 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1446,17 +1446,28 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
pass1. Therefore the individual check_data functions
should do a range check. */
rc = pass->check_data(ip, metablock, block, pass->private);
- if (!error && rc) {
- error = rc;
+ if (rc && (!error || (rc < error))) {
log_info("\n");
if (rc < 0) {
- *error_blk = block;
+ /* A fatal error trumps a non-fatal one. */
+ if ((*error_blk == 0) || (rc < error)) {
+ log_debug(_("Fatal error on block 0x"
+ "%llx preempts non-fatal "
+ "error on block 0x%llx\n"),
+ (unsigned long long)block,
+ (unsigned long long)*error_blk);
+ *error_blk = block;
+ }
log_info(_("Unrecoverable "));
+ } else { /* nonfatal error */
+ if ((*error_blk) == 0)
+ *error_blk = block;
}
log_info(_("data block error %d on block %llu "
"(0x%llx).\n"), rc,
(unsigned long long)block,
(unsigned long long)block);
+ error = rc;
}
if (rc < 0)
return rc;
@@ -1467,10 +1478,11 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
uint64_t *ptr_start, char *ptr_end,
- uint64_t error_blk)
+ uint64_t error_blk, int error)
{
int rc = 0;
uint64_t block, *ptr;
+ int found_error_blk = 0;
/* If there isn't much pointer corruption check the pointers */
for (ptr = ptr_start ; (char *)ptr < ptr_end && !fsck_abort; ptr++) {
@@ -1480,13 +1492,25 @@ static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
if (skip_this_pass || fsck_abort)
return 1;
block = be64_to_cpu(*ptr);
- if (block == error_blk)
- return 1;
+ if (block == error_blk) {
+ if (error < 0) { /* A fatal error that stopped it? */
+ log_debug(_("Stopping the undo process: "
+ "fatal error block 0x%llx was "
+ "found.\n"),
+ (unsigned long long)error_blk);
+ return 1;
+ }
+ found_error_blk = 1;
+ log_debug(_("The non-fatal error block 0x%llx was "
+ "found, but undo processing will continue "
+ "until the end of this metadata block.\n"),
+ (unsigned long long)error_blk);
+ }
rc = pass->undo_check_data(ip, block, pass->private);
if (rc < 0)
return rc;
}
- return 0;
+ return found_error_blk;
}
static int hdr_size(struct gfs2_buffer_head *bh, int height)
@@ -1589,9 +1613,11 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
undo_metalist:
if (!error)
goto out;
- log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors.\n"),
+ log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors at "
+ "%lld (0x%llx).\n"),
(unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr);
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)error_blk, (unsigned long long)error_blk);
if (!query( _("Remove the invalid inode? (y/n) "))) {
free_metalist(ip, &metalist[0]);
log_err(_("Invalid inode not deleted.\n"));
@@ -1620,7 +1646,7 @@ undo_metalist:
(uint64_t *)
(bh->b_data + head_size),
(bh->b_data + ip->i_sbd->bsize),
- error_blk);
+ error_blk, error);
if (rc > 0) {
hit_error_blk = 1;
rc = 0;
9 years, 3 months
gfs2-utils: master - fsck.gfs2: Adjust when hash table is doubled
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=060e7a17...
Commit: 060e7a17bc411d7e44e6e433ed7e8220c088f839
Parent: 158ebfe22afff6c3c1a8357c8cbe31c8982cac34
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Jan 6 08:51:20 2015 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri Jan 9 13:17:04 2015 -0600
fsck.gfs2: Adjust when hash table is doubled
If directory entries are found to be on the wrong leaf block, they are
moved as needed. That sometimes requires the leaf block to be split,
which, in turn, sometimes requires the hash table be doubled. The
problem was that the functions that traverse the hash tables were
not taking into account the fact that the hash table could just
unexpectly double, throwing off all the pointers. This patch attempts
to correct that by fixing up the pointers as necessary.
---
gfs2/fsck/metawalk.c | 34 +++++++++++++++++++++++++++++++---
gfs2/fsck/metawalk.h | 2 +-
gfs2/fsck/pass1.c | 2 +-
gfs2/fsck/pass2.c | 25 ++++++++++++++++---------
4 files changed, 49 insertions(+), 14 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 9aa9fb8..cd224fe 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -461,7 +461,7 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
} else {
error = pass->check_dentry(ip, dent, prev, bh,
filename, count,
- lindex,
+ &lindex,
pass->private);
if (error < 0) {
stack;
@@ -504,6 +504,7 @@ int check_leaf(struct gfs2_inode *ip, int lindex, struct metawalk_fxns *pass,
uint32_t count = 0;
struct gfs2_sbd *sdp = ip->i_sbd;
const char *msg;
+ int di_depth = ip->i_di.di_depth;
/* Make sure the block number is in range. */
if (!valid_block(ip->i_sbd, *leaf_no)) {
@@ -604,6 +605,16 @@ int check_leaf(struct gfs2_inode *ip, int lindex, struct metawalk_fxns *pass,
}
}
out:
+ if (di_depth < ip->i_di.di_depth) {
+ log_debug(_("Depth of directory %lld (0x%llx) changed from "
+ "%d to %d; adjusting ref_count from %d to %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ di_depth, ip->i_di.di_depth,
+ *ref_count,
+ (*ref_count) << (ip->i_di.di_depth - di_depth));
+ (*ref_count) <<= (ip->i_di.di_depth - di_depth);
+ }
brelse(lbh);
return 0;
@@ -618,6 +629,16 @@ bad_leaf:
return fix;
}
+ if (di_depth < ip->i_di.di_depth) {
+ log_debug(_("Depth of directory %lld (0x%llx) changed from "
+ "%d to %d. Adjusting ref_count from %d to %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ di_depth, ip->i_di.di_depth,
+ *ref_count,
+ (*ref_count) << (ip->i_di.di_depth - di_depth));
+ (*ref_count) <<= (ip->i_di.di_depth - di_depth);
+ }
return 1;
}
@@ -773,8 +794,13 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
}
error = check_leaf(ip, lindex, pass, &leaf_no, &leaf,
&ref_count);
- if (ref_count != orig_ref_count)
+ if (ref_count != orig_ref_count) {
+ log_debug(_("Ref count of leaf 0x%llx "
+ "changed from %d to %d.\n"),
+ (unsigned long long)leaf_no,
+ orig_ref_count, ref_count);
tbl_valid = 0;
+ }
if (!leaf.lf_next || error)
break;
leaf_no = leaf.lf_next;
@@ -787,6 +813,8 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
(unsigned long long)ip->i_di.di_num.no_addr,
orig_di_depth, ip->i_di.di_depth);
tbl_valid = 0;
+ lindex <<= (ip->i_di.di_depth - orig_di_depth);
+ hsize = (1 << ip->i_di.di_depth);
}
if (orig_di_height != ip->i_di.di_height) {
log_debug(_("Height of 0x%llx changed from %d to "
@@ -1663,7 +1691,7 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
static int remove_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh,
- char *filename, uint32_t *count, int lindex,
+ char *filename, uint32_t *count, int *lindex,
void *private)
{
/* the metawalk_fxn's private field must be set to the dentry
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index b3c1299..0d9de3f 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -130,7 +130,7 @@ struct metawalk_fxns {
struct gfs2_dirent *prev,
struct gfs2_buffer_head *bh,
char *filename, uint32_t *count,
- int lindex, void *private);
+ int *lindex, void *private);
int (*check_eattr_entry) (struct gfs2_inode *ip,
struct gfs2_buffer_head *leaf_bh,
struct gfs2_ea_header *ea_hdr,
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 60f0356..a4ba04c 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -162,7 +162,7 @@ static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
static int resuscitate_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, int lindex, void *priv)
+ uint32_t *count, int *lindex, void *priv)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct gfs2_dirent dentry, *de;
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index bdcf77c..27b7336 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -307,7 +307,7 @@ static int check_leaf_depth(struct gfs2_inode *ip, uint64_t leaf_no,
* or -1 on error
*/
static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
- const char *tmp_name, int lindex, int lindex_max,
+ const char *tmp_name, int *lindex, int lindex_max,
int hash_index, struct gfs2_buffer_head *bh,
struct dir_status *ds, struct gfs2_dirent *dent,
struct gfs2_dirent *de, struct gfs2_dirent *prev_de,
@@ -318,13 +318,14 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
uint64_t planned_leaf, real_leaf;
int li, dest_ref, error;
uint64_t *tbl;
+ int di_depth;
log_err(_("Directory entry '%s' at block %lld (0x%llx) is on the "
"wrong leaf block.\n"), tmp_name,
(unsigned long long)entry->no_addr,
(unsigned long long)entry->no_addr);
log_err(_("Leaf index is: 0x%x. The range for this leaf block is "
- "0x%x - 0x%x\n"), hash_index, lindex, lindex_max);
+ "0x%x - 0x%x\n"), hash_index, *lindex, lindex_max);
if (!query( _("Move the misplaced directory entry to "
"a valid leaf block? (y/n) "))) {
log_err( _("Misplaced directory entry not moved.\n"));
@@ -339,8 +340,8 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
}
planned_leaf = be64_to_cpu(tbl[hash_index]);
log_err(_("Moving it from leaf %llu (0x%llx) to %llu (0x%llx)\n"),
- (unsigned long long)be64_to_cpu(tbl[lindex]),
- (unsigned long long)be64_to_cpu(tbl[lindex]),
+ (unsigned long long)be64_to_cpu(tbl[*lindex]),
+ (unsigned long long)be64_to_cpu(tbl[*lindex]),
(unsigned long long)planned_leaf,
(unsigned long long)planned_leaf);
/* Can't trust lf_depth; we have to count */
@@ -366,10 +367,16 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
return 1; /* nuke the dent upon return */
}
+ di_depth = ip->i_di.di_depth;
if (dir_add(ip, tmp_name, de->de_name_len, &de->de_inum,
de->de_type) == 0) {
log_err(_("The misplaced directory entry was moved to a "
"valid leaf block.\n"));
+ if (ip->i_di.di_depth > di_depth) {
+ log_err(_("Directory hash table was doubled.\n"));
+ hash_index <<= (ip->i_di.di_depth - di_depth);
+ (*lindex) <<= (ip->i_di.di_depth - di_depth);
+ }
if (lgfs2_get_leaf_ptr(ip, hash_index, &real_leaf)) {
log_err(_("Could not read leaf %d in dinode %"PRIu64": %s\n"), hash_index,
(uint64_t)ip->i_di.di_num.no_addr, strerror(errno));
@@ -389,7 +396,7 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
appear later, we'll count it has part of our normal
processing when we get to that leaf block later on in the
hash table. */
- if (hash_index > lindex) {
+ if (hash_index > *lindex) {
log_err(_("Accounting deferred.\n"));
return 1; /* nuke the dent upon return */
}
@@ -655,7 +662,7 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, int lindex, void *priv)
+ uint32_t *count, int *lindex, void *priv)
{
struct gfs2_sbd *sdp = ip->i_sbd;
uint8_t q = 0;
@@ -811,8 +818,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
block have a hash table index that fits */
if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
hash_index = hash_table_index(de->de_hash, ip);
- lindex_max = hash_table_max(lindex, ip, bh);
- if (hash_index < lindex || hash_index > lindex_max) {
+ lindex_max = hash_table_max(*lindex, ip, bh);
+ if (hash_index < *lindex || hash_index > lindex_max) {
int nuke_dent;
nuke_dent = wrong_leaf(ip, &entry, tmp_name, lindex,
@@ -1024,7 +1031,7 @@ static int lost_leaf(struct gfs2_inode *ip, uint64_t *tbl, uint64_t leafno,
static int basic_check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, int lindex, void *priv)
+ uint32_t *count, int *lindex, void *priv)
{
uint8_t q = 0;
char tmp_name[MAX_FILENAME];
9 years, 3 months
gfs2-utils: master - fsck.gfs2: Print out block number when pass3 finds a bad directory
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=158ebfe2...
Commit: 158ebfe22afff6c3c1a8357c8cbe31c8982cac34
Parent: ab8b0e0ba2fff7585a386673ff378f3cdc992063
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Dec 17 10:22:25 2014 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri Jan 9 13:14:30 2015 -0600
fsck.gfs2: Print out block number when pass3 finds a bad directory
This patch changes pass3 so that it prints out the directory inode
number when it finds a directory containing a bad block.
---
gfs2/fsck/pass3.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index 9582b5b..33865df 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -246,7 +246,10 @@ int pass3(struct gfs2_sbd *sdp)
q = block_type(di->dinode.no_addr);
if (q == gfs2_bad_block) {
log_err( _("Found unlinked directory "
- "containing bad block\n"));
+ "containing bad block at block %llu"
+ " (0x%llx)\n"),
+ (unsigned long long)di->dinode.no_addr,
+ (unsigned long long)di->dinode.no_addr);
if (query(_("Clear unlinked directory "
"with bad blocks? (y/n) "))) {
log_warn( _("inode %lld (0x%llx) is "
9 years, 3 months