Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 148958 Details for
Bug 229484
gfs_fsck not good at fixing corrupt directory entries
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
First go at a fix
229484.patch (text/plain), 16.28 KB, created by
Robert Peterson
on 2007-02-28 20:26:01 UTC
(
hide
)
Description:
First go at a fix
Filename:
MIME Type:
Creator:
Robert Peterson
Created:
2007-02-28 20:26:01 UTC
Size:
16.28 KB
patch
obsolete
>? fs_dir.rhel4U5.c >? fs_dir.rhel4U5.h >? metawalk.RHEL4U5.c >Index: fs_dir.c >=================================================================== >RCS file: /cvs/cluster/cluster/gfs/gfs_fsck/fs_dir.c,v >retrieving revision 1.10 >diff -w -u -p -p -u -r1.10 fs_dir.c >--- fs_dir.c 1 Aug 2005 16:20:48 -0000 1.10 >+++ fs_dir.c 28 Feb 2007 20:02:21 -0000 >@@ -18,7 +18,8 @@ > #include "fs_inode.h" > #include "bio.h" > #include "link.h" >- >+#include "limits.h" >+#include "metawalk.h" > #include "fs_dir.h" > > #define IS_LEAF (1) >@@ -1633,6 +1634,34 @@ int get_leaf_nr(struct fsck_inode *dip, > > > /** >+ * put_leaf_nr - Put a leaf number associated with the index >+ * @dip: The GFS inode >+ * @index: >+ * @leaf_out: >+ * >+ * Returns: 0 on success, error code otherwise >+ */ >+ >+int put_leaf_nr(struct fsck_inode *dip, uint32 index, uint64 leaf_out) >+{ >+ uint64 leaf_no; >+ int error = -1; >+ >+ leaf_no = cpu_to_gfs64(leaf_out); >+ >+ error = writei(dip, (char *)&leaf_no, >+ index * sizeof(uint64), sizeof(uint64)); >+ if (error != sizeof(uint64)){ >+ log_debug("put_leaf_nr: Bad internal write. (rtn = %d)\n", >+ error); >+ return (error < 0) ? error : -EIO; >+ } >+ >+ return 0; >+} >+ >+ >+/** > * fs_filecmp - Compare two filenames > * @file1: The first filename > * @file2: The second filename >@@ -1685,3 +1714,52 @@ int fs_dir_search(struct fsck_inode *dip > > return error; > } >+ >+/** >+ * dirent_repair - attempt to repair a corrupt directory entry. >+ * @bh - The buffer header that contains the bad dirent >+ * @de - The directory entry in native format >+ * @dent - The directory entry in on-disk format >+ * @type - Type of directory (DIR_LINEAR or DIR_EXHASH) >+ * @first - TRUE if this is the first dirent in the buffer >+ * >+ * This function tries to repair a corrupt directory entry. All we >+ * know at this point is that the length field is wrong. >+ */ >+int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de, >+ struct gfs_dirent *dent, int type, int first) >+{ >+ char *bh_end, *p; >+ int calc_de_name_len = 0; >+ >+ /* If this is a sentinel, just fix the length and move on */ >+ if (first && !de->de_inum.no_formal_ino) { /* Is it a sentinel? */ >+ if (type == DIR_LINEAR) >+ de->de_rec_len = BH_SIZE(bh) - >+ sizeof(struct gfs_dinode); >+ else >+ de->de_rec_len = BH_SIZE(bh) - sizeof(struct gfs_leaf); >+ } >+ else { >+ bh_end = BH_DATA(bh) + BH_SIZE(bh); >+ /* first, figure out a probable name length */ >+ p = (char *)dent + sizeof(struct gfs_dirent); >+ while (*p && /* while there's a non-zero char and */ >+ p < bh_end) { /* not past end of buffer */ >+ calc_de_name_len++; >+ p++; >+ } >+ if (!calc_de_name_len) >+ return 1; >+ /* There can often be noise at the end, so only */ >+ /* Trust the shorter of the two in case we have too much */ >+ /* Or rather, only trust ours if it's shorter. */ >+ if (!de->de_name_len || de->de_name_len > NAME_MAX || >+ calc_de_name_len < de->de_name_len) /* if dent is hosed */ >+ de->de_name_len = calc_de_name_len; /* use ours */ >+ de->de_rec_len = GFS_DIRENT_SIZE(de->de_name_len); >+ } >+ gfs_dirent_out(de, (char *)dent); >+ write_buf(ip->i_sbd, bh, 0); >+ return 0; >+} >Index: fs_dir.h >=================================================================== >RCS file: /cvs/cluster/cluster/gfs/gfs_fsck/fs_dir.h,v >retrieving revision 1.3 >diff -w -u -p -p -u -r1.3 fs_dir.h >--- fs_dir.h 7 Feb 2005 15:27:45 -0000 1.3 >+++ fs_dir.h 28 Feb 2007 20:02:21 -0000 >@@ -31,6 +31,7 @@ int dirent_del(struct fsck_inode *dip, o > int fsck_inode_is_stuffed(struct fsck_inode *ip); > int dirent_first(osi_buf_t *bh, struct gfs_dirent **dent); > int get_leaf_nr(struct fsck_inode *dip, uint32 index, uint64 *leaf_out); >+int put_leaf_nr(struct fsck_inode *dip, uint32 index, uint64 leaf_out); > int fs_filecmp(osi_filename_t *file1, char *file2, int len_of_file2); > int fs_dirent_del(struct fsck_inode *dip, osi_buf_t *bh, osi_filename_t *filename); > int fs_dir_add(struct fsck_inode *dip, osi_filename_t *filename, >@@ -39,5 +40,7 @@ int fs_dirent_alloc(struct fsck_inode *d > int name_len, struct gfs_dirent **dent_out); > > int fs_dir_search(struct fsck_inode *dip, identifier_t *id, unsigned int *type); >+int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de, >+ struct gfs_dirent *dent, int type, int first); > > #endif /* __FS_DIR_H__ */ >Index: metawalk.c >=================================================================== >RCS file: /cvs/cluster/cluster/gfs/gfs_fsck/metawalk.c,v >retrieving revision 1.9 >diff -w -u -p -p -u -r1.9 metawalk.c >--- metawalk.c 20 Feb 2007 18:20:28 -0000 1.9 >+++ metawalk.c 28 Feb 2007 20:02:21 -0000 >@@ -59,6 +59,24 @@ int check_entries(struct fsck_inode *ip, > gfs_dirent_in(&de, (char *)dent); > filename = (char *)dent + sizeof(struct gfs_dirent); > >+ if (de.de_rec_len < sizeof(struct gfs_dirent) + >+ de.de_name_len) { >+ log_err("Directory block %" >+ PRIu64 ", entry %d of directory %" >+ PRIu64 " is corrupt.\n", BH_BLKNO(bh), >+ (*count) + 1, ip->i_di.di_num.no_addr); >+ if (query(ip->i_sbd, "Attempt to repair it? (y/n) ")) { >+ if (dirent_repair(ip, bh, &de, dent, type, >+ first)) >+ break; >+ } >+ else { >+ log_err("Corrupt directory entry %d ignored, " >+ "stopped after checking %d entries.\n", >+ *count); >+ break; >+ } >+ } > if (!de.de_inum.no_formal_ino){ > if(first){ > log_debug("First dirent is a sentinel (place holder).\n"); >@@ -83,12 +101,6 @@ int check_entries(struct fsck_inode *ip, > }*/ > } > >- if (de.de_rec_len < sizeof(struct gfs_dirent)) { >- log_err("Entry %"PRIu64" of directory %" >- PRIu64" is corrupt, skipping.\n", >- BH_BLKNO(bh), ip->i_di.di_num.no_addr); >- break; >- } > if ((char *)dent + de.de_rec_len >= bh_end){ > log_debug("Last entry processed.\n"); > break; >@@ -109,13 +121,36 @@ int check_entries(struct fsck_inode *ip, > } > > >+/* Process a bad leaf pointer and ask to repair the first time. */ >+/* The repair process involves extending the previous leaf's entries */ >+/* so that they replace the bad ones. We have to hack up the old */ >+/* leaf a bit, but it's better than deleting the whole directory, */ >+/* which is what used to happen before. */ >+void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no, >+ uint64_t *bad_leaf, uint64_t old_leaf, int index, >+ const char *msg) >+{ >+ if (*bad_leaf != *leaf_no) { >+ log_err("Directory Inode %" PRIu64 " points to leaf %" >+ PRIu64 " %s.\n", ip->i_di.di_num.no_addr, *leaf_no, >+ msg); >+ } >+ if (*leaf_no == *bad_leaf || >+ query(ip->i_sbd, "Attempt to patch around it? (y/n) ")) { >+ put_leaf_nr(ip, index, old_leaf); >+ } >+ else >+ log_err("Bad leaf left in place.\n"); >+ *bad_leaf = *leaf_no; >+ *leaf_no = old_leaf; >+} > > /* Checks exthash directory entries */ > int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass) > { > int error; >- struct gfs_leaf leaf; >- uint64_t leaf_no, old_leaf; >+ struct gfs_leaf leaf, oldleaf; >+ uint64_t leaf_no, old_leaf, bad_leaf = -1; > osi_buf_t *lbh; > int index; > struct fsck_sb *sbp = ip->i_sbd; >@@ -123,6 +158,7 @@ int check_leaf(struct fsck_inode *ip, in > int ref_count = 0, exp_count = 0; > > old_leaf = 0; >+ memset(&oldleaf, 0, sizeof(oldleaf)); > for(index = 0; index < (1 << ip->i_di.di_depth); index++) { > if(get_leaf_nr(ip, index, &leaf_no)) { > log_err("Unable to get leaf block number in dir %" >@@ -138,7 +174,12 @@ int check_leaf(struct fsck_inode *ip, in > /* GFS has multiple indirect pointers to the same leaf > * until those extra pointers are needed, so skip the > * dups */ >- if(old_leaf == leaf_no) { >+ if (leaf_no == bad_leaf) { >+ put_leaf_nr(ip, index, old_leaf); /* fill w/old leaf */ >+ ref_count++; >+ continue; >+ } >+ else if(old_leaf == leaf_no) { > ref_count++; > continue; > } else { >@@ -150,6 +191,22 @@ int check_leaf(struct fsck_inode *ip, in > old_leaf, > ref_count, > exp_count); >+ if (query(ip->i_sbd, "Attempt to fix it? (y/n) ")) { >+ int factor = 0, divisor = ref_count; >+ >+ get_and_read_buf(sbp, old_leaf, &lbh, >+ 0); >+ while (divisor > 1) { >+ factor++; >+ divisor /= 2; >+ } >+ oldleaf.lf_depth = ip->i_di.di_depth - >+ factor; >+ gfs_leaf_out(&oldleaf, BH_DATA(lbh)); >+ write_buf(sbp, lbh, 0); >+ relse_buf(sbp, lbh); >+ } >+ else > return 1; > } > ref_count = 1; >@@ -157,47 +214,45 @@ int check_leaf(struct fsck_inode *ip, in > > count = 0; > do { >- /* FIXME: Do other checks (see old >- * pass3:dir_exhash_scan() */ >- lbh = NULL; >- if(pass->check_leaf) { >- error = pass->check_leaf(ip, leaf_no, &lbh, >- pass->private); >- if(error < 0) { >- stack; >- relse_buf(sbp, lbh); >- return -1; >- } >- if(error > 0) { >- relse_buf(sbp, lbh); >- lbh = NULL; >- return 1; >- } >+ /* Make sure the block number is in range. */ >+ if(check_range(ip->i_sbd, leaf_no)){ >+ log_err("Leaf block #%"PRIu64" is out of " >+ "range for directory #%"PRIu64".\n", >+ leaf_no, ip->i_di.di_num.no_addr); >+ warn_and_patch(ip, &leaf_no, &bad_leaf, >+ old_leaf, index, >+ "that is out of range"); >+ memcpy(&leaf, &oldleaf, sizeof(oldleaf)); >+ break; > } >- >- if (!lbh){ >- if(get_and_read_buf(sbp, leaf_no, >- &lbh, 0)){ >+ /* Try to read in the leaf block. */ >+ if(get_and_read_buf(sbp, leaf_no, &lbh, 0)){ > log_err("Unable to read leaf block #%" > PRIu64" for " > "directory #%"PRIu64".\n", >- leaf_no, >- ip->i_di.di_num.no_addr); >- /* FIXME: should i error out >- * if this fails? */ >+ leaf_no, ip->i_di.di_num.no_addr); >+ warn_and_patch(ip, &leaf_no, &bad_leaf, >+ old_leaf, index, >+ "that cannot be read"); >+ memcpy(&leaf, &oldleaf, sizeof(oldleaf)); >+ relse_buf(sbp, lbh); > break; > } >- } >- gfs_leaf_in(&leaf, BH_DATA(lbh)); >- >- /* Make sure it's really a leaf. */ >- if (leaf.lf_header.mh_type != GFS_METATYPE_LF) { >- log_err("Inode %" PRIu64 " points to bad leaf " >- PRIu64 ".\n", ip->i_di.di_num.no_addr, >- leaf_no); >+ /* Make sure it's really a valid leaf block. */ >+ if (check_meta(lbh, GFS_METATYPE_LF)) { >+ warn_and_patch(ip, &leaf_no, &bad_leaf, >+ old_leaf, index, >+ "that is not really a leaf"); >+ memcpy(&leaf, &oldleaf, sizeof(oldleaf)); > relse_buf(sbp, lbh); > break; > } >+ gfs_leaf_in(&leaf, BH_DATA(lbh)); >+ if(pass->check_leaf) { >+ error = pass->check_leaf(ip, leaf_no, lbh, >+ pass->private); >+ } >+ > exp_count = (1 << (ip->i_di.di_depth - leaf.lf_depth)); > log_debug("expected count %u - %u %u\n", exp_count, > ip->i_di.di_depth, leaf.lf_depth); >@@ -210,8 +265,7 @@ int check_leaf(struct fsck_inode *ip, in > > /* Since the buffer possibly got > updated directly, release it now, >- and grab it again later if we need >- it */ >+ and grab it again later if we need it */ > relse_buf(sbp, lbh); > if(error < 0) { > stack; >@@ -261,6 +315,7 @@ int check_leaf(struct fsck_inode *ip, in > } > } while(1); > old_leaf = leaf_no; >+ memcpy(&oldleaf, &leaf, sizeof(oldleaf)); > } > return 0; > } >Index: metawalk.h >=================================================================== >RCS file: /cvs/cluster/cluster/gfs/gfs_fsck/metawalk.h,v >retrieving revision 1.3 >diff -w -u -p -p -u -r1.3 metawalk.h >--- metawalk.h 11 Feb 2005 22:01:04 -0000 1.3 >+++ metawalk.h 28 Feb 2007 20:02:21 -0000 >@@ -45,7 +45,7 @@ int dinode_hash_remove(osi_list_t *bucke > struct metawalk_fxns { > void *private; > int (*check_leaf) (struct fsck_inode *ip, uint64_t block, >- osi_buf_t **bh, void *private); >+ osi_buf_t *bh, void *private); > int (*check_metalist) (struct fsck_inode *ip, uint64_t block, > osi_buf_t **bh, void *private); > int (*check_data) (struct fsck_inode *ip, uint64_t block, >Index: pass1.c >=================================================================== >RCS file: /cvs/cluster/cluster/gfs/gfs_fsck/pass1.c,v >retrieving revision 1.15 >diff -w -u -p -p -u -r1.15 pass1.c >--- pass1.c 17 Nov 2006 16:43:49 -0000 1.15 >+++ pass1.c 28 Feb 2007 20:02:22 -0000 >@@ -41,50 +41,14 @@ struct block_count { > uint64_t ea_count; > }; > >-static int leaf(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh, >+static int leaf(struct fsck_inode *ip, uint64_t block, osi_buf_t *bh, > void *private) > { > struct fsck_sb *sdp = ip->i_sbd; > struct block_count *bc = (struct block_count *) private; >- if(check_range(sdp, block)){ >- log_warn("Leaf block #%"PRIu64" is out of range for " >- "directory #%"PRIu64".\n", >- block, ip->i_di.di_num.no_addr); >- block_set(sdp->bl, ip->i_di.di_num.no_addr, bad_block); >- return 1; >- } >- if(get_and_read_buf(sdp, block, bh, 0)){ >- log_err("Unable to read leaf block #%"PRIu64" for " >- "directory #%"PRIu64".\n", >- block, ip->i_di.di_num.no_addr); >- if(query(sdp, "Clear directory inode at %"PRIu64"? (y/n) ", >- ip->i_di.di_num.no_addr)) { >- block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval); >- } else { >- log_err("Unreadable block %"PRIu64" ignored\n"); >- } >- return 1; >- } > >- if(check_meta(*bh, GFS_METATYPE_LF)){ >- log_err("Bad meta header for leaf block #%"PRIu64 >- " in directory #%"PRIu64". - is %u, should be %u\n", >- BH_BLKNO(*bh), ip->i_di.di_num.no_addr, >- ((struct gfs_meta_header *)BH_DATA((*bh)))->mh_type, >- GFS_METATYPE_LF); >- if(query(sdp, "Clear directory inode at %"PRIu64"? (y/n) ", >- ip->i_di.di_num.no_addr)) { >- block_set(sdp->bl, ip->i_di.di_num.no_addr, >- meta_inval); >- log_err("Directory inode marked invalid\n"); >- } else { >- log_err("Invalid block %"PRIu64" ignored\n"); >- } >- return 1; >- } >- >- log_debug("\tLeaf block at %15"PRIu64"\n", BH_BLKNO(*bh)); >- block_set(sdp->bl, BH_BLKNO(*bh), leaf_blk); >+ log_debug("\tLeaf block at %15"PRIu64"\n", BH_BLKNO(bh)); >+ block_set(sdp->bl, BH_BLKNO(bh), leaf_blk); > bc->indir_count++; > > return 0; >@@ -473,7 +437,7 @@ int clear_data(struct fsck_inode *ip, ui > } > > int clear_leaf(struct fsck_inode *ip, uint64_t block, >- osi_buf_t **bh, void *private) >+ osi_buf_t *bh, void *private) > { > > struct fsck_sb *sdp = ip->i_sbd; >@@ -529,8 +493,6 @@ int add_to_dir_list(struct fsck_sb *sbp, > } > > >- >- > int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree) > { > struct block_query q = {0}; >Index: pass2.c >=================================================================== >RCS file: /cvs/cluster/cluster/gfs/gfs_fsck/pass2.c,v >retrieving revision 1.14 >diff -w -u -p -p -u -r1.14 pass2.c >--- pass2.c 20 Feb 2007 18:20:28 -0000 1.14 >+++ pass2.c 28 Feb 2007 20:02:22 -0000 >@@ -36,63 +36,6 @@ struct dir_status { > }; > > >-static int check_leaf(struct fsck_inode *ip, uint64_t block, osi_buf_t **lbh, >- void *private) >-{ >- uint64_t chain_no; >- struct fsck_sb *sbp = ip->i_sbd; >- struct gfs_leaf leaf; >- osi_buf_t *chain_head = NULL; >- osi_buf_t *bh = NULL; >- int chain=0; >- int error; >- >- chain_no = block; >- >- do { >- /* FIXME: check the range of the leaf? */ >- /* check the leaf and stuff */ >- >- error = get_and_read_buf(sbp, chain_no, &bh, 0); >- >- if(error){ >- stack; >- goto fail; >- } >- >- gfs_leaf_in(&leaf, BH_DATA(bh)); >- >- /* Check the leaf headers */ >- >- if(!chain){ >- chain = 1; >- chain_head = bh; >- chain_no = leaf.lf_next; >- } >- else { >- relse_buf(sbp, bh); >- bh = NULL; >- break; >- } >- } while(chain_no); >- >- *lbh = chain_head; >- return 0; >- >- fail: >- /* FIXME: check this error path */ >- if(chain_head){ >- if(chain_head == bh){ bh = NULL; } >- relse_buf(sbp, chain_head); >- } >- if(bh) >- relse_buf(sbp, bh); >- >- return -1; >- >-} >- >- > /* Set children's parent inode in dir_info structure - ext2 does not set > * dotdot inode here, but instead in pass3 - should we? */ > int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock, >@@ -328,7 +271,8 @@ int check_dentry(struct fsck_inode *ip, > q.block_type != inode_lnk && q.block_type != inode_blk && > q.block_type != inode_chr && q.block_type != inode_fifo && > q.block_type != inode_sock) { >- log_err("Found directory entry '%s' in %"PRIu64" to something" >+ log_err("Found directory entry '%s' in block %" >+ PRIu64" to something" > " not a file or directory!\n", tmp_name, > ip->i_num.no_addr); > log_debug("block #%"PRIu64" in %"PRIu64"\n", >@@ -558,7 +502,7 @@ int check_dentry(struct fsck_inode *ip, > > struct metawalk_fxns pass2_fxns = { > .private = NULL, >- .check_leaf = check_leaf, >+ .check_leaf = NULL, > .check_metalist = NULL, > .check_data = NULL, > .check_eattr_indir = check_eattr_indir,
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 229484
:
148958
|
154072