gfs2-utils: master - fsck.gfs2: Move function to read directory hash table to util.c
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=1bc68e62...
Commit: 1bc68e624966a4d73c98500452d69290cd15caa8
Parent: 3d2064e22dbc3e884c7c4e37ed5edc372494656c
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Feb 25 13:07:19 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 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.
---
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 02408e0..32afc46 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -639,24 +639,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 94d532e..5be260c 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -654,3 +654,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 1a4811c..7b587d4 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -185,6 +185,7 @@ extern char generic_interrupt(const char *caller, const char *where,
const char *answers);
extern char gfs2_getch(void);
extern uint64_t find_free_blk(struct gfs2_sbd *sdp);
+extern uint64_t *get_dir_hash(struct gfs2_inode *ip);
#define stack log_debug("<backtrace> - %s()\n", __func__)
11 years
gfs2-utils: master - fsck.gfs2: Special case '..' when processing bad formal inode number
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=3d2064e2...
Commit: 3d2064e22dbc3e884c7c4e37ed5edc372494656c
Parent: fbe26dc0d7dc0d2b369cffc20e3f306c44b1a2cc
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Jan 28 09:11:17 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 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.
---
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 01c78f3..00e226c 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -260,7 +260,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"));
11 years
gfs2-utils: master - fsck.gfs2: Add new function to check dir hash tables
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=fbe26dc0...
Commit: fbe26dc0d7dc0d2b369cffc20e3f306c44b1a2cc
Parent: 8c92d6111f9388ec48af7bd297cfd8fe87352499
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 12:27:40 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 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.
---
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 a40a6af..02408e0 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -714,6 +714,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. */
@@ -766,21 +784,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);
@@ -1687,3 +1690,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 9a34e97..cc69e84 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -60,8 +60,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,
@@ -92,7 +90,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 = {
@@ -204,145 +201,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 7c0c104..01c78f3 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -15,6 +15,7 @@
#include "eattr.h"
#include "metawalk.h"
#include "link.h"
+#include "lost_n_found.h"
#include "inode_hash.h"
#define MAX_FILENAME 256
@@ -295,6 +296,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
@@ -715,6 +721,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,
@@ -725,6 +1255,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 */
11 years
gfs2-utils: master - fsck.gfs2: Add formal inode check to basic dirent checks
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=8c92d611...
Commit: 8c92d6111f9388ec48af7bd297cfd8fe87352499
Parent: afed5d57db947587e26fb9f8d3d08eccb1e01556
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 12:15:33 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 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.
---
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 8d66ff4..7c0c104 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -315,6 +315,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 "
@@ -490,6 +491,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;
}
11 years
gfs2-utils: master - fsck.gfs2: Move basic directory entry checks to separate function
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=afed5d57...
Commit: afed5d57db947587e26fb9f8d3d08eccb1e01556
Parent: 824fe89669e6854b9c3d8ad94975f0801c118044
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 11:55:06 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 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.
---
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 3053711..8d66ff4 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -19,6 +19,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,
@@ -288,51 +295,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)++;
@@ -349,7 +340,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"),
@@ -371,7 +362,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);
@@ -380,7 +371,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
@@ -394,25 +385,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);
@@ -421,29 +412,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
@@ -462,20 +453,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;
}
@@ -483,22 +474,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"),
11 years
gfs2-utils: master - fsck.gfs2: shorten some debug messages in lost+found
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=824fe896...
Commit: 824fe89669e6854b9c3d8ad94975f0801c118044
Parent: 26255a8ce1999ede7d06663e482fe974b6faaa7d
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 10:29:38 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 2013 -0500
fsck.gfs2: shorten some debug messages in lost+found
This patch changes the debug output of lost+found such that it
only prints the block number in hexadecimal. This shortens the output
and makes debug output easier to read.
---
gfs2/fsck/lost_n_found.c | 12 ++++--------
1 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 3d9acb5..3226ac9 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -34,11 +34,9 @@ static void add_dotdot(struct gfs2_inode *ip)
if (di && valid_block(sdp, di->dotdot_parent.no_addr)) {
struct gfs2_inode *dip;
- log_debug(_("Directory %lld (0x%llx) already had a "
- "\"..\" link to %lld (0x%llx).\n"),
+ log_debug(_("Directory (0x%llx) already had a "
+ "\"..\" link to (0x%llx).\n"),
(unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)di->dotdot_parent.no_addr,
(unsigned long long)di->dotdot_parent.no_addr);
dip = fsck_load_inode(sdp, di->dotdot_parent.no_addr);
if (dip->i_di.di_num.no_formal_ino ==
@@ -76,15 +74,13 @@ static void add_dotdot(struct gfs2_inode *ip)
} else {
if (di)
log_debug(_("Couldn't find a valid \"..\" entry "
- "for orphan directory %lld (0x%llx): "
+ "for orphan directory (0x%llx): "
"'..' = 0x%llx\n"),
(unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)di->dotdot_parent.no_addr);
else
- log_debug(_("Couldn't find directory %lld (0x%llx) "
+ log_debug(_("Couldn't find directory (0x%llx) "
"in directory tree.\n"),
- (unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)ip->i_di.di_num.no_addr);
}
if (gfs2_dirent_del(ip, "..", 2))
11 years
gfs2-utils: master - fsck.gfs2: Check for formal inode mismatch when adding to lost+found
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=26255a8c...
Commit: 26255a8ce1999ede7d06663e482fe974b6faaa7d
Parent: b5eb47795a7cec433ba89c0717459eb7dd39a780
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Mar 6 10:28:20 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 2013 -0500
fsck.gfs2: Check for formal inode mismatch when adding to lost+found
This patch adds a check to the code that adds inodes to lost+found
so that dinodes with formal inode mismatches are logged, but not added.
---
gfs2/fsck/lost_n_found.c | 44 ++++++++++++++++++++++++++++----------------
1 files changed, 28 insertions(+), 16 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index f379646..3d9acb5 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -40,24 +40,36 @@ static void add_dotdot(struct gfs2_inode *ip)
(unsigned long long)ip->i_di.di_num.no_addr,
(unsigned long long)di->dotdot_parent.no_addr,
(unsigned long long)di->dotdot_parent.no_addr);
- decr_link_count(di->dotdot_parent.no_addr,
- ip->i_di.di_num.no_addr,
- _(".. unlinked, moving to lost+found"));
dip = fsck_load_inode(sdp, di->dotdot_parent.no_addr);
- if (dip->i_di.di_nlink > 0) {
- dip->i_di.di_nlink--;
- set_di_nlink(dip); /* keep inode tree in sync */
- log_debug(_("Decrementing its links to %d\n"),
- dip->i_di.di_nlink);
- bmodified(dip->i_bh);
- } else if (!dip->i_di.di_nlink) {
- log_debug(_("Its link count is zero.\n"));
+ if (dip->i_di.di_num.no_formal_ino ==
+ di->dotdot_parent.no_formal_ino) {
+ decr_link_count(di->dotdot_parent.no_addr,
+ ip->i_di.di_num.no_addr,
+ _(".. unlinked, moving to lost+found"));
+ if (dip->i_di.di_nlink > 0) {
+ dip->i_di.di_nlink--;
+ set_di_nlink(dip); /* keep inode tree in sync */
+ log_debug(_("Decrementing its links to %d\n"),
+ dip->i_di.di_nlink);
+ bmodified(dip->i_bh);
+ } else if (!dip->i_di.di_nlink) {
+ log_debug(_("Its link count is zero.\n"));
+ } else {
+ log_debug(_("Its link count is %d! Changing "
+ "it to 0.\n"), dip->i_di.di_nlink);
+ dip->i_di.di_nlink = 0;
+ set_di_nlink(dip); /* keep inode tree in sync */
+ bmodified(dip->i_bh);
+ }
} else {
- log_debug(_("Its link count is %d! Changing "
- "it to 0.\n"), dip->i_di.di_nlink);
- dip->i_di.di_nlink = 0;
- set_di_nlink(dip); /* keep inode tree in sync */
- bmodified(dip->i_bh);
+ log_debug(_("Directory (0x%llx)'s link to parent "
+ "(0x%llx) had a formal inode discrepancy: "
+ "was 0x%llx, expected 0x%llx\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)di->dotdot_parent.no_addr,
+ di->dotdot_parent.no_formal_ino,
+ dip->i_di.di_num.no_formal_ino);
+ log_debug(_("The parent directory was not changed.\n"));
}
fsck_inode_put(&dip);
di = NULL;
11 years
gfs2-utils: master - fsck.gfs2: Split out function to make sure lost+found exists
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=b5eb4779...
Commit: b5eb47795a7cec433ba89c0717459eb7dd39a780
Parent: 286f616cdc0c68cd30bc8a5c42a4cc8ed4a7fa28
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Mon Feb 25 10:18:55 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 2013 -0500
fsck.gfs2: Split out function to make sure lost+found exists
This patch extracts a section of code from the lost+found functions
and makes a new make_sure_lf_exists function that can be called
from more places.
---
gfs2/fsck/lost_n_found.c | 130 +++++++++++++++++++++++-----------------------
gfs2/fsck/lost_n_found.h | 1 +
2 files changed, 66 insertions(+), 65 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 1fb5076..f379646 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -88,6 +88,70 @@ static void add_dotdot(struct gfs2_inode *ip)
}
}
+void make_sure_lf_exists(struct gfs2_inode *ip)
+{
+ uint8_t q;
+ struct dir_info *di;
+ struct gfs2_sbd *sdp = ip->i_sbd;
+ uint32_t mode;
+
+ if (lf_dip)
+ return;
+
+ log_info( _("Locating/Creating lost+found directory\n"));
+
+ /* if this is gfs1, we have to trick createi into using
+ no_formal_ino = no_addr, so we set next_inum to the
+ free block we're about to allocate. */
+ if (sdp->gfs1)
+ sdp->md.next_inum = find_free_blk(sdp);
+ mode = (sdp->gfs1 ? DT2IF(GFS_FILE_DIR) : S_IFDIR) | 0700;
+ if (sdp->gfs1)
+ lf_dip = gfs_createi(sdp->md.rooti, "lost+found", mode, 0);
+ else
+ lf_dip = createi(sdp->md.rooti, "lost+found",
+ S_IFDIR | 0700, 0);
+ if (lf_dip == NULL) {
+ log_crit(_("Error creating lost+found: %s\n"),
+ strerror(errno));
+ exit(FSCK_ERROR);
+ }
+
+ /* createi will have incremented the di_nlink link count for the root
+ directory. We must set the nlink value in the hash table to keep
+ them in sync so that pass4 can detect and fix any descrepancies. */
+ set_di_nlink(sdp->md.rooti);
+
+ q = block_type(lf_dip->i_di.di_num.no_addr);
+ if (q != gfs2_inode_dir) {
+ /* This is a new lost+found directory, so set its block type
+ and increment link counts for the directories */
+ /* FIXME: i'd feel better about this if fs_mkdir returned
+ whether it created a new directory or just found an old one,
+ and we used that instead of the block_type to run this */
+ fsck_blockmap_set(ip, lf_dip->i_di.di_num.no_addr,
+ _("lost+found dinode"), gfs2_inode_dir);
+ dirtree_insert(lf_dip->i_di.di_num);
+ /* root inode links to lost+found */
+ incr_link_count(sdp->md.rooti->i_di.di_num, lf_dip, _("root"));
+ /* lost+found link for '.' from itself */
+ incr_link_count(lf_dip->i_di.di_num, lf_dip, "\".\"");
+ /* lost+found link for '..' back to root */
+ incr_link_count(lf_dip->i_di.di_num, sdp->md.rooti, "\"..\"");
+ if (sdp->gfs1)
+ lf_dip->i_di.__pad1 = GFS_FILE_DIR;
+ }
+ log_info( _("lost+found directory is dinode %lld (0x%llx)\n"),
+ (unsigned long long)lf_dip->i_di.di_num.no_addr,
+ (unsigned long long)lf_dip->i_di.di_num.no_addr);
+ di = dirtree_find(lf_dip->i_di.di_num.no_addr);
+ if (di) {
+ log_info( _("Marking lost+found inode connected\n"));
+ di->checked = 1;
+ di = NULL;
+ }
+}
+
/* add_inode_to_lf - Add dir entry to lost+found for the inode
* @ip: inode to add to lost + found
*
@@ -102,74 +166,10 @@ int add_inode_to_lf(struct gfs2_inode *ip){
__be32 inode_type;
uint64_t lf_blocks;
struct gfs2_sbd *sdp = ip->i_sbd;
- struct dir_info *di;
int err = 0;
uint32_t mode;
- if (!lf_dip) {
- uint8_t q;
-
- log_info( _("Locating/Creating lost+found directory\n"));
-
- /* if this is gfs1, we have to trick createi into using
- no_formal_ino = no_addr, so we set next_inum to the
- free block we're about to allocate. */
- if (sdp->gfs1)
- sdp->md.next_inum = find_free_blk(sdp);
- mode = (sdp->gfs1 ? DT2IF(GFS_FILE_DIR) : S_IFDIR) | 0700;
- if (sdp->gfs1)
- lf_dip = gfs_createi(sdp->md.rooti, "lost+found",
- mode, 0);
- else
- lf_dip = createi(sdp->md.rooti, "lost+found",
- S_IFDIR | 0700, 0);
- if (lf_dip == NULL) {
- log_crit(_("Error creating lost+found: %s\n"),
- strerror(errno));
- exit(FSCK_ERROR);
- }
-
- /* createi will have incremented the di_nlink link count for
- the root directory. We must set the nlink value
- in the hash table to keep them in sync so that pass4 can
- detect and fix any descrepancies. */
- set_di_nlink(sdp->md.rooti);
-
- q = block_type(lf_dip->i_di.di_num.no_addr);
- if (q != gfs2_inode_dir) {
- /* This is a new lost+found directory, so set its
- * block type and increment link counts for
- * the directories */
- /* FIXME: i'd feel better about this if
- * fs_mkdir returned whether it created a new
- * directory or just found an old one, and we
- * used that instead of the block_type to run
- * this */
- fsck_blockmap_set(ip, lf_dip->i_di.di_num.no_addr,
- _("lost+found dinode"),
- gfs2_inode_dir);
- /* root inode links to lost+found */
- incr_link_count(sdp->md.rooti->i_di.di_num,
- lf_dip, _("root"));
- /* lost+found link for '.' from itself */
- incr_link_count(lf_dip->i_di.di_num,
- lf_dip, "\".\"");
- /* lost+found link for '..' back to root */
- incr_link_count(lf_dip->i_di.di_num, sdp->md.rooti,
- "\"..\"");
- if (sdp->gfs1)
- lf_dip->i_di.__pad1 = GFS_FILE_DIR;
- }
- log_info( _("lost+found directory is dinode %lld (0x%llx)\n"),
- (unsigned long long)lf_dip->i_di.di_num.no_addr,
- (unsigned long long)lf_dip->i_di.di_num.no_addr);
- di = dirtree_find(lf_dip->i_di.di_num.no_addr);
- if (di) {
- log_info( _("Marking lost+found inode connected\n"));
- di->checked = 1;
- di = NULL;
- }
- }
+ make_sure_lf_exists(ip);
if (ip->i_di.di_num.no_addr == lf_dip->i_di.di_num.no_addr) {
log_err( _("Trying to add lost+found to itself...skipping"));
return 0;
diff --git a/gfs2/fsck/lost_n_found.h b/gfs2/fsck/lost_n_found.h
index f28a1d9..2b76cc2 100644
--- a/gfs2/fsck/lost_n_found.h
+++ b/gfs2/fsck/lost_n_found.h
@@ -4,5 +4,6 @@
#include "libgfs2.h"
int add_inode_to_lf(struct gfs2_inode *ip);
+void make_sure_lf_exists(struct gfs2_inode *ip);
#endif /* __LOST_N_FOUND_H__ */
11 years
gfs2-utils: master - fsck.gfs2: Move function find_free_blk to util.c
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=286f616c...
Commit: 286f616cdc0c68cd30bc8a5c42a4cc8ed4a7fa28
Parent: 12c7cb64437966e3d18b66789b94a0fbb96eea08
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Thu Feb 21 09:43:22 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 2013 -0500
fsck.gfs2: Move function find_free_blk to util.c
In a future patch to fsck, function find_free_blk will be called in
order to correctly report blocks that will need to be allocated for
things such as leaf splits. This patch moves function find_free_blk
to a more centralized place, util.c, to that end.
---
gfs2/fsck/lost_n_found.c | 39 ---------------------------------------
gfs2/fsck/util.c | 39 +++++++++++++++++++++++++++++++++++++++
gfs2/fsck/util.h | 2 ++
3 files changed, 41 insertions(+), 39 deletions(-)
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 570f3a8..1fb5076 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -88,45 +88,6 @@ static void add_dotdot(struct gfs2_inode *ip)
}
}
-static uint64_t find_free_blk(struct gfs2_sbd *sdp)
-{
- struct osi_node *n, *next = NULL;
- struct rgrp_tree *rl = NULL;
- struct gfs2_rindex *ri;
- struct gfs2_rgrp *rg;
- unsigned int block, bn = 0, x = 0, y = 0;
- unsigned int state;
- struct gfs2_buffer_head *bh;
-
- memset(&rg, 0, sizeof(rg));
- for (n = osi_first(&sdp->rgtree); n; n = next) {
- next = osi_next(n);
- rl = (struct rgrp_tree *)n;
- if (rl->rg.rg_free)
- break;
- }
-
- if (n == NULL)
- return 0;
-
- ri = &rl->ri;
- rg = &rl->rg;
-
- for (block = 0; block < ri->ri_length; block++) {
- bh = rl->bh[block];
- x = (block) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
-
- for (; x < sdp->bsize; x++)
- for (y = 0; y < GFS2_NBBY; y++) {
- state = (bh->b_data[x] >> (GFS2_BIT_SIZE * y)) & 0x03;
- if (state == GFS2_BLKST_FREE)
- return ri->ri_data0 + bn;
- bn++;
- }
- }
- return 0;
-}
-
/* add_inode_to_lf - Add dir entry to lost+found for the inode
* @ip: inode to add to lost + found
*
diff --git a/gfs2/fsck/util.c b/gfs2/fsck/util.c
index 7c89155..94d532e 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -615,3 +615,42 @@ bad_dinode:
stack;
return -EPERM;
}
+
+uint64_t find_free_blk(struct gfs2_sbd *sdp)
+{
+ struct osi_node *n, *next = NULL;
+ struct rgrp_tree *rl = NULL;
+ struct gfs2_rindex *ri;
+ struct gfs2_rgrp *rg;
+ unsigned int block, bn = 0, x = 0, y = 0;
+ unsigned int state;
+ struct gfs2_buffer_head *bh;
+
+ memset(&rg, 0, sizeof(rg));
+ for (n = osi_first(&sdp->rgtree); n; n = next) {
+ next = osi_next(n);
+ rl = (struct rgrp_tree *)n;
+ if (rl->rg.rg_free)
+ break;
+ }
+
+ if (n == NULL)
+ return 0;
+
+ ri = &rl->ri;
+ rg = &rl->rg;
+
+ for (block = 0; block < ri->ri_length; block++) {
+ bh = rl->bh[block];
+ x = (block) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
+
+ for (; x < sdp->bsize; x++)
+ for (y = 0; y < GFS2_NBBY; y++) {
+ state = (bh->b_data[x] >> (GFS2_BIT_SIZE * y)) & 0x03;
+ if (state == GFS2_BLKST_FREE)
+ return ri->ri_data0 + bn;
+ bn++;
+ }
+ }
+ return 0;
+}
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
index 80ed0c4..1a4811c 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -184,6 +184,8 @@ extern char generic_interrupt(const char *caller, const char *where,
const char *progress, const char *question,
const char *answers);
extern char gfs2_getch(void);
+extern uint64_t find_free_blk(struct gfs2_sbd *sdp);
+
#define stack log_debug("<backtrace> - %s()\n", __func__)
#endif /* __UTIL_H__ */
11 years
gfs2-utils: master - libgfs2: let dir_split_leaf receive a "broken" lindex
by Bob Peterson
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=12c7cb64...
Commit: 12c7cb64437966e3d18b66789b94a0fbb96eea08
Parent: c3058a31dce54f3b2fc4d07a4fac0b9add885c08
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Thu Feb 21 09:36:01 2013 -0700
Committer: Bob Peterson <rpeterso(a)redhat.com>
CommitterDate: Mon May 20 11:12:45 2013 -0500
libgfs2: let dir_split_leaf receive a "broken" lindex
For ordinary leaf blocks, the hash table must follow the rules,
which means it needs to follow a power-of-two boundary. In other
words, it needs to enforce that: start = (lindex & ~(len - 1));
But when doing repairs, fsck will need to detect when hash tables
violate this rule and fix it. In that case, it may need to pass
in an invalid starting offset for a leaf to split. This patch
moves the responsibility for checking the starting block to the
calling function.
---
gfs2/libgfs2/fs_ops.c | 13 +++++++------
gfs2/libgfs2/libgfs2.h | 2 +-
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
index 89adf32..d009e2f 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -925,13 +925,13 @@ void gfs2_put_leaf_nr(struct gfs2_inode *dip, uint32_t inx, uint64_t leaf_out)
}
}
-void dir_split_leaf(struct gfs2_inode *dip, uint32_t lindex, uint64_t leaf_no,
+void dir_split_leaf(struct gfs2_inode *dip, uint32_t start, uint64_t leaf_no,
struct gfs2_buffer_head *obh)
{
struct gfs2_buffer_head *nbh;
struct gfs2_leaf *nleaf, *oleaf;
struct gfs2_dirent *dent, *prev = NULL, *next = NULL, *new;
- uint32_t start, len, half_len, divider;
+ uint32_t len, half_len, divider;
uint64_t bn, *lp;
uint32_t name_len;
int x, moved = FALSE;
@@ -957,8 +957,6 @@ void dir_split_leaf(struct gfs2_inode *dip, uint32_t lindex, uint64_t leaf_no,
len = 1 << (dip->i_di.di_depth - be16_to_cpu(oleaf->lf_depth));
half_len = len >> 1;
- start = (lindex & ~(len - 1));
-
lp = calloc(1, half_len * sizeof(uint64_t));
if (lp == NULL) {
fprintf(stderr, "Out of memory in %s\n", __FUNCTION__);
@@ -1160,7 +1158,7 @@ static int dir_e_add(struct gfs2_inode *dip, const char *filename, int len,
struct gfs2_buffer_head *bh, *nbh;
struct gfs2_leaf *leaf, *nleaf;
struct gfs2_dirent *dent;
- uint32_t lindex;
+ uint32_t lindex, llen;
uint32_t hash;
uint64_t leaf_no, bn;
int err = 0;
@@ -1182,7 +1180,10 @@ restart:
if (dirent_alloc(dip, bh, len, &dent)) {
if (be16_to_cpu(leaf->lf_depth) < dip->i_di.di_depth) {
- dir_split_leaf(dip, lindex, leaf_no, bh);
+ llen = 1 << (dip->i_di.di_depth -
+ be16_to_cpu(leaf->lf_depth));
+ dir_split_leaf(dip, lindex & ~(llen - 1),
+ leaf_no, bh);
brelse(bh);
goto restart;
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 6e99b94..0d2f87f 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -469,7 +469,7 @@ extern void block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
extern void gfs2_get_leaf_nr(struct gfs2_inode *dip, uint32_t index,
uint64_t *leaf_out);
extern void gfs2_put_leaf_nr(struct gfs2_inode *dip, uint32_t inx, uint64_t leaf_out);
-extern void dir_split_leaf(struct gfs2_inode *dip, uint32_t lindex,
+extern void dir_split_leaf(struct gfs2_inode *dip, uint32_t start,
uint64_t leaf_no, struct gfs2_buffer_head *obh);
extern void gfs2_free_block(struct gfs2_sbd *sdp, uint64_t block);
extern int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t block);
11 years