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