Hi,

If directory entries are found to be on the wrong leaf block, they are
moved as needed. That sometimes requires the leaf block to be split,
which, in turn, sometimes requires the hash table be doubled. The
problem was that the functions that traverse the hash tables were
not taking into account the fact that the hash table could just
unexpectly double, throwing off all the pointers. This patch attempts
to correct that by fixing up the pointers as necessary.

Regards,

Bob Peterson
Red Hat File Systems

Signed-off-by: Bob Peterson <rpete...@redhat.com> 
---
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 9aa9fb8..cd224fe 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -461,7 +461,7 @@ static int check_entries(struct gfs2_inode *ip, struct 
gfs2_buffer_head *bh,
                        } else {
                                error = pass->check_dentry(ip, dent, prev, bh,
                                                           filename, count,
-                                                          lindex,
+                                                          &lindex,
                                                           pass->private);
                                if (error < 0) {
                                        stack;
@@ -504,6 +504,7 @@ int check_leaf(struct gfs2_inode *ip, int lindex, struct 
metawalk_fxns *pass,
        uint32_t count = 0;
        struct gfs2_sbd *sdp = ip->i_sbd;
        const char *msg;
+       int di_depth = ip->i_di.di_depth;
 
        /* Make sure the block number is in range. */
        if (!valid_block(ip->i_sbd, *leaf_no)) {
@@ -604,6 +605,16 @@ int check_leaf(struct gfs2_inode *ip, int lindex, struct 
metawalk_fxns *pass,
                }
        }
 out:
+       if (di_depth < ip->i_di.di_depth) {
+               log_debug(_("Depth of directory %lld (0x%llx) changed from "
+                           "%d to %d; adjusting ref_count from %d to %d\n"),
+                         (unsigned long long)ip->i_di.di_num.no_addr,
+                         (unsigned long long)ip->i_di.di_num.no_addr,
+                         di_depth, ip->i_di.di_depth,
+                         *ref_count,
+                         (*ref_count) << (ip->i_di.di_depth - di_depth));
+               (*ref_count) <<= (ip->i_di.di_depth - di_depth);
+       }
        brelse(lbh);
        return 0;
 
@@ -618,6 +629,16 @@ bad_leaf:
                        return fix;
 
        }
+       if (di_depth < ip->i_di.di_depth) {
+               log_debug(_("Depth of directory %lld (0x%llx) changed from "
+                           "%d to %d. Adjusting ref_count from %d to %d\n"),
+                         (unsigned long long)ip->i_di.di_num.no_addr,
+                         (unsigned long long)ip->i_di.di_num.no_addr,
+                         di_depth, ip->i_di.di_depth,
+                         *ref_count,
+                         (*ref_count) << (ip->i_di.di_depth - di_depth));
+               (*ref_count) <<= (ip->i_di.di_depth - di_depth);
+       }
        return 1;
 }
 
@@ -773,8 +794,13 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct 
metawalk_fxns *pass)
                        }
                        error = check_leaf(ip, lindex, pass, &leaf_no, &leaf,
                                           &ref_count);
-                       if (ref_count != orig_ref_count)
+                       if (ref_count != orig_ref_count) {
+                               log_debug(_("Ref count of leaf 0x%llx "
+                                           "changed from %d to %d.\n"),
+                                         (unsigned long long)leaf_no,
+                                         orig_ref_count, ref_count);
                                tbl_valid = 0;
+                       }
                        if (!leaf.lf_next || error)
                                break;
                        leaf_no = leaf.lf_next;
@@ -787,6 +813,8 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct 
metawalk_fxns *pass)
                                  (unsigned long long)ip->i_di.di_num.no_addr,
                                  orig_di_depth, ip->i_di.di_depth);
                        tbl_valid = 0;
