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