xref: /freebsd/sys/fs/ext2fs/ext2_csum.c (revision 137a344c6341d1469432e9deb3a25593f96672ad)
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_lo, old_hi;
539 	uint32_t inum, gen, crc;
540 
541 	fs = ip->i_e2fs;
542 
543 	old_lo = ei->e2di_chksum_lo;
544 	ei->e2di_chksum_lo = 0;
545 	if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
546 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
547 		old_hi = ei->e2di_chksum_hi;
548 		ei->e2di_chksum_hi = 0;
549 	}
550 
551 	inum = ip->i_number;
552 	gen = ip->i_gen;
553 
554 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
555 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
556 	crc = calculate_crc32c(crc, (uint8_t *)ei, fs->e2fs->e2fs_inode_size);
557 
558 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
559 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
560 		ei->e2di_chksum_hi = old_hi;
561 
562 	return (crc);
563 }
564 
565 int
566 ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
567 {
568 	struct m_ext2fs *fs;
569 	const static struct ext2fs_dinode ei_zero;
570 	uint32_t hi, provided, calculated;
571 
572 	fs = ip->i_e2fs;
573 
574 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
575 		return (0);
576 
577 	/* Check case, when dinode was not initialized */
578 	if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
579 		return (0);
580 
581 	provided = ei->e2di_chksum_lo;
582 	calculated = ext2_ei_csum(ip, ei);
583 
584 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
585 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
586 		hi = ei->e2di_chksum_hi;
587 		provided |= hi << 16;
588 	} else
589 		calculated &= 0xFFFF;
590 
591 	if (provided != calculated)
592 		return (EIO);
593 
594 	return (0);
595 }
596 
597 void
598 ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
599 {
600 	struct m_ext2fs *fs;
601 	uint32_t crc;
602 
603 	fs = ip->i_e2fs;
604 
605 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
606 		return;
607 
608 	crc = ext2_ei_csum(ip, ei);
609 
610 	ei->e2di_chksum_lo = crc & 0xFFFF;
611 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
612 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
613 		ei->e2di_chksum_hi = crc >> 16;
614 }
615 
616 static uint16_t
617 ext2_crc16(uint16_t crc, const void *buffer, unsigned int len)
618 {
619 	const unsigned char *cp = buffer;
620 	/* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
621 	static uint16_t const crc16_table[256] = {
622 		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
623 		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
624 		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
625 		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
626 		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
627 		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
628 		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
629 		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
630 		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
631 		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
632 		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
633 		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
634 		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
635 		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
636 		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
637 		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
638 		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
639 		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
640 		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
641 		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
642 		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
643 		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
644 		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
645 		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
646 		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
647 		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
648 		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
649 		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
650 		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
651 		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
652 		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
653 		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
654 	};
655 
656 	while (len--)
657 		crc = (((crc >> 8) & 0xffU) ^
658 		    crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
659 	return crc;
660 }
661 
662 static uint16_t
663 ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
664 {
665 	size_t offset;
666 	uint32_t csum32;
667 	uint16_t crc, dummy_csum;
668 
669 	offset = offsetof(struct ext2_gd, ext4bgd_csum);
670 
671 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
672 		csum32 = calculate_crc32c(fs->e2fs_csum_seed,
673 		    (uint8_t *)&block_group, sizeof(block_group));
674 		csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
675 		dummy_csum = 0;
676 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
677 		    sizeof(dummy_csum));
678 		offset += sizeof(dummy_csum);
679 		if (offset < fs->e2fs->e3fs_desc_size)
680 			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
681 			    fs->e2fs->e3fs_desc_size - offset);
682 
683 		crc = csum32 & 0xFFFF;
684 		return (crc);
685 	} else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
686 		crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid,
687 		    sizeof(fs->e2fs->e2fs_uuid));
688 		crc = ext2_crc16(crc, (uint8_t *)&block_group,
689 		    sizeof(block_group));
690 		crc = ext2_crc16(crc, (uint8_t *)gd, offset);
691 		offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
692 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
693 		    offset < fs->e2fs->e3fs_desc_size)
694 			crc = ext2_crc16(crc, (uint8_t *)gd + offset,
695 			    fs->e2fs->e3fs_desc_size - offset);
696 		return (crc);
697 	}
698 
699 	return (0);
700 }
701 
702 int
703 ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
704 {
705 	unsigned int i;
706 	int error = 0;
707 
708 	for (i = 0; i < fs->e2fs_gcount; i++) {
709 		if (fs->e2fs_gd[i].ext4bgd_csum !=
710 		    ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
711 			printf(
712 "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
713 			    devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
714 			    ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
715 			error = EIO;
716 			break;
717 		}
718 	}
719 
720 	return (error);
721 }
722 
723 void
724 ext2_gd_csum_set(struct m_ext2fs *fs)
725 {
726 	unsigned int i;
727 
728 	for (i = 0; i < fs->e2fs_gcount; i++)
729 		    fs->e2fs_gd[i].ext4bgd_csum =
730 			ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
731 }
732