]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/ext2fs/ext2_csum.c
MFV r348552: 9682 page fault in dsl_async_clone_destroy() while opening pool
[FreeBSD/FreeBSD.git] / sys / fs / ext2fs / ext2_csum.c
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
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
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
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
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
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
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
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
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 *
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }