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