public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* cluster: RHEL5 - GFS2: fsck.gfs2 sometimes needs to be run twice
@ 2009-08-10 16:54 Bob Peterson
0 siblings, 0 replies; only message in thread
From: Bob Peterson @ 2009-08-10 16:54 UTC (permalink / raw)
To: cluster-cvs-relay
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=726f9de7c9483aa0b797f1c2482cea37d73ed0cc
Commit: 726f9de7c9483aa0b797f1c2482cea37d73ed0cc
Parent: 8bae5c5c8121dea9bd80382851020d17be3d8552
Author: Bob Peterson <rpeterso@redhat.com>
AuthorDate: Mon Aug 10 11:47:02 2009 -0500
Committer: Bob Peterson <rpeterso@redhat.com>
CommitterDate: Mon Aug 10 11:52:24 2009 -0500
GFS2: fsck.gfs2 sometimes needs to be run twice
bz 500483
This patch fixes numerous bugs whereby fsck.gfs2 was not "following
through" with its changes, and therefore a second run was often
needed to completely clean things up.
---
gfs2/edit/hexedit.c | 63 ------
gfs2/fsck/main.c | 6 +-
gfs2/fsck/metawalk.c | 471 ++++++++++++++++++++++++++++++++-----------
gfs2/fsck/metawalk.h | 15 ++-
gfs2/fsck/pass1.c | 493 ++++++++++++++++++++++++++++-----------------
gfs2/fsck/pass1b.c | 127 ++++++++----
gfs2/fsck/pass1c.c | 191 ++++++++++--------
gfs2/fsck/pass2.c | 238 ++++++++++++++--------
gfs2/fsck/pass3.c | 3 +-
gfs2/fsck/pass4.c | 43 ++++-
gfs2/fsck/pass5.c | 51 ++++-
gfs2/fsck/rgrepair.c | 2 +-
gfs2/libgfs2/block_list.c | 28 ++-
gfs2/libgfs2/fs_bits.c | 61 ++++++
gfs2/libgfs2/fs_ops.c | 2 +
gfs2/libgfs2/libgfs2.h | 8 +-
16 files changed, 1196 insertions(+), 606 deletions(-)
diff --git a/gfs2/edit/hexedit.c b/gfs2/edit/hexedit.c
index 5937663..3f932da 100644
--- a/gfs2/edit/hexedit.c
+++ b/gfs2/edit/hexedit.c
@@ -438,8 +438,6 @@ void print_usage(void)
Erase();
}
-
-
/* ------------------------------------------------------------------------ */
/* get_block_type */
/* returns: metatype if block is a GFS2 structure block type */
@@ -457,67 +455,6 @@ int get_block_type(const char *lpBuffer)
return ret_type;
}
-/*
- * fs_get_bitmap - get value of FS bitmap
- * @sdp: super block
- * @blkno: block number relative to file system
- *
- * This function gets the value of a bit of the
- * file system bitmap.
- * Possible state values for a block in the bitmap are:
- * GFS_BLKST_FREE (0)
- * GFS_BLKST_USED (1)
- * GFS_BLKST_INVALID (2)
- * GFS_BLKST_DINODE (3)
- *
- * Returns: state on success, -1 on error
- */
-int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
- struct rgrp_list *rgd)
-{
- int buf, val;
- uint32_t rgrp_block;
- struct gfs2_bitmap *bits = NULL;
- unsigned int bit;
- unsigned char *byte;
- int local_rgd = 0;
-
- if(gfs2_check_range(sdp, blkno))
- return -1;
- if(rgd == NULL) {
- local_rgd = 1;
- rgd = gfs2_blk2rgrpd(sdp, blkno);
- }
- if(rgd == NULL)
- return -1;
- if(gfs2_rgrp_read(sdp, rgd))
- return -1;
-
- rgrp_block = (uint32_t)(blkno - rgd->ri.ri_data0);
-
- for(buf= 0; buf < rgd->ri.ri_length; buf++){
- bits = &(rgd->bits[buf]);
- if(rgrp_block < ((bits->bi_start + bits->bi_len)*GFS2_NBBY)){
- break;
- }
- }
-
- if(buf >= rgd->ri.ri_length){
- gfs2_rgrp_relse(rgd, not_updated);
- return -1;
- }
-
- byte = (unsigned char *)(rgd->bh[buf]->b_data + bits->bi_offset) +
- (rgrp_block/GFS2_NBBY - bits->bi_start);
- bit = (rgrp_block % GFS2_NBBY) * GFS2_BIT_SIZE;
-
- val = ((*byte >> bit) & GFS2_BIT_MASK);
- if(local_rgd)
- gfs2_rgrp_relse(rgd, not_updated);
-
- return val;
-}
-
/* ------------------------------------------------------------------------ */
/* display_block_type */
/* returns: metatype if block is a GFS2 structure block type */
diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c
index 8c85c8d..0b76790 100644
--- a/gfs2/fsck/main.c
+++ b/gfs2/fsck/main.c
@@ -199,9 +199,9 @@ int check_system_inode(struct gfs2_inode *sysinode, const char *filename,
if(!sysinode || ds.q.block_type != mark) {
log_err("Invalid or missing %s system inode.\n", filename);
errors_found++;
- if ((errors_corrected +=
- query(&opts, "Create new %s system inode? (y/n) ",
- filename))) {
+ if (query(&opts, "Create new %s system inode? (y/n) ",
+ filename)) {
+ errors_corrected++;
builder(sysinode->i_sbd);
gfs2_block_set(sysinode->i_sbd, bl,
sysinode->i_di.di_num.no_addr,
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 0730835..be62cfa 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -103,6 +103,17 @@ void fsck_inode_put(struct gfs2_inode *ip, enum update_flags update)
}
}
+/**
+ * dirent_repair - attempt to repair a corrupt directory entry.
+ * @bh - The buffer header that contains the bad dirent
+ * @de - The directory entry in native format
+ * @dent - The directory entry in on-disk format
+ * @type - Type of directory (DIR_LINEAR or DIR_EXHASH)
+ * @first - TRUE if this is the first dirent in the buffer
+ *
+ * This function tries to repair a corrupt directory entry. All we
+ * know at this point is that the length field is wrong.
+ */
int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
struct gfs2_dirent *de, struct gfs2_dirent *dent,
int type, int first)
@@ -118,8 +129,7 @@ int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
else
de->de_rec_len = ip->i_sbd->bsize -
sizeof(struct gfs2_leaf);
- }
- else {
+ } else {
bh_end = bh->b_data + ip->i_sbd->bsize;
/* first, figure out a probable name length */
p = (char *)dent + sizeof(struct gfs2_dirent);
@@ -142,8 +152,40 @@ int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
return 0;
}
+/**
+ * dirblk_truncate - truncate a directory block
+ */
+static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
+ struct gfs2_buffer_head *bh)
+{
+ char *bh_end;
+ struct gfs2_dirent de;
+ uint16_t old_rec_len;
+
+ bh_end = bh->b_data + ip->i_sbd->sd_sb.sb_bsize;
+ /* truncate the block to save the most dentries. To do this we
+ have to patch the previous dent. */
+ gfs2_dirent_in(&de, (char *)fixb);
+ old_rec_len = de.de_rec_len;
+ de.de_rec_len = bh_end - (char *)fixb;
+ gfs2_dirent_out(&de, (char *)fixb);
+}
+
+/*
+ * 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
+ */
int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
- int index, int type, enum update_flags *update,
+ int type, enum update_flags *update,
uint16_t *count, struct metawalk_fxns *pass)
{
struct gfs2_leaf *leaf = NULL;
@@ -175,12 +217,15 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
return 0;
while(1) {
+ if (skip_this_pass || fsck_abort)
+ return FSCK_OK;
memset(&de, 0, sizeof(struct gfs2_dirent));
gfs2_dirent_in(&de, (char *)dent);
filename = (char *)dent + sizeof(struct gfs2_dirent);
if (de.de_rec_len < sizeof(struct gfs2_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 "(0x%"
PRIx64 "), entry %d of directory %"
PRIu64 "(0x%" PRIx64 ") is corrupt.\n",
@@ -190,18 +235,28 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
errors_found++;
if (query(&opts, "Attempt to repair it? (y/n) ")) {
if (dirent_repair(ip, bh, &de, dent, type,
- first))
- break;
- else {
+ first)) {
+ if (first) /* make a new sentinel */
+ dirblk_truncate(ip, dent, bh);
+ else
+ dirblk_truncate(ip, prev, bh);
+ *update = updated;
+ log_err("Unable to repair corrupt "
+ "directory entry; the entry "
+ "was removed instead.\n");
+ return 0;
+ } else {
+ log_err("Corrupt directory entry "
+ "repaired.\n");
errors_corrected++;
*update = updated;
+ /* keep looping through dentries */
}
- }
- else {
+ } else {
log_err("Corrupt directory entry ignored, "
"stopped after checking %d entries.\n",
*count);
- break;
+ return 0;
}
}
if (!de.de_inum.no_formal_ino){
@@ -209,12 +264,26 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
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 "(0x%" PRIx64 ") of directory %" PRIu64
- " (0x%" PRIx64 ")!\n", bh->b_blocknr, bh->b_blocknr,
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
- return 1;
+ log_err("Directory entry with inode number of "
+ "zero in leaf %" PRIu64 "(0x%" PRIx64
+ ") of directory %" PRIu64 " (0x%"
+ PRIx64 ")!\n", bh->b_blocknr,
+ bh->b_blocknr,
+ ip->i_di.di_num.no_addr,
+ ip->i_di.di_num.no_addr);
+ if (query(&opts,
+ "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 {
if (!de.de_inum.no_addr && first) { /* reverse sentinel */
@@ -236,9 +305,6 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
stack;
return -1;
}
- /*if(error > 0) {
- return 1;
- }*/
}
}
@@ -263,12 +329,12 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
/* leaf a bit, but it's better than deleting the whole directory, */
/* which is what used to happen before. */
void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
- uint64_t *bad_leaf, uint64_t old_leaf, int index,
- const char *msg)
+ uint64_t *bad_leaf, uint64_t old_leaf,
+ uint64_t first_ok_leaf, int index, const char *msg)
{
if (*bad_leaf != *leaf_no) {
log_err("Directory Inode %" PRIu64 "(0x%"
- PRIx64 ") points to leaf %" PRIu64 "(0x%"
+ PRIx64 ") points to leaf %" PRIu64 " (0x%"
PRIx64 ") %s.\n", ip->i_di.di_num.no_addr,
ip->i_di.di_num.no_addr, *leaf_no, *leaf_no, msg);
}
@@ -276,7 +342,13 @@ void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
if (*leaf_no == *bad_leaf ||
query(&opts, "Attempt to patch around it? (y/n) ")) {
errors_corrected++;
- gfs2_put_leaf_nr(ip, index, old_leaf);
+ if (gfs2_check_range(ip->i_sbd, old_leaf) == 0)
+ gfs2_put_leaf_nr(ip, index, old_leaf);
+ else
+ gfs2_put_leaf_nr(ip, index, first_ok_leaf);
+ log_err("Directory Inode %" PRIu64 "(0x%" PRIx64
+ ") repaired.\n", ip->i_di.di_num.no_addr,
+ ip->i_di.di_num.no_addr);
}
else
log_err("Bad leaf left in place.\n");
@@ -285,21 +357,42 @@ void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
}
/* Checks exhash directory entries */
-int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
- struct metawalk_fxns *pass)
+int check_leaf_blks(struct gfs2_inode *ip, enum update_flags *update,
+ struct metawalk_fxns *pass)
{
int error;
struct gfs2_leaf leaf, oldleaf;
uint64_t leaf_no, old_leaf, bad_leaf = -1;
+ uint64_t first_leaf_ptr = -1, first_ok_leaf = -1;
struct gfs2_buffer_head *lbh;
int index;
struct gfs2_sbd *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(index = 0; index < (1 << ip->i_di.di_depth); index++) {
+ gfs2_get_leaf_nr(ip, index, &first_ok_leaf);
+ if (first_leaf_ptr == -1)
+ first_leaf_ptr = first_ok_leaf;
+ if(gfs2_check_range(ip->i_sbd, first_ok_leaf) == 0) {
+ lbh = bread(&sbp->buf_list, first_ok_leaf);
+ /* Make sure it's really a valid leaf block. */
+ if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) == 0) {
+ brelse(lbh, not_updated);
+ break;
+ }
+ brelse(lbh, not_updated);
+ }
+ }
+ old_leaf = -1;
memset(&oldleaf, 0, sizeof(oldleaf));
for(index = 0; index < (1 << ip->i_di.di_depth); index++) {
+ if (fsck_abort)
+ break;
gfs2_get_leaf_nr(ip, index, &leaf_no);
/* GFS has multiple indirect pointers to the same leaf
@@ -314,41 +407,39 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
else if(old_leaf == leaf_no) {
ref_count++;
continue;
- } else {
- if(ref_count != exp_count){
- log_err("Dir #%" PRIu64 " (0x%"
- PRIx64 ") has an incorrect "
- "number of pointers to leaf #%"
- PRIu64 " (0x%" PRIx64
- ")\n\tFound: %u, Expected: %u\n",
- ip->i_di.di_num.no_addr,
- ip->i_di.di_num.no_addr,
- old_leaf, old_leaf, ref_count,
- exp_count);
- errors_found++;
- if (query(&opts, "Attempt to fix it? (y/n) "))
- {
- int factor = 0, divisor = ref_count;
-
- errors_corrected++;
- lbh = bread(&sbp->buf_list, old_leaf);
- while (divisor > 1) {
- factor++;
- divisor /= 2;
- }
- oldleaf.lf_depth = ip->i_di.di_depth -
- factor;
- gfs2_leaf_out(&oldleaf, lbh->b_data);
- brelse(lbh, updated);
+ }
+ if (gfs2_check_range(ip->i_sbd, old_leaf) == 0 &&
+ ref_count != exp_count) {
+ log_err("Dir #%" PRIu64 " (0x%" PRIx64 ") has an "
+ "incorrect number of pointers to leaf #%"
+ PRIu64 " (0x%" PRIx64 ")\n\tFound: %u, "
+ "Expected: %u\n", ip->i_di.di_num.no_addr,
+ ip->i_di.di_num.no_addr, old_leaf, old_leaf,
+ ref_count, exp_count);
+ errors_found++;
+ if (query(&opts, "Attempt to fix it? (y/n) ")) {
+ int factor = 0, divisor = ref_count;
+
+ errors_corrected++;
+ lbh = bread(&sbp->buf_list, old_leaf);
+ while (divisor > 1) {
+ factor++;
+ divisor /= 2;
}
- else
- return 1;
+ gfs2_leaf_in(&oldleaf, lbh->b_data);
+ oldleaf.lf_depth = ip->i_di.di_depth - factor;
+ gfs2_leaf_out(&oldleaf, lbh->b_data);
+ brelse(lbh, updated);
}
- 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(gfs2_check_range(ip->i_sbd, leaf_no)){
log_err("Leaf block #%" PRIu64 " (0x%"
@@ -358,8 +449,8 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
ip->i_di.di_num.no_addr,
ip->i_di.di_num.no_addr);
warn_and_patch(ip, &leaf_no, &bad_leaf,
- old_leaf, index,
- "that is out of range");
+ old_leaf, first_ok_leaf,
+ index, "that is out of range");
memcpy(&leaf, &oldleaf, sizeof(oldleaf));
break;
}
@@ -370,10 +461,10 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
/* Make sure it's really a valid leaf block. */
if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) {
warn_and_patch(ip, &leaf_no, &bad_leaf,
- old_leaf, index,
+ old_leaf, first_ok_leaf, index,
"that is not really a leaf");
memcpy(&leaf, &oldleaf, sizeof(oldleaf));
- brelse(lbh, (opts.no ? not_updated : updated));
+ brelse(lbh, not_updated);
break;
}
gfs2_leaf_in(&leaf, lbh->b_data);
@@ -402,7 +493,9 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
log_err("Inode %" PRIu64 " (0x%"
PRIx64 ") points to bad leaf "
PRIu64 " (0x%" PRIx64 ").\n",
- ip->i_di.di_num.no_addr, leaf_no);
+ ip->i_di.di_num.no_addr,
+ ip->i_di.di_num.no_addr,
+ leaf_no, leaf_no);
brelse(lbh, *update);
break;
}
@@ -412,9 +505,8 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
if(pass->check_dentry &&
S_ISDIR(ip->i_di.di_mode)) {
- 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,
@@ -427,20 +519,24 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
return -1;
}
- if(error > 0)
- return 1;
-
if(update && (count != leaf.lf_entries)) {
enum update_flags f = not_updated;
lbh = bread(&sbp->buf_list, leaf_no);
gfs2_leaf_in(&leaf, lbh->b_data);
- log_err("Leaf %"PRIu64" (0x%" PRIx64
- ") entry count in directory %" PRIu64
- " doesn't match number of entries found - is %u, found %u\n",
- leaf_no, leaf_no, ip->i_di.di_num.no_addr,
- leaf.lf_entries, count);
+ log_err( "Leaf %llu (0x%llx) entry "
+ "count in directory %llu"
+ " (0x%llx) doesn't match "
+ "number of entries found "
+ "- is %u, found %u\n",
+ (unsigned long long)leaf_no,
+ (unsigned long long)leaf_no,
+ (unsigned long long)
+ ip->i_di.di_num.no_addr,
+ (unsigned long long)
+ ip->i_di.di_num.no_addr,
+ leaf.lf_entries, count);
errors_found++;
if(query(&opts, "Update leaf entry count? (y/n) ")) {
errors_corrected++;
@@ -454,7 +550,7 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
}
/* FIXME: Need to get entry count and
* compare it against leaf->lf_entries */
- break;
+ break; /* not a chain; go back to outer loop */
} else {
brelse(lbh, *update);
if(!leaf.lf_next)
@@ -462,10 +558,10 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
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;
}
@@ -489,12 +585,18 @@ static int check_eattr_entries(struct gfs2_inode *ip,
sizeof(struct gfs2_meta_header));
while(1){
- error = pass->check_eattr_entry(ip, bh, ea_hdr, ea_hdr_prev,
- pass->private);
+ if (ea_hdr->ea_type == GFS2_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;
}
+ if (error > 0)
+ *update_it = updated;
if(error == 0 && pass->check_eattr_extentry &&
ea_hdr->ea_num_ptrs) {
uint32_t tot_ealen = 0;
@@ -519,7 +621,8 @@ static int check_eattr_entries(struct gfs2_inode *ip,
update_it,
pass->private)) {
errors_found++;
- if (query(&opts, "Repair the bad EA? "
+ if (query(&opts, "Repair the bad "
+ "Extended Attribute? "
"(y/n) ")) {
errors_corrected++;
ea_hdr->ea_num_ptrs = i;
@@ -530,9 +633,15 @@ static int check_eattr_entries(struct gfs2_inode *ip,
/* Endianness doesn't matter
in this case because it's
a single byte. */
- return -1;
+ gfs2_block_set(sdp, bl,
+ ip->i_di.di_eattr,
+ gfs2_meta_eattr);
+ 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->sd_sb.sb_bsize -
sizeof(struct gfs2_meta_header);
@@ -541,7 +650,9 @@ static int check_eattr_entries(struct gfs2_inode *ip,
}
offset += be32_to_cpu(ea_hdr->ea_rec_len);
if(ea_hdr->ea_flags & GFS2_EAFLAG_LAST ||
- offset >= ip->i_sbd->sd_sb.sb_bsize || ea_hdr->ea_rec_len == 0){
+ offset >= ip->i_sbd->sd_sb.sb_bsize ||
+ ea_hdr->ea_rec_len == 0) {
+
break;
}
ea_hdr_prev = ea_hdr;
@@ -550,7 +661,7 @@ static int check_eattr_entries(struct gfs2_inode *ip,
be32_to_cpu(ea_hdr->ea_rec_len));
}
- return 0;
+ return error;
}
/**
@@ -558,7 +669,7 @@ static int check_eattr_entries(struct gfs2_inode *ip,
* @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 gfs2_inode *ip, uint64_t block,
uint64_t parent, enum update_flags *want_updated,
@@ -566,23 +677,33 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block,
{
struct gfs2_buffer_head *bh = NULL;
int error = 0;
+ enum update_flags updated_this_leaf = not_updated;
log_debug("Checking EA leaf block #%"PRIu64" (0x%" PRIx64 ").\n",
block, block);
if(pass->check_eattr_leaf) {
error = pass->check_eattr_leaf(ip, block, parent, &bh,
- want_updated, pass->private);
+ &updated_this_leaf,
+ pass->private);
+ if (updated_this_leaf) /* if this leaf was updated */
+ *want_updated = updated; /* signal it for the parent */
if(error < 0) {
stack;
return -1;
}
if(error > 0) {
+ if (bh)
+ brelse(bh, updated_this_leaf);
return 1;
}
if (bh) {
- error = check_eattr_entries(ip, bh, pass, want_updated);
- brelse(bh, *want_updated);
+ error = check_eattr_entries(ip, bh, pass,
+ &updated_this_leaf);
+ brelse(bh, updated_this_leaf);
+ if (updated_this_leaf) /* if this leaf was updated */
+ *want_updated = updated; /* signal it for
+ the parent */
}
return error;
}
@@ -599,12 +720,16 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block,
*/
static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
enum update_flags *want_updated,
- struct metawalk_fxns *pass){
+ struct metawalk_fxns *pass)
+{
int error = 0;
uint64_t *ea_leaf_ptr, *end;
uint64_t block;
struct gfs2_buffer_head *indirect_buf = NULL;
struct gfs2_sbd *sdp = ip->i_sbd;
+ enum update_flags update_indir_block = not_updated;
+ int first_ea_is_bad = 0;
+ uint64_t di_eattr_save = ip->i_di.di_eattr;
*want_updated = not_updated;
log_debug("Checking EA indirect block #%"PRIu64" (0x%" PRIx64 ").\n",
@@ -619,32 +744,72 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
int leaf_pointers = 0, leaf_pointer_errors = 0;
ea_leaf_ptr = (uint64_t *)(indirect_buf->b_data
- + sizeof(struct gfs2_meta_header));
+ + sizeof(struct gfs2_meta_header));
end = ea_leaf_ptr + ((sdp->sd_sb.sb_bsize
- - sizeof(struct gfs2_meta_header)) / 8);
+ - sizeof(struct gfs2_meta_header)) / 8);
while(*ea_leaf_ptr && (ea_leaf_ptr < end)){
block = be64_to_cpu(*ea_leaf_ptr);
leaf_pointers++;
error = check_leaf_eattr(ip, block, indirect,
want_updated, pass);
- if (error)
+ if (error) {
leaf_pointer_errors++;
+ if (update_indir_block == not_updated) {
+ errors_found++;
+ if (query(&opts, "Fix the indirect "
+ "block too? (y/n) ")) {
+ update_indir_block = updated;
+ errors_corrected++;
+ *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,
+ want_updated,
+ 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, want_updated,
- 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,
+ want_updated,
+ pass->private);
+ }
+ if (leaf_pointer_errors == leaf_pointers) {
if (*want_updated)
gfs2_set_bitmap(sdp, indirect,
GFS2_BLKST_FREE);
- gfs2_block_clear(sdp, bl, indirect,
- gfs2_indir_blk);
gfs2_block_set(sdp, bl, indirect,
gfs2_block_free);
error = 1;
@@ -652,7 +817,7 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
}
}
if (indirect_buf)
- brelse(indirect_buf, not_updated);
+ brelse(indirect_buf, update_indir_block);
return error;
}
@@ -668,21 +833,22 @@ int check_inode_eattr(struct gfs2_inode *ip, enum update_flags *want_updated,
{
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 " (0x%" PRIx64
- ").\n", ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
+ log_debug("Extended attributes exist for inode #%llu (0x%llx).\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
if(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT){
if((error = check_indirect_eattr(ip, ip->i_di.di_eattr,
want_updated, pass)))
stack;
} else {
- if((error = check_leaf_eattr(ip, ip->i_di.di_eattr,
- ip->i_di.di_num.no_addr,
- want_updated, pass)))
+ error = check_leaf_eattr(ip, ip->i_di.di_eattr,
+ ip->i_di.di_num.no_addr,
+ want_updated, pass);
+ if (error)
stack;
}
@@ -721,15 +887,23 @@ static int build_and_check_metalist(struct gfs2_inode *ip,
bh = osi_list_entry(tmp, struct gfs2_buffer_head,
b_altlist);
- head_size = (i > 1 ?
- sizeof(struct gfs2_meta_header) :
- sizeof(struct gfs2_dinode));
+ if (i > 1) {
+ /* if this isn't really a block list skip it */
+ if (gfs2_check_meta(bh, GFS2_METATYPE_IN))
+ continue;
+ head_size = sizeof(struct gfs2_meta_header);
+ } else {
+ /* if this isn't really a dinode, skip it */
+ if (gfs2_check_meta(bh, GFS2_METATYPE_DI))
+ continue;
+ head_size = sizeof(struct gfs2_dinode);
+ }
for (ptr = (uint64_t *)(bh->b_data + head_size);
(char *)ptr < (bh->b_data + ip->i_sbd->bsize);
ptr++) {
nbh = NULL;
-
+
if (!*ptr)
continue;
@@ -764,6 +938,7 @@ fail:
while (!osi_list_empty(list)) {
nbh = osi_list_entry(list->next,
struct gfs2_buffer_head, b_altlist);
+ brelse(nbh, not_updated);
osi_list_del(&nbh->b_altlist);
}
}
@@ -804,8 +979,9 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
/* We don't need to record directory blocks - they will be
* recorded later...i think... */
if (S_ISDIR(ip->i_di.di_mode))
- log_debug("Directory with height > 0 at %"PRIu64"\n",
- ip->i_di.di_num.no_addr);
+ log_debug( "Directory with height > 0 at %llu (0x%llx)\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
/* check data blocks */
list = &metalist[height - 1];
@@ -813,8 +989,17 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
for (tmp = list->next; tmp != list; tmp = tmp->next) {
bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist);
- head_size = (height != 1 ? sizeof(struct gfs2_meta_header) :
- sizeof(struct gfs2_dinode));
+ if (height > 1) {
+ /* if this isn't really a block list skip it */
+ if (gfs2_check_meta(bh, GFS2_METATYPE_IN))
+ continue;
+ head_size = sizeof(struct gfs2_meta_header);
+ } else {
+ /* if this isn't really a dinode, skip it */
+ if (gfs2_check_meta(bh, GFS2_METATYPE_DI))
+ continue;
+ head_size = sizeof(struct gfs2_dinode);
+ }
ptr = (uint64_t *)(bh->b_data + head_size);
for ( ; (char *)ptr < (bh->b_data + ip->i_sbd->bsize); ptr++) {
@@ -848,7 +1033,7 @@ end:
if (S_ISDIR(ip->i_di.di_mode)) {
/* check validity of leaf blocks and leaf chains */
if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
- error = check_leaf(ip, &update, pass);
+ error = check_leaf_blks(ip, &update, pass);
if(error < 0)
return -1;
if(error > 0)
@@ -866,7 +1051,7 @@ int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
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;
@@ -887,7 +1072,7 @@ int check_dir(struct gfs2_sbd *sbp, uint64_t block, struct metawalk_fxns *pass)
ip = fsck_inode_get(sbp, bh);
if(ip->i_di.di_flags & GFS2_DIF_EXHASH) {
- error = check_leaf(ip, &update, pass);
+ error = check_leaf_blks(ip, &update, pass);
if(error < 0) {
stack;
fsck_inode_put(ip, not_updated); /* does brelse(bh); */
@@ -1035,3 +1220,55 @@ int dinode_hash_remove(osi_list_t *buckets, uint64_t key)
}
return -1;
}
+
+/**
+ * delete_blocks - delete blocks associated with an inode
+ */
+int delete_blocks(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh, const char *btype,
+ void *private)
+{
+ struct gfs2_block_query q = {0};
+
+ if (gfs2_check_range(ip->i_sbd, block) == 0) {
+ if (gfs2_block_check(ip->i_sbd, bl, block, &q))
+ return 0;
+ if (!q.dup_block) {
+ log_info("Deleting %s block %lld (0x%llx) as part "
+ "of inode %lld (0x%llx)\n", btype,
+ (unsigned long long)block,
+ (unsigned long long)block,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_free);
+ gfs2_free_block(ip->i_sbd, block);
+ }
+ }
+ return 0;
+}
+
+int delete_metadata(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh, void *private)
+{
+ return delete_blocks(ip, block, bh, "metadata", private);
+}
+
+int delete_data(struct gfs2_inode *ip, uint64_t block, void *private)
+{
+ return delete_blocks(ip, block, NULL, "data", private);
+}
+
+int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+ struct gfs2_buffer_head **bh,
+ enum update_flags *want_updated, void *private)
+{
+ return delete_blocks(ip, block, NULL, "indirect extended attribute",
+ private);
+}
+
+int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+ struct gfs2_buffer_head **bh,
+ enum update_flags *want_updated, void *private)
+{
+ return delete_blocks(ip, block, NULL, "extended attribute", private);
+}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 44ac31a..6568bba 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -28,6 +28,18 @@ int remove_dentry_from_dir(struct gfs2_sbd *sbp, uint64_t dir,
int find_di(struct gfs2_sbd *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);
+int delete_blocks(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh, const char *btype,
+ void *private);
+int delete_metadata(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh, void *private);
+int delete_data(struct gfs2_inode *ip, uint64_t block, void *private);
+int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+ struct gfs2_buffer_head **bh,
+ enum update_flags *want_updated, void *private);
+int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+ struct gfs2_buffer_head **bh,
+ enum update_flags *want_updated, void *private);
/* metawalk_fxns: function pointers to check various parts of the fs
*
@@ -78,7 +90,8 @@ struct metawalk_fxns {
struct gfs2_ea_header *ea_hdr_prev,
enum update_flags *want_updated,
void *private);
- int (*finish_eattr_indir) (struct gfs2_inode *ip, int indir_ok,
+ int (*finish_eattr_indir) (struct gfs2_inode *ip, int leaf_pointers,
+ int leaf_pointer_errors,
enum update_flags *want_updated,
void *private);
};
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 329e95f..c03bb51 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -65,7 +65,8 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
struct gfs2_ea_header *ea_hdr_prev,
enum update_flags *want_updated,
void *private);
-static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok,
+static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
+ int leaf_pointer_errors,
enum update_flags *want_updated, void *private);
struct metawalk_fxns pass1_fxns = {
@@ -115,27 +116,30 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
return -1;
}
if(q.block_type != gfs2_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);
gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block);
found_dup = 1;
}
nbh = bread(&ip->i_sbd->buf_list, block);
if (gfs2_check_meta(nbh, GFS2_METATYPE_IN)){
- log_debug("Bad indirect block pointer "
- "(points to something that is not an indirect block).\n");
+ log_debug("Bad indirect block pointer (points to "
+ "something that is not an indirect block).\n");
if(!found_dup) {
gfs2_block_set(ip->i_sbd, bl, block, gfs2_meta_inval);
brelse(nbh, not_updated);
return 1;
}
- }else /* blk check ok */
+ brelse(nbh, not_updated);
+ } else /* blk check ok */
*bh = nbh;
- log_debug("Setting %" PRIu64 " (0x%" PRIx64 ") to indirect block.\n",
- block, block);
- gfs2_block_set(ip->i_sbd, bl, block, gfs2_indir_blk);
+ if (!found_dup) {
+ log_debug("Setting %" PRIu64 " (0x%" PRIx64 ") to indirect "
+ "block.\n", block, block);
+ gfs2_block_set(ip->i_sbd, bl, block, gfs2_indir_blk);
+ }
bc->indir_count++;
return 0;
@@ -145,30 +149,134 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
{
struct gfs2_block_query q = {0};
struct block_count *bc = (struct block_count *) private;
+ int error = 0, btype;
if (gfs2_check_range(ip->i_sbd, block)) {
- log_err( "Bad data block pointer (out of range)\n");
+ log_err("inode %lld (0x%llx) has a bad data block pointer "
+ "%lld (out of range)\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)block);
/* Mark the owner of this block with the bad_block
- * designator so we know to check it for out of range blocks later */
+ * designator so we know to check it for out of range
+ * blocks later */
gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
gfs2_bad_block);
return 1;
}
if(gfs2_block_check(ip->i_sbd, bl, block, &q)) {
stack;
+ log_err("Found bad block referenced as data at %"
+ PRIu64 " (0x%"PRIx64 ")\n", block, block);
return -1;
}
if(q.block_type != gfs2_block_free) {
- log_debug("Found duplicate block at %" PRIu64 " (0x%"PRIx64 ")\n",
- block, block);
+ log_err("Found duplicate block referenced as data at %"
+ PRIu64 " (0x%"PRIx64 ")\n", block, block);
+ if (q.block_type != gfs2_meta_inval) {
+ gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block);
+ /* 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 == gfs2_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 data block. */
+ error = 1;
+ gfs2_block_unmark(ip->i_sbd, bl, block, gfs2_meta_inval);
gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block);
- bc->data_count++;
- return 1;
}
- log_debug("Setting %" PRIu64 " (0x%" PRIx64 ") to data block\n", block,
- block);
- gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_used);
+ log_debug("Marking block %llu (0x%llx) as data block\n",
+ (unsigned long long)block, (unsigned long long)block);
+ gfs2_block_mark(ip->i_sbd, bl, block, gfs2_block_used);
+
+ /* This is also confusing, so I'll clarify. There are two bitmaps:
+ (1) The gfs2_bmap that fsck uses to keep track of what block
+ type has been discovered, and (2) The rgrp bitmap. Function
+ gfs2_block_set is used to set the former and gfs2_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 gfs2_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 = gfs2_get_bitmap(ip->i_sbd, block, NULL);
+ if (btype != GFS2_BLKST_USED) {
+ const char *allocdesc[] = {"free space", "data", "unlinked",
+ "metadata", "reserved"};
+
+ errors_found++;
+ log_err("Block #%llu (0x%llx) seems to be data, but is marked "
+ "as %s.\n", (unsigned long long)block,
+ (unsigned long long)block, allocdesc[btype]);
+ if(query(&opts, "Okay to mark it as 'data'? (y/n)")) {
+ errors_corrected++;
+ gfs2_set_bitmap(ip->i_sbd, block, GFS2_BLKST_USED);
+ log_err("The block was reassigned as data.\n");
+ } else {
+ log_err("The invalid block was ignored.\n");
+ }
+ }
bc->data_count++;
+ return error;
+}
+
+static int remove_inode_eattr(struct gfs2_inode *ip, struct block_count *bc,
+ int duplicate, enum update_flags *want_updated)
+{
+ if (!duplicate) {
+ gfs2_set_bitmap(ip->i_sbd, ip->i_di.di_eattr,
+ GFS2_BLKST_FREE);
+ gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_eattr,
+ gfs2_block_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 &= ~GFS2_DIF_EA_INDIRECT;
+ *want_updated = updated;
+ return 0;
+}
+
+static int ask_remove_inode_eattr(struct gfs2_inode *ip,
+ struct block_count *bc,
+ enum update_flags *want_updated)
+{
+ log_err("Inode %lld (0x%llx) has unrecoverable Extended Attribute "
+ "errors.\n", (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ errors_found++;
+ if (query(&opts, "Clear all Extended Attributes from the "
+ "inode? (y/n) ")) {
+ struct gfs2_block_query q;
+
+ errors_corrected++;
+ if(gfs2_block_check(ip->i_sbd, bl, ip->i_di.di_eattr, &q)) {
+ stack;
+ return -1;
+ }
+ if (!remove_inode_eattr(ip, bc, q.dup_block, want_updated))
+ 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;
}
@@ -190,30 +298,24 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc,
*want_updated = not_updated;
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");
+ log_err(" at block #%lld (0x%llx).\n",
+ (unsigned long long)block, (unsigned long long)block);
errors_found++;
- if ((errors_corrected += query(&opts, "Clear the bad EA? (y/n) "))) {
- if (block == 0)
- block = ip->i_di.di_eattr;
- gfs2_block_clear(sdp, bl, block, gfs2_eattr_block);
- if (!duplicate) {
- gfs2_block_clear(sdp, bl, block, gfs2_indir_blk);
+ if (query(&opts, "Clear the bad Extended Attribute? (y/n) ")) {
+ errors_corrected++;
+ if (block == ip->i_di.di_eattr) {
+ remove_inode_eattr(ip, bc, duplicate, want_updated);
+ log_err("The bad extended attribute was removed.\n");
+ } else if (!duplicate) {
gfs2_block_set(sdp, bl, block, gfs2_block_free);
gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+ log_err("The bad Extended Attribute was "
+ "removed.\n");
}
- ip->i_di.di_flags &= ~GFS2_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;
- gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
*want_updated = updated;
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;
}
@@ -251,48 +353,131 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
if(gfs2_check_meta(*bh, GFS2_METATYPE_IN)) {
if(q.block_type != gfs2_block_free) { /* Duplicate? */
if (!clear_eas(ip, bc, indirect, 1, want_updated,
- "Bad indirect EA duplicate found"))
- gfs2_block_set(sdp, bl, indirect,
- gfs2_dup_block);
+ "Bad indirect Extended Attribute "
+ "duplicate found")) {
+ gfs2_block_mark(sdp, bl, indirect,
+ gfs2_dup_block);
+ bc->ea_count++;
+ }
return 1;
}
clear_eas(ip, bc, indirect, 0, want_updated,
- "EA indirect block has incorrect type");
+ "Extended Attribute indirect block has incorrect "
+ "type");
return 1;
}
if(q.block_type != gfs2_block_free) { /* Duplicate? */
log_err("Inode #%" PRIu64 " (0x%" PRIx64
- "): Duplicate EA indirect block found at #%" PRIu64
- " (0x%" PRIx64 ").\n",
+ "): Duplicate Extended Attribute indirect block "
+ "found at #%" PRIu64 " (0x%" PRIx64 ").\n",
ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
indirect, indirect);
- gfs2_block_set(sdp, bl, indirect, gfs2_dup_block);
+ gfs2_block_mark(sdp, bl, indirect, gfs2_dup_block);
bc->ea_count++;
ret = 1;
} else {
log_debug("Setting #%" PRIu64 " (0x%" PRIx64
- ") to indirect EA block\n", indirect, indirect);
+ ") to indirect Extended Attribute block\n",
+ indirect, indirect);
gfs2_block_set(sdp, bl, indirect, gfs2_indir_blk);
bc->ea_count++;
}
return ret;
}
-static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok,
+static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
+ int leaf_pointer_errors,
enum update_flags *want_updated, void *private)
{
- if (indir_ok) {
- log_debug("Marking inode #%" PRIu64 " (0x%"
- PRIx64 ") with eattr block\n",
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
- /* Mark the inode as having an eattr in the block map
- so pass1c can check it. */
- gfs2_block_mark(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
- gfs2_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, want_updated);
+ log_debug("Marking inode #%lld (0x%llx) with extended "
+ "attribute block\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ /* Mark the inode as having an eattr in the block map
+ so pass1c can check it. */
+ gfs2_block_mark(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
+ gfs2_eattr_block);
+ bc->ea_count++;
+ if (!leaf_pointer_errors)
return 0;
+ log_err("Inode %lld (0x%llx) has recoverable indirect "
+ "Extended Attribute errors.\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ errors_found++;
+ if (query(&opts, "Okay to fix the block count for the inode? "
+ "(y/n) ")) {
+ errors_corrected++;
+ ip->i_di.di_blocks = 1 + bc->indir_count +
+ bc->data_count + bc->ea_count;
+ *want_updated = updated;
+ log_err("Block count fixed.\n");
+ return 1;
+ }
+ log_err("Block count not fixed.\n");
+ return 1;
+}
+
+static int check_leaf_block(struct gfs2_inode *ip, uint64_t block, int btype,
+ struct gfs2_buffer_head **bh,
+ enum update_flags *want_updated, void *private)
+{
+ struct gfs2_buffer_head *leaf_bh = NULL;
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ struct gfs2_block_query q = {0};
+ struct block_count *bc = (struct block_count *) private;
+
+ if(gfs2_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. */
+ leaf_bh = bread(&sdp->buf_list, block);
+ if(gfs2_check_meta(leaf_bh, btype)) {
+ if(q.block_type != gfs2_block_free) { /* Duplicate? */
+ clear_eas(ip, bc, block, 1, want_updated,
+ "Bad Extended Attribute duplicate found");
+ } else {
+ clear_eas(ip, bc, block, 0, want_updated,
+ "Extended Attribute leaf block "
+ "has incorrect type");
+ }
+ brelse(leaf_bh, *want_updated);
+ return 1;
}
- clear_eas(ip, (struct block_count *)private, 0, 0, want_updated,
- "has unrecoverable indirect EA errors");
+ if(q.block_type != gfs2_block_free) { /* Duplicate? */
+ log_debug("Duplicate block found at #%lld (0x%llx).\n",
+ (unsigned long long)block,
+ (unsigned long long)block);
+ gfs2_block_mark(sdp, bl, block, gfs2_dup_block);
+ bc->ea_count++;
+ brelse(leaf_bh, not_updated);
+ 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, want_updated,
+ "Extended Attribute block removed due to "
+ "previous errors.\n");
+ brelse(leaf_bh, *want_updated);
+ 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. */
+ gfs2_block_set(sdp, bl, block, gfs2_meta_eattr);
+ bc->ea_count++;
+ *bh = leaf_bh;
return 0;
}
@@ -314,60 +499,29 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
enum update_flags *want_updated,
void *private)
{
- struct gfs2_buffer_head *el_buf;
- struct gfs2_sbd *sdp = ip->i_sbd;
- struct gfs2_block_query q;
uint64_t el_blk = be64_to_cpu(*data_ptr);
- struct block_count *bc = (struct block_count *) private;
- int ret = 0;
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ struct gfs2_buffer_head *bh = NULL;
+ int error;
if(gfs2_check_range(sdp, el_blk)){
- log_err("Inode #%" PRIu64 " (0x%" PRIx64 "): EA extended "
- "leaf block #%" PRIu64 " (0x%" PRIx64
- ") is out of range.\n",
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
- el_blk, el_blk);
+ log_err("Inode #%llu (0x%llx): Extended Attribute block "
+ "%llu (0x%llx) has an extended leaf block #%llu "
+ "(0x%llx) that is out of range.\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_eattr,
+ (unsigned long long)ip->i_di.di_eattr,
+ (unsigned long long)el_blk,
+ (unsigned long long)el_blk);
gfs2_block_set(sdp, bl, ip->i_di.di_eattr, gfs2_bad_block);
return 1;
}
-
- if(gfs2_block_check(sdp, bl, el_blk, &q)) {
- stack;
- return -1;
- }
- el_buf = bread(&sdp->buf_list, el_blk);
-
- /* 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(gfs2_check_meta(el_buf, GFS2_METATYPE_ED)) {
- if(q.block_type != gfs2_block_free) /* Duplicate? */
- clear_eas(ip, bc, el_blk, 1, want_updated,
- "has bad extended EA duplicate");
- else
- clear_eas(ip, bc, el_blk, 0, want_updated,
- "EA extended leaf block has incorrect type");
- ret = 1;
- } else { /* If this looks like an EA */
- if(q.block_type != gfs2_block_free) { /* Duplicate? */
- log_debug("Duplicate block found at #%" PRIu64
- " (0x%" PRIx64 ").\n",
- el_blk, el_blk);
- gfs2_block_set(sdp, bl, el_blk, gfs2_dup_block);
- bc->ea_count++;
- ret = 1;
- } else {
- log_debug("Setting block #%" PRIu64
- " (0x%" PRIx64 ") to eattr block\n",
- el_blk, el_blk);
- gfs2_block_set(sdp, bl, el_blk, gfs2_meta_eattr);
- bc->ea_count++;
- }
- }
-
- brelse(el_buf, not_updated);
- return ret;
+ error = check_leaf_block(ip, el_blk, GFS2_METATYPE_ED, &bh,
+ want_updated, private);
+ if (bh)
+ brelse(bh, not_updated);
+ return error;
}
static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
@@ -375,69 +529,27 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
enum update_flags *want_updated, void *private)
{
struct gfs2_sbd *sdp = ip->i_sbd;
- struct gfs2_buffer_head *leaf_bh = NULL;
- int ret = 0;
- struct gfs2_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 " (0x%" PRIx64 ") to eattr block\n",
- parent, parent);
- gfs2_block_set(sdp, bl, parent, gfs2_eattr_block);
- }
- if(gfs2_check_range(sdp, block)){
- log_warn("Inode #%" PRIu64 " (0x%" PRIx64 "): EA leaf block "
- "#%" PRIu64 " (0x%" PRIx64 ") is out of range.\n",
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
- block, 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 %lld (0x%llx) as having eattr "
+ "block\n", (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ gfs2_block_mark(sdp, bl, ip->i_di.di_num.no_addr, gfs2_eattr_block);
+ if(gfs2_check_range(sdp, block)) {
+ log_warn("Inode #%llu (0x%llx): Extended Attribute leaf "
+ "block #%llu (0x%llx) is out of range.\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)block, (unsigned long long)block);
gfs2_block_set(sdp, bl, ip->i_di.di_eattr, gfs2_bad_block);
- ret = 1;
- }
- else if(gfs2_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. */
- leaf_bh = bread(&sdp->buf_list, block);
- if(gfs2_check_meta(leaf_bh, GFS2_METATYPE_EA)) {
- if(q.block_type != gfs2_block_free) { /* Duplicate? */
- clear_eas(ip, bc, block, 1, want_updated,
- "Bad EA duplicate found");
- } else {
- clear_eas(ip, bc, block, 0, want_updated,
- "EA leaf block has incorrect type");
- }
- ret = 1;
- brelse(leaf_bh, not_updated);
- } else { /* If this looks like an EA */
- if(q.block_type != gfs2_block_free) { /* Duplicate? */
- log_debug("Duplicate block found at #%" PRIu64
- " (0x%" PRIx64 ").\n",
- block, block);
- gfs2_block_set(sdp, bl, block, gfs2_dup_block);
- bc->ea_count++;
- ret = 1;
- brelse(leaf_bh, not_updated);
- } else {
- log_debug("Setting block #%" PRIu64
- " (0x%" PRIx64 ") to eattr block\n",
- block, block);
- gfs2_block_set(sdp, bl, block,
- gfs2_meta_eattr);
- bc->ea_count++;
- }
- }
+ return 1;
}
- if (!ret)
- *bh = leaf_bh;
-
- return ret;
+ return check_leaf_block(ip, block, GFS2_METATYPE_EA, bh, want_updated,
+ private);
}
static int check_eattr_entries(struct gfs2_inode *ip,
@@ -519,6 +631,7 @@ int clear_leaf(struct gfs2_inode *ip, uint64_t block,
struct gfs2_buffer_head *bh, void *private)
{
struct gfs2_block_query q = {0};
+
log_crit("Clearing leaf #%" PRIu64 " (0x%" PRIx64 ")\n", block, block);
if(gfs2_block_check(ip->i_sbd, bl, block, &q)) {
@@ -535,7 +648,6 @@ int clear_leaf(struct gfs2_inode *ip, uint64_t block,
return 0;
}
return 0;
-
}
int add_to_dir_list(struct gfs2_sbd *sbp, uint64_t block)
@@ -590,9 +702,9 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
block, block, ip->i_di.di_num.no_addr,
ip->i_di.di_num.no_addr);
errors_found++;
- if((errors_corrected +=
- query(&opts, "Fix address in inode at block #%"
- PRIu64 " (0x%" PRIx64 ")? (y/n) ", block, block))) {
+ if(query(&opts, "Fix address in inode at block #%"
+ PRIu64 " (0x%" PRIx64 ")? (y/n) ", block, block)) {
+ errors_corrected++;
ip->i_di.di_num.no_addr = ip->i_di.di_num.no_formal_ino = block;
gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
f = updated;
@@ -607,8 +719,8 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
return -1;
}
if(q.block_type != gfs2_block_free) {
- log_debug("Found duplicate block at #%" PRIu64 " (0x%" PRIx64 ")\n",
- block, block);
+ log_err("Found duplicate block referenced as an inode at #%"
+ PRIu64 " (0x%" PRIx64 ")\n", block, block);
if(gfs2_block_mark(sdp, bl, block, gfs2_dup_block)) {
stack;
fsck_inode_put(ip, f);
@@ -749,13 +861,14 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
pass1_fxns.private = &bc;
error = check_metatree(ip, &pass1_fxns);
- if(error < 0) {
+ if (fsck_abort || error < 0) {
fsck_inode_put(ip, f);
return 0;
}
if(error > 0) {
- log_warn("Marking inode #%" PRIu64 " (0x%" PRIx64 ") invalid\n",
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
+ log_warn("Marking inode #%lld (0x%llx) invalid\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ ip->i_di.di_num.no_addr);
/* FIXME: Must set all leaves invalid as well */
check_metatree(ip, &invalidate_metatree);
gfs2_block_set(sdp, bl, ip->i_di.di_num.no_addr,
@@ -765,7 +878,11 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
return 0;
}
- check_inode_eattr(ip, &f, &pass1_fxns);
+ error = check_inode_eattr(ip, &f, &pass1_fxns);
+
+ if (error && f == updated &&
+ !(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT))
+ ask_remove_inode_eattr(ip, &bc, &f);
if (ip->i_di.di_blocks !=
(1 + bc.indir_count + bc.data_count + bc.ea_count)) {
@@ -774,9 +891,13 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
ip->i_di.di_blocks,
1 + bc.indir_count + bc.data_count + bc.ea_count);
+ 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);
errors_found++;
- if ((errors_corrected +=
- query(&opts, "Fix ondisk block count? (y/n) "))) {
+ if (query(&opts, "Fix ondisk block count? (y/n) ")) {
+ errors_corrected++;
ip->i_di.di_blocks = 1 + bc.indir_count + bc.data_count +
bc.ea_count;
gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
@@ -795,13 +916,21 @@ int scan_meta(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
uint64_t block)
{
if (gfs2_check_meta(bh, 0)) {
- log_debug("Found invalid metadata at #%" PRIu64 " (0x%" PRIx64 ")\n",
- block, block);
+ errors_found++;
+
+ log_err("Found invalid metadata block at #%llu (0x%llx)\n",
+ (unsigned long long)block, (unsigned long long)block);
if(gfs2_block_set(sdp, bl, block, gfs2_meta_inval)) {
stack;
return -1;
}
- gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+ if(query(&opts, "Okay to free the invalid block? (y/n)")) {
+ errors_corrected++;
+ gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+ log_err("The invalid block was freed.\n");
+ } else {
+ log_err("The invalid block was ignored.\n");
+ }
return 0;
}
@@ -817,12 +946,12 @@ int scan_meta(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
return -1;
}
}
- /* Ignore everything else - they should be hit by the handle_di step. */
- /* Don't check NONE either, because check_meta passes everything if */
- /* GFS2_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. */
+ /* Ignore everything else - they should be hit by the handle_di step.
+ * Don't check NONE either, because check_meta passes everything if
+ * GFS2_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;
}
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index 0ca9191..9b11cab 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -173,13 +173,15 @@ static int clear_dup_metalist(struct gfs2_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",
- dh->id->name ? dh->id->name : "unknown name",
- ip->i_di.di_num.no_addr, block);
+ log_err("Found duplicate reference in inode \"%s\" at block #%"
+ PRIu64 " (0x%" PRIx64 ") to block #%" PRIu64 " (0x%"
+ PRIx64 ")\n",
+ dh->id->name ? dh->id->name : "unknown name",
+ ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
+ block, block);
log_err("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n",
- dh->id->name ? dh->id->name : "",
- dh->id->parent, dh->id->parent);
+ dh->id->name ? dh->id->name : "", dh->id->parent,
+ dh->id->parent);
inode_hash_remove(inode_hash, ip->i_di.di_num.no_addr);
/* Setting the block to invalid means the inode is
* cleared in pass2 */
@@ -191,29 +193,9 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
static int clear_dup_data(struct gfs2_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\" for block #%" PRIu64
- " (0x%" PRIx64 ") at block #%" PRIu64 " (0x%" PRIx64 ")\n",
- dh->id->name ? dh->id->name : "unknown name",
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr, block,
- block);
- log_err("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n",
- dh->id->name ? dh->id->name : "", dh->id->parent,
- dh->id->parent);
- inode_hash_remove(inode_hash, ip->i_di.di_num.no_addr);
- /* Setting the block to invalid means the inode is
- * cleared in pass2 */
- gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
- gfs2_meta_inval);
- }
-
- return 0;
+ return clear_dup_metalist(ip, block, NULL, private);
}
+
static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block,
uint64_t parent, struct gfs2_buffer_head **bh,
enum update_flags *want_updated,
@@ -361,15 +343,15 @@ int find_block_ref(struct gfs2_sbd *sbp, uint64_t inode, struct dup_blocks *b)
enum update_flags update;
ip = fsck_load_inode(sbp, inode); /* bread, inode_get */
- log_info("Checking inode %" PRIu64 " (0x%" PRIx64
- ")'s metatree for references to block %" PRIu64 " (0x%" PRIx64
- ")\n", inode, inode, b->block_no, b->block_no);
+ log_debug("Checking inode %" PRIu64 " (0x%" PRIx64 ")'s "
+ "metatree for references to block %" PRIu64" (0x% " PRIx64
+ ")\n", inode, inode, b->block_no, b->block_no);
if(check_metatree(ip, &find_refs)) {
stack;
fsck_inode_put(ip, not_updated); /* out, brelse, free */
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, &update, &find_refs) < 0){
stack;
@@ -421,18 +403,74 @@ int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b)
dh.ref_inode_count++;
dh.ref_count += id->dup_count;
}
+ /* 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) {
+ struct gfs2_buffer_head *bh;
+ uint32_t cmagic;
+
+ bh = bread(&sbp->buf_list, b->block_no);
+ cmagic = ((struct gfs2_meta_header *)(bh->b_data))->mh_magic;
+ brelse(bh, not_updated);
+ if (be32_to_cpu(cmagic) == GFS2_MAGIC) {
+ tmp = b->ref_inode_list.next;
+ id = osi_list_entry(tmp, struct inode_with_dups, list);
+ log_warn("Inode %s (%lld/0x%llx) has a reference to "
+ "data block %"PRIu64" (0x%" PRIx64 "), but "
+ "the block is really metadata.\n",
+ id->name, id->block_no, id->block_no,
+ b->block_no, b->block_no);
+ errors_found++;
+ if (query(&opts, "Clear the inode? (y/n) ")) {
+ errors_corrected++;
+ log_warn("Clearing inode %lld (0x%llx)...\n",
+ (unsigned long long)id->block_no,
+ (unsigned long long)id->block_no);
+ ip = fsck_load_inode(sbp, id->block_no);
+ inode_hash_remove(inode_hash,
+ ip->i_di.di_num.no_addr);
+ /* Setting the block to invalid means the inode
+ is cleared in pass2 */
+ gfs2_block_set(ip->i_sbd, bl,
+ ip->i_di.di_num.no_addr,
+ gfs2_meta_inval);
+ fsck_inode_put(ip, updated);
+ } else {
+ log_warn("The bad inode was not cleared.");
+ }
+ return 0;
+ }
+ }
+
log_notice("Block %" PRIu64 " (0x%" PRIx64 ") has %d inodes referencing it"
" for a total of %d duplicate references\n",
b->block_no, b->block_no, dh.ref_inode_count,
- dh.ref_inode_count, dh.ref_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 (%lld/0x%llx) has %d reference(s) to "
+ "block %"PRIu64" (0x%" PRIx64 ")\n",
+ id->name, id->block_no, id->block_no,
+ id->dup_count, b->block_no, b->block_no);
+ }
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
- " (0x%" PRIx64 ")\n", id->name, id->dup_count, b->block_no,
- b->block_no);
- /* FIXME: User input */
- log_warn("Clearing...\n");
+ errors_found++;
+ if (!(query(&opts, "Okay to clear inode %lld (0x%llx)? (y/n) ",
+ id->block_no, id->block_no))) {
+ log_warn("The bad inode was not cleared...\n");
+ continue;
+ }
+ errors_corrected++;
+ log_warn("Clearing inode %lld (0x%llx)...\n",
+ (unsigned long long)id->block_no,
+ (unsigned long long)id->block_no);
ip = fsck_load_inode(sbp, id->block_no);
dh.b = b;
dh.id = id;
@@ -443,7 +481,9 @@ int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b)
if(!id->ea_only)
check_metatree(ip, &clear_dup_fxns);
- fsck_inode_put(ip, not_updated); /* out, brelse, free */
+ gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
+ gfs2_meta_inval);
+ fsck_inode_put(ip, updated); /* out, brelse, free */
dh.ref_inode_count--;
if(dh.ref_inode_count == 1)
break;
@@ -519,13 +559,10 @@ int pass1b(struct gfs2_sbd *sbp)
* it later */
log_info("Handling duplicate blocks\n");
out:
- while (!osi_list_empty(&sbp->dup_blocks.list)) {
- b = osi_list_entry(sbp->dup_blocks.list.next,
- struct dup_blocks, list);
+ osi_list_foreach_safe(tmp, &sbp->dup_blocks.list, x) {
+ b = osi_list_entry(tmp, struct dup_blocks, list);
if (!skip_this_pass && !rc) /* no error & not asked to skip the rest */
handle_dup_blk(sbp, b);
- osi_list_del(&b->list);
- free(b);
}
return rc;
}
diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c
index ac3ed27..4736433 100644
--- a/gfs2/fsck/pass1c.c
+++ b/gfs2/fsck/pass1c.c
@@ -26,52 +26,95 @@ static int remove_eattr_entry(struct gfs2_sbd *sdp,
struct gfs2_ea_header *curr,
struct gfs2_ea_header *prev)
{
- log_warn("Removing EA located in block #%"PRIu64" (0x%" PRIx64 ").\n",
- leaf_bh->b_blocknr, leaf_bh->b_blocknr);
- if(!prev)
+ if (!prev)
curr->ea_type = GFS2_EATYPE_UNUSED;
else {
- prev->ea_rec_len =
- cpu_to_be32(be32_to_cpu(curr->ea_rec_len) +
- be32_to_cpu(prev->ea_rec_len));
+ uint32_t tmp32 = be32_to_cpu(curr->ea_rec_len) +
+ be32_to_cpu(prev->ea_rec_len);
+ prev->ea_rec_len = cpu_to_be32(tmp32);
if (curr->ea_flags & GFS2_EAFLAG_LAST)
prev->ea_flags |= GFS2_EAFLAG_LAST;
}
+ log_err("Bad Extended Attribute at block #%"PRIu64
+ " (0x%" PRIx64 ") removed.\n",
+ leaf_bh->b_blocknr, leaf_bh->b_blocknr);
return 0;
}
-int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
+static int ask_remove_eattr_entry(struct gfs2_sbd *sdp,
+ struct gfs2_buffer_head *leaf_bh,
+ struct gfs2_ea_header *curr,
+ struct gfs2_ea_header *prev,
+ int fix_curr, int fix_curr_len)
+{
+ errors_found++;
+ if (query(&opts, "Remove the bad Extended Attribute entry? "
+ "(y/n) ")) {
+ errors_corrected++;
+ if (fix_curr)
+ curr->ea_flags |= GFS2_EAFLAG_LAST;
+ if (fix_curr_len) {
+ uint32_t max_size = sdp->sd_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 gfs2_inode *ip,
+ enum update_flags *need_update)
+{
+ errors_found++;
+ if (query(&opts, "Remove the bad Extended Attribute? (y/n) ")) {
+ errors_corrected++;
+ ip->i_di.di_eattr = 0;
+ *need_update = updated;
+ log_err("Bad Extended Attribute removed.\n");
+ } else
+ log_err("Bad Extended Attribute not removed.\n");
+ return 1;
+}
+
+static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
uint64_t parent, struct gfs2_buffer_head **bh,
- enum update_flags *update, void *private)
+ enum update_flags *need_update, void *private)
{
struct gfs2_sbd *sbp = ip->i_sbd;
struct gfs2_block_query q;
struct gfs2_buffer_head *indir_bh = NULL;
- *update = not_updated;
+ *need_update = not_updated;
if(gfs2_check_range(sbp, block)) {
- log_err("Extended attributes indirect block #%"PRIu64
- " (0x%" PRIx64 ") for inode #%" PRIu64
- " (0x%" PRIx64 ") out of range...removing\n",
- block, block, ip->i_di.di_num.no_addr,
- ip->i_di.di_num.no_addr);
- ip->i_di.di_eattr = 0;
- *update = (opts.no ? not_updated : updated);
- return 1;
+ log_err("Extended attributes indirect block #%llu"
+ " (0x%llx) for inode #%llu"
+ " (0x%llx) out of range...removing\n",
+ (unsigned long long)block,
+ (unsigned long long)block,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ return ask_remove_eattr(ip, need_update);
}
else if (gfs2_block_check(sbp, bl, block, &q)) {
stack;
return -1;
}
else if(q.block_type != gfs2_indir_blk) {
- log_err("Extended attributes indirect block #%"PRIu64
- " (0x%" PRIx64 ") for inode #%" PRIu64
- " (0x%" PRIx64 ") invalid...removing\n",
- block, block, ip->i_di.di_num.no_addr,
- ip->i_di.di_num.no_addr);
- ip->i_di.di_eattr = 0;
- *update = (opts.no ? not_updated : updated);
- return 1;
+ log_err("Extended attributes indirect block #%llu"
+ " (0x%llx) for inode #%llu"
+ " (0x%llx) invalid.\n",
+ (unsigned long long)block,
+ (unsigned long long)block,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ return ask_remove_eattr(ip, need_update);
}
else
indir_bh = bread(&sbp->buf_list, block);
@@ -80,33 +123,30 @@ int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
return 0;
}
-int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
+static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
uint64_t parent, struct gfs2_buffer_head **bh,
- enum update_flags *update, void *private)
+ enum update_flags *need_update, void *private)
{
struct gfs2_sbd *sbp = ip->i_sbd;
struct gfs2_block_query q;
- *update = not_updated;
if(gfs2_check_range(sbp, block)) {
- log_err("Extended attributes block for inode #%" PRIu64
- " (0x%" PRIx64 ") out of range...removing\n",
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
- ip->i_di.di_eattr = 0;
- *update = (opts.no ? not_updated : updated);
- return 1;
+ log_err("Extended attributes block for inode #%llu"
+ " (0x%llx) out of range.\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ return ask_remove_eattr(ip, need_update);
}
else if (gfs2_block_check(sbp, bl, block, &q)) {
stack;
return -1;
}
else if(q.block_type != gfs2_meta_eattr) {
- log_err("Extended attributes block for inode #%"PRIu64
- " (0x%" PRIx64 ") invalid...removing\n",
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
- ip->i_di.di_eattr = 0;
- *update = (opts.no ? not_updated : updated);
- return 1;
+ log_err("Extended attributes block for inode #%llu"
+ " (0x%llx) invalid.\n",
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ return ask_remove_eattr(ip, need_update);
}
else
*bh = bread(&sbp->buf_list, block);
@@ -128,41 +168,24 @@ static int check_eattr_entry(struct gfs2_inode *ip,
if(!ea_hdr->ea_name_len){
log_err("EA has name length == 0\n");
- ea_hdr->ea_flags |= GFS2_EAFLAG_LAST;
- ea_hdr->ea_rec_len = cpu_to_be32(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 + be32_to_cpu(ea_hdr->ea_rec_len) > max_size){
log_err("EA rec length too long\n");
- ea_hdr->ea_flags |= GFS2_EAFLAG_LAST;
- ea_hdr->ea_rec_len = cpu_to_be32(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 + be32_to_cpu(ea_hdr->ea_rec_len) == max_size &&
(ea_hdr->ea_flags & GFS2_EAFLAG_LAST) == 0){
log_err("last EA has no last entry flag\n");
- ea_hdr->ea_flags |= GFS2_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));
@@ -173,11 +196,8 @@ static int check_eattr_entry(struct gfs2_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, GFS2_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){
@@ -188,24 +208,22 @@ static int check_eattr_entry(struct gfs2_inode *ip,
max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len)+avail_size-1)/avail_size;
if(max_ptrs > ea_hdr->ea_num_ptrs){
- log_err("EA (%s) has incorrect number of pointers.\n", ea_name);
- log_err(" Required: %d\n"
- " Reported: %d\n",
+ log_err("EA (%s) has incorrect number of pointers.\n",
+ ea_name);
+ 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);
+ log_debug(" Pointers Required: %d\n"
+ "Pointers Reported: %d\n",
+ max_ptrs, ea_hdr->ea_num_ptrs);
}
}
return 0;
}
-int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_ptr,
+static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_ptr,
struct gfs2_buffer_head *leaf_bh,
struct gfs2_ea_header *ea_hdr,
struct gfs2_ea_header *ea_hdr_prev,
@@ -214,7 +232,6 @@ int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_ptr,
struct gfs2_block_query q;
struct gfs2_sbd *sbp = ip->i_sbd;
- *want_updated = not_updated;
if(gfs2_block_check(sbp, bl, be64_to_cpu(*ea_ptr), &q)) {
stack;
return -1;
@@ -252,18 +269,20 @@ int pass1c(struct gfs2_sbd *sbp)
osi_list_foreach_safe(tmp, &sbp->eattr_blocks.list, x) {
ea_block = osi_list_entry(tmp, struct special_blocks, list);
block_no = ea_block->block;
+ warm_fuzzy_stuff(block_no);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return FSCK_OK;
bh = bread(&sbp->buf_list, block_no);
- if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) { /* if a dinode */
+ if (!gfs2_check_meta(bh, GFS2_METATYPE_DI)) { /* if a dinode */
log_info("EA in inode %"PRIu64" (0x%" PRIx64 ")\n",
block_no, block_no);
- gfs2_block_clear(sbp, bl, block_no, gfs2_eattr_block);
+ gfs2_block_unmark(sbp, bl, block_no, gfs2_eattr_block);
ip = fsck_inode_get(sbp, bh);
- log_debug("Found eattr at %"PRIu64" (0x%" PRIx64 ")\n",
- ip->i_di.di_eattr, ip->i_di.di_eattr);
+ log_debug("Found eattr at %llu (0x%llx)\n",
+ (unsigned long long)ip->i_di.di_eattr,
+ (unsigned long long)ip->i_di.di_eattr);
/* FIXME: Handle walking the eattr here */
error = check_inode_eattr(ip, &want_updated,
&pass1c_fxns);
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 307b6ff..c664226 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -105,10 +105,11 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
const char *de_type_string(uint8_t de_type)
{
- const char *de_types[15] = {"unknown", "fifo", "chrdev", "invalid",
- "directory", "invalid", "blkdev", "invalid",
- "file", "invalid", "symlink", "invalid",
- "socket", "invalid", "wht"};
+ const char *de_types[15] =
+ {"unknown", "fifo", "chrdev", "invalid",
+ "directory", "invalid", "blkdev", "invalid",
+ "file", "invalid", "symlink", "invalid",
+ "socket", "invalid", "wht"};
if (de_type < 15)
return de_types[de_type];
return de_types[3]; /* invalid */
@@ -153,6 +154,14 @@ static int check_file_type(uint8_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 gfs2_inode *ip, struct gfs2_dirent *dent,
@@ -171,7 +180,6 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent dentry, *de;
uint32_t calculated_hash;
- *update = not_updated;
memset(&dentry, 0, sizeof(struct gfs2_dirent));
gfs2_dirent_in(&dentry, (char *)dent);
de = &dentry;
@@ -253,27 +261,23 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_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);
errors_found++;
- if(query(&opts, "Clear entry to inode containing bad blocks? (y/n)")) {
-
+ if(query(&opts, "Delete inode containing bad blocks? (y/n)")) {
errors_corrected++;
entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr);
- check_inode_eattr(entry_ip, update, &clear_eattrs);
- fsck_inode_put(entry_ip, not_updated);
-
- /* FIXME: make sure all blocks referenced by
- * this inode are cleared in the bitmap */
-
+ check_inode_eattr(entry_ip, update,
+ &pass2_fxns_delete);
+ check_metatree(entry_ip, &pass2_fxns_delete);
+ fsck_inode_put(entry_ip, updated);
dirent2_del(ip, bh, prev_de, dent);
-
gfs2_block_set(sbp, bl, de->de_inum.no_addr,
- gfs2_meta_inval);
+ gfs2_block_free);
*update = updated;
+ log_warn("The inode containing bad blocks was "
+ "deleted.\n");
return 1;
} else {
log_warn("Entry to inode containing bad blocks remains\n");
@@ -287,21 +291,45 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
q.block_type != gfs2_inode_lnk && q.block_type != gfs2_inode_blk &&
q.block_type != gfs2_inode_chr && q.block_type != gfs2_inode_fifo &&
q.block_type != gfs2_inode_sock) {
- log_err("Directory entry '%s' at block %" PRIu64 " (0x%" PRIx64
- ") in dir inode %" PRIu64 " (0x%" PRIx64
- ") has an invalid block type: %d.\n", tmp_name,
+ log_err("Directory entry '%s' for block %" PRIu64
+ " (0x%" PRIx64 ") in dir inode %" PRIu64 " (0x%" PRIx64
+ ") block type %d: %s.\n", tmp_name,
de->de_inum.no_addr, de->de_inum.no_addr,
ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
- q.block_type);
+ q.block_type, q.block_type == gfs2_meta_inval ?
+ "previously marked invalid" : "is not an inode");
errors_found++;
- if(query(&opts, "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(&opts, "Clear directory entry to non-inode block? "
+ "(y/n) ")) {
+ struct gfs2_buffer_head *bhi;
+
errors_corrected++;
dirent2_del(ip, bh, prev_de, dent);
*update = updated;
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 != gfs2_meta_inval)
+ return 1;
+
+ /* Now try to clear the dinode, if it is an dinode */
+ bhi = bread(&sbp->buf_list, de->de_inum.no_addr);
+ error = gfs2_check_meta(bhi, GFS2_METATYPE_DI);
+ brelse(bhi, updated);
+ if (error)
+ return 1; /* not a dinode: nothing to delete */
+
+ entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr);
+ check_inode_eattr(entry_ip, update,
+ &pass2_fxns_delete);
+ check_metatree(entry_ip, &pass2_fxns_delete);
+ fsck_inode_put(entry_ip, updated);
+ gfs2_block_set(sbp, bl, de->de_inum.no_addr,
+ gfs2_block_free);
+
return 1;
} else {
log_err("Directory entry to non-inode block remains\n");
@@ -331,6 +359,7 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
dirent2_del(ip, bh, prev_de, dent);
*update = updated;
+ log_err("Stale directory entry deleted\n");
return 1;
} else {
log_err("Stale directory entry remains\n");
@@ -376,12 +405,16 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
/* check that '.' refers to this inode */
if(de->de_inum.no_addr != ip->i_di.di_num.no_addr) {
- log_err("'.' entry's value incorrect in directory %" PRIu64
- " (0x%" PRIx64 "). Points to %"PRIu64
- " (0x%" PRIx64 ") when it should point to %" PRIu64
- " (0x%" PRIx64 ").\n",
- de->de_inum.no_addr, de->de_inum.no_addr,
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
+ log_err("'.' entry's value incorrect in directory %llu"
+ " (0x%llx). Points to %llu"
+ " (0x%llx) when it should point to %llu"
+ " (0x%llx).\n",
+ (unsigned long long)de->de_inum.no_addr,
+ (unsigned long long)de->de_inum.no_addr,
+ (unsigned long long)de->de_inum.no_addr,
+ (unsigned long long)de->de_inum.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
errors_found++;
if(query(&opts, "Remove '.' reference? (y/n) ")) {
errors_corrected++;
@@ -587,27 +620,33 @@ int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
}
if(!ds.dotdir) {
log_err("No '.' entry found for %s directory.\n", dirname);
- sprintf(tmp_name, ".");
- filename_len = strlen(tmp_name); /* no trailing NULL */
- if(!(filename = malloc(sizeof(char) * filename_len))) {
- log_err("Unable to allocate name string\n");
- stack;
- return -1;
- }
- if(!(memset(filename, 0, sizeof(char) * filename_len))) {
- log_err("Unable to zero name string\n");
- stack;
- return -1;
- }
- memcpy(filename, tmp_name, filename_len);
- log_warn("Adding '.' entry\n");
- dir_add(sysinode, filename, filename_len,
+ errors_found++;
+ if (query(&opts, "Is it okay to add '.' entry? (y/n) ")) {
+ errors_corrected++;
+ sprintf(tmp_name, ".");
+ filename_len = strlen(tmp_name); /* no trailing NULL */
+ if(!(filename = malloc(sizeof(char) * filename_len))) {
+ log_err("Unable to allocate name string\n");
+ stack;
+ return -1;
+ }
+ if(!(memset(filename, 0, sizeof(char) *
+ filename_len))) {
+ log_err("Unable to zero name string\n");
+ stack;
+ return -1;
+ }
+ memcpy(filename, tmp_name, filename_len);
+ log_warn("Adding '.' entry\n");
+ dir_add(sysinode, filename, filename_len,
&(sysinode->i_di.di_num), DT_DIR);
- increment_link(sysinode->i_sbd,
- sysinode->i_di.di_num.no_addr);
- ds.entry_count++;
- free(filename);
- update = 1;
+ increment_link(sysinode->i_sbd,
+ sysinode->i_di.di_num.no_addr);
+ ds.entry_count++;
+ free(filename);
+ update = 1;
+ } else
+ log_err("The directory was not fixed.\n");
}
if(sysinode->i_di.di_entries != ds.entry_count) {
log_err("%s inode %" PRIu64 " (0x%" PRIx64
@@ -666,11 +705,13 @@ int pass2(struct gfs2_sbd *sbp)
struct gfs2_block_query q;
struct dir_status ds = {0};
struct gfs2_inode *ip;
- struct gfs2_buffer_head b, *bh = &b;
+ struct gfs2_buffer_head *bh = NULL;
char *filename;
int filename_len;
char tmp_name[256];
int error = 0;
+ enum update_flags need_update = NOT_UPDATED;
+ struct dup_blocks *b;
/* Check all the system directory inodes. */
if (check_system_dir(sbp->md.jiinode, "jindex", build_jindex)) {
@@ -692,6 +733,7 @@ int pass2(struct gfs2_sbd *sbp)
log_info("Checking directory inodes.\n");
/* Grab each directory inode, and run checks on it */
for(i = 0; i < last_fs_block; i++) {
+ need_update = 0;
warm_fuzzy_stuff(i);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return FSCK_OK;
@@ -719,8 +761,8 @@ int pass2(struct gfs2_sbd *sbp)
* is valid */
ip = fsck_load_inode(sbp, i);
if(check_metatree(ip, &pass2_fxns)) {
+ fsck_inode_put(ip, not_updated);
stack;
- free(ip);
return FSCK_ERROR;
}
fsck_inode_put(ip, not_updated);
@@ -766,41 +808,73 @@ int pass2(struct gfs2_sbd *sbp)
bh = bread(&sbp->buf_list, i);
ip = fsck_inode_get(sbp, bh);
if(!ds.dotdir) {
- log_err("No '.' entry found\n");
- sprintf(tmp_name, ".");
- filename_len = strlen(tmp_name); /* no trailing NULL */
- if(!(filename = malloc(sizeof(char) * filename_len))) {
- log_err("Unable to allocate name string\n");
- stack;
- return FSCK_ERROR;
- }
- if(!memset(filename, 0, sizeof(char) * filename_len)) {
- log_err("Unable to zero name string\n");
- stack;
- return FSCK_ERROR;
- }
- memcpy(filename, tmp_name, filename_len);
-
- dir_add(ip, filename, filename_len, &(ip->i_di.di_num), DT_DIR);
- increment_link(ip->i_sbd, ip->i_di.di_num.no_addr);
- ds.entry_count++;
- free(filename);
+ log_err("No '.' entry found for directory inode at "
+ "block %"PRIu64" (0x%" PRIx64 ")\n", i, i);
+ errors_found++;
+ if (query(&opts,
+ "Is it okay to add '.' entry? (y/n) ")) {
+ errors_corrected++;
+ sprintf(tmp_name, ".");
+ filename_len = strlen(tmp_name); /* no trailing
+ NULL */
+ if(!(filename = malloc(sizeof(char) *
+ filename_len))) {
+ log_err("Unable to allocate name\n");
+ stack;
+ return FSCK_ERROR;
+ }
+ if(!memset(filename, 0, sizeof(char) *
+ filename_len)) {
+ log_err("Unable to zero name\n");
+ stack;
+ return FSCK_ERROR;
+ }
+ memcpy(filename, tmp_name, filename_len);
+
+ dir_add(ip, filename, filename_len,
+ &(ip->i_di.di_num), DT_DIR);
+ increment_link(ip->i_sbd,
+ ip->i_di.di_num.no_addr);
+ ds.entry_count++;
+ free(filename);
+ log_err("The directory was fixed.\n");
+ need_update = UPDATED;
+ } else {
+ log_err("The directory was not fixed.\n");
+ }
}
- fsck_inode_put(ip, not_updated); /* does a brelse */
- bh = bread(&sbp->buf_list, i);
- ip = fsck_inode_get(sbp, bh);
if(ip->i_di.di_entries != ds.entry_count) {
- log_err("Entries is %d - should be %d for inode block %" PRIu64
- " (0x%" PRIx64 ")\n",
- ip->i_di.di_entries, ds.entry_count,
- ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
- ip->i_di.di_entries = ds.entry_count;
- fsck_inode_put(ip, updated); /* does a gfs2_dinode_out, brelse */
+ log_err("Entries is %d - should be %d for inode "
+ "block %" PRIu64 " (0x%" PRIx64 ")\n",
+ ip->i_di.di_entries, ds.entry_count,
+ ip->i_di.di_num.no_addr,
+ ip->i_di.di_num.no_addr);
+ errors_found++;
+ if (query(&opts,
+ "Fix the entry count? (y/n) ")) {
+ errors_corrected++;
+ ip->i_di.di_entries = ds.entry_count;
+ need_update = UPDATED;
+ } else {
+ log_err("The entry count was not fixed.\n");
+ }
}
- else
- fsck_inode_put(ip, not_updated); /* does a brelse */
+ fsck_inode_put(ip, need_update); /* does a gfs2_dinode_out,
+ brelse */
+ }
+ /* Now that we've deleted the inodes marked "bad" we can safely
+ get rid of the duplicate block list. If we do it any sooner,
+ we won't discover that a given block is a duplicate and avoid
+ deleting it from both inodes referencing it. Note: The other
+ returns from this function are premature exits of the program
+ and gfs2_block_list_destroy should get rid of the list for us. */
+ while (!osi_list_empty(&sbp->dup_blocks.list)) {
+ b = osi_list_entry(sbp->dup_blocks.list.next,
+ struct dup_blocks, list);
+ osi_list_del(&b->list);
+ free(b);
}
return FSCK_OK;
}
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index b61cc8d..a78d81e 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -283,10 +283,11 @@ int pass3(struct gfs2_sbd *sbp)
return FSCK_ERROR;
}
log_warn("Directory relinked to lost+found\n");
+ fsck_inode_put(ip, updated);
} else {
log_err("Unlinked directory remains unlinked\n");
+ fsck_inode_put(ip, not_updated);
}
- fsck_inode_put(ip, not_updated);
break;
}
else {
diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c
index ce0e562..a06647b 100644
--- a/gfs2/fsck/pass4.c
+++ b/gfs2/fsck/pass4.c
@@ -19,6 +19,15 @@
#include "fsck.h"
#include "lost_n_found.h"
#include "inode_hash.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 */
@@ -65,11 +74,19 @@ int scan_inode_list(struct gfs2_sbd *sbp, osi_list_t *list) {
return -1;
}
if(q.bad_block) {
- log_err("Unlinked inode contains bad blocks\n", ii->inode);
+ log_err("Unlinked inode %llu (0x%llx) contains"
+ "bad blocks\n",
+ (unsigned long long)ii->inode,
+ (unsigned long long)ii->inode);
errors_found++;
- if(query(&opts,
- "Clear unlinked inode with bad blocks? (y/n) ")) {
+ if(query(&opts, "Delete unlinked inode with "
+ "bad blocks? (y/n) ")) {
errors_corrected++;
+ ip = fsck_load_inode(sbp, ii->inode);
+ check_inode_eattr(ip, &f,
+ &pass4_fxns_delete);
+ check_metatree(ip, &pass4_fxns_delete);
+ fsck_inode_put(ip, updated);
gfs2_block_set(sbp, bl, ii->inode,
gfs2_block_free);
continue;
@@ -83,10 +100,22 @@ int scan_inode_list(struct gfs2_sbd *sbp, osi_list_t *list) {
q.block_type != gfs2_inode_chr &&
q.block_type != gfs2_inode_fifo &&
q.block_type != gfs2_inode_sock) {
- log_err("Unlinked block marked as inode not an inode\n");
- gfs2_block_set(sbp, bl, ii->inode,
- gfs2_block_free);
- log_err("Cleared\n");
+ log_err("Unlinked block marked as inode is "
+ "not an inode (%d)\n", q.block_type);
+ ip = fsck_load_inode(sbp, ii->inode);
+ if(query(&opts, "Delete unlinked inode "
+ "? (y/n) ")) {
+ check_inode_eattr(ip, &f,
+ &pass4_fxns_delete);
+ check_metatree(ip, &pass4_fxns_delete);
+ fsck_inode_put(ip, updated);
+ gfs2_block_set(sbp, bl, ii->inode,
+ gfs2_block_free);
+ log_err("The inode was deleted\n");
+ } else {
+ log_err("The inode was not deleted\n");
+ fsck_inode_put(ip, not_updated);
+ }
continue;
}
ip = fsck_load_inode(sbp, ii->inode);
diff --git a/gfs2/fsck/pass5.c b/gfs2/fsck/pass5.c
index 27f7251..ec89adf 100644
--- a/gfs2/fsck/pass5.c
+++ b/gfs2/fsck/pass5.c
@@ -72,6 +72,7 @@ int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int buflen,
unsigned char rg_status, block_status;
struct gfs2_block_query q;
uint64_t block;
+ static int free_unlinked = -1;
/* FIXME verify cast */
byte = (unsigned char *) buffer;
@@ -95,19 +96,51 @@ int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int buflen,
So we ignore it. */
if (rg_status == GFS2_BLKST_UNLINKED &&
block_status == GFS2_BLKST_FREE) {
- log_warn("Unlinked block found at block %"
- PRIu64" (0x%" PRIx64 "), left unchanged.\n",
- block, block);
+ errors_found++;
+ if (free_unlinked == -1) {
+ log_err("Unlinked inode block found at block %"
+ "llu (0x%llx).\n",
+ (unsigned long long)block,
+ (unsigned long long)block);
+ if(query(&opts, "Do you want me to fix the "
+ "bitmap for all unlinked blocks? "
+ "(y/n) "))
+ free_unlinked = 1;
+ else
+ free_unlinked = 0;
+ }
+ if (free_unlinked) {
+ if(gfs2_set_bitmap(sbp, block, block_status))
+ log_err("Unlinked block %llu (0x%llx)"
+ " bitmap not fixed.\n",
+ (unsigned long long)block,
+ (unsigned long long)block);
+ else {
+ log_err("Unlinked block %llu (0x%llx)"
+ " bitmap fixed.\n",
+ (unsigned long long)block,
+ (unsigned long long)block);
+ errors_corrected++;
+ }
+ } else {
+ log_info("Unlinked block found at block %"
+ PRIu64" (0x%" PRIx64 "), left "
+ "unchanged.\n", block, block);
+ }
} else if (rg_status != block_status) {
- const char *blockstatus[] = {"Free", "Data", "Unlinked", "inode"};
+ const char *blockstatus[] = {"Free", "Data",
+ "Unlinked", "inode"};
log_err("Ondisk and fsck bitmaps differ at"
- " block %"PRIu64" (0x%" PRIx64 ") \n", block, 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]);
+ " block %"PRIu64" (0x%" PRIx64 ") \n",
+ block, 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));
+ block_type_string(&q));
errors_found++;
if(query(&opts, "Fix bitmap for block %"
diff --git a/gfs2/fsck/rgrepair.c b/gfs2/fsck/rgrepair.c
index e11b72b..e8429a6 100644
--- a/gfs2/fsck/rgrepair.c
+++ b/gfs2/fsck/rgrepair.c
@@ -429,7 +429,7 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count)
{
int error, descrepencies;
osi_list_t expected_rglist;
- int calc_rg_count, rgcount_from_index, rg;
+ int calc_rg_count = 0, rgcount_from_index, rg;
osi_list_t *exp, *act; /* expected, actual */
struct gfs2_rindex buf;
diff --git a/gfs2/libgfs2/block_list.c b/gfs2/libgfs2/block_list.c
index 67de5e1..9fc3ac5 100644
--- a/gfs2/libgfs2/block_list.c
+++ b/gfs2/libgfs2/block_list.c
@@ -174,12 +174,13 @@ int gfs2_block_mark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
return err;
}
-int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
- uint64_t block, enum gfs2_mark_block m)
+/* gfs2_block_unmark clears ONE mark for the given block */
+int gfs2_block_unmark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
+ uint64_t block, enum gfs2_mark_block mark)
{
int err = 0;
- switch (m) {
+ switch (mark) {
case gfs2_dup_block:
gfs2_dup_clear(&sdp->dup_blocks, block);
break;
@@ -197,12 +198,25 @@ int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
return err;
}
+/* gfs2_block_clear clears all the marks for the given block */
+int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
+ uint64_t block)
+{
+ int err = 0;
+
+ gfs2_dup_clear(&sdp->dup_blocks, block);
+ gfs2_special_clear(&sdp->bad_blocks, block);
+ gfs2_special_clear(&sdp->eattr_blocks, block);
+ err = gfs2_bitmap_clear(&il->list.gbmap.group_map, block);
+ return err;
+}
+
int gfs2_block_set(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
uint64_t block, enum gfs2_mark_block mark)
{
int err;
- err = gfs2_block_clear(sdp, il, block, mark);
+ err = gfs2_block_clear(sdp, il, block); /* clear all block status */
if(!err)
err = gfs2_block_mark(sdp, il, block, mark);
return err;
@@ -216,15 +230,15 @@ int gfs2_block_check(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
val->bad_block = 0;
val->dup_block = 0;
val->eattr_block = 0;
- if((err = gfs2_bitmap_get(&il->list.gbmap.group_map, block,
- &val->block_type)))
- return err;
if (blockfind(&sdp->bad_blocks, block))
val->bad_block = 1;
if (dupfind(&sdp->dup_blocks, block))
val->dup_block = 1;
if (blockfind(&sdp->eattr_blocks, block))
val->eattr_block = 1;
+ if((err = gfs2_bitmap_get(&il->list.gbmap.group_map, block,
+ &val->block_type)))
+ return err;
return 0;
}
diff --git a/gfs2/libgfs2/fs_bits.c b/gfs2/libgfs2/fs_bits.c
index 4cd41b4..b295261 100644
--- a/gfs2/libgfs2/fs_bits.c
+++ b/gfs2/libgfs2/fs_bits.c
@@ -201,3 +201,64 @@ int gfs2_set_bitmap(struct gfs2_sbd *sdp, uint64_t blkno, int state)
gfs2_rgrp_relse(rgd, updated);
return 0;
}
+
+/*
+ * fs_get_bitmap - get value of FS bitmap
+ * @sdp: super block
+ * @blkno: block number relative to file system
+ *
+ * This function gets the value of a bit of the
+ * file system bitmap.
+ * Possible state values for a block in the bitmap are:
+ * GFS_BLKST_FREE (0)
+ * GFS_BLKST_USED (1)
+ * GFS_BLKST_INVALID (2)
+ * GFS_BLKST_DINODE (3)
+ *
+ * Returns: state on success, -1 on error
+ */
+int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
+ struct rgrp_list *rgd)
+{
+ int buf, val;
+ uint32_t rgrp_block;
+ struct gfs2_bitmap *bits = NULL;
+ unsigned int bit;
+ unsigned char *byte;
+ int local_rgd = 0;
+
+ if(gfs2_check_range(sdp, blkno))
+ return -1;
+ if(rgd == NULL) {
+ local_rgd = 1;
+ rgd = gfs2_blk2rgrpd(sdp, blkno);
+ }
+ if(rgd == NULL)
+ return -1;
+ if(gfs2_rgrp_read(sdp, rgd))
+ return -1;
+
+ rgrp_block = (uint32_t)(blkno - rgd->ri.ri_data0);
+
+ for(buf= 0; buf < rgd->ri.ri_length; buf++){
+ bits = &(rgd->bits[buf]);
+ if(rgrp_block < ((bits->bi_start + bits->bi_len)*GFS2_NBBY)){
+ break;
+ }
+ }
+
+ if(buf >= rgd->ri.ri_length){
+ gfs2_rgrp_relse(rgd, not_updated);
+ return -1;
+ }
+
+ byte = (unsigned char *)(rgd->bh[buf]->b_data + bits->bi_offset) +
+ (rgrp_block/GFS2_NBBY - bits->bi_start);
+ bit = (rgrp_block % GFS2_NBBY) * GFS2_BIT_SIZE;
+
+ val = ((*byte >> bit) & GFS2_BIT_MASK);
+ if(local_rgd)
+ gfs2_rgrp_relse(rgd, not_updated);
+
+ return val;
+}
diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
index 3ccc9c8..9cc6dea 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -695,6 +695,8 @@ void dirent2_del(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
{
uint16_t cur_rec_len, prev_rec_len;
+ if (dip->i_di.di_entries)
+ dip->i_di.di_entries--;
if (!prev) {
cur->de_inum.no_formal_ino = 0;
return;
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index a458feb..64d36d0 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -387,8 +387,12 @@ int gfs2_block_mark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
uint64_t block, enum gfs2_mark_block mark);
int gfs2_block_set(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
uint64_t block, enum gfs2_mark_block mark);
+/* gfs2_block_unmark clears ONE mark for the given block */
+int gfs2_block_unmark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
+ uint64_t block, enum gfs2_mark_block m);
+/* gfs2_block_clear clears all the marks for the given block */
int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
- uint64_t block, enum gfs2_mark_block m);
+ uint64_t block);
int gfs2_block_check(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
uint64_t block, struct gfs2_block_query *val);
void *gfs2_block_list_destroy(struct gfs2_sbd *sdp,
@@ -429,7 +433,7 @@ int gfs2_check_range(struct gfs2_sbd *sdp, uint64_t blkno);
/* functions with blk #'s that are file system relative */
int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
- struct rgrp_list *rgd);
+ struct rgrp_list *rgd);
int gfs2_set_bitmap(struct gfs2_sbd *sdp, uint64_t blkno, int state);
/* fs_geometry.c */
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2009-08-10 16:54 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-08-10 16:54 cluster: RHEL5 - GFS2: fsck.gfs2 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).