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