+                       lindex <<= (ip->i_di.di_depth - orig_di_depth);
+                       hsize = (1 << ip->i_di.di_depth);
                }
                if (orig_di_height != ip->i_di.di_height) {
                        log_debug(_("Height of 0x%llx changed from %d to "
@@ -1663,7 +1691,7 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, 
struct metawalk_fxns *pass)
 static int remove_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
                         struct gfs2_dirent *prev_de,
                         struct gfs2_buffer_head *bh,
-                        char *filename, uint32_t *count, int lindex,
+                        char *filename, uint32_t *count, int *lindex,
                         void *private)
 {
        /* the metawalk_fxn's private field must be set to the dentry
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index b3c1299..0d9de3f 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -130,7 +130,7 @@ struct metawalk_fxns {
                             struct gfs2_dirent *prev,
                             struct gfs2_buffer_head *bh,
                             char *filename, uint32_t *count,
-                            int lindex, void *private);
+                            int *lindex, void *private);
        int (*check_eattr_entry) (struct gfs2_inode *ip,
                                  struct gfs2_buffer_head *leaf_bh,
                                  struct gfs2_ea_header *ea_hdr,
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 60f0356..a4ba04c 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -162,7 +162,7 @@ static int resuscitate_metalist(struct gfs2_inode *ip, 
uint64_t block,
 static int resuscitate_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
                              struct gfs2_dirent *prev_de,
                              struct gfs2_buffer_head *bh, char *filename,
-                             uint32_t *count, int lindex, void *priv)
+                             uint32_t *count, int *lindex, void *priv)
 {
        struct gfs2_sbd *sdp = ip->i_sbd;
        struct gfs2_dirent dentry, *de;
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index bdcf77c..27b7336 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -307,7 +307,7 @@ static int check_leaf_depth(struct gfs2_inode *ip, uint64_t 
leaf_no,
  *          or -1 on error
  */
 static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
-                     const char *tmp_name, int lindex, int lindex_max,
+                     const char *tmp_name, int *lindex, int lindex_max,
                      int hash_index, struct gfs2_buffer_head *bh,
                      struct dir_status *ds, struct gfs2_dirent *dent,
                      struct gfs2_dirent *de, struct gfs2_dirent *prev_de,
@@ -318,13 +318,14 @@ static int wrong_leaf(struct gfs2_inode *ip, struct 
gfs2_inum *entry,
        uint64_t planned_leaf, real_leaf;
        int li, dest_ref, error;
        uint64_t *tbl;
+       int di_depth;
 
        log_err(_("Directory entry '%s' at block %lld (0x%llx) is on the "
                  "wrong leaf block.\n"), tmp_name,
                (unsigned long long)entry->no_addr,
                (unsigned long long)entry->no_addr);
        log_err(_("Leaf index is: 0x%x. The range for this leaf block is "
-                 "0x%x - 0x%x\n"), hash_index, lindex, lindex_max);
+                 "0x%x - 0x%x\n"), hash_index, *lindex, lindex_max);
        if (!query( _("Move the misplaced directory entry to "
                      "a valid leaf block? (y/n) "))) {
                log_err( _("Misplaced directory entry not moved.\n"));
@@ -339,8 +340,8 @@ static int wrong_leaf(struct gfs2_inode *ip, struct 
gfs2_inum *entry,
        }
        planned_leaf = be64_to_cpu(tbl[hash_index]);
        log_err(_("Moving it from leaf %llu (0x%llx) to %llu (0x%llx)\n"),
-               (unsigned long long)be64_to_cpu(tbl[lindex]),
-               (unsigned long long)be64_to_cpu(tbl[lindex]),
+               (unsigned long long)be64_to_cpu(tbl[*lindex]),
+               (unsigned long long)be64_to_cpu(tbl[*lindex]),
                (unsigned long long)planned_leaf,
                (unsigned long long)planned_leaf);
        /* Can't trust lf_depth; we have to count */
@@ -366,10 +367,16 @@ static int wrong_leaf(struct gfs2_inode *ip, struct 
gfs2_inum *entry,
                return 1; /* nuke the dent upon return */
        }
 
+       di_depth = ip->i_di.di_depth;
        if (dir_add(ip, tmp_name, de->de_name_len, &de->de_inum,
                    de->de_type) == 0) {
                log_err(_("The misplaced directory entry was moved to a "
                          "valid leaf block.\n"));
+               if (ip->i_di.di_depth > di_depth) {
+                       log_err(_("Directory hash table was doubled.\n"));
+                       hash_index <<= (ip->i_di.di_depth - di_depth);
+                       (*lindex) <<= (ip->i_di.di_depth - di_depth);
+               }
                if (lgfs2_get_leaf_ptr(ip, hash_index, &real_leaf)) {
                        log_err(_("Could not read leaf %d in dinode %"PRIu64": 
%s\n"), hash_index,
                                (uint64_t)ip->i_di.di_num.no_addr, 
strerror(errno));
@@ -389,7 +396,7 @@ static int wrong_leaf(struct gfs2_inode *ip, struct 
gfs2_inum *entry,
                   appear later, we'll count it has part of our normal
                   processing when we get to that leaf block later on in the
                   hash table. */
-               if (hash_index > lindex) {
+               if (hash_index > *lindex) {
                        log_err(_("Accounting deferred.\n"));
                        return 1; /* nuke the dent upon return */
                }
@@ -655,7 +662,7 @@ static int basic_dentry_checks(struct gfs2_inode *ip, 
struct gfs2_dirent *dent,
 static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
                        struct gfs2_dirent *prev_de,
                        struct gfs2_buffer_head *bh, char *filename,
-                       uint32_t *count, int lindex, void *priv)
+                       uint32_t *count, int *lindex, void *priv)
 {
        struct gfs2_sbd *sdp = ip->i_sbd;
        uint8_t q = 0;
@@ -811,8 +818,8 @@ static int check_dentry(struct gfs2_inode *ip, struct 
gfs2_dirent *dent,
           block have a hash table index that fits */
        if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
                hash_index = hash_table_index(de->de_hash, ip);
-               lindex_max = hash_table_max(lindex, ip, bh);
-               if (hash_index < lindex || hash_index > lindex_max) {
+               lindex_max = hash_table_max(*lindex, ip, bh);
+               if (hash_index < *lindex || hash_index > lindex_max) {
                        int nuke_dent;
 
                        nuke_dent = wrong_leaf(ip, &entry, tmp_name, lindex,
@@ -1024,7 +1031,7 @@ static int lost_leaf(struct gfs2_inode *ip, uint64_t 
*tbl, uint64_t leafno,
 static int basic_check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
                              struct gfs2_dirent *prev_de,
                              struct gfs2_buffer_head *bh, char *filename,
-                             uint32_t *count, int lindex, void *priv)
+                             uint32_t *count, int *lindex, void *priv)
 {
        uint8_t q = 0;
        char tmp_name[MAX_FILENAME];

Reply via email to