Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=84d59ca4ac7e0…
Commit: 84d59ca4ac7e0c1479c5a9718f622bcbcbd09adb
Parent: 6839b4acd056e8267b4fb1872e3d0783f49a4ef6
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Apr 2 13:03:15 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:27:55 2013 -0500
fsck.gfs2: don't remove buffers from the list when errors are found
Before this patch, if an error was encountered while marking the
data blocks, the blocks would be removed from the linked list.
Now that we've got "undo" functions, we need to be able to undo
the designations of those blocks, which means we need to keep those
buffers on the linked list so they're found later. If we don't,
the undo data block function won't process them, and therefore they'll
be marked as "data" blocks in the bitmap, but no files will reference
the blocks (because the error causes the inode to be deleted).
With this patch, the metadata that points to the faulty data is kept
on the linked list, and after the error is found, the undo function
will therefore find it and mark its blocks as "free".
rhbz#902920
---
gfs2/fsck/metawalk.c | 17 +++++------------
1 files changed, 5 insertions(+), 12 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 1cd377e..16ddb97 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1381,7 +1381,7 @@ static int hdr_size(struct gfs2_buffer_head *bh, int height)
int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
{
osi_list_t metalist[GFS2_MAX_META_HEIGHT];
- osi_list_t *list;
+ osi_list_t *list, *tmp;
struct gfs2_buffer_head *bh;
uint32_t height = ip->i_di.di_height;
int i, head_size;
@@ -1421,23 +1421,16 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
last_reported_fblock = -10000000;
- while (!error && !osi_list_empty(list)) {
+ for (tmp = list->next; !error && tmp != list; tmp = tmp->next) {
if (fsck_abort) {
free_metalist(ip, &metalist[0]);
return 0;
}
- bh = osi_list_entry(list->next, struct gfs2_buffer_head,
- b_altlist);
-
+ bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist);
head_size = hdr_size(bh, height);
- if (!head_size) {
- if (bh == ip->i_bh)
- osi_list_del(&bh->b_altlist);
- else
- brelse(bh);
+ if (!head_size)
continue;
- }
-
+
if (pass->check_data)
error = check_data(ip, pass, bh, head_size,
&blks_checked);
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=1db69f5d39848…
Commit: 1db69f5d398482c2e121404c4376f6fb75031a70
Parent: b178d9ade44ed9a8e8609920b96960d1e79f7c84
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Apr 2 12:24:15 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:26:20 2013 -0500
fsck.gfs2: Do not invalidate metablocks of dinodes with invalid mode
Before this patch, when fsck.gfs2 encountered a dinode with an invalid
mode, it would take steps to invalidate its metadata. That's wrong
because if the mode is invalid, you don't know how to treat it.
It's especially wrong if its metadata references the same blocks
that other valid dinodes reference, because then we could end up
deleting blocks belonging to valid files and directories.
rhbz#902920
---
gfs2/fsck/pass1.c | 25 ++++++++-----------------
1 files changed, 8 insertions(+), 17 deletions(-)
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 92a8ff8..90b865f 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -1004,23 +1004,14 @@ static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip)
error = set_ip_blockmap(ip, 1);
if (error == -EINVAL) {
- /* We found a dinode that has an invalid mode, so we can't
- tell if it's a data file, directory or a socket.
- Regardless, we have to invalidate its metadata in case there
- are duplicate blocks referenced. If we don't call
- check_metatree, the blocks it references will be deleted
- wholesale by pass2, and if any of those blocks are
- duplicates--referenced by another dinode for some reason--
- we will mark it free, even though it's in use. In other
- words, we would introduce file system corruption. So we
- need to keep track of the fact that it's invalid and
- skip parts that we can't be sure of based on dinode type. */
- log_debug("Invalid mode dinode found at block %lld (0x%llx): "
- "Invalidating all its metadata.\n",
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr);
- check_metatree(ip, &invalidate_fxns);
- check_inode_eattr(ip, &invalidate_fxns);
+ /* We found a dinode that has an invalid mode. At this point
+ set_ip_blockmap returned an error, which means it never
+ got inserted into the inode tree. Since we haven't even
+ processed its metadata with pass1_fxns, none of its
+ metadata will be flagged as metadata or data blocks yet.
+ Therefore, we don't need to invalidate anything. */
+ fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+ _("invalid mode"), gfs2_block_free);
return 0;
} else if (error)
goto bad_dinode;
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=b178d9ade44ed…
Commit: b178d9ade44ed9a8e8609920b96960d1e79f7c84
Parent: d5a2afb07d27e1822f14d1dd3b33464a738fa6f6
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Apr 2 11:50:31 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:26:12 2013 -0500
fsck.gfs2: print block count values when fixing them
Before this patch, block counts were fixed, but it didn't log what
the new value was changed to. That made it very difficult to track
down block count problems. This patch changes the logging so that
it prints the new block count, and a breakdown of how many blocks
were counted for metadata, data, extended attributes, etc.
rhbz#902920
---
gfs2/fsck/pass1.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index a4967a4..92a8ff8 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -612,7 +612,11 @@ static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
ip->i_di.di_blocks = 1 + bc->indir_count +
bc->data_count + bc->ea_count;
bmodified(ip->i_bh);
- log_err( _("Block count fixed.\n"));
+ log_err(_("Block count fixed: 1+%lld+%lld+%lld = %lld.\n"),
+ (unsigned long long)bc->indir_count,
+ (unsigned long long)bc->data_count,
+ (unsigned long long)bc->ea_count,
+ (unsigned long long)ip->i_di.di_blocks);
return 1;
}
log_err( _("Block count not fixed.\n"));
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=15cc2f20f2a70…
Commit: 15cc2f20f2a7003f74636b71611991e376007aa9
Parent: f7b8a81a21d93398873fd96dd3329f2e0b120e5b
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Apr 2 11:14:55 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:24:50 2013 -0500
fsck.gfs2: Keep proper counts when duplicates are found
When fsck.gfs2 discovered a duplicate reference to the same block,
it was not properly incrementing the block counters for data and
metadata. Therefore, when the duplicate situation is resolved, the
resulting dinode is likely to have the wrong block count. This patch
makes it increment the counters, regardless of whether the block is
a duplicate reference.
rhbz#902920
---
gfs2/fsck/pass1.c | 9 ++-------
1 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 05e7c68..cdb1b46 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -217,6 +217,7 @@ static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
/* Note if we've gotten this far, the block has already passed the
check in metawalk: gfs2_check_meta(lbh, GFS2_METATYPE_LF).
So we know it's a leaf block. */
+ bc->indir_count++;
q = block_type(block);
if (q != gfs2_block_free) {
log_err( _("Found duplicate block #%llu (0x%llx) referenced "
@@ -234,7 +235,6 @@ static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
return -EEXIST;
}
fsck_blockmap_set(ip, block, _("directory leaf"), gfs2_leaf_blk);
- bc->indir_count++;
return 0;
}
@@ -400,6 +400,7 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
gfs2_bad_block);
return 1;
}
+ bc->data_count++; /* keep the count sane anyway */
q = block_type(block);
if (q != gfs2_block_free) {
log_err( _("Found duplicate %s block %llu (0x%llx) "
@@ -414,16 +415,11 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
"sort it out in pass1b.\n"));
add_duplicate_ref(ip, block, ref_as_data, 0,
INODE_VALID);
- /* If the prev ref was as data, this is likely a data
- block, so keep the block count for both refs. */
- if (q == gfs2_block_used)
- bc->data_count++;
return 1;
}
log_info( _("The block was invalid as metadata but might be "
"okay as data. I'll sort it out in pass1b.\n"));
add_duplicate_ref(ip, block, ref_as_data, 0, INODE_VALID);
- bc->data_count++;
return 1;
}
/* In gfs1, rgrp indirect blocks are marked in the bitmap as "meta".
@@ -442,7 +438,6 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
fsck_blockmap_set(ip, block, _("jdata"), gfs2_jdata);
} else
fsck_blockmap_set(ip, block, _("data"), gfs2_block_used);
- bc->data_count++;
return 0;
}
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=988a76cb1d229…
Commit: 988a76cb1d229832aea0d0f928ae4a9a24e8feaa
Parent: 790a299e775f2a4aab64b37c3cdf03e11e52ccf5
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Mar 12 06:54:39 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:23:36 2013 -0500
fsck.gfs2: Rework the "undo" functions
In pass1, it traverses the metadata free, processing each dinode and
marking which blocks are used by that dinode. If a dinode is found
to have unrecoverable errors, it does a bunch of work to "undo" the
things it did. This is especially important for the processing of
duplicate block references. Suppose dinode X references block 1234.
Later in pass1, suppose a different dinode, Y, also references block
1234. This is flagged as a duplicate block reference. Still later,
suppose pass1 determines dinode Y is bad. Now it has to undo the
work it did. It needs to properly unmark the data and metadata
blocks it marked as no longer "free" so that valid references that
follow aren't flagged as duplicate references. At the same time,
it needs to unflag block 1234 as a duplicate reference as well, so
that dinode X's original reference is still considered valid.
Before this patch, fsck.gfs2 was trying to traverse the entire
metadata tree for the bad dinode, trying to "undo" the designations.
That becomes a huge problem if the damage was discovered in the
middle of the metadata, in which case it may never have flagged any
of the data blocks as "in use as data" in its blockmap. The result
of "undoing" the designations sometimes resulted in blocks improperly
being marked as "free" when they were, in fact, referenced by other
valid dinodes.
For example, suppose corrupt dinode Y references metadata blocks
1234, 1235, and 1236. Now suppose a serious problem is found as part
of its processing of block 1234, and so it stopped its metadata tree
traversal there. Metadata blocks 1235 and 1236 are still listed as
metadata for the bad dinode, but if we traverse the entire tree,
those two blocks may be improperly processed. If another dinode
actually uses blocks 1235 or 1236, the improper "undo" processing
of those two blocks can screw up the valid references.
This patch reworks the "undo" functions so that the "undo" functions
don't get called on the entire metadata and data of the defective
dinode. Instead, only the metadata and data blocks queued onto the
metadata list are processed. This should ensure that the "undo"
functions only operate on blocks that were processed in the first
place.
rhbz#902920
---
gfs2/fsck/metawalk.c | 119 +++++++++++++++++++++++-----------
gfs2/fsck/metawalk.h | 4 +
gfs2/fsck/pass1.c | 172 +++++++++++++++----------------------------------
3 files changed, 137 insertions(+), 158 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index e0fbd33..39c39ce 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1257,7 +1257,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
if (err < 0) {
stack;
error = err;
- goto fail;
+ return error;
}
if (err > 0) {
if (!error)
@@ -1276,14 +1276,11 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
}
if (!nbh)
nbh = bread(ip->i_sbd, block);
- osi_list_add(&nbh->b_altlist, cur_list);
+ osi_list_add_prev(&nbh->b_altlist, cur_list);
} /* for all data on the indirect block */
} /* for blocks at that height */
} /* for height */
- return error;
-fail:
- free_metalist(ip, mlp);
- return error;
+ return 0;
}
/**
@@ -1329,6 +1326,27 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
return error;
}
+static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
+ uint64_t *ptr_start, char *ptr_end)
+{
+ int rc = 0;
+ uint64_t block, *ptr;
+
+ /* If there isn't much pointer corruption check the pointers */
+ for (ptr = ptr_start ; (char *)ptr < ptr_end && !fsck_abort; ptr++) {
+ if (!*ptr)
+ continue;
+
+ if (skip_this_pass || fsck_abort)
+ return 1;
+ block = be64_to_cpu(*ptr);
+ rc = pass->undo_check_data(ip, block, pass->private);
+ if (rc < 0)
+ return rc;
+ }
+ return 0;
+}
+
static int hdr_size(struct gfs2_buffer_head *bh, int height)
{
if (height > 1) {
@@ -1361,6 +1379,7 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
int i, head_size;
uint64_t blks_checked = 0;
int error, rc;
+ int metadata_clean = 0;
if (!height && !is_dir(&ip->i_di, ip->i_sbd->gfs1))
return 0;
@@ -1372,35 +1391,21 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
error = build_and_check_metalist(ip, &metalist[0], pass);
if (error) {
stack;
- free_metalist(ip, &metalist[0]);
- return error;
+ goto undo_metalist;
}
+ metadata_clean = 1;
/* For directories, we've already checked the "data" blocks which
* comprise the directory hash table, so we perform the directory
* checks and exit. */
if (is_dir(&ip->i_di, ip->i_sbd->gfs1)) {
- free_metalist(ip, &metalist[0]);
if (!(ip->i_di.di_flags & GFS2_DIF_EXHASH))
- return 0;
+ goto out;
/* check validity of leaf blocks and leaf chains */
error = check_leaf_blks(ip, pass);
- return error;
- }
-
- /* Free the metalist buffers from heights we don't need to check.
- For the rest we'll free as we check them to save time.
- metalist[0] will only have the dinode bh, so we can skip it. */
- for (i = 1; i < height - 1; i++) {
- list = &metalist[i];
- while (!osi_list_empty(list)) {
- bh = osi_list_entry(list->next,
- struct gfs2_buffer_head, b_altlist);
- if (bh == ip->i_bh)
- osi_list_del(&bh->b_altlist);
- else
- brelse(bh);
- }
+ if (error)
+ goto undo_metalist;
+ goto out;
}
/* check data blocks */
@@ -1408,7 +1413,7 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
last_reported_fblock = -10000000;
- while (error >= 0 && !osi_list_empty(list)) {
+ while (!error && !osi_list_empty(list)) {
if (fsck_abort) {
free_metalist(ip, &metalist[0]);
return 0;
@@ -1426,21 +1431,13 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
}
if (pass->check_data)
- rc = check_data(ip, pass, (uint64_t *)
- (bh->b_data + head_size),
- (bh->b_data + ip->i_sbd->bsize),
- &blks_checked);
- else
- rc = 0;
+ error = check_data(ip, pass, (uint64_t *)
+ (bh->b_data + head_size),
+ (bh->b_data + ip->i_sbd->bsize),
+ &blks_checked);
- if (rc && (!error || rc < 0))
- error = rc;
if (pass->big_file_msg && ip->i_di.di_blocks > COMFORTABLE_BLKS)
pass->big_file_msg(ip, blks_checked);
- if (bh == ip->i_bh)
- osi_list_del(&bh->b_altlist);
- else
- brelse(bh);
}
if (pass->big_file_msg && ip->i_di.di_blocks > COMFORTABLE_BLKS) {
log_notice( _("\rLarge file at %lld (0x%llx) - 100 percent "
@@ -1450,6 +1447,50 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
(unsigned long long)ip->i_di.di_num.no_addr);
fflush(stdout);
}
+undo_metalist:
+ if (!error)
+ goto out;
+ log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ if (!query( _("Remove the invalid inode? (y/n) "))) {
+ free_metalist(ip, &metalist[0]);
+ log_err(_("Invalid inode not deleted.\n"));
+ return error;
+ }
+ for (i = 0; pass->undo_check_meta && i < height; i++) {
+ while (!osi_list_empty(&metalist[i])) {
+ list = &metalist[i];
+ bh = osi_list_entry(list->next,
+ struct gfs2_buffer_head,
+ b_altlist);
+ log_err(_("Undoing metadata work for block %llu "
+ "(0x%llx)\n"),
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)bh->b_blocknr);
+ if (i)
+ rc = pass->undo_check_meta(ip, bh->b_blocknr,
+ i, pass->private);
+ else
+ rc = 0;
+ if (metadata_clean && rc == 0 && i == height - 1) {
+ head_size = hdr_size(bh, height);
+ if (head_size)
+ undo_check_data(ip, pass, (uint64_t *)
+ (bh->b_data + head_size),
+ (bh->b_data + ip->i_sbd->bsize));
+ }
+ if (bh == ip->i_bh)
+ osi_list_del(&bh->b_altlist);
+ else
+ brelse(bh);
+ }
+ }
+ /* Set the dinode as "bad" so it gets deleted */
+ fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+ _("corrupt"), gfs2_block_free);
+ log_err(_("The corrupt inode was invalidated.\n"));
+out:
free_metalist(ip, &metalist[0]);
return error;
}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 486c6eb..f5e71e1 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -108,6 +108,10 @@ struct metawalk_fxns {
int (*repair_leaf) (struct gfs2_inode *ip, uint64_t *leaf_no,
int lindex, int ref_count, const char *msg,
void *private);
+ int (*undo_check_meta) (struct gfs2_inode *ip, uint64_t block,
+ int h, void *private);
+ int (*undo_check_data) (struct gfs2_inode *ip, uint64_t block,
+ void *private);
};
#endif /* _METAWALK_H */
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 6f9a953..05e7c68 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -38,8 +38,7 @@ static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private);
static int check_metalist(struct gfs2_inode *ip, uint64_t block,
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, int h,
- void *private);
+ 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);
@@ -103,12 +102,8 @@ struct metawalk_fxns pass1_fxns = {
.finish_eattr_indir = finish_eattr_indir,
.big_file_msg = big_file_comfort,
.repair_leaf = pass1_repair_leaf,
-};
-
-struct metawalk_fxns undo_fxns = {
- .private = NULL,
- .check_metalist = undo_check_metalist,
- .check_data = undo_check_data,
+ .undo_check_meta = undo_check_metalist,
+ .undo_check_data = undo_check_data,
};
struct metawalk_fxns invalidate_fxns = {
@@ -325,53 +320,67 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
return 0;
}
-static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
- struct gfs2_buffer_head **bh, int h,
- void *private)
+/* undo_reference - undo previously processed data or metadata
+ * We've treated the metadata for this dinode as good so far, but not we
+ * realize it's bad. So we need to undo what we've done.
+ *
+ * Returns: 0 - We need to process the block as metadata. In other words,
+ * we need to undo any blocks it refers to.
+ * 1 - We can't process the block as metadata.
+ */
+
+static int undo_reference(struct gfs2_inode *ip, uint64_t block, int meta,
+ void *private)
{
- int found_dup = 0, iblk_type;
- struct gfs2_buffer_head *nbh;
struct block_count *bc = (struct block_count *)private;
-
- *bh = NULL;
+ struct duptree *dt;
+ struct inode_with_dups *id;
if (!valid_block(ip->i_sbd, block)) { /* blk outside of FS */
fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
_("bad block referencing"), gfs2_block_free);
return 1;
}
- if (is_dir(&ip->i_di, ip->i_sbd->gfs1) && h == ip->i_di.di_height)
- iblk_type = GFS2_METATYPE_JD;
- else
- iblk_type = GFS2_METATYPE_IN;
- found_dup = find_remove_dup(ip, block, _("Metadata"));
- nbh = bread(ip->i_sbd, block);
+ if (meta)
+ bc->indir_count--;
+ dt = dupfind(block);
+ if (dt) {
+ /* remove all duplicate reference structures from this inode */
+ do {
+ id = find_dup_ref_inode(dt, ip);
+ if (!id)
+ break;
- if (gfs2_check_meta(nbh, iblk_type)) {
- if (!found_dup) {
- fsck_blockmap_set(ip, block, _("bad indirect"),
- gfs2_block_free);
- brelse(nbh);
+ dup_listent_delete(id);
+ } while (id);
+
+ if (dt->refs) {
+ log_err(_("Block %llu (0x%llx) is still referenced "
+ "from another inode; not freeing.\n"),
+ (unsigned long long)block,
+ (unsigned long long)block);
return 1;
}
- brelse(nbh);
- nbh = NULL;
- } else /* blk check ok */
- *bh = nbh;
-
- bc->indir_count--;
- if (found_dup) {
- if (nbh)
- brelse(nbh);
- *bh = NULL;
- return 1; /* don't process the metadata again */
- } else
- fsck_blockmap_set(ip, block, _("bad indirect"),
- gfs2_block_free);
+ }
+ fsck_blockmap_set(ip, block,
+ meta ? _("bad indirect") : _("referenced data"),
+ gfs2_block_free);
return 0;
}
+static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
+ int h, void *private)
+{
+ return undo_reference(ip, block, 1, private);
+}
+
+static int undo_check_data(struct gfs2_inode *ip, uint64_t block,
+ void *private)
+{
+ return undo_reference(ip, block, 0, private);
+}
+
static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
{
uint8_t q;
@@ -437,71 +446,9 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
return 0;
}
-static int undo_check_data(struct gfs2_inode *ip, uint64_t block,
- void *private)
-{
- struct block_count *bc = (struct block_count *) private;
-
- if (!valid_block(ip->i_sbd, block)) {
- /* Mark the owner of this block with the bad_block
- * designator so we know to check it for out of range
- * blocks later */
- fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
- _("bad (invalid or out of range) data"),
- gfs2_block_free);
- return 1;
- }
- bc->data_count--;
- return free_block_if_notdup(ip, block, _("data"));
-}
-
static int remove_inode_eattr(struct gfs2_inode *ip, struct block_count *bc)
{
- struct duptree *dt;
- struct inode_with_dups *id;
- osi_list_t *ref;
- int moved = 0;
-
- /* If it's a duplicate reference to the block, we need to check
- if the reference is on the valid or invalid inodes list.
- If it's on the valid inode's list, move it to the invalid
- inodes list. The reason is simple: This inode, although
- valid, has an now-invalid reference, so we should not give
- this reference preferential treatment over others. */
- dt = dupfind(ip->i_di.di_eattr);
- if (dt) {
- osi_list_foreach(ref, &dt->ref_inode_list) {
- id = osi_list_entry(ref, struct inode_with_dups, list);
- if (id->block_no == ip->i_di.di_num.no_addr) {
- log_debug( _("Moving inode %lld (0x%llx)'s "
- "duplicate reference to %lld "
- "(0x%llx) from the valid to the "
- "invalid reference list.\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_eattr,
- (unsigned long long)
- ip->i_di.di_eattr);
- /* Move from the normal to the invalid list */
- osi_list_del(&id->list);
- osi_list_add_prev(&id->list,
- &dt->ref_invinode_list);
- moved = 1;
- break;
- }
- }
- if (!moved)
- log_debug( _("Duplicate reference to %lld "
- "(0x%llx) not moved.\n"),
- (unsigned long long)ip->i_di.di_eattr,
- (unsigned long long)ip->i_di.di_eattr);
- } else {
- delete_block(ip, ip->i_di.di_eattr, NULL,
- "extended attribute", NULL);
- }
+ undo_reference(ip, ip->i_di.di_eattr, 0, bc);
ip->i_di.di_eattr = 0;
bc->ea_count = 0;
ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count;
@@ -1079,23 +1026,10 @@ static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip)
if (lf_dip && lf_dip->i_di.di_blocks != lf_blks)
reprocess_inode(lf_dip, "lost+found");
- if (fsck_abort || error < 0)
+ /* We there was an error, we return 0 because we want fsck to continue
+ and analyze the other dinodes as well. */
+ if (fsck_abort || error != 0)
return 0;
- if (error > 0) {
- log_err( _("Error: inode %llu (0x%llx) has unrecoverable "
- "errors; invalidating.\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr);
- undo_fxns.private = &bc;
- check_metatree(ip, &undo_fxns);
- /* If we undo the metadata accounting, including metadatas
- duplicate block status, we need to make sure later passes
- don't try to free up the metadata referenced by this inode.
- Therefore we mark the inode as free space. */
- fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
- _("corrupt"), gfs2_block_free);
- return 0;
- }
error = check_inode_eattr(ip, &pass1_fxns);
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=7b98f01103283…
Commit: 7b98f01103283f5d925b03d643999b93617299c6
Parent: 7467be5dc58d4b71281666f365fd01fde91e87fd
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Fri Mar 8 11:50:56 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:16:51 2013 -0500
fsck.gfs2: link dinodes that only have extended attribute problems
The job of pass1b is to resolve duplicate references to the same block.
Eventually it does a fair job of determining the rightful owner of the
block, and then it has to deal with the other dinode(s) that referenced
the block improperly. If another dinode improperly referenced the block
as data or metadata, it's obvious file corruption and the dinode should
be deleted. However, if the other dinode improperly referenced the
block as an extended attribute, it can fix the situation by removing
the extended attributes from the dinode. Prior to this patch, there
was a check in the code for this situation so that the dinode was only
deleted if the bad block reference was as data or metadata. However,
regardless of the situation, the code removed the inode from the
inode rbtree. That resulted in the dinode being considered unlinked,
so it would get improperly tossed into lost+found and left in a
indeterminate state. Subsequent runs of fsck.gfs2 could find the
discrepancy and flag it as unlinked again. This patch adds another
check so that the inode is not removed from the inode rbtree, so it
is linked properly during pass2.
rhbz#902920
---
gfs2/fsck/pass1b.c | 9 ++++++---
1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index c9e6494..1cc2473 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -519,9 +519,12 @@ static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *b,
(unsigned long long)id->block_no);
ip = fsck_load_inode(sdp, id->block_no);
- ii = inodetree_find(ip->i_di.di_num.no_addr);
- if (ii)
- inodetree_delete(ii);
+ if (id->reftypecount[ref_as_data] ||
+ id->reftypecount[ref_as_meta]) {
+ ii = inodetree_find(ip->i_di.di_num.no_addr);
+ if (ii)
+ inodetree_delete(ii);
+ }
clear_dup_fxns.private = (void *) dh;
/* Clear the EAs for the inode first */
check_inode_eattr(ip, &clear_dup_fxns);
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=4e4d8b2937e95…
Commit: 4e4d8b2937e9593aa14bd7e1894849021749b52f
Parent: 0bb980d9b18906bce56d7ba4567efb8f5dd377aa
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Feb 25 13:28:00 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:15:28 2013 -0500
fsck.gfs2: Verify dirent hash values correspond to proper leaf block
This patch checks to make sure all the dirents on a leaf block have
hash values that are actually appropriate to the leaf block.
With extended hashing, the file name is used to generate a hash value.
Today's fsck checks that the hash value is proper to the file name,
but that's not enough. The hash value is also shifted by a certain
amount (determined by i_depth) to produce an index into the hash table.
For example, suppose di_depth == 8. Valid indexes into the hash table
go from 0 to 1<<di_depth-1 which is 1<<8-1 which is 256-1 or 255.
Now suppose we have four actual leaf blocks, and each leaf block is
repeated 64 (0x40) times in the index. So the hash table, indexed by
file name would be something like:
entries 00-3f = first leaf block
entries 40-7f = second leaf block
entries 80-bf = third leaf block
entries c0-ff = fourth leaf block
So ht index = name->hash >> (32 - ip->i_depth).
In our example, i_depth is 8, so:
ht index == hash >> (32 - 8) == hash >> 24
In this case, the hash value is shifted by a certain amount to get
the index into the table. For example, file name "Solar" has hash
value 0x59f4dde1. So the hr index == 0x59f4dde1 >> 24 == 0x59.
Therefore, a file with the name "Solar" better appear on the second
leaf, which covers index values from 0x40 to 0x7f.
What this patch does is verify that all the dirents on the first
leaf block have a hash value starting with 0x00 to 0x3f, and all
the dirents on the second leaf block have a hash value starting with
0x40 to 0x7f, and so forth. If they appear on the wrong leaf block,
they need to be relocated to the proper leaf block.
rhbz#902920
---
gfs2/fsck/metawalk.c | 12 ++-
gfs2/fsck/metawalk.h | 3 +-
gfs2/fsck/pass1.c | 2 +-
gfs2/fsck/pass1b.c | 5 +-
gfs2/fsck/pass2.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 197 insertions(+), 15 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 709ff94..6d6156c 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -340,7 +340,8 @@ static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
* -1 - error occurred
*/
static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
- int type, uint32_t *count, struct metawalk_fxns *pass)
+ int type, uint32_t *count, int lindex,
+ struct metawalk_fxns *pass)
{
struct gfs2_dirent *dent;
struct gfs2_dirent de, *prev;
@@ -448,6 +449,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,
pass->private);
if (error < 0) {
stack;
@@ -587,7 +589,8 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
}
if (pass->check_dentry && is_dir(&ip->i_di, sdp->gfs1)) {
- error = check_entries(ip, lbh, DIR_EXHASH, &count, pass);
+ error = check_entries(ip, lbh, DIR_EXHASH, &count, lindex,
+ pass);
if (skip_this_pass || fsck_abort)
goto out;
@@ -1448,7 +1451,7 @@ int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
int error = 0;
uint32_t count = 0;
- error = check_entries(ip, bh, DIR_LINEAR, &count, pass);
+ error = check_entries(ip, bh, DIR_LINEAR, &count, 0, pass);
if (error < 0) {
stack;
return -1;
@@ -1479,7 +1482,8 @@ 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, void *private)
+ char *filename, uint32_t *count, int lindex,
+ void *private)
{
/* the metawalk_fxn's private field must be set to the dentry
* block we want to clear */
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index c43baf0..bef99ae 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -85,7 +85,8 @@ struct metawalk_fxns {
int (*check_dentry) (struct gfs2_inode *ip, struct gfs2_dirent *de,
struct gfs2_dirent *prev,
struct gfs2_buffer_head *bh,
- char *filename, uint32_t *count, void *private);
+ char *filename, uint32_t *count,
+ 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 fe80369..6cce07c 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -147,7 +147,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, 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/pass1b.c b/gfs2/fsck/pass1b.c
index e519c20..c9e6494 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -48,7 +48,8 @@ static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
void *private);
static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
struct gfs2_dirent *prev, struct gfs2_buffer_head *bh,
- char *filename, uint32_t *count, void *priv);
+ char *filename, uint32_t *count, int lindex,
+ void *priv);
struct metawalk_fxns find_refs = {
.private = NULL,
@@ -172,7 +173,7 @@ static int check_dir_dup_ref(struct gfs2_inode *ip, struct gfs2_dirent *de,
static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
struct gfs2_dirent *prev,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, void *priv)
+ uint32_t *count, int lindex, void *priv)
{
struct osi_node *n, *next = NULL;
osi_list_t *tmp2;
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index e67f42a..b1cc6dc 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -299,6 +299,166 @@ static int hash_table_index(uint32_t hash, struct gfs2_inode *ip)
return hash >> (32 - ip->i_di.di_depth);
}
+static int hash_table_max(int lindex, struct gfs2_inode *ip,
+ struct gfs2_buffer_head *bh)
+{
+ struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data;
+ return (1 << (ip->i_di.di_depth - be16_to_cpu(leaf->lf_depth))) +
+ lindex - 1;
+}
+
+static int check_leaf_depth(struct gfs2_inode *ip, uint64_t leaf_no,
+ int ref_count, struct gfs2_buffer_head *lbh)
+{
+ struct gfs2_leaf *leaf = (struct gfs2_leaf *)lbh->b_data;
+ int cur_depth = be16_to_cpu(leaf->lf_depth);
+ int exp_count = 1 << (ip->i_di.di_depth - cur_depth);
+ int divisor;
+ int factor, correct_depth;
+
+ if (exp_count == ref_count)
+ return 0;
+
+ factor = 0;
+ divisor = ref_count;
+ while (divisor > 1) {
+ factor++;
+ divisor >>= 1;
+ }
+ correct_depth = ip->i_di.di_depth - factor;
+ if (cur_depth == correct_depth)
+ return 0;
+
+ log_err(_("Leaf block %llu (0x%llx) in dinode %llu (0x%llx) has the "
+ "wrong depth: is %d (length %d), should be %d (length "
+ "%d).\n"),
+ (unsigned long long)leaf_no, (unsigned long long)leaf_no,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ cur_depth, ref_count, correct_depth, exp_count);
+ if (!query( _("Fix the leaf block? (y/n)"))) {
+ log_err( _("The leaf block was not fixed.\n"));
+ return 0;
+ }
+
+ leaf->lf_depth = cpu_to_be16(correct_depth);
+ bmodified(lbh);
+ log_err( _("The leaf block depth was fixed.\n"));
+ return 1;
+}
+
+/* wrong_leaf: Deal with a dirent discovered to be on the wrong leaf block
+ *
+ * Returns: 1 if the dirent is to be removed, 0 if it needs to be kept,
+ * 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,
+ 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,
+ uint32_t *count, uint8_t q)
+{
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ struct gfs2_buffer_head *dest_lbh;
+ uint64_t planned_leaf, real_leaf;
+ int li, dest_ref, error;
+ uint64_t *tbl;
+
+ 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);
+ if (!query( _("Move the misplaced directory entry to "
+ "a valid leaf block? (y/n) "))) {
+ log_err( _("Misplaced directory entry not moved.\n"));
+ return 0;
+ }
+
+ /* check the destination leaf block's depth */
+ tbl = get_dir_hash(ip);
+ if (tbl == NULL) {
+ perror("get_dir_hash");
+ return -1;
+ }
+ 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)planned_leaf,
+ (unsigned long long)planned_leaf);
+ /* Can't trust lf_depth; we have to count */
+ dest_ref = 0;
+ for (li = 0; li < (1 << ip->i_di.di_depth); li++) {
+ if (be64_to_cpu(tbl[li]) == planned_leaf)
+ dest_ref++;
+ else if (dest_ref)
+ break;
+ }
+ dest_lbh = bread(sdp, planned_leaf);
+ check_leaf_depth(ip, planned_leaf, dest_ref, dest_lbh);
+ brelse(dest_lbh);
+ free(tbl);
+
+ /* check if it's already on the correct leaf block */
+ error = dir_search(ip, tmp_name, de->de_name_len, NULL, &de->de_inum);
+ if (!error) {
+ log_err(_("The misplaced directory entry already appears on "
+ "the correct leaf block.\n"));
+ log_err( _("The bad duplicate directory entry "
+ "'%s' was cleared.\n"), tmp_name);
+ return 1; /* nuke the dent upon return */
+ }
+
+ 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"));
+ gfs2_get_leaf_nr(ip, hash_index, &real_leaf);
+ if (real_leaf != planned_leaf) {
+ log_err(_("The planned leaf was split. The new leaf "
+ "is: %llu (0x%llx)"),
+ (unsigned long long)real_leaf,
+ (unsigned long long)real_leaf);
+ fsck_blockmap_set(ip, real_leaf, _("split leaf"),
+ gfs2_indir_blk);
+ }
+ /* If the misplaced dirent was supposed to be earlier in the
+ hash table, we need to adjust our counts for the blocks
+ that have already been processed. If it's supposed to
+ 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) {
+ log_err(_("Accounting deferred.\n"));
+ return 1; /* nuke the dent upon return */
+ }
+ /* If we get here, it's because we moved a dent to another
+ leaf, but that leaf has already been processed. So we have
+ to nuke the dent from this leaf when we return, but we
+ still need to do the "good dent" accounting. */
+ error = incr_link_count(*entry, ip, _("valid reference"));
+ if (error > 0 &&
+ bad_formal_ino(ip, dent, *entry, tmp_name, q, de, bh) == 1)
+ return 1; /* nuke it */
+
+ /* You cannot do this:
+ (*count)++;
+ The reason is: *count is the count of dentries on the leaf,
+ and we moved the dentry to a previous leaf within the same
+ directory dinode. So the directory counts still get
+ incremented, but not leaf entries. When we called dir_add
+ above, it should have fixed that prev leaf's lf_entries. */
+ ds->entry_count++;
+ return 1;
+ } else {
+ log_err(_("Error moving directory entry.\n"));
+ return 1; /* nuke it */
+ }
+}
+
/* basic_dentry_checks - fundamental checks for directory entries
*
* @ip: pointer to the incode inode structure
@@ -520,9 +680,9 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
/* FIXME: should maybe refactor this a bit - but need to deal with
* FIXMEs internally first */
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, void *priv)
+ struct gfs2_dirent *prev_de,
+ struct gfs2_buffer_head *bh, char *filename,
+ uint32_t *count, int lindex, void *priv)
{
struct gfs2_sbd *sdp = ip->i_sbd;
uint8_t q = 0;
@@ -532,6 +692,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
int error;
struct gfs2_inode *entry_ip = NULL;
struct gfs2_dirent dentry, *de;
+ int hash_index; /* index into the hash table based on the hash */
+ int lindex_max; /* largest acceptable hash table index for hash */
memset(&dentry, 0, sizeof(struct gfs2_dirent));
gfs2_dirent_in(&dentry, (char *)dent);
@@ -672,6 +834,21 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
ds->dotdotdir = 1;
goto dentry_is_valid;
}
+ /* If this is an exhash directory, make sure the dentries in the leaf
+ 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) {
+ int nuke_dent;
+
+ nuke_dent = wrong_leaf(ip, &entry, tmp_name, lindex,
+ lindex_max, hash_index, bh, ds,
+ dent, de, prev_de, count, q);
+ if (nuke_dent)
+ goto nuke_dentry;
+ }
+ }
/* After this point we're only concerned with directories */
if (q != gfs2_inode_dir) {
@@ -703,10 +880,9 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
dentry_is_valid:
/* This directory inode links to this inode via this dentry */
error = incr_link_count(entry, ip, _("valid reference"));
- if (error > 0) {
- if (bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1)
- goto nuke_dentry;
- }
+ if (error > 0 &&
+ bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1)
+ goto nuke_dentry;
(*count)++;
ds->entry_count++;
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=56c536851aed2…
Commit: 56c536851aed2ccb182d0a56981829a3eab57142
Parent: b2533640cb0ec2ff0404a9647defee6b959f2534
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Jan 28 09:11:17 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:12:16 2013 -0500
fsck.gfs2: Special case '..' when processing bad formal inode number
In a recent patch to fsck.gfs2, we added the ability to make sure the
formal inode number in each directory entry matches the formal inode
number in the dinode. If it doesn't match, fsck tries to fix it up.
We can't do much for regular files, but we can fix up directories.
If the directory linkage is intact, it just fixes the formal inode number.
But to check if the directory linkage is intact, we were checking to make
sure the child directory points to the parent with its "..". For example,
suppose we have gfs2 mounted as /mnt/gfs2, and at the root, we have
directory "a", and within "a" we have a subdirectory "b". In other words:
/mnt/gfs2/a/b/...
Now suppose fsck.gfs2 finds a formal inode number mismatch between the
dirent inside "a" which points to "b" and the inode "b" itself. Since
both "a" and "b" are directories, it tries to determine if the directory
linkage is intact by testing whether b's ".." dirent actually points
back to "a". And if it's good, we can just fix the formal inode number
so that they match.
That's all well and good, and works for the most part. However, if
the dirent found to be wrong isn't "b" but ".." we've got a problem.
Today's algorithm would look up the ".." of ".." which won't be
pointingi back to what we want.
For this patch, I'm special-casing ".." and making it just delete the
correct directory entry. However, we have to do it in such a way that
it doesn't decrement di_entries, since the entry is invalid.
rhbz#902920
---
gfs2/fsck/pass2.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 359bca0..c0efacc 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -258,7 +258,7 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
(unsigned long long)entry.no_formal_ino,
(unsigned long long)ii->di_num.no_formal_ino,
(unsigned long long)ii->di_num.no_formal_ino);
- if (q != gfs2_inode_dir) {
+ if (q != gfs2_inode_dir || !strcmp("..", tmp_name)) {
if (query( _("Remove the corrupt directory entry? (y/n) ")))
return 1;
log_err( _("Corrupt directory entry not removed.\n"));
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=b2533640cb0ec…
Commit: b2533640cb0ec2ff0404a9647defee6b959f2534
Parent: d94ef845ac19bed44faaee79d629c9f4b9862a28
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 12:27:40 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:11:02 2013 -0500
fsck.gfs2: Add new function to check dir hash tables
It's very important that fsck.gfs2 checks for a valid directory hash
table before operating further on the directory. Before this patch,
we were doing some incomplete testing, after we had already operated
on the directory, with function check_num_ptrs. This patch replaces
that scheme with a new one that preemptively checks the hash table
with a new function called check_hash_tbl.
We've got to make sure the hash table is sane. Each leaf needs to
be counted a proper power of 2. We can't just have 3 pointers to a leaf.
The number of pointers must correspond to the proper leaf depth, and they
must all fall on power-of-two boundaries. For example, suppose we have
directory that's had one of its middle leaf blocks split several times.
The split history might look something like this:
leaf
split lindex length depth pointers leaf split
----- ------ ------ ----- -------- -------------
0: 0x00 0x100 0 00 - ff <--split to 1:
1: 0x00 0x80 1 00 - 7f <--split to 2:
0x80 0x80 1 80 - ff
2: 0x00 0x40 2 00 - 3f <--split to 3:
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
3: 0x00 0x20 3 00 - 1f
0x20 0x20 3 20 - 3f <--split to 4
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
4: 0x00 0x20 3 00 - 1f
0x20 0x10 4 20 - 2f
0x30 0x10 4 30 - 3f <--split to 5
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
5: 0x00 0x20 3 00 - 1f
0x20 0x10 4 20 - 2f
0x30 0x8 5 30 - 37 <--split to 6
0x38 0x8 5 38 - 3f
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
6: 0x00 0x20 3 00 - 1f
0x20 0x10 4 20 - 2f
0x30 0x4 6 30 - 33
0x34 0x4 6 34 - 37
0x38 0x8 5 38 - 3f
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
You can see from this example that it's impossible for a leaf block to have
a lf_depth of 5 and lindex 0x34. As shown in "5:" above, a leaf depth of 5
can only fall at offset 0x30 or 0x38. If it somehow falls elsewhere, say
0x34, the proper depth should be 6, and there should only be 4 pointers,
as per split "6:" above. The leaf block pointers all need to fall properly
on these boundaries, otherwise the kernel code's calculations will land it
on the wrong leaf block while it's searching, and the result will be files
you can see with ls, but can't open, delete or use them.
rhbz#902920
---
gfs2/fsck/metawalk.c | 174 +++++++++++++++--
gfs2/fsck/metawalk.h | 12 +-
gfs2/fsck/pass1.c | 142 --------------
gfs2/fsck/pass2.c | 532 ++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 700 insertions(+), 160 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index ce80738..d5d518a 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -716,6 +716,24 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
/* Readahead */
dir_leaf_reada(ip, tbl, hsize);
+ if (pass->check_hash_tbl) {
+ error = pass->check_hash_tbl(ip, tbl, hsize, pass->private);
+ if (error < 0) {
+ free(tbl);
+ posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
+ return error;
+ }
+ /* If hash table changes were made, read it in again. */
+ if (error) {
+ free(tbl);
+ tbl = get_dir_hash(ip);
+ if (tbl == NULL) {
+ perror("get_dir_hash");
+ return -1;
+ }
+ }
+ }
+
/* Find the first valid leaf pointer in range and use it as our "old"
leaf. That way, bad blocks at the beginning will be overwritten
with the first valid leaf. */
@@ -768,21 +786,6 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
return 0;
}
- /* If the old leaf was a duplicate referenced by a
- previous dinode, we can't check the number of
- pointers because the number of pointers may be for
- that other dinode's reference, not this one. */
- if (pass->check_num_ptrs && !old_was_dup &&
- valid_block(ip->i_sbd, old_leaf)) {
- error = pass->check_num_ptrs(ip, old_leaf,
- &ref_count,
- &lindex,
- &oldleaf);
- if (error) {
- free(tbl);
- return error;
- }
- }
error = check_leaf(ip, lindex, pass, &ref_count,
&leaf_no, old_leaf, &bad_leaf,
first_ok_leaf, &leaf, &oldleaf);
@@ -1688,3 +1691,144 @@ void reprocess_inode(struct gfs2_inode *ip, const char *desc)
log_err( _("Error %d reprocessing the %s metadata tree.\n"),
error, desc);
}
+
+/*
+ * write_new_leaf - allocate and write a new leaf to cover a gap in hash table
+ * @dip: the directory inode
+ * @start_lindex: where in the hash table to start writing
+ * @num_copies: number of copies of the pointer to write into hash table
+ * @before_or_after: desc. of whether this is being added before/after/etc.
+ * @bn: pointer to return the newly allocated leaf's block number
+ */
+int write_new_leaf(struct gfs2_inode *dip, int start_lindex, int num_copies,
+ const char *before_or_after, uint64_t *bn)
+{
+ struct gfs2_buffer_head *nbh;
+ struct gfs2_leaf *leaf;
+ struct gfs2_dirent *dent;
+ int count, i;
+ int factor = 0, pad_size;
+ uint64_t *cpyptr;
+ char *padbuf;
+ int divisor = num_copies;
+ int end_lindex = start_lindex + num_copies;
+
+ padbuf = malloc(num_copies * sizeof(uint64_t));
+ /* calculate the depth needed for the new leaf */
+ while (divisor > 1) {
+ factor++;
+ divisor /= 2;
+ }
+ /* Make sure the number of copies is properly a factor of 2 */
+ if ((1 << factor) != num_copies) {
+ log_err(_("Program error: num_copies not a factor of 2.\n"));
+ log_err(_("num_copies=%d, dinode = %lld (0x%llx)\n"),
+ num_copies,
+ (unsigned long long)dip->i_di.di_num.no_addr,
+ (unsigned long long)dip->i_di.di_num.no_addr);
+ log_err(_("lindex = %d (0x%x)\n"), start_lindex, start_lindex);
+ stack;
+ free(padbuf);
+ return -1;
+ }
+
+ /* allocate and write out a new leaf block */
+ *bn = meta_alloc(dip);
+ fsck_blockmap_set(dip, *bn, _("directory leaf"), gfs2_leaf_blk);
+ log_err(_("A new directory leaf was allocated at block %lld "
+ "(0x%llx) to fill the %d (0x%x) pointer gap %s the existing "
+ "pointer at index %d (0x%x).\n"), (unsigned long long)*bn,
+ (unsigned long long)*bn, num_copies, num_copies,
+ before_or_after, start_lindex, start_lindex);
+ dip->i_di.di_blocks++;
+ bmodified(dip->i_bh);
+ nbh = bget(dip->i_sbd, *bn);
+ memset(nbh->b_data, 0, dip->i_sbd->bsize);
+ leaf = (struct gfs2_leaf *)nbh->b_data;
+ leaf->lf_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
+ leaf->lf_header.mh_type = cpu_to_be32(GFS2_METATYPE_LF);
+ leaf->lf_header.mh_format = cpu_to_be32(GFS2_FORMAT_LF);
+ leaf->lf_depth = cpu_to_be16(dip->i_di.di_depth - factor);
+
+ /* initialize the first dirent on the new leaf block */
+ dent = (struct gfs2_dirent *)(nbh->b_data + sizeof(struct gfs2_leaf));
+ dent->de_rec_len = cpu_to_be16(dip->i_sbd->bsize -
+ sizeof(struct gfs2_leaf));
+ bmodified(nbh);
+ brelse(nbh);
+
+ /* pad the hash table with the new leaf block */
+ cpyptr = (uint64_t *)padbuf;
+ for (i = start_lindex; i < end_lindex; i++) {
+ *cpyptr = cpu_to_be64(*bn);
+ cpyptr++;
+ }
+ pad_size = num_copies * sizeof(uint64_t);
+ log_err(_("Writing to the hash table of directory %lld "
+ "(0x%llx) at index: 0x%x for 0x%lx pointers.\n"),
+ (unsigned long long)dip->i_di.di_num.no_addr,
+ (unsigned long long)dip->i_di.di_num.no_addr,
+ start_lindex, pad_size / sizeof(uint64_t));
+ if (dip->i_sbd->gfs1)
+ count = gfs1_writei(dip, padbuf, start_lindex *
+ sizeof(uint64_t), pad_size);
+ else
+ count = gfs2_writei(dip, padbuf, start_lindex *
+ sizeof(uint64_t), pad_size);
+ free(padbuf);
+ if (count != pad_size) {
+ log_err( _("Error: bad write while fixing directory leaf "
+ "pointers.\n"));
+ return -1;
+ }
+ return 0;
+}
+
+/* repair_leaf - Warn the user of an error and ask permission to fix it
+ * Process a bad leaf pointer and ask to repair the first time.
+ * The repair process involves extending the previous leaf's entries
+ * so that they replace the bad ones. We have to hack up the old
+ * leaf a bit, but it's better than deleting the whole directory,
+ * which is what used to happen before. */
+int repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no, int lindex,
+ int ref_count, const char *msg)
+{
+ int new_leaf_blks = 0, error, refs;
+ uint64_t bn = 0;
+
+ log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
+ " (0x%llx) %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)*leaf_no,
+ (unsigned long long)*leaf_no, msg);
+ if (!query( _("Attempt to patch around it? (y/n) "))) {
+ log_err( _("Bad leaf left in place.\n"));
+ goto out;
+ }
+ /* We can only write leafs in quantities that are factors of
+ two, since leaves are doubled, not added sequentially.
+ So if we have a hole that's not a factor of 2, we have to
+ break it down into separate leaf blocks that are. */
+ while (ref_count) {
+ refs = 1;
+ while (refs <= ref_count) {
+ if (refs * 2 > ref_count)
+ break;
+ refs *= 2;
+ }
+ error = write_new_leaf(ip, lindex, refs, _("replacing"), &bn);
+ if (error)
+ return error;
+
+ new_leaf_blks++;
+ lindex += refs;
+ ref_count -= refs;
+ }
+ log_err( _("Directory Inode %llu (0x%llx) repaired.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+out:
+ *leaf_no = bn;
+ return new_leaf_blks;
+}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index e114427..c43baf0 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -39,6 +39,11 @@ extern struct gfs2_inode *fsck_system_inode(struct gfs2_sbd *sdp,
uint64_t block);
extern int find_remove_dup(struct gfs2_inode *ip, uint64_t block,
const char *btype);
+extern int write_new_leaf(struct gfs2_inode *dip, int start_lindex,
+ int num_copies, const char *before_or_after,
+ uint64_t *bn);
+extern int repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no, int lindex,
+ int ref_count, const char *msg);
extern int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
const char *btype);
@@ -95,9 +100,10 @@ struct metawalk_fxns {
int (*finish_eattr_indir) (struct gfs2_inode *ip, int leaf_pointers,
int leaf_pointer_errors, void *private);
void (*big_file_msg) (struct gfs2_inode *ip, uint64_t blks_checked);
- int (*check_num_ptrs) (struct gfs2_inode *ip, uint64_t leafno,
- int *ref_count, int *lindex,
- struct gfs2_leaf *leaf);
+ int (*check_hash_tbl) (struct gfs2_inode *ip, uint64_t *tbl,
+ unsigned hsize, void *private);
+ int (*repair_leaf) (struct gfs2_inode *ip, uint64_t *leaf_no,
+ int lindex, int ref_count, const char *msg);
};
#endif /* _METAWALK_H */
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 9a2398a..13cb1cb 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -59,8 +59,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_num_ptrs(struct gfs2_inode *ip, uint64_t leafno,
- int *ref_count, int *lindex, struct gfs2_leaf *leaf);
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,
@@ -91,7 +89,6 @@ struct metawalk_fxns pass1_fxns = {
.check_eattr_extentry = check_extended_leaf_eattr,
.finish_eattr_indir = finish_eattr_indir,
.big_file_msg = big_file_comfort,
- .check_num_ptrs = check_num_ptrs,
};
struct metawalk_fxns undo_fxns = {
@@ -203,145 +200,6 @@ struct metawalk_fxns sysdir_fxns = {
.check_dentry = resuscitate_dentry,
};
-/*
- * 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. */
- if (dip->i_sbd->gfs1)
- count = gfs1_writei(dip, ptrbuf + (off_by * sizeof(uint64_t)),
- start_lindex * sizeof(uint64_t), bufsize);
- else
- 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));
- if (dip->i_sbd->gfs1)
- gfs1_writei(dip, ptrbuf, (start_lindex * sizeof(uint64_t)) +
- bufsize, off_by * sizeof(uint64_t));
- else
- 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;
-}
-
-/**
- * check_num_ptrs - check a previously processed leaf's pointer count in the
- * hash table.
- *
- * The number of pointers in a directory hash table that point to any given
- * leaf block should always be a factor of two. The difference between the
- * leaf block's depth and the dinode's di_depth gives us the factor.
- * This function makes sure the leaf follows the rules properly.
- *
- * ip - pointer to the in-core inode structure
- * leafno - the leaf number we're operating on
- * ref_count - the number of pointers to this leaf we actually counted.
- * exp_count - the number of pointers to this leaf we expect based on
- * ip depth minus leaf depth.
- * lindex - leaf index number
- * leaf - the leaf structure for the leaf block to check
- */
-static int check_num_ptrs(struct gfs2_inode *ip, uint64_t leafno,
- int *ref_count, int *lindex, struct gfs2_leaf *leaf)
-{
- int factor = 0, divisor = *ref_count, multiple = 1, error = 0;
- struct gfs2_buffer_head *lbh;
- int exp_count;
-
- /* 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)leafno,
- (unsigned long long)leafno, *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 based on the leaf depth. */
- exp_count = (1 << (ip->i_di.di_depth - leaf->lf_depth));
- 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)leafno,
- (unsigned long long)leafno, *ref_count, exp_count);
- if (!query( _("Attempt to fix it? (y/n) "))) {
- log_err( _("Directory leaf was not fixed.\n"));
- return 1;
- }
- lbh = bread(ip->i_sbd, leafno);
- gfs2_leaf_in(leaf, lbh);
- log_err( _("Leaf depth was %d, changed to %d\n"),
- leaf->lf_depth, ip->i_di.di_depth - factor);
- leaf->lf_depth = ip->i_di.di_depth - factor;
- gfs2_leaf_out(leaf, lbh);
- brelse(lbh);
- log_err( _("Directory leaf was fixed.\n"));
- }
- return 0;
-}
-
static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
{
struct block_count *bc = (struct block_count *) private;
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index e27a9f7..359bca0 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -13,6 +13,7 @@
#include "eattr.h"
#include "metawalk.h"
#include "link.h"
+#include "lost_n_found.h"
#include "inode_hash.h"
#define MAX_FILENAME 256
@@ -293,6 +294,11 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
return 0;
}
+static int hash_table_index(uint32_t hash, struct gfs2_inode *ip)
+{
+ return hash >> (32 - ip->i_di.di_depth);
+}
+
/* basic_dentry_checks - fundamental checks for directory entries
*
* @ip: pointer to the incode inode structure
@@ -713,6 +719,530 @@ nuke_dentry:
return 1;
}
+/* pad_with_leafblks - pad a hash table with pointers to new leaf blocks
+ *
+ * @ip: pointer to the dinode structure
+ * @tbl: pointer to the hash table in memory
+ * @lindex: index location within the hash table to pad
+ * @len: number of pointers to be padded
+ */
+static void pad_with_leafblks(struct gfs2_inode *ip, uint64_t *tbl,
+ int lindex, int len)
+{
+ int new_len, i;
+ uint32_t proper_start = lindex;
+ uint64_t new_leaf_blk;
+
+ log_err(_("Padding inode %llu (0x%llx) hash table at offset %d (0x%x) "
+ "for %d pointers.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr, lindex, lindex,
+ len);
+ while (len) {
+ new_len = 1;
+ /* Determine the next factor of 2 down from extras. We can't
+ just write out a leaf block on a power-of-two boundary.
+ We also need to make sure it has a length that will
+ ensure a "proper start" block as well. */
+ while ((new_len << 1) <= len) {
+ /* Translation: If doubling the size of the new leaf
+ will make its start boundary wrong, we have to
+ settle for a smaller length (and iterate more). */
+ proper_start = (lindex & ~((new_len << 1) - 1));
+ if (lindex != proper_start)
+ break;
+ new_len <<= 1;
+ }
+ write_new_leaf(ip, lindex, new_len, "after", &new_leaf_blk);
+ log_err(_("New leaf block was allocated at %llu (0x%llx) for "
+ "index %d (0x%x), length %d\n"),
+ (unsigned long long)new_leaf_blk,
+ (unsigned long long)new_leaf_blk,
+ lindex, lindex, new_len);
+ fsck_blockmap_set(ip, new_leaf_blk, _("pad leaf"),
+ gfs2_leaf_blk);
+ /* Fix the hash table in memory to have the new leaf */
+ for (i = 0; i < new_len; i++)
+ tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
+ len -= new_len;
+ lindex += new_len;
+ }
+}
+
+/* lost_leaf - repair a leaf block that's on the wrong directory inode
+ *
+ * If the correct index is less than the starting index, we have a problem.
+ * Since we process the index sequentially, the previous index has already
+ * been processed, fixed, and is now correct. But this leaf wants to overwrite
+ * a previously written good leaf. The only thing we can do is move all the
+ * directory entries to lost+found so we don't overwrite the good leaf. Then
+ * we need to pad the gap we leave.
+ */
+static int lost_leaf(struct gfs2_inode *ip, uint64_t *tbl, uint64_t leafno,
+ int ref_count, int lindex, struct gfs2_buffer_head *bh)
+{
+ char *filename;
+ char *bh_end = bh->b_data + ip->i_sbd->bsize;
+ struct gfs2_dirent de, *dent;
+ int error;
+
+ log_err(_("Leaf block %llu (0x%llx) seems to be out of place and its "
+ "contents need to be moved to lost+found.\n"),
+ (unsigned long long)leafno, (unsigned long long)leafno);
+ if (!query( _("Attempt to fix it? (y/n) "))) {
+ log_err( _("Directory leaf was not fixed.\n"));
+ return 0;
+ }
+ make_sure_lf_exists(ip);
+
+ dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
+ while (1) {
+ char tmp_name[PATH_MAX];
+
+ memset(&de, 0, sizeof(struct gfs2_dirent));
+ gfs2_dirent_in(&de, (char *)dent);
+ filename = (char *)dent + sizeof(struct gfs2_dirent);
+ memset(tmp_name, 0, sizeof(tmp_name));
+ if (de.de_name_len > sizeof(filename)) {
+ log_debug(_("Encountered bad filename length; "
+ "stopped processing.\n"));
+ break;
+ }
+ memcpy(tmp_name, filename, de.de_name_len);
+ if ((de.de_name_len == 1 && filename[0] == '.')) {
+ log_debug(_("Skipping entry '.'\n"));
+ } else if (de.de_name_len == 2 && filename[0] == '.' &&
+ filename[1] == '.') {
+ log_debug(_("Skipping entry '..'\n"));
+ } else if (!de.de_inum.no_formal_ino) { /* sentinel */
+ log_debug(_("Skipping sentinel '%s'\n"), tmp_name);
+ } else {
+ uint32_t count;
+ struct dir_status ds = {0};
+ uint8_t q = 0;
+
+ error = basic_dentry_checks(ip, dent, &de.de_inum,
+ tmp_name, &count, &de,
+ &ds, &q, bh);
+ if (error) {
+ log_err(_("Not relocating corrupt entry "
+ "\"%s\".\n"), tmp_name);
+ } else {
+ error = dir_add(lf_dip, filename,
+ de.de_name_len, &de.de_inum,
+ de.de_type);
+ if (error && error != -EEXIST) {
+ log_err(_("Error %d encountered while "
+ "trying to relocate \"%s\" "
+ "to lost+found.\n"), error,
+ tmp_name);
+ return error;
+ }
+ /* This inode is linked from lost+found */
+ incr_link_count(de.de_inum, lf_dip,
+ _("from lost+found"));
+ /* If it's a directory, lost+found is
+ back-linked to it via .. */
+ if (q == gfs2_inode_dir)
+ incr_link_count(lf_dip->i_di.di_num,
+ NULL,
+ _("to lost+found"));
+ log_err(_("Relocated \"%s\", block %llu "
+ "(0x%llx) to lost+found.\n"),
+ tmp_name,
+ (unsigned long long)de.de_inum.no_addr,
+ (unsigned long long)de.de_inum.no_addr);
+ }
+ }
+ if ((char *)dent + de.de_rec_len >= bh_end)
+ break;
+ dent = (struct gfs2_dirent *)((char *)dent + de.de_rec_len);
+ }
+ log_err(_("Directory entries from misplaced leaf block were relocated "
+ "to lost+found.\n"));
+ /* Free the lost leaf. */
+ fsck_blockmap_set(ip, leafno, _("lost leaf"), gfs2_block_free);
+ ip->i_di.di_blocks--;
+ bmodified(ip->i_bh);
+ /* Now we have to deal with the bad hash table entries pointing to the
+ misplaced leaf block. But we can't just fill the gap with a single
+ leaf. We have to write on nice power-of-two boundaries, and we have
+ to pad out any extra pointers. */
+ pad_with_leafblks(ip, tbl, lindex, ref_count);
+ return 1;
+}
+
+/* fix_hashtable - fix a corrupt hash table
+ *
+ * The main intent of this function is to sort out hash table problems.
+ * That is, it needs to determine if leaf blocks are in the wrong place,
+ * if the count of pointers is wrong, and if there are extra pointers.
+ * Everything should be placed on correct power-of-two boundaries appropriate
+ * to their leaf depth, and extra pointers should be correctly padded with new
+ * leaf blocks.
+ *
+ * @ip: the directory dinode structure pointer
+ * @tbl: hash table that's already read into memory
+ * @hsize: hash table size, as dictated by the dinode's di_depth
+ * @leafblk: the leaf block number that appears at this lindex in the tbl
+ * @lindex: leaf index that has a problem
+ * @proper_start: where this leaf's pointers should start, as far as the
+ * hash table is concerned (sight unseen; trusting the leaf
+ * really belongs here).
+ * @len: count of pointers in the hash table to this leafblk
+ * @proper_len: pointer to return the proper number of pointers, as the kernel
+ * calculates it, based on the leaf depth.
+ * @factor: the proper depth, given this number of pointers (rounded down).
+ *
+ * Returns: 0 - no changes made, or X if changes were made
+ */
+static int fix_hashtable(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize,
+ uint64_t leafblk, int lindex, uint32_t proper_start,
+ int len, int *proper_len, int factor)
+{
+ struct gfs2_buffer_head *lbh;
+ struct gfs2_leaf *leaf;
+ struct gfs2_dirent dentry, *de;
+ int changes = 0, error, i, extras, hash_index;
+ uint64_t new_leaf_blk;
+ uint32_t leaf_proper_start;
+
+ *proper_len = len;
+ log_err(_("Dinode %llu (0x%llx) has a hash table error at index "
+ "0x%x, length 0x%x: leaf block %llu (0x%llx)\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr, lindex, len,
+ (unsigned long long)leafblk, (unsigned long long)leafblk);
+ if (!query( _("Fix the hash table? (y/n) "))) {
+ log_err(_("Hash table not fixed.\n"));
+ return 0;
+ }
+
+ lbh = bread(ip->i_sbd, leafblk);
+ leaf = (struct gfs2_leaf *)lbh->b_data;
+ /* If the leaf's depth is out of range for this dinode, it's obviously
+ attached to the wrong dinode. Move the dirents to lost+found. */
+ if (be16_to_cpu(leaf->lf_depth) > ip->i_di.di_depth) {
+ log_err(_("This leaf block's depth (%d) is too big for this "
+ "dinode's depth (%d)\n"),
+ be16_to_cpu(leaf->lf_depth), ip->i_di.di_depth);
+ error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+ brelse(lbh);
+ return error;
+ }
+
+ memset(&dentry, 0, sizeof(struct gfs2_dirent));
+ de = (struct gfs2_dirent *)(lbh->b_data + sizeof(struct gfs2_leaf));
+ gfs2_dirent_in(&dentry, (char *)de);
+
+ /* If this is an empty leaf, we can just delete it and pad. */
+ if ((dentry.de_rec_len == cpu_to_be16(ip->i_sbd->bsize -
+ sizeof(struct gfs2_leaf))) &&
+ (dentry.de_inum.no_formal_ino == 0)) {
+ brelse(lbh);
+ gfs2_free_block(ip->i_sbd, leafblk);
+ log_err(_("Out of place leaf block %llu (0x%llx) had no "
+ "entries, so it was deleted.\n"),
+ (unsigned long long)leafblk,
+ (unsigned long long)leafblk);
+ pad_with_leafblks(ip, tbl, lindex, len);
+ log_err(_("Reprocessing index 0x%x (case 1).\n"), lindex);
+ return 1;
+ }
+
+ /* Calculate the proper number of pointers based on the leaf depth. */
+ *proper_len = 1 << (ip->i_di.di_depth - be16_to_cpu(leaf->lf_depth));
+
+ /* Look at the first dirent and check its hash value to see if it's
+ at the proper starting offset. */
+ hash_index = hash_table_index(dentry.de_hash, ip);
+ if (hash_index < lindex || hash_index > lindex + len) {
+ log_err(_("This leaf block has hash index %d, which is out of "
+ "bounds for where it appears in the hash table "
+ "(%d - %d)\n"),
+ hash_index, lindex, lindex + len);
+ error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+ brelse(lbh);
+ return error;
+ }
+
+ /* Now figure out where this leaf should start, and pad any pointers
+ up to that point with new leaf blocks. */
+ leaf_proper_start = (hash_index & ~(*proper_len - 1));
+ if (lindex < leaf_proper_start) {
+ log_err(_("Leaf pointers start at %d (0x%x), should be %d "
+ "(%x).\n"), lindex, lindex,
+ leaf_proper_start, leaf_proper_start);
+ pad_with_leafblks(ip, tbl, lindex, leaf_proper_start - lindex);
+ brelse(lbh);
+ return 1; /* reprocess the starting lindex */
+ }
+ /* If the proper start according to the leaf's hash index is later
+ than the proper start according to the hash table, it's once
+ again lost and we have to relocate it. The same applies if the
+ leaf's hash index is prior to the proper state, but the leaf is
+ already at its maximum depth. */
+ if ((leaf_proper_start < proper_start) ||
+ ((*proper_len > len || lindex > leaf_proper_start) &&
+ be16_to_cpu(leaf->lf_depth) == ip->i_di.di_depth)) {
+ log_err(_("Leaf block should start at 0x%x, but it appears at "
+ "0x%x in the hash table.\n"), leaf_proper_start,
+ proper_start);
+ error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+ brelse(lbh);
+ return error;
+ }
+
+ /* If we SHOULD have more pointers than we do, we can solve the
+ problem by splitting the block to a lower depth. Then we may have
+ the right number of pointers. If the leaf block pointers start
+ later than they should, we can split the leaf to give it a smaller
+ footprint in the hash table. */
+ if ((*proper_len > len || lindex > leaf_proper_start) &&
+ ip->i_di.di_depth > be16_to_cpu(leaf->lf_depth)) {
+ log_err(_("For depth %d, length %d, the proper start is: "
+ "0x%x.\n"), factor, len, proper_start);
+ changes++;
+ new_leaf_blk = find_free_blk(ip->i_sbd);
+ dir_split_leaf(ip, lindex, leafblk, lbh);
+ /* re-read the leaf to pick up dir_split_leaf's changes */
+ gfs2_leaf_in(leaf, lbh);
+ *proper_len = 1 << (ip->i_di.di_depth -
+ be16_to_cpu(leaf->lf_depth));
+ log_err(_("Leaf block %llu (0x%llx) was split from length "
+ "%d to %d\n"), (unsigned long long)leafblk,
+ (unsigned long long)leafblk, len, *proper_len);
+ if (*proper_len < 0) {
+ log_err(_("Programming error: proper_len=%d, "
+ "di_depth = %d, lf_depth = %d.\n"),
+ *proper_len, ip->i_di.di_depth,
+ be16_to_cpu(leaf->lf_depth));
+ exit(FSCK_ERROR);
+ }
+ log_err(_("New split-off leaf block was allocated at %lld "
+ "(0x%llx) for index %d (0x%x)\n"),
+ (unsigned long long)new_leaf_blk,
+ (unsigned long long)new_leaf_blk, lindex, lindex);
+ fsck_blockmap_set(ip, new_leaf_blk, _("split leaf"),
+ gfs2_leaf_blk);
+ log_err(_("Hash table repaired.\n"));
+ /* Fix up the hash table in memory to include the new leaf */
+ for (i = 0; i < *proper_len; i++)
+ tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
+ if (*proper_len < (len >> 1)) {
+ log_err(_("One leaf split is not enough. The hash "
+ "table will need to be reprocessed.\n"));
+ brelse(lbh);
+ return changes;
+ }
+ lindex += (*proper_len); /* skip the new leaf from the split */
+ len -= (*proper_len);
+ }
+ if (*proper_len < len) {
+ log_err(_("There are %d pointers, but leaf 0x%llx's "
+ "depth, %d, only allows %d\n"),
+ len, (unsigned long long)leafblk,
+ be16_to_cpu(leaf->lf_depth), *proper_len);
+ }
+ brelse(lbh);
+ /* At this point, lindex should be at the proper end of the pointers.
+ Now we need to replace any extra duplicate pointers to the old
+ (original) leafblk (that ran off the end) with new leaf blocks. */
+ lindex += (*proper_len); /* Skip past the normal good pointers */
+ len -= (*proper_len);
+ extras = 0;
+ for (i = 0; i < len; i++) {
+ if (be64_to_cpu(tbl[lindex + i]) == leafblk)
+ extras++;
+ else
+ break;
+ }
+ if (extras) {
+ log_err(_("Found %d extra pointers to leaf %llu (0x%llx)\n"),
+ extras, (unsigned long long)leafblk,
+ (unsigned long long)leafblk);
+ pad_with_leafblks(ip, tbl, lindex, extras);
+ log_err(_("Reprocessing index 0x%x (case 2).\n"), lindex);
+ return 1;
+ }
+ return changes;
+}
+
+/* check_hash_tbl - check that the hash table is sane
+ *
+ * We've got to make sure the hash table is sane. Each leaf needs to
+ * be counted a proper power of 2. We can't just have 3 pointers to a leaf.
+ * The number of pointers must correspond to the proper leaf depth, and they
+ * must all fall on power-of-two boundaries. The leaf block pointers all need
+ * to fall properly on these boundaries, otherwise the kernel code's
+ * calculations will land it on the wrong leaf block while it's searching,
+ * and the result will be files you can see with ls, but can't open, delete
+ * or use them.
+ *
+ * The goal of this function is to check the hash table to make sure the
+ * boundaries and lengths all line up properly, and if not, to fix it.
+ *
+ * Note: There's a delicate balance here, because this function gets called
+ * BEFORE leaf blocks are checked by function check_leaf from function
+ * check_leaf_blks: the hash table has to be sane before we can start
+ * checking all the leaf blocks. And yet if there's hash table corruption
+ * we may need to reference leaf blocks to fix it, which means we need
+ * to check and/or fix a leaf block along the way.
+ */
+static int check_hash_tbl(struct gfs2_inode *ip, uint64_t *tbl,
+ unsigned hsize, void *private)
+{
+ int error = 0;
+ int lindex, len, proper_len, i, changes = 0;
+ uint64_t leafblk;
+ struct gfs2_leaf leaf;
+ struct gfs2_buffer_head *lbh;
+ int factor;
+ uint32_t proper_start;
+
+ lindex = 0;
+ while (lindex < hsize) {
+ if (fsck_abort)
+ return changes;
+ len = 1;
+ factor = 0;
+ leafblk = be64_to_cpu(tbl[lindex]);
+ while (lindex + (len << 1) - 1 < hsize) {
+ if (be64_to_cpu(tbl[lindex + (len << 1) - 1]) !=
+ leafblk)
+ break;
+ len <<= 1;
+ factor++;
+ }
+
+ /* Check for leftover pointers after the factor of two: */
+ proper_len = len; /* A factor of 2 that fits nicely */
+ while (lindex + len < hsize &&
+ be64_to_cpu(tbl[lindex + len]) == leafblk)
+ len++;
+
+ /* See if that leaf block is valid. If not, write a new one
+ that falls on a proper boundary. If it doesn't naturally,
+ we may need more. */
+ if (!valid_block(ip->i_sbd, leafblk)) {
+ uint64_t new_leafblk;
+
+ log_err(_("Dinode %llu (0x%llx) has bad leaf pointers "
+ "at offset %d for %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ lindex, len);
+ if (!query( _("Fix the hash table? (y/n) "))) {
+ log_err(_("Hash table not fixed.\n"));
+ lindex += len;
+ continue;
+ }
+ error = write_new_leaf(ip, lindex, proper_len,
+ _("replacing"), &new_leafblk);
+ if (error)
+ return error;
+
+ for (i = lindex; i < lindex + proper_len; i++)
+ tbl[i] = cpu_to_be64(new_leafblk);
+ lindex += proper_len;
+ continue;
+ }
+ /* Make sure they call on proper leaf-split boundaries. This
+ is the calculation used by the kernel, and dir_split_leaf */
+ proper_start = (lindex & ~(proper_len - 1));
+ if (lindex != proper_start) {
+ log_debug(_("lindex 0x%llx is not a proper starting "
+ "point for this leaf: 0x%llx\n"),
+ (unsigned long long)lindex,
+ (unsigned long long)proper_start);
+ changes = fix_hashtable(ip, tbl, hsize, leafblk,
+ lindex, proper_start, len,
+ &proper_len, factor);
+ /* Check if we need to split more leaf blocks */
+ if (changes) {
+ if (proper_len < (len >> 1))
+ log_err(_("More leaf splits are "
+ "needed; "));
+ log_err(_("Reprocessing index 0x%x (case 3).\n"),
+ lindex);
+ continue; /* Make it reprocess the lindex */
+ }
+ }
+ /* Check for extra pointers to this leaf. At this point, len
+ is the number of pointers we have. proper_len is the proper
+ number of pointers if the hash table is assumed correct.
+ Function fix_hashtable will read in the leaf block and
+ determine the "actual" proper length based on the leaf
+ depth, and adjust the hash table accordingly. */
+ if (len != proper_len) {
+ log_err(_("Length %d (0x%x) is not a proper length "
+ "for this leaf. Valid boundary assumed to "
+ "be %d (0x%x).\n"),
+ len, len, proper_len, proper_len);
+ lbh = bread(ip->i_sbd, leafblk);
+ gfs2_leaf_in(&leaf, lbh);
+ if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) ||
+ leaf.lf_depth > ip->i_di.di_depth)
+ leaf.lf_depth = factor;
+ brelse(lbh);
+ changes = fix_hashtable(ip, tbl, hsize, leafblk,
+ lindex, lindex, len,
+ &proper_len, leaf.lf_depth);
+ /* If fixing the hash table made changes, we can no
+ longer count on the leaf block pointers all pointing
+ to the same leaf (which is checked below). To avoid
+ flagging another error, reprocess the offset. */
+ if (changes) {
+ log_err(_("Reprocessing index 0x%x (case 4).\n"),
+ lindex);
+ continue; /* Make it reprocess the lindex */
+ }
+ }
+
+ /* Now make sure they're all the same pointer */
+ for (i = lindex; i < lindex + proper_len; i++) {
+ if (fsck_abort)
+ return changes;
+
+ if (be64_to_cpu(tbl[i]) == leafblk) /* No problem */
+ continue;
+
+ log_err(_("Dinode %llu (0x%llx) has a hash table "
+ "inconsistency at index %d (0x%d) for %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ i, i, len);
+ if (!query( _("Fix the hash table? (y/n) "))) {
+ log_err(_("Hash table not fixed.\n"));
+ continue;
+ }
+ changes++;
+ /* Now we have to determine if the hash table is
+ corrupt, or if the leaf has the wrong depth. */
+ lbh = bread(ip->i_sbd, leafblk);
+ gfs2_leaf_in(&leaf, lbh);
+ brelse(lbh);
+ /* Calculate the expected pointer count based on the
+ leaf depth. */
+ proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
+ if (proper_len != len) {
+ log_debug(_("Length 0x%x is not proper for "
+ "this leaf: 0x%x"),
+ len, proper_len);
+ changes = fix_hashtable(ip, tbl, hsize,
+ leafblk, lindex,
+ lindex, len,
+ &proper_len,
+ leaf.lf_depth);
+ break;
+ }
+ }
+ lindex += proper_len;
+ }
+ if (!error && changes)
+ error = 1;
+ return error;
+}
struct metawalk_fxns pass2_fxns = {
.private = NULL,
@@ -723,6 +1253,8 @@ struct metawalk_fxns pass2_fxns = {
.check_eattr_leaf = check_eattr_leaf,
.check_dentry = check_dentry,
.check_eattr_entry = NULL,
+ .check_hash_tbl = check_hash_tbl,
+ .repair_leaf = repair_leaf,
};
/* Check system directory inode */
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=d94ef845ac19b…
Commit: d94ef845ac19bed44faaee79d629c9f4b9862a28
Parent: 6c3ff422c0062fa691e7b27fc95681694152d051
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 12:15:33 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 14:57:04 2013 -0500
fsck.gfs2: Add formal inode check to basic dirent checks
This patch adds a check to the basic directory entry checks which
compares the formal inode number of the directory entry to the
formal inode number in the inode tree that was set up by pass1.
If the numbers don't match, this directory entry is corrupt.
rhbz#902920
---
gfs2/fsck/pass2.c | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 4460508..e27a9f7 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -313,6 +313,7 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
uint32_t calculated_hash;
struct gfs2_inode *entry_ip = NULL;
int error;
+ struct inode_info *ii;
if (!valid_block(ip->i_sbd, entry->no_addr)) {
log_err( _("Block # referenced by directory entry %s in inode "
@@ -488,6 +489,25 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
fsck_inode_put(&entry_ip);
return 1;
}
+ /* We need to verify the formal inode number matches. If it doesn't,
+ it needs to be deleted. */
+ ii = inodetree_find(entry->no_addr);
+ if (ii && ii->di_num.no_formal_ino != entry->no_formal_ino) {
+ log_err( _("Directory entry '%s' pointing to block %llu "
+ "(0x%llx) in directory %llu (0x%llx) has the "
+ "wrong 'formal' inode number.\n"), tmp_name,
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ log_err( _("The directory entry has %llu (0x%llx) but the "
+ "inode has %llu (0x%llx)\n"),
+ (unsigned long long)entry->no_formal_ino,
+ (unsigned long long)entry->no_formal_ino,
+ (unsigned long long)ii->di_num.no_formal_ino,
+ (unsigned long long)ii->di_num.no_formal_ino);
+ return 1;
+ }
return 0;
}