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