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