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