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