cluster: RHEL6 - fsck.gfs2: check leaf depth when validating leaf blocks
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=0d0256d317f...
Commit: 0d0256d317fe73aa7e5322df91053e2f4d3408af
Parent: 0321077e899faa8e927c535a1ce37b08a646c68a
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Fri Mar 8 09:34:32 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:16:27 2013 -0500
fsck.gfs2: check leaf depth when validating leaf blocks
Now that fsck.gfs2 can validate the hash table is relatively sane
without actually reading the leaf blocks, when it does need to read
in those leaf blocks, we need to check the leaf depth is
appropriate for the (now sane) number of pointers we encountered in
the hash table. This patch adds a call to check the leaf depth from
pass2.
rhbz#902920
---
gfs2/fsck/metawalk.c | 3 +++
gfs2/fsck/metawalk.h | 2 ++
gfs2/fsck/pass2.c | 1 +
3 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index e5603d5..1659da0 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -512,6 +512,9 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
msg = _("that is not really a leaf");
goto bad_leaf;
}
+ if (pass->check_leaf_depth)
+ error = pass->check_leaf_depth(ip, *leaf_no, *ref_count, lbh);
+
if (pass->check_leaf) {
error = pass->check_leaf(ip, *leaf_no, pass->private);
if (error) {
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index e11b5e0..486c6eb 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -69,6 +69,8 @@ extern int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
*/
struct metawalk_fxns {
void *private;
+ int (*check_leaf_depth) (struct gfs2_inode *ip, uint64_t leaf_no,
+ int ref_count, struct gfs2_buffer_head *lbh);
int (*check_leaf) (struct gfs2_inode *ip, uint64_t block,
void *private);
int (*check_metalist) (struct gfs2_inode *ip, uint64_t block,
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 013d3e7..4000610 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1429,6 +1429,7 @@ static int pass2_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
struct metawalk_fxns pass2_fxns = {
.private = NULL,
+ .check_leaf_depth = check_leaf_depth,
.check_leaf = NULL,
.check_metalist = NULL,
.check_data = NULL,
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: fix leaf blocks, don't try to patch the hash table
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=0321077e899...
Commit: 0321077e899faa8e927c535a1ce37b08a646c68a
Parent: 51bd1bde1d2b08b8c5097bf535bc5fab4f8ab4cd
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Fri Mar 8 09:22:11 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:16:19 2013 -0500
fsck.gfs2: fix leaf blocks, don't try to patch the hash table
Before this patch, when we detected a bad leaf block, fsck.gfs2 would
try to patch the hash table. That's very wrong, because the hash table
needs to be on nice power-of-two boundaries. This patch changes the
code so that the hash table is actually repaired.
rhbz#902920
---
gfs2/fsck/metawalk.c | 135 ++++++++++++++++++--------------------------------
gfs2/fsck/metawalk.h | 3 +-
gfs2/fsck/pass1.c | 14 +++++
gfs2/fsck/pass2.c | 9 +++-
4 files changed, 72 insertions(+), 89 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 463eb27..e5603d5 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -478,52 +478,14 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
return 0;
}
-/* warn_and_patch - Warn the user of an error and ask permission to fix it
- * Process a bad leaf pointer and ask to repair the first time.
- * The repair process involves extending the previous leaf's entries
- * so that they replace the bad ones. We have to hack up the old
- * leaf a bit, but it's better than deleting the whole directory,
- * which is what used to happen before. */
-static int warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
- uint64_t *bad_leaf, uint64_t old_leaf,
- uint64_t first_ok_leaf, int pindex, const char *msg)
-{
- int okay_to_fix = 0;
-
- if (*bad_leaf != *leaf_no) {
- log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
- " (0x%llx) %s.\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)*leaf_no,
- (unsigned long long)*leaf_no, msg);
- }
- if (*leaf_no == *bad_leaf ||
- (okay_to_fix = query( _("Attempt to patch around it? (y/n) ")))) {
- if (valid_block(ip->i_sbd, old_leaf))
- 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"));
- *bad_leaf = *leaf_no;
- *leaf_no = old_leaf;
- return okay_to_fix;
-}
-
/**
* check_leaf - check a leaf block for errors
* Reads in the leaf block
* Leaves the buffer around for further analysis (caller must brelse)
*/
static int check_leaf(struct gfs2_inode *ip, int lindex,
- struct metawalk_fxns *pass, int *ref_count,
- uint64_t *leaf_no, uint64_t old_leaf, uint64_t *bad_leaf,
- uint64_t first_ok_leaf, struct gfs2_leaf *leaf,
- struct gfs2_leaf *oldleaf)
+ struct metawalk_fxns *pass,
+ uint64_t *leaf_no, struct gfs2_leaf *leaf, int *ref_count)
{
int error = 0, fix;
struct gfs2_buffer_head *lbh = NULL;
@@ -531,7 +493,6 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
struct gfs2_sbd *sdp = ip->i_sbd;
const char *msg;
- *ref_count = 1;
/* Make sure the block number is in range. */
if (!valid_block(ip->i_sbd, *leaf_no)) {
log_err( _("Leaf block #%llu (0x%llx) is out of range for "
@@ -541,7 +502,7 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
msg = _("that is out of range");
- goto out_copy_old_leaf;
+ goto bad_leaf;
}
/* Try to read in the leaf block. */
@@ -549,7 +510,7 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
/* Make sure it's really a valid leaf block. */
if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) {
msg = _("that is not really a leaf");
- goto out_copy_old_leaf;
+ goto bad_leaf;
}
if (pass->check_leaf) {
error = pass->check_leaf(ip, *leaf_no, pass->private);
@@ -585,7 +546,7 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
(unsigned long long)*leaf_no,
(unsigned long long)*leaf_no);
msg = _("that is not a leaf");
- goto out_copy_old_leaf;
+ goto bad_leaf;
}
if (pass->check_dentry && is_dir(&ip->i_di, sdp->gfs1)) {
@@ -597,16 +558,18 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
if (error < 0) {
stack;
- goto out;
+ goto out; /* This seems wrong: needs investigation */
}
- if (count != leaf->lf_entries) {
- /* release and re-read the leaf in case check_entries
- changed it. */
- brelse(lbh);
- lbh = bread(sdp, *leaf_no);
- gfs2_leaf_in(leaf, lbh);
+ if (count == leaf->lf_entries)
+ goto out;
+ /* release and re-read the leaf in case check_entries
+ changed it. */
+ brelse(lbh);
+ lbh = bread(sdp, *leaf_no);
+ gfs2_leaf_in(leaf, lbh);
+ if (count != leaf->lf_entries) {
log_err( _("Leaf %llu (0x%llx) entry count in "
"directory %llu (0x%llx) does not match "
"number of entries found - is %u, found %u\n"),
@@ -628,17 +591,16 @@ out:
brelse(lbh);
return 0;
-out_copy_old_leaf:
- /* The leaf we read in is bad. So we'll copy the old leaf into the
- * new one. However, that will make us shift our ref count. */
- fix = warn_and_patch(ip, leaf_no, bad_leaf, old_leaf,
- first_ok_leaf, lindex, msg);
- (*ref_count)++;
- memcpy(leaf, oldleaf, sizeof(struct gfs2_leaf));
- if (lbh) {
- if (fix)
- bmodified(lbh);
+bad_leaf:
+ if (lbh)
brelse(lbh);
+ if (pass->repair_leaf) {
+ /* The leaf we read in is bad so we need to repair it. */
+ fix = pass->repair_leaf(ip, leaf_no, lindex, *ref_count, msg,
+ pass->private);
+ if (fix < 0)
+ return fix;
+
}
return 1;
}
@@ -677,17 +639,17 @@ static void dir_leaf_reada(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize)
/* Checks exhash directory entries */
static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
{
- int error;
- struct gfs2_leaf leaf, oldleaf;
+ int error = 0;
+ struct gfs2_leaf leaf;
unsigned hsize = (1 << ip->i_di.di_depth);
- uint64_t leaf_no, old_leaf, bad_leaf = -1;
+ uint64_t leaf_no, leaf_next;
uint64_t first_ok_leaf, orig_di_blocks;
struct gfs2_buffer_head *lbh;
int lindex;
struct gfs2_sbd *sdp = ip->i_sbd;
- int ref_count = 0, orig_ref_count, orig_di_depth, orig_di_height, old_was_dup;
+ int ref_count, orig_ref_count, orig_di_depth, orig_di_height;
uint64_t *tbl;
- int tbl_valid;
+ int chained_leaf, tbl_valid;
tbl = get_dir_hash(ip);
if (tbl == NULL) {
@@ -749,10 +711,11 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
return 1;
}
- old_leaf = -1;
- memset(&oldleaf, 0, sizeof(oldleaf));
- old_was_dup = 0;
- for (lindex = 0; lindex < hsize; lindex++) {
+ lindex = 0;
+ leaf_next = -1;
+ while (lindex < hsize) {
+ int l;
+
if (fsck_abort)
break;
@@ -772,38 +735,36 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
}
leaf_no = be64_to_cpu(tbl[lindex]);
- /* GFS has multiple indirect pointers to the same leaf
- * until those extra pointers are needed, so skip the dups */
- if (leaf_no == bad_leaf) {
- tbl[lindex] = cpu_to_be64(old_leaf);
- gfs2_put_leaf_nr(ip, lindex, old_leaf);
- ref_count++;
- continue;
- } else if (old_leaf == leaf_no) {
+ /* count the number of block pointers to this leaf. We don't
+ need to count the current lindex, because we already know
+ it's a reference */
+ ref_count = 1;
+
+ for (l = lindex + 1; l < hsize; l++) {
+ leaf_next = be64_to_cpu(tbl[l]);
+ if (leaf_next != leaf_no)
+ break;
ref_count++;
- continue;
}
orig_ref_count = ref_count;
+ chained_leaf = 0;
do {
if (fsck_abort) {
free(tbl);
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
return 0;
}
- error = check_leaf(ip, lindex, pass, &ref_count,
- &leaf_no, old_leaf, &bad_leaf,
- first_ok_leaf, &leaf, &oldleaf);
+ error = check_leaf(ip, lindex, pass, &leaf_no, &leaf,
+ &ref_count);
if (ref_count != orig_ref_count)
tbl_valid = 0;
- old_was_dup = (error == -EEXIST);
- old_leaf = leaf_no;
- memcpy(&oldleaf, &leaf, sizeof(oldleaf));
if (!leaf.lf_next || error)
break;
leaf_no = leaf.lf_next;
- log_debug( _("Leaf chain (0x%llx) detected.\n"),
- (unsigned long long)leaf_no);
+ chained_leaf++;
+ log_debug( _("Leaf chain #%d (0x%llx) detected.\n"),
+ chained_leaf, (unsigned long long)leaf_no);
} while (1); /* while we have chained leaf blocks */
if (orig_di_depth != ip->i_di.di_depth) {
log_debug(_("Depth of 0x%llx changed from %d to %d\n"),
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index bef99ae..e11b5e0 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -104,7 +104,8 @@ struct metawalk_fxns {
int (*check_hash_tbl) (struct gfs2_inode *ip, uint64_t *tbl,
unsigned hsize, void *private);
int (*repair_leaf) (struct gfs2_inode *ip, uint64_t *leaf_no,
- int lindex, int ref_count, const char *msg);
+ int lindex, int ref_count, const char *msg,
+ void *private);
};
#endif /* _METAWALK_H */
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 6cce07c..fa74b76 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -77,6 +77,19 @@ static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
void *private);
static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip);
+static int pass1_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
+ int lindex, int ref_count, const char *msg,
+ void *private)
+{
+ struct block_count *bc = (struct block_count *)private;
+ int new_leaf_blks;
+
+ new_leaf_blks = repair_leaf(ip, leaf_no, lindex, ref_count, msg);
+ bc->indir_count += new_leaf_blks;
+
+ return new_leaf_blks;
+}
+
struct metawalk_fxns pass1_fxns = {
.private = NULL,
.check_leaf = check_leaf,
@@ -89,6 +102,7 @@ struct metawalk_fxns pass1_fxns = {
.check_eattr_extentry = check_extended_leaf_eattr,
.finish_eattr_indir = finish_eattr_indir,
.big_file_msg = big_file_comfort,
+ .repair_leaf = pass1_repair_leaf,
};
struct metawalk_fxns undo_fxns = {
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index b1cc6dc..013d3e7 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1420,6 +1420,13 @@ static int check_hash_tbl(struct gfs2_inode *ip, uint64_t *tbl,
return error;
}
+static int pass2_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
+ int lindex, int ref_count, const char *msg,
+ void *private)
+{
+ return repair_leaf(ip, leaf_no, lindex, ref_count, msg);
+}
+
struct metawalk_fxns pass2_fxns = {
.private = NULL,
.check_leaf = NULL,
@@ -1430,7 +1437,7 @@ struct metawalk_fxns pass2_fxns = {
.check_dentry = check_dentry,
.check_eattr_entry = NULL,
.check_hash_tbl = check_hash_tbl,
- .repair_leaf = repair_leaf,
+ .repair_leaf = pass2_repair_leaf,
};
/* Check system directory inode */
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: re-read hash table if directory height or depth changes
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=51bd1bde1d2...
Commit: 51bd1bde1d2b08b8c5097bf535bc5fab4f8ab4cd
Parent: 4e4d8b2937e9593aa14bd7e1894849021749b52f
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Thu Mar 7 13:58:18 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:15:49 2013 -0500
fsck.gfs2: re-read hash table if directory height or depth changes
There may be times when fsck.gfs2 wants to move things around. For
example, if it finds dirents entries on the wrong leaf block, it
may want to move them to a different leaf. If it does, it may need
to split the leaf, which means we're adding another block. We may
in fact have doubled our exhash table, so the table in cache is no
longer valid. In this case, we need to discard the old one and read
it in again. This patch checks for these things and re-reads the
hash table as appropriate.
rhbz#902920
---
gfs2/fsck/metawalk.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 6d6156c..463eb27 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -681,18 +681,23 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
struct gfs2_leaf leaf, oldleaf;
unsigned hsize = (1 << ip->i_di.di_depth);
uint64_t leaf_no, old_leaf, bad_leaf = -1;
- uint64_t first_ok_leaf;
+ uint64_t first_ok_leaf, orig_di_blocks;
struct gfs2_buffer_head *lbh;
int lindex;
struct gfs2_sbd *sdp = ip->i_sbd;
- int ref_count = 0, old_was_dup;
+ int ref_count = 0, orig_ref_count, orig_di_depth, orig_di_height, old_was_dup;
uint64_t *tbl;
+ int tbl_valid;
tbl = get_dir_hash(ip);
if (tbl == NULL) {
perror("get_dir_hash");
return -1;
}
+ tbl_valid = 1;
+ orig_di_depth = ip->i_di.di_depth;
+ orig_di_height = ip->i_di.di_height;
+ orig_di_blocks = ip->i_di.di_blocks;
/* Turn off system readahead */
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_RANDOM);
@@ -750,6 +755,21 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
for (lindex = 0; lindex < hsize; lindex++) {
if (fsck_abort)
break;
+
+ if (!tbl_valid) {
+ free(tbl);
+ log_debug(_("Re-reading 0x%llx hash table.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ tbl = get_dir_hash(ip);
+ if (tbl == NULL) {
+ perror("get_dir_hash");
+ return -1;
+ }
+ tbl_valid = 1;
+ orig_di_depth = ip->i_di.di_depth;
+ orig_di_height = ip->i_di.di_height;
+ orig_di_blocks = ip->i_di.di_blocks;
+ }
leaf_no = be64_to_cpu(tbl[lindex]);
/* GFS has multiple indirect pointers to the same leaf
@@ -763,6 +783,7 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
ref_count++;
continue;
}
+ orig_ref_count = ref_count;
do {
if (fsck_abort) {
@@ -773,6 +794,8 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
error = check_leaf(ip, lindex, pass, &ref_count,
&leaf_no, old_leaf, &bad_leaf,
first_ok_leaf, &leaf, &oldleaf);
+ if (ref_count != orig_ref_count)
+ tbl_valid = 0;
old_was_dup = (error == -EEXIST);
old_leaf = leaf_no;
memcpy(&oldleaf, &leaf, sizeof(oldleaf));
@@ -782,6 +805,28 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
log_debug( _("Leaf chain (0x%llx) detected.\n"),
(unsigned long long)leaf_no);
} while (1); /* while we have chained leaf blocks */
+ if (orig_di_depth != ip->i_di.di_depth) {
+ log_debug(_("Depth of 0x%llx changed from %d to %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ orig_di_depth, ip->i_di.di_depth);
+ tbl_valid = 0;
+ }
+ if (orig_di_height != ip->i_di.di_height) {
+ log_debug(_("Height of 0x%llx changed from %d to "
+ "%d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ orig_di_height, ip->i_di.di_height);
+ tbl_valid = 0;
+ }
+ if (orig_di_blocks != ip->i_di.di_blocks) {
+ log_debug(_("Block count of 0x%llx changed from %llu "
+ "to %llu\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)orig_di_blocks,
+ (unsigned long long)ip->i_di.di_blocks);
+ tbl_valid = 0;
+ }
+ lindex += ref_count;
} /* for every leaf block */
free(tbl);
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: Verify dirent hash values correspond to proper leaf block
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=4e4d8b2937e...
Commit: 4e4d8b2937e9593aa14bd7e1894849021749b52f
Parent: 0bb980d9b18906bce56d7ba4567efb8f5dd377aa
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Feb 25 13:28:00 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:15:28 2013 -0500
fsck.gfs2: Verify dirent hash values correspond to proper leaf block
This patch checks to make sure all the dirents on a leaf block have
hash values that are actually appropriate to the leaf block.
With extended hashing, the file name is used to generate a hash value.
Today's fsck checks that the hash value is proper to the file name,
but that's not enough. The hash value is also shifted by a certain
amount (determined by i_depth) to produce an index into the hash table.
For example, suppose di_depth == 8. Valid indexes into the hash table
go from 0 to 1<<di_depth-1 which is 1<<8-1 which is 256-1 or 255.
Now suppose we have four actual leaf blocks, and each leaf block is
repeated 64 (0x40) times in the index. So the hash table, indexed by
file name would be something like:
entries 00-3f = first leaf block
entries 40-7f = second leaf block
entries 80-bf = third leaf block
entries c0-ff = fourth leaf block
So ht index = name->hash >> (32 - ip->i_depth).
In our example, i_depth is 8, so:
ht index == hash >> (32 - 8) == hash >> 24
In this case, the hash value is shifted by a certain amount to get
the index into the table. For example, file name "Solar" has hash
value 0x59f4dde1. So the hr index == 0x59f4dde1 >> 24 == 0x59.
Therefore, a file with the name "Solar" better appear on the second
leaf, which covers index values from 0x40 to 0x7f.
What this patch does is verify that all the dirents on the first
leaf block have a hash value starting with 0x00 to 0x3f, and all
the dirents on the second leaf block have a hash value starting with
0x40 to 0x7f, and so forth. If they appear on the wrong leaf block,
they need to be relocated to the proper leaf block.
rhbz#902920
---
gfs2/fsck/metawalk.c | 12 ++-
gfs2/fsck/metawalk.h | 3 +-
gfs2/fsck/pass1.c | 2 +-
gfs2/fsck/pass1b.c | 5 +-
gfs2/fsck/pass2.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 197 insertions(+), 15 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 709ff94..6d6156c 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -340,7 +340,8 @@ static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
* -1 - error occurred
*/
static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
- int type, uint32_t *count, struct metawalk_fxns *pass)
+ int type, uint32_t *count, int lindex,
+ struct metawalk_fxns *pass)
{
struct gfs2_dirent *dent;
struct gfs2_dirent de, *prev;
@@ -448,6 +449,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,
pass->private);
if (error < 0) {
stack;
@@ -587,7 +589,8 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
}
if (pass->check_dentry && is_dir(&ip->i_di, sdp->gfs1)) {
- error = check_entries(ip, lbh, DIR_EXHASH, &count, pass);
+ error = check_entries(ip, lbh, DIR_EXHASH, &count, lindex,
+ pass);
if (skip_this_pass || fsck_abort)
goto out;
@@ -1448,7 +1451,7 @@ int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
int error = 0;
uint32_t count = 0;
- error = check_entries(ip, bh, DIR_LINEAR, &count, pass);
+ error = check_entries(ip, bh, DIR_LINEAR, &count, 0, pass);
if (error < 0) {
stack;
return -1;
@@ -1479,7 +1482,8 @@ 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, void *private)
+ char *filename, uint32_t *count, int lindex,
+ void *private)
{
/* the metawalk_fxn's private field must be set to the dentry
* block we want to clear */
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index c43baf0..bef99ae 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -85,7 +85,8 @@ struct metawalk_fxns {
int (*check_dentry) (struct gfs2_inode *ip, struct gfs2_dirent *de,
struct gfs2_dirent *prev,
struct gfs2_buffer_head *bh,
- char *filename, uint32_t *count, void *private);
+ char *filename, uint32_t *count,
+ 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 fe80369..6cce07c 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -147,7 +147,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, 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/pass1b.c b/gfs2/fsck/pass1b.c
index e519c20..c9e6494 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -48,7 +48,8 @@ static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
void *private);
static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
struct gfs2_dirent *prev, struct gfs2_buffer_head *bh,
- char *filename, uint32_t *count, void *priv);
+ char *filename, uint32_t *count, int lindex,
+ void *priv);
struct metawalk_fxns find_refs = {
.private = NULL,
@@ -172,7 +173,7 @@ static int check_dir_dup_ref(struct gfs2_inode *ip, struct gfs2_dirent *de,
static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
struct gfs2_dirent *prev,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, void *priv)
+ uint32_t *count, int lindex, void *priv)
{
struct osi_node *n, *next = NULL;
osi_list_t *tmp2;
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index e67f42a..b1cc6dc 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -299,6 +299,166 @@ static int hash_table_index(uint32_t hash, struct gfs2_inode *ip)
return hash >> (32 - ip->i_di.di_depth);
}
+static int hash_table_max(int lindex, struct gfs2_inode *ip,
+ struct gfs2_buffer_head *bh)
+{
+ struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data;
+ return (1 << (ip->i_di.di_depth - be16_to_cpu(leaf->lf_depth))) +
+ lindex - 1;
+}
+
+static int check_leaf_depth(struct gfs2_inode *ip, uint64_t leaf_no,
+ int ref_count, struct gfs2_buffer_head *lbh)
+{
+ struct gfs2_leaf *leaf = (struct gfs2_leaf *)lbh->b_data;
+ int cur_depth = be16_to_cpu(leaf->lf_depth);
+ int exp_count = 1 << (ip->i_di.di_depth - cur_depth);
+ int divisor;
+ int factor, correct_depth;
+
+ if (exp_count == ref_count)
+ return 0;
+
+ factor = 0;
+ divisor = ref_count;
+ while (divisor > 1) {
+ factor++;
+ divisor >>= 1;
+ }
+ correct_depth = ip->i_di.di_depth - factor;
+ if (cur_depth == correct_depth)
+ return 0;
+
+ log_err(_("Leaf block %llu (0x%llx) in dinode %llu (0x%llx) has the "
+ "wrong depth: is %d (length %d), should be %d (length "
+ "%d).\n"),
+ (unsigned long long)leaf_no, (unsigned long long)leaf_no,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ cur_depth, ref_count, correct_depth, exp_count);
+ if (!query( _("Fix the leaf block? (y/n)"))) {
+ log_err( _("The leaf block was not fixed.\n"));
+ return 0;
+ }
+
+ leaf->lf_depth = cpu_to_be16(correct_depth);
+ bmodified(lbh);
+ log_err( _("The leaf block depth was fixed.\n"));
+ return 1;
+}
+
+/* wrong_leaf: Deal with a dirent discovered to be on the wrong leaf block
+ *
+ * Returns: 1 if the dirent is to be removed, 0 if it needs to be kept,
+ * 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,
+ 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,
+ uint32_t *count, uint8_t q)
+{
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ struct gfs2_buffer_head *dest_lbh;
+ uint64_t planned_leaf, real_leaf;
+ int li, dest_ref, error;
+ uint64_t *tbl;
+
+ 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);
+ if (!query( _("Move the misplaced directory entry to "
+ "a valid leaf block? (y/n) "))) {
+ log_err( _("Misplaced directory entry not moved.\n"));
+ return 0;
+ }
+
+ /* check the destination leaf block's depth */
+ tbl = get_dir_hash(ip);
+ if (tbl == NULL) {
+ perror("get_dir_hash");
+ return -1;
+ }
+ 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)planned_leaf,
+ (unsigned long long)planned_leaf);
+ /* Can't trust lf_depth; we have to count */
+ dest_ref = 0;
+ for (li = 0; li < (1 << ip->i_di.di_depth); li++) {
+ if (be64_to_cpu(tbl[li]) == planned_leaf)
+ dest_ref++;
+ else if (dest_ref)
+ break;
+ }
+ dest_lbh = bread(sdp, planned_leaf);
+ check_leaf_depth(ip, planned_leaf, dest_ref, dest_lbh);
+ brelse(dest_lbh);
+ free(tbl);
+
+ /* check if it's already on the correct leaf block */
+ error = dir_search(ip, tmp_name, de->de_name_len, NULL, &de->de_inum);
+ if (!error) {
+ log_err(_("The misplaced directory entry already appears on "
+ "the correct leaf block.\n"));
+ log_err( _("The bad duplicate directory entry "
+ "'%s' was cleared.\n"), tmp_name);
+ return 1; /* nuke the dent upon return */
+ }
+
+ 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"));
+ gfs2_get_leaf_nr(ip, hash_index, &real_leaf);
+ if (real_leaf != planned_leaf) {
+ log_err(_("The planned leaf was split. The new leaf "
+ "is: %llu (0x%llx)"),
+ (unsigned long long)real_leaf,
+ (unsigned long long)real_leaf);
+ fsck_blockmap_set(ip, real_leaf, _("split leaf"),
+ gfs2_indir_blk);
+ }
+ /* If the misplaced dirent was supposed to be earlier in the
+ hash table, we need to adjust our counts for the blocks
+ that have already been processed. If it's supposed to
+ 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) {
+ log_err(_("Accounting deferred.\n"));
+ return 1; /* nuke the dent upon return */
+ }
+ /* If we get here, it's because we moved a dent to another
+ leaf, but that leaf has already been processed. So we have
+ to nuke the dent from this leaf when we return, but we
+ still need to do the "good dent" accounting. */
+ error = incr_link_count(*entry, ip, _("valid reference"));
+ if (error > 0 &&
+ bad_formal_ino(ip, dent, *entry, tmp_name, q, de, bh) == 1)
+ return 1; /* nuke it */
+
+ /* You cannot do this:
+ (*count)++;
+ The reason is: *count is the count of dentries on the leaf,
+ and we moved the dentry to a previous leaf within the same
+ directory dinode. So the directory counts still get
+ incremented, but not leaf entries. When we called dir_add
+ above, it should have fixed that prev leaf's lf_entries. */
+ ds->entry_count++;
+ return 1;
+ } else {
+ log_err(_("Error moving directory entry.\n"));
+ return 1; /* nuke it */
+ }
+}
+
/* basic_dentry_checks - fundamental checks for directory entries
*
* @ip: pointer to the incode inode structure
@@ -520,9 +680,9 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
/* 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,
- struct gfs2_dirent *prev_de,
- struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, void *priv)
+ struct gfs2_dirent *prev_de,
+ struct gfs2_buffer_head *bh, char *filename,
+ uint32_t *count, int lindex, void *priv)
{
struct gfs2_sbd *sdp = ip->i_sbd;
uint8_t q = 0;
@@ -532,6 +692,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
int error;
struct gfs2_inode *entry_ip = NULL;
struct gfs2_dirent dentry, *de;
+ int hash_index; /* index into the hash table based on the hash */
+ int lindex_max; /* largest acceptable hash table index for hash */
memset(&dentry, 0, sizeof(struct gfs2_dirent));
gfs2_dirent_in(&dentry, (char *)dent);
@@ -672,6 +834,21 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
ds->dotdotdir = 1;
goto dentry_is_valid;
}
+ /* If this is an exhash directory, make sure the dentries in the leaf
+ 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) {
+ int nuke_dent;
+
+ nuke_dent = wrong_leaf(ip, &entry, tmp_name, lindex,
+ lindex_max, hash_index, bh, ds,
+ dent, de, prev_de, count, q);
+ if (nuke_dent)
+ goto nuke_dentry;
+ }
+ }
/* After this point we're only concerned with directories */
if (q != gfs2_inode_dir) {
@@ -703,10 +880,9 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
dentry_is_valid:
/* This directory inode links to this inode via this dentry */
error = incr_link_count(entry, ip, _("valid reference"));
- if (error > 0) {
- if (bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1)
- goto nuke_dentry;
- }
+ if (error > 0 &&
+ bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1)
+ goto nuke_dentry;
(*count)++;
ds->entry_count++;
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: Misc cleanups from upstream
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=0bb980d9b18...
Commit: 0bb980d9b18906bce56d7ba4567efb8f5dd377aa
Parent: 037d87223adc6cd056c91e2b2bc6bdde5078528c
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Fri Feb 22 06:31:22 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:13:28 2013 -0500
fsck.gfs2: Misc cleanups from upstream
This patch fixes some minor things that appear in the upstream version.
rhbz#902920
---
gfs2/fsck/initialize.c | 13 +++++++------
gfs2/fsck/inode_hash.c | 2 +-
gfs2/fsck/metawalk.c | 29 ++++++++++++++++-------------
gfs2/fsck/pass1.c | 8 ++------
gfs2/fsck/pass1c.c | 9 +++++----
gfs2/fsck/pass2.c | 11 +++++++----
gfs2/fsck/pass5.c | 15 ++++++++-------
gfs2/fsck/util.c | 3 ++-
8 files changed, 48 insertions(+), 42 deletions(-)
diff --git a/gfs2/fsck/initialize.c b/gfs2/fsck/initialize.c
index a288ed2..79e295b 100644
--- a/gfs2/fsck/initialize.c
+++ b/gfs2/fsck/initialize.c
@@ -174,8 +174,9 @@ static int set_block_ranges(struct gfs2_sbd *sdp)
error = read(sdp->device_fd, buf, sdp->sd_sb.sb_bsize);
if (error != sdp->sd_sb.sb_bsize){
log_crit( _("Can't read last block in file system (error %u), "
- "last_fs_block: %"PRIu64" (0x%" PRIx64 ")\n"), error,
- last_fs_block, last_fs_block);
+ "last_fs_block: %llu (0x%llx)\n"), error,
+ (unsigned long long)last_fs_block,
+ (unsigned long long)last_fs_block);
goto fail;
}
@@ -798,7 +799,7 @@ static int init_system_inodes(struct gfs2_sbd *sdp)
bl = gfs2_bmap_create(sdp, last_fs_block+1, &addl_mem_needed);
if (!bl) {
- log_crit( _("This system doesn't have enough memory + swap space to fsck this file system.\n"));
+ log_crit( _("This system doesn't have enough memory and swap space to fsck this file system.\n"));
log_crit( _("Additional memory needed is approximately: %lluMB\n"),
(unsigned long long)(addl_mem_needed / 1048576ULL));
log_crit( _("Please increase your swap space by that amount and run gfs2_fsck again.\n"));
@@ -1029,7 +1030,7 @@ static void peruse_user_dinode(struct gfs2_sbd *sdp, struct gfs2_dinode *di,
gfs2_lookupi(ip, "..", 2, &parent_ip);
if (parent_ip && parent_ip->i_di.di_num.no_addr ==
ip->i_di.di_num.no_addr) {
- log_warn(_("fsck found the root inode at: 0x%llx\n"),
+ log_warn(_("Found the root directory at: 0x%llx\n"),
ip->i_di.di_num.no_addr);
sdp->sd_sb.sb_root_dir.no_addr =
ip->i_di.di_num.no_addr;
@@ -1207,7 +1208,7 @@ static int sb_repair(struct gfs2_sbd *sdp)
"be the root; using master - 1.\n"));
possible_root = sdp->sd_sb.sb_master_dir.no_addr - 1;
}
- log_err(_("Found a root directory candidate at 0x%llx\n"),
+ log_err(_("Found a possible root at: 0x%llx\n"),
(unsigned long long)possible_root);
sdp->sd_sb.sb_root_dir.no_addr = possible_root;
sdp->md.rooti = inode_read(sdp, possible_root);
@@ -1235,7 +1236,7 @@ static int sb_repair(struct gfs2_sbd *sdp)
/* Step 3 - Rebuild the lock protocol and file system table name */
get_lockproto_table(sdp);
if (query(_("Okay to fix the GFS2 superblock? (y/n)"))) {
- log_info(_("Master system directory found at: 0x%llx\n"),
+ log_info(_("Found system master directory at: 0x%llx\n"),
sdp->sd_sb.sb_master_dir.no_addr);
sdp->master_dir = inode_read(sdp,
sdp->sd_sb.sb_master_dir.no_addr);
diff --git a/gfs2/fsck/inode_hash.c b/gfs2/fsck/inode_hash.c
index ac88057..56581b7 100644
--- a/gfs2/fsck/inode_hash.c
+++ b/gfs2/fsck/inode_hash.c
@@ -1,10 +1,10 @@
#include <stdint.h>
#include <unistd.h>
#include <libintl.h>
+#include <string.h>
#include "libgfs2.h"
#include "osi_list.h"
-#include "hash.h"
#include "inode_hash.h"
#include "fsck.h"
#define _(String) gettext(String)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 4164722..709ff94 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -15,7 +15,6 @@
#include "fsck.h"
#include "util.h"
#include "metawalk.h"
-#include "hash.h"
#include "inode_hash.h"
#define COMFORTABLE_BLKS 5242880 /* 20GB in 4K blocks */
@@ -36,7 +35,7 @@ int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
old_bitmap_state = gfs2_get_bitmap(sdp, blk, rgd);
if (old_bitmap_state < 0) {
- log_err( _("Block %lld (0x%llx) is not represented in the "
+ log_err( _("Block %llu (0x%llx) is not represented in the "
"system bitmap; part of an rgrp or superblock.\n"),
(unsigned long long)blk, (unsigned long long)blk);
return -1;
@@ -317,13 +316,11 @@ static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
{
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);
bmodified(bh);
@@ -336,6 +333,7 @@ static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
* bh - buffer for the leaf block
* type - type of block this is (linear or exhash)
* @count - set to the count entries
+ * @lindex - the last inde
* @pass - structure pointing to pass-specific functions
*
* returns: 0 - good block or it was repaired to be good
@@ -344,7 +342,6 @@ static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
int type, uint32_t *count, struct metawalk_fxns *pass)
{
- struct gfs2_leaf *leaf = NULL;
struct gfs2_dirent *dent;
struct gfs2_dirent de, *prev;
int error = 0;
@@ -359,9 +356,9 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
}
else if (type == DIR_EXHASH) {
dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
- leaf = (struct gfs2_leaf *)bh->b_data;
- log_debug( _("Checking leaf %" PRIu64 " (0x%" PRIx64 ")\n"),
- bh->b_blocknr, bh->b_blocknr);
+ log_debug( _("Checking leaf %llu (0x%llu)\n"),
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)bh->b_blocknr);
}
else {
log_err( _("Invalid directory type %d specified\n"), type);
@@ -517,6 +514,8 @@ static int warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
/**
* check_leaf - check a leaf block for errors
+ * Reads in the leaf block
+ * Leaves the buffer around for further analysis (caller must brelse)
*/
static int check_leaf(struct gfs2_inode *ip, int lindex,
struct metawalk_fxns *pass, int *ref_count,
@@ -1038,8 +1037,9 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
uint64_t di_eattr_save = ip->i_di.di_eattr;
uint64_t offset = ip->i_sbd->gfs1 ? sizeof(struct gfs_indirect) : sizeof(struct gfs2_meta_header);
- log_debug( _("Checking EA indirect block #%"PRIu64" (0x%" PRIx64 ").\n"),
- indirect, indirect);
+ log_debug( _("Checking EA indirect block #%llu (0x%llx).\n"),
+ (unsigned long long)indirect,
+ (unsigned long long)indirect);
if (!pass->check_eattr_indir)
return 0;
@@ -1171,6 +1171,9 @@ static void free_metalist(struct gfs2_inode *ip, osi_list_t *mlp)
* This includes hash table blocks for directories
* which are technically "data" in the bitmap.
*
+ * Returns: 0 - all is well, process the blocks this metadata references
+ * 1 - something went wrong, but process the sub-blocks anyway
+ * -1 - something went wrong, so don't process the sub-blocks
* @ip:
* @mlp:
*/
@@ -1248,9 +1251,9 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
if (err > 0) {
if (!error)
error = err;
- log_debug( _("Skipping block %" PRIu64
- " (0x%" PRIx64 ")\n"),
- block, block);
+ log_debug( _("Skipping block %llu (0x%llx)\n"),
+ (unsigned long long)block,
+ (unsigned long long)block);
continue;
}
if (!valid_block(ip->i_sbd, block)) {
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 13cb1cb..fe80369 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -210,7 +210,7 @@ static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
So we know it's a leaf block. */
q = block_type(block);
if (q != gfs2_block_free) {
- log_err( _("Found duplicate block %llu (0x%llx) referenced "
+ log_err( _("Found duplicate block #%llu (0x%llx) referenced "
"as a directory leaf in dinode "
"%llu (0x%llx) - was marked %d (%s)\n"),
(unsigned long long)block,
@@ -263,7 +263,7 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
}
q = block_type(block);
if (q != gfs2_block_free) {
- log_err( _("Found duplicate block %llu (0x%llx) referenced "
+ log_err( _("Found duplicate block #%llu (0x%llx) referenced "
"as metadata in indirect block for dinode "
"%llu (0x%llx) - was marked %d (%s)\n"),
(unsigned long long)block,
@@ -1423,8 +1423,6 @@ int pass1(struct gfs2_sbd *sdp)
struct rgrp_tree *rgd;
int first;
uint64_t i;
- uint64_t blk_count;
- uint64_t offset;
uint64_t rg_count = 0;
osi_list_init(&gfs1_rindex_blks.list);
@@ -1468,8 +1466,6 @@ int pass1(struct gfs2_sbd *sdp)
gfs2_meta_rgrp);*/
}
- offset = sizeof(struct gfs2_rgrp);
- blk_count = 1;
first = 1;
while (1) {
diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c
index f7a7842..f9bfa6a 100644
--- a/gfs2/fsck/pass1c.c
+++ b/gfs2/fsck/pass1c.c
@@ -24,9 +24,10 @@ static int remove_eattr_entry(struct gfs2_sbd *sdp,
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);
+ log_err( _("Bad Extended Attribute at block #%llu"
+ " (0x%llx) removed.\n"),
+ (unsigned long long)leaf_bh->b_blocknr,
+ (unsigned long long)leaf_bh->b_blocknr);
bmodified(leaf_bh);
return 0;
}
@@ -192,7 +193,7 @@ static int check_eattr_entry(struct gfs2_inode *ip,
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"),
+ log_debug( _(" Pointers Required: %d\n Pointers Reported: %d\n"),
max_ptrs, ea_hdr->ea_num_ptrs);
}
}
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index c0efacc..e67f42a 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1315,6 +1315,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
filename_len))) {
log_err( _("Unable to zero name string\n"));
stack;
+ free(filename);
return -1;
}
memcpy(filename, tmp_name, filename_len);
@@ -1326,6 +1327,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
if (error) {
log_err(_("Error adding directory %s: %s\n"),
filename, strerror(errno));
+ free(filename);
return -errno;
}
if (cur_blks != sysinode->i_di.di_blocks)
@@ -1445,8 +1447,8 @@ int pass2(struct gfs2_sbd *sdp)
if (q != gfs2_inode_dir)
continue;
- log_debug( _("Checking directory inode at block %"PRIu64" (0x%"
- PRIx64 ")\n"), dirblk, dirblk);
+ log_debug( _("Checking directory inode at block %llu (0x%llx)\n"),
+ (unsigned long long)dirblk, (unsigned long long)dirblk);
memset(&ds, 0, sizeof(ds));
pass2_fxns.private = (void *) &ds;
@@ -1513,8 +1515,9 @@ int pass2(struct gfs2_sbd *sdp)
ip = fsck_load_inode(sdp, dirblk);
if (!ds.dotdir) {
log_err(_("No '.' entry found for directory inode at "
- "block %"PRIu64" (0x%" PRIx64 ")\n"),
- dirblk, dirblk);
+ "block %llu (0x%llx)\n"),
+ (unsigned long long)dirblk,
+ (unsigned long long)dirblk);
if (query( _("Is it okay to add '.' entry? (y/n) "))) {
uint64_t cur_blks;
diff --git a/gfs2/fsck/pass5.c b/gfs2/fsck/pass5.c
index 2540b17..7e85b69 100644
--- a/gfs2/fsck/pass5.c
+++ b/gfs2/fsck/pass5.c
@@ -97,7 +97,6 @@ static int gfs2_convert_mark(uint8_t q, uint32_t *count)
return -1;
}
-
static int check_block_status(struct gfs2_sbd *sdp, char *buffer,
unsigned int buflen, uint64_t *rg_block,
uint64_t rg_data, uint32_t *count)
@@ -159,9 +158,10 @@ static int check_block_status(struct gfs2_sbd *sdp, char *buffer,
(unsigned long long)block,
(unsigned long long)block);
} else {
- log_info( _("Unlinked block found at block %"
- PRIu64" (0x%" PRIx64 "), left "
- "unchanged.\n"), block, block);
+ log_info( _("Unlinked block found at block %llu"
+ " (0x%llx), left unchanged.\n"),
+ (unsigned long long)block,
+ (unsigned long long)block);
}
} else if (rg_status != block_status) {
const char *blockstatus[] = {"Free", "Data",
@@ -185,8 +185,9 @@ static int check_block_status(struct gfs2_sbd *sdp, char *buffer,
else
log_err( _("Fixed.\n"));
} else
- log_err( _("Bitmap at block %"PRIu64" (0x%" PRIx64
- ") left inconsistent\n"), block, block);
+ log_err( _("Bitmap at block %llu (0x%llx) left inconsistent\n"),
+ (unsigned long long)block,
+ (unsigned long long)block);
}
(*rg_block)++;
bit += GFS2_BIT_SIZE;
@@ -295,7 +296,7 @@ int pass5(struct gfs2_sbd *sdp)
next = osi_next(n);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
return FSCK_OK;
- log_info( _("Verifying Resource Group #%" PRIu64 "\n"), rg_count);
+ log_info( _("Verifying Resource Group #%llu\n"), (unsigned long long)rg_count);
memset(count, 0, sizeof(count));
rgp = (struct rgrp_tree *)n;
diff --git a/gfs2/fsck/util.c b/gfs2/fsck/util.c
index cfcb701..a33e452 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -77,7 +77,8 @@ void warm_fuzzy_stuff(uint64_t block)
seconds = tv.tv_sec;
if (last_fs_block) {
percent = (block * 100) / last_fs_block;
- log_notice( _("\r%" PRIu64 " percent complete.\r"), percent);
+ log_notice( _("\r%llu percent complete.\r"),
+ (unsigned long long)percent);
fflush(stdout);
}
}
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: Move function to read directory hash table to util.c
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=037d87223ad...
Commit: 037d87223adc6cd056c91e2b2bc6bdde5078528c
Parent: 56c536851aed2ccb182d0a56981829a3eab57142
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Feb 25 13:07:19 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:12:24 2013 -0500
fsck.gfs2: Move function to read directory hash table to util.c
This patch moves function get_dir_hash from metawalk.c to util.c.
This was done because a future patch will need access to the function.
rhbz#902920
---
gfs2/fsck/metawalk.c | 18 ------------------
gfs2/fsck/util.c | 19 +++++++++++++++++++
gfs2/fsck/util.h | 1 +
3 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index d5d518a..4164722 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -641,24 +641,6 @@ out_copy_old_leaf:
return 1;
}
-static uint64_t *get_dir_hash(struct gfs2_inode *ip)
-{
- unsigned hsize = (1 << ip->i_di.di_depth) * sizeof(uint64_t);
- int ret;
- uint64_t *tbl = malloc(hsize);
-
- if (tbl == NULL)
- return NULL;
-
- ret = gfs2_readi(ip, tbl, 0, hsize);
- if (ret != hsize) {
- free(tbl);
- return NULL;
- }
-
- return tbl;
-}
-
static int u64cmp(const void *p1, const void *p2)
{
uint64_t a = *(uint64_t *)p1;
diff --git a/gfs2/fsck/util.c b/gfs2/fsck/util.c
index 5e14dd4..cfcb701 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -571,3 +571,22 @@ uint64_t find_free_blk(struct gfs2_sbd *sdp)
}
return 0;
}
+
+uint64_t *get_dir_hash(struct gfs2_inode *ip)
+{
+ unsigned hsize = (1 << ip->i_di.di_depth) * sizeof(uint64_t);
+ int ret;
+ uint64_t *tbl = malloc(hsize);
+
+ if (tbl == NULL)
+ return NULL;
+
+ ret = gfs2_readi(ip, tbl, 0, hsize);
+ if (ret != hsize) {
+ free(tbl);
+ return NULL;
+ }
+
+ return tbl;
+}
+
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
index 5ee38ad..3f15fca 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -181,4 +181,5 @@ extern int gfs2_blockmap_set(struct gfs2_bmap *il, uint64_t block,
enum gfs2_mark_block mark);
extern int set_ip_blockmap(struct gfs2_inode *ip, int instree);
extern uint64_t find_free_blk(struct gfs2_sbd *sdp);
+extern uint64_t *get_dir_hash(struct gfs2_inode *ip);
#endif /* __UTIL_H__ */
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: Special case '..' when processing bad formal inode number
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=56c536851ae...
Commit: 56c536851aed2ccb182d0a56981829a3eab57142
Parent: b2533640cb0ec2ff0404a9647defee6b959f2534
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Jan 28 09:11:17 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:12:16 2013 -0500
fsck.gfs2: Special case '..' when processing bad formal inode number
In a recent patch to fsck.gfs2, we added the ability to make sure the
formal inode number in each directory entry matches the formal inode
number in the dinode. If it doesn't match, fsck tries to fix it up.
We can't do much for regular files, but we can fix up directories.
If the directory linkage is intact, it just fixes the formal inode number.
But to check if the directory linkage is intact, we were checking to make
sure the child directory points to the parent with its "..". For example,
suppose we have gfs2 mounted as /mnt/gfs2, and at the root, we have
directory "a", and within "a" we have a subdirectory "b". In other words:
/mnt/gfs2/a/b/...
Now suppose fsck.gfs2 finds a formal inode number mismatch between the
dirent inside "a" which points to "b" and the inode "b" itself. Since
both "a" and "b" are directories, it tries to determine if the directory
linkage is intact by testing whether b's ".." dirent actually points
back to "a". And if it's good, we can just fix the formal inode number
so that they match.
That's all well and good, and works for the most part. However, if
the dirent found to be wrong isn't "b" but ".." we've got a problem.
Today's algorithm would look up the ".." of ".." which won't be
pointingi back to what we want.
For this patch, I'm special-casing ".." and making it just delete the
correct directory entry. However, we have to do it in such a way that
it doesn't decrement di_entries, since the entry is invalid.
rhbz#902920
---
gfs2/fsck/pass2.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 359bca0..c0efacc 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -258,7 +258,7 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
(unsigned long long)entry.no_formal_ino,
(unsigned long long)ii->di_num.no_formal_ino,
(unsigned long long)ii->di_num.no_formal_ino);
- if (q != gfs2_inode_dir) {
+ if (q != gfs2_inode_dir || !strcmp("..", tmp_name)) {
if (query( _("Remove the corrupt directory entry? (y/n) ")))
return 1;
log_err( _("Corrupt directory entry not removed.\n"));
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: Add new function to check dir hash tables
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=b2533640cb0...
Commit: b2533640cb0ec2ff0404a9647defee6b959f2534
Parent: d94ef845ac19bed44faaee79d629c9f4b9862a28
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 12:27:40 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 15:11:02 2013 -0500
fsck.gfs2: Add new function to check dir hash tables
It's very important that fsck.gfs2 checks for a valid directory hash
table before operating further on the directory. Before this patch,
we were doing some incomplete testing, after we had already operated
on the directory, with function check_num_ptrs. This patch replaces
that scheme with a new one that preemptively checks the hash table
with a new function called check_hash_tbl.
We've got to make sure the hash table is sane. Each leaf needs to
be counted a proper power of 2. We can't just have 3 pointers to a leaf.
The number of pointers must correspond to the proper leaf depth, and they
must all fall on power-of-two boundaries. For example, suppose we have
directory that's had one of its middle leaf blocks split several times.
The split history might look something like this:
leaf
split lindex length depth pointers leaf split
----- ------ ------ ----- -------- -------------
0: 0x00 0x100 0 00 - ff <--split to 1:
1: 0x00 0x80 1 00 - 7f <--split to 2:
0x80 0x80 1 80 - ff
2: 0x00 0x40 2 00 - 3f <--split to 3:
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
3: 0x00 0x20 3 00 - 1f
0x20 0x20 3 20 - 3f <--split to 4
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
4: 0x00 0x20 3 00 - 1f
0x20 0x10 4 20 - 2f
0x30 0x10 4 30 - 3f <--split to 5
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
5: 0x00 0x20 3 00 - 1f
0x20 0x10 4 20 - 2f
0x30 0x8 5 30 - 37 <--split to 6
0x38 0x8 5 38 - 3f
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
6: 0x00 0x20 3 00 - 1f
0x20 0x10 4 20 - 2f
0x30 0x4 6 30 - 33
0x34 0x4 6 34 - 37
0x38 0x8 5 38 - 3f
0x40 0x40 2 40 - 7f
0x80 0x80 1 80 - ff
You can see from this example that it's impossible for a leaf block to have
a lf_depth of 5 and lindex 0x34. As shown in "5:" above, a leaf depth of 5
can only fall at offset 0x30 or 0x38. If it somehow falls elsewhere, say
0x34, the proper depth should be 6, and there should only be 4 pointers,
as per split "6:" above. The leaf block pointers all need to fall properly
on these boundaries, otherwise the kernel code's calculations will land it
on the wrong leaf block while it's searching, and the result will be files
you can see with ls, but can't open, delete or use them.
rhbz#902920
---
gfs2/fsck/metawalk.c | 174 +++++++++++++++--
gfs2/fsck/metawalk.h | 12 +-
gfs2/fsck/pass1.c | 142 --------------
gfs2/fsck/pass2.c | 532 ++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 700 insertions(+), 160 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index ce80738..d5d518a 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -716,6 +716,24 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
/* Readahead */
dir_leaf_reada(ip, tbl, hsize);
+ if (pass->check_hash_tbl) {
+ error = pass->check_hash_tbl(ip, tbl, hsize, pass->private);
+ if (error < 0) {
+ free(tbl);
+ posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
+ return error;
+ }
+ /* If hash table changes were made, read it in again. */
+ if (error) {
+ free(tbl);
+ tbl = get_dir_hash(ip);
+ if (tbl == NULL) {
+ perror("get_dir_hash");
+ return -1;
+ }
+ }
+ }
+
/* 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. */
@@ -768,21 +786,6 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
return 0;
}
- /* If the old leaf was a duplicate referenced by a
- previous dinode, we can't check the number of
- pointers because the number of pointers may be for
- that other dinode's reference, not this one. */
- if (pass->check_num_ptrs && !old_was_dup &&
- valid_block(ip->i_sbd, old_leaf)) {
- error = pass->check_num_ptrs(ip, old_leaf,
- &ref_count,
- &lindex,
- &oldleaf);
- if (error) {
- free(tbl);
- return error;
- }
- }
error = check_leaf(ip, lindex, pass, &ref_count,
&leaf_no, old_leaf, &bad_leaf,
first_ok_leaf, &leaf, &oldleaf);
@@ -1688,3 +1691,144 @@ void reprocess_inode(struct gfs2_inode *ip, const char *desc)
log_err( _("Error %d reprocessing the %s metadata tree.\n"),
error, desc);
}
+
+/*
+ * write_new_leaf - allocate and write a new leaf to cover a gap in hash table
+ * @dip: the directory inode
+ * @start_lindex: where in the hash table to start writing
+ * @num_copies: number of copies of the pointer to write into hash table
+ * @before_or_after: desc. of whether this is being added before/after/etc.
+ * @bn: pointer to return the newly allocated leaf's block number
+ */
+int write_new_leaf(struct gfs2_inode *dip, int start_lindex, int num_copies,
+ const char *before_or_after, uint64_t *bn)
+{
+ struct gfs2_buffer_head *nbh;
+ struct gfs2_leaf *leaf;
+ struct gfs2_dirent *dent;
+ int count, i;
+ int factor = 0, pad_size;
+ uint64_t *cpyptr;
+ char *padbuf;
+ int divisor = num_copies;
+ int end_lindex = start_lindex + num_copies;
+
+ padbuf = malloc(num_copies * sizeof(uint64_t));
+ /* calculate the depth needed for the new leaf */
+ while (divisor > 1) {
+ factor++;
+ divisor /= 2;
+ }
+ /* Make sure the number of copies is properly a factor of 2 */
+ if ((1 << factor) != num_copies) {
+ log_err(_("Program error: num_copies not a factor of 2.\n"));
+ log_err(_("num_copies=%d, dinode = %lld (0x%llx)\n"),
+ num_copies,
+ (unsigned long long)dip->i_di.di_num.no_addr,
+ (unsigned long long)dip->i_di.di_num.no_addr);
+ log_err(_("lindex = %d (0x%x)\n"), start_lindex, start_lindex);
+ stack;
+ free(padbuf);
+ return -1;
+ }
+
+ /* allocate and write out a new leaf block */
+ *bn = meta_alloc(dip);
+ fsck_blockmap_set(dip, *bn, _("directory leaf"), gfs2_leaf_blk);
+ log_err(_("A new directory leaf was allocated at block %lld "
+ "(0x%llx) to fill the %d (0x%x) pointer gap %s the existing "
+ "pointer at index %d (0x%x).\n"), (unsigned long long)*bn,
+ (unsigned long long)*bn, num_copies, num_copies,
+ before_or_after, start_lindex, start_lindex);
+ dip->i_di.di_blocks++;
+ bmodified(dip->i_bh);
+ nbh = bget(dip->i_sbd, *bn);
+ memset(nbh->b_data, 0, dip->i_sbd->bsize);
+ leaf = (struct gfs2_leaf *)nbh->b_data;
+ leaf->lf_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
+ leaf->lf_header.mh_type = cpu_to_be32(GFS2_METATYPE_LF);
+ leaf->lf_header.mh_format = cpu_to_be32(GFS2_FORMAT_LF);
+ leaf->lf_depth = cpu_to_be16(dip->i_di.di_depth - factor);
+
+ /* initialize the first dirent on the new leaf block */
+ dent = (struct gfs2_dirent *)(nbh->b_data + sizeof(struct gfs2_leaf));
+ dent->de_rec_len = cpu_to_be16(dip->i_sbd->bsize -
+ sizeof(struct gfs2_leaf));
+ bmodified(nbh);
+ brelse(nbh);
+
+ /* pad the hash table with the new leaf block */
+ cpyptr = (uint64_t *)padbuf;
+ for (i = start_lindex; i < end_lindex; i++) {
+ *cpyptr = cpu_to_be64(*bn);
+ cpyptr++;
+ }
+ pad_size = num_copies * sizeof(uint64_t);
+ log_err(_("Writing to the hash table of directory %lld "
+ "(0x%llx) at index: 0x%x for 0x%lx pointers.\n"),
+ (unsigned long long)dip->i_di.di_num.no_addr,
+ (unsigned long long)dip->i_di.di_num.no_addr,
+ start_lindex, pad_size / sizeof(uint64_t));
+ if (dip->i_sbd->gfs1)
+ count = gfs1_writei(dip, padbuf, start_lindex *
+ sizeof(uint64_t), pad_size);
+ else
+ count = gfs2_writei(dip, padbuf, start_lindex *
+ sizeof(uint64_t), pad_size);
+ free(padbuf);
+ if (count != pad_size) {
+ log_err( _("Error: bad write while fixing directory leaf "
+ "pointers.\n"));
+ return -1;
+ }
+ return 0;
+}
+
+/* repair_leaf - Warn the user of an error and ask permission to fix it
+ * 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. */
+int repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no, int lindex,
+ int ref_count, const char *msg)
+{
+ int new_leaf_blks = 0, error, refs;
+ uint64_t bn = 0;
+
+ log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
+ " (0x%llx) %s.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)*leaf_no,
+ (unsigned long long)*leaf_no, msg);
+ if (!query( _("Attempt to patch around it? (y/n) "))) {
+ log_err( _("Bad leaf left in place.\n"));
+ goto out;
+ }
+ /* We can only write leafs in quantities that are factors of
+ two, since leaves are doubled, not added sequentially.
+ So if we have a hole that's not a factor of 2, we have to
+ break it down into separate leaf blocks that are. */
+ while (ref_count) {
+ refs = 1;
+ while (refs <= ref_count) {
+ if (refs * 2 > ref_count)
+ break;
+ refs *= 2;
+ }
+ error = write_new_leaf(ip, lindex, refs, _("replacing"), &bn);
+ if (error)
+ return error;
+
+ new_leaf_blks++;
+ lindex += refs;
+ ref_count -= refs;
+ }
+ 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);
+out:
+ *leaf_no = bn;
+ return new_leaf_blks;
+}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index e114427..c43baf0 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -39,6 +39,11 @@ extern struct gfs2_inode *fsck_system_inode(struct gfs2_sbd *sdp,
uint64_t block);
extern int find_remove_dup(struct gfs2_inode *ip, uint64_t block,
const char *btype);
+extern int write_new_leaf(struct gfs2_inode *dip, int start_lindex,
+ int num_copies, const char *before_or_after,
+ uint64_t *bn);
+extern int repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no, int lindex,
+ int ref_count, const char *msg);
extern int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
const char *btype);
@@ -95,9 +100,10 @@ struct metawalk_fxns {
int (*finish_eattr_indir) (struct gfs2_inode *ip, int leaf_pointers,
int leaf_pointer_errors, void *private);
void (*big_file_msg) (struct gfs2_inode *ip, uint64_t blks_checked);
- int (*check_num_ptrs) (struct gfs2_inode *ip, uint64_t leafno,
- int *ref_count, int *lindex,
- struct gfs2_leaf *leaf);
+ int (*check_hash_tbl) (struct gfs2_inode *ip, uint64_t *tbl,
+ unsigned hsize, void *private);
+ int (*repair_leaf) (struct gfs2_inode *ip, uint64_t *leaf_no,
+ int lindex, int ref_count, const char *msg);
};
#endif /* _METAWALK_H */
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 9a2398a..13cb1cb 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -59,8 +59,6 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
struct gfs2_ea_header *ea_hdr,
struct gfs2_ea_header *ea_hdr_prev,
void *private);
-static int check_num_ptrs(struct gfs2_inode *ip, uint64_t leafno,
- int *ref_count, int *lindex, struct gfs2_leaf *leaf);
static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
int leaf_pointer_errors, void *private);
static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
@@ -91,7 +89,6 @@ struct metawalk_fxns pass1_fxns = {
.check_eattr_extentry = check_extended_leaf_eattr,
.finish_eattr_indir = finish_eattr_indir,
.big_file_msg = big_file_comfort,
- .check_num_ptrs = check_num_ptrs,
};
struct metawalk_fxns undo_fxns = {
@@ -203,145 +200,6 @@ struct metawalk_fxns sysdir_fxns = {
.check_dentry = resuscitate_dentry,
};
-/*
- * fix_leaf_pointers - fix a directory dinode that has a number of pointers
- * that is not a multiple of 2.
- * dip - the directory inode having the problem
- * lindex - the index of the leaf right after the problem (need to back up)
- * cur_numleafs - current (incorrect) number of instances of the leaf block
- * correct_numleafs - the correct number instances of the leaf block
- */
-static int fix_leaf_pointers(struct gfs2_inode *dip, int *lindex,
- int cur_numleafs, int correct_numleafs)
-{
- int count;
- char *ptrbuf;
- int start_lindex = *lindex - cur_numleafs; /* start of bad ptrs */
- int tot_num_ptrs = (1 << dip->i_di.di_depth) - start_lindex;
- int bufsize = tot_num_ptrs * sizeof(uint64_t);
- int off_by = cur_numleafs - correct_numleafs;
-
- ptrbuf = malloc(bufsize);
- if (!ptrbuf) {
- log_err( _("Error: Cannot allocate memory to fix the leaf "
- "pointers.\n"));
- return -1;
- }
- /* Read all the pointers, starting with the first bad one */
- count = gfs2_readi(dip, ptrbuf, start_lindex * sizeof(uint64_t),
- bufsize);
- if (count != bufsize) {
- log_err( _("Error: bad read while fixing leaf pointers.\n"));
- free(ptrbuf);
- return -1;
- }
-
- bufsize -= off_by * sizeof(uint64_t); /* We need to write fewer */
- /* Write the same pointers, but offset them so they fit within the
- smaller factor of 2. So if we have 12 pointers, write out only
- the last 8 of them. If we have 7, write the last 4, etc.
- We need to write these starting at the current lindex and adjust
- lindex accordingly. */
- if (dip->i_sbd->gfs1)
- count = gfs1_writei(dip, ptrbuf + (off_by * sizeof(uint64_t)),
- start_lindex * sizeof(uint64_t), bufsize);
- else
- count = gfs2_writei(dip, ptrbuf + (off_by * sizeof(uint64_t)),
- start_lindex * sizeof(uint64_t), bufsize);
- if (count != bufsize) {
- log_err( _("Error: bad read while fixing leaf pointers.\n"));
- free(ptrbuf);
- return -1;
- }
- /* Now zero out the hole left at the end */
- memset(ptrbuf, 0, off_by * sizeof(uint64_t));
- if (dip->i_sbd->gfs1)
- gfs1_writei(dip, ptrbuf, (start_lindex * sizeof(uint64_t)) +
- bufsize, off_by * sizeof(uint64_t));
- else
- gfs2_writei(dip, ptrbuf, (start_lindex * sizeof(uint64_t)) +
- bufsize, off_by * sizeof(uint64_t));
- free(ptrbuf);
- *lindex -= off_by; /* adjust leaf index to account for the change */
- return 0;
-}
-
-/**
- * check_num_ptrs - check a previously processed leaf's pointer count in the
- * hash table.
- *
- * The number of pointers in a directory hash table that point to any given
- * leaf block should always be a factor of two. The difference between the
- * leaf block's depth and the dinode's di_depth gives us the factor.
- * This function makes sure the leaf follows the rules properly.
- *
- * ip - pointer to the in-core inode structure
- * leafno - the leaf number we're operating on
- * ref_count - the number of pointers to this leaf we actually counted.
- * exp_count - the number of pointers to this leaf we expect based on
- * ip depth minus leaf depth.
- * lindex - leaf index number
- * leaf - the leaf structure for the leaf block to check
- */
-static int check_num_ptrs(struct gfs2_inode *ip, uint64_t leafno,
- int *ref_count, int *lindex, struct gfs2_leaf *leaf)
-{
- int factor = 0, divisor = *ref_count, multiple = 1, error = 0;
- struct gfs2_buffer_head *lbh;
- int exp_count;
-
- /* Check to see if the number of pointers we found is a power of 2.
- It needs to be and if it's not we need to fix it.*/
- while (divisor > 1) {
- factor++;
- divisor /= 2;
- multiple = multiple << 1;
- }
- if (*ref_count != multiple) {
- log_err( _("Directory #%llu (0x%llx) has an invalid number of "
- "pointers to leaf #%llu (0x%llx)\n\tFound: %u, "
- "which is not a factor of 2.\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)leafno,
- (unsigned long long)leafno, *ref_count);
- if (!query( _("Attempt to fix it? (y/n) "))) {
- log_err( _("Directory inode was not fixed.\n"));
- return 1;
- }
- error = fix_leaf_pointers(ip, lindex, *ref_count, multiple);
- if (error)
- return error;
- *ref_count = multiple;
- log_err( _("Directory inode was fixed.\n"));
- }
- /* Check to see if the counted number of leaf pointers is what we
- expect based on the leaf depth. */
- exp_count = (1 << (ip->i_di.di_depth - leaf->lf_depth));
- if (*ref_count != exp_count) {
- log_err( _("Directory #%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)leafno,
- (unsigned long long)leafno, *ref_count, exp_count);
- if (!query( _("Attempt to fix it? (y/n) "))) {
- log_err( _("Directory leaf was not fixed.\n"));
- return 1;
- }
- lbh = bread(ip->i_sbd, leafno);
- gfs2_leaf_in(leaf, lbh);
- log_err( _("Leaf depth was %d, changed to %d\n"),
- leaf->lf_depth, ip->i_di.di_depth - factor);
- leaf->lf_depth = ip->i_di.di_depth - factor;
- gfs2_leaf_out(leaf, lbh);
- brelse(lbh);
- log_err( _("Directory leaf was fixed.\n"));
- }
- return 0;
-}
-
static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
{
struct block_count *bc = (struct block_count *) private;
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index e27a9f7..359bca0 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -13,6 +13,7 @@
#include "eattr.h"
#include "metawalk.h"
#include "link.h"
+#include "lost_n_found.h"
#include "inode_hash.h"
#define MAX_FILENAME 256
@@ -293,6 +294,11 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
return 0;
}
+static int hash_table_index(uint32_t hash, struct gfs2_inode *ip)
+{
+ return hash >> (32 - ip->i_di.di_depth);
+}
+
/* basic_dentry_checks - fundamental checks for directory entries
*
* @ip: pointer to the incode inode structure
@@ -713,6 +719,530 @@ nuke_dentry:
return 1;
}
+/* pad_with_leafblks - pad a hash table with pointers to new leaf blocks
+ *
+ * @ip: pointer to the dinode structure
+ * @tbl: pointer to the hash table in memory
+ * @lindex: index location within the hash table to pad
+ * @len: number of pointers to be padded
+ */
+static void pad_with_leafblks(struct gfs2_inode *ip, uint64_t *tbl,
+ int lindex, int len)
+{
+ int new_len, i;
+ uint32_t proper_start = lindex;
+ uint64_t new_leaf_blk;
+
+ log_err(_("Padding inode %llu (0x%llx) hash table at offset %d (0x%x) "
+ "for %d pointers.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr, lindex, lindex,
+ len);
+ while (len) {
+ new_len = 1;
+ /* Determine the next factor of 2 down from extras. We can't
+ just write out a leaf block on a power-of-two boundary.
+ We also need to make sure it has a length that will
+ ensure a "proper start" block as well. */
+ while ((new_len << 1) <= len) {
+ /* Translation: If doubling the size of the new leaf
+ will make its start boundary wrong, we have to
+ settle for a smaller length (and iterate more). */
+ proper_start = (lindex & ~((new_len << 1) - 1));
+ if (lindex != proper_start)
+ break;
+ new_len <<= 1;
+ }
+ write_new_leaf(ip, lindex, new_len, "after", &new_leaf_blk);
+ log_err(_("New leaf block was allocated at %llu (0x%llx) for "
+ "index %d (0x%x), length %d\n"),
+ (unsigned long long)new_leaf_blk,
+ (unsigned long long)new_leaf_blk,
+ lindex, lindex, new_len);
+ fsck_blockmap_set(ip, new_leaf_blk, _("pad leaf"),
+ gfs2_leaf_blk);
+ /* Fix the hash table in memory to have the new leaf */
+ for (i = 0; i < new_len; i++)
+ tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
+ len -= new_len;
+ lindex += new_len;
+ }
+}
+
+/* lost_leaf - repair a leaf block that's on the wrong directory inode
+ *
+ * If the correct index is less than the starting index, we have a problem.
+ * Since we process the index sequentially, the previous index has already
+ * been processed, fixed, and is now correct. But this leaf wants to overwrite
+ * a previously written good leaf. The only thing we can do is move all the
+ * directory entries to lost+found so we don't overwrite the good leaf. Then
+ * we need to pad the gap we leave.
+ */
+static int lost_leaf(struct gfs2_inode *ip, uint64_t *tbl, uint64_t leafno,
+ int ref_count, int lindex, struct gfs2_buffer_head *bh)
+{
+ char *filename;
+ char *bh_end = bh->b_data + ip->i_sbd->bsize;
+ struct gfs2_dirent de, *dent;
+ int error;
+
+ log_err(_("Leaf block %llu (0x%llx) seems to be out of place and its "
+ "contents need to be moved to lost+found.\n"),
+ (unsigned long long)leafno, (unsigned long long)leafno);
+ if (!query( _("Attempt to fix it? (y/n) "))) {
+ log_err( _("Directory leaf was not fixed.\n"));
+ return 0;
+ }
+ make_sure_lf_exists(ip);
+
+ dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
+ while (1) {
+ char tmp_name[PATH_MAX];
+
+ memset(&de, 0, sizeof(struct gfs2_dirent));
+ gfs2_dirent_in(&de, (char *)dent);
+ filename = (char *)dent + sizeof(struct gfs2_dirent);
+ memset(tmp_name, 0, sizeof(tmp_name));
+ if (de.de_name_len > sizeof(filename)) {
+ log_debug(_("Encountered bad filename length; "
+ "stopped processing.\n"));
+ break;
+ }
+ memcpy(tmp_name, filename, de.de_name_len);
+ if ((de.de_name_len == 1 && filename[0] == '.')) {
+ log_debug(_("Skipping entry '.'\n"));
+ } else if (de.de_name_len == 2 && filename[0] == '.' &&
+ filename[1] == '.') {
+ log_debug(_("Skipping entry '..'\n"));
+ } else if (!de.de_inum.no_formal_ino) { /* sentinel */
+ log_debug(_("Skipping sentinel '%s'\n"), tmp_name);
+ } else {
+ uint32_t count;
+ struct dir_status ds = {0};
+ uint8_t q = 0;
+
+ error = basic_dentry_checks(ip, dent, &de.de_inum,
+ tmp_name, &count, &de,
+ &ds, &q, bh);
+ if (error) {
+ log_err(_("Not relocating corrupt entry "
+ "\"%s\".\n"), tmp_name);
+ } else {
+ error = dir_add(lf_dip, filename,
+ de.de_name_len, &de.de_inum,
+ de.de_type);
+ if (error && error != -EEXIST) {
+ log_err(_("Error %d encountered while "
+ "trying to relocate \"%s\" "
+ "to lost+found.\n"), error,
+ tmp_name);
+ return error;
+ }
+ /* This inode is linked from lost+found */
+ incr_link_count(de.de_inum, lf_dip,
+ _("from lost+found"));
+ /* If it's a directory, lost+found is
+ back-linked to it via .. */
+ if (q == gfs2_inode_dir)
+ incr_link_count(lf_dip->i_di.di_num,
+ NULL,
+ _("to lost+found"));
+ log_err(_("Relocated \"%s\", block %llu "
+ "(0x%llx) to lost+found.\n"),
+ tmp_name,
+ (unsigned long long)de.de_inum.no_addr,
+ (unsigned long long)de.de_inum.no_addr);
+ }
+ }
+ if ((char *)dent + de.de_rec_len >= bh_end)
+ break;
+ dent = (struct gfs2_dirent *)((char *)dent + de.de_rec_len);
+ }
+ log_err(_("Directory entries from misplaced leaf block were relocated "
+ "to lost+found.\n"));
+ /* Free the lost leaf. */
+ fsck_blockmap_set(ip, leafno, _("lost leaf"), gfs2_block_free);
+ ip->i_di.di_blocks--;
+ bmodified(ip->i_bh);
+ /* Now we have to deal with the bad hash table entries pointing to the
+ misplaced leaf block. But we can't just fill the gap with a single
+ leaf. We have to write on nice power-of-two boundaries, and we have
+ to pad out any extra pointers. */
+ pad_with_leafblks(ip, tbl, lindex, ref_count);
+ return 1;
+}
+
+/* fix_hashtable - fix a corrupt hash table
+ *
+ * The main intent of this function is to sort out hash table problems.
+ * That is, it needs to determine if leaf blocks are in the wrong place,
+ * if the count of pointers is wrong, and if there are extra pointers.
+ * Everything should be placed on correct power-of-two boundaries appropriate
+ * to their leaf depth, and extra pointers should be correctly padded with new
+ * leaf blocks.
+ *
+ * @ip: the directory dinode structure pointer
+ * @tbl: hash table that's already read into memory
+ * @hsize: hash table size, as dictated by the dinode's di_depth
+ * @leafblk: the leaf block number that appears at this lindex in the tbl
+ * @lindex: leaf index that has a problem
+ * @proper_start: where this leaf's pointers should start, as far as the
+ * hash table is concerned (sight unseen; trusting the leaf
+ * really belongs here).
+ * @len: count of pointers in the hash table to this leafblk
+ * @proper_len: pointer to return the proper number of pointers, as the kernel
+ * calculates it, based on the leaf depth.
+ * @factor: the proper depth, given this number of pointers (rounded down).
+ *
+ * Returns: 0 - no changes made, or X if changes were made
+ */
+static int fix_hashtable(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize,
+ uint64_t leafblk, int lindex, uint32_t proper_start,
+ int len, int *proper_len, int factor)
+{
+ struct gfs2_buffer_head *lbh;
+ struct gfs2_leaf *leaf;
+ struct gfs2_dirent dentry, *de;
+ int changes = 0, error, i, extras, hash_index;
+ uint64_t new_leaf_blk;
+ uint32_t leaf_proper_start;
+
+ *proper_len = len;
+ log_err(_("Dinode %llu (0x%llx) has a hash table error at index "
+ "0x%x, length 0x%x: leaf block %llu (0x%llx)\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr, lindex, len,
+ (unsigned long long)leafblk, (unsigned long long)leafblk);
+ if (!query( _("Fix the hash table? (y/n) "))) {
+ log_err(_("Hash table not fixed.\n"));
+ return 0;
+ }
+
+ lbh = bread(ip->i_sbd, leafblk);
+ leaf = (struct gfs2_leaf *)lbh->b_data;
+ /* If the leaf's depth is out of range for this dinode, it's obviously
+ attached to the wrong dinode. Move the dirents to lost+found. */
+ if (be16_to_cpu(leaf->lf_depth) > ip->i_di.di_depth) {
+ log_err(_("This leaf block's depth (%d) is too big for this "
+ "dinode's depth (%d)\n"),
+ be16_to_cpu(leaf->lf_depth), ip->i_di.di_depth);
+ error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+ brelse(lbh);
+ return error;
+ }
+
+ memset(&dentry, 0, sizeof(struct gfs2_dirent));
+ de = (struct gfs2_dirent *)(lbh->b_data + sizeof(struct gfs2_leaf));
+ gfs2_dirent_in(&dentry, (char *)de);
+
+ /* If this is an empty leaf, we can just delete it and pad. */
+ if ((dentry.de_rec_len == cpu_to_be16(ip->i_sbd->bsize -
+ sizeof(struct gfs2_leaf))) &&
+ (dentry.de_inum.no_formal_ino == 0)) {
+ brelse(lbh);
+ gfs2_free_block(ip->i_sbd, leafblk);
+ log_err(_("Out of place leaf block %llu (0x%llx) had no "
+ "entries, so it was deleted.\n"),
+ (unsigned long long)leafblk,
+ (unsigned long long)leafblk);
+ pad_with_leafblks(ip, tbl, lindex, len);
+ log_err(_("Reprocessing index 0x%x (case 1).\n"), lindex);
+ return 1;
+ }
+
+ /* Calculate the proper number of pointers based on the leaf depth. */
+ *proper_len = 1 << (ip->i_di.di_depth - be16_to_cpu(leaf->lf_depth));
+
+ /* Look at the first dirent and check its hash value to see if it's
+ at the proper starting offset. */
+ hash_index = hash_table_index(dentry.de_hash, ip);
+ if (hash_index < lindex || hash_index > lindex + len) {
+ log_err(_("This leaf block has hash index %d, which is out of "
+ "bounds for where it appears in the hash table "
+ "(%d - %d)\n"),
+ hash_index, lindex, lindex + len);
+ error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+ brelse(lbh);
+ return error;
+ }
+
+ /* Now figure out where this leaf should start, and pad any pointers
+ up to that point with new leaf blocks. */
+ leaf_proper_start = (hash_index & ~(*proper_len - 1));
+ if (lindex < leaf_proper_start) {
+ log_err(_("Leaf pointers start at %d (0x%x), should be %d "
+ "(%x).\n"), lindex, lindex,
+ leaf_proper_start, leaf_proper_start);
+ pad_with_leafblks(ip, tbl, lindex, leaf_proper_start - lindex);
+ brelse(lbh);
+ return 1; /* reprocess the starting lindex */
+ }
+ /* If the proper start according to the leaf's hash index is later
+ than the proper start according to the hash table, it's once
+ again lost and we have to relocate it. The same applies if the
+ leaf's hash index is prior to the proper state, but the leaf is
+ already at its maximum depth. */
+ if ((leaf_proper_start < proper_start) ||
+ ((*proper_len > len || lindex > leaf_proper_start) &&
+ be16_to_cpu(leaf->lf_depth) == ip->i_di.di_depth)) {
+ log_err(_("Leaf block should start at 0x%x, but it appears at "
+ "0x%x in the hash table.\n"), leaf_proper_start,
+ proper_start);
+ error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+ brelse(lbh);
+ return error;
+ }
+
+ /* If we SHOULD have more pointers than we do, we can solve the
+ problem by splitting the block to a lower depth. Then we may have
+ the right number of pointers. If the leaf block pointers start
+ later than they should, we can split the leaf to give it a smaller
+ footprint in the hash table. */
+ if ((*proper_len > len || lindex > leaf_proper_start) &&
+ ip->i_di.di_depth > be16_to_cpu(leaf->lf_depth)) {
+ log_err(_("For depth %d, length %d, the proper start is: "
+ "0x%x.\n"), factor, len, proper_start);
+ changes++;
+ new_leaf_blk = find_free_blk(ip->i_sbd);
+ dir_split_leaf(ip, lindex, leafblk, lbh);
+ /* re-read the leaf to pick up dir_split_leaf's changes */
+ gfs2_leaf_in(leaf, lbh);
+ *proper_len = 1 << (ip->i_di.di_depth -
+ be16_to_cpu(leaf->lf_depth));
+ log_err(_("Leaf block %llu (0x%llx) was split from length "
+ "%d to %d\n"), (unsigned long long)leafblk,
+ (unsigned long long)leafblk, len, *proper_len);
+ if (*proper_len < 0) {
+ log_err(_("Programming error: proper_len=%d, "
+ "di_depth = %d, lf_depth = %d.\n"),
+ *proper_len, ip->i_di.di_depth,
+ be16_to_cpu(leaf->lf_depth));
+ exit(FSCK_ERROR);
+ }
+ log_err(_("New split-off leaf block was allocated at %lld "
+ "(0x%llx) for index %d (0x%x)\n"),
+ (unsigned long long)new_leaf_blk,
+ (unsigned long long)new_leaf_blk, lindex, lindex);
+ fsck_blockmap_set(ip, new_leaf_blk, _("split leaf"),
+ gfs2_leaf_blk);
+ log_err(_("Hash table repaired.\n"));
+ /* Fix up the hash table in memory to include the new leaf */
+ for (i = 0; i < *proper_len; i++)
+ tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
+ if (*proper_len < (len >> 1)) {
+ log_err(_("One leaf split is not enough. The hash "
+ "table will need to be reprocessed.\n"));
+ brelse(lbh);
+ return changes;
+ }
+ lindex += (*proper_len); /* skip the new leaf from the split */
+ len -= (*proper_len);
+ }
+ if (*proper_len < len) {
+ log_err(_("There are %d pointers, but leaf 0x%llx's "
+ "depth, %d, only allows %d\n"),
+ len, (unsigned long long)leafblk,
+ be16_to_cpu(leaf->lf_depth), *proper_len);
+ }
+ brelse(lbh);
+ /* At this point, lindex should be at the proper end of the pointers.
+ Now we need to replace any extra duplicate pointers to the old
+ (original) leafblk (that ran off the end) with new leaf blocks. */
+ lindex += (*proper_len); /* Skip past the normal good pointers */
+ len -= (*proper_len);
+ extras = 0;
+ for (i = 0; i < len; i++) {
+ if (be64_to_cpu(tbl[lindex + i]) == leafblk)
+ extras++;
+ else
+ break;
+ }
+ if (extras) {
+ log_err(_("Found %d extra pointers to leaf %llu (0x%llx)\n"),
+ extras, (unsigned long long)leafblk,
+ (unsigned long long)leafblk);
+ pad_with_leafblks(ip, tbl, lindex, extras);
+ log_err(_("Reprocessing index 0x%x (case 2).\n"), lindex);
+ return 1;
+ }
+ return changes;
+}
+
+/* check_hash_tbl - check that the hash table is sane
+ *
+ * We've got to make sure the hash table is sane. Each leaf needs to
+ * be counted a proper power of 2. We can't just have 3 pointers to a leaf.
+ * The number of pointers must correspond to the proper leaf depth, and they
+ * must all fall on power-of-two boundaries. The leaf block pointers all need
+ * to fall properly on these boundaries, otherwise the kernel code's
+ * calculations will land it on the wrong leaf block while it's searching,
+ * and the result will be files you can see with ls, but can't open, delete
+ * or use them.
+ *
+ * The goal of this function is to check the hash table to make sure the
+ * boundaries and lengths all line up properly, and if not, to fix it.
+ *
+ * Note: There's a delicate balance here, because this function gets called
+ * BEFORE leaf blocks are checked by function check_leaf from function
+ * check_leaf_blks: the hash table has to be sane before we can start
+ * checking all the leaf blocks. And yet if there's hash table corruption
+ * we may need to reference leaf blocks to fix it, which means we need
+ * to check and/or fix a leaf block along the way.
+ */
+static int check_hash_tbl(struct gfs2_inode *ip, uint64_t *tbl,
+ unsigned hsize, void *private)
+{
+ int error = 0;
+ int lindex, len, proper_len, i, changes = 0;
+ uint64_t leafblk;
+ struct gfs2_leaf leaf;
+ struct gfs2_buffer_head *lbh;
+ int factor;
+ uint32_t proper_start;
+
+ lindex = 0;
+ while (lindex < hsize) {
+ if (fsck_abort)
+ return changes;
+ len = 1;
+ factor = 0;
+ leafblk = be64_to_cpu(tbl[lindex]);
+ while (lindex + (len << 1) - 1 < hsize) {
+ if (be64_to_cpu(tbl[lindex + (len << 1) - 1]) !=
+ leafblk)
+ break;
+ len <<= 1;
+ factor++;
+ }
+
+ /* Check for leftover pointers after the factor of two: */
+ proper_len = len; /* A factor of 2 that fits nicely */
+ while (lindex + len < hsize &&
+ be64_to_cpu(tbl[lindex + len]) == leafblk)
+ len++;
+
+ /* See if that leaf block is valid. If not, write a new one
+ that falls on a proper boundary. If it doesn't naturally,
+ we may need more. */
+ if (!valid_block(ip->i_sbd, leafblk)) {
+ uint64_t new_leafblk;
+
+ log_err(_("Dinode %llu (0x%llx) has bad leaf pointers "
+ "at offset %d for %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ lindex, len);
+ if (!query( _("Fix the hash table? (y/n) "))) {
+ log_err(_("Hash table not fixed.\n"));
+ lindex += len;
+ continue;
+ }
+ error = write_new_leaf(ip, lindex, proper_len,
+ _("replacing"), &new_leafblk);
+ if (error)
+ return error;
+
+ for (i = lindex; i < lindex + proper_len; i++)
+ tbl[i] = cpu_to_be64(new_leafblk);
+ lindex += proper_len;
+ continue;
+ }
+ /* Make sure they call on proper leaf-split boundaries. This
+ is the calculation used by the kernel, and dir_split_leaf */
+ proper_start = (lindex & ~(proper_len - 1));
+ if (lindex != proper_start) {
+ log_debug(_("lindex 0x%llx is not a proper starting "
+ "point for this leaf: 0x%llx\n"),
+ (unsigned long long)lindex,
+ (unsigned long long)proper_start);
+ changes = fix_hashtable(ip, tbl, hsize, leafblk,
+ lindex, proper_start, len,
+ &proper_len, factor);
+ /* Check if we need to split more leaf blocks */
+ if (changes) {
+ if (proper_len < (len >> 1))
+ log_err(_("More leaf splits are "
+ "needed; "));
+ log_err(_("Reprocessing index 0x%x (case 3).\n"),
+ lindex);
+ continue; /* Make it reprocess the lindex */
+ }
+ }
+ /* Check for extra pointers to this leaf. At this point, len
+ is the number of pointers we have. proper_len is the proper
+ number of pointers if the hash table is assumed correct.
+ Function fix_hashtable will read in the leaf block and
+ determine the "actual" proper length based on the leaf
+ depth, and adjust the hash table accordingly. */
+ if (len != proper_len) {
+ log_err(_("Length %d (0x%x) is not a proper length "
+ "for this leaf. Valid boundary assumed to "
+ "be %d (0x%x).\n"),
+ len, len, proper_len, proper_len);
+ lbh = bread(ip->i_sbd, leafblk);
+ gfs2_leaf_in(&leaf, lbh);
+ if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) ||
+ leaf.lf_depth > ip->i_di.di_depth)
+ leaf.lf_depth = factor;
+ brelse(lbh);
+ changes = fix_hashtable(ip, tbl, hsize, leafblk,
+ lindex, lindex, len,
+ &proper_len, leaf.lf_depth);
+ /* If fixing the hash table made changes, we can no
+ longer count on the leaf block pointers all pointing
+ to the same leaf (which is checked below). To avoid
+ flagging another error, reprocess the offset. */
+ if (changes) {
+ log_err(_("Reprocessing index 0x%x (case 4).\n"),
+ lindex);
+ continue; /* Make it reprocess the lindex */
+ }
+ }
+
+ /* Now make sure they're all the same pointer */
+ for (i = lindex; i < lindex + proper_len; i++) {
+ if (fsck_abort)
+ return changes;
+
+ if (be64_to_cpu(tbl[i]) == leafblk) /* No problem */
+ continue;
+
+ log_err(_("Dinode %llu (0x%llx) has a hash table "
+ "inconsistency at index %d (0x%d) for %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ i, i, len);
+ if (!query( _("Fix the hash table? (y/n) "))) {
+ log_err(_("Hash table not fixed.\n"));
+ continue;
+ }
+ changes++;
+ /* Now we have to determine if the hash table is
+ corrupt, or if the leaf has the wrong depth. */
+ lbh = bread(ip->i_sbd, leafblk);
+ gfs2_leaf_in(&leaf, lbh);
+ brelse(lbh);
+ /* Calculate the expected pointer count based on the
+ leaf depth. */
+ proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
+ if (proper_len != len) {
+ log_debug(_("Length 0x%x is not proper for "
+ "this leaf: 0x%x"),
+ len, proper_len);
+ changes = fix_hashtable(ip, tbl, hsize,
+ leafblk, lindex,
+ lindex, len,
+ &proper_len,
+ leaf.lf_depth);
+ break;
+ }
+ }
+ lindex += proper_len;
+ }
+ if (!error && changes)
+ error = 1;
+ return error;
+}
struct metawalk_fxns pass2_fxns = {
.private = NULL,
@@ -723,6 +1253,8 @@ struct metawalk_fxns pass2_fxns = {
.check_eattr_leaf = check_eattr_leaf,
.check_dentry = check_dentry,
.check_eattr_entry = NULL,
+ .check_hash_tbl = check_hash_tbl,
+ .repair_leaf = repair_leaf,
};
/* Check system directory inode */
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: Add formal inode check to basic dirent checks
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=d94ef845ac1...
Commit: d94ef845ac19bed44faaee79d629c9f4b9862a28
Parent: 6c3ff422c0062fa691e7b27fc95681694152d051
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 12:15:33 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 14:57:04 2013 -0500
fsck.gfs2: Add formal inode check to basic dirent checks
This patch adds a check to the basic directory entry checks which
compares the formal inode number of the directory entry to the
formal inode number in the inode tree that was set up by pass1.
If the numbers don't match, this directory entry is corrupt.
rhbz#902920
---
gfs2/fsck/pass2.c | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 4460508..e27a9f7 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -313,6 +313,7 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
uint32_t calculated_hash;
struct gfs2_inode *entry_ip = NULL;
int error;
+ struct inode_info *ii;
if (!valid_block(ip->i_sbd, entry->no_addr)) {
log_err( _("Block # referenced by directory entry %s in inode "
@@ -488,6 +489,25 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
fsck_inode_put(&entry_ip);
return 1;
}
+ /* We need to verify the formal inode number matches. If it doesn't,
+ it needs to be deleted. */
+ ii = inodetree_find(entry->no_addr);
+ if (ii && ii->di_num.no_formal_ino != entry->no_formal_ino) {
+ log_err( _("Directory entry '%s' pointing to block %llu "
+ "(0x%llx) in directory %llu (0x%llx) has the "
+ "wrong 'formal' inode number.\n"), tmp_name,
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr);
+ log_err( _("The directory entry has %llu (0x%llx) but the "
+ "inode has %llu (0x%llx)\n"),
+ (unsigned long long)entry->no_formal_ino,
+ (unsigned long long)entry->no_formal_ino,
+ (unsigned long long)ii->di_num.no_formal_ino,
+ (unsigned long long)ii->di_num.no_formal_ino);
+ return 1;
+ }
return 0;
}
10 years, 12 months
cluster: RHEL6 - fsck.gfs2: Move basic directory entry checks to separate function
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=6c3ff422c00...
Commit: 6c3ff422c0062fa691e7b27fc95681694152d051
Parent: 89e2e4a2f24f54c14cf7b241daea375324a0a5eb
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 11:55:06 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Fri May 17 14:56:56 2013 -0500
fsck.gfs2: Move basic directory entry checks to separate function
This patch moves a huge chunk of code from bloated function
check_dentry. The moved section of code performs basic directory entry
checks. The code is basically unchanged, but I made clear_eattrs
metawalk functions global.
rhbz#902920
---
gfs2/fsck/pass2.c | 160 +++++++++++++++++++++++++++++++----------------------
1 files changed, 94 insertions(+), 66 deletions(-)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 0c1c2f5..4460508 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -17,6 +17,13 @@
#define MAX_FILENAME 256
+struct metawalk_fxns clear_eattrs = {
+ .check_eattr_indir = delete_eattr_indir,
+ .check_eattr_leaf = delete_eattr_leaf,
+ .check_eattr_entry = clear_eattr_entry,
+ .check_eattr_extentry = clear_eattr_extentry,
+};
+
/* Set children's parent inode in dir_info structure - ext2 does not set
* dotdot inode here, but instead in pass3 - should we? */
static int set_parent_dir(struct gfs2_sbd *sdp, struct gfs2_inum child,
@@ -286,51 +293,35 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
return 0;
}
-/* 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,
- struct gfs2_dirent *prev_de,
- struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, void *priv)
+/* basic_dentry_checks - fundamental checks for directory entries
+ *
+ * @ip: pointer to the incode inode structure
+ * @entry: pointer to the inum info
+ * @tmp_name: user-friendly file name
+ * @count: pointer to the entry count
+ * @de: pointer to the directory entry
+ *
+ * Returns: 1 means corruption, nuke the dentry, 0 means checks pass
+ */
+static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
+ struct gfs2_inum *entry, const char *tmp_name,
+ uint32_t *count, struct gfs2_dirent *de,
+ struct dir_status *ds, uint8_t *q,
+ struct gfs2_buffer_head *bh)
{
struct gfs2_sbd *sdp = ip->i_sbd;
- uint8_t q = 0;
- char tmp_name[MAX_FILENAME];
- struct gfs2_inum entry;
- struct dir_status *ds = (struct dir_status *) priv;
- int error;
- struct gfs2_inode *entry_ip = NULL;
- struct metawalk_fxns clear_eattrs = {0};
- struct gfs2_dirent dentry, *de;
uint32_t calculated_hash;
+ struct gfs2_inode *entry_ip = NULL;
+ int error;
- memset(&dentry, 0, sizeof(struct gfs2_dirent));
- gfs2_dirent_in(&dentry, (char *)dent);
- de = &dentry;
-
- clear_eattrs.check_eattr_indir = delete_eattr_indir;
- clear_eattrs.check_eattr_leaf = delete_eattr_leaf;
- clear_eattrs.check_eattr_entry = clear_eattr_entry;
- clear_eattrs.check_eattr_extentry = clear_eattr_extentry;
-
- entry.no_addr = de->de_inum.no_addr;
- entry.no_formal_ino = de->de_inum.no_formal_ino;
-
- /* Start of checks */
- memset(tmp_name, 0, MAX_FILENAME);
- if (de->de_name_len < MAX_FILENAME)
- strncpy(tmp_name, filename, de->de_name_len);
- else
- strncpy(tmp_name, filename, MAX_FILENAME - 1);
-
- if (!valid_block(ip->i_sbd, entry.no_addr)) {
+ if (!valid_block(ip->i_sbd, entry->no_addr)) {
log_err( _("Block # referenced by directory entry %s in inode "
"%lld (0x%llx) is invalid\n"),
tmp_name, (unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
if (query( _("Clear directory entry to out of range block? "
"(y/n) "))) {
- goto nuke_dentry;
+ return 1;
} else {
log_err( _("Directory entry to out of range block remains\n"));
(*count)++;
@@ -347,7 +338,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
de->de_rec_len, de->de_name_len);
if (!query( _("Clear the directory entry? (y/n) "))) {
log_err( _("Directory entry not fixed.\n"));
- goto dentry_is_valid;
+ return 0;
}
fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
_("corrupt directory entry"),
@@ -369,7 +360,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
tmp_name)) {
log_err( _("Directory entry hash for %s not "
"fixed.\n"), tmp_name);
- goto dentry_is_valid;
+ return 0;
}
de->de_hash = calculated_hash;
gfs2_dirent_out(de, (char *)dent);
@@ -378,7 +369,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
tmp_name);
}
- q = block_type(entry.no_addr);
+ *q = block_type(entry->no_addr);
/* Get the status of the directory inode */
/**
* 1. Blocks marked "invalid" were invalidated due to duplicate
@@ -392,25 +383,25 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
* 2. Blocks marked "bad" need to have their entire
* metadata tree deleted.
*/
- if (q == gfs2_inode_invalid || q == gfs2_bad_block) {
+ if (*q == gfs2_inode_invalid || *q == gfs2_bad_block) {
/* This entry's inode has bad blocks in it */
/* Handle bad blocks */
log_err( _("Found directory entry '%s' pointing to invalid "
"block %lld (0x%llx)\n"), tmp_name,
- (unsigned long long)entry.no_addr,
- (unsigned long long)entry.no_addr);
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)entry->no_addr);
if (!query( _("Delete inode containing bad blocks? (y/n)"))) {
log_warn( _("Entry to inode containing bad blocks remains\n"));
- goto dentry_is_valid;
+ return 0;
}
- if (q == gfs2_bad_block) {
- if (ip->i_di.di_num.no_addr == entry.no_addr)
+ if (*q == gfs2_bad_block) {
+ if (ip->i_di.di_num.no_addr == entry->no_addr)
entry_ip = ip;
else
- entry_ip = fsck_load_inode(sdp, entry.no_addr);
+ entry_ip = fsck_load_inode(sdp, entry->no_addr);
if (ip->i_di.di_eattr) {
check_inode_eattr(entry_ip,
&pass2_fxns_delete);
@@ -419,29 +410,29 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
if (entry_ip != ip)
fsck_inode_put(&entry_ip);
}
- fsck_blockmap_set(ip, entry.no_addr,
+ fsck_blockmap_set(ip, entry->no_addr,
_("bad directory entry"), gfs2_block_free);
log_err( _("Inode %lld (0x%llx) was deleted.\n"),
- (unsigned long long)entry.no_addr,
- (unsigned long long)entry.no_addr);
- goto nuke_dentry;
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)entry->no_addr);
+ return 1;
}
- if (q < gfs2_inode_dir || q > gfs2_inode_sock) {
+ if (*q < gfs2_inode_dir || *q > gfs2_inode_sock) {
log_err( _("Directory entry '%s' referencing inode %llu "
"(0x%llx) in dir inode %llu (0x%llx) block type "
"%d: %s.\n"), tmp_name,
- (unsigned long long)entry.no_addr,
- (unsigned long long)entry.no_addr,
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)entry->no_addr,
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr,
- q, q == gfs2_inode_invalid ?
+ *q, *q == gfs2_inode_invalid ?
_("was previously marked invalid") :
_("was deleted or is not an inode"));
if (!query( _("Clear directory entry to non-inode block? "
"(y/n) "))) {
log_err( _("Directory entry to non-inode block remains\n"));
- goto dentry_is_valid;
+ return 0;
}
/* Don't decrement the link here: Here in pass2, we increment
@@ -460,20 +451,20 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
count on "delete_block_if_notdup" knowing whether it's
really a duplicate block if we never traversed the metadata
tree for the invalid inode. */
- goto nuke_dentry;
+ return 1;
}
- error = check_file_type(de->de_type, q, sdp->gfs1);
+ error = check_file_type(de->de_type, *q, sdp->gfs1);
if (error < 0) {
log_err( _("Error: directory entry type is "
"incompatible with block type at block %lld "
"(0x%llx) in directory inode %llu (0x%llx).\n"),
- (unsigned long long)entry.no_addr,
- (unsigned long long)entry.no_addr,
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)entry->no_addr,
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
log_err( _("Directory entry type is %d, block type is %d.\n"),
- de->de_type, q);
+ de->de_type, *q);
stack;
return -1;
}
@@ -481,22 +472,59 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
log_err( _("Type '%s' in dir entry (%s, %llu/0x%llx) conflicts"
" with type '%s' in dinode. (Dir entry is stale.)\n"),
de_type_string(de->de_type), tmp_name,
- (unsigned long long)entry.no_addr,
- (unsigned long long)entry.no_addr,
- block_type_string(q));
+ (unsigned long long)entry->no_addr,
+ (unsigned long long)entry->no_addr,
+ block_type_string(*q));
if (!query( _("Clear stale directory entry? (y/n) "))) {
log_err( _("Stale directory entry remains\n"));
- goto dentry_is_valid;
+ return 0;
}
- if (ip->i_di.di_num.no_addr == entry.no_addr)
+ if (ip->i_di.di_num.no_addr == entry->no_addr)
entry_ip = ip;
else
- entry_ip = fsck_load_inode(sdp, entry.no_addr);
+ entry_ip = fsck_load_inode(sdp, entry->no_addr);
check_inode_eattr(entry_ip, &clear_eattrs);
if (entry_ip != ip)
fsck_inode_put(&entry_ip);
- goto nuke_dentry;
+ return 1;
}
+ return 0;
+}
+
+/* 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,
+ struct gfs2_dirent *prev_de,
+ struct gfs2_buffer_head *bh, char *filename,
+ uint32_t *count, void *priv)
+{
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ uint8_t q = 0;
+ char tmp_name[MAX_FILENAME];
+ struct gfs2_inum entry;
+ struct dir_status *ds = (struct dir_status *) priv;
+ int error;
+ struct gfs2_inode *entry_ip = NULL;
+ struct gfs2_dirent dentry, *de;
+
+ memset(&dentry, 0, sizeof(struct gfs2_dirent));
+ gfs2_dirent_in(&dentry, (char *)dent);
+ de = &dentry;
+
+ entry.no_addr = de->de_inum.no_addr;
+ entry.no_formal_ino = de->de_inum.no_formal_ino;
+
+ /* Start of checks */
+ memset(tmp_name, 0, MAX_FILENAME);
+ if (de->de_name_len < MAX_FILENAME)
+ strncpy(tmp_name, filename, de->de_name_len);
+ else
+ strncpy(tmp_name, filename, MAX_FILENAME - 1);
+
+ error = basic_dentry_checks(ip, dent, &entry, tmp_name, count, de,
+ ds, &q, bh);
+ if (error)
+ goto nuke_dentry;
if (!strcmp(".", tmp_name)) {
log_debug( _("Found . dentry in directory %lld (0x%llx)\n"),
10 years, 12 months