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