xref: /freebsd/sys/fs/ext2fs/ext2_csum.c (revision 6d4a4ed7479306db342fc8f0297da8c29b86f801)
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>
46512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dinode.h>
47d23db91eSPedro F. Giffuni #include <fs/ext2fs/inode.h>
48512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dir.h>
49512f29d1SFedor Uporov #include <fs/ext2fs/htree.h>
50512f29d1SFedor Uporov #include <fs/ext2fs/ext2_extattr.h>
51d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h>
52d23db91eSPedro F. Giffuni 
53512f29d1SFedor Uporov #define EXT2_BG_INODE_BITMAP_CSUM_HI_END	\
54512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \
55512f29d1SFedor Uporov 	 sizeof(uint16_t))
56512f29d1SFedor Uporov 
57512f29d1SFedor Uporov #define EXT2_INODE_CSUM_HI_EXTRA_END	\
58512f29d1SFedor Uporov 	(offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \
59512f29d1SFedor Uporov 	 E2FS_REV0_INODE_SIZE)
60512f29d1SFedor Uporov 
61512f29d1SFedor Uporov #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION	\
62512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \
63512f29d1SFedor Uporov 	 sizeof(uint16_t))
64512f29d1SFedor Uporov 
65512f29d1SFedor Uporov void
66512f29d1SFedor Uporov ext2_sb_csum_set_seed(struct m_ext2fs *fs)
67512f29d1SFedor Uporov {
68512f29d1SFedor Uporov 
69512f29d1SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED))
70512f29d1SFedor Uporov 		fs->e2fs_csum_seed = fs->e2fs->e4fs_chksum_seed;
71512f29d1SFedor Uporov 	else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
72512f29d1SFedor Uporov 		fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid,
73512f29d1SFedor Uporov 		    sizeof(fs->e2fs->e2fs_uuid));
74512f29d1SFedor Uporov 	}
75512f29d1SFedor Uporov 	else
76512f29d1SFedor Uporov 		fs->e2fs_csum_seed = 0;
77512f29d1SFedor Uporov }
78512f29d1SFedor Uporov 
79512f29d1SFedor Uporov int
80512f29d1SFedor Uporov ext2_sb_csum_verify(struct m_ext2fs *fs)
81512f29d1SFedor Uporov {
82512f29d1SFedor Uporov 
83512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) {
84512f29d1SFedor Uporov 		printf(
85512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt);
86512f29d1SFedor Uporov 		return (EINVAL);
87512f29d1SFedor Uporov 	}
88512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_sbchksum !=
89512f29d1SFedor Uporov 	    calculate_crc32c(~0, (const char *)fs->e2fs,
90512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum))) {
91512f29d1SFedor Uporov 		printf(
92512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n",
93512f29d1SFedor Uporov 		    fs->e2fs_fsmnt, fs->e2fs->e4fs_sbchksum, calculate_crc32c(~0,
94512f29d1SFedor Uporov 		    (const char *)fs->e2fs, offsetof(struct ext2fs, e4fs_sbchksum)));
95512f29d1SFedor Uporov 		return (EINVAL);
96512f29d1SFedor Uporov 	}
97512f29d1SFedor Uporov 
98512f29d1SFedor Uporov 	return (0);
99512f29d1SFedor Uporov }
100512f29d1SFedor Uporov 
101512f29d1SFedor Uporov void
102512f29d1SFedor Uporov ext2_sb_csum_set(struct m_ext2fs *fs)
103512f29d1SFedor Uporov {
104512f29d1SFedor Uporov 
105512f29d1SFedor Uporov 	fs->e2fs->e4fs_sbchksum = calculate_crc32c(~0, (const char *)fs->e2fs,
106512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum));
107512f29d1SFedor Uporov }
108512f29d1SFedor Uporov 
109512f29d1SFedor Uporov static uint32_t
110512f29d1SFedor Uporov ext2_extattr_blk_csum(struct inode *ip, uint64_t facl,
111512f29d1SFedor Uporov     struct ext2fs_extattr_header *header)
112512f29d1SFedor Uporov {
113512f29d1SFedor Uporov 	struct m_ext2fs *fs;
114512f29d1SFedor Uporov 	uint32_t crc, old_crc;
115512f29d1SFedor Uporov 
116512f29d1SFedor Uporov 	fs = ip->i_e2fs;
117512f29d1SFedor Uporov 
118512f29d1SFedor Uporov 	old_crc = header->h_checksum;
119512f29d1SFedor Uporov 
120512f29d1SFedor Uporov 	header->h_checksum = 0;
121512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl, sizeof(facl));
122512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)header, fs->e2fs_bsize);
123512f29d1SFedor Uporov 	header->h_checksum = old_crc;
124512f29d1SFedor Uporov 
125512f29d1SFedor Uporov 	return (crc);
126512f29d1SFedor Uporov }
127512f29d1SFedor Uporov 
128512f29d1SFedor Uporov int
129512f29d1SFedor Uporov ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp)
130512f29d1SFedor Uporov {
131512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
132512f29d1SFedor Uporov 
133512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
134512f29d1SFedor Uporov 
135512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) &&
136512f29d1SFedor Uporov 	    (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) {
137512f29d1SFedor Uporov 		printf("WARNING: bad extattr csum detected, ip=%lu - run fsck\n",
138512f29d1SFedor Uporov 		    (unsigned long)ip->i_number);
139512f29d1SFedor Uporov 		return (EIO);
140512f29d1SFedor Uporov 	}
141512f29d1SFedor Uporov 
142512f29d1SFedor Uporov 	return (0);
143512f29d1SFedor Uporov }
144512f29d1SFedor Uporov 
145512f29d1SFedor Uporov void
146512f29d1SFedor Uporov ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp)
147512f29d1SFedor Uporov {
148512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
149512f29d1SFedor Uporov 
150512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
151512f29d1SFedor Uporov 		return;
152512f29d1SFedor Uporov 
153512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
154512f29d1SFedor Uporov 	header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header);
155512f29d1SFedor Uporov }
156512f29d1SFedor Uporov 
157*6d4a4ed7SFedor Uporov void
158*6d4a4ed7SFedor Uporov ext2_init_dirent_tail(struct ext2fs_direct_tail *tp)
159512f29d1SFedor Uporov {
160*6d4a4ed7SFedor Uporov 	memset(tp, 0, sizeof(struct ext2fs_direct_tail));
161*6d4a4ed7SFedor Uporov 	tp->e2dt_rec_len = sizeof(struct ext2fs_direct_tail);
162*6d4a4ed7SFedor Uporov 	tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM;
163*6d4a4ed7SFedor Uporov }
164512f29d1SFedor Uporov 
165*6d4a4ed7SFedor Uporov struct ext2fs_direct_tail *
166*6d4a4ed7SFedor Uporov ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
167*6d4a4ed7SFedor Uporov {
168*6d4a4ed7SFedor Uporov 	struct ext2fs_direct_2 *dep;
169*6d4a4ed7SFedor Uporov 	void *top;
170*6d4a4ed7SFedor Uporov 	struct ext2fs_direct_tail *tp;
171*6d4a4ed7SFedor Uporov 	unsigned int rec_len;
172*6d4a4ed7SFedor Uporov 
173*6d4a4ed7SFedor Uporov 	dep = ep;
174*6d4a4ed7SFedor Uporov 	top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
175*6d4a4ed7SFedor Uporov 	rec_len = dep->e2d_reclen;
176*6d4a4ed7SFedor Uporov 
177*6d4a4ed7SFedor Uporov 	while (rec_len && !(rec_len & 0x3)) {
178*6d4a4ed7SFedor Uporov 		dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len);
179*6d4a4ed7SFedor Uporov 		if ((void *)dep >= top)
180*6d4a4ed7SFedor Uporov 			break;
181*6d4a4ed7SFedor Uporov 		rec_len = dep->e2d_reclen;
182*6d4a4ed7SFedor Uporov 	}
183*6d4a4ed7SFedor Uporov 
184*6d4a4ed7SFedor Uporov 	if (dep != top)
185*6d4a4ed7SFedor Uporov 		return (NULL);
186*6d4a4ed7SFedor Uporov 
187*6d4a4ed7SFedor Uporov 	tp = (struct ext2fs_direct_tail *)dep;
188512f29d1SFedor Uporov 	if (tp->e2dt_reserved_zero1 ||
189512f29d1SFedor Uporov 	    tp->e2dt_rec_len != sizeof(struct ext2fs_direct_tail) ||
190512f29d1SFedor Uporov 	    tp->e2dt_reserved_zero2 ||
191512f29d1SFedor Uporov 	    tp->e2dt_reserved_ft != EXT2_FT_DIR_CSUM)
192512f29d1SFedor Uporov 		return (NULL);
193512f29d1SFedor Uporov 
194512f29d1SFedor Uporov 	return (tp);
195512f29d1SFedor Uporov }
196512f29d1SFedor Uporov 
197512f29d1SFedor Uporov static uint32_t
198512f29d1SFedor Uporov ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
199512f29d1SFedor Uporov {
200512f29d1SFedor Uporov 	struct m_ext2fs *fs;
201512f29d1SFedor Uporov 	char *buf;
202512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
203512f29d1SFedor Uporov 
204512f29d1SFedor Uporov 	fs = ip->i_e2fs;
205512f29d1SFedor Uporov 
206512f29d1SFedor Uporov 	buf = (char *)ep;
207512f29d1SFedor Uporov 
208512f29d1SFedor Uporov 	inum = ip->i_number;
209512f29d1SFedor Uporov 	gen = ip->i_gen;
210512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
211512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
212512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
213512f29d1SFedor Uporov 
214512f29d1SFedor Uporov 	return (crc);
215512f29d1SFedor Uporov }
216512f29d1SFedor Uporov 
217*6d4a4ed7SFedor Uporov int
218512f29d1SFedor Uporov ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
219512f29d1SFedor Uporov {
220512f29d1SFedor Uporov 	uint32_t calculated;
221512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
222512f29d1SFedor Uporov 
223*6d4a4ed7SFedor Uporov 	tp = ext2_dirent_get_tail(ip, ep);
224512f29d1SFedor Uporov 	if (tp == NULL)
225512f29d1SFedor Uporov 		return (0);
226512f29d1SFedor Uporov 
227512f29d1SFedor Uporov 	calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
228512f29d1SFedor Uporov 	if (calculated != tp->e2dt_checksum)
229512f29d1SFedor Uporov 		return (EIO);
230512f29d1SFedor Uporov 
231512f29d1SFedor Uporov 	return (0);
232512f29d1SFedor Uporov }
233512f29d1SFedor Uporov 
234512f29d1SFedor Uporov static struct ext2fs_htree_count *
235512f29d1SFedor Uporov ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset)
236512f29d1SFedor Uporov {
237512f29d1SFedor Uporov 	struct ext2fs_direct_2 *dp;
238512f29d1SFedor Uporov 	struct ext2fs_htree_root_info *root;
239512f29d1SFedor Uporov 	int count_offset;
240512f29d1SFedor Uporov 
241512f29d1SFedor Uporov 	if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs))
242512f29d1SFedor Uporov 		count_offset = 8;
243512f29d1SFedor Uporov 	else if (ep->e2d_reclen == 12) {
244512f29d1SFedor Uporov 		dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12);
245512f29d1SFedor Uporov 		if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12)
246512f29d1SFedor Uporov 			return (NULL);
247512f29d1SFedor Uporov 
248512f29d1SFedor Uporov 		root = (struct ext2fs_htree_root_info *)(((char *)dp + 12));
249512f29d1SFedor Uporov 		if (root->h_reserved1 ||
250512f29d1SFedor Uporov 		    root->h_info_len != sizeof(struct ext2fs_htree_root_info))
251512f29d1SFedor Uporov 			return (NULL);
252512f29d1SFedor Uporov 
253512f29d1SFedor Uporov 		count_offset = 32;
254512f29d1SFedor Uporov 	} else
255512f29d1SFedor Uporov 		return (NULL);
256512f29d1SFedor Uporov 
257512f29d1SFedor Uporov 	if (offset)
258512f29d1SFedor Uporov 		*offset = count_offset;
259512f29d1SFedor Uporov 
260512f29d1SFedor Uporov 	return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset));
261512f29d1SFedor Uporov }
262512f29d1SFedor Uporov 
263512f29d1SFedor Uporov static uint32_t
264512f29d1SFedor Uporov ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
265512f29d1SFedor Uporov     int count, struct ext2fs_htree_tail *tp)
266512f29d1SFedor Uporov {
267512f29d1SFedor Uporov 	struct m_ext2fs *fs;
268512f29d1SFedor Uporov 	char *buf;
269512f29d1SFedor Uporov 	int size;
270512f29d1SFedor Uporov 	uint32_t inum, old_csum, gen, crc;
271512f29d1SFedor Uporov 
272512f29d1SFedor Uporov 	fs = ip->i_e2fs;
273512f29d1SFedor Uporov 
274512f29d1SFedor Uporov 	buf = (char *)ep;
275512f29d1SFedor Uporov 
276512f29d1SFedor Uporov 	size = count_offset + (count * sizeof(struct ext2fs_htree_entry));
277512f29d1SFedor Uporov 	old_csum = tp->ht_checksum;
278512f29d1SFedor Uporov 	tp->ht_checksum = 0;
279512f29d1SFedor Uporov 
280512f29d1SFedor Uporov 	inum = ip->i_number;
281512f29d1SFedor Uporov 	gen = ip->i_gen;
282512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
283512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
284512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
285512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail));
286512f29d1SFedor Uporov 	tp->ht_checksum = old_csum;
287512f29d1SFedor Uporov 
288512f29d1SFedor Uporov 	return (crc);
289512f29d1SFedor Uporov }
290512f29d1SFedor Uporov 
291*6d4a4ed7SFedor Uporov int
292512f29d1SFedor Uporov ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
293512f29d1SFedor Uporov {
294512f29d1SFedor Uporov 	uint32_t calculated;
295512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
296512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
297512f29d1SFedor Uporov 	int count_offset, limit, count;
298512f29d1SFedor Uporov 
299512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
300512f29d1SFedor Uporov 	if (cp == NULL)
301512f29d1SFedor Uporov 		return (0);
302512f29d1SFedor Uporov 
303512f29d1SFedor Uporov 	limit = cp->h_entries_max;
304512f29d1SFedor Uporov 	count = cp->h_entries_num;
305512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
306512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
307512f29d1SFedor Uporov 		return (EIO);
308512f29d1SFedor Uporov 
309512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
310512f29d1SFedor Uporov 	calculated = ext2_dx_csum(ip, ep,  count_offset, count, tp);
311512f29d1SFedor Uporov 
312512f29d1SFedor Uporov 	if (tp->ht_checksum != calculated)
313512f29d1SFedor Uporov 		return (EIO);
314512f29d1SFedor Uporov 
315512f29d1SFedor Uporov 	return (0);
316512f29d1SFedor Uporov }
317512f29d1SFedor Uporov 
318512f29d1SFedor Uporov int
319512f29d1SFedor Uporov ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
320512f29d1SFedor Uporov {
321512f29d1SFedor Uporov 	struct m_ext2fs *fs;
322512f29d1SFedor Uporov 	struct ext2fs_direct_2 *ep;
323512f29d1SFedor Uporov 	int error = 0;
324512f29d1SFedor Uporov 
325512f29d1SFedor Uporov 	fs = ip->i_e2fs;
326512f29d1SFedor Uporov 
327512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
328512f29d1SFedor Uporov 		return (error);
329512f29d1SFedor Uporov 
330512f29d1SFedor Uporov 	ep = (struct ext2fs_direct_2 *)bp->b_data;
331512f29d1SFedor Uporov 
332*6d4a4ed7SFedor Uporov 	if (ext2_dirent_get_tail(ip, ep) != NULL)
333512f29d1SFedor Uporov 		error = ext2_dirent_csum_verify(ip, ep);
334512f29d1SFedor Uporov 	else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
335512f29d1SFedor Uporov 		error = ext2_dx_csum_verify(ip, ep);
336512f29d1SFedor Uporov 
337512f29d1SFedor Uporov 	if (error)
338512f29d1SFedor Uporov 		printf("WARNING: bad directory csum detected, ip=%lu"
339512f29d1SFedor Uporov 		    " - run fsck\n", (unsigned long)ip->i_number);
340512f29d1SFedor Uporov 
341512f29d1SFedor Uporov 	return (error);
342512f29d1SFedor Uporov }
343512f29d1SFedor Uporov 
344*6d4a4ed7SFedor Uporov void
345512f29d1SFedor Uporov ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
346512f29d1SFedor Uporov {
347*6d4a4ed7SFedor Uporov 	struct m_ext2fs *fs;
348512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
349512f29d1SFedor Uporov 
350*6d4a4ed7SFedor Uporov 	fs = ip->i_e2fs;
351*6d4a4ed7SFedor Uporov 
352*6d4a4ed7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
353*6d4a4ed7SFedor Uporov 		return;
354*6d4a4ed7SFedor Uporov 
355*6d4a4ed7SFedor Uporov 	tp = ext2_dirent_get_tail(ip, ep);
356512f29d1SFedor Uporov 	if (tp == NULL)
357512f29d1SFedor Uporov 		return;
358512f29d1SFedor Uporov 
359512f29d1SFedor Uporov 	tp->e2dt_checksum =
360512f29d1SFedor Uporov 	    ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
361512f29d1SFedor Uporov }
362512f29d1SFedor Uporov 
363*6d4a4ed7SFedor Uporov void
364512f29d1SFedor Uporov ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
365512f29d1SFedor Uporov {
366*6d4a4ed7SFedor Uporov 	struct m_ext2fs *fs;
367512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
368512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
369512f29d1SFedor Uporov 	int count_offset, limit, count;
370512f29d1SFedor Uporov 
371*6d4a4ed7SFedor Uporov 	fs = ip->i_e2fs;
372*6d4a4ed7SFedor Uporov 
373*6d4a4ed7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
374*6d4a4ed7SFedor Uporov 		return;
375*6d4a4ed7SFedor Uporov 
376512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
377512f29d1SFedor Uporov 	if (cp == NULL)
378512f29d1SFedor Uporov 		return;
379512f29d1SFedor Uporov 
380512f29d1SFedor Uporov 	limit = cp->h_entries_max;
381512f29d1SFedor Uporov 	count = cp->h_entries_num;
382512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
383512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
384512f29d1SFedor Uporov 		return;
385512f29d1SFedor Uporov 
386512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
387512f29d1SFedor Uporov 	tp->ht_checksum = ext2_dx_csum(ip, ep,  count_offset, count, tp);
388512f29d1SFedor Uporov }
389512f29d1SFedor Uporov 
390512f29d1SFedor Uporov static uint32_t
391512f29d1SFedor Uporov ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
392512f29d1SFedor Uporov {
393512f29d1SFedor Uporov 	struct m_ext2fs *fs;
394512f29d1SFedor Uporov 	size_t size;
395512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
396512f29d1SFedor Uporov 
397512f29d1SFedor Uporov 	fs = ip->i_e2fs;
398512f29d1SFedor Uporov 
399512f29d1SFedor Uporov 	size = EXT4_EXTENT_TAIL_OFFSET(ehp) +
400512f29d1SFedor Uporov 	    offsetof(struct ext4_extent_tail, et_checksum);
401512f29d1SFedor Uporov 
402512f29d1SFedor Uporov 	inum = ip->i_number;
403512f29d1SFedor Uporov 	gen = ip->i_gen;
404512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
405512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
406512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ehp, size);
407512f29d1SFedor Uporov 
408512f29d1SFedor Uporov 	return (crc);
409512f29d1SFedor Uporov }
410512f29d1SFedor Uporov 
411512f29d1SFedor Uporov int
412512f29d1SFedor Uporov ext2_extent_blk_csum_verify(struct inode *ip, void *data)
413512f29d1SFedor Uporov {
414512f29d1SFedor Uporov 	struct m_ext2fs *fs;
415512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
416512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
417512f29d1SFedor Uporov 	uint32_t provided, calculated;
418512f29d1SFedor Uporov 
419512f29d1SFedor Uporov 	fs = ip->i_e2fs;
420512f29d1SFedor Uporov 
421512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
422512f29d1SFedor Uporov 		return (0);
423512f29d1SFedor Uporov 
424512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
425512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)ehp) +
426512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
427512f29d1SFedor Uporov 
428512f29d1SFedor Uporov 	provided = etp->et_checksum;
429512f29d1SFedor Uporov 	calculated = ext2_extent_blk_csum(ip, ehp);
430512f29d1SFedor Uporov 
431512f29d1SFedor Uporov 	if (provided != calculated) {
432512f29d1SFedor Uporov 		printf("WARNING: bad extent csum detected, ip=%lu - run fsck\n",
433512f29d1SFedor Uporov 		    (unsigned long)ip->i_number);
434512f29d1SFedor Uporov 		return (EIO);
435512f29d1SFedor Uporov 	}
436512f29d1SFedor Uporov 
437512f29d1SFedor Uporov 	return (0);
438512f29d1SFedor Uporov }
439512f29d1SFedor Uporov 
440512f29d1SFedor Uporov void
441512f29d1SFedor Uporov ext2_extent_blk_csum_set(struct inode *ip, void *data)
442512f29d1SFedor Uporov {
443512f29d1SFedor Uporov 	struct m_ext2fs *fs;
444512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
445512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
446512f29d1SFedor Uporov 
447512f29d1SFedor Uporov 	fs = ip->i_e2fs;
448512f29d1SFedor Uporov 
449512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
450512f29d1SFedor Uporov 		return;
451512f29d1SFedor Uporov 
452512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
453512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)data) +
454512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
455512f29d1SFedor Uporov 
456512f29d1SFedor Uporov 	etp->et_checksum = ext2_extent_blk_csum(ip,
457512f29d1SFedor Uporov 	    (struct ext4_extent_header *)data);
458512f29d1SFedor Uporov }
459512f29d1SFedor Uporov 
460512f29d1SFedor Uporov int
461512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
462512f29d1SFedor Uporov {
463512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
464512f29d1SFedor Uporov 
465512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
466512f29d1SFedor Uporov 		return (0);
467512f29d1SFedor Uporov 
468512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum;
469512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
470512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
471512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) {
472512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi;
473512f29d1SFedor Uporov 		provided |= (hi << 16);
474512f29d1SFedor Uporov 	} else
475512f29d1SFedor Uporov 		calculated &= 0xFFFF;
476512f29d1SFedor Uporov 
477512f29d1SFedor Uporov 	if (provided != calculated) {
478512f29d1SFedor Uporov 		printf("WARNING: bad inode bitmap csum detected, "
479512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
480512f29d1SFedor Uporov 		return (EIO);
481512f29d1SFedor Uporov 	}
482512f29d1SFedor Uporov 
483512f29d1SFedor Uporov 	return (0);
484512f29d1SFedor Uporov }
485512f29d1SFedor Uporov 
486512f29d1SFedor Uporov void
487512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
488512f29d1SFedor Uporov {
489512f29d1SFedor Uporov 	uint32_t csum;
490512f29d1SFedor Uporov 
491512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
492512f29d1SFedor Uporov 		return;
493512f29d1SFedor Uporov 
494512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
495512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
496512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF;
497512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END)
498512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16;
499512f29d1SFedor Uporov }
500512f29d1SFedor Uporov 
501512f29d1SFedor Uporov int
502512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
503512f29d1SFedor Uporov {
504512f29d1SFedor Uporov 	uint32_t hi, provided, calculated, size;
505512f29d1SFedor Uporov 
506512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
507512f29d1SFedor Uporov 		return (0);
508512f29d1SFedor Uporov 
509512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
510512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum;
511512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
512512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) {
513512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi;
514512f29d1SFedor Uporov 		provided |= (hi << 16);
515512f29d1SFedor Uporov 	} else
516512f29d1SFedor Uporov 		calculated &= 0xFFFF;
517512f29d1SFedor Uporov 
518512f29d1SFedor Uporov 	if (provided != calculated) {
519512f29d1SFedor Uporov 		printf("WARNING: bad block bitmap csum detected, "
520512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
521512f29d1SFedor Uporov 		return (EIO);
522512f29d1SFedor Uporov 	}
523512f29d1SFedor Uporov 
524512f29d1SFedor Uporov 	return (0);
525512f29d1SFedor Uporov }
526512f29d1SFedor Uporov 
527512f29d1SFedor Uporov void
528512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
529512f29d1SFedor Uporov {
530512f29d1SFedor Uporov 	uint32_t csum, size;
531512f29d1SFedor Uporov 
532512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
533512f29d1SFedor Uporov 		return;
534512f29d1SFedor Uporov 
535512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
536512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
537512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF;
538512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
539512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16;
540512f29d1SFedor Uporov }
541512f29d1SFedor Uporov 
542512f29d1SFedor Uporov static uint32_t
543512f29d1SFedor Uporov ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
544512f29d1SFedor Uporov {
545512f29d1SFedor Uporov 	struct m_ext2fs *fs;
546c4aa9a02SFedor Uporov 	uint32_t inode_csum_seed, inum, gen, crc;
547c4aa9a02SFedor Uporov 	uint16_t dummy_csum = 0;
548c4aa9a02SFedor Uporov 	unsigned int offset, csum_size;
549512f29d1SFedor Uporov 
550512f29d1SFedor Uporov 	fs = ip->i_e2fs;
551c4aa9a02SFedor Uporov 	offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo);
552c4aa9a02SFedor Uporov 	csum_size = sizeof(dummy_csum);
553512f29d1SFedor Uporov 	inum = ip->i_number;
554c4aa9a02SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed,
555c4aa9a02SFedor Uporov 	    (uint8_t *)&inum, sizeof(inum));
556*6d4a4ed7SFedor Uporov 	gen = ip->i_gen;
557c4aa9a02SFedor Uporov 	inode_csum_seed = calculate_crc32c(crc,
558c4aa9a02SFedor Uporov 	    (uint8_t *)&gen, sizeof(gen));
559512f29d1SFedor Uporov 
560c4aa9a02SFedor Uporov 	crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset);
561c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size);
562c4aa9a02SFedor Uporov 	offset += csum_size;
563c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
564c4aa9a02SFedor Uporov 	    E2FS_REV0_INODE_SIZE - offset);
565512f29d1SFedor Uporov 
566c4aa9a02SFedor Uporov 	if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) {
567c4aa9a02SFedor Uporov 		offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi);
568c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei +
569c4aa9a02SFedor Uporov 		    E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE);
570c4aa9a02SFedor Uporov 
571c4aa9a02SFedor Uporov 		if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
572c4aa9a02SFedor Uporov 		    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
573c4aa9a02SFedor Uporov 			crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum,
574c4aa9a02SFedor Uporov 			    csum_size);
575c4aa9a02SFedor Uporov 			offset += csum_size;
576c4aa9a02SFedor Uporov 		}
577c4aa9a02SFedor Uporov 
578c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
579c4aa9a02SFedor Uporov 		    EXT2_INODE_SIZE(fs) - offset);
580c4aa9a02SFedor Uporov 	}
581512f29d1SFedor Uporov 
582512f29d1SFedor Uporov 	return (crc);
583512f29d1SFedor Uporov }
584512f29d1SFedor Uporov 
585512f29d1SFedor Uporov int
586512f29d1SFedor Uporov ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
587512f29d1SFedor Uporov {
588512f29d1SFedor Uporov 	struct m_ext2fs *fs;
589512f29d1SFedor Uporov 	const static struct ext2fs_dinode ei_zero;
590512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
591512f29d1SFedor Uporov 
592512f29d1SFedor Uporov 	fs = ip->i_e2fs;
593512f29d1SFedor Uporov 
594512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
595512f29d1SFedor Uporov 		return (0);
596512f29d1SFedor Uporov 
597512f29d1SFedor Uporov 	provided = ei->e2di_chksum_lo;
598512f29d1SFedor Uporov 	calculated = ext2_ei_csum(ip, ei);
599512f29d1SFedor Uporov 
600512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
601512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
602512f29d1SFedor Uporov 		hi = ei->e2di_chksum_hi;
603512f29d1SFedor Uporov 		provided |= hi << 16;
604512f29d1SFedor Uporov 	} else
605512f29d1SFedor Uporov 		calculated &= 0xFFFF;
606512f29d1SFedor Uporov 
607c4aa9a02SFedor Uporov 	if (provided != calculated) {
608c4aa9a02SFedor Uporov 		/*
609c4aa9a02SFedor Uporov 		 * If it is first time used dinode,
610c4aa9a02SFedor Uporov 		 * it is expected that it will be zeroed
611c4aa9a02SFedor Uporov 		 * and we will not return checksum error in this case.
612c4aa9a02SFedor Uporov 		 */
613c4aa9a02SFedor Uporov 		if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
614c4aa9a02SFedor Uporov 			return (0);
615c4aa9a02SFedor Uporov 
616512f29d1SFedor Uporov 		return (EIO);
617c4aa9a02SFedor Uporov 	}
618512f29d1SFedor Uporov 
619512f29d1SFedor Uporov 	return (0);
620512f29d1SFedor Uporov }
621512f29d1SFedor Uporov 
622512f29d1SFedor Uporov void
623512f29d1SFedor Uporov ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
624512f29d1SFedor Uporov {
625512f29d1SFedor Uporov 	struct m_ext2fs *fs;
626512f29d1SFedor Uporov 	uint32_t crc;
627512f29d1SFedor Uporov 
628512f29d1SFedor Uporov 	fs = ip->i_e2fs;
629512f29d1SFedor Uporov 
630512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
631512f29d1SFedor Uporov 		return;
632512f29d1SFedor Uporov 
633512f29d1SFedor Uporov 	crc = ext2_ei_csum(ip, ei);
634512f29d1SFedor Uporov 
635512f29d1SFedor Uporov 	ei->e2di_chksum_lo = crc & 0xFFFF;
636512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
637512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
638512f29d1SFedor Uporov 		ei->e2di_chksum_hi = crc >> 16;
639512f29d1SFedor Uporov }
640512f29d1SFedor Uporov 
641d23db91eSPedro F. Giffuni static uint16_t
642d23db91eSPedro F. Giffuni ext2_crc16(uint16_t crc, const void *buffer, unsigned int len)
643d23db91eSPedro F. Giffuni {
644d23db91eSPedro F. Giffuni 	const unsigned char *cp = buffer;
645d23db91eSPedro F. Giffuni 	/* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
646d23db91eSPedro F. Giffuni 	static uint16_t const crc16_table[256] = {
647d23db91eSPedro F. Giffuni 		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
648d23db91eSPedro F. Giffuni 		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
649d23db91eSPedro F. Giffuni 		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
650d23db91eSPedro F. Giffuni 		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
651d23db91eSPedro F. Giffuni 		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
652d23db91eSPedro F. Giffuni 		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
653d23db91eSPedro F. Giffuni 		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
654d23db91eSPedro F. Giffuni 		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
655d23db91eSPedro F. Giffuni 		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
656d23db91eSPedro F. Giffuni 		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
657d23db91eSPedro F. Giffuni 		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
658d23db91eSPedro F. Giffuni 		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
659d23db91eSPedro F. Giffuni 		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
660d23db91eSPedro F. Giffuni 		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
661d23db91eSPedro F. Giffuni 		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
662d23db91eSPedro F. Giffuni 		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
663d23db91eSPedro F. Giffuni 		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
664d23db91eSPedro F. Giffuni 		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
665d23db91eSPedro F. Giffuni 		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
666d23db91eSPedro F. Giffuni 		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
667d23db91eSPedro F. Giffuni 		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
668d23db91eSPedro F. Giffuni 		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
669d23db91eSPedro F. Giffuni 		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
670d23db91eSPedro F. Giffuni 		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
671d23db91eSPedro F. Giffuni 		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
672d23db91eSPedro F. Giffuni 		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
673d23db91eSPedro F. Giffuni 		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
674d23db91eSPedro F. Giffuni 		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
675d23db91eSPedro F. Giffuni 		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
676d23db91eSPedro F. Giffuni 		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
677d23db91eSPedro F. Giffuni 		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
678d23db91eSPedro F. Giffuni 		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
679d23db91eSPedro F. Giffuni 	};
680d23db91eSPedro F. Giffuni 
681d23db91eSPedro F. Giffuni 	while (len--)
682d23db91eSPedro F. Giffuni 		crc = (((crc >> 8) & 0xffU) ^
683d23db91eSPedro F. Giffuni 		    crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
684d23db91eSPedro F. Giffuni 	return crc;
685d23db91eSPedro F. Giffuni }
686d23db91eSPedro F. Giffuni 
687d23db91eSPedro F. Giffuni static uint16_t
688d23db91eSPedro F. Giffuni ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
689d23db91eSPedro F. Giffuni {
690d23db91eSPedro F. Giffuni 	size_t offset;
691512f29d1SFedor Uporov 	uint32_t csum32;
692512f29d1SFedor Uporov 	uint16_t crc, dummy_csum;
693d23db91eSPedro F. Giffuni 
694d23db91eSPedro F. Giffuni 	offset = offsetof(struct ext2_gd, ext4bgd_csum);
695d23db91eSPedro F. Giffuni 
696512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
697512f29d1SFedor Uporov 		csum32 = calculate_crc32c(fs->e2fs_csum_seed,
698512f29d1SFedor Uporov 		    (uint8_t *)&block_group, sizeof(block_group));
699512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
700512f29d1SFedor Uporov 		dummy_csum = 0;
701512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
702512f29d1SFedor Uporov 		    sizeof(dummy_csum));
703512f29d1SFedor Uporov 		offset += sizeof(dummy_csum);
704512f29d1SFedor Uporov 		if (offset < fs->e2fs->e3fs_desc_size)
705512f29d1SFedor Uporov 			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
706512f29d1SFedor Uporov 			    fs->e2fs->e3fs_desc_size - offset);
707512f29d1SFedor Uporov 
708512f29d1SFedor Uporov 		crc = csum32 & 0xFFFF;
709512f29d1SFedor Uporov 		return (crc);
710512f29d1SFedor Uporov 	} else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
711d23db91eSPedro F. Giffuni 		crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid,
712d23db91eSPedro F. Giffuni 		    sizeof(fs->e2fs->e2fs_uuid));
713d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)&block_group,
714d23db91eSPedro F. Giffuni 		    sizeof(block_group));
715d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)gd, offset);
716d23db91eSPedro F. Giffuni 		offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
717d23db91eSPedro F. Giffuni 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
718d23db91eSPedro F. Giffuni 		    offset < fs->e2fs->e3fs_desc_size)
719d23db91eSPedro F. Giffuni 			crc = ext2_crc16(crc, (uint8_t *)gd + offset,
720d23db91eSPedro F. Giffuni 			    fs->e2fs->e3fs_desc_size - offset);
721d23db91eSPedro F. Giffuni 		return (crc);
722d23db91eSPedro F. Giffuni 	}
723d23db91eSPedro F. Giffuni 
724d23db91eSPedro F. Giffuni 	return (0);
725d23db91eSPedro F. Giffuni }
726d23db91eSPedro F. Giffuni 
727d23db91eSPedro F. Giffuni int
728d23db91eSPedro F. Giffuni ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
729d23db91eSPedro F. Giffuni {
730d23db91eSPedro F. Giffuni 	unsigned int i;
731d23db91eSPedro F. Giffuni 	int error = 0;
732d23db91eSPedro F. Giffuni 
733d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++) {
734d23db91eSPedro F. Giffuni 		if (fs->e2fs_gd[i].ext4bgd_csum !=
735d23db91eSPedro F. Giffuni 		    ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
736d23db91eSPedro F. Giffuni 			printf(
737d23db91eSPedro F. Giffuni "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
738d23db91eSPedro F. Giffuni 			    devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
739d23db91eSPedro F. Giffuni 			    ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
740512f29d1SFedor Uporov 			error = EIO;
741d23db91eSPedro F. Giffuni 			break;
742d23db91eSPedro F. Giffuni 		}
743d23db91eSPedro F. Giffuni 	}
744d23db91eSPedro F. Giffuni 
745d23db91eSPedro F. Giffuni 	return (error);
746d23db91eSPedro F. Giffuni }
747d23db91eSPedro F. Giffuni 
748d23db91eSPedro F. Giffuni void
749d23db91eSPedro F. Giffuni ext2_gd_csum_set(struct m_ext2fs *fs)
750d23db91eSPedro F. Giffuni {
751d23db91eSPedro F. Giffuni 	unsigned int i;
752d23db91eSPedro F. Giffuni 
753d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++)
754d23db91eSPedro F. Giffuni 		    fs->e2fs_gd[i].ext4bgd_csum =
755d23db91eSPedro F. Giffuni 			ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
756d23db91eSPedro F. Giffuni }
757