xref: /freebsd/sys/fs/ext2fs/ext2_csum.c (revision 512f29d141ca70969c6b5a367a955fb9cef169b5)
1d23db91eSPedro F. Giffuni /*-
27abc09cdSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37abc09cdSPedro F. Giffuni  *
4d23db91eSPedro F. Giffuni  * Copyright (c) 2017, Fedor Uporov
5d23db91eSPedro F. Giffuni  * All rights reserved.
6d23db91eSPedro F. Giffuni  *
7d23db91eSPedro F. Giffuni  * Redistribution and use in source and binary forms, with or without
8d23db91eSPedro F. Giffuni  * modification, are permitted provided that the following conditions
9d23db91eSPedro F. Giffuni  * are met:
10d23db91eSPedro F. Giffuni  * 1. Redistributions of source code must retain the above copyright
11d23db91eSPedro F. Giffuni  *    notice, this list of conditions and the following disclaimer.
12d23db91eSPedro F. Giffuni  * 2. Redistributions in binary form must reproduce the above copyright
13d23db91eSPedro F. Giffuni  *    notice, this list of conditions and the following disclaimer in the
14d23db91eSPedro F. Giffuni  *    documentation and/or other materials provided with the distribution.
15d23db91eSPedro F. Giffuni  *
16d23db91eSPedro F. Giffuni  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17d23db91eSPedro F. Giffuni  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d23db91eSPedro F. Giffuni  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d23db91eSPedro F. Giffuni  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20d23db91eSPedro F. Giffuni  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d23db91eSPedro F. Giffuni  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d23db91eSPedro F. Giffuni  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d23db91eSPedro F. Giffuni  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d23db91eSPedro F. Giffuni  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d23db91eSPedro F. Giffuni  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d23db91eSPedro F. Giffuni  * SUCH DAMAGE.
27d23db91eSPedro F. Giffuni  *
28d23db91eSPedro F. Giffuni  * $FreeBSD$
29d23db91eSPedro F. Giffuni  */
30d23db91eSPedro F. Giffuni 
31d23db91eSPedro F. Giffuni #include <sys/param.h>
32d23db91eSPedro F. Giffuni #include <sys/systm.h>
33d23db91eSPedro F. Giffuni #include <sys/types.h>
34d23db91eSPedro F. Giffuni #include <sys/stat.h>
35d23db91eSPedro F. Giffuni #include <sys/kernel.h>
36d23db91eSPedro F. Giffuni #include <sys/malloc.h>
37d23db91eSPedro F. Giffuni #include <sys/vnode.h>
38d23db91eSPedro F. Giffuni #include <sys/bio.h>
39d23db91eSPedro F. Giffuni #include <sys/buf.h>
40d23db91eSPedro F. Giffuni #include <sys/endian.h>
41d23db91eSPedro F. Giffuni #include <sys/conf.h>
42d23db91eSPedro F. Giffuni #include <sys/mount.h>
43d23db91eSPedro F. Giffuni 
44d23db91eSPedro F. Giffuni #include <fs/ext2fs/fs.h>
45d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2fs.h>
46*512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dinode.h>
47d23db91eSPedro F. Giffuni #include <fs/ext2fs/inode.h>
48*512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dir.h>
49*512f29d1SFedor Uporov #include <fs/ext2fs/htree.h>
50*512f29d1SFedor Uporov #include <fs/ext2fs/ext2_extattr.h>
51d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h>
52d23db91eSPedro F. Giffuni 
53*512f29d1SFedor Uporov #define EXT2_BG_INODE_BITMAP_CSUM_HI_END	\
54*512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \
55*512f29d1SFedor Uporov 	 sizeof(uint16_t))
56*512f29d1SFedor Uporov 
57*512f29d1SFedor Uporov #define EXT2_INODE_CSUM_HI_EXTRA_END	\
58*512f29d1SFedor Uporov 	(offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \
59*512f29d1SFedor Uporov 	 E2FS_REV0_INODE_SIZE)
60*512f29d1SFedor Uporov 
61*512f29d1SFedor Uporov #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION	\
62*512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \
63*512f29d1SFedor Uporov 	 sizeof(uint16_t))
64*512f29d1SFedor Uporov 
65*512f29d1SFedor Uporov void
66*512f29d1SFedor Uporov ext2_sb_csum_set_seed(struct m_ext2fs *fs)
67*512f29d1SFedor Uporov {
68*512f29d1SFedor Uporov 
69*512f29d1SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED))
70*512f29d1SFedor Uporov 		fs->e2fs_csum_seed = fs->e2fs->e4fs_chksum_seed;
71*512f29d1SFedor Uporov 	else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
72*512f29d1SFedor Uporov 		fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid,
73*512f29d1SFedor Uporov 		    sizeof(fs->e2fs->e2fs_uuid));
74*512f29d1SFedor Uporov 	}
75*512f29d1SFedor Uporov 	else
76*512f29d1SFedor Uporov 		fs->e2fs_csum_seed = 0;
77*512f29d1SFedor Uporov }
78*512f29d1SFedor Uporov 
79*512f29d1SFedor Uporov int
80*512f29d1SFedor Uporov ext2_sb_csum_verify(struct m_ext2fs *fs)
81*512f29d1SFedor Uporov {
82*512f29d1SFedor Uporov 
83*512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) {
84*512f29d1SFedor Uporov 		printf(
85*512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt);
86*512f29d1SFedor Uporov 		return (EINVAL);
87*512f29d1SFedor Uporov 	}
88*512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_sbchksum !=
89*512f29d1SFedor Uporov 	    calculate_crc32c(~0, (const char *)fs->e2fs,
90*512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum))) {
91*512f29d1SFedor Uporov 		printf(
92*512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n",
93*512f29d1SFedor Uporov 		    fs->e2fs_fsmnt, fs->e2fs->e4fs_sbchksum, calculate_crc32c(~0,
94*512f29d1SFedor Uporov 		    (const char *)fs->e2fs, offsetof(struct ext2fs, e4fs_sbchksum)));
95*512f29d1SFedor Uporov 		return (EINVAL);
96*512f29d1SFedor Uporov 	}
97*512f29d1SFedor Uporov 
98*512f29d1SFedor Uporov 	return (0);
99*512f29d1SFedor Uporov }
100*512f29d1SFedor Uporov 
101*512f29d1SFedor Uporov void
102*512f29d1SFedor Uporov ext2_sb_csum_set(struct m_ext2fs *fs)
103*512f29d1SFedor Uporov {
104*512f29d1SFedor Uporov 
105*512f29d1SFedor Uporov 	fs->e2fs->e4fs_sbchksum = calculate_crc32c(~0, (const char *)fs->e2fs,
106*512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum));
107*512f29d1SFedor Uporov }
108*512f29d1SFedor Uporov 
109*512f29d1SFedor Uporov static uint32_t
110*512f29d1SFedor Uporov ext2_extattr_blk_csum(struct inode *ip, uint64_t facl,
111*512f29d1SFedor Uporov     struct ext2fs_extattr_header *header)
112*512f29d1SFedor Uporov {
113*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
114*512f29d1SFedor Uporov 	uint32_t crc, old_crc;
115*512f29d1SFedor Uporov 
116*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
117*512f29d1SFedor Uporov 
118*512f29d1SFedor Uporov 	old_crc = header->h_checksum;
119*512f29d1SFedor Uporov 
120*512f29d1SFedor Uporov 	header->h_checksum = 0;
121*512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl, sizeof(facl));
122*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)header, fs->e2fs_bsize);
123*512f29d1SFedor Uporov 	header->h_checksum = old_crc;
124*512f29d1SFedor Uporov 
125*512f29d1SFedor Uporov 	return (crc);
126*512f29d1SFedor Uporov }
127*512f29d1SFedor Uporov 
128*512f29d1SFedor Uporov int
129*512f29d1SFedor Uporov ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp)
130*512f29d1SFedor Uporov {
131*512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
132*512f29d1SFedor Uporov 
133*512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
134*512f29d1SFedor Uporov 
135*512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) &&
136*512f29d1SFedor Uporov 	    (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) {
137*512f29d1SFedor Uporov 		printf("WARNING: bad extattr csum detected, ip=%lu - run fsck\n",
138*512f29d1SFedor Uporov 		    (unsigned long)ip->i_number);
139*512f29d1SFedor Uporov 		return (EIO);
140*512f29d1SFedor Uporov 	}
141*512f29d1SFedor Uporov 
142*512f29d1SFedor Uporov 	return (0);
143*512f29d1SFedor Uporov }
144*512f29d1SFedor Uporov 
145*512f29d1SFedor Uporov void
146*512f29d1SFedor Uporov ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp)
147*512f29d1SFedor Uporov {
148*512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
149*512f29d1SFedor Uporov 
150*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
151*512f29d1SFedor Uporov 		return;
152*512f29d1SFedor Uporov 
153*512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
154*512f29d1SFedor Uporov 	header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header);
155*512f29d1SFedor Uporov }
156*512f29d1SFedor Uporov 
157*512f29d1SFedor Uporov static struct ext2fs_direct_tail *
158*512f29d1SFedor Uporov ext2_get_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
159*512f29d1SFedor Uporov {
160*512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
161*512f29d1SFedor Uporov 
162*512f29d1SFedor Uporov 	tp = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
163*512f29d1SFedor Uporov 	if (tp->e2dt_reserved_zero1 ||
164*512f29d1SFedor Uporov 	    tp->e2dt_rec_len != sizeof(struct ext2fs_direct_tail) ||
165*512f29d1SFedor Uporov 	    tp->e2dt_reserved_zero2 ||
166*512f29d1SFedor Uporov 	    tp->e2dt_reserved_ft != EXT2_FT_DIR_CSUM)
167*512f29d1SFedor Uporov 		return (NULL);
168*512f29d1SFedor Uporov 
169*512f29d1SFedor Uporov 	return (tp);
170*512f29d1SFedor Uporov }
171*512f29d1SFedor Uporov 
172*512f29d1SFedor Uporov static uint32_t
173*512f29d1SFedor Uporov ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
174*512f29d1SFedor Uporov {
175*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
176*512f29d1SFedor Uporov 	char *buf;
177*512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
178*512f29d1SFedor Uporov 
179*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
180*512f29d1SFedor Uporov 
181*512f29d1SFedor Uporov 	buf = (char *)ep;
182*512f29d1SFedor Uporov 
183*512f29d1SFedor Uporov 	inum = ip->i_number;
184*512f29d1SFedor Uporov 	gen = ip->i_gen;
185*512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
186*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
187*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
188*512f29d1SFedor Uporov 
189*512f29d1SFedor Uporov 	return (crc);
190*512f29d1SFedor Uporov }
191*512f29d1SFedor Uporov 
192*512f29d1SFedor Uporov static int
193*512f29d1SFedor Uporov ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
194*512f29d1SFedor Uporov {
195*512f29d1SFedor Uporov 	uint32_t calculated;
196*512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
197*512f29d1SFedor Uporov 
198*512f29d1SFedor Uporov 	tp = ext2_get_dirent_tail(ip, ep);
199*512f29d1SFedor Uporov 	if (tp == NULL)
200*512f29d1SFedor Uporov 		return (0);
201*512f29d1SFedor Uporov 
202*512f29d1SFedor Uporov 	calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
203*512f29d1SFedor Uporov 	if (calculated != tp->e2dt_checksum)
204*512f29d1SFedor Uporov 		return (EIO);
205*512f29d1SFedor Uporov 
206*512f29d1SFedor Uporov 	return (0);
207*512f29d1SFedor Uporov }
208*512f29d1SFedor Uporov 
209*512f29d1SFedor Uporov static struct ext2fs_htree_count *
210*512f29d1SFedor Uporov ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset)
211*512f29d1SFedor Uporov {
212*512f29d1SFedor Uporov 	struct ext2fs_direct_2 *dp;
213*512f29d1SFedor Uporov 	struct ext2fs_htree_root_info *root;
214*512f29d1SFedor Uporov 	int count_offset;
215*512f29d1SFedor Uporov 
216*512f29d1SFedor Uporov 	if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs))
217*512f29d1SFedor Uporov 		count_offset = 8;
218*512f29d1SFedor Uporov 	else if (ep->e2d_reclen == 12) {
219*512f29d1SFedor Uporov 		dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12);
220*512f29d1SFedor Uporov 		if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12)
221*512f29d1SFedor Uporov 			return (NULL);
222*512f29d1SFedor Uporov 
223*512f29d1SFedor Uporov 		root = (struct ext2fs_htree_root_info *)(((char *)dp + 12));
224*512f29d1SFedor Uporov 		if (root->h_reserved1 ||
225*512f29d1SFedor Uporov 		    root->h_info_len != sizeof(struct ext2fs_htree_root_info))
226*512f29d1SFedor Uporov 			return (NULL);
227*512f29d1SFedor Uporov 
228*512f29d1SFedor Uporov 		count_offset = 32;
229*512f29d1SFedor Uporov 	} else
230*512f29d1SFedor Uporov 		return (NULL);
231*512f29d1SFedor Uporov 
232*512f29d1SFedor Uporov 	if (offset)
233*512f29d1SFedor Uporov 		*offset = count_offset;
234*512f29d1SFedor Uporov 
235*512f29d1SFedor Uporov 	return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset));
236*512f29d1SFedor Uporov }
237*512f29d1SFedor Uporov 
238*512f29d1SFedor Uporov static uint32_t
239*512f29d1SFedor Uporov ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
240*512f29d1SFedor Uporov     int count, struct ext2fs_htree_tail *tp)
241*512f29d1SFedor Uporov {
242*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
243*512f29d1SFedor Uporov 	char *buf;
244*512f29d1SFedor Uporov 	int size;
245*512f29d1SFedor Uporov 	uint32_t inum, old_csum, gen, crc;
246*512f29d1SFedor Uporov 
247*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
248*512f29d1SFedor Uporov 
249*512f29d1SFedor Uporov 	buf = (char *)ep;
250*512f29d1SFedor Uporov 
251*512f29d1SFedor Uporov 	size = count_offset + (count * sizeof(struct ext2fs_htree_entry));
252*512f29d1SFedor Uporov 	old_csum = tp->ht_checksum;
253*512f29d1SFedor Uporov 	tp->ht_checksum = 0;
254*512f29d1SFedor Uporov 
255*512f29d1SFedor Uporov 	inum = ip->i_number;
256*512f29d1SFedor Uporov 	gen = ip->i_gen;
257*512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
258*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
259*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
260*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail));
261*512f29d1SFedor Uporov 	tp->ht_checksum = old_csum;
262*512f29d1SFedor Uporov 
263*512f29d1SFedor Uporov 	return (crc);
264*512f29d1SFedor Uporov }
265*512f29d1SFedor Uporov 
266*512f29d1SFedor Uporov static int
267*512f29d1SFedor Uporov ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
268*512f29d1SFedor Uporov {
269*512f29d1SFedor Uporov 	uint32_t calculated;
270*512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
271*512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
272*512f29d1SFedor Uporov 	int count_offset, limit, count;
273*512f29d1SFedor Uporov 
274*512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
275*512f29d1SFedor Uporov 	if (cp == NULL)
276*512f29d1SFedor Uporov 		return (0);
277*512f29d1SFedor Uporov 
278*512f29d1SFedor Uporov 	limit = cp->h_entries_max;
279*512f29d1SFedor Uporov 	count = cp->h_entries_num;
280*512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
281*512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
282*512f29d1SFedor Uporov 		return (EIO);
283*512f29d1SFedor Uporov 
284*512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
285*512f29d1SFedor Uporov 	calculated = ext2_dx_csum(ip, ep,  count_offset, count, tp);
286*512f29d1SFedor Uporov 
287*512f29d1SFedor Uporov 	if (tp->ht_checksum != calculated)
288*512f29d1SFedor Uporov 		return (EIO);
289*512f29d1SFedor Uporov 
290*512f29d1SFedor Uporov 	return (0);
291*512f29d1SFedor Uporov }
292*512f29d1SFedor Uporov 
293*512f29d1SFedor Uporov int
294*512f29d1SFedor Uporov ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
295*512f29d1SFedor Uporov {
296*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
297*512f29d1SFedor Uporov 	struct ext2fs_direct_2 *ep;
298*512f29d1SFedor Uporov 	int error = 0;
299*512f29d1SFedor Uporov 
300*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
301*512f29d1SFedor Uporov 
302*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
303*512f29d1SFedor Uporov 		return (error);
304*512f29d1SFedor Uporov 
305*512f29d1SFedor Uporov 	ep = (struct ext2fs_direct_2 *)bp->b_data;
306*512f29d1SFedor Uporov 
307*512f29d1SFedor Uporov 	if (ext2_get_dirent_tail(ip, ep) != NULL)
308*512f29d1SFedor Uporov 		error = ext2_dirent_csum_verify(ip, ep);
309*512f29d1SFedor Uporov 	else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
310*512f29d1SFedor Uporov 		error = ext2_dx_csum_verify(ip, ep);
311*512f29d1SFedor Uporov 
312*512f29d1SFedor Uporov 	if (error)
313*512f29d1SFedor Uporov 		printf("WARNING: bad directory csum detected, ip=%lu"
314*512f29d1SFedor Uporov 		    " - run fsck\n", (unsigned long)ip->i_number);
315*512f29d1SFedor Uporov 
316*512f29d1SFedor Uporov 	return (error);
317*512f29d1SFedor Uporov }
318*512f29d1SFedor Uporov 
319*512f29d1SFedor Uporov static void
320*512f29d1SFedor Uporov ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
321*512f29d1SFedor Uporov {
322*512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
323*512f29d1SFedor Uporov 
324*512f29d1SFedor Uporov 	tp = ext2_get_dirent_tail(ip, ep);
325*512f29d1SFedor Uporov 	if (tp == NULL)
326*512f29d1SFedor Uporov 		return;
327*512f29d1SFedor Uporov 
328*512f29d1SFedor Uporov 	tp->e2dt_checksum =
329*512f29d1SFedor Uporov 	    ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
330*512f29d1SFedor Uporov }
331*512f29d1SFedor Uporov 
332*512f29d1SFedor Uporov static void
333*512f29d1SFedor Uporov ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
334*512f29d1SFedor Uporov {
335*512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
336*512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
337*512f29d1SFedor Uporov 	int count_offset, limit, count;
338*512f29d1SFedor Uporov 
339*512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
340*512f29d1SFedor Uporov 	if (cp == NULL)
341*512f29d1SFedor Uporov 		return;
342*512f29d1SFedor Uporov 
343*512f29d1SFedor Uporov 	limit = cp->h_entries_max;
344*512f29d1SFedor Uporov 	count = cp->h_entries_num;
345*512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
346*512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
347*512f29d1SFedor Uporov 		return;
348*512f29d1SFedor Uporov 
349*512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
350*512f29d1SFedor Uporov 	tp->ht_checksum = ext2_dx_csum(ip, ep,  count_offset, count, tp);
351*512f29d1SFedor Uporov }
352*512f29d1SFedor Uporov 
353*512f29d1SFedor Uporov void
354*512f29d1SFedor Uporov ext2_dir_blk_csum_set_mem(struct inode *ip, char *buf, int size)
355*512f29d1SFedor Uporov {
356*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
357*512f29d1SFedor Uporov 	struct ext2fs_direct_2 *ep;
358*512f29d1SFedor Uporov 
359*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
360*512f29d1SFedor Uporov 
361*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
362*512f29d1SFedor Uporov 		return;
363*512f29d1SFedor Uporov 
364*512f29d1SFedor Uporov 	ep = (struct ext2fs_direct_2 *)buf;
365*512f29d1SFedor Uporov 
366*512f29d1SFedor Uporov 	if (ext2_htree_has_idx(ip)) {
367*512f29d1SFedor Uporov 		if (ext2_get_dx_count(ip, ep, NULL) != NULL)
368*512f29d1SFedor Uporov 			ext2_dx_csum_set(ip, ep);
369*512f29d1SFedor Uporov 	} else {
370*512f29d1SFedor Uporov 		if (ext2_get_dirent_tail(ip, ep) != NULL)
371*512f29d1SFedor Uporov 			ext2_dirent_csum_set(ip, ep);
372*512f29d1SFedor Uporov 	}
373*512f29d1SFedor Uporov }
374*512f29d1SFedor Uporov 
375*512f29d1SFedor Uporov void
376*512f29d1SFedor Uporov ext2_dir_blk_csum_set(struct inode *ip, struct buf *bp)
377*512f29d1SFedor Uporov {
378*512f29d1SFedor Uporov 
379*512f29d1SFedor Uporov 	ext2_dir_blk_csum_set_mem(ip, bp->b_data, bp->b_bufsize);
380*512f29d1SFedor Uporov }
381*512f29d1SFedor Uporov 
382*512f29d1SFedor Uporov static uint32_t
383*512f29d1SFedor Uporov ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
384*512f29d1SFedor Uporov {
385*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
386*512f29d1SFedor Uporov 	size_t size;
387*512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
388*512f29d1SFedor Uporov 
389*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
390*512f29d1SFedor Uporov 
391*512f29d1SFedor Uporov 	size = EXT4_EXTENT_TAIL_OFFSET(ehp) +
392*512f29d1SFedor Uporov 	    offsetof(struct ext4_extent_tail, et_checksum);
393*512f29d1SFedor Uporov 
394*512f29d1SFedor Uporov 	inum = ip->i_number;
395*512f29d1SFedor Uporov 	gen = ip->i_gen;
396*512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
397*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
398*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ehp, size);
399*512f29d1SFedor Uporov 
400*512f29d1SFedor Uporov 	return (crc);
401*512f29d1SFedor Uporov }
402*512f29d1SFedor Uporov 
403*512f29d1SFedor Uporov int
404*512f29d1SFedor Uporov ext2_extent_blk_csum_verify(struct inode *ip, void *data)
405*512f29d1SFedor Uporov {
406*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
407*512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
408*512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
409*512f29d1SFedor Uporov 	uint32_t provided, calculated;
410*512f29d1SFedor Uporov 
411*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
412*512f29d1SFedor Uporov 
413*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
414*512f29d1SFedor Uporov 		return (0);
415*512f29d1SFedor Uporov 
416*512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
417*512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)ehp) +
418*512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
419*512f29d1SFedor Uporov 
420*512f29d1SFedor Uporov 	provided = etp->et_checksum;
421*512f29d1SFedor Uporov 	calculated = ext2_extent_blk_csum(ip, ehp);
422*512f29d1SFedor Uporov 
423*512f29d1SFedor Uporov 	if (provided != calculated) {
424*512f29d1SFedor Uporov 		printf("WARNING: bad extent csum detected, ip=%lu - run fsck\n",
425*512f29d1SFedor Uporov 		    (unsigned long)ip->i_number);
426*512f29d1SFedor Uporov 		return (EIO);
427*512f29d1SFedor Uporov 	}
428*512f29d1SFedor Uporov 
429*512f29d1SFedor Uporov 	return (0);
430*512f29d1SFedor Uporov }
431*512f29d1SFedor Uporov 
432*512f29d1SFedor Uporov void
433*512f29d1SFedor Uporov ext2_extent_blk_csum_set(struct inode *ip, void *data)
434*512f29d1SFedor Uporov {
435*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
436*512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
437*512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
438*512f29d1SFedor Uporov 
439*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
440*512f29d1SFedor Uporov 
441*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
442*512f29d1SFedor Uporov 		return;
443*512f29d1SFedor Uporov 
444*512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
445*512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)data) +
446*512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
447*512f29d1SFedor Uporov 
448*512f29d1SFedor Uporov 	etp->et_checksum = ext2_extent_blk_csum(ip,
449*512f29d1SFedor Uporov 	    (struct ext4_extent_header *)data);
450*512f29d1SFedor Uporov }
451*512f29d1SFedor Uporov 
452*512f29d1SFedor Uporov int
453*512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
454*512f29d1SFedor Uporov {
455*512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
456*512f29d1SFedor Uporov 
457*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
458*512f29d1SFedor Uporov 		return (0);
459*512f29d1SFedor Uporov 
460*512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum;
461*512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
462*512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
463*512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) {
464*512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi;
465*512f29d1SFedor Uporov 		provided |= (hi << 16);
466*512f29d1SFedor Uporov 	} else
467*512f29d1SFedor Uporov 		calculated &= 0xFFFF;
468*512f29d1SFedor Uporov 
469*512f29d1SFedor Uporov 	if (provided != calculated) {
470*512f29d1SFedor Uporov 		printf("WARNING: bad inode bitmap csum detected, "
471*512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
472*512f29d1SFedor Uporov 		return (EIO);
473*512f29d1SFedor Uporov 	}
474*512f29d1SFedor Uporov 
475*512f29d1SFedor Uporov 	return (0);
476*512f29d1SFedor Uporov }
477*512f29d1SFedor Uporov 
478*512f29d1SFedor Uporov void
479*512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
480*512f29d1SFedor Uporov {
481*512f29d1SFedor Uporov 	uint32_t csum;
482*512f29d1SFedor Uporov 
483*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
484*512f29d1SFedor Uporov 		return;
485*512f29d1SFedor Uporov 
486*512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
487*512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
488*512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF;
489*512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END)
490*512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16;
491*512f29d1SFedor Uporov }
492*512f29d1SFedor Uporov 
493*512f29d1SFedor Uporov int
494*512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
495*512f29d1SFedor Uporov {
496*512f29d1SFedor Uporov 	uint32_t hi, provided, calculated, size;
497*512f29d1SFedor Uporov 
498*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
499*512f29d1SFedor Uporov 		return (0);
500*512f29d1SFedor Uporov 
501*512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
502*512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum;
503*512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
504*512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) {
505*512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi;
506*512f29d1SFedor Uporov 		provided |= (hi << 16);
507*512f29d1SFedor Uporov 	} else
508*512f29d1SFedor Uporov 		calculated &= 0xFFFF;
509*512f29d1SFedor Uporov 
510*512f29d1SFedor Uporov 	if (provided != calculated) {
511*512f29d1SFedor Uporov 		printf("WARNING: bad block bitmap csum detected, "
512*512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
513*512f29d1SFedor Uporov 		return (EIO);
514*512f29d1SFedor Uporov 	}
515*512f29d1SFedor Uporov 
516*512f29d1SFedor Uporov 	return (0);
517*512f29d1SFedor Uporov }
518*512f29d1SFedor Uporov 
519*512f29d1SFedor Uporov void
520*512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
521*512f29d1SFedor Uporov {
522*512f29d1SFedor Uporov 	uint32_t csum, size;
523*512f29d1SFedor Uporov 
524*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
525*512f29d1SFedor Uporov 		return;
526*512f29d1SFedor Uporov 
527*512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
528*512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
529*512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF;
530*512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
531*512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16;
532*512f29d1SFedor Uporov }
533*512f29d1SFedor Uporov 
534*512f29d1SFedor Uporov static uint32_t
535*512f29d1SFedor Uporov ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
536*512f29d1SFedor Uporov {
537*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
538*512f29d1SFedor Uporov 	uint16_t old_lo, old_hi;
539*512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
540*512f29d1SFedor Uporov 
541*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
542*512f29d1SFedor Uporov 
543*512f29d1SFedor Uporov 	old_lo = ei->e2di_chksum_lo;
544*512f29d1SFedor Uporov 	ei->e2di_chksum_lo = 0;
545*512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
546*512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
547*512f29d1SFedor Uporov 		old_hi = ei->e2di_chksum_hi;
548*512f29d1SFedor Uporov 		ei->e2di_chksum_hi = 0;
549*512f29d1SFedor Uporov 	}
550*512f29d1SFedor Uporov 
551*512f29d1SFedor Uporov 	inum = ip->i_number;
552*512f29d1SFedor Uporov 	gen = ip->i_gen;
553*512f29d1SFedor Uporov 
554*512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
555*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
556*512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ei, fs->e2fs->e2fs_inode_size);
557*512f29d1SFedor Uporov 
558*512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
559*512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
560*512f29d1SFedor Uporov 		ei->e2di_chksum_hi = old_hi;
561*512f29d1SFedor Uporov 
562*512f29d1SFedor Uporov 	return (crc);
563*512f29d1SFedor Uporov }
564*512f29d1SFedor Uporov 
565*512f29d1SFedor Uporov int
566*512f29d1SFedor Uporov ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
567*512f29d1SFedor Uporov {
568*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
569*512f29d1SFedor Uporov 	const static struct ext2fs_dinode ei_zero;
570*512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
571*512f29d1SFedor Uporov 
572*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
573*512f29d1SFedor Uporov 
574*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
575*512f29d1SFedor Uporov 		return (0);
576*512f29d1SFedor Uporov 
577*512f29d1SFedor Uporov 	/* Check case, when dinode was not initialized */
578*512f29d1SFedor Uporov 	if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
579*512f29d1SFedor Uporov 		return (0);
580*512f29d1SFedor Uporov 
581*512f29d1SFedor Uporov 	provided = ei->e2di_chksum_lo;
582*512f29d1SFedor Uporov 	calculated = ext2_ei_csum(ip, ei);
583*512f29d1SFedor Uporov 
584*512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
585*512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
586*512f29d1SFedor Uporov 		hi = ei->e2di_chksum_hi;
587*512f29d1SFedor Uporov 		provided |= hi << 16;
588*512f29d1SFedor Uporov 	} else
589*512f29d1SFedor Uporov 		calculated &= 0xFFFF;
590*512f29d1SFedor Uporov 
591*512f29d1SFedor Uporov 	if (provided != calculated)
592*512f29d1SFedor Uporov 		return (EIO);
593*512f29d1SFedor Uporov 
594*512f29d1SFedor Uporov 	return (0);
595*512f29d1SFedor Uporov }
596*512f29d1SFedor Uporov 
597*512f29d1SFedor Uporov void
598*512f29d1SFedor Uporov ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
599*512f29d1SFedor Uporov {
600*512f29d1SFedor Uporov 	struct m_ext2fs *fs;
601*512f29d1SFedor Uporov 	uint32_t crc;
602*512f29d1SFedor Uporov 
603*512f29d1SFedor Uporov 	fs = ip->i_e2fs;
604*512f29d1SFedor Uporov 
605*512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
606*512f29d1SFedor Uporov 		return;
607*512f29d1SFedor Uporov 
608*512f29d1SFedor Uporov 	crc = ext2_ei_csum(ip, ei);
609*512f29d1SFedor Uporov 
610*512f29d1SFedor Uporov 	ei->e2di_chksum_lo = crc & 0xFFFF;
611*512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
612*512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
613*512f29d1SFedor Uporov 		ei->e2di_chksum_hi = crc >> 16;
614*512f29d1SFedor Uporov }
615*512f29d1SFedor Uporov 
616d23db91eSPedro F. Giffuni static uint16_t
617d23db91eSPedro F. Giffuni ext2_crc16(uint16_t crc, const void *buffer, unsigned int len)
618d23db91eSPedro F. Giffuni {
619d23db91eSPedro F. Giffuni 	const unsigned char *cp = buffer;
620d23db91eSPedro F. Giffuni 	/* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
621d23db91eSPedro F. Giffuni 	static uint16_t const crc16_table[256] = {
622d23db91eSPedro F. Giffuni 		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
623d23db91eSPedro F. Giffuni 		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
624d23db91eSPedro F. Giffuni 		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
625d23db91eSPedro F. Giffuni 		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
626d23db91eSPedro F. Giffuni 		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
627d23db91eSPedro F. Giffuni 		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
628d23db91eSPedro F. Giffuni 		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
629d23db91eSPedro F. Giffuni 		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
630d23db91eSPedro F. Giffuni 		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
631d23db91eSPedro F. Giffuni 		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
632d23db91eSPedro F. Giffuni 		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
633d23db91eSPedro F. Giffuni 		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
634d23db91eSPedro F. Giffuni 		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
635d23db91eSPedro F. Giffuni 		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
636d23db91eSPedro F. Giffuni 		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
637d23db91eSPedro F. Giffuni 		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
638d23db91eSPedro F. Giffuni 		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
639d23db91eSPedro F. Giffuni 		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
640d23db91eSPedro F. Giffuni 		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
641d23db91eSPedro F. Giffuni 		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
642d23db91eSPedro F. Giffuni 		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
643d23db91eSPedro F. Giffuni 		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
644d23db91eSPedro F. Giffuni 		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
645d23db91eSPedro F. Giffuni 		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
646d23db91eSPedro F. Giffuni 		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
647d23db91eSPedro F. Giffuni 		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
648d23db91eSPedro F. Giffuni 		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
649d23db91eSPedro F. Giffuni 		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
650d23db91eSPedro F. Giffuni 		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
651d23db91eSPedro F. Giffuni 		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
652d23db91eSPedro F. Giffuni 		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
653d23db91eSPedro F. Giffuni 		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
654d23db91eSPedro F. Giffuni 	};
655d23db91eSPedro F. Giffuni 
656d23db91eSPedro F. Giffuni 	while (len--)
657d23db91eSPedro F. Giffuni 		crc = (((crc >> 8) & 0xffU) ^
658d23db91eSPedro F. Giffuni 		    crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
659d23db91eSPedro F. Giffuni 	return crc;
660d23db91eSPedro F. Giffuni }
661d23db91eSPedro F. Giffuni 
662d23db91eSPedro F. Giffuni static uint16_t
663d23db91eSPedro F. Giffuni ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
664d23db91eSPedro F. Giffuni {
665d23db91eSPedro F. Giffuni 	size_t offset;
666*512f29d1SFedor Uporov 	uint32_t csum32;
667*512f29d1SFedor Uporov 	uint16_t crc, dummy_csum;
668d23db91eSPedro F. Giffuni 
669d23db91eSPedro F. Giffuni 	offset = offsetof(struct ext2_gd, ext4bgd_csum);
670d23db91eSPedro F. Giffuni 
671*512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
672*512f29d1SFedor Uporov 		csum32 = calculate_crc32c(fs->e2fs_csum_seed,
673*512f29d1SFedor Uporov 		    (uint8_t *)&block_group, sizeof(block_group));
674*512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
675*512f29d1SFedor Uporov 		dummy_csum = 0;
676*512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
677*512f29d1SFedor Uporov 		    sizeof(dummy_csum));
678*512f29d1SFedor Uporov 		offset += sizeof(dummy_csum);
679*512f29d1SFedor Uporov 		if (offset < fs->e2fs->e3fs_desc_size)
680*512f29d1SFedor Uporov 			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
681*512f29d1SFedor Uporov 			    fs->e2fs->e3fs_desc_size - offset);
682*512f29d1SFedor Uporov 
683*512f29d1SFedor Uporov 		crc = csum32 & 0xFFFF;
684*512f29d1SFedor Uporov 		return (crc);
685*512f29d1SFedor Uporov 	} else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
686d23db91eSPedro F. Giffuni 		crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid,
687d23db91eSPedro F. Giffuni 		    sizeof(fs->e2fs->e2fs_uuid));
688d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)&block_group,
689d23db91eSPedro F. Giffuni 		    sizeof(block_group));
690d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)gd, offset);
691d23db91eSPedro F. Giffuni 		offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
692d23db91eSPedro F. Giffuni 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
693d23db91eSPedro F. Giffuni 		    offset < fs->e2fs->e3fs_desc_size)
694d23db91eSPedro F. Giffuni 			crc = ext2_crc16(crc, (uint8_t *)gd + offset,
695d23db91eSPedro F. Giffuni 			    fs->e2fs->e3fs_desc_size - offset);
696d23db91eSPedro F. Giffuni 		return (crc);
697d23db91eSPedro F. Giffuni 	}
698d23db91eSPedro F. Giffuni 
699d23db91eSPedro F. Giffuni 	return (0);
700d23db91eSPedro F. Giffuni }
701d23db91eSPedro F. Giffuni 
702d23db91eSPedro F. Giffuni int
703d23db91eSPedro F. Giffuni ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
704d23db91eSPedro F. Giffuni {
705d23db91eSPedro F. Giffuni 	unsigned int i;
706d23db91eSPedro F. Giffuni 	int error = 0;
707d23db91eSPedro F. Giffuni 
708d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++) {
709d23db91eSPedro F. Giffuni 		if (fs->e2fs_gd[i].ext4bgd_csum !=
710d23db91eSPedro F. Giffuni 		    ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
711d23db91eSPedro F. Giffuni 			printf(
712d23db91eSPedro F. Giffuni "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
713d23db91eSPedro F. Giffuni 			    devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
714d23db91eSPedro F. Giffuni 			    ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
715*512f29d1SFedor Uporov 			error = EIO;
716d23db91eSPedro F. Giffuni 			break;
717d23db91eSPedro F. Giffuni 		}
718d23db91eSPedro F. Giffuni 	}
719d23db91eSPedro F. Giffuni 
720d23db91eSPedro F. Giffuni 	return (error);
721d23db91eSPedro F. Giffuni }
722d23db91eSPedro F. Giffuni 
723d23db91eSPedro F. Giffuni void
724d23db91eSPedro F. Giffuni ext2_gd_csum_set(struct m_ext2fs *fs)
725d23db91eSPedro F. Giffuni {
726d23db91eSPedro F. Giffuni 	unsigned int i;
727d23db91eSPedro F. Giffuni 
728d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++)
729d23db91eSPedro F. Giffuni 		    fs->e2fs_gd[i].ext4bgd_csum =
730d23db91eSPedro F. Giffuni 			ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
731d23db91eSPedro F. Giffuni }
732