From 21ab754b6d1b4de06efa61d7e9dad9f8b011964a Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 04 2020 09:57:38 +0000 Subject: Apply patch bz1659490-gfs2_utils_Wrong_hash_value_used_to_clean_journals.patch patch_name: bz1659490-gfs2_utils_Wrong_hash_value_used_to_clean_journals.patch present_in_specfile: true --- diff --git a/gfs2/libgfs2/recovery.c b/gfs2/libgfs2/recovery.c index 6b14bf9..06f8111 100644 --- a/gfs2/libgfs2/recovery.c +++ b/gfs2/libgfs2/recovery.c @@ -241,7 +241,7 @@ int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head) lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1); lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT); lh->lh_blkno = cpu_to_be32(lblock); - hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header)); + hash = lgfs2_log_header_hash(bh->b_data); lh->lh_hash = cpu_to_be32(hash); bmodified(bh); brelse(bh); diff --git a/gfs2/libgfs2/recovery.c.bz1659490-gfs2_utils_Wrong_hash_value_used_to_clean_journals b/gfs2/libgfs2/recovery.c.bz1659490-gfs2_utils_Wrong_hash_value_used_to_clean_journals new file mode 100644 index 0000000..6b14bf9 --- /dev/null +++ b/gfs2/libgfs2/recovery.c.bz1659490-gfs2_utils_Wrong_hash_value_used_to_clean_journals @@ -0,0 +1,251 @@ +#include "clusterautoconfig.h" + +/* + * NOTE: + * + * This code was pilfered from the gfs2 kernel and adapted to userland. + * If you change this part, you should evaluate whether the upstream kernel + * version of recovery.c should be changed as well. Likewise, if the + * upstream version changes, this part should be kept in sync. + * + */ + +#include +#include +#include "libgfs2.h" + +void gfs2_replay_incr_blk(struct gfs2_inode *ip, unsigned int *blk) +{ + uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize; + + if (++*blk == jd_blocks) + *blk = 0; +} + +int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk, + struct gfs2_buffer_head **bh) +{ + int new = 0; + uint64_t dblock; + + block_map(ip, blk, &new, &dblock, NULL, FALSE); + if (!dblock) + return -EIO; + + *bh = bread(ip->i_sbd, dblock); + return 0; +} + +/** + * get_log_header - read the log header for a given segment + * @ip: the journal incore inode + * @blk: the block to look at + * @lh: the log header to return + * + * Read the log header for a given segement in a given journal. Do a few + * sanity checks on it. + * + * Returns: 0 on success, + * 1 if the header was invalid or incomplete, + * errno on error + */ + +int get_log_header(struct gfs2_inode *ip, unsigned int blk, + struct gfs2_log_header *head) +{ + struct gfs2_buffer_head *bh; + struct gfs2_log_header lh, *tmp; + uint32_t hash, saved_hash; + uint32_t lh_crc = 0; + uint32_t crc; + int error; + + error = gfs2_replay_read_block(ip, blk, &bh); + if (error) + return error; + + tmp = (struct gfs2_log_header *)bh->b_data; + saved_hash = tmp->lh_hash; + tmp->lh_hash = 0; + hash = lgfs2_log_header_hash(bh->b_data); + tmp->lh_hash = saved_hash; + crc = lgfs2_log_header_crc(bh->b_data, ip->i_sbd->bsize); + gfs2_log_header_in(&lh, bh->b_data); + brelse(bh); +#ifdef GFS2_HAS_LH_V2 + lh_crc = lh.lh_crc; +#endif + if (error || lh.lh_blkno != blk || lh.lh_hash != hash) + return 1; + /* Don't check the crc if it's zero, as it is in pre-v2 log headers */ + if (lh_crc != 0 && lh_crc != crc) + return 1; + + *head = lh; + + return 0; +} + +/** + * find_good_lh - find a good log header + * @ip: the journal incore inode + * @blk: the segment to start searching from + * @lh: the log header to fill in + * @forward: if true search forward in the log, else search backward + * + * Call get_log_header() to get a log header for a segment, but if the + * segment is bad, either scan forward or backward until we find a good one. + * + * Returns: errno + */ +static int find_good_lh(struct gfs2_inode *ip, unsigned int *blk, struct gfs2_log_header *head) +{ + unsigned int orig_blk = *blk; + int error; + uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize; + + for (;;) { + error = get_log_header(ip, *blk, head); + if (error <= 0) + return error; + + if (++*blk == jd_blocks) + *blk = 0; + + if (*blk == orig_blk) + return -EIO; + } +} + +/** + * jhead_scan - make sure we've found the head of the log + * @jd: the journal + * @head: this is filled in with the log descriptor of the head + * + * At this point, seg and lh should be either the head of the log or just + * before. Scan forward until we find the head. + * + * Returns: errno + */ + +static int jhead_scan(struct gfs2_inode *ip, struct gfs2_log_header *head) +{ + unsigned int blk = head->lh_blkno; + uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize; + struct gfs2_log_header lh; + int error; + + for (;;) { + if (++blk == jd_blocks) + blk = 0; + + error = get_log_header(ip, blk, &lh); + if (error < 0) + return error; + if (error == 1) + continue; + + if (lh.lh_sequence == head->lh_sequence) + return -EIO; + if (lh.lh_sequence < head->lh_sequence) + break; + + *head = lh; + } + + return 0; +} + +/** + * gfs2_find_jhead - find the head of a log + * @jd: the journal + * @head: the log descriptor for the head of the log is returned here + * + * Do a binary search of a journal and find the valid log entry with the + * highest sequence number. (i.e. the log head) + * + * Returns: errno + */ + +int gfs2_find_jhead(struct gfs2_inode *ip, struct gfs2_log_header *head) +{ + struct gfs2_log_header lh_1, lh_m; + uint32_t blk_1, blk_2, blk_m; + uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize; + int error; + + blk_1 = 0; + blk_2 = jd_blocks - 1; + + for (;;) { + blk_m = (blk_1 + blk_2) / 2; + + error = find_good_lh(ip, &blk_1, &lh_1); + if (error) + return error; + + error = find_good_lh(ip, &blk_m, &lh_m); + if (error) + return error; + + if (blk_1 == blk_m || blk_m == blk_2) + break; + + if (lh_1.lh_sequence <= lh_m.lh_sequence) + blk_1 = blk_m; + else + blk_2 = blk_m; + } + + error = jhead_scan(ip, &lh_1); + if (error) + return error; + + *head = lh_1; + + return error; +} + +/** + * clean_journal - mark a dirty journal as being clean + * @sdp: the filesystem + * @jd: the journal + * @head: the head journal to start from + * + * Returns: errno + */ + +int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head) +{ + unsigned int lblock; + struct gfs2_log_header *lh; + uint32_t hash; + struct gfs2_buffer_head *bh; + int new = 0; + uint64_t dblock; + + lblock = head->lh_blkno; + gfs2_replay_incr_blk(ip, &lblock); + block_map(ip, lblock, &new, &dblock, NULL, 0); + if (!dblock) + return -EIO; + + bh = bread(ip->i_sbd, dblock); + memset(bh->b_data, 0, ip->i_sbd->bsize); + + lh = (struct gfs2_log_header *)bh->b_data; + memset(lh, 0, sizeof(struct gfs2_log_header)); + lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC); + lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH); + lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH); + lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1); + lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT); + lh->lh_blkno = cpu_to_be32(lblock); + hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header)); + lh->lh_hash = cpu_to_be32(hash); + bmodified(bh); + brelse(bh); + + return 0; +} +