From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26104 invoked by alias); 10 Aug 2009 16:34:56 -0000 Received: (qmail 26098 invoked by alias); 10 Aug 2009 16:34:56 -0000 X-SWARE-Spam-Status: No, hits=3.0 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_21,J_CHICKENPOX_22,J_CHICKENPOX_25,J_CHICKENPOX_26,J_CHICKENPOX_31,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=3.0 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_21,J_CHICKENPOX_22,J_CHICKENPOX_25,J_CHICKENPOX_26,J_CHICKENPOX_31,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: 596c48c9d5ecc6c72ec2fd1339a0d94319edcd4b X-Git-Newrev: 7bf4eed5c57fa0536a0dba1f2c2bd094b5dd2769 From: Bob Peterson Message-Id: <20090810163428.4A9991201F1@lists.fedorahosted.org> Date: Mon, 10 Aug 2009 16:34: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/msg00170.txt.bz2 Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=7bf4eed5c57fa0536a0dba1f2c2bd094b5dd2769 Commit: 7bf4eed5c57fa0536a0dba1f2c2bd094b5dd2769 Parent: 596c48c9d5ecc6c72ec2fd1339a0d94319edcd4b Author: Bob Peterson AuthorDate: Mon Aug 10 11:23:47 2009 -0500 Committer: Bob Peterson CommitterDate: Mon Aug 10 11:31:27 2009 -0500 GFS2: fsck.gfs2 sometimes needs to be run twice bz 500483 This patch fixes numerous bugs whereby fsck.gfs2 was not "following through" with its changes, and therefore a second run was often needed to completely clean things up. --- gfs2/fsck/main.c | 6 +- gfs2/fsck/metawalk.c | 286 +++++++++++++++++++++++++++++++-------- gfs2/fsck/metawalk.h | 15 ++- gfs2/fsck/pass1.c | 373 +++++++++++++++++++++++++++++--------------------- gfs2/fsck/pass1b.c | 31 +++-- gfs2/fsck/pass1c.c | 215 ++++++++++------------------- gfs2/fsck/pass2.c | 63 ++++----- gfs2/fsck/pass4.c | 38 +++++- gfs2/fsck/pass5.c | 39 +++++- 9 files changed, 650 insertions(+), 416 deletions(-) diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c index 513c2b6..f74e09d 100644 --- a/gfs2/fsck/main.c +++ b/gfs2/fsck/main.c @@ -189,9 +189,9 @@ static int check_system_inode(struct gfs2_inode *sysinode, const char *filename, if(!sysinode || ds.q.block_type != mark) { log_err( _("Invalid or missing %s system inode.\n"), filename); errors_found++; - if ((errors_corrected += - query(&opts, _("Create new %s system inode? (y/n) "), - filename))) { + if (query(&opts, _("Create new %s system inode? (y/n) "), + filename)) { + errors_corrected++; builder(sysinode->i_sbd); gfs2_block_set(sysinode->i_sbd, bl, sysinode->i_di.di_num.no_addr, diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c index fda6058..b60b8ca 100644 --- a/gfs2/fsck/metawalk.c +++ b/gfs2/fsck/metawalk.c @@ -96,6 +96,17 @@ void fsck_inode_put(struct gfs2_inode *ip, enum update_flags update) } } +/** + * dirent_repair - attempt to repair a corrupt directory entry. + * @bh - The buffer header that contains the bad dirent + * @de - The directory entry in native format + * @dent - The directory entry in on-disk format + * @type - Type of directory (DIR_LINEAR or DIR_EXHASH) + * @first - TRUE if this is the first dirent in the buffer + * + * This function tries to repair a corrupt directory entry. All we + * know at this point is that the length field is wrong. + */ static int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, struct gfs2_dirent *de, struct gfs2_dirent *dent, int type, int first) @@ -111,8 +122,7 @@ static int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, else de->de_rec_len = ip->i_sbd->bsize - sizeof(struct gfs2_leaf); - } - else { + } else { bh_end = bh->b_data + ip->i_sbd->bsize; /* first, figure out a probable name length */ p = (char *)dent + sizeof(struct gfs2_dirent); @@ -135,8 +145,40 @@ static int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, return 0; } +/** + * dirblk_truncate - truncate a directory block + */ +static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb, + struct gfs2_buffer_head *bh) +{ + char *bh_end; + struct gfs2_dirent de; + uint16_t old_rec_len; + + bh_end = bh->b_data + ip->i_sbd->sd_sb.sb_bsize; + /* truncate the block to save the most dentries. To do this we + have to patch the previous dent. */ + gfs2_dirent_in(&de, (char *)fixb); + old_rec_len = de.de_rec_len; + de.de_rec_len = bh_end - (char *)fixb; + gfs2_dirent_out(&de, (char *)fixb); +} + +/* + * check_entries - check directory entries for a given block + * + * @ip - dinode associated with this leaf block + * bh - buffer for the leaf block + * type - type of block this is (linear or exhash) + * @update - set to 1 if the block was updated + * @count - set to the count entries + * @pass - structure pointing to pass-specific functions + * + * returns: 0 - good block or it was repaired to be good + * -1 - error occurred + */ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, - int eindex, int type, enum update_flags *update, + int type, enum update_flags *update, uint16_t *count, struct metawalk_fxns *pass) { struct gfs2_leaf *leaf = NULL; @@ -175,7 +217,8 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, filename = (char *)dent + sizeof(struct gfs2_dirent); if (de.de_rec_len < sizeof(struct gfs2_dirent) + - de.de_name_len || !de.de_name_len) { + de.de_name_len || + (de.de_inum.no_formal_ino && !de.de_name_len && !first)) { log_err( _("Directory block %llu (0x%llx" "), entry %d of directory %llu" "(0x%llx) is corrupt.\n"), @@ -187,18 +230,29 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, errors_found++; if (query(&opts, _("Attempt to repair it? (y/n) "))) { if (dirent_repair(ip, bh, &de, dent, type, - first)) - break; - else { + first)) { + if (first) /* make a new sentinel */ + dirblk_truncate(ip, dent, bh); + else + dirblk_truncate(ip, prev, bh); + *update = updated; + log_err( _("Unable to repair corrupt " + "directory entry; the " + "entry was removed " + "instead.\n")); + return 0; + } else { + log_err( _("Corrupt directory entry " + "repaired.\n")); errors_corrected++; *update = updated; + /* keep looping through dentries */ } - } - else { + } else { log_err( _("Corrupt directory entry ignored, " "stopped after checking %d entries.\n"), *count); - break; + return 0; } } if (!de.de_inum.no_formal_ino){ @@ -214,7 +268,19 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, (unsigned long long)bh->b_blocknr, (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); - return 1; + if (query(&opts, + _("Attempt to remove it? (y/n) "))) { + dirblk_truncate(ip, prev, bh); + *update = 1; + log_err(_("The corrupt directory " + "entry was removed.\n")); + } else { + log_err( _("Corrupt directory entry " + "ignored, stopped after " + "checking %d entries.\n"), + *count); + } + return 0; } } else { if (!de.de_inum.no_addr && first) { /* reverse sentinel */ @@ -236,9 +302,6 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, stack; return -1; } - /*if(error > 0) { - return 1; - }*/ } } @@ -293,8 +356,8 @@ static void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no, } /* Checks exhash directory entries */ -static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, - struct metawalk_fxns *pass) +static int check_leaf_blks(struct gfs2_inode *ip, enum update_flags *update, + struct metawalk_fxns *pass) { int error; struct gfs2_leaf leaf, oldleaf; @@ -344,8 +407,8 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, ref_count++; continue; } - if(gfs2_check_range(ip->i_sbd, old_leaf) == 0 && - ref_count != exp_count){ + 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: " @@ -450,9 +513,8 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, if(pass->check_dentry && S_ISDIR(ip->i_di.di_mode)) { - error = check_entries(ip, lbh, 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, @@ -465,9 +527,6 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, return -1; } - if(error > 0) - return 1; - if(update && (count != leaf.lf_entries)) { enum update_flags f = not_updated; @@ -499,7 +558,7 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, } /* FIXME: Need to get entry count and * compare it against leaf->lf_entries */ - break; + break; /* not a chain; go back to outer loop */ } else { brelse(lbh, *update); if(!leaf.lf_next) @@ -507,10 +566,10 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update, leaf_no = leaf.lf_next; log_debug( _("Leaf chain detected.\n")); } - } while(1); + } while(1); /* while we have chained leaf blocks */ old_leaf = leaf_no; memcpy(&oldleaf, &leaf, sizeof(oldleaf)); - } + } /* for every leaf block */ return 0; } @@ -534,12 +593,18 @@ static int check_eattr_entries(struct gfs2_inode *ip, sizeof(struct gfs2_meta_header)); while(1){ - error = pass->check_eattr_entry(ip, bh, ea_hdr, ea_hdr_prev, - pass->private); + if (ea_hdr->ea_type == GFS2_EATYPE_UNUSED) + error = 0; + else + error = pass->check_eattr_entry(ip, bh, ea_hdr, + ea_hdr_prev, + pass->private); if(error < 0) { stack; return -1; } + if (error > 0) + *update_it = updated; if(error == 0 && pass->check_eattr_extentry && ea_hdr->ea_num_ptrs) { uint32_t tot_ealen = 0; @@ -564,7 +629,6 @@ static int check_eattr_entries(struct gfs2_inode *ip, update_it, pass->private)) { errors_found++; - error = 1; if (query(&opts, _("Repair the bad " "Extended Attribute? " "(y/n) "))) { @@ -582,9 +646,11 @@ static int check_eattr_entries(struct gfs2_inode *ip, gfs2_meta_eattr); log_err( _("The EA was " "fixed.\n")); - } else + } else { + error = 1; log_err( _("The bad EA was " "not fixed.\n")); + } } tot_ealen += sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header); @@ -618,23 +684,33 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block, { struct gfs2_buffer_head *bh = NULL; int error = 0; + enum update_flags updated_this_leaf = not_updated; log_debug( _("Checking EA leaf block #%"PRIu64" (0x%" PRIx64 ").\n"), block, block); if(pass->check_eattr_leaf) { error = pass->check_eattr_leaf(ip, block, parent, &bh, - want_updated, pass->private); + &updated_this_leaf, + pass->private); + if (updated_this_leaf) /* if this leaf was updated */ + *want_updated = updated; /* signal it for the parent */ if(error < 0) { stack; return -1; } if(error > 0) { + if (bh) + brelse(bh, updated_this_leaf); return 1; } if (bh) { - error = check_eattr_entries(ip, bh, pass, want_updated); - brelse(bh, *want_updated); + error = check_eattr_entries(ip, bh, pass, + &updated_this_leaf); + brelse(bh, updated_this_leaf); + if (updated_this_leaf) /* if this leaf was updated */ + *want_updated = updated; /* signal it for + the parent */ } return error; } @@ -651,12 +727,16 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block, */ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect, enum update_flags *want_updated, - struct metawalk_fxns *pass){ + struct metawalk_fxns *pass) +{ int error = 0; uint64_t *ea_leaf_ptr, *end; uint64_t block; struct gfs2_buffer_head *indirect_buf = NULL; struct gfs2_sbd *sdp = ip->i_sbd; + enum update_flags update_indir_block = not_updated; + int first_ea_is_bad = 0; + uint64_t di_eattr_save = ip->i_di.di_eattr; *want_updated = not_updated; log_debug( _("Checking EA indirect block #%"PRIu64" (0x%" PRIx64 ").\n"), @@ -671,27 +751,69 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect, int leaf_pointers = 0, leaf_pointer_errors = 0; ea_leaf_ptr = (uint64_t *)(indirect_buf->b_data - + sizeof(struct gfs2_meta_header)); + + sizeof(struct gfs2_meta_header)); end = ea_leaf_ptr + ((sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_meta_header)) / 8); + - sizeof(struct gfs2_meta_header)) / 8); while(*ea_leaf_ptr && (ea_leaf_ptr < end)){ block = be64_to_cpu(*ea_leaf_ptr); leaf_pointers++; error = check_leaf_eattr(ip, block, indirect, want_updated, pass); - if (error) + if (error) { leaf_pointer_errors++; + if (update_indir_block == not_updated) { + errors_found++; + if (query(&opts, _("Fix the indirect " + "block too? (y/n) "))) { + update_indir_block = updated; + errors_corrected++; + *ea_leaf_ptr = 0; + } + } else + *ea_leaf_ptr = 0; + } + /* If the first eattr lead is bad, we can't have + a hole, so we have to treat this as an unrecoverable + eattr error and delete all eattr info. Calling + finish_eattr_indir here causes ip->i_di.di_eattr = 0 + and that ensures that subsequent calls to + check_leaf_eattr result in the eattr + check_leaf_block nuking them all "due to previous + errors" */ + if (leaf_pointers == 1 && leaf_pointer_errors == 1) { + first_ea_is_bad = 1; + if (pass->finish_eattr_indir) + pass->finish_eattr_indir(ip, + leaf_pointers, + leaf_pointer_errors, + want_updated, + pass->private); + } else if (leaf_pointer_errors) { + /* This is a bit tricky. We can't have eattr + holes. So if we have 4 good eattrs, 1 bad + eattr and 5 more good ones: GGGGBGGGGG, + we need to tell check_leaf_eattr to delete + all eattrs after the bad one. So we want: + GGGG when we finish. To do that, we set + di_eattr to 0 temporarily. */ + ip->i_di.di_eattr = 0; + } ea_leaf_ptr++; } if (pass->finish_eattr_indir) { - int indir_ok = 1; - - if (leaf_pointer_errors == leaf_pointers) - indir_ok = 0; - pass->finish_eattr_indir(ip, indir_ok, want_updated, - pass->private); - if (!indir_ok) { + if (!first_ea_is_bad) { + /* If the first ea is good but subsequent ones + were bad and deleted, we need to restore + the saved di_eattr block. */ + if (leaf_pointer_errors) + ip->i_di.di_eattr = di_eattr_save; + pass->finish_eattr_indir(ip, leaf_pointers, + leaf_pointer_errors, + want_updated, + pass->private); + } + if (leaf_pointer_errors == leaf_pointers) { if (*want_updated) gfs2_set_bitmap(sdp, indirect, GFS2_BLKST_FREE); @@ -702,7 +824,7 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect, } } if (indirect_buf) - brelse(indirect_buf, not_updated); + brelse(indirect_buf, update_indir_block); return error; } @@ -718,9 +840,8 @@ int check_inode_eattr(struct gfs2_inode *ip, enum update_flags *want_updated, { int error = 0; - if(!ip->i_di.di_eattr){ + if(!ip->i_di.di_eattr) return 0; - } log_debug( _("Extended attributes exist for inode #%llu (0x%llx).\n"), (unsigned long long)ip->i_di.di_num.no_addr, @@ -731,9 +852,10 @@ int check_inode_eattr(struct gfs2_inode *ip, enum update_flags *want_updated, want_updated, pass))) stack; } else { - if((error = check_leaf_eattr(ip, ip->i_di.di_eattr, - ip->i_di.di_num.no_addr, - want_updated, pass))) + error = check_leaf_eattr(ip, ip->i_di.di_eattr, + ip->i_di.di_num.no_addr, + want_updated, pass); + if (error) stack; } @@ -788,7 +910,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, (char *)ptr < (bh->b_data + ip->i_sbd->bsize); ptr++) { nbh = NULL; - + if (!*ptr) continue; @@ -823,6 +945,7 @@ fail: while (!osi_list_empty(list)) { nbh = osi_list_entry(list->next, struct gfs2_buffer_head, b_altlist); + brelse(nbh, not_updated); osi_list_del(&nbh->b_altlist); } } @@ -917,7 +1040,7 @@ end: if (S_ISDIR(ip->i_di.di_mode)) { /* check validity of leaf blocks and leaf chains */ if (ip->i_di.di_flags & GFS2_DIF_EXHASH) { - error = check_leaf(ip, &update, pass); + error = check_leaf_blks(ip, &update, pass); if(error < 0) return -1; if(error > 0) @@ -935,7 +1058,7 @@ static int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh, int error = 0; uint16_t count = 0; - error = check_entries(ip, bh, 0, DIR_LINEAR, update, &count, pass); + error = check_entries(ip, bh, DIR_LINEAR, update, &count, pass); if(error < 0) { stack; return -1; @@ -956,7 +1079,7 @@ int check_dir(struct gfs2_sbd *sbp, uint64_t block, struct metawalk_fxns *pass) ip = fsck_inode_get(sbp, bh); if(ip->i_di.di_flags & GFS2_DIF_EXHASH) { - error = check_leaf(ip, &update, pass); + error = check_leaf_blks(ip, &update, pass); if(error < 0) { stack; fsck_inode_put(ip, not_updated); /* does brelse(bh); */ @@ -1104,3 +1227,56 @@ int dinode_hash_remove(osi_list_t *buckets, uint64_t key) } return -1; } + +/** + * delete_blocks - delete blocks associated with an inode + */ +int delete_blocks(struct gfs2_inode *ip, uint64_t block, + struct gfs2_buffer_head **bh, const char *btype, + void *private) +{ + struct gfs2_block_query q = {0}; + + if (gfs2_check_range(ip->i_sbd, block) == 0) { + if (gfs2_block_check(ip->i_sbd, bl, block, &q)) + return 0; + if (!q.dup_block) { + log_info( _("Deleting %s block %lld (0x%llx) as part " + "of inode %lld (0x%llx)\n"), btype, + (unsigned long long)block, + (unsigned long long)block, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); + gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_free); + gfs2_free_block(ip->i_sbd, block); + } + } + return 0; +} + +int delete_metadata(struct gfs2_inode *ip, uint64_t block, + struct gfs2_buffer_head **bh, void *private) +{ + return delete_blocks(ip, block, bh, _("metadata"), private); +} + +int delete_data(struct gfs2_inode *ip, uint64_t block, void *private) +{ + return delete_blocks(ip, block, NULL, _("data"), private); +} + +int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent, + struct gfs2_buffer_head **bh, + enum update_flags *want_updated, void *private) +{ + return delete_blocks(ip, block, NULL, _("indirect extended attribute"), + private); +} + +int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent, + struct gfs2_buffer_head **bh, + enum update_flags *want_updated, void *private) +{ + return delete_blocks(ip, block, NULL, _("extended attribute"), + private); +} diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h index fd8f1b7..0721ed1 100644 --- a/gfs2/fsck/metawalk.h +++ b/gfs2/fsck/metawalk.h @@ -16,6 +16,18 @@ int remove_dentry_from_dir(struct gfs2_sbd *sbp, uint64_t dir, int find_di(struct gfs2_sbd *sbp, uint64_t childblock, struct dir_info **dip); int dinode_hash_insert(osi_list_t *buckets, uint64_t key, struct dir_info *di); int dinode_hash_remove(osi_list_t *buckets, uint64_t key); +int delete_blocks(struct gfs2_inode *ip, uint64_t block, + struct gfs2_buffer_head **bh, const char *btype, + void *private); +int delete_metadata(struct gfs2_inode *ip, uint64_t block, + struct gfs2_buffer_head **bh, void *private); +int delete_data(struct gfs2_inode *ip, uint64_t block, void *private); +int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent, + struct gfs2_buffer_head **bh, + enum update_flags *want_updated, void *private); +int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent, + struct gfs2_buffer_head **bh, + enum update_flags *want_updated, void *private); /* metawalk_fxns: function pointers to check various parts of the fs * @@ -66,7 +78,8 @@ struct metawalk_fxns { struct gfs2_ea_header *ea_hdr_prev, enum update_flags *want_updated, void *private); - int (*finish_eattr_indir) (struct gfs2_inode *ip, int indir_ok, + int (*finish_eattr_indir) (struct gfs2_inode *ip, int leaf_pointers, + int leaf_pointer_errors, enum update_flags *want_updated, void *private); }; diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c index 5abe00d..dfed4fd 100644 --- a/gfs2/fsck/pass1.c +++ b/gfs2/fsck/pass1.c @@ -53,7 +53,8 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, struct gfs2_ea_header *ea_hdr_prev, enum update_flags *want_updated, void *private); -static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok, +static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers, + int leaf_pointer_errors, enum update_flags *want_updated, void *private); struct metawalk_fxns pass1_fxns = { @@ -105,20 +106,21 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block, if(q.block_type != gfs2_block_free) { 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); + gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block); found_dup = 1; } nbh = bread(&ip->i_sbd->buf_list, block); if (gfs2_check_meta(nbh, GFS2_METATYPE_IN)){ - log_debug( _("Bad indirect block pointer " - "(points to something that is not an indirect block).\n")); + log_debug( _("Bad indirect block pointer (points to " + "something that is not an indirect block).\n")); if(!found_dup) { gfs2_block_set(ip->i_sbd, bl, block, gfs2_meta_inval); brelse(nbh, not_updated); return 1; } - }else /* blk check ok */ + brelse(nbh, not_updated); + } else /* blk check ok */ *bh = nbh; if (!found_dup) { @@ -138,15 +140,22 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private) int error = 0, btype; if (gfs2_check_range(ip->i_sbd, block)) { - log_err( _("Bad data block pointer (out of range)\n")); + log_err( _("inode %lld (0x%llx) has a bad data block pointer " + "%lld (out of range)\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)block); /* Mark the owner of this block with the bad_block - * designator so we know to check it for out of range blocks later */ + * designator so we know to check it for out of range + * blocks later */ gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr, gfs2_bad_block); return 1; } if(gfs2_block_check(ip->i_sbd, bl, block, &q)) { stack; + log_err( _("Found bad block referenced as data at %" + PRIu64 " (0x%"PRIx64 ")\n"), block, block); return -1; } if(q.block_type != gfs2_block_free) { @@ -154,6 +163,10 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private) PRIu64 " (0x%"PRIx64 ")\n"), block, block); if (q.block_type != gfs2_meta_inval) { gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block); + /* If the prev ref was as data, this is likely a data + block, so keep the block count for both refs. */ + if (q.block_type == gfs2_block_used) + bc->data_count++; return 1; } /* An explanation is in order here. At this point we found @@ -210,6 +223,51 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private) return error; } +static int remove_inode_eattr(struct gfs2_inode *ip, struct block_count *bc, + int duplicate, enum update_flags *want_updated) +{ + if (!duplicate) { + gfs2_set_bitmap(ip->i_sbd, ip->i_di.di_eattr, + GFS2_BLKST_FREE); + gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_eattr, + gfs2_block_free); + } + ip->i_di.di_eattr = 0; + bc->ea_count = 0; + ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count; + ip->i_di.di_flags &= ~GFS2_DIF_EA_INDIRECT; + *want_updated = updated; + return 0; +} + +static int ask_remove_inode_eattr(struct gfs2_inode *ip, + struct block_count *bc, + enum update_flags *want_updated) +{ + log_err( _("Inode %lld (0x%llx) has unrecoverable Extended Attribute " + "errors.\n"), (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); + errors_found++; + if (query(&opts, _("Clear all Extended Attributes from the " + "inode? (y/n) "))) { + struct gfs2_block_query q; + + errors_corrected++; + if(gfs2_block_check(ip->i_sbd, bl, ip->i_di.di_eattr, &q)) { + stack; + return -1; + } + if (!remove_inode_eattr(ip, bc, q.dup_block, want_updated)) + log_err( _("Extended attributes were removed.\n")); + else + log_err( _("Unable to remove inode eattr pointer; " + "the error remains.\n")); + } else { + log_err( _("Extended attributes were not removed.\n")); + } + return 0; +} + /* clear_eas - clear the extended attributes for an inode * * @ip - in core inode pointer @@ -229,27 +287,22 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc, log_err( _("Inode #%llu (0x%llx): %s"), (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr, emsg); - if (block) - log_err( _(" at block #%" PRIu64 " (0x%" PRIx64 ")"), - block, block); - log_err(".\n"); + log_err( _(" at block #%lld (0x%llx).\n"), + (unsigned long long)block, (unsigned long long)block); errors_found++; - if ((errors_corrected += query(&opts, _("Clear the bad Extended " - "Attribute? (y/n) ")))) { - if (block == 0) - block = ip->i_di.di_eattr; - if (!duplicate) { + if (query(&opts, _("Clear the bad Extended Attribute? (y/n) "))) { + errors_corrected++; + if (block == ip->i_di.di_eattr) { + remove_inode_eattr(ip, bc, duplicate, want_updated); + log_err( _("The bad extended attribute was " + "removed.\n")); + } else if (!duplicate) { gfs2_block_set(sdp, bl, block, gfs2_block_free); gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE); + log_err( _("The bad Extended Attribute was " + "removed.\n")); } - ip->i_di.di_flags &= ~GFS2_DIF_EA_INDIRECT; - if (block == ip->i_di.di_eattr) - ip->i_di.di_eattr = 0; - bc->ea_count = 0; - ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count; - gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data); *want_updated = updated; - log_err( _("The bad Extended Attribute was removed.\n")); return 1; } else { log_err( _("The bad Extended Attribute was not fixed.\n")); @@ -292,8 +345,8 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect, if (!clear_eas(ip, bc, indirect, 1, want_updated, _("Bad indirect Extended Attribute " "duplicate found"))) { - gfs2_block_set(sdp, bl, indirect, - gfs2_dup_block); + gfs2_block_mark(sdp, bl, indirect, + gfs2_dup_block); bc->ea_count++; } return 1; @@ -311,7 +364,7 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect, (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); + gfs2_block_mark(sdp, bl, indirect, gfs2_dup_block); bc->ea_count++; ret = 1; } else { @@ -324,21 +377,99 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect, return ret; } -static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok, +static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers, + int leaf_pointer_errors, enum update_flags *want_updated, void *private) { - if (indir_ok) { - log_debug( _("Marking inode #%llu (0x%llx) with eattr block\n"), - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr); - /* Mark the inode as having an eattr in the block map - so pass1c can check it. */ - gfs2_block_mark(ip->i_sbd, bl, ip->i_di.di_num.no_addr, - gfs2_eattr_block); + struct block_count *bc = (struct block_count *) private; + + if (leaf_pointer_errors == leaf_pointers) /* All eas were bad */ + return ask_remove_inode_eattr(ip, bc, want_updated); + log_debug( _("Marking inode #%llu (0x%llx) with extended " + "attribute block\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); + /* Mark the inode as having an eattr in the block map + so pass1c can check it. */ + gfs2_block_mark(ip->i_sbd, bl, ip->i_di.di_num.no_addr, + gfs2_eattr_block); + bc->ea_count++; + if (!leaf_pointer_errors) return 0; + log_err( _("Inode %lld (0x%llx) has recoverable indirect " + "Extended Attribute errors.\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); + errors_found++; + if (query(&opts, _("Okay to fix the block count for the inode? " + "(y/n) "))) { + errors_corrected++; + ip->i_di.di_blocks = 1 + bc->indir_count + + bc->data_count + bc->ea_count; + *want_updated = updated; + log_err( _("Block count fixed.\n")); + return 1; + } + log_err( _("Block count not fixed.\n")); + return 1; +} + +static int check_leaf_block(struct gfs2_inode *ip, uint64_t block, int btype, + struct gfs2_buffer_head **bh, + enum update_flags *want_updated, void *private) +{ + struct gfs2_buffer_head *leaf_bh = NULL; + struct gfs2_sbd *sdp = ip->i_sbd; + struct gfs2_block_query q = {0}; + struct block_count *bc = (struct block_count *) private; + + if(gfs2_block_check(sdp, bl, block, &q)) { + stack; + return -1; + } + /* Special duplicate processing: If we have an EA block, check if it + really is an EA. If it is, let duplicate handling sort it out. + If it isn't, clear it but don't count it as a duplicate. */ + leaf_bh = bread(&sdp->buf_list, block); + if(gfs2_check_meta(leaf_bh, btype)) { + if(q.block_type != gfs2_block_free) { /* Duplicate? */ + clear_eas(ip, bc, block, 1, want_updated, + _("Bad Extended Attribute duplicate found")); + } else { + clear_eas(ip, bc, block, 0, want_updated, + _("Extended Attribute leaf block " + "has incorrect type")); + } + brelse(leaf_bh, *want_updated); + return 1; + } + if(q.block_type != gfs2_block_free) { /* Duplicate? */ + log_debug( _("Duplicate block found at #%lld (0x%llx).\n"), + (unsigned long long)block, + (unsigned long long)block); + gfs2_block_mark(sdp, bl, block, gfs2_dup_block); + bc->ea_count++; + brelse(leaf_bh, not_updated); + return 1; + } + if (ip->i_di.di_eattr == 0) { + /* Can only get in here if there were unrecoverable ea + errors that caused clear_eas to be called. What we + need to do here is remove the subsequent ea blocks. */ + clear_eas(ip, bc, block, 0, want_updated, + _("Extended Attribute block removed due to " + "previous errors.\n")); + brelse(leaf_bh, *want_updated); + return 1; } - clear_eas(ip, (struct block_count *)private, 0, 0, want_updated, - _("has unrecoverable indirect Extended Attribute errors")); + log_debug( _("Setting block #%lld (0x%llx) to eattr block\n"), + (unsigned long long)block, (unsigned long long)block); + /* Point of confusion: We've got to set the ea block itself to + gfs2_meta_eattr here. Elsewhere we mark the inode with + gfs2_eattr_block meaning it contains an eattr for pass1c. */ + gfs2_block_set(sdp, bl, block, gfs2_meta_eattr); + bc->ea_count++; + *bh = leaf_bh; return 0; } @@ -360,16 +491,14 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, enum update_flags *want_updated, void *private) { - struct gfs2_buffer_head *el_buf; - struct gfs2_sbd *sdp = ip->i_sbd; - struct gfs2_block_query q; uint64_t el_blk = be64_to_cpu(*data_ptr); - struct block_count *bc = (struct block_count *) private; - int ret = 0; + struct gfs2_sbd *sdp = ip->i_sbd; + struct gfs2_buffer_head *bh = NULL; + int error; if(gfs2_check_range(sdp, el_blk)){ log_err( _("Inode #%llu (0x%llx): Extended Attribute block " - "%llu (0x%llx) has an extemded leaf block #%llu " + "%llu (0x%llx) has an extended leaf block #%llu " "(0x%llx) that is out of range.\n"), (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr, @@ -380,46 +509,11 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, gfs2_block_set(sdp, bl, ip->i_di.di_eattr, gfs2_bad_block); return 1; } - - if(gfs2_block_check(sdp, bl, el_blk, &q)) { - stack; - return -1; - } - el_buf = bread(&sdp->buf_list, el_blk); - - /* Special duplicate processing: If we have an EA block, - check if it really is an EA. If it is, let duplicate - handling sort it out. If it isn't, clear it but don't - count it as a duplicate. */ - if(gfs2_check_meta(el_buf, GFS2_METATYPE_ED)) { - if(q.block_type != gfs2_block_free) /* Duplicate? */ - clear_eas(ip, bc, el_blk, 1, want_updated, - _("has bad extended Extended Attribute " - "duplicate")); - else - clear_eas(ip, bc, el_blk, 0, want_updated, - _("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? */ - log_debug( _("Duplicate block found at #%" PRIu64 - " (0x%" PRIx64 ").\n"), - el_blk, el_blk); - gfs2_block_set(sdp, bl, el_blk, gfs2_dup_block); - bc->ea_count++; - ret = 1; - } else { - log_debug( _("Setting block #%" PRIu64 - " (0x%" PRIx64 ") to eattr block\n"), - el_blk, el_blk); - gfs2_block_set(sdp, bl, el_blk, gfs2_meta_eattr); - bc->ea_count++; - } - } - - brelse(el_buf, not_updated); - return ret; + error = check_leaf_block(ip, el_blk, GFS2_METATYPE_ED, &bh, + want_updated, private); + if (bh) + brelse(bh, not_updated); + return error; } static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, @@ -427,75 +521,28 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, enum update_flags *want_updated, void *private) { struct gfs2_sbd *sdp = ip->i_sbd; - struct gfs2_buffer_head *leaf_bh = NULL; - int ret = 0; - struct gfs2_block_query q = {0}; - struct block_count *bc = (struct block_count *) private; - /* 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*/ - 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)){ + * eattr attributes points to a non-zero block. + * Clarification: If we're here we're checking a leaf block, and the + * source dinode needs to be marked as having extended attributes. + * That instructs pass1c to check the contents of the ea blocks. */ + log_debug( _("Setting inode %lld (0x%llx) as having eattr " + "block(s) attached.\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); + gfs2_block_mark(sdp, bl, ip->i_di.di_num.no_addr, gfs2_eattr_block); + if(gfs2_check_range(sdp, block)) { log_warn( _("Inode #%llu (0x%llx): Extended Attribute leaf " "block #%llu (0x%llx) is out of range.\n"), (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)block, (unsigned long long)block); gfs2_block_set(sdp, bl, ip->i_di.di_eattr, gfs2_bad_block); - ret = 1; - } - else if(gfs2_block_check(sdp, bl, block, &q)) { - stack; - return -1; - } - else { - /* Special duplicate processing: If we have an EA block, - check if it really is an EA. If it is, let duplicate - handling sort it out. If it isn't, clear it but don't - count it as a duplicate. */ - leaf_bh = bread(&sdp->buf_list, block); - if(gfs2_check_meta(leaf_bh, GFS2_METATYPE_EA)) { - if(q.block_type != gfs2_block_free) { /* Duplicate? */ - clear_eas(ip, bc, block, 1, want_updated, - _("Bad Extended Attribute duplicate " - "found")); - } else { - clear_eas(ip, bc, block, 0, want_updated, - _("Extended Attribute leaf block " - "has incorrect type")); - } - ret = 1; - brelse(leaf_bh, not_updated); - } else { /* If this looks like an EA */ - if(q.block_type != gfs2_block_free) { /* Duplicate? */ - log_debug( _("Duplicate block found at #%" PRIu64 - " (0x%" PRIx64 ").\n"), - block, block); - gfs2_block_set(sdp, bl, block, gfs2_dup_block); - bc->ea_count++; - ret = 1; - brelse(leaf_bh, not_updated); - } else { - log_debug( _("Setting block #%" PRIu64 - " (0x%" PRIx64 ") to eattr block\n"), - block, block); - gfs2_block_set(sdp, bl, block, - gfs2_meta_eattr); - bc->ea_count++; - } - } + return 1; } - if (!ret) - *bh = leaf_bh; - - return ret; + return check_leaf_block(ip, block, GFS2_METATYPE_EA, bh, want_updated, + private); } static int check_eattr_entries(struct gfs2_inode *ip, @@ -577,7 +624,9 @@ static int clear_leaf(struct gfs2_inode *ip, uint64_t block, struct gfs2_buffer_head *bh, void *private) { struct gfs2_block_query q = {0}; - log_crit("Clearing leaf #%" PRIu64 " (0x%" PRIx64 ")\n", block, block); + + log_crit( _("Clearing leaf #%" PRIu64 " (0x%" PRIx64 ")\n"), + block, block); if(gfs2_block_check(ip->i_sbd, bl, block, &q)) { stack; @@ -593,7 +642,6 @@ static int clear_leaf(struct gfs2_inode *ip, uint64_t block, return 0; } return 0; - } int add_to_dir_list(struct gfs2_sbd *sbp, uint64_t block) @@ -649,9 +697,10 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh, (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, _("Fix address in inode at block #%" - PRIu64 " (0x%" PRIx64 ")? (y/n) "), block, block))) { + if(query(&opts, _("Fix address in inode at block #%" + PRIu64 " (0x%" PRIx64 ")? (y/n) "), + block, block)) { + errors_corrected++; ip->i_di.di_num.no_addr = ip->i_di.di_num.no_formal_ino = block; gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data); f = updated; @@ -807,7 +856,11 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh, return 0; } - check_inode_eattr(ip, &f, &pass1_fxns); + error = check_inode_eattr(ip, &f, &pass1_fxns); + + if (error && f == updated && + !(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT)) + ask_remove_inode_eattr(ip, &bc, &f); if (ip->i_di.di_blocks != (1 + bc.indir_count + bc.data_count + bc.ea_count)) { @@ -818,9 +871,15 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh, (unsigned long long)ip->i_di.di_blocks, (unsigned long long)1 + bc.indir_count + bc.data_count + bc.ea_count); + log_info( _("inode has: %lld, but fsck counts: Dinode:1 + " + "indir:%lld + data: %lld + ea: %lld\n"), + (unsigned long long)ip->i_di.di_blocks, + (unsigned long long)bc.indir_count, + (unsigned long long)bc.data_count, + (unsigned long long)bc.ea_count); errors_found++; - if ((errors_corrected += - query(&opts, _("Fix ondisk block count? (y/n) ")))) { + if (query(&opts, _("Fix ondisk block count? (y/n) "))) { + errors_corrected++; ip->i_di.di_blocks = 1 + bc.indir_count + bc.data_count + bc.ea_count; gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data); @@ -871,12 +930,12 @@ static int scan_meta(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh, return -1; } } - /* Ignore everything else - they should be hit by the handle_di step. */ - /* Don't check NONE either, because check_meta passes everything if */ - /* GFS2_METATYPE_NONE is specified. */ - /* Hopefully, other metadata types such as indirect blocks will be */ - /* handled when the inode itself is processed, and if it's not, it */ - /* should be caught in pass5. */ + /* Ignore everything else - they should be hit by the handle_di step. + * Don't check NONE either, because check_meta passes everything if + * GFS2_METATYPE_NONE is specified. + * Hopefully, other metadata types such as indirect blocks will be + * handled when the inode itself is processed, and if it's not, it + * should be caught in pass5. */ return 0; } diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c index c02317b..3cf102b 100644 --- a/gfs2/fsck/pass1b.c +++ b/gfs2/fsck/pass1b.c @@ -166,7 +166,7 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block, return 1; if(block == dh->b->block_no) { log_err( _("Found duplicate reference in inode \"%s\" at " - "block #%llu (0x%llx) with block #%llu (0x%llx)\n"), + "block #%llu (0x%llx) to 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, @@ -426,9 +426,11 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b) (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")); + if (query(&opts, _("Clear the inode? (y/n) "))) { + errors_corrected++; + log_warn( _("Clearing inode %lld (0x%llx)...\n"), + (unsigned long long)id->block_no, + (unsigned long long)id->block_no); ip = fsck_load_inode(sbp, id->block_no); inode_hash_remove(inode_hash, ip->i_di.di_num.no_addr); @@ -459,13 +461,21 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b) (unsigned long long)id->block_no, id->dup_count, (unsigned long long)b->block_no, (unsigned long long)b->block_no); + } + osi_list_foreach(tmp, &b->ref_inode_list) { + id = osi_list_entry(tmp, struct inode_with_dups, list); errors_found++; - if (!(errors_corrected += - query(&opts, _("Clear the inode? (y/n) ")))) { + if (!(query(&opts, _("Okay to clear inode %lld (0x%llx)? " + "(y/n) "), + (unsigned long long)id->block_no, + (unsigned long long)id->block_no))) { log_warn( _("The bad inode was not cleared...\n")); continue; } - log_warn( _("Clearing...\n")); + errors_corrected++; + log_warn( _("Clearing inode %lld (0x%llx)...\n"), + (unsigned long long)id->block_no, + (unsigned long long)id->block_no); ip = fsck_load_inode(sbp, id->block_no); dh.b = b; dh.id = id; @@ -554,13 +564,10 @@ int pass1b(struct gfs2_sbd *sbp) * it later */ log_info( _("Handling duplicate blocks\n")); out: - while (!osi_list_empty(&sbp->dup_blocks.list)) { - b = osi_list_entry(sbp->dup_blocks.list.next, - struct dup_blocks, list); + osi_list_foreach_safe(tmp, &sbp->dup_blocks.list, x) { + b = osi_list_entry(tmp, struct dup_blocks, list); if (!skip_this_pass && !rc) /* no error & not asked to skip the rest */ handle_dup_blk(sbp, b); - osi_list_del(&b->list); - free(b); } return rc; } diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c index e0f4576..62026f6 100644 --- a/gfs2/fsck/pass1c.c +++ b/gfs2/fsck/pass1c.c @@ -15,32 +15,72 @@ static int remove_eattr_entry(struct gfs2_sbd *sdp, struct gfs2_ea_header *curr, struct gfs2_ea_header *prev) { - log_err( _("Bad Extended Attribute at block #%"PRIu64 - " (0x%" PRIx64 ") removed.\n"), - leaf_bh->b_blocknr, leaf_bh->b_blocknr); - if(!prev) + if (!prev) curr->ea_type = GFS2_EATYPE_UNUSED; else { - prev->ea_rec_len = - be32_to_cpu(curr->ea_rec_len); + uint32_t tmp32 = be32_to_cpu(curr->ea_rec_len) + be32_to_cpu(prev->ea_rec_len); - cpu_to_be32(curr->ea_rec_len + prev->ea_rec_len); - + prev->ea_rec_len = cpu_to_be32(tmp32); if (curr->ea_flags & GFS2_EAFLAG_LAST) prev->ea_flags |= GFS2_EAFLAG_LAST; } + log_err( _("Bad Extended Attribute at block #%"PRIu64 + " (0x%" PRIx64 ") removed.\n"), + leaf_bh->b_blocknr, leaf_bh->b_blocknr); return 0; } +static int ask_remove_eattr_entry(struct gfs2_sbd *sdp, + struct gfs2_buffer_head *leaf_bh, + struct gfs2_ea_header *curr, + struct gfs2_ea_header *prev, + int fix_curr, int fix_curr_len) +{ + errors_found++; + if (query(&opts, _("Remove the bad Extended Attribute entry? " + "(y/n) "))) { + errors_corrected++; + if (fix_curr) + curr->ea_flags |= GFS2_EAFLAG_LAST; + if (fix_curr_len) { + uint32_t max_size = sdp->sd_sb.sb_bsize; + uint32_t offset = (uint32_t)(((unsigned long)curr) - + ((unsigned long)leaf_bh->b_data)); + curr->ea_rec_len = cpu_to_be32(max_size - offset); + } + if (remove_eattr_entry(sdp, leaf_bh, curr, prev)) { + stack; + return -1; + } + } else { + log_err( _("Bad Extended Attribute not removed.\n")); + } + return 1; +} + +static int ask_remove_eattr(struct gfs2_inode *ip, + enum update_flags *need_update) +{ + errors_found++; + if (query(&opts, _("Remove the bad Extended Attribute? (y/n) "))) { + errors_corrected++; + ip->i_di.di_eattr = 0; + *need_update = updated; + log_err( _("Bad Extended Attribute removed.\n")); + } else + log_err( _("Bad Extended Attribute not removed.\n")); + return 1; +} + static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, - enum update_flags *update, void *private) + enum update_flags *need_update, void *private) { struct gfs2_sbd *sbp = ip->i_sbd; struct gfs2_block_query q; struct gfs2_buffer_head *indir_bh = NULL; - *update = not_updated; + *need_update = not_updated; if(gfs2_check_range(sbp, block)) { log_err( _("Extended attributes indirect block #%llu" " (0x%llx) for inode #%llu" @@ -49,16 +89,7 @@ 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); - 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; + return ask_remove_eattr(ip, need_update); } else if (gfs2_block_check(sbp, bl, block, &q)) { stack; @@ -72,17 +103,7 @@ 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); - 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; + return ask_remove_eattr(ip, need_update); } else indir_bh = bread(&sbp->buf_list, block); @@ -93,28 +114,17 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block, static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, - enum update_flags *update, void *private) + enum update_flags *need_update, void *private) { struct gfs2_sbd *sbp = ip->i_sbd; struct gfs2_block_query q; - *update = not_updated; if(gfs2_check_range(sbp, block)) { log_err( _("Extended attributes block for inode #%llu" " (0x%llx) out of range.\n"), (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); - 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; + return ask_remove_eattr(ip, need_update); } else if (gfs2_block_check(sbp, bl, block, &q)) { stack; @@ -125,17 +135,7 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, " (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; + return ask_remove_eattr(ip, need_update); } else *bh = bread(&sbp->buf_list, block); @@ -157,73 +157,24 @@ static int check_eattr_entry(struct gfs2_inode *ip, if(!ea_hdr->ea_name_len){ log_err( _("EA has name length == 0\n")); - 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; + return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev, 1, 1); } if(offset + be32_to_cpu(ea_hdr->ea_rec_len) > max_size){ log_err( _("EA rec length too long\n")); - 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; + return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev, 1, 1); } if(offset + be32_to_cpu(ea_hdr->ea_rec_len) == max_size && (ea_hdr->ea_flags & GFS2_EAFLAG_LAST) == 0){ log_err( _("last EA has no last entry flag\n")); - 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; + 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")); - 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; + return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev, 0, 0); } memset(ea_name, 0, sizeof(ea_name)); @@ -234,19 +185,8 @@ static int check_eattr_entry(struct gfs2_inode *ip, ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){ log_err( _("EA (%s) type is invalid (%d > %d).\n"), ea_name, ea_hdr->ea_type, GFS2_EATYPE_LAST); - 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; + return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr, + ea_hdr_prev, 0, 0); } if(ea_hdr->ea_num_ptrs){ @@ -258,23 +198,10 @@ static int check_eattr_entry(struct gfs2_inode *ip, if(max_ptrs > ea_hdr->ea_num_ptrs){ log_err( _("EA (%s) has incorrect number of pointers.\n"), ea_name); - log_err( _(" Required: %d\n" - " Reported: %d\n"), - max_ptrs, ea_hdr->ea_num_ptrs); - 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; + log_err( _(" Required: %d\n Reported: %d\n"), + max_ptrs, ea_hdr->ea_num_ptrs); + 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); @@ -292,7 +219,6 @@ static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_ptr, struct gfs2_block_query q; struct gfs2_sbd *sbp = ip->i_sbd; - *want_updated = not_updated; if(gfs2_block_check(sbp, bl, be64_to_cpu(*ea_ptr), &q)) { stack; return -1; @@ -330,11 +256,12 @@ int pass1c(struct gfs2_sbd *sbp) osi_list_foreach_safe(tmp, &sbp->eattr_blocks.list, x) { ea_block = osi_list_entry(tmp, struct special_blocks, list); block_no = ea_block->block; + warm_fuzzy_stuff(block_no); if (skip_this_pass || fsck_abort) /* if asked to skip the rest */ return FSCK_OK; bh = bread(&sbp->buf_list, block_no); - if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) { /* if a dinode */ + if (!gfs2_check_meta(bh, GFS2_METATYPE_DI)) { /* if a dinode */ log_info( _("EA in inode %"PRIu64" (0x%" PRIx64 ")\n"), block_no, block_no); gfs2_block_unmark(sbp, bl, block_no, gfs2_eattr_block); diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c index c6961b7..55944b6 100644 --- a/gfs2/fsck/pass2.c +++ b/gfs2/fsck/pass2.c @@ -142,36 +142,12 @@ 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_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 @@ -277,18 +253,19 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, log_err( _("Found a bad directory entry: %s\n"), filename); errors_found++; - if(query(&opts, _("Clear inode containing bad blocks? (y/n)"))) { + if(query(&opts, _("Delete inode containing bad blocks? (y/n)"))) { errors_corrected++; entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr); - check_inode_eattr(entry_ip, update, &clear_eattrs); - fsck_inode_put(entry_ip, not_updated); - + check_inode_eattr(entry_ip, update, + &pass2_fxns_delete); check_metatree(entry_ip, &pass2_fxns_delete); + fsck_inode_put(entry_ip, updated); dirent2_del(ip, bh, prev_de, dent); - gfs2_block_set(sbp, bl, de->de_inum.no_addr, - gfs2_meta_inval); + gfs2_block_free); *update = updated; + log_warn( _("The inode containing bad blocks was " + "deleted.\n")); return 1; } else { log_warn( _("Entry to inode containing bad blocks remains\n")); @@ -336,6 +313,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, return 1; /* not a dinode: nothing to delete */ entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr); + check_inode_eattr(entry_ip, update, + &pass2_fxns_delete); check_metatree(entry_ip, &pass2_fxns_delete); fsck_inode_put(entry_ip, updated); gfs2_block_set(sbp, bl, de->de_inum.no_addr, @@ -721,12 +700,13 @@ int pass2(struct gfs2_sbd *sbp) struct gfs2_block_query q; struct dir_status ds = {0}; struct gfs2_inode *ip; - struct gfs2_buffer_head b, *bh = &b; + struct gfs2_buffer_head *bh = NULL; char *filename; int filename_len; char tmp_name[256]; int error = 0; enum update_flags need_update = NOT_UPDATED; + struct dup_blocks *b; /* Check all the system directory inodes. */ if (check_system_dir(sbp->md.jiinode, "jindex", build_jindex)) { @@ -748,6 +728,7 @@ int pass2(struct gfs2_sbd *sbp) log_info( _("Checking directory inodes.\n")); /* Grab each directory inode, and run checks on it */ for(i = 0; i < last_fs_block; i++) { + need_update = 0; warm_fuzzy_stuff(i); if (skip_this_pass || fsck_abort) /* if asked to skip the rest */ return FSCK_OK; @@ -775,8 +756,8 @@ int pass2(struct gfs2_sbd *sbp) * is valid */ ip = fsck_load_inode(sbp, i); if(check_metatree(ip, &pass2_fxns)) { + fsck_inode_put(ip, not_updated); stack; - free(ip); return FSCK_ERROR; } fsck_inode_put(ip, not_updated); @@ -878,6 +859,18 @@ int pass2(struct gfs2_sbd *sbp) fsck_inode_put(ip, need_update); /* does a gfs2_dinode_out, brelse */ } + /* Now that we've deleted the inodes marked "bad" we can safely + get rid of the duplicate block list. If we do it any sooner, + we won't discover that a given block is a duplicate and avoid + deleting it from both inodes referencing it. Note: The other + returns from this function are premature exits of the program + and gfs2_block_list_destroy should get rid of the list for us. */ + while (!osi_list_empty(&sbp->dup_blocks.list)) { + b = osi_list_entry(sbp->dup_blocks.list.next, + struct dup_blocks, list); + osi_list_del(&b->list); + free(b); + } return FSCK_OK; } diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c index 809df91..7ece88a 100644 --- a/gfs2/fsck/pass4.c +++ b/gfs2/fsck/pass4.c @@ -8,6 +8,15 @@ #include "fsck.h" #include "lost_n_found.h" #include "inode_hash.h" +#include "metawalk.h" + +struct metawalk_fxns pass4_fxns_delete = { + .private = NULL, + .check_metalist = delete_metadata, + .check_data = delete_data, + .check_eattr_indir = delete_eattr_indir, + .check_eattr_leaf = delete_eattr_leaf, +}; /* Updates the link count of an inode to what the fsck has seen for * link count */ @@ -62,8 +71,13 @@ static int scan_inode_list(struct gfs2_sbd *sbp, osi_list_t *list) { (unsigned long long)ii->inode); errors_found++; if(query(&opts, - _("Clear unlinked inode with bad blocks? (y/n) "))) { + _("Delete unlinked inode with bad blocks? (y/n) "))) { errors_corrected++; + ip = fsck_load_inode(sbp, ii->inode); + check_inode_eattr(ip, &f, + &pass4_fxns_delete); + check_metatree(ip, &pass4_fxns_delete); + fsck_inode_put(ip, updated); gfs2_block_set(sbp, bl, ii->inode, gfs2_block_free); continue; @@ -77,10 +91,24 @@ static int scan_inode_list(struct gfs2_sbd *sbp, osi_list_t *list) { q.block_type != gfs2_inode_chr && q.block_type != gfs2_inode_fifo && q.block_type != gfs2_inode_sock) { - log_err( _("Unlinked block marked as inode not an inode\n")); - gfs2_block_set(sbp, bl, ii->inode, - gfs2_block_free); - log_err( _("Cleared\n")); + log_err( _("Unlinked block marked as inode is " + "not an inode (%d)\n"), + q.block_type); + ip = fsck_load_inode(sbp, ii->inode); + if(query(&opts, _("Delete unlinked inode " + "? (y/n) "))) { + check_inode_eattr(ip, &f, + &pass4_fxns_delete); + check_metatree(ip, &pass4_fxns_delete); + fsck_inode_put(ip, updated); + gfs2_block_set(sbp, bl, ii->inode, + gfs2_block_free); + log_err( _("The inode was deleted\n")); + } else { + log_err( _("The inode was not " + "deleted\n")); + fsck_inode_put(ip, not_updated); + } continue; } ip = fsck_load_inode(sbp, ii->inode); diff --git a/gfs2/fsck/pass5.c b/gfs2/fsck/pass5.c index 9f879d0..53aef44 100644 --- a/gfs2/fsck/pass5.c +++ b/gfs2/fsck/pass5.c @@ -61,6 +61,7 @@ static int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int b unsigned char rg_status, block_status; struct gfs2_block_query q; uint64_t block; + static int free_unlinked = -1; /* FIXME verify cast */ byte = (unsigned char *) buffer; @@ -84,11 +85,41 @@ static int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int b So we ignore it. */ if (rg_status == GFS2_BLKST_UNLINKED && block_status == GFS2_BLKST_FREE) { - log_warn( _("Unlinked block found at block %" - PRIu64" (0x%" PRIx64 "), left unchanged.\n"), - block, block); + errors_found++; + if (free_unlinked == -1) { + log_err( _("Unlinked inode block found at " + "block %llu (0x%llx).\n"), + (unsigned long long)block, + (unsigned long long)block); + if(query(&opts, _("Do you want me to fix the " + "bitmap for all unlinked " + "blocks? (y/n) "))) + free_unlinked = 1; + else + free_unlinked = 0; + } + if (free_unlinked) { + if(gfs2_set_bitmap(sbp, block, block_status)) + log_err(_("Unlinked block %llu " + "(0x%llx) bitmap not fixed." + "\n"), + (unsigned long long)block, + (unsigned long long)block); + else { + log_err(_("Unlinked block %llu " + "(0x%llx) bitmap fixed.\n"), + (unsigned long long)block, + (unsigned long long)block); + errors_corrected++; + } + } else { + log_info( _("Unlinked block found at block %" + PRIu64" (0x%" PRIx64 "), left " + "unchanged.\n"), block, block); + } } else if (rg_status != block_status) { - const char *blockstatus[] = {"Free", "Data", "Unlinked", "inode"}; + const char *blockstatus[] = {"Free", "Data", + "Unlinked", "inode"}; log_err( _("Ondisk and fsck bitmaps differ at" " block %"PRIu64" (0x%" PRIx64 ") \n"), block, block);