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