xref: /freebsd/sys/fs/ext2fs/ext2_csum.c (revision d91f8db5f1822c43cd256f19aae1d059e4b25a26)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2017, Fedor Uporov
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/types.h>
34 #include <sys/sdt.h>
35 #include <sys/stat.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/vnode.h>
39 #include <sys/bio.h>
40 #include <sys/buf.h>
41 #include <sys/endian.h>
42 #include <sys/conf.h>
43 #include <sys/gsb_crc32.h>
44 #include <sys/crc16.h>
45 #include <sys/mount.h>
46 
47 #include <fs/ext2fs/fs.h>
48 #include <fs/ext2fs/ext2fs.h>
49 #include <fs/ext2fs/ext2_dinode.h>
50 #include <fs/ext2fs/inode.h>
51 #include <fs/ext2fs/ext2_dir.h>
52 #include <fs/ext2fs/htree.h>
53 #include <fs/ext2fs/ext2_extattr.h>
54 #include <fs/ext2fs/ext2_extern.h>
55 
56 SDT_PROVIDER_DECLARE(ext2fs);
57 /*
58  * ext2fs trace probe:
59  * arg0: verbosity. Higher numbers give more verbose messages
60  * arg1: Textual message
61  */
62 SDT_PROBE_DEFINE2(ext2fs, , trace, csum, "int", "char*");
63 
64 #define EXT2_BG_INODE_BITMAP_CSUM_HI_END	\
65 	(offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \
66 	 sizeof(uint16_t))
67 
68 #define EXT2_INODE_CSUM_HI_EXTRA_END	\
69 	(offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \
70 	 E2FS_REV0_INODE_SIZE)
71 
72 #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION	\
73 	(offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \
74 	 sizeof(uint16_t))
75 
76 void
77 ext2_sb_csum_set_seed(struct m_ext2fs *fs)
78 {
79 
80 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED))
81 		fs->e2fs_csum_seed = le32toh(fs->e2fs->e4fs_chksum_seed);
82 	else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
83 		fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid,
84 		    sizeof(fs->e2fs->e2fs_uuid));
85 	}
86 	else
87 		fs->e2fs_csum_seed = 0;
88 }
89 
90 int
91 ext2_sb_csum_verify(struct m_ext2fs *fs)
92 {
93 
94 	if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) {
95 		printf(
96 "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt);
97 		return (EINVAL);
98 	}
99 	if (le32toh(fs->e2fs->e4fs_sbchksum) !=
100 	    calculate_crc32c(~0, (const char *)fs->e2fs,
101 	    offsetof(struct ext2fs, e4fs_sbchksum))) {
102 		printf(
103 "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n",
104 		    fs->e2fs_fsmnt, le32toh(fs->e2fs->e4fs_sbchksum),
105 		    calculate_crc32c(~0, (const char *)fs->e2fs,
106 		    offsetof(struct ext2fs, e4fs_sbchksum)));
107 		return (EINVAL);
108 	}
109 
110 	return (0);
111 }
112 
113 void
114 ext2_sb_csum_set(struct m_ext2fs *fs)
115 {
116 
117 	fs->e2fs->e4fs_sbchksum =
118 	    htole32(calculate_crc32c(~0, (const char *)fs->e2fs,
119 	    offsetof(struct ext2fs, e4fs_sbchksum)));
120 }
121 
122 static uint32_t
123 ext2_extattr_blk_csum(struct inode *ip, uint64_t facl,
124     struct ext2fs_extattr_header *header)
125 {
126 	struct m_ext2fs *fs;
127 	uint32_t crc, dummy_crc = 0;
128 	uint64_t facl_bn = htole64(facl);
129 	int offset = offsetof(struct ext2fs_extattr_header, h_checksum);
130 
131 	fs = ip->i_e2fs;
132 
133 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl_bn,
134 	    sizeof(facl_bn));
135 	crc = calculate_crc32c(crc, (uint8_t *)header, offset);
136 	crc = calculate_crc32c(crc, (uint8_t *)&dummy_crc,
137 	    sizeof(dummy_crc));
138 	offset += sizeof(dummy_crc);
139 	crc = calculate_crc32c(crc, (uint8_t *)header + offset,
140 	    fs->e2fs_bsize - offset);
141 
142 	return (htole32(crc));
143 }
144 
145 int
146 ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp)
147 {
148 	struct ext2fs_extattr_header *header;
149 
150 	header = (struct ext2fs_extattr_header *)bp->b_data;
151 
152 	if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) &&
153 	    (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) {
154 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extattr csum detected");
155 		return (EIO);
156 	}
157 
158 	return (0);
159 }
160 
161 void
162 ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp)
163 {
164 	struct ext2fs_extattr_header *header;
165 
166 	if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
167 		return;
168 
169 	header = (struct ext2fs_extattr_header *)bp->b_data;
170 	header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header);
171 }
172 
173 void
174 ext2_init_dirent_tail(struct ext2fs_direct_tail *tp)
175 {
176 	memset(tp, 0, sizeof(struct ext2fs_direct_tail));
177 	tp->e2dt_rec_len = le16toh(sizeof(struct ext2fs_direct_tail));
178 	tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM;
179 }
180 
181 int
182 ext2_is_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
183 {
184 	struct m_ext2fs *fs;
185 	struct ext2fs_direct_tail *tp;
186 
187 	fs = ip->i_e2fs;
188 
189 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
190 		return (0);
191 
192 	tp = (struct ext2fs_direct_tail *)ep;
193 	if (tp->e2dt_reserved_zero1 == 0 &&
194 	    le16toh(tp->e2dt_rec_len) == sizeof(struct ext2fs_direct_tail) &&
195 	    tp->e2dt_reserved_zero2 == 0 &&
196 	    tp->e2dt_reserved_ft == EXT2_FT_DIR_CSUM)
197 		return (1);
198 
199 	return (0);
200 }
201 
202 struct ext2fs_direct_tail *
203 ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
204 {
205 	struct ext2fs_direct_2 *dep;
206 	void *top;
207 	unsigned int rec_len;
208 
209 	dep = ep;
210 	top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
211 	rec_len = le16toh(dep->e2d_reclen);
212 
213 	while (rec_len && !(rec_len & 0x3)) {
214 		dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len);
215 		if ((void *)dep >= top)
216 			break;
217 		rec_len = le16toh(dep->e2d_reclen);
218 	}
219 
220 	if (dep != top)
221 		return (NULL);
222 
223 	if (ext2_is_dirent_tail(ip, dep))
224 		return ((struct ext2fs_direct_tail *)dep);
225 
226 	return (NULL);
227 }
228 
229 static uint32_t
230 ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
231 {
232 	struct m_ext2fs *fs;
233 	char *buf;
234 	uint32_t inum, gen, crc;
235 
236 	fs = ip->i_e2fs;
237 
238 	buf = (char *)ep;
239 
240 	inum = htole32(ip->i_number);
241 	gen = htole32(ip->i_gen);
242 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
243 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
244 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
245 
246 	return (crc);
247 }
248 
249 int
250 ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
251 {
252 	uint32_t calculated;
253 	struct ext2fs_direct_tail *tp;
254 
255 	tp = ext2_dirent_get_tail(ip, ep);
256 	if (tp == NULL)
257 		return (0);
258 
259 	calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
260 	if (calculated != le32toh(tp->e2dt_checksum))
261 		return (EIO);
262 
263 	return (0);
264 }
265 
266 static struct ext2fs_htree_count *
267 ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset)
268 {
269 	struct ext2fs_direct_2 *dp;
270 	struct ext2fs_htree_root_info *root;
271 	int count_offset;
272 
273 	if (le16toh(ep->e2d_reclen) == EXT2_BLOCK_SIZE(ip->i_e2fs))
274 		count_offset = 8;
275 	else if (le16toh(ep->e2d_reclen) == 12) {
276 		dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12);
277 		if (le16toh(dp->e2d_reclen) != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12)
278 			return (NULL);
279 
280 		root = (struct ext2fs_htree_root_info *)(((char *)dp + 12));
281 		if (root->h_reserved1 ||
282 		    root->h_info_len != sizeof(struct ext2fs_htree_root_info))
283 			return (NULL);
284 
285 		count_offset = 32;
286 	} else
287 		return (NULL);
288 
289 	if (offset)
290 		*offset = count_offset;
291 
292 	return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset));
293 }
294 
295 static uint32_t
296 ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
297     int count, struct ext2fs_htree_tail *tp)
298 {
299 	struct m_ext2fs *fs;
300 	char *buf;
301 	int size;
302 	uint32_t inum, old_csum, gen, crc;
303 
304 	fs = ip->i_e2fs;
305 
306 	buf = (char *)ep;
307 
308 	size = count_offset + (count * sizeof(struct ext2fs_htree_entry));
309 	old_csum = tp->ht_checksum;
310 	tp->ht_checksum = 0;
311 
312 	inum = htole32(ip->i_number);
313 	gen = htole32(ip->i_gen);
314 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
315 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
316 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
317 	crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail));
318 	tp->ht_checksum = old_csum;
319 
320 	return htole32(crc);
321 }
322 
323 int
324 ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
325 {
326 	uint32_t calculated;
327 	struct ext2fs_htree_count *cp;
328 	struct ext2fs_htree_tail *tp;
329 	int count_offset, limit, count;
330 
331 	cp = ext2_get_dx_count(ip, ep, &count_offset);
332 	if (cp == NULL)
333 		return (0);
334 
335 	limit = le16toh(cp->h_entries_max);
336 	count = le16toh(cp->h_entries_num);
337 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
338 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
339 		return (EIO);
340 
341 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
342 	calculated = ext2_dx_csum(ip, ep,  count_offset, count, tp);
343 
344 	if (tp->ht_checksum != calculated)
345 		return (EIO);
346 
347 	return (0);
348 }
349 
350 int
351 ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
352 {
353 	struct m_ext2fs *fs;
354 	struct ext2fs_direct_2 *ep;
355 	int error = 0;
356 
357 	fs = ip->i_e2fs;
358 
359 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
360 		return (error);
361 
362 	ep = (struct ext2fs_direct_2 *)bp->b_data;
363 
364 	if (ext2_dirent_get_tail(ip, ep) != NULL)
365 		error = ext2_dirent_csum_verify(ip, ep);
366 	else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
367 		error = ext2_dx_csum_verify(ip, ep);
368 
369 	if (error)
370 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad directory csum detected");
371 
372 	return (error);
373 }
374 
375 void
376 ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
377 {
378 	struct m_ext2fs *fs;
379 	struct ext2fs_direct_tail *tp;
380 
381 	fs = ip->i_e2fs;
382 
383 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
384 		return;
385 
386 	tp = ext2_dirent_get_tail(ip, ep);
387 	if (tp == NULL)
388 		return;
389 
390 	tp->e2dt_checksum =
391 	    htole32(ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep));
392 }
393 
394 void
395 ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
396 {
397 	struct m_ext2fs *fs;
398 	struct ext2fs_htree_count *cp;
399 	struct ext2fs_htree_tail *tp;
400 	int count_offset, limit, count;
401 
402 	fs = ip->i_e2fs;
403 
404 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
405 		return;
406 
407 	cp = ext2_get_dx_count(ip, ep, &count_offset);
408 	if (cp == NULL)
409 		return;
410 
411 	limit = le16toh(cp->h_entries_max);
412 	count = le16toh(cp->h_entries_num);
413 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
414 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
415 		return;
416 
417 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
418 	tp->ht_checksum = ext2_dx_csum(ip, ep,  count_offset, count, tp);
419 }
420 
421 static uint32_t
422 ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
423 {
424 	struct m_ext2fs *fs;
425 	size_t size;
426 	uint32_t inum, gen, crc;
427 
428 	fs = ip->i_e2fs;
429 
430 	size = EXT4_EXTENT_TAIL_OFFSET(ehp) +
431 	    offsetof(struct ext4_extent_tail, et_checksum);
432 
433 	inum = htole32(ip->i_number);
434 	gen = htole32(ip->i_gen);
435 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
436 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
437 	crc = calculate_crc32c(crc, (uint8_t *)ehp, size);
438 
439 	return (crc);
440 }
441 
442 int
443 ext2_extent_blk_csum_verify(struct inode *ip, void *data)
444 {
445 	struct m_ext2fs *fs;
446 	struct ext4_extent_header *ehp;
447 	struct ext4_extent_tail *etp;
448 	uint32_t provided, calculated;
449 
450 	fs = ip->i_e2fs;
451 
452 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
453 		return (0);
454 
455 	ehp = (struct ext4_extent_header *)data;
456 	etp = (struct ext4_extent_tail *)(((char *)ehp) +
457 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
458 
459 	provided = le32toh(etp->et_checksum);
460 	calculated = ext2_extent_blk_csum(ip, ehp);
461 
462 	if (provided != calculated) {
463 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extent csum detected");
464 		return (EIO);
465 	}
466 
467 	return (0);
468 }
469 
470 void
471 ext2_extent_blk_csum_set(struct inode *ip, void *data)
472 {
473 	struct m_ext2fs *fs;
474 	struct ext4_extent_header *ehp;
475 	struct ext4_extent_tail *etp;
476 
477 	fs = ip->i_e2fs;
478 
479 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
480 		return;
481 
482 	ehp = (struct ext4_extent_header *)data;
483 	etp = (struct ext4_extent_tail *)(((char *)data) +
484 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
485 
486 	etp->et_checksum = htole32(ext2_extent_blk_csum(ip,
487 	    (struct ext4_extent_header *)data));
488 }
489 
490 int
491 ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
492 {
493 	uint32_t hi, provided, calculated;
494 
495 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
496 		return (0);
497 
498 	provided = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum);
499 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
500 	    fs->e2fs_ipg / 8);
501 	if (le16toh(fs->e2fs->e3fs_desc_size) >=
502 	    EXT2_BG_INODE_BITMAP_CSUM_HI_END) {
503 		hi = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi);
504 		provided |= (hi << 16);
505 	} else
506 		calculated &= 0xFFFF;
507 
508 	if (provided != calculated) {
509 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode bitmap csum detected");
510 		return (EIO);
511 	}
512 
513 	return (0);
514 }
515 
516 void
517 ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
518 {
519 	uint32_t csum;
520 
521 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
522 		return;
523 
524 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
525 	    fs->e2fs_ipg / 8);
526 	fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = htole16(csum & 0xFFFF);
527 	if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_INODE_BITMAP_CSUM_HI_END)
528 		fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = htole16(csum >> 16);
529 }
530 
531 int
532 ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
533 {
534 	uint32_t hi, provided, calculated, size;
535 
536 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
537 		return (0);
538 
539 	size = fs->e2fs_fpg / 8;
540 	provided = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum);
541 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
542 	if (le16toh(fs->e2fs->e3fs_desc_size) >=
543 	    EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) {
544 		hi = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi);
545 		provided |= (hi << 16);
546 	} else
547 		calculated &= 0xFFFF;
548 
549 	if (provided != calculated) {
550 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad block bitmap csum detected");
551 		return (EIO);
552 	}
553 
554 	return (0);
555 }
556 
557 void
558 ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
559 {
560 	uint32_t csum, size;
561 
562 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
563 		return;
564 
565 	size = fs->e2fs_fpg / 8;
566 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
567 	fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = htole16(csum & 0xFFFF);
568 	if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
569 		fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = htole16(csum >> 16);
570 }
571 
572 static uint32_t
573 ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
574 {
575 	struct m_ext2fs *fs;
576 	uint32_t inode_csum_seed, inum, gen, crc;
577 	uint16_t dummy_csum = 0;
578 	unsigned int offset, csum_size;
579 
580 	fs = ip->i_e2fs;
581 	offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo);
582 	csum_size = sizeof(dummy_csum);
583 	inum = htole32(ip->i_number);
584 	crc = calculate_crc32c(fs->e2fs_csum_seed,
585 	    (uint8_t *)&inum, sizeof(inum));
586 	gen = htole32(ip->i_gen);
587 	inode_csum_seed = calculate_crc32c(crc,
588 	    (uint8_t *)&gen, sizeof(gen));
589 
590 	crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset);
591 	crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size);
592 	offset += csum_size;
593 	crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
594 	    E2FS_REV0_INODE_SIZE - offset);
595 
596 	if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) {
597 		offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi);
598 		crc = calculate_crc32c(crc, (uint8_t *)ei +
599 		    E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE);
600 
601 		if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
602 		    le16toh(ei->e2di_extra_isize) >=
603 		    EXT2_INODE_CSUM_HI_EXTRA_END)) {
604 			crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum,
605 			    csum_size);
606 			offset += csum_size;
607 		}
608 
609 		crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
610 		    EXT2_INODE_SIZE(fs) - offset);
611 	}
612 
613 	return (crc);
614 }
615 
616 int
617 ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
618 {
619 	struct m_ext2fs *fs;
620 	const static struct ext2fs_dinode ei_zero;
621 	uint32_t hi, provided, calculated;
622 
623 	fs = ip->i_e2fs;
624 
625 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
626 		return (0);
627 
628 	provided = le16toh(ei->e2di_chksum_lo);
629 	calculated = ext2_ei_csum(ip, ei);
630 
631 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
632 	    le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
633 		hi = le16toh(ei->e2di_chksum_hi);
634 		provided |= hi << 16;
635 	} else
636 		calculated &= 0xFFFF;
637 
638 	if (provided != calculated) {
639 		/*
640 		 * If it is first time used dinode,
641 		 * it is expected that it will be zeroed
642 		 * and we will not return checksum error in this case.
643 		 */
644 		if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
645 			return (0);
646 
647 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode csum");
648 
649 		return (EIO);
650 	}
651 
652 	return (0);
653 }
654 
655 void
656 ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
657 {
658 	struct m_ext2fs *fs;
659 	uint32_t crc;
660 
661 	fs = ip->i_e2fs;
662 
663 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
664 		return;
665 
666 	crc = ext2_ei_csum(ip, ei);
667 
668 	ei->e2di_chksum_lo = htole16(crc & 0xFFFF);
669 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
670 	    le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END))
671 		ei->e2di_chksum_hi = htole16(crc >> 16);
672 }
673 
674 static uint16_t
675 ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
676 {
677 	size_t offset;
678 	uint32_t csum32;
679 	uint16_t crc, dummy_csum;
680 
681 	offset = offsetof(struct ext2_gd, ext4bgd_csum);
682 
683 	block_group = htole32(block_group);
684 
685 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
686 		csum32 = calculate_crc32c(fs->e2fs_csum_seed,
687 		    (uint8_t *)&block_group, sizeof(block_group));
688 		csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
689 		dummy_csum = 0;
690 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
691 		    sizeof(dummy_csum));
692 		offset += sizeof(dummy_csum);
693 		if (offset < le16toh(fs->e2fs->e3fs_desc_size))
694 			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
695 			    le16toh(fs->e2fs->e3fs_desc_size) - offset);
696 
697 		crc = csum32 & 0xFFFF;
698 		return (htole16(crc));
699 	} else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
700 		crc = crc16(~0, fs->e2fs->e2fs_uuid,
701 		    sizeof(fs->e2fs->e2fs_uuid));
702 		crc = crc16(crc, (uint8_t *)&block_group,
703 		    sizeof(block_group));
704 		crc = crc16(crc, (uint8_t *)gd, offset);
705 		offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
706 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
707 		    offset < le16toh(fs->e2fs->e3fs_desc_size))
708 			crc = crc16(crc, (uint8_t *)gd + offset,
709 			    le16toh(fs->e2fs->e3fs_desc_size) - offset);
710 		return (htole16(crc));
711 	}
712 
713 	return (0);
714 }
715 
716 int
717 ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
718 {
719 	unsigned int i;
720 	int error = 0;
721 
722 	for (i = 0; i < fs->e2fs_gcount; i++) {
723 		if (fs->e2fs_gd[i].ext4bgd_csum !=
724 		    ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
725 			printf(
726 "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
727 			    devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
728 			    ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
729 			error = EIO;
730 			break;
731 		}
732 	}
733 
734 	return (error);
735 }
736 
737 void
738 ext2_gd_csum_set(struct m_ext2fs *fs)
739 {
740 	unsigned int i;
741 
742 	for (i = 0; i < fs->e2fs_gcount; i++)
743 		fs->e2fs_gd[i].ext4bgd_csum = ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
744 }
745