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