From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6257 invoked by alias); 17 Jun 2009 13:36:04 -0000 Received: (qmail 6251 invoked by alias); 17 Jun 2009 13:36:04 -0000 X-SWARE-Spam-Status: No, hits=2.9 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_21,J_CHICKENPOX_25,J_CHICKENPOX_26,J_CHICKENPOX_36,J_CHICKENPOX_41,J_CHICKENPOX_45,J_CHICKENPOX_48,J_CHICKENPOX_62,J_CHICKENPOX_63,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Status: No, hits=2.9 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_21,J_CHICKENPOX_25,J_CHICKENPOX_26,J_CHICKENPOX_36,J_CHICKENPOX_41,J_CHICKENPOX_45,J_CHICKENPOX_48,J_CHICKENPOX_62,J_CHICKENPOX_63,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Check-By: sourceware.org X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on bastion2.fedora.phx.redhat.com Subject: cluster: STABLE3 - GFS2: fsck.gfs2 sometimes needs to be run twice To: cluster-cvs-relay@redhat.com X-Project: Cluster Project X-Git-Module: cluster.git X-Git-Refname: refs/heads/STABLE3 X-Git-Reftype: branch X-Git-Oldrev: 2a6375988a6fd3a9ff0bc22e174b493d655e2b5c X-Git-Newrev: 992908c4470e8fd57b1dc031bee98e29b05c2049 From: Bob Peterson Message-Id: <20090617133510.74C021201EA@lists.fedorahosted.org> Date: Wed, 17 Jun 2009 13:36: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-q2/txt/msg00544.txt.bz2 Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=992908c4470e8fd57b1dc031bee98e29b05c2049 Commit: 992908c4470e8fd57b1dc031bee98e29b05c2049 Parent: 2a6375988a6fd3a9ff0bc22e174b493d655e2b5c Author: Bob Peterson AuthorDate: Wed Jun 17 08:12:47 2009 -0500 Committer: Bob Peterson CommitterDate: Wed Jun 17 08:32:57 2009 -0500 GFS2: fsck.gfs2 sometimes needs to be run twice bz 500483 This patch fixes a lot of fsck.gfs2 bugs. Most of these bugs have to do with improper bitmap setting that resulted in needing to run fsck.gfs2 a second time in order to fix the file system. There were also cases where changes were not written out and cases where the user was not asked for permission to make changes. There were also fixes related to extended attributes. --- gfs2/fsck/metawalk.c | 157 +++++++++++++++++++++++------------ gfs2/fsck/pass1.c | 134 ++++++++++++++++++++---------- gfs2/fsck/pass1b.c | 114 ++++++++++++++++--------- gfs2/fsck/pass1c.c | 169 ++++++++++++++++++++++++++++---------- gfs2/fsck/pass2.c | 203 +++++++++++++++++++++++++++++++-------------- gfs2/fsck/pass3.c | 3 +- gfs2/libgfs2/block_list.c | 28 +++++-- gfs2/libgfs2/fs_ops.c | 2 + gfs2/libgfs2/libgfs2.h | 6 +- 9 files changed, 564 insertions(+), 252 deletions(-) diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c index 8abb0bd..7088d8f 100644 --- a/gfs2/fsck/metawalk.c +++ b/gfs2/fsck/metawalk.c @@ -168,6 +168,8 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, return 0; while(1) { + if (skip_this_pass || fsck_abort) + return FSCK_OK; memset(&de, 0, sizeof(struct gfs2_dirent)); gfs2_dirent_in(&de, (char *)dent); filename = (char *)dent + sizeof(struct gfs2_dirent); @@ -261,8 +263,8 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, /* leaf a bit, but it's better than deleting the whole directory, */ /* which is what used to happen before. */ static void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no, - uint64_t *bad_leaf, uint64_t old_leaf, int pindex, - const char *msg) + uint64_t *bad_leaf, uint64_t old_leaf, + uint64_t first_ok_leaf, int pindex, const char *msg) { if (*bad_leaf != *leaf_no) { log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu" @@ -276,7 +278,13 @@ static void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no, if (*leaf_no == *bad_leaf || query(&opts, _("Attempt to patch around it? (y/n) "))) { errors_corrected++; - gfs2_put_leaf_nr(ip, pindex, old_leaf); + if (gfs2_check_range(ip->i_sbd, old_leaf) == 0) + gfs2_put_leaf_nr(ip, pindex, old_leaf); + else + gfs2_put_leaf_nr(ip, pindex, first_ok_leaf); + log_err( _("Directory Inode %llu (0x%llx) repaired.\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); } else log_err( _("Bad leaf left in place.\n")); @@ -291,15 +299,36 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, int error; struct gfs2_leaf leaf, oldleaf; uint64_t leaf_no, old_leaf, bad_leaf = -1; + uint64_t first_leaf_ptr = -1, first_ok_leaf = -1; struct gfs2_buffer_head *lbh; int lindex; struct gfs2_sbd *sbp = ip->i_sbd; uint16_t count; int ref_count = 0, exp_count = 0; - old_leaf = 0; + /* Find the first valid leaf pointer in range and use it as our "old" + leaf. That way, bad blocks at the beginning will be overwritten + with the first valid leaf. */ + first_ok_leaf = -1; + for(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) { + gfs2_get_leaf_nr(ip, lindex, &first_ok_leaf); + if (first_leaf_ptr == -1) + first_leaf_ptr = first_ok_leaf; + if(gfs2_check_range(ip->i_sbd, first_ok_leaf) == 0) { + lbh = bread(&sbp->buf_list, first_ok_leaf); + /* Make sure it's really a valid leaf block. */ + if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) == 0) { + brelse(lbh, not_updated); + break; + } + brelse(lbh, not_updated); + } + } + old_leaf = -1; memset(&oldleaf, 0, sizeof(oldleaf)); for(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) { + if (fsck_abort) + break; gfs2_get_leaf_nr(ip, lindex, &leaf_no); /* GFS has multiple indirect pointers to the same leaf @@ -314,42 +343,43 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, else if(old_leaf == leaf_no) { ref_count++; continue; - } else { - if(ref_count != exp_count){ - log_err( _("Dir #%llu (0x%llx) has an incorrect " - "number of pointers to leaf #%llu " - " (0x%llx)\n\tFound: %u, Expected: " - "%u\n"), (unsigned long long) - ip->i_di.di_num.no_addr, - (unsigned long long) - ip->i_di.di_num.no_addr, - (unsigned long long)old_leaf, - (unsigned long long)old_leaf, - ref_count, exp_count); - errors_found++; - if (query(&opts, _("Attempt to fix it? (y/n) "))) - { - int factor = 0, divisor = ref_count; - - errors_corrected++; - lbh = bread(&sbp->buf_list, old_leaf); - while (divisor > 1) { - factor++; - divisor /= 2; - } - oldleaf.lf_depth = ip->i_di.di_depth - - factor; - gfs2_leaf_out(&oldleaf, lbh->b_data); - brelse(lbh, updated); + } + if(gfs2_check_range(ip->i_sbd, old_leaf) == 0 && + ref_count != exp_count){ + log_err( _("Dir #%llu (0x%llx) has an incorrect " + "number of pointers to leaf #%llu " + " (0x%llx)\n\tFound: %u, Expected: " + "%u\n"), (unsigned long long) + ip->i_di.di_num.no_addr, + (unsigned long long) + ip->i_di.di_num.no_addr, + (unsigned long long)old_leaf, + (unsigned long long)old_leaf, + ref_count, exp_count); + errors_found++; + if (query(&opts, _("Attempt to fix it? (y/n) "))) { + int factor = 0, divisor = ref_count; + + errors_corrected++; + lbh = bread(&sbp->buf_list, old_leaf); + while (divisor > 1) { + factor++; + divisor /= 2; } - else - return 1; + gfs2_leaf_in(&oldleaf, lbh->b_data); + oldleaf.lf_depth = ip->i_di.di_depth - factor; + gfs2_leaf_out(&oldleaf, lbh->b_data); + brelse(lbh, updated); } - ref_count = 1; + else + return 1; } + ref_count = 1; count = 0; do { + if (fsck_abort) + break; /* Make sure the block number is in range. */ if(gfs2_check_range(ip->i_sbd, leaf_no)){ log_err( _("Leaf block #%llu (0x%llx) is out " @@ -361,7 +391,7 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, (unsigned long long) 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; @@ -373,10 +403,10 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, /* Make sure it's really a valid leaf block. */ if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) { warn_and_patch(ip, &leaf_no, &bad_leaf, - old_leaf, lindex, + old_leaf, first_ok_leaf, lindex, _("that is not really a leaf")); memcpy(&leaf, &oldleaf, sizeof(oldleaf)); - brelse(lbh, (opts.no ? not_updated : updated)); + brelse(lbh, not_updated); break; } gfs2_leaf_in(&leaf, lbh->b_data); @@ -534,8 +564,10 @@ static int check_eattr_entries(struct gfs2_inode *ip, update_it, pass->private)) { errors_found++; - if (query(&opts, _("Repair the bad EA? " - "(y/n) "))) { + error = 1; + if (query(&opts, _("Repair the bad " + "Extended Attribute? " + "(y/n) "))) { errors_corrected++; ea_hdr->ea_num_ptrs = i; ea_hdr->ea_data_len = @@ -545,9 +577,14 @@ static int check_eattr_entries(struct gfs2_inode *ip, /* Endianness doesn't matter in this case because it's a single byte. */ - return -1; - } - log_err( _("The bad EA was not fixed.\n")); + gfs2_block_set(sdp, bl, + ip->i_di.di_eattr, + gfs2_meta_eattr); + log_err( _("The EA was " + "fixed.\n")); + } else + log_err( _("The bad EA was " + "not fixed.\n")); } tot_ealen += sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header); @@ -565,7 +602,7 @@ static int check_eattr_entries(struct gfs2_inode *ip, be32_to_cpu(ea_hdr->ea_rec_len)); } - return 0; + return error; } /** @@ -573,7 +610,7 @@ static int check_eattr_entries(struct gfs2_inode *ip, * @ip: the inode the eattr comes from * @block: block number of the leaf * - * Returns: 0 on success, -1 if removal is needed + * Returns: 0 on success, 1 if removal is needed, -1 on error */ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block, uint64_t parent, enum update_flags *want_updated, @@ -595,9 +632,10 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block, if(error > 0) { return 1; } - check_eattr_entries(ip, bh, pass, want_updated); + error = check_eattr_entries(ip, bh, pass, want_updated); if (bh) brelse(bh, *want_updated); + return error; } return 0; @@ -656,8 +694,6 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect, if (*want_updated) gfs2_set_bitmap(sdp, indirect, GFS2_BLKST_FREE); - gfs2_block_clear(sdp, bl, indirect, - gfs2_indir_blk); gfs2_block_set(sdp, bl, indirect, gfs2_block_free); error = 1; @@ -735,9 +771,17 @@ static int build_and_check_metalist(struct gfs2_inode *ip, bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist); - head_size = (i > 1 ? - sizeof(struct gfs2_meta_header) : - sizeof(struct gfs2_dinode)); + if (i > 1) { + /* if this isn't really a block list skip it */ + if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) + continue; + head_size = sizeof(struct gfs2_meta_header); + } else { + /* if this isn't really a dinode, skip it */ + if (gfs2_check_meta(bh, GFS2_METATYPE_DI)) + continue; + head_size = sizeof(struct gfs2_dinode); + } for (ptr = (uint64_t *)(bh->b_data + head_size); (char *)ptr < (bh->b_data + ip->i_sbd->bsize); @@ -828,8 +872,17 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass) for (tmp = list->next; tmp != list; tmp = tmp->next) { bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist); - head_size = (height != 1 ? sizeof(struct gfs2_meta_header) : - sizeof(struct gfs2_dinode)); + if (height > 1) { + /* if this isn't really a block list skip it */ + if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) + continue; + head_size = sizeof(struct gfs2_meta_header); + } else { + /* if this isn't really a dinode, skip it */ + if (gfs2_check_meta(bh, GFS2_METATYPE_DI)) + continue; + head_size = sizeof(struct gfs2_dinode); + } ptr = (uint64_t *)(bh->b_data + head_size); for ( ; (char *)ptr < (bh->b_data + ip->i_sbd->bsize); ptr++) { diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c index d3cf70c..e53b666 100644 --- a/gfs2/fsck/pass1.c +++ b/gfs2/fsck/pass1.c @@ -103,9 +103,9 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block, return -1; } if(q.block_type != gfs2_block_free) { - log_debug( _("Found duplicate block in indirect block -" - " was marked %d\n"), q.block_type); - gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block); + log_err( _("Found duplicate block referenced as metadata in " + "indirect block - was marked %d\n"), q.block_type); + gfs2_block_set(ip->i_sbd, bl, block, gfs2_dup_block); found_dup = 1; } nbh = bread(&ip->i_sbd->buf_list, block); @@ -121,9 +121,11 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block, }else /* blk check ok */ *bh = nbh; - log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to indirect block.\n"), - block, block); - gfs2_block_set(ip->i_sbd, bl, block, gfs2_indir_blk); + if (!found_dup) { + log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to indirect " + "block.\n"), block, block); + gfs2_block_set(ip->i_sbd, bl, block, gfs2_indir_blk); + } bc->indir_count++; return 0; @@ -133,6 +135,7 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private) { struct gfs2_block_query q = {0}; struct block_count *bc = (struct block_count *) private; + int error = 0; if (gfs2_check_range(ip->i_sbd, block)) { log_err( _("Bad data block pointer (out of range)\n")); @@ -147,17 +150,46 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private) return -1; } if(q.block_type != gfs2_block_free) { - log_debug( _("Found duplicate block at %" PRIu64 " (0x%"PRIx64 ")\n"), - block, block); + log_err( _("Found duplicate block referenced as data at %" + PRIu64 " (0x%"PRIx64 ")\n"), block, block); + if (q.block_type != gfs2_meta_inval) { + gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block); + return 1; + } + /* An explanation is in order here. At this point we found + a duplicate block, a block that was already referenced + somewhere else. We'll resolve those duplicates in pass1b. + However, if the block is marked "invalid" that's a special + case. It's likely that the block was discovered to be + invalid metadata--i.e. doesn't have a metadata header. + However, it still may be a valid data block, since they + won't have metadata headers. In that case, the block is + marked as duplicate, but also as a data block. */ + error = 1; + log_err( _("Block %" PRIu64 " (0x%"PRIx64 ") previous " + "reference was invalid, so it likely is a data " + "block.\n"), + block, block); + gfs2_block_unmark(ip->i_sbd, bl, block, gfs2_meta_inval); gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block); - bc->data_count++; - return 1; } log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to data block\n"), block, block); - gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_used); + gfs2_block_mark(ip->i_sbd, bl, block, gfs2_block_used); + /* This is confusing, so I'll clarify. There are two bitmaps: + (1) The gfs2_bmap that fsck uses to keep track of what block + type has been discovered, and (2) The rgrp bitmap. Function + gfs2_block_set is used to set the former and gfs2_set_bitmap + is used to set the latter. In this function we need to set both + because we found a "data" block that could be "meta" in the rgrp + bitmap. If we don't we could run into the data block again as + metadata when we're traversing the metadata with gfs2_next_rg_meta + in func pass1(). If that happens, it will look at the block, + say "hey this isn't metadata" and mark it incorrectly as an + invalid metadata block and free it. */ + gfs2_set_bitmap(ip->i_sbd, block, GFS2_BLKST_USED); bc->data_count++; - return 0; + return error; } /* clear_eas - clear the extended attributes for an inode @@ -184,12 +216,11 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc, block, block); log_err(".\n"); errors_found++; - if ((errors_corrected += query(&opts, _("Clear the bad EA? (y/n) ")))) { + if ((errors_corrected += query(&opts, _("Clear the bad Extended " + "Attribute? (y/n) ")))) { if (block == 0) block = ip->i_di.di_eattr; - gfs2_block_clear(sdp, bl, block, gfs2_eattr_block); if (!duplicate) { - gfs2_block_clear(sdp, bl, block, gfs2_indir_blk); gfs2_block_set(sdp, bl, block, gfs2_block_free); gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE); } @@ -200,9 +231,10 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc, ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count; gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data); *want_updated = updated; + log_err( _("The bad Extended Attribute was removed.\n")); 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; } @@ -240,28 +272,34 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect, if(gfs2_check_meta(*bh, GFS2_METATYPE_IN)) { if(q.block_type != gfs2_block_free) { /* Duplicate? */ if (!clear_eas(ip, bc, indirect, 1, want_updated, - _("Bad indirect EA duplicate found"))) + _("Bad indirect Extended Attribute " + "duplicate found"))) { gfs2_block_set(sdp, bl, indirect, gfs2_dup_block); + bc->ea_count++; + } return 1; } clear_eas(ip, bc, indirect, 0, want_updated, - _("EA indirect block has incorrect type")); + _("Extended Attribute indirect block has incorrect " + "type")); return 1; } if(q.block_type != gfs2_block_free) { /* Duplicate? */ - log_err( _("Inode #%llu (0x%llx): Duplicate EA indirect " - "block found at #%llu (0x%llx).\n"), - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)indirect, - (unsigned long long)indirect); + log_err( _("Inode #%llu (0x%llx): Duplicate Extended " + "Attribute indirect block found at #%llu " + "(0x%llx).\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)indirect, + (unsigned long long)indirect); gfs2_block_set(sdp, bl, indirect, gfs2_dup_block); bc->ea_count++; ret = 1; } else { log_debug( _("Setting #%" PRIu64 " (0x%" PRIx64 - ") to indirect EA block\n"), indirect, indirect); + ") to indirect Extended Attribute block\n"), + indirect, indirect); gfs2_block_set(sdp, bl, indirect, gfs2_indir_blk); bc->ea_count++; } @@ -282,7 +320,7 @@ static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok, return 0; } clear_eas(ip, (struct block_count *)private, 0, 0, want_updated, - _("has unrecoverable indirect EA errors")); + _("has unrecoverable indirect Extended Attribute errors")); return 0; } @@ -312,12 +350,15 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, int ret = 0; if(gfs2_check_range(sdp, el_blk)){ - log_err( _("Inode #%llu (0x%llx): EA extended " - "leaf block #%llu (0x%llx) is out of range.\n"), - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)el_blk, - (unsigned long long)el_blk); + log_err( _("Inode #%llu (0x%llx): Extended Attribute block " + "%llu (0x%llx) has an extemded leaf block #%llu " + "(0x%llx) that is out of range.\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_eattr, + (unsigned long long)ip->i_di.di_eattr, + (unsigned long long)el_blk, + (unsigned long long)el_blk); gfs2_block_set(sdp, bl, ip->i_di.di_eattr, gfs2_bad_block); return 1; } @@ -335,10 +376,12 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, if(gfs2_check_meta(el_buf, GFS2_METATYPE_ED)) { if(q.block_type != gfs2_block_free) /* Duplicate? */ clear_eas(ip, bc, el_blk, 1, want_updated, - _("has bad extended EA duplicate")); + _("has bad extended Extended Attribute " + "duplicate")); else clear_eas(ip, bc, el_blk, 0, want_updated, - _("EA extended leaf block has incorrect type")); + _("Extended Attribute extended leaf block " + "has incorrect type")); ret = 1; } else { /* If this looks like an EA */ if(q.block_type != gfs2_block_free) { /* Duplicate? */ @@ -371,16 +414,19 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, struct gfs2_block_query q = {0}; struct block_count *bc = (struct block_count *) private; + /* Figure out the parent's block type */ + gfs2_block_check(sdp, bl, parent, &q); /* 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 */ + if (parent != ip->i_di.di_num.no_addr && /* if parent isn't the inode*/ + q.block_type != gfs2_indir_blk) { /* parent isn't indirect block */ log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to eattr block\n"), parent, parent); gfs2_block_set(sdp, bl, parent, gfs2_eattr_block); } if(gfs2_check_range(sdp, block)){ - log_warn( _("Inode #%llu (0x%llx): EA leaf block #%llu (0x%llx" - ") is out of range.\n"), + log_warn( _("Inode #%llu (0x%llx): Extended Attribute leaf " + "block #%llu (0x%llx) is out of range.\n"), (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)block, (unsigned long long)block); @@ -400,10 +446,12 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, if(gfs2_check_meta(leaf_bh, GFS2_METATYPE_EA)) { if(q.block_type != gfs2_block_free) { /* Duplicate? */ clear_eas(ip, bc, block, 1, want_updated, - _("Bad EA duplicate found")); + _("Bad Extended Attribute duplicate " + "found")); } else { clear_eas(ip, bc, block, 0, want_updated, - _("EA leaf block has incorrect type")); + _("Extended Attribute leaf block " + "has incorrect type")); } ret = 1; brelse(leaf_bh, not_updated); @@ -600,8 +648,8 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh, return -1; } if(q.block_type != gfs2_block_free) { - log_debug( _("Found duplicate block at #%" PRIu64 " (0x%" PRIx64 ")\n"), - block, block); + log_err( _("Found duplicate block referenced as an inode at " + "#%" PRIu64 " (0x%" PRIx64 ")\n"), block, block); if(gfs2_block_mark(sdp, bl, block, gfs2_dup_block)) { stack; fsck_inode_put(ip, f); @@ -724,7 +772,7 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh, pass1_fxns.private = &bc; error = check_metatree(ip, &pass1_fxns); - if(error < 0) { + if (fsck_abort || error < 0) { fsck_inode_put(ip, f); return 0; } @@ -774,7 +822,7 @@ static int scan_meta(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh, uint64_t block) { if (gfs2_check_meta(bh, 0)) { - log_debug( _("Found invalid metadata at #%" PRIu64 " (0x%" PRIx64 ")\n"), + log_info( _("Found invalid metadata at #%" PRIu64 " (0x%" PRIx64 ")\n"), block, block); if(gfs2_block_set(sdp, bl, block, gfs2_meta_inval)) { stack; diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c index 4e24611..c02317b 100644 --- a/gfs2/fsck/pass1b.c +++ b/gfs2/fsck/pass1b.c @@ -165,14 +165,15 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block, if(dh->ref_count == 1) return 1; if(block == dh->b->block_no) { - log_err( _("Found dup in inode \"%s\" (block #%llu" - ") with block #%llu\n"), - dh->id->name ? dh->id->name : _("unknown name"), - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)block); + log_err( _("Found duplicate reference in inode \"%s\" at " + "block #%llu (0x%llx) with block #%llu (0x%llx)\n"), + dh->id->name ? dh->id->name : _("unknown name"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)block, (unsigned long long)block); log_err( _("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n"), - dh->id->name ? dh->id->name : "", - dh->id->parent, dh->id->parent); + dh->id->name ? dh->id->name : "", dh->id->parent, + dh->id->parent); inode_hash_remove(inode_hash, ip->i_di.di_num.no_addr); /* Setting the block to invalid means the inode is * cleared in pass2 */ @@ -184,31 +185,9 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block, static int clear_dup_data(struct gfs2_inode *ip, uint64_t block, void *private) { - struct dup_handler *dh = (struct dup_handler *) private; - - if(dh->ref_count == 1) { - return 1; - } - if(block == dh->b->block_no) { - log_err( _("Found dup in inode \"%s\" for block #%llu" - " (0x%llx) at block #%llu (0x%llx)\n"), - dh->id->name ? dh->id->name : _("unknown name"), - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)block, - (unsigned long long)block); - log_err( _("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n"), - dh->id->name ? dh->id->name : "", dh->id->parent, - dh->id->parent); - inode_hash_remove(inode_hash, ip->i_di.di_num.no_addr); - /* Setting the block to invalid means the inode is - * cleared in pass2 */ - gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr, - gfs2_meta_inval); - } - - return 0; + return clear_dup_metalist(ip, block, NULL, private); } + static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, enum update_flags *want_updated, @@ -361,15 +340,15 @@ static int find_block_ref(struct gfs2_sbd *sbp, uint64_t inode, struct dup_block enum update_flags update; ip = fsck_load_inode(sbp, inode); /* bread, inode_get */ - log_info( _("Checking inode %" PRIu64 " (0x%" PRIx64 - ")'s metatree for references to block %" PRIu64 " (0x%" PRIx64 - ")\n"), inode, inode, b->block_no, b->block_no); + log_debug( _("Checking inode %" PRIu64 " (0x%" PRIx64 ")'s " + "metatree for references to block %" PRIu64 " (0x%" PRIx64 + ")\n"), inode, inode, b->block_no, b->block_no); if(check_metatree(ip, &find_refs)) { stack; fsck_inode_put(ip, not_updated); /* out, brelse, free */ return -1; } - log_info( _("Done checking metatree\n")); + log_debug( _("Done checking metatree\n")); /* Check for ea references in the inode */ if(check_inode_eattr(ip, &update, &find_refs) < 0){ stack; @@ -421,6 +400,51 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b) dh.ref_inode_count++; dh.ref_count += id->dup_count; } + /* A single reference to the block implies a possible situation where + a data pointer points to a metadata block. In other words, the + duplicate reference in the file system is (1) Metadata block X and + (2) A dinode reference such as a data pointer pointing to block X. + We can't really check for that in pass1 because user data might + just _look_ like metadata by coincidence, and at the time we're + checking, we might not have processed the referenced block. + Here in pass1b we're sure. */ + if (dh.ref_count == 1) { + struct gfs2_buffer_head *bh; + uint32_t cmagic; + + bh = bread(&sbp->buf_list, b->block_no); + cmagic = ((struct gfs2_meta_header *)(bh->b_data))->mh_magic; + brelse(bh, not_updated); + if (be32_to_cpu(cmagic) == GFS2_MAGIC) { + tmp = b->ref_inode_list.next; + id = osi_list_entry(tmp, struct inode_with_dups, list); + log_warn( _("Inode %s (%lld/0x%llx) has a reference to" + " data block %llu (0x%llx), " + "but the block is really metadata.\n"), + id->name, (unsigned long long)id->block_no, + (unsigned long long)id->block_no, + (unsigned long long)b->block_no, + (unsigned long long)b->block_no); + errors_found++; + if ((errors_corrected += + query(&opts, _("Clear the inode? (y/n) ")))) { + log_warn( _("Clearing...\n")); + ip = fsck_load_inode(sbp, id->block_no); + inode_hash_remove(inode_hash, + ip->i_di.di_num.no_addr); + /* Setting the block to invalid means the inode + is cleared in pass2 */ + gfs2_block_set(ip->i_sbd, bl, + ip->i_di.di_num.no_addr, + gfs2_meta_inval); + fsck_inode_put(ip, updated); + } else { + log_warn( _("The bad inode was not cleared.")); + } + return 0; + } + } + log_notice( _("Block %llu (0x%llx) has %d inodes referencing it" " for a total of %d duplicate references\n"), (unsigned long long)b->block_no, @@ -429,10 +453,18 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b) osi_list_foreach(tmp, &b->ref_inode_list) { id = osi_list_entry(tmp, struct inode_with_dups, list); - log_warn( _("Inode %s has %d reference(s) to block %"PRIu64 - " (0x%" PRIx64 ")\n"), id->name, id->dup_count, b->block_no, - b->block_no); - /* FIXME: User input */ + log_warn( _("Inode %s (%lld/0x%llx) has %d reference(s) to " + "block %llu (0x%llx)\n"), id->name, + (unsigned long long)id->block_no, + (unsigned long long)id->block_no, + id->dup_count, (unsigned long long)b->block_no, + (unsigned long long)b->block_no); + errors_found++; + if (!(errors_corrected += + query(&opts, _("Clear the inode? (y/n) ")))) { + log_warn( _("The bad inode was not cleared...\n")); + continue; + } log_warn( _("Clearing...\n")); ip = fsck_load_inode(sbp, id->block_no); dh.b = b; @@ -444,7 +476,9 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b) if(!id->ea_only) check_metatree(ip, &clear_dup_fxns); - fsck_inode_put(ip, not_updated); /* out, brelse, free */ + gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr, + gfs2_meta_inval); + fsck_inode_put(ip, updated); /* out, brelse, free */ dh.ref_inode_count--; if(dh.ref_inode_count == 1) break; diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c index f3b7985..e0f4576 100644 --- a/gfs2/fsck/pass1c.c +++ b/gfs2/fsck/pass1c.c @@ -15,7 +15,8 @@ static int remove_eattr_entry(struct gfs2_sbd *sdp, struct gfs2_ea_header *curr, struct gfs2_ea_header *prev) { - log_warn( _("Removing EA located in block #%"PRIu64" (0x%" PRIx64 ").\n"), + log_err( _("Bad Extended Attribute at block #%"PRIu64 + " (0x%" PRIx64 ") removed.\n"), leaf_bh->b_blocknr, leaf_bh->b_blocknr); if(!prev) curr->ea_type = GFS2_EATYPE_UNUSED; @@ -48,8 +49,15 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block, (unsigned long long)block, (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); - ip->i_di.di_eattr = 0; - *update = (opts.no ? not_updated : updated); + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + ip->i_di.di_eattr = 0; + *update = updated; + log_err( _("Bad Extended Attribute removed.\n")); + } else + log_err( _("Bad Extended Attribute not removed.\n")); return 1; } else if (gfs2_block_check(sbp, bl, block, &q)) { @@ -59,13 +67,21 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block, else if(q.block_type != gfs2_indir_blk) { log_err( _("Extended attributes indirect block #%llu" " (0x%llx) for inode #%llu" - " (0x%llx) invalid...removing\n"), + " (0x%llx) invalid.\n"), (unsigned long long)block, (unsigned long long)block, (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); - ip->i_di.di_eattr = 0; - *update = (opts.no ? not_updated : updated); + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + ip->i_di.di_eattr = 0; + *update = updated; + log_err( _("Bad Extended Attribute removed.\n")); + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } else @@ -85,11 +101,19 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, *update = not_updated; if(gfs2_check_range(sbp, block)) { log_err( _("Extended attributes block for inode #%llu" - " (0x%llx) out of range...removing\n"), + " (0x%llx) out of range.\n"), (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); - ip->i_di.di_eattr = 0; - *update = (opts.no ? not_updated : updated); + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + ip->i_di.di_eattr = 0; + *update = updated; + log_err( _("Bad Extended Attribute removed.\n")); + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } else if (gfs2_block_check(sbp, bl, block, &q)) { @@ -98,11 +122,19 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, } else if(q.block_type != gfs2_meta_eattr) { log_err( _("Extended attributes block for inode #%llu" - " (0x%llx) invalid...removing\n"), - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr); - ip->i_di.di_eattr = 0; - *update = (opts.no ? not_updated : updated); + " (0x%llx) invalid.\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + ip->i_di.di_eattr = 0; + *update = updated; + log_err( _("Bad Extended Attribute removed.\n")); + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } else @@ -125,40 +157,72 @@ static int check_eattr_entry(struct gfs2_inode *ip, if(!ea_hdr->ea_name_len){ log_err( _("EA has name length == 0\n")); - ea_hdr->ea_flags |= GFS2_EAFLAG_LAST; - ea_hdr->ea_rec_len = cpu_to_be32(max_size - offset); - if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){ - stack; - return -1; - } + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + ea_hdr->ea_flags |= GFS2_EAFLAG_LAST; + ea_hdr->ea_rec_len = cpu_to_be32(max_size - offset); + if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev)){ + stack; + return -1; + } + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } if(offset + be32_to_cpu(ea_hdr->ea_rec_len) > max_size){ log_err( _("EA rec length too long\n")); - ea_hdr->ea_flags |= GFS2_EAFLAG_LAST; - ea_hdr->ea_rec_len = cpu_to_be32(max_size - offset); - if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){ - stack; - return -1; - } + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + ea_hdr->ea_flags |= GFS2_EAFLAG_LAST; + ea_hdr->ea_rec_len = cpu_to_be32(max_size - offset); + if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev)){ + stack; + return -1; + } + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } if(offset + be32_to_cpu(ea_hdr->ea_rec_len) == max_size && (ea_hdr->ea_flags & GFS2_EAFLAG_LAST) == 0){ log_err( _("last EA has no last entry flag\n")); - ea_hdr->ea_flags |= GFS2_EAFLAG_LAST; - if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){ - stack; - return -1; - } + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + ea_hdr->ea_flags |= GFS2_EAFLAG_LAST; + if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev)){ + stack; + return -1; + } + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } 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; - } + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev)){ + stack; + return -1; + } + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } @@ -170,10 +234,18 @@ static int check_eattr_entry(struct gfs2_inode *ip, ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){ log_err( _("EA (%s) type is invalid (%d > %d).\n"), ea_name, ea_hdr->ea_type, GFS2_EATYPE_LAST); - if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){ - stack; - return -1; - } + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended Attribute? " + "(y/n) ")))) { + if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev)){ + stack; + return -1; + } + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; } @@ -189,10 +261,19 @@ static int check_eattr_entry(struct gfs2_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; - } + errors_found++; + if ((errors_corrected += + query(&opts, _("Remove the bad Extended " + "Attribute? (y/n) ")))) { + if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev)){ + stack; + return -1; + } + } else + log_err( _("Bad Extended Attribute not " + "removed.\n")); + return 1; } else { log_debug( _(" Pointers Required: %d\n Pointers Reported: %d\n"), @@ -256,7 +337,7 @@ int pass1c(struct gfs2_sbd *sbp) if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) { /* if a dinode */ log_info( _("EA in inode %"PRIu64" (0x%" PRIx64 ")\n"), block_no, block_no); - gfs2_block_clear(sbp, bl, block_no, gfs2_eattr_block); + gfs2_block_unmark(sbp, bl, block_no, gfs2_eattr_block); ip = fsck_inode_get(sbp, bh); log_debug( _("Found eattr at %llu (0x%llx)\n"), diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c index 31390c2..c6961b7 100644 --- a/gfs2/fsck/pass2.c +++ b/gfs2/fsck/pass2.c @@ -142,6 +142,38 @@ static int check_file_type(uint8_t de_type, uint8_t block_type) return 0; } +static int delete_blocks(struct gfs2_inode *ip, uint64_t block, + struct gfs2_buffer_head **bh, void *private) +{ + struct gfs2_block_query q = {0}; + + if (gfs2_check_range(ip->i_sbd, block) == 0) { + log_info(_("Deleting block %lld (0x%llx) as part of inode " + "%lld (0x%llx)\n"), (unsigned long long)block, + (unsigned long long)block, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); + if (gfs2_block_check(ip->i_sbd, bl, block, &q)) + return 0; + if (!q.dup_block) { + gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_free); + gfs2_free_block(ip->i_sbd, block); + } + } + return 0; +} + +static int delete_data(struct gfs2_inode *ip, uint64_t block, void *private) +{ + return delete_blocks(ip, block, NULL, private); +} + +struct metawalk_fxns pass2_fxns_delete = { + .private = NULL, + .check_metalist = delete_blocks, + .check_data = delete_data, +}; + /* FIXME: should maybe refactor this a bit - but need to deal with * FIXMEs internally first */ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, @@ -160,7 +192,6 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, struct gfs2_dirent dentry, *de; uint32_t calculated_hash; - *update = not_updated; memset(&dentry, 0, sizeof(struct gfs2_dirent)); gfs2_dirent_in(&dentry, (char *)dent); de = &dentry; @@ -242,22 +273,17 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, if(q.bad_block) { /* This entry's inode has bad blocks in it */ - /* FIXME: user interface */ - /* FIXME: do i want to kill the inode here? */ /* Handle bad blocks */ log_err( _("Found a bad directory entry: %s\n"), filename); errors_found++; - if(query(&opts, _("Clear entry to inode containing bad blocks? (y/n)"))) { - + if(query(&opts, _("Clear inode containing bad blocks? (y/n)"))) { errors_corrected++; entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr); check_inode_eattr(entry_ip, update, &clear_eattrs); fsck_inode_put(entry_ip, not_updated); - /* FIXME: make sure all blocks referenced by - * this inode are cleared in the bitmap */ - + check_metatree(entry_ip, &pass2_fxns_delete); dirent2_del(ip, bh, prev_de, dent); gfs2_block_set(sbp, bl, de->de_inum.no_addr, @@ -277,22 +303,44 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, q.block_type != gfs2_inode_chr && q.block_type != gfs2_inode_fifo && q.block_type != gfs2_inode_sock) { log_err( _("Directory entry '%s' at block %llu (0x%llx" - ") in dir inode %llu (0x%llx" - ") has an invalid block type: %d.\n"), tmp_name, - (unsigned long long)de->de_inum.no_addr, - (unsigned long long)de->de_inum.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr, - q.block_type); + ") in dir inode %llu (0x%llx" + ") block type %d: %s.\n"), tmp_name, + (unsigned long long)de->de_inum.no_addr, + (unsigned long long)de->de_inum.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + q.block_type, q.block_type == gfs2_meta_inval ? + _("previously marked invalid") : + _("is not an inode")); errors_found++; - if(query(&opts, _("Clear directory entry to non-inode block? (y/n) "))) { - /* FIXME: make sure all blocks referenced by - * this inode are cleared in the bitmap */ + if(query(&opts, _("Clear directory entry to non-inode block? " + "(y/n) "))) { + struct gfs2_buffer_head *bhi; + errors_corrected++; dirent2_del(ip, bh, prev_de, dent); *update = updated; log_warn( _("Directory entry '%s' cleared\n"), tmp_name); + /* If it was previously marked invalid (i.e. known + to be bad, not just a free block, etc.) then + delete any metadata it holds. If not, return. */ + if (q.block_type != gfs2_meta_inval) + return 1; + + /* Now try to clear the dinode, if it is an dinode */ + bhi = bread(&sbp->buf_list, de->de_inum.no_addr); + error = gfs2_check_meta(bhi, GFS2_METATYPE_DI); + brelse(bhi, updated); + if (error) + return 1; /* not a dinode: nothing to delete */ + + entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr); + check_metatree(entry_ip, &pass2_fxns_delete); + fsck_inode_put(entry_ip, updated); + gfs2_block_set(sbp, bl, de->de_inum.no_addr, + gfs2_block_free); + return 1; } else { log_err( _("Directory entry to non-inode block remains\n")); @@ -323,6 +371,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, dirent2_del(ip, bh, prev_de, dent); *update = updated; + log_err( _("Stale directory entry deleted\n")); return 1; } else { log_err( _("Stale directory entry remains\n")); @@ -585,27 +634,33 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname, } if(!ds.dotdir) { log_err( _("No '.' entry found for %s directory.\n"), dirname); - sprintf(tmp_name, "."); - filename_len = strlen(tmp_name); /* no trailing NULL */ - if(!(filename = malloc(sizeof(char) * filename_len))) { - log_err( _("Unable to allocate name string\n")); - stack; - return -1; - } - if(!(memset(filename, 0, sizeof(char) * filename_len))) { - log_err( _("Unable to zero name string\n")); - stack; - return -1; - } - memcpy(filename, tmp_name, filename_len); - log_warn( _("Adding '.' entry\n")); - dir_add(sysinode, filename, filename_len, + errors_found++; + if (query(&opts, _("Is it okay to add '.' entry? (y/n) "))) { + errors_corrected++; + sprintf(tmp_name, "."); + filename_len = strlen(tmp_name); /* no trailing NULL */ + if(!(filename = malloc(sizeof(char) * filename_len))) { + log_err( _("Unable to allocate name string\n")); + stack; + return -1; + } + if(!(memset(filename, 0, sizeof(char) * + filename_len))) { + log_err( _("Unable to zero name string\n")); + stack; + return -1; + } + memcpy(filename, tmp_name, filename_len); + log_warn( _("Adding '.' entry\n")); + dir_add(sysinode, filename, filename_len, &(sysinode->i_di.di_num), DT_DIR); - increment_link(sysinode->i_sbd, - sysinode->i_di.di_num.no_addr); - ds.entry_count++; - free(filename); - update = 1; + increment_link(sysinode->i_sbd, + sysinode->i_di.di_num.no_addr); + ds.entry_count++; + free(filename); + update = 1; + } else + log_err( _("The directory was not fixed.\n")); } if(sysinode->i_di.di_entries != ds.entry_count) { log_err( _("%s inode %llu (0x%llx" @@ -671,6 +726,7 @@ int pass2(struct gfs2_sbd *sbp) int filename_len; char tmp_name[256]; int error = 0; + enum update_flags need_update = NOT_UPDATED; /* Check all the system directory inodes. */ if (check_system_dir(sbp->md.jiinode, "jindex", build_jindex)) { @@ -766,42 +822,61 @@ int pass2(struct gfs2_sbd *sbp) bh = bread(&sbp->buf_list, i); ip = fsck_inode_get(sbp, bh); if(!ds.dotdir) { - log_err( _("No '.' entry found\n")); - sprintf(tmp_name, "."); - filename_len = strlen(tmp_name); /* no trailing NULL */ - if(!(filename = malloc(sizeof(char) * filename_len))) { - log_err( _("Unable to allocate name string\n")); - stack; - return FSCK_ERROR; - } - if(!memset(filename, 0, sizeof(char) * filename_len)) { - log_err( _("Unable to zero name string\n")); - stack; - return FSCK_ERROR; - } - memcpy(filename, tmp_name, filename_len); - - dir_add(ip, filename, filename_len, &(ip->i_di.di_num), DT_DIR); - increment_link(ip->i_sbd, ip->i_di.di_num.no_addr); - ds.entry_count++; - free(filename); + log_err(_("No '.' entry found for directory inode at " + "block %"PRIu64" (0x%" PRIx64 ")\n"), i, i); + errors_found++; + if (query(&opts, + _("Is it okay to add '.' entry? (y/n) "))) { + errors_corrected++; + sprintf(tmp_name, "."); + filename_len = strlen(tmp_name); /* no trailing + NULL */ + if(!(filename = malloc(sizeof(char) * + filename_len))) { + log_err(_("Unable to allocate name\n")); + stack; + return FSCK_ERROR; + } + if(!memset(filename, 0, sizeof(char) * + filename_len)) { + log_err( _("Unable to zero name\n")); + stack; + return FSCK_ERROR; + } + memcpy(filename, tmp_name, filename_len); + + dir_add(ip, filename, filename_len, + &(ip->i_di.di_num), DT_DIR); + increment_link(ip->i_sbd, + ip->i_di.di_num.no_addr); + ds.entry_count++; + free(filename); + log_err( _("The directory was fixed.\n")); + need_update = UPDATED; + } else { + log_err( _("The directory was not fixed.\n")); + } } - fsck_inode_put(ip, not_updated); /* does a brelse */ - bh = bread(&sbp->buf_list, i); - ip = fsck_inode_get(sbp, bh); if(ip->i_di.di_entries != ds.entry_count) { log_err( _("Entries is %d - should be %d for inode " "block %llu (0x%llx)\n"), ip->i_di.di_entries, ds.entry_count, (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); - ip->i_di.di_entries = ds.entry_count; - fsck_inode_put(ip, updated); /* does a gfs2_dinode_out, brelse */ + errors_found++; + if (query(&opts, + _("Fix the entry count? (y/n) "))) { + errors_corrected++; + ip->i_di.di_entries = ds.entry_count; + need_update = UPDATED; + } else { + log_err( _("The entry count was not fixed.\n")); + } } - else - fsck_inode_put(ip, not_updated); /* does a brelse */ + fsck_inode_put(ip, need_update); /* does a gfs2_dinode_out, + brelse */ } return FSCK_OK; } diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c index 83f2e50..9bb7cb4 100644 --- a/gfs2/fsck/pass3.c +++ b/gfs2/fsck/pass3.c @@ -272,10 +272,11 @@ int pass3(struct gfs2_sbd *sbp) return FSCK_ERROR; } log_warn( _("Directory relinked to lost+found\n")); + fsck_inode_put(ip, updated); } else { log_err( _("Unlinked directory remains unlinked\n")); + fsck_inode_put(ip, not_updated); } - fsck_inode_put(ip, not_updated); break; } else { diff --git a/gfs2/libgfs2/block_list.c b/gfs2/libgfs2/block_list.c index 2bea6aa..571c737 100644 --- a/gfs2/libgfs2/block_list.c +++ b/gfs2/libgfs2/block_list.c @@ -293,12 +293,13 @@ int gfs2_block_mark(struct gfs2_sbd *sdp, struct gfs2_block_list *il, return err; } -int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il, - uint64_t block, enum gfs2_mark_block m) +/* gfs2_block_unmark clears ONE mark for the given block */ +int gfs2_block_unmark(struct gfs2_sbd *sdp, struct gfs2_block_list *il, + uint64_t block, enum gfs2_mark_block mark) { int err = 0; - switch (m) { + switch (mark) { case gfs2_dup_block: gfs2_dup_clear(&sdp->dup_blocks, block); break; @@ -316,12 +317,25 @@ int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il, return err; } +/* gfs2_block_clear clears all the marks for the given block */ +int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il, + uint64_t block) +{ + int err = 0; + + gfs2_dup_clear(&sdp->dup_blocks, block); + gfs2_special_clear(&sdp->bad_blocks, block); + gfs2_special_clear(&sdp->eattr_blocks, block); + err = gfs2_bitmap_clear(&il->list.gbmap.group_map, block); + return err; +} + int gfs2_block_set(struct gfs2_sbd *sdp, struct gfs2_block_list *il, uint64_t block, enum gfs2_mark_block mark) { int err; - err = gfs2_block_clear(sdp, il, block, mark); + err = gfs2_block_clear(sdp, il, block); /* clear all block status */ if(!err) err = gfs2_block_mark(sdp, il, block, mark); return err; @@ -335,15 +349,15 @@ int gfs2_block_check(struct gfs2_sbd *sdp, struct gfs2_block_list *il, val->bad_block = 0; val->dup_block = 0; val->eattr_block = 0; - if((err = gfs2_bitmap_get(&il->list.gbmap.group_map, block, - &val->block_type))) - return err; if (blockfind(&sdp->bad_blocks, block)) val->bad_block = 1; if (dupfind(&sdp->dup_blocks, block)) val->dup_block = 1; if (blockfind(&sdp->eattr_blocks, block)) val->eattr_block = 1; + if((err = gfs2_bitmap_get(&il->list.gbmap.group_map, block, + &val->block_type))) + return err; return 0; } diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c index 2624c24..fc898ce 100644 --- a/gfs2/libgfs2/fs_ops.c +++ b/gfs2/libgfs2/fs_ops.c @@ -705,6 +705,8 @@ void dirent2_del(struct gfs2_inode *dip, struct gfs2_buffer_head *bh, { uint16_t cur_rec_len, prev_rec_len; + if (dip->i_di.di_entries) + dip->i_di.di_entries--; if (!prev) { cur->de_inum.no_formal_ino = 0; return; diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h index a2a6a9b..b64edd5 100644 --- a/gfs2/libgfs2/libgfs2.h +++ b/gfs2/libgfs2/libgfs2.h @@ -371,8 +371,12 @@ extern int gfs2_block_mark(struct gfs2_sbd *sdp, struct gfs2_block_list *il, uint64_t block, enum gfs2_mark_block mark); extern int gfs2_block_set(struct gfs2_sbd *sdp, struct gfs2_block_list *il, uint64_t block, enum gfs2_mark_block mark); +/* gfs2_block_unmark clears ONE mark for the given block */ +extern int gfs2_block_unmark(struct gfs2_sbd *sdp, struct gfs2_block_list *il, + uint64_t block, enum gfs2_mark_block m); +/* gfs2_block_clear clears all the marks for the given block */ extern int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il, - uint64_t block, enum gfs2_mark_block m); + uint64_t block); extern int gfs2_block_check(struct gfs2_sbd *sdp, struct gfs2_block_list *il, uint64_t block, struct gfs2_block_query *val); extern void *gfs2_block_list_destroy(struct gfs2_sbd *sdp,