Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=de…
Commit: de4850b3764331085a4657c57fd518b3586d5b6c
Parent: fd0a7e2b1ba68ef3e96fa10b54b1049cf963202e
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Mon Jan 25 09:03:16 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:31 2010 -0600
fsck.gfs2: Check for massive amounts of pointer corruption
Sometimes, due to faulty hardware or whatever, a whole bunch of
random nonsense is written into a block. If that block happens
to be a indirect list of pointers, pass1 may not find the
corruption for a long time. This happens when the corruption
starts, for example, at offset 0x200, or if the corruption just
happens to look like valid pointers for a while, like low
blocks that correspond to system inodes, rgrps, or journals.
If pass1 marks a whole bunch of pointers as valid, then later
decides the whole inode is corrupt, it becomes a major pain to
undo what it has done. For example, if it had found one of the
"bad" pointers to be the statfs file's dinode and marked that as
a duplicate reference, it's a pain to undo that once it becomes
apparent that there's too much damage to recover.
This patch introduces a block range check function that pass1
can use to traverse the metadata tree initially, just checking
for lots of damage to pointers. If there are a lot of damaged
metadata pointers it's better to just mark the dinode as free
space and let pass5 clean up any blocks that it referenced.
If a bridge has too many damaged rungs to cross, it's better to
find that out first rather than to cross half-way and have to
tip-toe back to the start.
rhbz#455300
---
gfs2/fsck/fsck.h | 2 +
gfs2/fsck/pass1.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 122 insertions(+), 8 deletions(-)
diff --git a/gfs2/fsck/fsck.h b/gfs2/fsck/fsck.h
index 5948210..30eb223 100644
--- a/gfs2/fsck/fsck.h
+++ b/gfs2/fsck/fsck.h
@@ -23,6 +23,8 @@
#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
#define FSCK_LIBRARY 128 /* Shared library error */
+#define BAD_POINTER_TOLERANCE 10 /* How many bad pointers is too many? */
+
struct inode_info
{
struct osi_node node;
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 04938db..4382ad9 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -554,6 +554,97 @@ static int clear_leaf(struct gfs2_inode *ip, uint64_t block,
return 0;
}
+/**
+ * Check for massive amounts of pointer corruption. If the block has
+ * lots of out-of-range pointers, we can't trust any of the pointers.
+ * For example, a stray pointer with a value of 0x1d might be
+ * corruption/nonsense, and if so, we don't want to delete an
+ * important file (like master or the root directory) because of it.
+ * We need to check for a large number of bad pointers BEFORE we start
+ * messing with them because we don't want to mark a block as a
+ * duplicate (for example) until we know if the pointers in general can
+ * be trusted. Thus it needs to be in a separate loop.
+ */
+static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh,
+ const char *btype, void *private)
+{
+ long *bad_pointers = (long *)private;
+ uint8_t q;
+
+ if (gfs2_check_range(ip->i_sbd, block) != 0) {
+ (*bad_pointers)++;
+ log_debug( _("Bad %s block pointer (out of range #%ld) "
+ "found in inode %lld (0x%llx).\n"), btype,
+ *bad_pointers,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
+ return ENOENT;
+ else
+ return -ENOENT; /* Exits check_metatree quicker */
+ }
+ /* See how many duplicate blocks it has */
+ q = block_type(block);
+ if (q != gfs2_block_free) {
+ (*bad_pointers)++;
+ log_debug( _("Duplicated %s block pointer (violation #%ld) "
+ "found in inode %lld (0x%llx).\n"), btype,
+ *bad_pointers,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
+ return ENOENT;
+ else
+ return -ENOENT; /* Exits check_metatree quicker */
+ }
+ return 0;
+}
+
+static int rangecheck_metadata(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh, void *private)
+{
+ return rangecheck_block(ip, block, bh, _("metadata"), private);
+}
+
+static int rangecheck_leaf(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head *bh, void *private)
+{
+ return rangecheck_block(ip, block, &bh, _("leaf"), private);
+}
+
+static int rangecheck_data(struct gfs2_inode *ip, uint64_t block,
+ void *private)
+{
+ return rangecheck_block(ip, block, NULL, _("data"), private);
+}
+
+static int rangecheck_eattr_indir(struct gfs2_inode *ip, uint64_t block,
+ uint64_t parent,
+ struct gfs2_buffer_head **bh, void *private)
+{
+ return rangecheck_block(ip, block, NULL,
+ _("indirect extended attribute"),
+ private);
+}
+
+static int rangecheck_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
+ uint64_t parent, struct gfs2_buffer_head **bh,
+ void *private)
+{
+ return rangecheck_block(ip, block, NULL, _("extended attribute"),
+ private);
+}
+
+struct metawalk_fxns rangecheck_fxns = {
+ .private = NULL,
+ .check_metalist = rangecheck_metadata,
+ .check_data = rangecheck_data,
+ .check_leaf = rangecheck_leaf,
+ .check_eattr_indir = rangecheck_eattr_indir,
+ .check_eattr_leaf = rangecheck_eattr_leaf,
+};
+
static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
uint64_t block)
{
@@ -562,10 +653,16 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
int error;
struct block_count bc = {0};
struct metawalk_fxns invalidate_metatree = {0};
+ long bad_pointers;
- invalidate_metatree.check_metalist = clear_metalist;
- invalidate_metatree.check_data = clear_data;
- invalidate_metatree.check_leaf = clear_leaf;
+ q = block_type(block);
+ if(q != gfs2_block_free) {
+ log_err( _("Found duplicate block referenced as an inode at "
+ "#%" PRIu64 " (0x%" PRIx64 ")\n"), block, block);
+ gfs2_dup_set(block);
+ fsck_inode_put(&ip);
+ return 0;
+ }
ip = fsck_inode_get(sdp, bh);
if (ip->i_di.di_num.no_addr != block) {
@@ -584,11 +681,22 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
" (0x%" PRIx64 ") not fixed\n"), block, block);
}
- q = block_type(block);
- if(q != gfs2_block_free) {
- log_err( _("Found duplicate block referenced as an inode at "
- "#%" PRIu64 " (0x%" PRIx64 ")\n"), block, block);
- gfs2_dup_set(block);
+ bad_pointers = 0L;
+
+ /* First, check the metadata for massive amounts of pointer corruption.
+ Such corruption can only lead us to ruin trying to clean it up,
+ so it's better to check it up front and delete the inode if
+ there is corruption. */
+ rangecheck_fxns.private = &bad_pointers;
+ error = check_metatree(ip, &rangecheck_fxns);
+ if (bad_pointers > BAD_POINTER_TOLERANCE) {
+ log_err( _("Error: inode %llu (0x%llx) has more than "
+ "%d bad pointers.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ BAD_POINTER_TOLERANCE);
+ fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+ _("badly corrupt"), gfs2_block_free);
fsck_inode_put(&ip);
return 0;
}
@@ -703,6 +811,10 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
"errors; invalidating.\n"),
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
+ invalidate_metatree.check_metalist = clear_metalist;
+ invalidate_metatree.check_data = clear_data;
+ invalidate_metatree.check_leaf = clear_leaf;
+
/* FIXME: Must set all leaves invalid as well */
check_metatree(ip, &invalidate_metatree);
fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=9e…
Commit: 9e52bf1d064e6e0fee60927b8cd5496e19917196
Parent: b46c19c385f182f950c6df56bafd3db15459c48e
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Fri Jan 22 15:29:19 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:31 2010 -0600
fsck.gfs2: reprocess lost+found and other inode metadata when blocks are added
On rare occasions, fsck.gfs2 needs to allocate blocks in the file system.
For example, this can happen when new directory leafs are added to
lost+found and when lost+found's hash table needs to be doubled.
While libgfs2 did a stellar job of updating the rgrp bitmap information,
those newly allocated blocks were unfortunately not being properly
accounted for in the fsck.gfs2 blockmap. Therefore, pass5 would
mistakenly mark them as free blocks. Further use of the file
system would often allocate those blocks a second time, creating
a duplicate block reference. That means very bad file system
corruption. This patch fixes the problem by checking for block
count changes in inodes at critical points, and subsequently fixing
up the blockmap using new "alloc_*" helper functions.
rhbz#455300
---
gfs2/fsck/lost_n_found.c | 8 ++++
gfs2/fsck/metawalk.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++
gfs2/fsck/metawalk.h | 1 +
gfs2/fsck/pass2.c | 16 +++++++
gfs2/fsck/pass3.c | 10 +++++
5 files changed, 133 insertions(+), 0 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 6d26096..5b9be67 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -27,6 +27,7 @@
int add_inode_to_lf(struct gfs2_inode *ip){
char tmp_name[256];
__be32 inode_type;
+ uint64_t lf_blocks;
struct dir_info *di;
if(!lf_dip) {
@@ -81,6 +82,8 @@ 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;
+
switch(ip->i_di.di_mode & S_IFMT){
case S_IFDIR:
log_info( _("Adding .. entry pointing to lost+found for "
@@ -163,6 +166,11 @@ int add_inode_to_lf(struct gfs2_inode *ip){
dir_add(lf_dip, tmp_name, strlen(tmp_name), &(ip->i_di.di_num),
inode_type);
+ /* 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 */
increment_link(ip->i_di.di_num.no_addr, lf_dip->i_di.di_num.no_addr,
_("from lost+found"));
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 2ad47fe..3ccb0bc 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1430,3 +1430,101 @@ int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
return delete_blocks(ip, block, NULL, _("extended attribute"),
private);
}
+
+static int alloc_metalist(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh, void *private)
+{
+ uint8_t q;
+ const char *desc = (const char *)private;
+
+ /* No need to range_check here--if it was added, it's in range. */
+ /* We can't check the bitmap here because this function is called
+ after the bitmap has been set but before the blockmap has. */
+ *bh = bread(ip->i_sbd, block);
+ q = block_type(block);
+ if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) { /* If not marked yet */
+ log_debug(_("%s reference to new metadata block "
+ "%lld (0x%llx) is now marked as indirect.\n"),
+ desc, (unsigned long long)block,
+ (unsigned long long)block);
+ gfs2_blockmap_set(bl, block, gfs2_indir_blk);
+ }
+ return 0;
+}
+
+static int alloc_data(struct gfs2_inode *ip, uint64_t block, void *private)
+{
+ uint8_t q;
+ const char *desc = (const char *)private;
+
+ /* No need to range_check here--if it was added, it's in range. */
+ /* We can't check the bitmap here because this function is called
+ after the bitmap has been set but before the blockmap has. */
+ q = block_type(block);
+ if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) { /* If not marked yet */
+ log_debug(_("%s reference to new data block "
+ "%lld (0x%llx) is now marked as data.\n"),
+ desc, (unsigned long long)block,
+ (unsigned long long)block);
+ gfs2_blockmap_set(bl, block, gfs2_block_used);
+ }
+ return 0;
+}
+
+static int alloc_leaf(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head *bh, void *private)
+{
+ uint8_t q;
+
+ /* No need to range_check here--if it was added, it's in range. */
+ /* We can't check the bitmap here because this function is called
+ after the bitmap has been set but before the blockmap has. */
+ q = block_type(block);
+ if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) /* If not marked yet */
+ fsck_blockmap_set(ip, block, _("newly allocated leaf"),
+ gfs2_leaf_blk);
+ return 0;
+}
+
+struct metawalk_fxns alloc_fxns = {
+ .private = NULL,
+ .check_leaf = alloc_leaf,
+ .check_metalist = alloc_metalist,
+ .check_data = alloc_data,
+ .check_eattr_indir = NULL,
+ .check_eattr_leaf = NULL,
+ .check_dentry = NULL,
+ .check_eattr_entry = NULL,
+ .check_eattr_extentry = NULL,
+ .finish_eattr_indir = NULL,
+};
+
+/*
+ * reprocess_inode - fixes the blockmap to match the bitmap due to an
+ * unexpected block allocation via libgfs2.
+ *
+ * The problem we're trying to overcome here is when a new block must be
+ * added to a dinode because of a write. This will happen when lost+found
+ * needs a new indirect block for its hash table. In that case, the write
+ * causes a new block to be assigned in the bitmap but that block is not yet
+ * accurately reflected in the fsck blockmap. We need to compensate here.
+ *
+ * We can't really use fsck_blockmap_set here because the new block
+ * was already allocated by libgfs2 and therefore it took care of
+ * the rgrp free space variable. fsck_blockmap_set adjusts the free space
+ * in the rgrp according to the change, which has already been done.
+ * So it's only our blockmap that now disagrees with the rgrp bitmap, so we
+ * need to fix only that.
+ */
+void reprocess_inode(struct gfs2_inode *ip, const char *desc)
+{
+ int error;
+
+ alloc_fxns.private = (void *)desc;
+ log_info( _("%s had blocks added; reprocessing its metadata tree "
+ "at height=%d.\n"), desc, ip->i_di.di_height);
+ error = check_metatree(ip, &alloc_fxns);
+ if (error)
+ log_err( _("Error %d reprocessing the %s metadata tree.\n"),
+ error, desc);
+}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 72c282a..653b512 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -32,6 +32,7 @@ extern int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
const char *caller, int line);
extern int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
enum gfs2_mark_block new_blockmap_state);
+extern void reprocess_inode(struct gfs2_inode *ip, const char *desc);
extern struct duptree *dupfind(uint64_t block);
#define is_duplicate(dblock) ((dupfind(dblock)) ? 1 : 0)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 0650966..583bc09 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -549,6 +549,8 @@ 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) "))) {
+ uint64_t cur_blks = sysinode->i_di.di_blocks;
+
sprintf(tmp_name, ".");
filename_len = strlen(tmp_name); /* no trailing NULL */
if(!(filename = malloc(sizeof(char) * filename_len))) {
@@ -566,6 +568,8 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
log_warn( _("Adding '.' entry\n"));
dir_add(sysinode, filename, filename_len,
&(sysinode->i_di.di_num), DT_DIR);
+ if (cur_blks != sysinode->i_di.di_blocks)
+ reprocess_inode(sysinode, dirname);
/* This system inode is linked to itself via '.' */
increment_link(sysinode->i_di.di_num.no_addr,
sysinode->i_di.di_num.no_addr,
@@ -731,6 +735,8 @@ int pass2(struct gfs2_sbd *sbp)
dirblk, dirblk);
if (query( _("Is it okay to add '.' entry? (y/n) "))) {
+ uint64_t cur_blks;
+
sprintf(tmp_name, ".");
filename_len = strlen(tmp_name); /* no trailing
NULL */
@@ -748,8 +754,18 @@ int pass2(struct gfs2_sbd *sbp)
}
memcpy(filename, tmp_name, filename_len);
+ cur_blks = ip->i_di.di_blocks;
dir_add(ip, filename, filename_len,
&(ip->i_di.di_num), DT_DIR);
+ if (cur_blks != ip->i_di.di_blocks) {
+ char dirname[80];
+
+ sprintf(dirname, _("Directory at %lld "
+ "(0x%llx)"),
+ (unsigned long long)dirblk,
+ (unsigned long long)dirblk);
+ reprocess_inode(ip, dirname);
+ }
/* directory links to itself via '.' */
increment_link(ip->i_di.di_num.no_addr,
ip->i_di.di_num.no_addr,
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index 2ff4fbf..260f4fe 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -20,6 +20,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sbp, uint64_t newdotdot,
char *filename;
int filename_len;
struct gfs2_inode *ip, *pip;
+ uint64_t cur_blks;
ip = fsck_load_inode(sbp, block);
pip = fsck_load_inode(sbp, newdotdot);
@@ -50,7 +51,16 @@ static int attach_dotdot_to(struct gfs2_sbd *sbp, uint64_t newdotdot,
log_warn( _("Unable to remove \"..\" directory entry.\n"));
else
decrement_link(olddotdot, block, _("old \"..\""));
+ cur_blks = ip->i_di.di_blocks;
dir_add(ip, filename, filename_len, &pip->i_di.di_num, DT_DIR);
+ if (cur_blks != ip->i_di.di_blocks) {
+ char dirname[80];
+
+ sprintf(dirname, _("Directory at %lld (0x%llx)"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ reprocess_inode(ip, dirname);
+ }
increment_link(newdotdot, block, _("new \"..\""));
bmodified(ip->i_bh);
fsck_inode_put(&ip);
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=b4…
Commit: b46c19c385f182f950c6df56bafd3db15459c48e
Parent: 5df69b1d9f020df9e97629240196596c330567fa
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Fri Jan 22 15:07:19 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:31 2010 -0600
lost+found link count and connections were not properly managed
When the lost+found directory was created, function createi in
libgfs2 incremented the di_nlink link count for the root directory,
but fsck.gfs2 did not increment the nlink value in the hash
table. As a result, pass4 doesn't find and fix the discrepancy.
Subsequent runs of fsck.gfs2 detect the problem, though.
This patch adjusts for the new link and keeps everything sane.
In a similar fashion, if the dinode being moved to lost+found
happens to be a directory, the links were not properly adjusted
for that directory's ".." dentry. This patch also takes care of
that so the links don't get off.
rhbz#455300
---
gfs2/fsck/lost_n_found.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 44 insertions(+), 0 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index ea7b370..6d26096 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -27,6 +27,7 @@
int add_inode_to_lf(struct gfs2_inode *ip){
char tmp_name[256];
__be32 inode_type;
+ struct dir_info *di;
if(!lf_dip) {
uint8_t q;
@@ -35,6 +36,13 @@ int add_inode_to_lf(struct gfs2_inode *ip){
lf_dip = createi(ip->i_sbd->md.rooti, "lost+found",
S_IFDIR | 0700, 0);
+ /* createi will have incremented the di_nlink link count for
+ the root directory. We must increment the nlink value
+ in the hash table to keep them in sync so that pass4 can
+ detect and fix any discrepancies. */
+ set_link_count(ip->i_sbd->sd_sb.sb_root_dir.no_addr,
+ ip->i_sbd->md.rooti->i_di.di_nlink);
+
q = block_type(lf_dip->i_di.di_num.no_addr);
if(q != gfs2_inode_dir) {
/* This is a new lost+found directory, so set its
@@ -59,6 +67,15 @@ int add_inode_to_lf(struct gfs2_inode *ip){
ip->i_sbd->md.rooti->i_di.di_num.no_addr,
"\"..\"");
}
+ log_info( _("lost+found directory is dinode %lld (0x%llx)\n"),
+ (unsigned long long)lf_dip->i_di.di_num.no_addr,
+ (unsigned long long)lf_dip->i_di.di_num.no_addr);
+ di = dirtree_find(lf_dip->i_di.di_num.no_addr);
+ if (di) {
+ log_info( _("Marking lost+found inode connected\n"));
+ di->checked = 1;
+ di = NULL;
+ }
}
if(ip->i_di.di_num.no_addr == lf_dip->i_di.di_num.no_addr) {
log_err( _("Trying to add lost+found to itself...skipping"));
@@ -71,6 +88,33 @@ int add_inode_to_lf(struct gfs2_inode *ip){
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
+ /* If there's a pre-existing .. directory entry, we have to
+ back out the links. */
+ di = dirtree_find(ip->i_di.di_num.no_addr);
+ if (di && gfs2_check_range(ip->i_sbd, di->dotdot_parent) == 0){
+ struct gfs2_inode *dip;
+
+ log_debug(_("Directory %lld (0x%llx) already had a "
+ "\"..\" link to %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)di->dotdot_parent,
+ (unsigned long long)di->dotdot_parent);
+ decrement_link(di->dotdot_parent,
+ ip->i_di.di_num.no_addr,
+ _(".. unlinked, moving to lost+found"));
+ dip = fsck_load_inode(ip->i_sbd, di->dotdot_parent);
+ dip->i_di.di_nlink--;
+ log_debug(_("Decrementing its links to %d\n"),
+ dip->i_di.di_nlink);
+ bmodified(dip->i_bh);
+ fsck_inode_put(&dip);
+ di = NULL;
+ } else
+ log_debug(_("Couldn't find a valid \"..\" entry "
+ "for orphan directory %lld (0x%llx)\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
if(gfs2_dirent_del(ip, "..", 2))
log_warn( _("add_inode_to_lf: Unable to remove "
"\"..\" directory entry.\n"));
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=5d…
Commit: 5df69b1d9f020df9e97629240196596c330567fa
Parent: 611c213c35e1af8dc36f3b15fccc5ee5c352eaa3
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Fri Jan 22 14:38:05 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:31 2010 -0600
fsck.gfs2: separate check_data function in check_metatree
Function check_metatree had a loop for checking data blocks. I broke
this loop into its own function. That serves two purposes: First, it
makes check_metatree smaller and easier to read (it's already big an
unruly). Second, it avoids looping unnecessarily when the caller
has no check_data() function of its own. Net result is faster code.
rhbz#455300
---
gfs2/fsck/metawalk.c | 72 ++++++++++++++++++++++++++++++++++++++------------
1 files changed, 55 insertions(+), 17 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 13290e1..2ad47fe 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1157,6 +1157,49 @@ fail:
}
/**
+ * check_data - check all data pointers for a given buffer
+ * This does not include "data" blocks that are really
+ * hash table blocks for directories.
+ *
+ * @ip:
+ *
+ * returns: +ENOENT if there are too many bad pointers
+ * -1 if a more serious error occurred.
+ * 0 if no errors occurred
+ * 1 if errors were found and corrected
+ * 2 (ENOENT) is there were too many bad pointers
+ */
+static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
+ uint64_t *ptr_start, char *ptr_end,
+ uint64_t *blks_checked)
+{
+ int error = 0, 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 error;
+ block = be64_to_cpu(*ptr);
+ /* It's important that we don't call gfs2_check_range and
+ bypass calling check_data on invalid blocks because that
+ would defeat the rangecheck_block related functions in
+ pass1. Therefore the individual check_data functions
+ should do a range check. */
+ rc = pass->check_data(ip, block, pass->private);
+ if (rc < 0)
+ return rc;
+ if (!error && rc)
+ error = rc;
+ (*blks_checked)++;
+ }
+ return error;
+}
+
+/**
* check_metatree
* @ip:
* @rgd:
@@ -1167,11 +1210,10 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
osi_list_t metalist[GFS2_MAX_META_HEIGHT];
osi_list_t *list;
struct gfs2_buffer_head *bh;
- uint64_t block, *ptr;
uint32_t height = ip->i_di.di_height;
int i, head_size;
uint64_t blks_checked = 0;
- int error;
+ int error, rc;
if (!height && !S_ISDIR(ip->i_di.di_mode))
return 0;
@@ -1227,23 +1269,19 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
}
head_size = sizeof(struct gfs2_dinode);
}
- ptr = (uint64_t *)(bh->b_data + head_size);
- for ( ; (char *)ptr < (bh->b_data + ip->i_sbd->bsize); ptr++) {
- if (!*ptr)
- continue;
-
- block = be64_to_cpu(*ptr);
+ 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;
- if(pass->check_data &&
- (pass->check_data(ip, block, pass->private) < 0)) {
- stack;
- return -1;
- }
- blks_checked++;
- if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
- big_file_comfort(ip, blks_checked);
- }
+ if (rc && (!error || rc < 0))
+ error = rc;
+ if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
+ big_file_comfort(ip, blks_checked);
if (bh == ip->i_bh)
osi_list_del(&bh->b_altlist);
else
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=61…
Commit: 611c213c35e1af8dc36f3b15fccc5ee5c352eaa3
Parent: 013d20413ffb8d960089836be487b9c16a9f12d6
Author: Bob Peterson <bob(a)ganesha.peterson>
AuthorDate: Fri Jan 22 14:24:31 2010 -0600
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Tue Jan 26 14:39:31 2010 -0600
fsck.gfs2: metawalk was not checking many directories
The fsck.gfs2 program was not checking the directory hash tables
of many subdirectories. It was checking the leaf blocks, but
it wasn't checking the hash table block pointers if the height
value was equal to 0, which means every directory with a small
number of leaf blocks. That means before, you could have all
kinds of directory corruption and never know it.
rhbz#455300
---
gfs2/fsck/metawalk.c | 70 +++++++++++++++++++++++++++----------------------
1 files changed, 39 insertions(+), 31 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index a0ca65b..13290e1 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1070,12 +1070,24 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
uint32_t height = ip->i_di.di_height;
struct gfs2_buffer_head *bh, *nbh, *metabh = ip->i_bh;
osi_list_t *prev_list, *cur_list, *tmp;
- int i, head_size;
+ int i, head_size, iblk_type;
uint64_t *ptr, block;
- int err;
+ int error = 0, err;
osi_list_add(&metabh->b_altlist, &mlp[0]);
+ /* Directories are special. Their 'data' is the hash table, which is
+ basically an indirect block list. Their height is not important
+ because it checks everything through the hash table using
+ "depth" field calculations. However, we still have to check the
+ indirect blocks, even if the height == 1. */
+ if (S_ISDIR(ip->i_di.di_mode)) {
+ height++;
+ iblk_type = GFS2_METATYPE_JD;
+ } else {
+ iblk_type = GFS2_METATYPE_IN;
+ }
+
/* if(<there are no indirect blocks to check>) */
if (height < 2)
return 0;
@@ -1089,7 +1101,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
if (i > 1) {
/* if this isn't really a block list skip it */
- if (gfs2_check_meta(bh, GFS2_METATYPE_IN))
+ if (gfs2_check_meta(bh, iblk_type))
continue;
head_size = sizeof(struct gfs2_meta_header);
} else {
@@ -1114,9 +1126,12 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
it gets with "bread". */
if(err < 0) {
stack;
+ error = err;
goto fail;
}
if(err > 0) {
+ if (!error)
+ error = err;
log_debug( _("Skipping block %" PRIu64
" (0x%" PRIx64 ")\n"),
block, block);
@@ -1135,10 +1150,10 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
} /* for all data on the indirect block */
} /* for blocks at that height */
} /* for height */
- return 0;
+ return error;
fail:
free_metalist(ip, mlp);
- return -1;
+ return error;
}
/**
@@ -1156,26 +1171,32 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
uint32_t height = ip->i_di.di_height;
int i, head_size;
uint64_t blks_checked = 0;
- int error = 0;
+ int error;
- if (!height)
- goto end;
+ if (!height && !S_ISDIR(ip->i_di.di_mode))
+ return 0;
for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
osi_list_init(&metalist[i]);
- /* create metalist for each level */
- if (build_and_check_metalist(ip, &metalist[0], pass)){
+ /* create and check the metadata list for each height */
+ error = build_and_check_metalist(ip, &metalist[0], pass);
+ if (error) {
stack;
- return -1;
+ return error;
}
- /* We don't need to record directory blocks - they will be
- * recorded later...i think... */
- if (S_ISDIR(ip->i_di.di_mode))
- log_debug( _("Directory with height > 0 at %llu (0x%llx)\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr);
+ /* For directories, we've already checked the "data" blocks which
+ * comprise the directory hash table, so we perform the directory
+ * checks and exit. */
+ if (S_ISDIR(ip->i_di.di_mode)) {
+ free_metalist(ip, &metalist[0]);
+ if (!(ip->i_di.di_flags & GFS2_DIF_EXHASH))
+ return 0;
+ /* check validity of leaf blocks and leaf chains */
+ error = check_leaf_blks(ip, pass);
+ return error;
+ }
/* check data blocks */
list = &metalist[height - 1];
@@ -1236,20 +1257,7 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
(unsigned long long)ip->i_di.di_num.no_addr);
fflush(stdout);
}
-
-end:
- if (S_ISDIR(ip->i_di.di_mode)) {
- /* check validity of leaf blocks and leaf chains */
- if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
- error = check_leaf_blks(ip, pass);
- if(error < 0)
- return -1;
- if(error > 0)
- return 1;
- }
- }
-
- return 0;
+ return error;
}
/* Checks stuffed inode directories */