From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 8218 invoked by alias); 10 Aug 2009 16:52:50 -0000 Received: (qmail 8211 invoked by alias); 10 Aug 2009 16:52:50 -0000 X-SWARE-Spam-Status: No, hits=3.3 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_21,J_CHICKENPOX_22,J_CHICKENPOX_23,J_CHICKENPOX_25,J_CHICKENPOX_26,J_CHICKENPOX_31,J_CHICKENPOX_35,J_CHICKENPOX_41,J_CHICKENPOX_45,J_CHICKENPOX_62,J_CHICKENPOX_63,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Status: No, hits=3.3 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_21,J_CHICKENPOX_22,J_CHICKENPOX_23,J_CHICKENPOX_25,J_CHICKENPOX_26,J_CHICKENPOX_31,J_CHICKENPOX_35,J_CHICKENPOX_41,J_CHICKENPOX_45,J_CHICKENPOX_62,J_CHICKENPOX_63,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Check-By: sourceware.org X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on bastion2.fedora.phx.redhat.com Subject: cluster: STABLE2 - GFS2: fsck.gfs2 sometimes needs to be run twice To: cluster-cvs-relay@redhat.com X-Project: Cluster Project X-Git-Module: cluster.git X-Git-Refname: refs/heads/STABLE2 X-Git-Reftype: branch X-Git-Oldrev: 5dfd1c68fc5821ca877712dac998ce87e1e54b63 X-Git-Newrev: 92cec48f73496aeb7bc6fb480a9decb6dc951a6a From: Bob Peterson Message-Id: <20090810165208.909611201F1@lists.fedorahosted.org> Date: Mon, 10 Aug 2009 16:52:00 -0000 X-Scanned-By: MIMEDefang 2.58 on 172.16.52.254 Mailing-List: contact cluster-cvs-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: cluster-cvs-owner@sourceware.org X-SW-Source: 2009-q3/txt/msg00171.txt.bz2 Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=92cec48f73496aeb7bc6fb480a9decb6dc951a6a Commit: 92cec48f73496aeb7bc6fb480a9decb6dc951a6a Parent: 5dfd1c68fc5821ca877712dac998ce87e1e54b63 Author: Bob Peterson AuthorDate: Mon Aug 10 11:47:02 2009 -0500 Committer: Bob Peterson CommitterDate: Mon Aug 10 11:55:03 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 51be25a..4d2098e 100644 --- a/gfs2/edit/hexedit.c +++ b/gfs2/edit/hexedit.c @@ -423,8 +423,6 @@ void print_usage(void) Erase(); } - - /* ------------------------------------------------------------------------ */ /* get_block_type */ /* returns: metatype if block is a GFS2 structure block type */ @@ -442,67 +440,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 57c452a..8557305 100644 --- a/gfs2/fsck/main.c +++ b/gfs2/fsck/main.c @@ -187,9 +187,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 99104ea..d58db42 100644 --- a/gfs2/fsck/metawalk.c +++ b/gfs2/fsck/metawalk.c @@ -91,6 +91,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) @@ -106,8 +117,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); @@ -130,8 +140,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; @@ -163,12 +205,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", @@ -178,18 +223,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){ @@ -197,12 +252,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 */ @@ -224,9 +293,6 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, stack; return -1; } - /*if(error > 0) { - return 1; - }*/ } } @@ -251,12 +317,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); } @@ -264,7 +330,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"); @@ -273,21 +345,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 @@ -302,41 +395,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%" @@ -346,8 +437,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; } @@ -358,10 +449,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); @@ -390,7 +481,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; } @@ -400,9 +493,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, @@ -415,20 +507,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++; @@ -442,7 +538,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) @@ -450,10 +546,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; } @@ -477,12 +573,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; @@ -507,7 +609,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; @@ -518,9 +621,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); @@ -529,7 +638,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; @@ -538,7 +649,7 @@ static int check_eattr_entries(struct gfs2_inode *ip, be32_to_cpu(ea_hdr->ea_rec_len)); } - return 0; + return error; } /** @@ -546,7 +657,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, @@ -554,23 +665,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; } @@ -587,12 +708,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", @@ -607,32 +732,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; @@ -640,7 +805,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; } @@ -656,21 +821,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; } @@ -709,15 +875,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; @@ -752,6 +926,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); } } @@ -792,8 +967,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]; @@ -801,8 +977,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++) { @@ -836,7 +1021,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) @@ -854,7 +1039,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; @@ -875,7 +1060,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); */ @@ -1023,3 +1208,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 fd8f1b7..0721ed1 100644 --- a/gfs2/fsck/metawalk.h +++ b/gfs2/fsck/metawalk.h @@ -16,6 +16,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 * @@ -66,7 +78,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 ab724aa..89436dd 100644 --- a/gfs2/fsck/pass1.c +++ b/gfs2/fsck/pass1.c @@ -52,7 +52,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 = { @@ -102,27 +103,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; @@ -132,30 +136,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; } @@ -177,30 +285,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; } @@ -238,48 +340,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; } @@ -301,60 +486,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, @@ -362,69 +516,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, @@ -506,6 +618,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)) { @@ -522,7 +635,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) @@ -577,9 +689,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; @@ -594,8 +706,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); @@ -736,13 +848,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, @@ -752,7 +865,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)) { @@ -761,9 +878,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); @@ -782,13 +903,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; } @@ -804,12 +933,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 d8a5190..cc0c33d 100644 --- a/gfs2/fsck/pass1b.c +++ b/gfs2/fsck/pass1b.c @@ -160,13 +160,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 */ @@ -178,29 +180,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, @@ -348,15 +330,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; @@ -408,18 +390,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; @@ -430,7 +468,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; @@ -506,13 +546,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 df827df..2169dc8 100644 --- a/gfs2/fsck/pass1c.c +++ b/gfs2/fsck/pass1c.c @@ -14,52 +14,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); @@ -68,33 +111,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); @@ -116,41 +156,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)); @@ -161,11 +184,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){ @@ -176,24 +196,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, @@ -202,7 +220,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; @@ -240,18 +257,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 68dcd5c..2fe96b6 100644 --- a/gfs2/fsck/pass2.c +++ b/gfs2/fsck/pass2.c @@ -92,10 +92,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 */ @@ -140,6 +141,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, @@ -158,7 +167,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; @@ -240,27 +248,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"); @@ -274,21 +278,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"); @@ -318,6 +346,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"); @@ -363,12 +392,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++; @@ -574,27 +607,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 @@ -653,11 +692,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)) { @@ -679,6 +720,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; @@ -706,8 +748,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); @@ -753,41 +795,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 a5c0202..66d2af9 100644 --- a/gfs2/fsck/pass3.c +++ b/gfs2/fsck/pass3.c @@ -270,10 +270,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 2998e3a..e202f37 100644 --- a/gfs2/fsck/pass4.c +++ b/gfs2/fsck/pass4.c @@ -6,6 +6,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 */ @@ -52,11 +61,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; @@ -70,10 +87,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 60e4e65..24ef2ef 100644 --- a/gfs2/fsck/pass5.c +++ b/gfs2/fsck/pass5.c @@ -59,6 +59,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; @@ -82,19 +83,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 66b7a4e..8c96163 100644 --- a/gfs2/fsck/rgrepair.c +++ b/gfs2/fsck/rgrepair.c @@ -416,7 +416,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 7793981..2d844e0 100644 --- a/gfs2/libgfs2/block_list.c +++ b/gfs2/libgfs2/block_list.c @@ -162,12 +162,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; @@ -185,12 +186,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; @@ -204,15 +218,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 e564e07..0b14637 100644 --- a/gfs2/libgfs2/fs_bits.c +++ b/gfs2/libgfs2/fs_bits.c @@ -188,3 +188,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 58c3f47..ed4216d 100644 --- a/gfs2/libgfs2/fs_ops.c +++ b/gfs2/libgfs2/fs_ops.c @@ -682,6 +682,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 5b69b76..84551d3 100644 --- a/gfs2/libgfs2/libgfs2.h +++ b/gfs2/libgfs2/libgfs2.h @@ -374,8 +374,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, @@ -416,7 +420,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 */