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