public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* cluster: STABLE2 - GFS: gfs_fsck sometimes needs to be run twice
@ 2009-08-10 19:21 Bob Peterson
0 siblings, 0 replies; only message in thread
From: Bob Peterson @ 2009-08-10 19:21 UTC (permalink / raw)
To: cluster-cvs-relay
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=1028257becdaca26b9b14fa5abfb78280804d627
Commit: 1028257becdaca26b9b14fa5abfb78280804d627
Parent: 92cec48f73496aeb7bc6fb480a9decb6dc951a6a
Author: Bob Peterson <rpeterso@redhat.com>
AuthorDate: Mon Aug 10 14:01:22 2009 -0500
Committer: Bob Peterson <rpeterso@redhat.com>
CommitterDate: Mon Aug 10 14:02:02 2009 -0500
GFS: gfs_fsck sometimes needs to be run twice
bz 509225
This patch fixes numerous bugs whereby gfs_fsck was not "following
through" with its changes, and therefore a second run was often
needed to completely clean things up.
---
gfs/gfs_fsck/block_list.c | 24 ++-
gfs/gfs_fsck/block_list.h | 3 +-
gfs/gfs_fsck/fs_dir.c | 23 ++-
gfs/gfs_fsck/fs_dir.h | 2 +
gfs/gfs_fsck/metawalk.c | 471 +++++++++++++++++++++++-----------
gfs/gfs_fsck/metawalk.h | 13 +-
gfs/gfs_fsck/pass1.c | 640 +++++++++++++++++++++++++--------------------
gfs/gfs_fsck/pass1b.c | 124 ++++++----
gfs/gfs_fsck/pass1c.c | 177 +++++++------
gfs/gfs_fsck/pass2.c | 285 ++++++++++++---------
gfs/gfs_fsck/pass3.c | 23 +-
gfs/gfs_fsck/pass4.c | 51 +++-
gfs/gfs_fsck/pass5.c | 44 +++-
gfs/gfs_fsck/rgrp.c | 3 +-
gfs/gfs_fsck/super.c | 2 +-
15 files changed, 1160 insertions(+), 725 deletions(-)
diff --git a/gfs/gfs_fsck/block_list.c b/gfs/gfs_fsck/block_list.c
index ecc9dbd..23b8087 100644
--- a/gfs/gfs_fsck/block_list.c
+++ b/gfs/gfs_fsck/block_list.c
@@ -116,13 +116,13 @@ int block_mark(struct block_list *il, uint64_t block, enum mark_block mark)
int block_set(struct block_list *il, uint64_t block, enum mark_block mark)
{
int err = 0;
- err = block_clear(il, block, mark);
+ err = block_clear(il, block);
if(!err)
err = block_mark(il, block, mark);
return err;
}
-int block_clear(struct block_list *il, uint64_t block, enum mark_block m)
+int block_unmark(struct block_list *il, uint64_t block, enum mark_block m)
{
int err = 0;
@@ -154,6 +154,26 @@ int block_clear(struct block_list *il, uint64_t block, enum mark_block m)
return err;
}
+int block_clear(struct block_list *il, uint64_t block)
+{
+ int err = 0;
+
+ switch(il->type) {
+ case gbmap:
+ err = bitmap_clear(&il->list.gbmap.dup_map, block);
+ err = bitmap_clear(&il->list.gbmap.bad_map, block);
+ err = bitmap_clear(&il->list.gbmap.eattr_map, block);
+ err = bitmap_clear(&il->list.gbmap.group_map, block);
+ break;
+ default:
+ log_err("block list type %d not implemented\n",
+ il->type);
+ err = -1;
+ break;
+ }
+ return err;
+}
+
int block_check(struct block_list *il, uint64_t block, struct block_query *val)
{
int err = 0;
diff --git a/gfs/gfs_fsck/block_list.h b/gfs/gfs_fsck/block_list.h
index ca1524b..71c5642 100644
--- a/gfs/gfs_fsck/block_list.h
+++ b/gfs/gfs_fsck/block_list.h
@@ -77,7 +77,8 @@ struct block_list {
struct block_list *block_list_create(uint64_t size, enum block_list_type type);
int block_mark(struct block_list *il, uint64_t block, enum mark_block mark);
int block_set(struct block_list *il, uint64_t block, enum mark_block mark);
-int block_clear(struct block_list *il, uint64_t block, enum mark_block m);
+int block_unmark(struct block_list *il, uint64_t block, enum mark_block m);
+int block_clear(struct block_list *il, uint64_t block);
int block_check(struct block_list *il, uint64_t block,
struct block_query *val);
int block_check_for_mark(struct block_list *il, uint64_t block,
diff --git a/gfs/gfs_fsck/fs_dir.c b/gfs/gfs_fsck/fs_dir.c
index 399575e..c826e87 100644
--- a/gfs/gfs_fsck/fs_dir.c
+++ b/gfs/gfs_fsck/fs_dir.c
@@ -1726,8 +1726,7 @@ int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de,
sizeof(struct gfs_dinode);
else
de->de_rec_len = BH_SIZE(bh) - sizeof(struct gfs_leaf);
- }
- else {
+ } else {
bh_end = BH_DATA(bh) + BH_SIZE(bh);
/* first, figure out a probable name length */
p = (char *)dent + sizeof(struct gfs_dirent);
@@ -1750,3 +1749,23 @@ int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de,
write_buf(ip->i_sbd, bh, 0);
return 0;
}
+
+/**
+ * dirblk_truncate - truncate a directory block
+ */
+void dirblk_truncate(struct fsck_inode *ip, struct gfs_dirent *fixb,
+ osi_buf_t *bh)
+{
+ char *bh_end;
+ struct gfs_dirent de;
+ uint16_t old_rec_len;
+
+ bh_end = BH_DATA(bh) + BH_SIZE(bh);
+ /* truncate the block to save the most dentries. To do this we
+ have to patch the previous dent. */
+ gfs_dirent_in(&de, (char *)fixb);
+ old_rec_len = de.de_rec_len;
+ de.de_rec_len = bh_end - (char *)fixb;
+ gfs_dirent_out(&de, (char *)fixb);
+ write_buf(ip->i_sbd, bh, 0);
+}
diff --git a/gfs/gfs_fsck/fs_dir.h b/gfs/gfs_fsck/fs_dir.h
index c0e514b..f741723 100644
--- a/gfs/gfs_fsck/fs_dir.h
+++ b/gfs/gfs_fsck/fs_dir.h
@@ -29,5 +29,7 @@ int fs_dirent_alloc(struct fsck_inode *dip, osi_buf_t *bh,
int fs_dir_search(struct fsck_inode *dip, identifier_t *id, unsigned int *type);
int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de,
struct gfs_dirent *dent, int type, int first);
+void dirblk_truncate(struct fsck_inode *ip, struct gfs_dirent *fixb,
+ osi_buf_t *bh);
#endif /* __FS_DIR_H__ */
diff --git a/gfs/gfs_fsck/metawalk.c b/gfs/gfs_fsck/metawalk.c
index 8667897..135d200 100644
--- a/gfs/gfs_fsck/metawalk.c
+++ b/gfs/gfs_fsck/metawalk.c
@@ -8,9 +8,22 @@
#include "metawalk.h"
-int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
- int type, int *update, uint16_t *count,
- struct metawalk_fxns *pass)
+/*
+ * check_entries - check directory entries for a given block
+ *
+ * @ip - dinode associated with this leaf block
+ * bh - buffer for the leaf block
+ * type - type of block this is (linear or exhash)
+ * @update - set to 1 if the block was updated
+ * @count - set to the count entries
+ * @pass - structure pointing to pass-specific functions
+ *
+ * returns: 0 - good block or it was repaired to be good
+ * -1 - error occurred
+ */
+static int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int type,
+ int *update, uint16_t *count,
+ struct metawalk_fxns *pass)
{
struct gfs_leaf *leaf = NULL;
struct gfs_dirent *dent;
@@ -38,31 +51,46 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
}
prev = NULL;
- if(!pass->check_dentry) {
+ if(!pass->check_dentry)
return 0;
- }
while(1) {
+ if (skip_this_pass || fsck_abort)
+ return 0;
memset(&de, 0, sizeof(struct gfs_dirent));
gfs_dirent_in(&de, (char *)dent);
filename = (char *)dent + sizeof(struct gfs_dirent);
if (de.de_rec_len < sizeof(struct gfs_dirent) +
- de.de_name_len || !de.de_name_len) {
+ de.de_name_len ||
+ (de.de_inum.no_formal_ino && !de.de_name_len && !first)) {
log_err("Directory block %"
PRIu64 ", entry %d of directory %"
PRIu64 " is corrupt.\n", BH_BLKNO(bh),
(*count) + 1, ip->i_di.di_num.no_addr);
if (query(ip->i_sbd, "Attempt to repair it? (y/n) ")) {
if (dirent_repair(ip, bh, &de, dent, type,
- first))
- break;
- }
- else {
- log_err("Corrupt directory entry %d ignored, "
+ first)) {
+ if (first) /* make a new sentinel */
+ dirblk_truncate(ip, dent, bh);
+ else
+ dirblk_truncate(ip, prev, bh);
+ *update = 1;
+ log_err("Unable to repair corrupt "
+ "directory entry; the entry "
+ "was removed instead.\n");
+ return 0;
+ } else {
+ log_err("Corrupt directory entry "
+ "repaired.\n");
+ *update = 1;
+ /* keep looping through dentries */
+ }
+ } else {
+ log_err("Corrupt directory entry ignored, "
"stopped after checking %d entries.\n",
*count);
- break;
+ return 0;
}
}
if (!de.de_inum.no_formal_ino){
@@ -70,9 +98,23 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
log_debug("First dirent is a sentinel (place holder).\n");
first = 0;
} else {
- /* FIXME: Do something about this */
- log_err("Directory entry with inode number of zero in leaf %"PRIu64" of directory %"PRIu64"!\n", BH_BLKNO(bh), ip->i_di.di_num.no_addr);
- return 1;
+ log_err("Directory entry with inode number of "
+ "zero in leaf %"PRIu64" of directory "
+ "%"PRIu64"!\n", BH_BLKNO(bh),
+ ip->i_di.di_num.no_addr);
+ if (query(ip->i_sbd,
+ "Attempt to remove it? (y/n) ")) {
+ dirblk_truncate(ip, prev, bh);
+ *update = 1;
+ log_err("The corrupt directory entry "
+ "was removed.\n");
+ } else {
+ log_err("Corrupt directory entry "
+ "ignored, stopped after "
+ "checking %d entries.\n",
+ *count);
+ }
+ return 0;
}
} else {
@@ -84,9 +126,6 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
stack;
return -1;
}
- /*if(error > 0) {
- return 1;
- }*/
}
if ((char *)dent + de.de_rec_len >= bh_end){
@@ -96,27 +135,22 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
/* If we didn't clear the dentry, or if we did, but it
* was the first dentry, set prev */
- if(!error || first) {
+ if(!error || first)
prev = dent;
- }
-
first = 0;
-
-
dent = (struct gfs_dirent *)((char *)dent + de.de_rec_len);
}
return 0;
}
-
/* 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. */
-void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no,
- uint64_t *bad_leaf, uint64_t old_leaf, int index,
- const char *msg)
+static void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no,
+ uint64_t *bad_leaf, uint64_t old_leaf,
+ uint64_t first_ok_leaf, int lindex, const char *msg)
{
if (*bad_leaf != *leaf_no) {
log_err("Directory Inode %" PRIu64 " points to leaf %"
@@ -125,7 +159,12 @@ void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no,
}
if (*leaf_no == *bad_leaf ||
query(ip->i_sbd, "Attempt to patch around it? (y/n) ")) {
- put_leaf_nr(ip, index, old_leaf);
+ if (check_range(ip->i_sbd, old_leaf) == 0)
+ put_leaf_nr(ip, lindex, old_leaf);
+ else
+ put_leaf_nr(ip, lindex, first_ok_leaf);
+ log_err("Directory Inode %" PRIu64 ") repaired.\n",
+ ip->i_di.di_num.no_addr);
}
else
log_err("Bad leaf left in place.\n");
@@ -133,29 +172,51 @@ void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no,
*leaf_no = old_leaf;
}
-/* Checks exthash directory entries */
-int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
+/* Checks all exhash directory leaf blocks (and their directory entries) */
+static int check_leaf_blks(struct fsck_inode *ip, int *update,
+ struct metawalk_fxns *pass)
{
int error;
struct gfs_leaf leaf, oldleaf;
uint64_t leaf_no, old_leaf, bad_leaf = -1;
+ uint64_t first_leaf_ptr = -1, first_ok_leaf = -1;
osi_buf_t *lbh;
- int index;
+ int lindex;
struct fsck_sb *sbp = ip->i_sbd;
uint16_t count;
int ref_count = 0, exp_count = 0;
- old_leaf = 0;
+ /* 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. */
+ first_ok_leaf = -1;
+ for(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
+ get_leaf_nr(ip, lindex, &first_ok_leaf);
+ if (first_leaf_ptr == -1)
+ first_leaf_ptr = first_ok_leaf;
+ if(check_range(sbp, first_ok_leaf) == 0) {
+ get_and_read_buf(sbp, first_ok_leaf, &lbh, 0);
+ /* Make sure it's really a valid leaf block. */
+ if (check_meta(lbh, GFS_METATYPE_LF) == 0) {
+ relse_buf(sbp, lbh);
+ break;
+ }
+ relse_buf(sbp, lbh);
+ }
+ }
+ old_leaf = -1;
memset(&oldleaf, 0, sizeof(oldleaf));
- for(index = 0; index < (1 << ip->i_di.di_depth); index++) {
- if(get_leaf_nr(ip, index, &leaf_no)) {
+ for(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
+ if (fsck_abort)
+ break;
+ if(get_leaf_nr(ip, lindex, &leaf_no)) {
log_err("Unable to get leaf block number in dir %"
PRIu64"\n"
"\tDepth = %u\n"
- "\tindex = %u\n",
+ "\tlindex = %u\n",
ip->i_num.no_addr,
ip->i_di.di_depth,
- index);
+ lindex);
return -1;
}
@@ -163,56 +224,55 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
* until those extra pointers are needed, so skip the
* dups */
if (leaf_no == bad_leaf) {
- put_leaf_nr(ip, index, old_leaf); /* fill w/old leaf */
+ put_leaf_nr(ip, lindex, old_leaf); /* fill w/old leaf */
ref_count++;
continue;
}
else if(old_leaf == leaf_no) {
ref_count++;
continue;
- } else {
- if(ref_count != exp_count){
- log_err("Dir #%"PRIu64" has an incorrect number "
- "of pointers to leaf #%"PRIu64"\n"
- "\tFound: %u, Expected: %u\n",
- ip->i_num.no_addr,
- old_leaf,
- ref_count,
- exp_count);
- if (query(ip->i_sbd, "Attempt to fix it? (y/n) ")) {
- int factor = 0, divisor = ref_count;
-
- get_and_read_buf(sbp, old_leaf, &lbh,
- 0);
- while (divisor > 1) {
- factor++;
- divisor /= 2;
- }
- oldleaf.lf_depth = ip->i_di.di_depth -
- factor;
- gfs_leaf_out(&oldleaf, BH_DATA(lbh));
- write_buf(sbp, lbh, 0);
- relse_buf(sbp, lbh);
+ }
+ if(check_range(sbp, old_leaf) == 0 && ref_count != exp_count){
+ log_err("Dir #%"PRIu64" has an incorrect number "
+ "of pointers to leaf #%"PRIu64"\n"
+ "\tFound: %u, Expected: %u\n",
+ ip->i_num.no_addr, old_leaf, ref_count,
+ exp_count);
+ if (query(ip->i_sbd, "Attempt to fix it? (y/n) ")) {
+ int factor = 0, divisor = ref_count;
+
+ get_and_read_buf(sbp, old_leaf, &lbh, 0);
+ while (divisor > 1) {
+ factor++;
+ divisor /= 2;
}
- else
- return 1;
+ gfs_leaf_in(&oldleaf, BH_DATA(lbh));
+ oldleaf.lf_depth = ip->i_di.di_depth - factor;
+ gfs_leaf_out(&oldleaf, BH_DATA(lbh));
+ write_buf(sbp, lbh, 0);
+ relse_buf(sbp, lbh);
}
- ref_count = 1;
+ else
+ return 1;
}
+ ref_count = 1;
count = 0;
do {
+ if (fsck_abort)
+ break;
/* Make sure the block number is in range. */
if(check_range(ip->i_sbd, leaf_no)){
log_err("Leaf block #%"PRIu64" is out of "
"range for directory #%"PRIu64".\n",
leaf_no, ip->i_di.di_num.no_addr);
warn_and_patch(ip, &leaf_no, &bad_leaf,
- old_leaf, index,
+ old_leaf, first_ok_leaf, lindex,
"that is out of range");
memcpy(&leaf, &oldleaf, sizeof(oldleaf));
break;
}
+ *update = 0;
/* Try to read in the leaf block. */
if(get_and_read_buf(sbp, leaf_no, &lbh, 0)){
log_err("Unable to read leaf block #%"
@@ -220,7 +280,7 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
"directory #%"PRIu64".\n",
leaf_no, ip->i_di.di_num.no_addr);
warn_and_patch(ip, &leaf_no, &bad_leaf,
- old_leaf, index,
+ old_leaf, first_ok_leaf, lindex,
"that cannot be read");
memcpy(&leaf, &oldleaf, sizeof(oldleaf));
relse_buf(sbp, lbh);
@@ -229,7 +289,7 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
/* Make sure it's really a valid leaf block. */
if (check_meta(lbh, GFS_METATYPE_LF)) {
warn_and_patch(ip, &leaf_no, &bad_leaf,
- old_leaf, index,
+ old_leaf, first_ok_leaf, lindex,
"that is not really a leaf");
memcpy(&leaf, &oldleaf, sizeof(oldleaf));
relse_buf(sbp, lbh);
@@ -241,29 +301,34 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
pass->private);
}
+ /* Make sure it's really a leaf. */
+ if (leaf.lf_header.mh_type != GFS_METATYPE_LF) {
+ log_err("Inode %" PRIu64 " points to bad leaf "
+ PRIu64 ".\n", ip->i_di.di_num.no_addr,
+ leaf_no);
+ relse_buf(sbp, lbh);
+ break;
+ }
+
exp_count = (1 << (ip->i_di.di_depth - leaf.lf_depth));
log_debug("expected count %u - %u %u\n", exp_count,
ip->i_di.di_depth, leaf.lf_depth);
if(pass->check_dentry &&
ip->i_di.di_type == GFS_FILE_DIR) {
- error = check_entries(ip, lbh, index,
- DIR_EXHASH, update,
- &count,
- pass);
+ error = check_entries(ip, lbh, DIR_EXHASH,
+ update, &count, pass);
/* Since the buffer possibly got
- updated directly, release it now,
- and grab it again later if we need it */
+ * updated directly, release it now,
+ * and grab it again later if we need it. */
+
relse_buf(sbp, lbh);
+
if(error < 0) {
stack;
return -1;
}
- if(error > 0) {
- return 1;
- }
-
if(update && (count != leaf.lf_entries)) {
if(get_and_read_buf(sbp, leaf_no,
@@ -289,22 +354,19 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
relse_buf(sbp, lbh);
}
/* FIXME: Need to get entry count and
- * compare it against
- * leaf->lf_entries */
-
- break;
+ * compare it against leaf->lf_entries */
+ break; /* not a chain; go back to outer loop */
} else {
relse_buf(sbp, lbh);
- if(!leaf.lf_next) {
+ if(!leaf.lf_next)
break;
- }
leaf_no = leaf.lf_next;
log_debug("Leaf chain detected.\n");
}
- } while(1);
+ } while(1); /* while we have chained leaf blocks */
old_leaf = leaf_no;
memcpy(&oldleaf, &leaf, sizeof(oldleaf));
- }
+ } /* for every leaf block */
return 0;
}
@@ -325,8 +387,12 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
sizeof(struct gfs_meta_header));
while(1){
- error = pass->check_eattr_entry(ip, bh, ea_hdr, ea_hdr_prev,
- pass->private);
+ if (ea_hdr->ea_type == GFS_EATYPE_UNUSED)
+ error = 0;
+ else
+ error = pass->check_eattr_entry(ip, bh, ea_hdr,
+ ea_hdr_prev,
+ pass->private);
if(error < 0) {
stack;
return -1;
@@ -346,6 +412,7 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
** In this case, the EA ** code leaves
** the blocks ** there for **
** reuse........... */
+
for(i = 0; i < ea_hdr->ea_num_ptrs; i++){
if(pass->check_eattr_extentry(ip,
ea_data_ptr,
@@ -353,8 +420,8 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
ea_hdr_prev,
pass->private)) {
if (query(ip->i_sbd,
- "Repair the bad EA? "
- "(y/n) ")) {
+ "Repair the bad Extended "
+ "Attribute? (y/n) ")) {
ea_hdr->ea_num_ptrs = i;
ea_hdr->ea_data_len =
cpu_to_be32(tot_ealen);
@@ -362,10 +429,16 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
/* Endianness doesn't matter
in this case because it's
a single byte. */
+ block_set(sdp->bl,
+ ip->i_di.di_eattr,
+ meta_eattr);
write_buf(sdp, bh, 0);
- return -1;
+ log_err("The EA was fixed.\n");
+ } else {
+ error = 1;
+ log_err("The bad EA was not "
+ "fixed.\n");
}
- log_err("The bad EA was not fixed.\n");
}
tot_ealen += sdp->sb.sb_bsize -
sizeof(struct gfs_meta_header);
@@ -383,7 +456,7 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
gfs32_to_cpu(ea_hdr->ea_rec_len));
}
- return 0;
+ return error;
}
/**
@@ -391,7 +464,7 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
* @ip: the inode the eattr comes from
* @block: block number of the leaf
*
- * Returns: 0 on success, -1 if removal is needed
+ * Returns: 0 on success, 1 if removal is needed, -1 on error
*/
static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
uint64_t parent, struct metawalk_fxns *pass)
@@ -408,7 +481,8 @@ static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
return -1;
}
if(error > 0) {
- relse_buf(ip->i_sbd, bh);
+ if (bh)
+ relse_buf(ip->i_sbd, bh);
return 1;
}
if (bh) {
@@ -421,10 +495,6 @@ static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
return 0;
}
-
-
-
-
/**
* check_indirect_eattr
* @ip: the inode the eattr comes from
@@ -433,12 +503,16 @@ static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
* Returns: 0 on success -1 on error
*/
static int check_indirect_eattr(struct fsck_inode *ip, uint64_t indirect,
- struct metawalk_fxns *pass){
+ struct metawalk_fxns *pass)
+{
int error = 0;
uint64_t *ea_leaf_ptr, *end;
uint64_t block;
osi_buf_t *indirect_buf = NULL;
struct fsck_sb *sdp = ip->i_sbd;
+ int update_indir_block = 0;
+ int first_ea_is_bad = 0;
+ uint64_t di_eattr_save = ip->i_di.di_eattr;
log_debug("Checking EA indirect block #%"PRIu64".\n", indirect);
@@ -451,41 +525,75 @@ static int check_indirect_eattr(struct fsck_inode *ip, uint64_t indirect,
ea_leaf_ptr = (uint64 *)(BH_DATA(indirect_buf)
+ sizeof(struct gfs_indirect));
- end = ea_leaf_ptr
- + ((sdp->sb.sb_bsize
- - sizeof(struct gfs_indirect)) / 8);
+ end = ea_leaf_ptr + ((sdp->sb.sb_bsize
+ - sizeof(struct gfs_indirect)) / 8);
while(*ea_leaf_ptr && (ea_leaf_ptr < end)){
block = gfs64_to_cpu(*ea_leaf_ptr);
leaf_pointers++;
error = check_leaf_eattr(ip, block, indirect, pass);
- if (error)
+ if (error) {
leaf_pointer_errors++;
+ if (!update_indir_block) {
+ if (query(sdp, "Fix the indirect "
+ "block too? (y/n) ")) {
+ update_indir_block = 1;
+ *ea_leaf_ptr = 0;
+ }
+ } else
+ *ea_leaf_ptr = 0;
+ }
+ /* If the first eattr lead is bad, we can't have
+ a hole, so we have to treat this as an unrecoverable
+ eattr error and delete all eattr info. Calling
+ finish_eattr_indir here causes ip->i_di.di_eattr = 0
+ and that ensures that subsequent calls to
+ check_leaf_eattr result in the eattr
+ check_leaf_block nuking them all "due to previous
+ errors" */
+ if (leaf_pointers == 1 && leaf_pointer_errors == 1) {
+ first_ea_is_bad = 1;
+ if (pass->finish_eattr_indir)
+ pass->finish_eattr_indir(ip,
+ leaf_pointers,
+ leaf_pointer_errors,
+ pass->private);
+ } else if (leaf_pointer_errors) {
+ /* This is a bit tricky. We can't have eattr
+ holes. So if we have 4 good eattrs, 1 bad
+ eattr and 5 more good ones: GGGGBGGGGG,
+ we need to tell check_leaf_eattr to delete
+ all eattrs after the bad one. So we want:
+ GGGG when we finish. To do that, we set
+ di_eattr to 0 temporarily. */
+ ip->i_di.di_eattr = 0;
+ }
ea_leaf_ptr++;
}
if (pass->finish_eattr_indir) {
- int indir_ok = 1;
-
- if (leaf_pointer_errors == leaf_pointers)
- indir_ok = 0;
- pass->finish_eattr_indir(ip, indir_ok, pass->private);
- if (!indir_ok) {
+ if (!first_ea_is_bad) {
+ /* If the first ea is good but subsequent ones
+ were bad and deleted, we need to restore
+ the saved di_eattr block. */
+ if (leaf_pointer_errors)
+ ip->i_di.di_eattr = di_eattr_save;
+ pass->finish_eattr_indir(ip, leaf_pointers,
+ leaf_pointer_errors,
+ pass->private);
+ }
+ if (leaf_pointer_errors == leaf_pointers) {
fs_set_bitmap(sdp, indirect, GFS_BLKST_FREE);
- block_clear(sdp->bl, indirect, indir_blk);
block_set(sdp->bl, indirect, block_free);
error = 1;
}
}
}
-
if (indirect_buf)
relse_buf(sdp, indirect_buf);
+
return error;
}
-
-
-
/**
* check_inode_eattr - check the EA's for a single inode
* @ip: the inode whose EA to check
@@ -496,9 +604,8 @@ int check_inode_eattr(struct fsck_inode *ip, struct metawalk_fxns *pass)
{
int error = 0;
- if(!ip->i_di.di_eattr){
+ if(!ip->i_di.di_eattr)
return 0;
- }
log_debug("Extended attributes exist for inode #%"PRIu64".\n",
ip->i_num.no_formal_ino);
@@ -507,8 +614,9 @@ int check_inode_eattr(struct fsck_inode *ip, struct metawalk_fxns *pass)
if((error = check_indirect_eattr(ip, ip->i_di.di_eattr, pass)))
stack;
} else {
- if((error = check_leaf_eattr(ip, ip->i_di.di_eattr,
- ip->i_di.di_num.no_addr, pass)))
+ error = check_leaf_eattr(ip, ip->i_di.di_eattr,
+ ip->i_di.di_num.no_addr, pass);
+ if (error)
stack;
}
@@ -519,31 +627,27 @@ int check_inode_eattr(struct fsck_inode *ip, struct metawalk_fxns *pass)
* build_metalist
* @ip:
* @mlp:
- *
*/
-
static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
struct metawalk_fxns *pass)
{
uint32 height = ip->i_di.di_height;
- osi_buf_t *bh, *nbh;
+ osi_buf_t *bh, *nbh, *metabh;
osi_list_t *prev_list, *cur_list, *tmp;
int i, head_size;
uint64 *ptr, block;
int err;
- if(get_and_read_buf(ip->i_sbd, ip->i_di.di_num.no_addr, &bh, 0)) {
+ if(get_and_read_buf(ip->i_sbd, ip->i_di.di_num.no_addr, &metabh, 0)) {
stack;
return -1;
}
- osi_list_add(&bh->b_list, &mlp[0]);
+ osi_list_add(&metabh->b_list, &mlp[0]);
/* if(<there are no indirect blocks to check>) */
- if (height < 2) {
+ if (height < 2)
return 0;
- }
-
for (i = 1; i < height; i++){
prev_list = &mlp[i - 1];
cur_list = &mlp[i];
@@ -551,9 +655,17 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
bh = osi_list_entry(tmp, osi_buf_t, b_list);
- head_size = (i > 1 ?
- sizeof(struct gfs_indirect) :
- sizeof(struct gfs_dinode));
+ if (i > 1) {
+ /* if this isn't really a block list skip it */
+ if (check_meta(bh, GFS_METATYPE_IN))
+ continue;
+ head_size = sizeof(struct gfs_indirect);
+ } else {
+ /* if this isn't really a dinode, skip it */
+ if (check_meta(bh, GFS_METATYPE_DI))
+ continue;
+ head_size = sizeof(struct gfs_dinode);
+ }
for (ptr = (uint64 *)(bh->b_data + head_size);
(char *)ptr < (bh->b_data + bh->b_size);
@@ -564,7 +676,6 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
continue;
block = gfs64_to_cpu(*ptr);
-
err = pass->check_metalist(ip, block, &nbh,
pass->private);
if(err < 0) {
@@ -590,8 +701,7 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
return 0;
fail:
- for (i = 0; i < GFS_MAX_META_HEIGHT; i++)
- {
+ for (i = 0; i < GFS_MAX_META_HEIGHT; i++) {
osi_list_t *list;
list = &mlp[i];
while (!osi_list_empty(list))
@@ -601,6 +711,8 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
relse_buf(ip->i_sbd, bh);
}
}
+ /* This is an error path, so we need to release the buffer here: */
+ relse_buf(ip->i_sbd, metabh);
return -1;
}
@@ -624,7 +736,6 @@ int check_metatree(struct fsck_inode *ip, struct metawalk_fxns *pass)
if (!height)
goto end;
-
for (i = 0; i < GFS_MAX_META_HEIGHT; i++)
osi_list_init(&metalist[i]);
@@ -639,21 +750,28 @@ int check_metatree(struct fsck_inode *ip, struct metawalk_fxns *pass)
if (ip->i_di.di_type == GFS_FILE_DIR) {
log_debug("Directory with height > 0 at %"PRIu64"\n",
ip->i_di.di_num.no_addr);
-
}
/* check data blocks */
list = &metalist[height - 1];
- for (tmp = list->next; tmp != list; tmp = tmp->next)
- {
+ for (tmp = list->next; tmp != list; tmp = tmp->next) {
bh = osi_list_entry(tmp, osi_buf_t, b_list);
- head_size = (height != 1 ? sizeof(struct gfs_indirect) : sizeof(struct gfs_dinode));
+ if (height > 1) {
+ /* if this isn't really a block list skip it */
+ if (check_meta(bh, GFS_METATYPE_IN))
+ continue;
+ head_size = sizeof(struct gfs_indirect);
+ } else {
+ /* if this isn't really a dinode, skip it */
+ if (check_meta(bh, GFS_METATYPE_DI))
+ continue;
+ head_size = sizeof(struct gfs_dinode);
+ }
ptr = (uint64 *)(bh->b_data + head_size);
- for ( ; (char *)ptr < (bh->b_data + bh->b_size); ptr++)
- {
+ for ( ; (char *)ptr < (bh->b_data + bh->b_size); ptr++) {
if (!*ptr)
continue;
@@ -667,7 +785,6 @@ int check_metatree(struct fsck_inode *ip, struct metawalk_fxns *pass)
}
}
-
/* free metalists */
for (i = 0; i < GFS_MAX_META_HEIGHT; i++)
{
@@ -684,7 +801,7 @@ end:
if (ip->i_di.di_type == GFS_FILE_DIR) {
/* check validity of leaf blocks and leaf chains */
if (ip->i_di.di_flags & GFS_DIF_EXHASH) {
- error = check_leaf(ip, &update, pass);
+ error = check_leaf_blks(ip, &update, pass);
if(error < 0)
return -1;
if(error > 0)
@@ -695,15 +812,14 @@ end:
return 0;
}
-
/* Checks stuffed inode directories */
-int check_linear_dir(struct fsck_inode *ip, osi_buf_t *bh, int *update,
+static int check_linear_dir(struct fsck_inode *ip, osi_buf_t *bh, int *update,
struct metawalk_fxns *pass)
{
int error = 0;
uint16_t count = 0;
- error = check_entries(ip, bh, 0, DIR_LINEAR, update, &count, pass);
+ error = check_entries(ip, bh, DIR_LINEAR, update, &count, pass);
if(error < 0) {
stack;
return -1;
@@ -734,8 +850,7 @@ int check_dir(struct fsck_sb *sbp, uint64_t block, struct metawalk_fxns *pass)
}
if(ip->i_di.di_flags & GFS_DIF_EXHASH) {
-
- error = check_leaf(ip, &update, pass);
+ error = check_leaf_blks(ip, &update, pass);
if(error < 0) {
stack;
free_inode(&ip);
@@ -757,10 +872,8 @@ int check_dir(struct fsck_sb *sbp, uint64_t block, struct metawalk_fxns *pass)
relse_buf(sbp, bh);
return error;
-
}
-
static int remove_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
struct gfs_dirent *prev_de,
osi_buf_t *bh, char *filename, int *update,
@@ -877,7 +990,6 @@ int dinode_hash_insert(osi_list_t *buckets, uint64_t key, struct dir_info *di)
return 0;
}
-
int dinode_hash_remove(osi_list_t *buckets, uint64_t key)
{
osi_list_t *tmp;
@@ -896,3 +1008,66 @@ int dinode_hash_remove(osi_list_t *buckets, uint64_t key)
}
return -1;
}
+
+/**
+ * free_block - free up a block given its block number
+ */
+void free_block(struct fsck_sb *sdp, uint64_t block)
+{
+ osi_buf_t *bh;
+ struct fsck_rgrp *rgd;
+
+ fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+ /* Adjust the free space count for the freed block */
+ rgd = fs_blk2rgrpd(sdp, block); /* find the rg for indir block */
+ get_and_read_buf(sdp, rgd->rd_ri.ri_addr, &bh, 0);
+ rgd->rd_rg.rg_free++; /* adjust the free count */
+ gfs_rgrp_out(&rgd->rd_rg, bh->b_data); /* back to the buffer */
+ relse_buf(sdp, bh); /* release the buffer */
+}
+
+/**
+ * delete_blocks - delete blocks associated with an inode
+ */
+int delete_blocks(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+ const char *btype, void *private)
+{
+ struct block_query q = {0};
+
+ if (check_range(ip->i_sbd, block) == 0) {
+ if (block_check(ip->i_sbd->bl, block, &q))
+ return 0;
+ if (!q.dup_block) {
+ log_info("Deleting %s block %lld as part "
+ "of inode %lld \n",
+ btype, block, ip->i_di.di_num.no_addr);
+ block_set(ip->i_sbd->bl, block, block_free);
+ free_block(ip->i_sbd, block);
+ }
+ }
+ return 0;
+}
+
+int delete_metadata(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+ void *private)
+{
+ return delete_blocks(ip, block, bh, "metadata", private);
+}
+
+int delete_data(struct fsck_inode *ip, uint64_t block, void *private)
+{
+ return delete_blocks(ip, block, NULL, "data", private);
+}
+
+int delete_eattr_indir(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+ osi_buf_t **bh, void *private)
+{
+ return delete_blocks(ip, block, NULL, "indirect extended attribute",
+ private);
+}
+
+int delete_eattr_leaf(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+ osi_buf_t **bh, void *private)
+{
+ return delete_blocks(ip, block, NULL, "extended attribute", private);
+}
diff --git a/gfs/gfs_fsck/metawalk.h b/gfs/gfs_fsck/metawalk.h
index e5e6bd5..2d4eed2 100644
--- a/gfs/gfs_fsck/metawalk.h
+++ b/gfs/gfs_fsck/metawalk.h
@@ -14,6 +14,16 @@ int remove_dentry_from_dir(struct fsck_sb *sbp, uint64_t dir,
int find_di(struct fsck_sb *sbp, uint64_t childblock, struct dir_info **dip);
int dinode_hash_insert(osi_list_t *buckets, uint64_t key, struct dir_info *di);
int dinode_hash_remove(osi_list_t *buckets, uint64_t key);
+void free_block(struct fsck_sb *sdp, uint64_t block);
+int delete_blocks(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+ const char *btype, void *private);
+int delete_metadata(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+ void *private);
+int delete_data(struct fsck_inode *ip, uint64_t block, void *private);
+int delete_eattr_indir(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+ osi_buf_t **bh, void *private);
+int delete_eattr_leaf(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+ osi_buf_t **bh, void *private);
/* metawalk_fxns: function pointers to check various parts of the fs
*
@@ -60,7 +70,8 @@ struct metawalk_fxns {
struct gfs_ea_header *ea_hdr,
struct gfs_ea_header *ea_hdr_prev,
void *private);
- int (*finish_eattr_indir) (struct fsck_inode *ip, int indir_ok,
+ int (*finish_eattr_indir) (struct fsck_inode *ip, int leaf_pointers,
+ int leaf_pointer_errors,
void *private);
};
diff --git a/gfs/gfs_fsck/pass1.c b/gfs/gfs_fsck/pass1.c
index 3768ebf..8569db5 100644
--- a/gfs/gfs_fsck/pass1.c
+++ b/gfs/gfs_fsck/pass1.c
@@ -48,8 +48,8 @@ static int check_extended_leaf_eattr(struct fsck_inode *ip, uint64_t *data_ptr,
struct gfs_ea_header *ea_hdr,
struct gfs_ea_header *ea_hdr_prev,
void *private);
-static int finish_eattr_indir(struct fsck_inode *ip, int indir_ok,
- void *private);
+static int finish_eattr_indir(struct fsck_inode *ip, int leaf_pointers,
+ int leaf_pointer_errors, void *private);
struct metawalk_fxns pass1_fxns = {
.private = NULL,
@@ -73,7 +73,6 @@ static int leaf(struct fsck_inode *ip, uint64_t block, osi_buf_t *bh,
log_debug("\tLeaf block at %15"PRIu64"\n", BH_BLKNO(bh));
block_set(sdp->bl, BH_BLKNO(bh), leaf_blk);
bc->indir_count++;
-
return 0;
}
@@ -93,23 +92,22 @@ static int check_metalist(struct fsck_inode *ip, uint64_t block,
log_debug("Bad indirect block pointer (out of range).\n");
return 1;
- }
+ }
if(block_check(sdp->bl, block, &q)) {
stack;
return -1;
}
if(q.block_type != block_free) {
- log_debug("Found duplicate block in indirect block -"
- " was marked %d\n", q.block_type);
+ log_err("Found duplicate block referenced as metadata in "
+ "indirect block - was marked %d\n", q.block_type);
block_mark(sdp->bl, block, dup_block);
found_dup = 1;
}
get_and_read_buf(ip->i_sbd, block, &nbh, 0);
- /** Attention -- experimental code **/
- if (check_meta(nbh, GFS_METATYPE_IN)){
- log_debug("Bad indirect block pointer "
- "(points to something that is not an indirect block).\n");
+ if (check_meta(nbh, GFS_METATYPE_IN)){
+ log_debug("Bad indirect block pointer (points to "
+ "something that is not an indirect block).\n");
if(!found_dup) {
block_set(sdp->bl, block, meta_inval);
relse_buf(ip->i_sbd, nbh);
@@ -117,29 +115,30 @@ static int check_metalist(struct fsck_inode *ip, uint64_t block,
}
relse_buf(ip->i_sbd, nbh);
- }else{ /* blk check ok */
+ } else /* blk check ok */
*bh = nbh;
- }
- /** Attention -- experimental code end **/
- block_set(sdp->bl, block, indir_blk);
+ if (!found_dup) {
+ log_debug("Setting %" PRIu64 " to indirect block.\n", block);
+ block_set(sdp->bl, block, indir_blk);
+ }
bc->indir_count++;
return 0;
}
-
-
static int check_data(struct fsck_inode *ip, uint64_t block, void *private)
{
struct fsck_sb *sdp = ip->i_sbd;
struct block_query q = {0};
osi_buf_t *data_bh;
struct block_count *bc = (struct block_count *) private;
+ int error = 0, btype;
if (check_range(ip->i_sbd, block)) {
-
- log_err( "Bad data block pointer (out of range)\n");
+ log_err("inode %lld has a bad data block pointer "
+ "%lld (out of range)\n",
+ ip->i_di.di_num.no_addr, block);
/* Mark the owner of this block with the bad_block
* designator so we know to check it for out of range
* blocks later */
@@ -148,60 +147,186 @@ static int check_data(struct fsck_inode *ip, uint64_t block, void *private)
return 1;
}
+ if(get_and_read_buf(ip->i_sbd, block, &data_bh, 0)) {
+ stack;
+ block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
+ return 1;
+ }
+ if(block_check(sdp->bl, block, &q)) {
+ stack;
+ log_err("Found bad block referenced as data at %"
+ PRIu64 " (0x%"PRIx64 ")\n", block, block);
+ relse_buf(sdp, data_bh);
+ return -1;
+ }
if (ip->i_di.di_flags & GFS_DIF_JDATA){
- /* Journaled data *is* metadata */
- if(get_and_read_buf(ip->i_sbd, block, &data_bh, 0)) {
- stack;
- block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
- return 1;
- }
if(check_meta(data_bh, GFS_METATYPE_JD)) {
log_err("Block #%"PRIu64" in inode %"PRIu64" does not have "
- "correct meta header. is %u should be %u\n",
+ "correct meta header. Is %u should be %u\n",
block, ip->i_di.di_num.no_addr,
gfs32_to_cpu(((struct gfs_meta_header *)
BH_DATA((data_bh)))->mh_type),
GFS_METATYPE_JD);
+ block_set(sdp->bl, ip->i_di.di_num.no_addr,
+ meta_inval);
relse_buf(sdp, data_bh);
- block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
return 1;
}
- if(block_check(sdp->bl, block, &q)) {
- stack;
- relse_buf(sdp, data_bh);
- return -1;
+ if(q.block_type != block_free) {
+ log_err("Found duplicate block referenced as "
+ "journaled data at %" PRIu64"\n", block);
+ if (q.block_type != meta_inval) {
+ block_mark(sdp->bl, block, dup_block);
+ relse_buf(sdp, data_bh);
+ /* If the prev ref was as data, this is likely
+ a data block, so keep the block count for
+ both refs. */
+ if (q.block_type == block_used)
+ bc->data_count++;
+ return 1;
+ }
+ /* An explanation is in order here. At this point we
+ found a duplicate block, a block that was already
+ referenced somewhere else. We'll resolve those
+ duplicates in pass1b. However, if the block is
+ marked "invalid" that's a special case. It's likely
+ that the block was discovered to be invalid
+ metadata--i.e. doesn't have a metadata header.
+ However, it still may be a valid data block, since
+ they won't have metadata headers. In that case,
+ the block is marked as duplicate, but also as a
+ journaled data block. */
+ error = 1;
+ log_debug("Marking %" PRIu64 " as journaled data "
+ "block\n", block);
+ block_unmark(sdp->bl, block, meta_inval);
+ block_mark(sdp->bl, block, dup_block);
}
+ block_mark(sdp->bl, block, journal_blk);
+ } else {
if(q.block_type != block_free) {
- log_debug("Found duplicate block at %"
- PRIu64"\n", block);
+ log_err("Found duplicate block referenced as data "
+ "at %" PRIu64"\n", block);
+ if (q.block_type != meta_inval) {
+ block_mark(sdp->bl, block, dup_block);
+ bc->data_count++;
+ relse_buf(sdp, data_bh);
+ return 1;
+ }
+ error = 1;
+ log_debug("Marking %"PRIu64 " as data block\n", block);
+ block_unmark(sdp->bl, block, meta_inval);
block_mark(sdp->bl, block, dup_block);
- bc->data_count++;
- relse_buf(sdp, data_bh);
- return 1;
}
- log_debug("Setting %"PRIu64 " to journal block\n", block);
- block_set(sdp->bl, block, journal_blk);
- bc->data_count++;
- relse_buf(sdp, data_bh);
+ block_mark(sdp->bl, block, block_used);
+ }
+ relse_buf(sdp, data_bh);
+ /* This is also confusing, so I'll clarify. There are two bitmaps:
+ (1) The bitmap that fsck uses to keep track of what block
+ type has been discovered, and (2) The rgrp bitmap. Function
+ gfs_block_set is used to set the former and set_bitmap
+ is used to set the latter. In this function we need to set both
+ because we found a "data" block that could be "meta" in the rgrp
+ bitmap. If we don't we could run into the data block again as
+ metadata when we're traversing the metadata with next_rg_meta
+ in func pass1(). If that happens, it will look at the block,
+ say "hey this isn't metadata" and mark it incorrectly as an
+ invalid metadata block and free it. Ordinarily, fsck will wait
+ until pass5 to sync (2) so that it agrees with (1). However, in
+ this case, it's better to do it upfront. The duplicate solving
+ code in pass1b.c is better at resolving metadata referencing a
+ data block than it is at resolving a data block referencing a
+ metadata block. */
+ btype = fs_get_bitmap(ip->i_sbd, block, NULL);
+ if (btype != GFS_BLKST_USED && btype != GFS_BLKST_USEDMETA) {
+ const char *allocdesc[] = {"free space", "data",
+ "free metadata", "metadata",
+ "reserved"};
+
+ if (ip->i_di.di_flags & GFS_DIF_JDATA) {
+ log_err("Block #%llu seems to be journaled data, but "
+ "is marked as %s.\n",
+ (unsigned long long)block, allocdesc[btype]);
+ if(query(sdp, "Okay to mark it as 'journaled data'? "
+ "(y/n)")) {
+ fs_set_bitmap(sdp, block, GFS_BLKST_USEDMETA);
+ log_err("The block was reassigned as "
+ "journaled data.\n");
+ } else {
+ log_err("The invalid block was ignored.\n");
+ }
+ } else {
+ log_err("Block #%llu seems to be data, but "
+ "is marked as %s.\n",
+ (unsigned long long)block, allocdesc[btype]);
+ if(query(sdp, "Okay to mark it as 'data'? "
+ "(y/n)")) {
+ fs_set_bitmap(sdp, block, GFS_BLKST_USED);
+ log_err("The block was reassigned as "
+ "journaled data.\n");
+ } else {
+ log_err("The invalid block was ignored.\n");
+ }
+ }
+ }
+ bc->data_count++;
+ return error;
+}
+
+static int remove_inode_eattr(struct fsck_inode *ip, struct block_count *bc,
+ int duplicate)
+{
+ osi_buf_t *dibh = NULL;
+
+ if (!duplicate) {
+ fs_set_bitmap(ip->i_sbd, ip->i_di.di_eattr,
+ GFS_BLKST_FREEMETA);
+ block_set(ip->i_sbd->bl, ip->i_di.di_eattr, meta_free);
+ }
+ ip->i_di.di_eattr = 0;
+ bc->ea_count = 0;
+ ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count;
+ ip->i_di.di_flags &= ~GFS_DIF_EA_INDIRECT;
+ if (get_and_read_buf(ip->i_sbd, ip->i_num.no_addr, &dibh, 0)) {
+ log_err("The bad Extended Attribute could not be fixed.\n");
+ bc->ea_count++;
+ return 1;
+ }
+ gfs_dinode_out(&ip->i_di, BH_DATA(dibh));
+ if (write_buf(ip->i_sbd, dibh, 0) < 0) {
+ stack;
+ log_crit("The bad Extended Attribute remains.\n");
+ relse_buf(ip->i_sbd, dibh);
+ return -1;
+ } else {
+ log_warn("The bad Extended Attribute was removed.\n");
}
- else {
- if(block_check(sdp->bl, block, &q)) {
+ relse_buf(ip->i_sbd, dibh);
+ return 0;
+}
+
+static int ask_remove_inode_eattr(struct fsck_inode *ip,
+ struct block_count *bc)
+{
+ log_err("Inode %lld has unrecoverable Extended Attribute "
+ "errors.\n", (unsigned long long)ip->i_di.di_num.no_addr);
+ if (query(ip->i_sbd, "Clear all Extended Attributes from the "
+ "inode? (y/n) ")) {
+ struct block_query q;
+
+ if(block_check(ip->i_sbd->bl, ip->i_di.di_eattr, &q)) {
stack;
return -1;
}
- if(q.block_type != block_free) {
- log_debug("Found duplicate block at %"
- PRIu64"\n", block);
- block_mark(sdp->bl, block, dup_block);
- bc->data_count++;
- return 1;
- }
- log_debug("Setting %"PRIu64 " to data block\n", block);
- block_set(sdp->bl, block, block_used);
- bc->data_count++;
+ if (!remove_inode_eattr(ip, bc, q.dup_block))
+ log_err("Extended attributes were removed.\n");
+ else
+ log_err("Unable to remove inode eattr pointer; "
+ "the error remains.\n");
+ } else {
+ log_err("Extended attributes were not removed.\n");
}
-
return 0;
}
@@ -218,44 +343,23 @@ static int clear_eas(struct fsck_inode *ip, struct block_count *bc,
uint64_t block, int duplicate, const char *emsg)
{
struct fsck_sb *sdp = ip->i_sbd;
- osi_buf_t *dibh = NULL;
log_err("Inode #%" PRIu64 " (0x%" PRIx64 "): %s",
ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr, emsg);
- if (block)
- log_err(" at block #%" PRIu64 " (0x%" PRIx64 ")",
- block, block);
- log_err(".\n");
- if (query(sdp, "Clear the bad EA? (y/n) ")) {
- if (block == 0)
- block = ip->i_di.di_eattr;
- block_clear(sdp->bl, block, eattr_block);
- if (!duplicate) {
- block_clear(sdp->bl, block, indir_blk);
- block_set(sdp->bl, block, block_free);
- fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
- }
- ip->i_di.di_flags &= ~GFS_DIF_EA_INDIRECT;
- if (block == ip->i_di.di_eattr)
- ip->i_di.di_eattr = 0;
- bc->ea_count = 0;
- ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count;
- if (get_and_read_buf(sdp, ip->i_num.no_addr, &dibh, 0)) {
- log_err("The bad EA could not be fixed.\n");
- bc->ea_count++;
- return 0;
+ log_err(" at block #%" PRIu64 ".\n", block);
+ if (query(sdp, "Clear the bad Extended Attribute? (y/n) ")) {
+ if (block == ip->i_di.di_eattr) {
+ remove_inode_eattr(ip, bc, duplicate);
+ log_warn("The bad Extended Attribute was removed.\n");
+ } else if (!duplicate) {
+ block_set(sdp->bl, block, meta_free);
+ fs_set_bitmap(sdp, block, GFS_BLKST_FREEMETA);
+ log_err("The bad Extended Attribute was "
+ "removed.\n");
}
- gfs_dinode_out(&ip->i_di, BH_DATA(dibh));
- if (write_buf(sdp, dibh, 0) < 0) {
- stack;
- log_crit("Bad EA reference remains.\n");
- } else {
- log_warn("Bad EA reference cleared.\n");
- }
- relse_buf(sdp, dibh);
return 1;
} else {
- log_err("The bad EA was not fixed.\n");
+ log_err("The bad Extended Attribute was not fixed.\n");
bc->ea_count++;
return 0;
}
@@ -279,7 +383,7 @@ static int check_eattr_indir(struct fsck_inode *ip, uint64_t indirect,
* in pass1c */
return 1;
}
- else if(block_check(sdp->bl, indirect, &q)) {
+ if(block_check(sdp->bl, indirect, &q)) {
stack;
return -1;
}
@@ -289,52 +393,129 @@ static int check_eattr_indir(struct fsck_inode *ip, uint64_t indirect,
handling sort it out. If it isn't, clear it but don't
count it as a duplicate. */
if(get_and_read_buf(sdp, indirect, bh, 0)) {
- log_warn("Unable to read EA indirect block #%"PRIu64".\n",
- indirect);
+ log_warn("Unable to read Extended Attribute indirect block "
+ "#%"PRIu64".\n", indirect);
block_set(sdp->bl, indirect, meta_inval);
return 1;
}
if(check_meta(*bh, GFS_METATYPE_IN)) {
if(q.block_type != block_free) {
if (!clear_eas(ip, bc, indirect, 1,
- "Bad indirect EA duplicate found"))
- block_set(sdp->bl, indirect, dup_block);
+ "Bad indirect Extended Attribute "
+ "duplicate found")) {
+ block_mark(sdp->bl, indirect, dup_block);
+ bc->ea_count++;
+ }
return 1;
}
clear_eas(ip, bc, indirect, 0,
- "EA indirect block has incorrect type");
+ "Extended Attribute indirect block has incorrect "
+ "type");
return 1;
}
if(q.block_type != block_free) { /* Duplicate? */
- log_err("Inode #%" PRIu64 "Duplicate block found at #%"PRIu64".\n",
- indirect);
- block_set(sdp->bl, indirect, dup_block);
+ log_err("Inode #%" PRIu64 "Duplicate Extended Attribute "
+ "indirect block found at #%"PRIu64".\n",
+ ip->i_di.di_num.no_addr, indirect);
+ block_mark(sdp->bl, indirect, dup_block);
bc->ea_count++;
ret = 1;
- }
- else {
+ } else {
log_debug("Setting #%" PRIu64
- ") to indirect EA block\n", indirect);
+ ") to indirect Extended Attribute block\n",
+ indirect);
block_set(sdp->bl, BH_BLKNO(*bh), indir_blk);
bc->ea_count++;
}
return ret;
}
-static int finish_eattr_indir(struct fsck_inode *ip, int indir_ok,
- void *private)
+static int finish_eattr_indir(struct fsck_inode *ip, int leaf_pointers,
+ int leaf_pointer_errors, void *private)
{
- if (indir_ok) {
- log_debug("Marking inode #%" PRIu64 ") with eattr block\n",
- ip->i_di.di_num.no_addr);
- /* Mark the inode as having an eattr in the block map
- so pass1c can check it. */
- block_mark(ip->i_sbd->bl, ip->i_di.di_num.no_addr,
- eattr_block);
+ struct block_count *bc = (struct block_count *) private;
+
+ if (leaf_pointer_errors == leaf_pointers) /* All eas were bad */
+ return ask_remove_inode_eattr(ip, bc);
+ log_debug("Marking inode #%lld with extended attribute block\n",
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ /* Mark the inode as having an eattr in the block map
+ so pass1c can check it. */
+ block_mark(ip->i_sbd->bl, ip->i_di.di_num.no_addr, eattr_block);
+ bc->ea_count++;
+ if (!leaf_pointer_errors)
return 0;
+ log_err("Inode %lld has recoverable indirect "
+ "Extended Attribute errors.\n",
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ if (query(ip->i_sbd, "Okay to fix the block count for the inode? "
+ "(y/n) ")) {
+ ip->i_di.di_blocks = 1 + bc->indir_count +
+ bc->data_count + bc->ea_count;
+ log_err("Block count fixed.\n");
+ return 1;
}
- clear_eas(ip, (struct block_count *)private, 0, 0,
- "has unrecoverable indirect EA errors");
+ log_err("Block count not fixed.\n");
+ return 1;
+}
+
+static int check_leaf_block(struct fsck_inode *ip, uint64_t block, int btype,
+ osi_buf_t **bh, void *private)
+{
+ osi_buf_t *leaf_bh = NULL;
+ struct fsck_sb *sdp = ip->i_sbd;
+ struct block_query q = {0};
+ struct block_count *bc = (struct block_count *) private;
+
+ if(block_check(sdp->bl, block, &q)) {
+ stack;
+ return -1;
+ }
+ /* Special duplicate processing: If we have an EA block, check if it
+ really is an EA. If it is, let duplicate handling sort it out.
+ If it isn't, clear it but don't count it as a duplicate. */
+ if(get_and_read_buf(sdp, block, &leaf_bh, 0)){
+ log_err("Unable to read leaf block %lld.\n", block);
+ return -1;
+ }
+ if(check_meta(leaf_bh, btype)) {
+ if(q.block_type != block_free) { /* Duplicate? */
+ clear_eas(ip, bc, block, 1,
+ "Bad Extended Attribute duplicate found");
+ } else {
+ clear_eas(ip, bc, block, 0,
+ "Extended Attribute leaf block "
+ "has incorrect type");
+ }
+ relse_buf(sdp, leaf_bh);
+ return 1;
+ }
+ if(q.block_type != block_free) { /* Duplicate? */
+ log_debug("Duplicate block found at #%lld.\n",
+ (unsigned long long)block);
+ block_mark(sdp->bl, block, dup_block);
+ bc->ea_count++;
+ relse_buf(sdp, leaf_bh);
+ return 1;
+ }
+ if (ip->i_di.di_eattr == 0) {
+ /* Can only get in here if there were unrecoverable ea
+ errors that caused clear_eas to be called. What we
+ need to do here is remove the subsequent ea blocks. */
+ clear_eas(ip, bc, block, 0,
+ "Extended Attribute block removed due to "
+ "previous errors.\n");
+ relse_buf(sdp, leaf_bh);
+ return 1;
+ }
+ log_debug("Setting block #%lld (0x%llx) to eattr block\n",
+ (unsigned long long)block, (unsigned long long)block);
+ /* Point of confusion: We've got to set the ea block itself to
+ gfs2_meta_eattr here. Elsewhere we mark the inode with
+ gfs2_eattr_block meaning it contains an eattr for pass1c. */
+ block_set(sdp->bl, block, meta_eattr);
+ bc->ea_count++;
+ *bh = leaf_bh;
return 0;
}
@@ -355,132 +536,46 @@ static int check_extended_leaf_eattr(struct fsck_inode *ip, uint64_t *data_ptr,
struct gfs_ea_header *ea_hdr_prev,
void *private)
{
- osi_buf_t *el_buf;
- struct fsck_sb *sdp = ip->i_sbd;
- struct block_query q;
uint64_t el_blk = gfs64_to_cpu(*data_ptr);
- struct block_count *bc = (struct block_count *) private;
- int ret = 0;
+ struct fsck_sb *sdp = ip->i_sbd;
+ osi_buf_t *bh = NULL;
+ int error;
if(check_range(sdp, el_blk)){
- log_err("EA extended leaf block #%"PRIu64" "
- "is out of range.\n",
- el_blk);
+ log_err("Inode #%" PRIu64 ": Extended Attribute block %" PRIu64
+ " has an extended leaf block #%" PRIu64 " that is out "
+ "of range.\n", ip->i_di.di_num.no_addr,
+ ip->i_di.di_eattr, el_blk);
block_set(sdp->bl, ip->i_di.di_eattr, bad_block);
return 1;
}
-
- if(block_check(sdp->bl, el_blk, &q)) {
- stack;
- return -1;
- }
- if(get_and_read_buf(sdp, el_blk, &el_buf, 0)){
- log_err("Unable to check extended leaf block.\n");
- block_set(sdp->bl, el_blk, meta_inval);
- return 1;
- }
-
- /* Special duplicate processing: If we have an EA block,
- check if it really is an EA. If it is, let duplicate
- handling sort it out. If it isn't, clear it but don't
- count it as a duplicate. */
- if(check_meta(el_buf, GFS_METATYPE_ED)) {
- if(q.block_type != block_free) /* Duplicate? */
- clear_eas(ip, bc, el_blk, 1,
- "has bad extended EA duplicate");
- else
- clear_eas(ip, bc, el_blk, 0,
- "EA extended leaf block has incorrect type");
- ret = 1;
- } else { /* If this looks like an EA */
- if(q.block_type != block_free) { /* Duplicate? */
- log_debug("Duplicate block found at #%" PRIu64").\n",
- el_blk);
- block_set(sdp->bl, el_blk, dup_block);
- bc->ea_count++;
- ret = 1;
- } else {
- log_debug("Setting block #%" PRIu64 ") to eattr block\n",
- el_blk);
- block_set(sdp->bl, el_blk, meta_eattr);
- bc->ea_count++;
- }
- }
-
- relse_buf(sdp, el_buf);
- return 0;
+ error = check_leaf_block(ip, el_blk, GFS_METATYPE_ED, &bh, private);
+ if (bh)
+ relse_buf(sdp, bh);
+ return error;
}
static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
uint64_t parent, osi_buf_t **bh, void *private)
{
struct fsck_sb *sdp = ip->i_sbd;
- osi_buf_t *leaf_bh;
- int ret = 0;
- struct block_query q = {0};
- struct block_count *bc = (struct block_count *) private;
/* This inode contains an eattr - it may be invalid, but the
- * eattr attributes points to a non-zero block */
- if (parent != ip->i_di.di_num.no_addr) { /* if parent isn't the inode */
- log_debug("Setting %" PRIu64 ") to eattr block\n", parent);
- block_set(sdp->bl, ip->i_num.no_addr, eattr_block);
- }
-
+ * eattr attributes points to a non-zero block.
+ * Clarification: If we're here we're checking a leaf block, and the
+ * source dinode needs to be marked as having extended attributes.
+ * That instructs pass1c to check the contents of the ea blocks. */
+ log_debug("Setting inode %" PRIu64 ") as having eattr "
+ "block\n", ip->i_num.no_addr);
+ block_mark(sdp->bl, ip->i_num.no_addr, eattr_block);
if(check_range(sdp, block)){
- log_warn("EA leaf block #%"PRIu64" in inode %"PRIu64
- " is out of range.\n",
+ log_warn("Inode #%" PRIu64 " Extended Attribute leaf block "
+ "#%" PRIu64 " is out of range.\n",
ip->i_num.no_addr, block);
block_set(sdp->bl, ip->i_di.di_eattr, bad_block);
- ret = 1;
- }
- else if(block_check(sdp->bl, block, &q)) {
- stack;
- return -1;
- }
- else {
- /* Special duplicate processing: If we have an EA block,
- check if it really is an EA. If it is, let duplicate
- handling sort it out. If it isn't, clear it but don't
- count it as a duplicate. */
- if(get_and_read_buf(sdp, block, &leaf_bh, 0)){
- log_warn("Unable to read EA leaf block #%"PRIu64".\n",
- block);
- block_set(sdp->bl, block, meta_inval);
- return 1;
- }
- if (check_meta(leaf_bh, GFS_METATYPE_EA)) {
- if (q.block_type != block_free) { /* Duplicate? */
- clear_eas(ip, bc, block, 1,
- "Bad EA duplicate found");
- } else {
- clear_eas(ip, bc, block, 0,
- "EA leaf block has incorrect type");
- }
- ret = 1;
- relse_buf(sdp, leaf_bh);
- } else { /* If this looks like an EA */
- if (q.block_type != block_free) { /* Duplicate? */
- log_debug("Duplicate block found at #%"PRIu64
- ".\n", block);
- block_set(sdp->bl, block, dup_block);
- bc->ea_count++;
- ret = 1;
- relse_buf(sdp, leaf_bh);
- } else {
- log_debug("Setting block #%" PRIu64
- " to eattr block\n", block);
- block_set(sdp->bl, BH_BLKNO(leaf_bh),
- meta_eattr);
- bc->ea_count++;
- }
- }
+ return 1;
}
-
- if (!ret)
- *bh = leaf_bh;
-
- return ret;
+ return check_leaf_block(ip, block, GFS_METATYPE_EA, bh, private);
}
static int check_eattr_entries(struct fsck_inode *ip,
@@ -508,7 +603,7 @@ static int check_eattr_entries(struct fsck_inode *ip,
}
if(ea_hdr->ea_num_ptrs){
- uint32 avail_size;
+ uint32_t avail_size;
int max_ptrs;
avail_size = sdp->sb.sb_bsize - sizeof(struct gfs_meta_header);
@@ -519,16 +614,13 @@ static int check_eattr_entries(struct fsck_inode *ip,
} else {
log_debug(" Pointers Required: %d\n"
" Pointers Reported: %d\n",
- max_ptrs,
- ea_hdr->ea_num_ptrs);
+ max_ptrs, ea_hdr->ea_num_ptrs);
}
-
}
-
return 0;
}
-int clear_metalist(struct fsck_inode *ip, uint64_t block,
+static int clear_metalist(struct fsck_inode *ip, uint64_t block,
osi_buf_t **bh, void *private)
{
struct fsck_sb *sdp = ip->i_sbd;
@@ -547,7 +639,7 @@ int clear_metalist(struct fsck_inode *ip, uint64_t block,
return 0;
}
-int clear_data(struct fsck_inode *ip, uint64_t block, void *private)
+static int clear_data(struct fsck_inode *ip, uint64_t block, void *private)
{
struct fsck_sb *sdp = ip->i_sbd;
struct block_query q = {0};
@@ -564,12 +656,12 @@ int clear_data(struct fsck_inode *ip, uint64_t block, void *private)
}
-int clear_leaf(struct fsck_inode *ip, uint64_t block,
+static int clear_leaf(struct fsck_inode *ip, uint64_t block,
osi_buf_t *bh, void *private)
{
-
- struct fsck_sb *sdp = ip->i_sbd;
struct block_query q = {0};
+ struct fsck_sb *sdp = ip->i_sbd;
+
log_crit("Clearing leaf %"PRIu64"\n", block);
if(block_check(sdp->bl, block, &q)) {
@@ -577,7 +669,7 @@ int clear_leaf(struct fsck_inode *ip, uint64_t block,
return -1;
}
if(!q.dup_block) {
- log_crit("Setting leaf invalid\n");
+ log_crit("Setting leaf #%" PRIu64 " invalid\n", block);
if(block_set(sdp->bl, block, block_free)) {
stack;
return -1;
@@ -585,7 +677,6 @@ int clear_leaf(struct fsck_inode *ip, uint64_t block,
return 0;
}
return 0;
-
}
int add_to_dir_list(struct fsck_sb *sbp, uint64_t block)
@@ -609,19 +700,17 @@ int add_to_dir_list(struct fsck_sb *sbp, uint64_t block)
return -1;
}
if(!memset(newdi, 0, sizeof(*newdi))) {
- log_crit("error while zeroing dir_info structure\n");
+ log_crit("Error while zeroing dir_info structure\n");
return -1;
}
newdi->dinode = block;
-
dinode_hash_insert(sbp->dir_hash, block, newdi);
-
return 0;
}
-int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
+static int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
{
struct block_query q = {0};
struct fsck_inode *ip;
@@ -696,8 +785,8 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
goto fail;
}
if(q.block_type != block_free) {
- log_debug("Found duplicate block at %"PRIu64"\n",
- block);
+ log_err("Found duplicate block referenced as an inode at #%"
+ PRIu64"\n", block);
if(block_mark(sdp->bl, block, dup_block)) {
stack;
goto fail;
@@ -775,7 +864,8 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
ip->i_di.di_num.no_addr, ip->i_di.di_height,
compute_height(sdp, ip->i_di.di_size));
/* once implemented, remove continue statement */
- log_warn("Marking inode invalid\n");
+ log_warn("Marking inode %lld invalid\n",
+ ip->i_di.di_num.no_addr);
if(block_set(sdp->bl, block, meta_inval)) {
stack;
goto fail;
@@ -785,17 +875,16 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
}
if (ip->i_di.di_type == (GFS_FILE_DIR &&
- (ip->i_di.di_flags & GFS_DIF_EXHASH)))
- {
+ (ip->i_di.di_flags & GFS_DIF_EXHASH))) {
if (((1 << ip->i_di.di_depth) * sizeof(uint64_t)) !=
- ip->i_di.di_size)
- {
+ ip->i_di.di_size) {
log_warn("Directory dinode #%"PRIu64" has bad depth. "
"Found %u, Expected %u\n",
ip->i_di.di_num.no_addr, ip->i_di.di_depth,
(1 >> (ip->i_di.di_size/sizeof(uint64))));
/* once implemented, remove continue statement */
- log_warn("Marking inode invalid\n");
+ log_warn("Marking inode %lld invalid\n",
+ ip->i_di.di_num.no_addr);
if(block_set(sdp->bl, block, meta_inval)) {
stack;
goto fail;
@@ -808,45 +897,38 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
pass1_fxns.private = &bc;
error = check_metatree(ip, &pass1_fxns);
- if(error < 0) {
+ if(fsck_abort || error < 0) {
return 0;
}
if(error > 0) {
- log_warn("Marking inode invalid\n");
+ log_warn("Marking inode %lld invalid\n",
+ ip->i_di.di_num.no_addr);
/* FIXME: Must set all leaves invalid as well */
check_metatree(ip, &invalidate_metatree);
block_set(ip->i_sbd->bl, ip->i_di.di_num.no_addr, meta_inval);
fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+ free(ip);
return 0;
}
- /* FIXME: is this correct? */
- if(check_inode_eattr(ip, &pass1_fxns) < 0){
- osi_buf_t *di_bh;
- ip->i_di.di_eattr = 0;
- if(get_and_read_buf(sdp, ip->i_di.di_num.no_addr, &di_bh, 0)){
- stack;
- log_crit("Bad EA reference remains.\n");
- } else {
- gfs_dinode_out(&ip->i_di, BH_DATA(di_bh));
- if(write_buf(ip->i_sbd, di_bh, 0) < 0){
- stack;
- log_crit("Bad EA reference remains.\n");
- } else {
- log_warn("Bad EA reference cleared.\n");
- }
- relse_buf(sdp, di_bh);
- }
- }
+ error = check_inode_eattr(ip, &pass1_fxns);
- if(ip->i_di.di_blocks != (1 + bc.indir_count + bc.data_count + bc.ea_count)) {
+ if (error && !(ip->i_di.di_flags & GFS_DIF_EA_INDIRECT))
+ ask_remove_inode_eattr(ip, &bc);
+
+ if (ip->i_di.di_blocks !=
+ (1 + bc.indir_count + bc.data_count + bc.ea_count)) {
osi_buf_t *di_bh;
+
log_err("Ondisk block count does not match what fsck"
" found for inode %"PRIu64"\n", ip->i_di.di_num.no_addr);
+ log_info("inode has: %lld, but fsck counts: Dinode:1 + indir:"
+ "%lld + data: %lld + ea: %lld\n",
+ ip->i_di.di_blocks, bc.indir_count, bc.data_count,
+ bc.ea_count);
if(query(sdp, "Fix ondisk block count? (y/n) ")) {
ip->i_di.di_blocks = 1 + bc.indir_count +
- bc.data_count +
- bc.ea_count;
+ bc.data_count + bc.ea_count;
if(get_and_read_buf(sdp, ip->i_di.di_num.no_addr,
&di_bh, 0)){
stack;
@@ -878,16 +960,20 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
}
-int scan_meta(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
+static int scan_meta(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
{
-
if (check_meta(bh, 0)) {
- log_debug("Found invalid metadata at %"PRIu64"\n", block);
+ log_err("Found invalid metadata block at %"PRIu64"\n", block);
if(block_set(sdp->bl, block, meta_inval)) {
stack;
return -1;
}
- fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+ if(query(sdp, "Okay to free the invalid block? (y/n)")) {
+ fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+ log_err("The invalid block was freed.\n");
+ } else {
+ log_err("The invalid block was ignored.\n");
+ }
return 0;
}
@@ -899,19 +985,12 @@ int scan_meta(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
return -1;
}
}
- else if (!check_type(bh, GFS_METATYPE_NONE)) {
- if(block_set(sdp->bl, block, meta_free)) {
- stack;
- return -1;
- }
- } else {
- log_debug("Metadata block %"PRIu64
- " not an inode or free metadata\n",
- block);
- }
- /* Ignore everything else - they should be hit by the
- * handle_di step */
-
+ /* Ignore everything else - they should be hit by the handle_di step.
+ * Don't check NONE either, because check_meta passes everything if
+ * GFS_METATYPE_NONE is specified.
+ * Hopefully, other metadata types such as indirect blocks will be
+ * handled when the inode itself is processed, and if it's not, it
+ * should be caught in pass5. */
return 0;
}
@@ -990,15 +1069,12 @@ int pass1(struct fsck_sb *sbp)
offset = sizeof(struct gfs_rgrp);
blk_count = 1;
-
first = 1;
while (1) {
-
/* "block" is relative to the entire file system */
if(next_rg_meta_free(rgd, &block, first, &mfree))
break;
-
warm_fuzzy_stuff(block);
if (fsck_abort) /* if asked to abort */
return 0;
diff --git a/gfs/gfs_fsck/pass1b.c b/gfs/gfs_fsck/pass1b.c
index 9f32aa6..301f7d0 100644
--- a/gfs/gfs_fsck/pass1b.c
+++ b/gfs/gfs_fsck/pass1b.c
@@ -87,7 +87,7 @@ static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
inc_if_found(block, 0, private);
if(get_and_read_buf(sbp, block, &leaf_bh, 0)){
- log_warn("Unable to read EA leaf block #%"PRIu64".\n",
+ log_err("Unable to read EA leaf block #%"PRIu64".\n",
block);
return 1;
}
@@ -166,11 +166,11 @@ static int clear_dup_metalist(struct fsck_inode *ip, uint64_t block,
if(dh->ref_count == 1)
return 1;
if(block == dh->b->block_no) {
- log_err("Found dup in inode \"%s\" (block #%"PRIu64
- ") with block #%"PRIu64"\n",
+ log_err("Found duplicate reference in inode \"%s\" at block #%"
+ PRIu64 " to block #%"PRIu64"\n",
dh->id->name ? dh->id->name : "unknown name",
ip->i_di.di_num.no_addr, block);
- log_err("inode %s is in directory %"PRIu64"\n",
+ log_err("Inode %s is in directory %"PRIu64"\n",
dh->id->name ? dh->id->name : "",
dh->id->parent);
inode_hash_remove(ip->i_sbd->inode_hash, ip->i_di.di_num.no_addr);
@@ -182,26 +182,7 @@ static int clear_dup_metalist(struct fsck_inode *ip, uint64_t block,
}
static int clear_dup_data(struct fsck_inode *ip, uint64_t block, void *private)
{
- struct dup_handler *dh = (struct dup_handler *) private;
-
- if(dh->ref_count == 1) {
- return 1;
- }
- if(block == dh->b->block_no) {
- log_err("Found dup in inode \"%s\" (block #%"PRIu64
- ") with block #%"PRIu64"\n",
- dh->id->name ? dh->id->name : "unknown name",
- ip->i_di.di_num.no_addr, block);
- log_err("inode %s is in directory %"PRIu64"\n",
- dh->id->name ? dh->id->name : "",
- dh->id->parent);
- inode_hash_remove(ip->i_sbd->inode_hash, ip->i_di.di_num.no_addr);
- /* Setting the block to invalid means the inode is
- * cleared in pass2 */
- block_set(ip->i_sbd->bl, ip->i_di.di_num.no_addr, meta_inval);
- }
-
- return 0;
+ return clear_dup_metalist(ip, block, NULL, private);
}
static int clear_dup_eattr_indir(struct fsck_inode *ip, uint64_t block,
uint64_t parent, osi_buf_t **bh,
@@ -210,6 +191,7 @@ static int clear_dup_eattr_indir(struct fsck_inode *ip, uint64_t block,
struct dup_handler *dh = (struct dup_handler *) private;
/* Can't use fxns from eattr.c since we need to check the ref
* count */
+ *bh = NULL;
if(dh->ref_count == 1)
return 1;
if(block == dh->b->block_no) {
@@ -286,8 +268,6 @@ static int clear_eattr_entry (struct fsck_inode *ip,
max_ptrs,
ea_hdr->ea_num_ptrs);
}
-
-
}
return 0;
}
@@ -317,7 +297,7 @@ static int clear_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_data_ptr,
}
/* Finds all references to duplicate blocks in the metadata */
-int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
+static int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
{
struct fsck_inode *ip;
struct fxn_info myfi = {b->block_no, 0, 1};
@@ -338,15 +318,20 @@ int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
stack;
return -1;
}
- log_info("Checking inode %"PRIu64"'s metatree for references to block %"PRIu64"\n",
- inode, b->block_no);
+ log_debug("Checking inode %"PRIu64"'s metatree for references to "
+ "block %"PRIu64"\n", inode, b->block_no);
if(check_metatree(ip, &find_refs)) {
stack;
free_inode(&ip);
return -1;
}
- log_info("Done checking metatree\n");
-
+ log_debug("Done checking metatree\n");
+ /* Check for ea references in the inode */
+ if(check_inode_eattr(ip, &find_refs) < 0){
+ stack;
+ free_inode(&ip);
+ return -1;
+ }
if (myfi.found) {
if(!(id = malloc(sizeof(*id)))) {
log_crit("Unable to allocate inode_with_dups structure\n");
@@ -363,15 +348,13 @@ int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
id->block_no = inode;
id->ea_only = myfi.ea_only;
osi_list_add_prev(&id->list, &b->ref_inode_list);
- free_inode(&ip);
- return 0;
}
free_inode(&ip);
return 0;
}
/* Finds all blocks marked in the duplicate block bitmap */
-int find_dup_blocks(struct fsck_sb *sbp)
+static int find_dup_blocks(struct fsck_sb *sbp)
{
uint64_t block_no = 0;
struct blocks *b;
@@ -394,9 +377,7 @@ int find_dup_blocks(struct fsck_sb *sbp)
return 0;
}
-
-
-int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
+static int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
{
osi_list_t *tmp;
struct inode_with_dups *id;
@@ -419,16 +400,65 @@ int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
dh.ref_inode_count++;
dh.ref_count += id->dup_count;
}
- log_notice("Block %"PRIu64" has %d inodes referencing it for"
- "a total of %d duplicate references\n",
+ /* A single reference to the block implies a possible situation where
+ a data pointer points to a metadata block. In other words, the
+ duplicate reference in the file system is (1) Metadata block X and
+ (2) A dinode reference such as a data pointer pointing to block X.
+ We can't really check for that in pass1 because user data might
+ just _look_ like metadata by coincidence, and at the time we're
+ checking, we might not have processed the referenced block.
+ Here in pass1b we're sure. */
+ if (dh.ref_count == 1) {
+ osi_buf_t *bh;
+ uint32_t cmagic;
+
+ get_and_read_buf(sbp, b->block_no, &bh, 0);
+ cmagic = ((struct gfs_meta_header *)(bh->b_data))->mh_magic;
+ relse_buf(sbp, bh);
+ if (be32_to_cpu(cmagic) == GFS_MAGIC) {
+ tmp = b->ref_inode_list.next;
+ id = osi_list_entry(tmp, struct inode_with_dups, list);
+ log_warn("Inode %s (%lld) has a reference to "
+ "data block %"PRIu64", but "
+ "the block is really metadata.\n",
+ id->name, id->block_no, b->block_no);
+ if (query(sbp, "Clear the inode? (y/n) ")) {
+ log_warn("Clearing inode %lld...\n",
+ id->block_no);
+ load_inode(sbp, id->block_no, &ip);
+ inode_hash_remove(ip->i_sbd->inode_hash,
+ ip->i_di.di_num.no_addr);
+ /* Setting the block to invalid means the inode
+ is cleared in pass2 */
+ block_set(sbp->bl, ip->i_di.di_num.no_addr,
+ meta_inval);
+ free_inode(&ip);
+ } else {
+ log_warn("The bad inode was not cleared.");
+ }
+ return 0;
+ }
+ }
+
+ log_notice("Block %"PRIu64" has %d inodes referencing it for "
+ "a total of %d duplicate references.\n",
b->block_no, dh.ref_inode_count, dh.ref_count);
osi_list_foreach(tmp, &b->ref_inode_list) {
id = osi_list_entry(tmp, struct inode_with_dups, list);
- log_warn("Inode %s has %d reference(s) to block %"PRIu64
- "\n", id->name, id->dup_count, b->block_no);
- /* FIXME: User input */
- log_warn("Clearing...\n");
+ log_warn("Inode %s (%lld) has %d reference(s) to block %"PRIu64
+ "\n", id->name, id->block_no, id->dup_count,
+ b->block_no);
+ }
+ osi_list_foreach(tmp, &b->ref_inode_list) {
+ id = osi_list_entry(tmp, struct inode_with_dups, list);
+ if (!query(sbp, "Okay to clear inode %lld? (y/n) ",
+ id->block_no)) {
+ log_warn("The inode %lld was not cleared...\n",
+ id->block_no);
+ continue;
+ }
+ log_warn("Clearing inode %lld...\n", id->block_no);
load_inode(sbp, id->block_no, &ip);
dh.b = b;
dh.id = id;
@@ -439,6 +469,7 @@ int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
if(!id->ea_only)
check_metatree(ip, &clear_dup_fxns);
+ block_set(sbp->bl, id->block_no, meta_inval);
free_inode(&ip);
dh.ref_inode_count--;
if(dh.ref_inode_count == 1)
@@ -460,11 +491,12 @@ int pass1b(struct fsck_sb *sbp)
struct blocks *b;
uint64_t i;
struct block_query q;
- osi_list_t *tmp;
+ osi_list_t *tmp = NULL, *x;
struct metawalk_fxns find_dirents = {0};
- find_dirents.check_dentry = &find_dentry;
int rc = 0;
+ find_dirents.check_dentry = &find_dentry;
+
osi_list_init(&sbp->dup_list);
/* Shove all blocks marked as duplicated into a list */
log_info("Looking for duplicate blocks...\n");
@@ -497,7 +529,7 @@ int pass1b(struct fsck_sb *sbp)
(q.block_type == inode_chr) ||
(q.block_type == inode_fifo) ||
(q.block_type == inode_sock)) {
- osi_list_foreach(tmp, &sbp->dup_list) {
+ osi_list_foreach_safe(tmp, &sbp->dup_list, x) {
b = osi_list_entry(tmp, struct blocks, list);
if(find_block_ref(sbp, i, b)) {
stack;
diff --git a/gfs/gfs_fsck/pass1c.c b/gfs/gfs_fsck/pass1c.c
index e65b862..d07d7cf 100644
--- a/gfs/gfs_fsck/pass1c.c
+++ b/gfs/gfs_fsck/pass1c.c
@@ -1,5 +1,6 @@
#include "fsck.h"
#include "fsck_incore.h"
+#include "fs_inode.h"
#include "bio.h"
#include "inode.h"
#include "util.h"
@@ -10,14 +11,12 @@ static int remove_eattr_entry(struct fsck_sb *sdp, osi_buf_t *leaf_bh,
struct gfs_ea_header *curr,
struct gfs_ea_header *prev)
{
- log_warn("Removing EA located in block #%"PRIu64".\n",
- BH_BLKNO(leaf_bh));
- if(!prev){
+ if(!prev)
curr->ea_type = GFS_EATYPE_UNUSED;
- } else {
- prev->ea_rec_len =
- cpu_to_gfs32(gfs32_to_cpu(curr->ea_rec_len) +
- gfs32_to_cpu(prev->ea_rec_len));
+ else {
+ uint32_t tmp32 = gfs32_to_cpu(curr->ea_rec_len) +
+ gfs32_to_cpu(prev->ea_rec_len);
+ prev->ea_rec_len = cpu_to_gfs32(tmp32);
if (curr->ea_flags & GFS_EAFLAG_LAST)
prev->ea_flags |= GFS_EAFLAG_LAST;
}
@@ -26,84 +25,120 @@ static int remove_eattr_entry(struct fsck_sb *sdp, osi_buf_t *leaf_bh,
log_err("EA removal failed.\n");
return -1;
}
+ log_err("Bad Extended Attribute at block #%"PRIu64" removed.\n",
+ BH_BLKNO(leaf_bh));
return 0;
}
-int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
- uint64_t parent, osi_buf_t **bh,
- void *private)
+static int ask_remove_eattr_entry(struct fsck_sb *sdp, osi_buf_t *leaf_bh,
+ struct gfs_ea_header *curr,
+ struct gfs_ea_header *prev,
+ int fix_curr, int fix_curr_len)
+{
+ if (query(sdp, "Remove the bad Extended Attribute? (y/n) ")) {
+ if (fix_curr)
+ curr->ea_flags |= GFS_EAFLAG_LAST;
+ if (fix_curr_len) {
+ uint32_t max_size = sdp->sb.sb_bsize;
+ uint32_t offset = (uint32_t)(((unsigned long)curr) -
+ ((unsigned long)leaf_bh->b_data));
+ curr->ea_rec_len = cpu_to_be32(max_size - offset);
+ }
+ if (remove_eattr_entry(sdp, leaf_bh, curr, prev)) {
+ stack;
+ return -1;
+ }
+ } else {
+ log_err("Bad Extended Attribute not removed.\n");
+ }
+ return 1;
+}
+
+static int ask_remove_eattr(struct fsck_inode *ip)
+{
+ if (query(ip->i_sbd, "Remove the bad Extended Attribute? (y/n) ")) {
+ ip->i_di.di_eattr = 0;
+ if (fs_copyout_dinode(ip))
+ log_err("Bad Extended Attribute could not be "
+ "removed.\n");
+ else
+ log_err("Bad Extended Attribute removed.\n");
+ } else
+ log_err("Bad Extended Attribute not removed.\n");
+ return 1;
+}
+
+static int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
+ uint64_t parent, osi_buf_t **bh,
+ void *private)
{
int *update = (int *) private;
struct fsck_sb *sbp = ip->i_sbd;
struct block_query q;
- osi_buf_t *indir_bh;
+ osi_buf_t *indir_bh = NULL;
+ *update = 0;
if(check_range(sbp, block)) {
- log_err("Extended attributes indirect block out of range...removing\n");
- ip->i_di.di_eattr = 0;
- *update = 1;
- return 1;
+ log_err("Extended attributes indirect block #%llu"
+ " for inode #%llu is out of range.\n",
+ (unsigned long long)block,
+ (unsigned long long)ip->i_num.no_addr);
+ return ask_remove_eattr(ip);
}
else if (block_check(sbp->bl, block, &q)) {
stack;
return -1;
}
else if(q.block_type != indir_blk) {
- log_err("Extended attributes indirect block invalid...removing\n");
- ip->i_di.di_eattr = 0;
- *update = 1;
- return 1;
+ log_err("Extended attributes indirect block #%llu"
+ " for inode #%llu is invalid.\n",
+ (unsigned long long)block,
+ (unsigned long long)ip->i_num.no_addr);
+ return ask_remove_eattr(ip);
}
else if(get_and_read_buf(sbp, block, &indir_bh, 0)){
- log_warn("Unable to read EA leaf block #%"PRIu64".\n",
- block);
- ip->i_di.di_eattr = 0;
- *update = 1;
- return 1;
+ log_warn("Unable to read Extended Attribute leaf block "
+ "#%"PRIu64".\n", block);
+ return ask_remove_eattr(ip);
}
*bh = indir_bh;
return 0;
}
-int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
- uint64_t parent, osi_buf_t **bh, void *private)
+
+static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
+ uint64_t parent, osi_buf_t **bh, void *private)
{
- int *update = (int *) private;
struct fsck_sb *sbp = ip->i_sbd;
struct block_query q;
osi_buf_t *leaf_bh;
if(check_range(sbp, block)) {
- log_err("Extended attributes block out of range...removing\n");
- ip->i_di.di_eattr = 0;
- *update = 1;
- return 1;
+ log_err("Extended attributes leaf block #%"PRIu64
+ " for inode #%" PRIu64 " is out of range.\n",
+ block, ip->i_num.no_addr);
+ return ask_remove_eattr(ip);
}
else if (block_check(sbp->bl, block, &q)) {
stack;
return -1;
}
else if(q.block_type != meta_eattr) {
- log_err("Extended attributes block invalid...removing\n");
- ip->i_di.di_eattr = 0;
- *update = 1;
- return 1;
+ log_err("Extended attributes leaf block #%"PRIu64
+ " for inode #%" PRIu64 " is invalid.\n",
+ block, ip->i_num.no_addr);
+ return ask_remove_eattr(ip);
}
else if(get_and_read_buf(sbp, block, &leaf_bh, 0)){
- log_warn("Unable to read EA leaf block #%"PRIu64".\n",
- block);
- ip->i_di.di_eattr = 0;
- *update = 1;
- return 1;
+ log_warn("Unable to read Extended attributes leaf block "
+ "#%"PRIu64".\n", block);
+ return ask_remove_eattr(ip);
}
*bh = leaf_bh;
-
return 0;
-
}
-
static int check_eattr_entry(struct fsck_inode *ip,
osi_buf_t *leaf_bh,
struct gfs_ea_header *ea_hdr,
@@ -117,41 +152,24 @@ static int check_eattr_entry(struct fsck_inode *ip,
uint32_t max_size = sdp->sb.sb_bsize;
if(!ea_hdr->ea_rec_len){
log_err("EA has rec length == 0\n");
- ea_hdr->ea_flags |= GFS_EAFLAG_LAST;
- ea_hdr->ea_rec_len = cpu_to_gfs32(max_size - offset);
- if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
- stack;
- return -1;
- }
- return 1;
+ return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+ ea_hdr_prev, 1, 1);
}
if(offset + gfs32_to_cpu(ea_hdr->ea_rec_len) > max_size){
log_err("EA rec length too long\n");
- ea_hdr->ea_flags |= GFS_EAFLAG_LAST;
- ea_hdr->ea_rec_len = cpu_to_gfs32(max_size - offset);
- if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
- stack;
- return -1;
- }
- return 1;
+ return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+ ea_hdr_prev, 1, 1);
}
if(offset + gfs32_to_cpu(ea_hdr->ea_rec_len) == max_size &&
(ea_hdr->ea_flags & GFS_EAFLAG_LAST) == 0){
log_err("last EA has no last entry flag\n");
- ea_hdr->ea_flags |= GFS_EAFLAG_LAST;
- if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
- stack;
- return -1;
- }
- return 1;
+ return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+ ea_hdr_prev, 0, 0);
}
if(!ea_hdr->ea_name_len){
log_err("EA has name length == 0\n");
- if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
- stack;
- return -1;
- }
- return 1;
+ return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+ ea_hdr_prev, 0, 0);
}
memset(ea_name, 0, sizeof(ea_name));
@@ -162,11 +180,8 @@ static int check_eattr_entry(struct fsck_inode *ip,
((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
log_err("EA (%s) type is invalid (%d > %d).\n",
ea_name, ea_hdr->ea_type, GFS_EATYPE_LAST);
- if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
- stack;
- return -1;
- }
- return 1;
+ return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+ ea_hdr_prev, 0, 0);
}
if(ea_hdr->ea_num_ptrs){
@@ -181,24 +196,18 @@ static int check_eattr_entry(struct fsck_inode *ip,
log_err(" Required: %d\n"
" Reported: %d\n",
max_ptrs, ea_hdr->ea_num_ptrs);
- if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
- stack;
- return -1;
- }
- return 1;
+ return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+ ea_hdr_prev, 0, 0);
} else {
log_debug(" Pointers Required: %d\n"
" Pointers Reported: %d\n",
- max_ptrs,
- ea_hdr->ea_num_ptrs);
+ max_ptrs, ea_hdr->ea_num_ptrs);
}
-
}
-
return 0;
}
-int check_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_ptr,
+static int check_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_ptr,
osi_buf_t *leaf_bh,
struct gfs_ea_header *ea_hdr,
struct gfs_ea_header *ea_hdr_prev,
@@ -206,6 +215,7 @@ int check_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_ptr,
{
struct block_query q;
struct fsck_sb *sbp = ip->i_sbd;
+
if(block_check(sbp->bl, gfs64_to_cpu(*ea_ptr), &q)) {
stack;
return -1;
@@ -252,6 +262,7 @@ int pass1c(struct fsck_sb *sbp)
return -1;
}
+ block_unmark(sbp->bl, block_no, eattr_block);
log_debug("Found eattr at %"PRIu64"\n", ip->i_di.di_eattr);
/* FIXME: Handle walking the eattr here */
error = check_inode_eattr(ip, &pass1c_fxns);
diff --git a/gfs/gfs_fsck/pass2.c b/gfs/gfs_fsck/pass2.c
index 450f26f..9c4a298 100644
--- a/gfs/gfs_fsck/pass2.c
+++ b/gfs/gfs_fsck/pass2.c
@@ -25,7 +25,7 @@ struct dir_status {
/* Set children's parent inode in dir_info structure - ext2 does not set
* dotdot inode here, but instead in pass3 - should we? */
-int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
+static int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
uint64_t parentblock)
{
struct dir_info *di;
@@ -51,7 +51,7 @@ int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
}
/* Set's the child's '..' directory inode number in dir_info structure */
-int set_dotdot_dir(struct fsck_sb *sbp, uint64_t childblock,
+static int set_dotdot_dir(struct fsck_sb *sbp, uint64_t childblock,
uint64_t parentblock)
{
struct dir_info *di;
@@ -84,12 +84,22 @@ static int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
uint64_t parent, osi_buf_t **bh, void *private)
{
+ osi_buf_t *indir_bh;
+
+ if(get_and_read_buf(ip->i_sbd, block, &indir_bh, 0)){
+ log_warn("Unable to read EA indir block #%"PRIu64".\n",
+ block);
+ block_set(ip->i_sbd->bl, block, meta_inval);
+ return 1;
+ }
+ *bh = indir_bh;
+
return 0;
}
static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
uint64_t parent, osi_buf_t **bh, void *private)
{
- osi_buf_t *leaf_bh;
+ osi_buf_t *leaf_bh = NULL;
if(get_and_read_buf(ip->i_sbd, block, &leaf_bh, 0)){
log_warn("Unable to read EA leaf block #%"PRIu64".\n",
@@ -97,12 +107,13 @@ static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
block_set(ip->i_sbd->bl, block, meta_inval);
return 1;
}
-
+ *bh = leaf_bh;
return 0;
}
-static int check_file_type(uint16_t de_type, uint8_t block_type) {
+static int check_file_type(uint16_t de_type, uint8_t block_type)
+{
switch(block_type) {
case inode_dir:
if(de_type != GFS_FILE_DIR)
@@ -140,9 +151,17 @@ static int check_file_type(uint16_t de_type, uint8_t block_type) {
return 0;
}
+struct metawalk_fxns pass2_fxns_delete = {
+ .private = NULL,
+ .check_metalist = delete_metadata,
+ .check_data = delete_data,
+ .check_eattr_indir = delete_eattr_indir,
+ .check_eattr_leaf = delete_eattr_leaf,
+};
+
/* FIXME: should maybe refactor this a bit - but need to deal with
* FIXMEs internally first */
-int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
+static int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
struct gfs_dirent *prev_de,
osi_buf_t *bh, char *filename, int *update,
uint16_t *count, void *priv)
@@ -196,11 +215,10 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
/* FIXME: This should probably go to the top of the fxn, and
* references to filename should be replaced with tmp_name */
memset(tmp_name, 0, MAX_FILENAME);
- if(de->de_name_len < MAX_FILENAME){
+ if(de->de_name_len < MAX_FILENAME)
strncpy(tmp_name, filename, de->de_name_len);
- } else {
+ else
strncpy(tmp_name, filename, MAX_FILENAME - 1);
- }
if(check_range(ip->i_sbd, entryblock)) {
log_err("Block # referenced by directory entry %s is out of range\n",
@@ -210,10 +228,10 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
if(dirent_del(ip, bh, prev_de, dent))
log_err("Error encountered while removing bad "
"directory entry. Skipping.\n");
+ *update = 1;
return 1;
} else {
log_err("Directory entry to out of range block remains\n");
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
@@ -227,27 +245,26 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
if(q.bad_block) {
/* This entry's inode has bad blocks in it */
- /* FIXME: user interface */
- /* FIXME: do i want to kill the inode here? */
/* Handle bad blocks */
- log_err("Found a bad directory entry: %s\n", filename);
-
- if(query(sbp, "Clear entry to inode containing bad blocks? (y/n)")) {
-
- load_inode(sbp, de->de_inum.no_addr, &entry_ip);
- check_inode_eattr(entry_ip, &clear_eattrs);
- free_inode(&entry_ip);
-
- /* FIXME: make sure all blocks referenced by
- * this inode are cleared in the bitmap */
-
+ log_err("Found a bad directory entry: %s at block %lld.\n",
+ filename, de->de_inum.no_addr);
+
+ if(query(sbp, "Delete the inode containing bad blocks? "
+ "(y/n)")) {
+ if (!load_inode(sbp, de->de_inum.no_addr, &entry_ip)) {
+ check_inode_eattr(entry_ip,
+ &pass2_fxns_delete);
+ check_metatree(entry_ip, &pass2_fxns_delete);
+ free_inode(&entry_ip);
+ }
dirent_del(ip, bh, prev_de, dent);
-
- block_set(sbp->bl, de->de_inum.no_addr, meta_inval);
+ block_set(sbp->bl, de->de_inum.no_addr, meta_free);
+ *update = 1;
+ log_warn("The inode containing bad blocks was "
+ "deleted.\n");
return 1;
} else {
log_warn("Entry to inode containing bad blocks remains\n");
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
@@ -258,32 +275,53 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
q.block_type != inode_lnk && q.block_type != inode_blk &&
q.block_type != inode_chr && q.block_type != inode_fifo &&
q.block_type != inode_sock) {
- log_err("Found directory entry '%s' in block %"
- PRIu64" to something"
- " not a file or directory!\n", tmp_name,
- ip->i_num.no_addr);
- log_debug("block #%"PRIu64" in %"PRIu64"\n",
- de->de_inum.no_addr, ip->i_num.no_addr);
+ log_err("Directory entry '%s' for block %" PRIu64
+ " in dir inode %" PRIu64 " block type %d: %s.\n",
+ tmp_name, de->de_inum.no_addr, ip->i_num.no_addr,
+ q.block_type, q.block_type == meta_inval ?
+ "previously marked invalid" : "is not an inode");
- if(query(sbp, "Clear directory entry to non-inode block? (y/n) ")) {
- /* FIXME: make sure all blocks referenced by
- * this inode are cleared in the bitmap */
+ if(query(sbp, "Clear directory entry to non-inode block? "
+ "(y/n) ")) {
+ osi_buf_t *bhi;
- if(dirent_del(ip, bh, prev_de, dent))
+ if(dirent_del(ip, bh, prev_de, dent)) {
log_err("Error encountered while removing bad "
"directory entry. Skipping.\n");
+ return -1;
+ }
+ *update = 1;
log_warn("Directory entry '%s' cleared\n", tmp_name);
+
+ /* If it was previously marked invalid (i.e. known
+ to be bad, not just a free block, etc.) then
+ delete any metadata it holds. If not, return. */
+ if (q.block_type != meta_inval)
+ return 1;
+
+ /* Now try to clear the dinode, if it is an dinode */
+ get_and_read_buf(sbp, de->de_inum.no_addr, &bhi, 0);
+ error = check_meta(bhi, GFS_METATYPE_DI);
+ relse_buf(sbp, bhi);
+ if (error)
+ return 1; /* not a dinode: nothing to delete */
+
+ if (!load_inode(sbp, de->de_inum.no_addr, &entry_ip)) {
+ check_inode_eattr(entry_ip,
+ &pass2_fxns_delete);
+ check_metatree(entry_ip, &pass2_fxns_delete);
+ free_inode(&entry_ip);
+ }
+ block_set(sbp->bl, de->de_inum.no_addr, block_free);
return 1;
} else {
log_err("Directory entry to non-inode block remains\n");
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
-
error = check_file_type(de->de_type, q.block_type);
if(error < 0) {
stack;
@@ -301,17 +339,17 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
if(dirent_del(ip, bh, prev_de, dent))
log_err("Error encountered while removing bad "
"directory entry. Skipping.\n");
+ *update = 1;
+ log_err("Stale directory entry deleted\n");
return 1;
} else {
log_err("Stale directory entry remains\n");
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
}
}
-
if(!strcmp(".", tmp_name)) {
log_debug("Found . dentry\n");
@@ -324,6 +362,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
free_inode(&entry_ip);
dirent_del(ip, bh, prev_de, dent);
+ *update = 1;
return 1;
} else {
log_err("Duplicate '.' entry remains\n");
@@ -331,7 +370,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
* and check the rest of the '.'
* entry? */
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
@@ -355,6 +393,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
free_inode(&entry_ip);
dirent_del(ip, bh, prev_de, dent);
+ *update = 1;
return 1;
} else {
@@ -362,7 +401,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
/* Not setting ds->dotdir here since
* this '.' entry is invalid */
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
@@ -371,7 +409,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
ds->dotdir = 1;
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
(*count)++;
ds->entry_count++;
@@ -396,7 +433,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
* and check the rest of the '..'
* entry? */
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
@@ -417,7 +453,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
} else {
log_err("Bad '..' directory entry remains\n");
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
@@ -437,7 +472,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
ds->dotdotdir = 1;
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
+ *update = (sbp->opts->no ? 0 : 1);
(*count)++;
ds->entry_count++;
return 0;
@@ -448,7 +483,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
if(q.block_type != inode_dir) {
log_debug("Found non-dir inode dentry\n");
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
+ *update = (sbp->opts->no ? 0 : 1);
(*count)++;
ds->entry_count++;
return 0;
@@ -468,7 +503,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
return 1;
} else {
log_err("Hard link to directory remains\n");
- *update = 1;
(*count)++;
ds->entry_count++;
return 0;
@@ -479,7 +513,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
return -1;
}
increment_link(sbp, de->de_inum.no_addr);
- *update = 1;
+ *update = (sbp->opts->no ? 0 : 1);
(*count)++;
ds->entry_count++;
/* End of checks */
@@ -501,7 +535,7 @@ struct metawalk_fxns pass2_fxns = {
-int build_rooti(struct fsck_sb *sbp)
+static int build_rooti(struct fsck_sb *sbp)
{
struct fsck_inode *ip;
osi_buf_t *bh;
@@ -553,7 +587,7 @@ int build_rooti(struct fsck_sb *sbp)
}
/* Check root inode and verify it's in the bitmap */
-int check_root_dir(struct fsck_sb *sbp)
+static int check_root_dir(struct fsck_sb *sbp)
{
uint64_t rootblock;
struct dir_status ds = {0};
@@ -647,30 +681,35 @@ int check_root_dir(struct fsck_sb *sbp)
if(!ds.dotdir) {
log_err("No '.' entry found\n");
- sprintf(tmp_name, ".");
- filename.len = strlen(tmp_name); /* no trailing NULL */
- if(!(filename.name = malloc(sizeof(char) * filename.len))) {
- log_err("Unable to allocate name string\n");
- stack;
- return -1;
- }
- if(!(memset(filename.name, 0, sizeof(char) * filename.len))) {
- log_err("Unable to zero name string\n");
- stack;
- return -1;
- }
- memcpy(filename.name, tmp_name, filename.len);
- log_warn("Adding '.' entry\n");
- if(fs_dir_add(ip, &filename, &(ip->i_num),
- ip->i_di.di_type)){
- log_err("Failed to link \".\" entry to directory.\n");
- return -1;
- }
-
- increment_link(ip->i_sbd, ip->i_num.no_addr);
- ds.entry_count++;
- free(filename.name);
- update = 1;
+ if (query(sbp, "Is it okay to add '.' entry? (y/n) ")) {
+ sprintf(tmp_name, ".");
+ filename.len = strlen(tmp_name); /* no trailing NULL */
+ if(!(filename.name =
+ malloc(sizeof(char) * filename.len))) {
+ log_err("Unable to allocate name string\n");
+ stack;
+ return -1;
+ }
+ if(!(memset(filename.name, 0, sizeof(char) *
+ filename.len))) {
+ log_err("Unable to zero name string\n");
+ stack;
+ return -1;
+ }
+ memcpy(filename.name, tmp_name, filename.len);
+ log_warn("Adding '.' entry\n");
+ if(fs_dir_add(ip, &filename, &(ip->i_num),
+ ip->i_di.di_type)){
+ log_err("Failed to link \".\" entry to "
+ "directory.\n");
+ return -1;
+ }
+ increment_link(ip->i_sbd, ip->i_num.no_addr);
+ ds.entry_count++;
+ free(filename.name);
+ update = 1;
+ } else
+ log_err("The directory was not fixed.\n");
}
free_inode(&ip);
if(get_and_read_buf(sbp, rootblock, &bh, 0)){
@@ -730,16 +769,18 @@ int pass2(struct fsck_sb *sbp, struct options *opts)
osi_buf_t b, *bh = &b;
osi_filename_t filename;
char tmp_name[256];
+ int error = 0;
+ int need_update = 0;
+
if(check_root_dir(sbp)) {
stack;
return -1;
}
- int error = 0;
log_info("Checking directory inodes.\n");
/* Grab each directory inode, and run checks on it */
for(i = 0; i < sbp->last_fs_block; i++) {
-
+ need_update = 0;
warm_fuzzy_stuff(i);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return 0;
@@ -823,63 +864,63 @@ int pass2(struct fsck_sb *sbp, struct options *opts)
relse_buf(sbp, bh);
return -1;
}
- /* FIXME: Should not have to do this here - fs_dir_add reads
- * the buffer too though, and commits the change to disk, so I
- * have to reread the buffer after calling it if I'm going to
- * make more changes */
- relse_buf(sbp, bh);
if(!ds.dotdir) {
- log_err("No '.' entry found\n");
- sprintf(tmp_name, ".");
- filename.len = strlen(tmp_name); /* no trailing NULL */
- if(!(filename.name = malloc(sizeof(char) * filename.len))) {
- log_err("Unable to allocate name string\n");
- stack;
- return -1;
- }
- if(!memset(filename.name, 0, sizeof(char) * filename.len)) {
- log_err("Unable to zero name string\n");
- stack;
- return -1;
- }
- memcpy(filename.name, tmp_name, filename.len);
+ log_err("No '.' entry found for directory inode at "
+ "block %" PRIu64 "\n", i);
+ if (query(sbp,
+ "Is it okay to add '.' entry? (y/n) ")) {
+ sprintf(tmp_name, ".");
+ filename.len = strlen(tmp_name); /* no trailing
+ NULL */
+ if(!(filename.name = malloc(sizeof(char) *
+ filename.len))) {
+ log_err("Unable to allocate name\n");
+ stack;
+ return -1;
+ }
+ if(!memset(filename.name, 0, sizeof(char) *
+ filename.len)) {
+ log_err("Unable to zero name\n");
+ stack;
+ return -1;
+ }
+ memcpy(filename.name, tmp_name, filename.len);
- if(fs_dir_add(ip, &filename, &(ip->i_num),
- ip->i_di.di_type)){
- log_err("Failed to link \".\" entry to directory.\n");
- return -1;
+ if(fs_dir_add(ip, &filename, &(ip->i_num),
+ ip->i_di.di_type)){
+ log_err("Failed to link \".\" entry "
+ "to directory.\n");
+ return -1;
+ }
+ increment_link(ip->i_sbd, ip->i_num.no_addr);
+ ds.entry_count++;
+ free(filename.name);
+ log_err("The directory was fixed.\n");
+ need_update = 1;
+ } else {
+ log_err("The directory was not fixed.\n");
}
-
- increment_link(ip->i_sbd, ip->i_num.no_addr);
- ds.entry_count++;
- free(filename.name);
-
- }
- free_inode(&ip);
-
- if(get_and_read_buf(sbp, i, &bh, 0)){
- log_err("Unable to retrieve block #%"PRIu64"\n",
- i);
- block_set(sbp->bl, i, meta_inval);
- return -1;
}
- if(copyin_inode(sbp, bh, &ip)) {
- stack;
- relse_buf(sbp, bh);
- return -1;
- }
if(ip->i_di.di_entries != ds.entry_count) {
- log_err("Entries is %d - should be %d for %"PRIu64"\n",
+ log_err("Entries is %d - should be %d for inode "
+ "block %" PRIu64 "\n",
ip->i_di.di_entries, ds.entry_count,
ip->i_di.di_num.no_addr);
- ip->i_di.di_entries = ds.entry_count;
+ if (query(sbp, "Fix the entry count? (y/n) ")) {
+ ip->i_di.di_entries = ds.entry_count;
+ need_update = 1;
+ } else {
+ log_err("The entry count was not fixed.\n");
+ }
+ }
+ if (need_update) {
gfs_dinode_out(&ip->i_di, BH_DATA(bh));
write_buf(sbp, bh, 0);
+ free_inode(&ip);
+ relse_buf(sbp, bh);
}
- free_inode(&ip);
- relse_buf(sbp, bh);
}
return 0;
}
diff --git a/gfs/gfs_fsck/pass3.c b/gfs/gfs_fsck/pass3.c
index 3991147..4f6781b 100644
--- a/gfs/gfs_fsck/pass3.c
+++ b/gfs/gfs_fsck/pass3.c
@@ -25,23 +25,25 @@ static int attach_dotdot_to(struct fsck_sb *sbp, uint64_t newdotdot,
* this case? */
filename.len = strlen("..");
- if(!(filename.name = malloc(sizeof(char) * filename.len))) {
+ if(!(filename.name = malloc(sizeof(char) * filename.len + 1))) {
log_err("Unable to allocate name\n");
+ free_inode(&ip);
+ free_inode(&pip);
stack;
return -1;
}
- if(!memset(filename.name, 0, sizeof(char) * filename.len)) {
+ if(!memset(filename.name, 0, (sizeof(char) * filename.len + 1))) {
log_err("Unable to zero name\n");
+ free_inode(&ip);
+ free_inode(&pip);
stack;
return -1;
}
memcpy(filename.name, "..", filename.len);
- if(fs_dirent_del(ip, NULL, &filename)){
+ if(fs_dirent_del(ip, NULL, &filename))
log_warn("Unable to remove \"..\" directory entry.\n");
- }
- else {
+ else
decrement_link(sbp, olddotdot);
- }
if(fs_dir_add(ip, &filename, &pip->i_num,
pip->i_di.di_type)){
log_err("Failed to link \"..\" entry to directory.\n");
@@ -56,7 +58,7 @@ static int attach_dotdot_to(struct fsck_sb *sbp, uint64_t newdotdot,
return 0;
}
-struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
+static struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
struct dir_info *di)
{
struct dir_info *pdi;
@@ -65,9 +67,8 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
di->checked = 1;
- if(!di->treewalk_parent) {
+ if(!di->treewalk_parent)
return NULL;
- }
if(di->dotdot_parent != di->treewalk_parent) {
log_warn(".. and treewalk conections are not the same for %"PRIu64
@@ -104,7 +105,6 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
attach_dotdot_to(sbp, di->treewalk_parent,
di->dotdot_parent, di->dinode);
di->dotdot_parent = di->treewalk_parent;
-
}
}
else {
@@ -145,7 +145,6 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
attach_dotdot_to(sbp, di->treewalk_parent,
di->dotdot_parent, di->dinode);
di->dotdot_parent = di->treewalk_parent;
-
}
}
}
@@ -165,7 +164,6 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
find_di(sbp, di->dotdot_parent, &pdi);
return pdi;
-
}
/**
@@ -250,6 +248,7 @@ int pass3(struct fsck_sb *sbp, struct options *opts)
if(query(sbp, "Add unlinked directory to l+f? (y/n) ")) {
if(add_inode_to_lf(ip)) {
stack;
+ free_inode(&ip);
return -1;
}
log_warn("Directory relinked to l+f\n");
diff --git a/gfs/gfs_fsck/pass4.c b/gfs/gfs_fsck/pass4.c
index bb9d61d..4455fc6 100644
--- a/gfs/gfs_fsck/pass4.c
+++ b/gfs/gfs_fsck/pass4.c
@@ -6,10 +6,19 @@
#include "inode_hash.h"
#include "inode.h"
#include "lost_n_found.h"
+#include "metawalk.h"
+
+struct metawalk_fxns pass4_fxns_delete = {
+ .private = NULL,
+ .check_metalist = delete_metadata,
+ .check_data = delete_data,
+ .check_eattr_indir = delete_eattr_indir,
+ .check_eattr_leaf = delete_eattr_leaf,
+};
/* Updates the link count of an inode to what the fsck has seen for
* link count */
-int fix_inode_count(struct fsck_sb *sbp, struct inode_info *ii,
+static int fix_inode_count(struct fsck_sb *sbp, struct inode_info *ii,
struct fsck_inode *ip)
{
log_info("Fixing inode count for %"PRIu64"\n",
@@ -26,7 +35,7 @@ int fix_inode_count(struct fsck_sb *sbp, struct inode_info *ii,
return 0;
}
-int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
+static int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
osi_list_t *tmp;
struct inode_info *ii;
struct fsck_inode *ip;
@@ -61,12 +70,18 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
log_err("Unlinked inode contains"
"bad blocks\n",
ii->inode);
- if(query(sbp, "Clear unlinked inode with bad blocks? (y/n) ")) {
- block_set(sbp->bl, ii->inode, block_free);
+ if(query(sbp, "Delete unlinked inode with bad "
+ "blocks? (y/n) ")) {
+ load_inode(sbp, ii->inode, &ip);
+ check_inode_eattr(ip,
+ &pass4_fxns_delete);
+ check_metatree(ip, &pass4_fxns_delete);
+ free_inode(&ip);
+ block_set(sbp->bl, ii->inode,
+ block_free);
continue;
- } else {
+ } else
log_err("Unlinked inode with bad blocks not cleared\n");
- }
}
if(q.block_type != inode_dir &&
q.block_type != inode_file &&
@@ -75,9 +90,23 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
q.block_type != inode_chr &&
q.block_type != inode_fifo &&
q.block_type != inode_sock) {
- log_err("Unlinked block marked as inode not an inode\n");
- block_set(sbp->bl, ii->inode, block_free);
- log_err("Cleared\n");
+ log_err("Unlinked block marked as an inode is "
+ "not an inode (%d)\n", q.block_type);
+ if(query(sbp, "Delete unlinked inode"
+ "? (y/n) ")) {
+ if (!load_inode(sbp, ii->inode, &ip)) {
+ check_inode_eattr(ip,
+ &pass4_fxns_delete);
+ check_metatree(ip,
+ &pass4_fxns_delete);
+ }
+ block_set(sbp->bl, ii->inode,
+ block_free);
+ free_inode(&ip);
+ log_err("The inode was deleted\n");
+ } else
+ log_err("The inode was not deleted\n");
+
continue;
}
if(load_inode(sbp, ii->inode, &ip)) {
@@ -106,9 +135,8 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
fix_inode_count(sbp, ii, ip);
lf_addition = 1;
}
- } else {
+ } else
log_err("Unlinked inode left unlinked\n");
- }
free_inode(&ip);
}
else if(ii->link_count != ii->counted_links) {
@@ -143,7 +171,6 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
}
}
-
return 0;
}
diff --git a/gfs/gfs_fsck/pass5.c b/gfs/gfs_fsck/pass5.c
index 86c9cc3..b40ab59 100644
--- a/gfs/gfs_fsck/pass5.c
+++ b/gfs/gfs_fsck/pass5.c
@@ -119,7 +119,7 @@ int count_bmaps(struct fsck_rgrp *rgp)
}
#endif /* DEBUG */
-int convert_mark(enum mark_block mark, uint32_t *count)
+static int convert_mark(enum mark_block mark, uint32_t *count)
{
switch(mark) {
@@ -162,8 +162,19 @@ int convert_mark(enum mark_block mark, uint32_t *count)
return -1;
}
+static const char *block_type_string(struct block_query *q)
+{
+ const char *blktyp[] = {"free", "used", "indirect data", "directory",
+ "file", "symlink", "block dev", "char dev",
+ "fifo", "socket", "dir leaf", "journ data",
+ "other meta", "free meta", "meta eattr",
+ "bad blk", "dup block", "eattr", "invalid"};
+ if (q->block_type < 18)
+ return (blktyp[q->block_type]);
+ return blktyp[18];
+}
-int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
+static int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
uint64_t *rg_block, uint64_t rg_data, uint32_t *count)
{
unsigned char *byte, *end;
@@ -179,7 +190,6 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
while(byte < end) {
rg_status = ((*byte >> bit) & GFS_BIT_MASK);
block = rg_data + *rg_block;
- log_debug("Checking block %" PRIu64 "\n", block);
warm_fuzzy_stuff(block);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return 0;
@@ -196,7 +206,11 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
PRIu64" to a free data block\n", block);
if(!sbp->opts->no) {
if(fs_set_bitmap(sbp, block, block_status)) {
- log_warn("Failed to convert free metadata block to free data block at %PRIu64.\n", block);
+ log_warn("Failed to convert "
+ "free metadata block "
+ "to free data block "
+ "at %"PRIu64".\n",
+ block);
}
else {
log_info("Succeeded.\n");
@@ -204,22 +218,30 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
}
}
else {
+ const char *blockstatus[] = {"Free", "Data",
+ "Free Meta",
+ "Metadata"};
log_err("ondisk and fsck bitmaps differ at"
" block %"PRIu64"\n", block);
+ log_err("Ondisk status is %u (%s) but FSCK "
+ "thinks it should be ",
+ rg_status, blockstatus[rg_status]);
+ log_err("%u (%s)\n", block_status,
+ blockstatus[block_status]);
+ log_err("Metadata type is %u (%s)\n",
+ q.block_type,
+ block_type_string(&q));
if(query(sbp, "Fix bitmap for block %"
PRIu64"? (y/n) ", block)) {
- if(fs_set_bitmap(sbp, block, block_status)) {
+ if(fs_set_bitmap(sbp, block, block_status))
log_err("Failed.\n");
- }
- else {
+ else
log_err("Succeeded.\n");
- }
- } else {
+ } else
log_err("Bitmap at block %"PRIu64
" left inconsistent\n", block);
- }
}
}
(*rg_block)++;
@@ -240,7 +262,7 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
#define FREE_META_COUNT 16
#define CONVERT_FREEMETA_TO_FREE (FREE_COUNT | FREE_META_COUNT)
-int update_rgrp(struct fsck_rgrp *rgp, uint32_t *count, int rgcount)
+static int update_rgrp(struct fsck_rgrp *rgp, uint32_t *count, int rgcount)
{
uint32_t i;
fs_bitmap_t *bits;
diff --git a/gfs/gfs_fsck/rgrp.c b/gfs/gfs_fsck/rgrp.c
index dcbb2cc..89a7c39 100644
--- a/gfs/gfs_fsck/rgrp.c
+++ b/gfs/gfs_fsck/rgrp.c
@@ -155,7 +155,6 @@ int fs_rgrp_read(struct fsck_rgrp *rgd, int repair_if_corrupted)
int error;
if(rgd->rd_open_count){
- log_debug("rgrp already read...\n");
rgd->rd_open_count++;
return 0;
}
@@ -236,7 +235,7 @@ void fs_rgrp_relse(struct fsck_rgrp *rgd)
rgd->rd_open_count--;
if(rgd->rd_open_count){
- log_debug("rgrp still held...\n");
+ ;
} else {
for (x = 0; x < length; x++){
if (rgd->rd_bh[x]) {
diff --git a/gfs/gfs_fsck/super.c b/gfs/gfs_fsck/super.c
index 507cf3f..f2ada28 100644
--- a/gfs/gfs_fsck/super.c
+++ b/gfs/gfs_fsck/super.c
@@ -1142,7 +1142,7 @@ int ri_update(struct fsck_sb *sdp)
osi_list_t expected_rglist; /* List of expected resource groups */
osi_list_t *tmp;
struct gfs_rindex buf;
- unsigned int rg, calc_rg_count;
+ unsigned int rg, calc_rg_count = 0;
int error, count1 = 0, count2 = 0;
int fix_grow_problems = 0, grow_problems = 0;
enum rgindex_trust_level { /* how far can we trust our RG index? */
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2009-08-10 19:21 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-08-10 19:21 cluster: STABLE2 - GFS: gfs_fsck sometimes needs to be run twice Bob Peterson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).