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