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