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