commit e6c114baac5102d0ac8cc542565d56657593e3c5
Author: Bob Peterson <rpeterso@redhat.com>
Date: Tue Sep 3 11:21:00 2013 -0500
fsck.gfs2: Check and repair per_node contents such as quota_changeX
This patch gives fsck.gfs2 the ability to check the system files that
are in the per_node directory: All the inum_rangeX, statfs_changeX
and quota_changeX files. If they're found to be corrupt, they are
deleted and rebuilt.
rhbz#1003059
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 727cc18..26f7d48 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1595,6 +1595,116 @@ struct metawalk_fxns pass2_fxns = {
.repair_leaf = pass2_repair_leaf,
};
+static int check_metalist_qc(struct gfs2_inode *ip, uint64_t block,
+ struct gfs2_buffer_head **bh, int h,
+ int *is_valid, int *was_duplicate, void *private)
+{
+ *was_duplicate = 0;
+ *is_valid = 1;
+ *bh = bread(ip->i_sbd, block);
+ return meta_is_good;
+}
+
+static int check_data_qc(struct gfs2_inode *ip, uint64_t metablock,
+ uint64_t block, void *private)
+{
+ struct gfs2_buffer_head *bh;
+
+ /* At this point, basic data block checks have already been done,
+ so we only need to make sure they're QC blocks. */
+ if (!valid_block(ip->i_sbd, block))
+ return -1;
+
+ bh = bread(ip->i_sbd, block);
+ if (gfs2_check_meta(bh, GFS2_METATYPE_QC) != 0) {
+ log_crit(_("Error: quota_change block at %lld (0x%llx) is "
+ "the wrong metadata type.\n"),
+ (unsigned long long)block, (unsigned long long)block);
+ brelse(bh);
+ return -1;
+ }
+ brelse(bh);
+ return 0;
+}
+
+struct metawalk_fxns quota_change_fxns = {
+ .check_metalist = check_metalist_qc,
+ .check_data = check_data_qc,
+};
+
+/* check_pernode_for - verify a file within the system per_node directory
+ * @x - index number X
+ * @per_node - pointer to the per_node inode
+ * @fn - system file name
+ * @filelen - the file length the system file needs to be
+ * @multiple - the file length must be a multiple (versus the exact value)
+ * @pass - a metawalk function for checking the data blocks (if any)
+ * @builder - a rebuild function for the file
+ *
+ * Returns: 0 if all went well, else error. */
+static int check_pernode_for(int x, struct gfs2_inode *pernode, const char *fn,
+ unsigned long long filelen, int multiple,
+ struct metawalk_fxns *pass,
+ int builder(struct gfs2_inode *per_node,
+ unsigned int j))
+{
+ struct gfs2_inode *ip;
+ int error, valid_size = 1;
+
+ log_debug(_("Checking system file %s\n"), fn);
+ error = gfs2_lookupi(pernode, fn, strlen(fn), &ip);
+ if (error) {
+ log_err(_("System file %s is missing.\n"), fn);
+ if (!query( _("Rebuild the system file? (y/n) ")))
+ return 0;
+ goto build_it;
+ }
+ if (!ip->i_di.di_size)
+ valid_size = 0;
+ else if (!multiple && ip->i_di.di_size != filelen)
+ valid_size = 0;
+ else if (multiple && (ip->i_di.di_size % filelen))
+ valid_size = 0;
+ if (!valid_size) {
+ log_err(_("System file %s has an invalid size. Is %llu, "
+ "should be %llu.\n"), fn, ip->i_di.di_size, filelen);
+ if (!query( _("Rebuild the system file? (y/n) ")))
+ goto out_good;
+ fsck_inode_put(&ip);
+ goto build_it;
+ }
+ if (pass) {
+ error = check_metatree(ip, pass);
+ if (!error)
+ goto out_good;
+ log_err(_("System file %s has bad contents.\n"), fn);
+ if (!query( _("Delete and rebuild the system file? (y/n) ")))
+ goto out_good;
+ check_metatree(ip, &pass2_fxns_delete);
+ fsck_inode_put(&ip);
+ gfs2_dirent_del(pernode, fn, strlen(fn));
+ goto build_it;
+ }
+out_good:
+ fsck_inode_put(&ip);
+ return 0;
+
+build_it:
+ if (builder(pernode, x)) {
+ log_err(_("Error building %s\n"), fn);
+ return -1;
+ }
+ error = gfs2_lookupi(pernode, fn, strlen(fn), &ip);
+ if (error) {
+ log_err(_("Error rebuilding %s.\n"), fn);
+ return -1;
+ }
+ fsck_blockmap_set(ip, ip->i_di.di_num.no_addr, fn, gfs2_inode_file);
+ reprocess_inode(ip, fn);
+ log_err(_("System file %s rebuilt.\n"), fn);
+ goto out_good;
+}
+
/* Check system directory inode */
/* Should work for all system directories: root, master, jindex, per_node */
static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
@@ -1707,7 +1817,26 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
sysinode->i_di.di_num.no_addr);
}
}
- return 0;
+ error = 0;
+ if (sysinode == sysinode->i_sbd->md.pinode) {
+ int j;
+ char fn[64];
+
+ /* Make sure all the per_node files are there, and valid */
+ for (j = 0; j < sysinode->i_sbd->md.journals; j++) {
+ sprintf(fn, "inum_range%d", j);
+ error += check_pernode_for(j, sysinode, fn, 16, 0,
+ NULL, build_inum_range);
+ sprintf(fn, "statfs_change%d", j);
+ error += check_pernode_for(j, sysinode, fn, 24, 0,
+ NULL, build_statfs_change);
+ sprintf(fn, "quota_change%d", j);
+ error += check_pernode_for(j, sysinode, fn, 1048576, 1,
+ "a_change_fxns,
+ build_quota_change);
+ }
+ }
+ return error;
}
/**
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 1548cf3..f864a08 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -770,6 +770,10 @@ extern int do_init_statfs(struct gfs2_sbd *sdp);
extern int gfs2_check_meta(struct gfs2_buffer_head *bh, int type);
extern unsigned lgfs2_bm_scan(struct rgrp_tree *rgd, unsigned idx,
uint64_t *buf, uint8_t state);
+extern int build_inum_range(struct gfs2_inode *per_node, unsigned int j);
+extern int build_statfs_change(struct gfs2_inode *per_node, unsigned int j);
+extern int build_quota_change(struct gfs2_inode *per_node, unsigned int j);
+
/* super.c */
extern int check_sb(struct gfs2_sb *sb);
extern int read_sb(struct gfs2_sbd *sdp);
diff --git a/gfs2/libgfs2/structures.c b/gfs2/libgfs2/structures.c
index 2a8c6f7..e888f1e 100644
--- a/gfs2/libgfs2/structures.c
+++ b/gfs2/libgfs2/structures.c
@@ -180,7 +180,7 @@ int build_jindex(struct gfs2_sbd *sdp)
return 0;
}
-static int build_inum_range(struct gfs2_inode *per_node, unsigned int j)
+int build_inum_range(struct gfs2_inode *per_node, unsigned int j)
{
struct gfs2_sbd *sdp = per_node->i_sbd;
char name[256];
@@ -204,7 +204,7 @@ static int build_inum_range(struct gfs2_inode *per_node, unsigned int j)
return 0;
}
-static int build_statfs_change(struct gfs2_inode *per_node, unsigned int j)
+int build_statfs_change(struct gfs2_inode *per_node, unsigned int j)
{
struct gfs2_sbd *sdp = per_node->i_sbd;
char name[256];
@@ -228,7 +228,7 @@ static int build_statfs_change(struct gfs2_inode *per_node, unsigned int j)
return 0;
}
-static int build_quota_change(struct gfs2_inode *per_node, unsigned int j)
+int build_quota_change(struct gfs2_inode *per_node, unsigned int j)
{
struct gfs2_sbd *sdp = per_node->i_sbd;
struct gfs2_meta_header mh;
diff --git a/gfs2/libgfs2/super.c b/gfs2/libgfs2/super.c
index eb97c40..f87734a 100644
--- a/gfs2/libgfs2/super.c
+++ b/gfs2/libgfs2/super.c
@@ -119,6 +119,7 @@ int read_sb(struct gfs2_sbd *sdp)
sdp->sb_addr = GFS2_SB_ADDR * GFS2_BASIC_BLOCK / sdp->bsize;
sdp->sd_blocks_per_bitmap = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header))
* GFS2_NBBY;
+ sdp->qcsize = GFS2_DEFAULT_QCSIZE;
return 0;
}