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