]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/ext2fs/ext2_csum.c
ldns: Upgrade to 1.8.3.
[FreeBSD/FreeBSD.git] / sys / fs / ext2fs / ext2_csum.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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/crc16.h>
45 #include <sys/mount.h>
46
47 #include <fs/ext2fs/fs.h>
48 #include <fs/ext2fs/ext2fs.h>
49 #include <fs/ext2fs/ext2_dinode.h>
50 #include <fs/ext2fs/inode.h>
51 #include <fs/ext2fs/ext2_dir.h>
52 #include <fs/ext2fs/htree.h>
53 #include <fs/ext2fs/ext2_extattr.h>
54 #include <fs/ext2fs/ext2_extern.h>
55
56 SDT_PROVIDER_DECLARE(ext2fs);
57 /*
58  * ext2fs trace probe:
59  * arg0: verbosity. Higher numbers give more verbose messages
60  * arg1: Textual message
61  */
62 SDT_PROBE_DEFINE2(ext2fs, , trace, csum, "int", "char*");
63
64 #define EXT2_BG_INODE_BITMAP_CSUM_HI_END        \
65         (offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \
66          sizeof(uint16_t))
67
68 #define EXT2_INODE_CSUM_HI_EXTRA_END    \
69         (offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \
70          E2FS_REV0_INODE_SIZE)
71
72 #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION   \
73         (offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \
74          sizeof(uint16_t))
75
76 void
77 ext2_sb_csum_set_seed(struct m_ext2fs *fs)
78 {
79
80         if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED))
81                 fs->e2fs_csum_seed = le32toh(fs->e2fs->e4fs_chksum_seed);
82         else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
83                 fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid,
84                     sizeof(fs->e2fs->e2fs_uuid));
85         }
86         else
87                 fs->e2fs_csum_seed = 0;
88 }
89
90 int
91 ext2_sb_csum_verify(struct m_ext2fs *fs)
92 {
93
94         if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) {
95                 printf(
96 "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt);
97                 return (EINVAL);
98         }
99         if (le32toh(fs->e2fs->e4fs_sbchksum) !=
100             calculate_crc32c(~0, (const char *)fs->e2fs,
101             offsetof(struct ext2fs, e4fs_sbchksum))) {
102                 printf(
103 "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n",
104                     fs->e2fs_fsmnt, le32toh(fs->e2fs->e4fs_sbchksum),
105                     calculate_crc32c(~0, (const char *)fs->e2fs,
106                     offsetof(struct ext2fs, e4fs_sbchksum)));
107                 return (EINVAL);
108         }
109
110         return (0);
111 }
112
113 void
114 ext2_sb_csum_set(struct m_ext2fs *fs)
115 {
116
117         fs->e2fs->e4fs_sbchksum =
118             htole32(calculate_crc32c(~0, (const char *)fs->e2fs,
119             offsetof(struct ext2fs, e4fs_sbchksum)));
120 }
121
122 static uint32_t
123 ext2_extattr_blk_csum(struct inode *ip, uint64_t facl,
124     struct ext2fs_extattr_header *header)
125 {
126         struct m_ext2fs *fs;
127         uint32_t crc, dummy_crc = 0;
128         uint64_t facl_bn = htole64(facl);
129         int offset = offsetof(struct ext2fs_extattr_header, h_checksum);
130
131         fs = ip->i_e2fs;
132
133         crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl_bn,
134             sizeof(facl_bn));
135         crc = calculate_crc32c(crc, (uint8_t *)header, offset);
136         crc = calculate_crc32c(crc, (uint8_t *)&dummy_crc,
137             sizeof(dummy_crc));
138         offset += sizeof(dummy_crc);
139         crc = calculate_crc32c(crc, (uint8_t *)header + offset,
140             fs->e2fs_bsize - offset);
141
142         return (htole32(crc));
143 }
144
145 int
146 ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp)
147 {
148         struct ext2fs_extattr_header *header;
149
150         header = (struct ext2fs_extattr_header *)bp->b_data;
151
152         if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) &&
153             (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) {
154                 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extattr csum detected");
155                 return (EIO);
156         }
157
158         return (0);
159 }
160
161 void
162 ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp)
163 {
164         struct ext2fs_extattr_header *header;
165
166         if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
167                 return;
168
169         header = (struct ext2fs_extattr_header *)bp->b_data;
170         header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header);
171 }
172
173 void
174 ext2_init_dirent_tail(struct ext2fs_direct_tail *tp)
175 {
176         memset(tp, 0, sizeof(struct ext2fs_direct_tail));
177         tp->e2dt_rec_len = le16toh(sizeof(struct ext2fs_direct_tail));
178         tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM;
179 }
180
181 int
182 ext2_is_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
183 {
184         struct m_ext2fs *fs;
185         struct ext2fs_direct_tail *tp;
186
187         fs = ip->i_e2fs;
188
189         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
190                 return (0);
191
192         tp = (struct ext2fs_direct_tail *)ep;
193         if (tp->e2dt_reserved_zero1 == 0 &&
194             le16toh(tp->e2dt_rec_len) == sizeof(struct ext2fs_direct_tail) &&
195             tp->e2dt_reserved_zero2 == 0 &&
196             tp->e2dt_reserved_ft == EXT2_FT_DIR_CSUM)
197                 return (1);
198
199         return (0);
200 }
201
202 struct ext2fs_direct_tail *
203 ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
204 {
205         struct ext2fs_direct_2 *dep;
206         void *top;
207         unsigned int rec_len;
208
209         dep = ep;
210         top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
211         rec_len = le16toh(dep->e2d_reclen);
212
213         while (rec_len && !(rec_len & 0x3)) {
214                 dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len);
215                 if ((void *)dep >= top)
216                         break;
217                 rec_len = le16toh(dep->e2d_reclen);
218         }
219
220         if (dep != top)
221                 return (NULL);
222
223         if (ext2_is_dirent_tail(ip, dep))
224                 return ((struct ext2fs_direct_tail *)dep);
225
226         return (NULL);
227 }
228
229 static uint32_t
230 ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
231 {
232         struct m_ext2fs *fs;
233         char *buf;
234         uint32_t inum, gen, crc;
235
236         fs = ip->i_e2fs;
237
238         buf = (char *)ep;
239
240         inum = htole32(ip->i_number);
241         gen = htole32(ip->i_gen);
242         crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
243         crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
244         crc = calculate_crc32c(crc, (uint8_t *)buf, size);
245
246         return (crc);
247 }
248
249 int
250 ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
251 {
252         uint32_t calculated;
253         struct ext2fs_direct_tail *tp;
254
255         tp = ext2_dirent_get_tail(ip, ep);
256         if (tp == NULL)
257                 return (0);
258
259         calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
260         if (calculated != le32toh(tp->e2dt_checksum))
261                 return (EIO);
262
263         return (0);
264 }
265
266 static struct ext2fs_htree_count *
267 ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset)
268 {
269         struct ext2fs_direct_2 *dp;
270         struct ext2fs_htree_root_info *root;
271         int count_offset;
272
273         if (le16toh(ep->e2d_reclen) == EXT2_BLOCK_SIZE(ip->i_e2fs))
274                 count_offset = 8;
275         else if (le16toh(ep->e2d_reclen) == 12) {
276                 dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12);
277                 if (le16toh(dp->e2d_reclen) != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12)
278                         return (NULL);
279
280                 root = (struct ext2fs_htree_root_info *)(((char *)dp + 12));
281                 if (root->h_reserved1 ||
282                     root->h_info_len != sizeof(struct ext2fs_htree_root_info))
283                         return (NULL);
284
285                 count_offset = 32;
286         } else
287                 return (NULL);
288
289         if (offset)
290                 *offset = count_offset;
291
292         return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset));
293 }
294
295 static uint32_t
296 ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
297     int count, struct ext2fs_htree_tail *tp)
298 {
299         struct m_ext2fs *fs;
300         char *buf;
301         int size;
302         uint32_t inum, old_csum, gen, crc;
303
304         fs = ip->i_e2fs;
305
306         buf = (char *)ep;
307
308         size = count_offset + (count * sizeof(struct ext2fs_htree_entry));
309         old_csum = tp->ht_checksum;
310         tp->ht_checksum = 0;
311
312         inum = htole32(ip->i_number);
313         gen = htole32(ip->i_gen);
314         crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
315         crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
316         crc = calculate_crc32c(crc, (uint8_t *)buf, size);
317         crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail));
318         tp->ht_checksum = old_csum;
319
320         return htole32(crc);
321 }
322
323 int
324 ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
325 {
326         uint32_t calculated;
327         struct ext2fs_htree_count *cp;
328         struct ext2fs_htree_tail *tp;
329         int count_offset, limit, count;
330
331         cp = ext2_get_dx_count(ip, ep, &count_offset);
332         if (cp == NULL)
333                 return (0);
334
335         limit = le16toh(cp->h_entries_max);
336         count = le16toh(cp->h_entries_num);
337         if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
338             ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
339                 return (EIO);
340
341         tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
342         calculated = ext2_dx_csum(ip, ep,  count_offset, count, tp);
343
344         if (tp->ht_checksum != calculated)
345                 return (EIO);
346
347         return (0);
348 }
349
350 int
351 ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
352 {
353         struct m_ext2fs *fs;
354         struct ext2fs_direct_2 *ep;
355         int error = 0;
356
357         fs = ip->i_e2fs;
358
359         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
360                 return (error);
361
362         ep = (struct ext2fs_direct_2 *)bp->b_data;
363
364         if (ext2_dirent_get_tail(ip, ep) != NULL)
365                 error = ext2_dirent_csum_verify(ip, ep);
366         else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
367                 error = ext2_dx_csum_verify(ip, ep);
368
369         if (error)
370                 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad directory csum detected");
371
372         return (error);
373 }
374
375 void
376 ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
377 {
378         struct m_ext2fs *fs;
379         struct ext2fs_direct_tail *tp;
380
381         fs = ip->i_e2fs;
382
383         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
384                 return;
385
386         tp = ext2_dirent_get_tail(ip, ep);
387         if (tp == NULL)
388                 return;
389
390         tp->e2dt_checksum =
391             htole32(ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep));
392 }
393
394 void
395 ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
396 {
397         struct m_ext2fs *fs;
398         struct ext2fs_htree_count *cp;
399         struct ext2fs_htree_tail *tp;
400         int count_offset, limit, count;
401
402         fs = ip->i_e2fs;
403
404         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
405                 return;
406
407         cp = ext2_get_dx_count(ip, ep, &count_offset);
408         if (cp == NULL)
409                 return;
410
411         limit = le16toh(cp->h_entries_max);
412         count = le16toh(cp->h_entries_num);
413         if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
414             ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
415                 return;
416
417         tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
418         tp->ht_checksum = ext2_dx_csum(ip, ep,  count_offset, count, tp);
419 }
420
421 static uint32_t
422 ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
423 {
424         struct m_ext2fs *fs;
425         size_t size;
426         uint32_t inum, gen, crc;
427
428         fs = ip->i_e2fs;
429
430         size = EXT4_EXTENT_TAIL_OFFSET(ehp) +
431             offsetof(struct ext4_extent_tail, et_checksum);
432
433         inum = htole32(ip->i_number);
434         gen = htole32(ip->i_gen);
435         crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
436         crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
437         crc = calculate_crc32c(crc, (uint8_t *)ehp, size);
438
439         return (crc);
440 }
441
442 int
443 ext2_extent_blk_csum_verify(struct inode *ip, void *data)
444 {
445         struct m_ext2fs *fs;
446         struct ext4_extent_header *ehp;
447         struct ext4_extent_tail *etp;
448         uint32_t provided, calculated;
449
450         fs = ip->i_e2fs;
451
452         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
453                 return (0);
454
455         ehp = (struct ext4_extent_header *)data;
456         etp = (struct ext4_extent_tail *)(((char *)ehp) +
457             EXT4_EXTENT_TAIL_OFFSET(ehp));
458
459         provided = le32toh(etp->et_checksum);
460         calculated = ext2_extent_blk_csum(ip, ehp);
461
462         if (provided != calculated) {
463                 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extent csum detected");
464                 return (EIO);
465         }
466
467         return (0);
468 }
469
470 void
471 ext2_extent_blk_csum_set(struct inode *ip, void *data)
472 {
473         struct m_ext2fs *fs;
474         struct ext4_extent_header *ehp;
475         struct ext4_extent_tail *etp;
476
477         fs = ip->i_e2fs;
478
479         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
480                 return;
481
482         ehp = (struct ext4_extent_header *)data;
483         etp = (struct ext4_extent_tail *)(((char *)data) +
484             EXT4_EXTENT_TAIL_OFFSET(ehp));
485
486         etp->et_checksum = htole32(ext2_extent_blk_csum(ip,
487             (struct ext4_extent_header *)data));
488 }
489
490 int
491 ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
492 {
493         uint32_t hi, provided, calculated;
494
495         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
496                 return (0);
497
498         provided = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum);
499         calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
500             fs->e2fs_ipg / 8);
501         if (le16toh(fs->e2fs->e3fs_desc_size) >=
502             EXT2_BG_INODE_BITMAP_CSUM_HI_END) {
503                 hi = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi);
504                 provided |= (hi << 16);
505         } else
506                 calculated &= 0xFFFF;
507
508         if (provided != calculated) {
509                 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode bitmap csum detected");
510                 return (EIO);
511         }
512
513         return (0);
514 }
515
516 void
517 ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
518 {
519         uint32_t csum;
520
521         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
522                 return;
523
524         csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
525             fs->e2fs_ipg / 8);
526         fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = htole16(csum & 0xFFFF);
527         if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_INODE_BITMAP_CSUM_HI_END)
528                 fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = htole16(csum >> 16);
529 }
530
531 int
532 ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
533 {
534         uint32_t hi, provided, calculated, size;
535
536         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
537                 return (0);
538
539         size = fs->e2fs_fpg / 8;
540         provided = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum);
541         calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
542         if (le16toh(fs->e2fs->e3fs_desc_size) >=
543             EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) {
544                 hi = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi);
545                 provided |= (hi << 16);
546         } else
547                 calculated &= 0xFFFF;
548
549         if (provided != calculated) {
550                 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad block bitmap csum detected");
551                 return (EIO);
552         }
553
554         return (0);
555 }
556
557 void
558 ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
559 {
560         uint32_t csum, size;
561
562         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
563                 return;
564
565         size = fs->e2fs_fpg / 8;
566         csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
567         fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = htole16(csum & 0xFFFF);
568         if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
569                 fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = htole16(csum >> 16);
570 }
571
572 static uint32_t
573 ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
574 {
575         struct m_ext2fs *fs;
576         uint32_t inode_csum_seed, inum, gen, crc;
577         uint16_t dummy_csum = 0;
578         unsigned int offset, csum_size;
579
580         fs = ip->i_e2fs;
581         offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo);
582         csum_size = sizeof(dummy_csum);
583         inum = htole32(ip->i_number);
584         crc = calculate_crc32c(fs->e2fs_csum_seed,
585             (uint8_t *)&inum, sizeof(inum));
586         gen = htole32(ip->i_gen);
587         inode_csum_seed = calculate_crc32c(crc,
588             (uint8_t *)&gen, sizeof(gen));
589
590         crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset);
591         crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size);
592         offset += csum_size;
593         crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
594             E2FS_REV0_INODE_SIZE - offset);
595
596         if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) {
597                 offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi);
598                 crc = calculate_crc32c(crc, (uint8_t *)ei +
599                     E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE);
600
601                 if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
602                     le16toh(ei->e2di_extra_isize) >=
603                     EXT2_INODE_CSUM_HI_EXTRA_END)) {
604                         crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum,
605                             csum_size);
606                         offset += csum_size;
607                 }
608
609                 crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
610                     EXT2_INODE_SIZE(fs) - offset);
611         }
612
613         return (crc);
614 }
615
616 int
617 ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
618 {
619         struct m_ext2fs *fs;
620         const static struct ext2fs_dinode ei_zero;
621         uint32_t hi, provided, calculated;
622
623         fs = ip->i_e2fs;
624
625         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
626                 return (0);
627
628         provided = le16toh(ei->e2di_chksum_lo);
629         calculated = ext2_ei_csum(ip, ei);
630
631         if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
632             le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
633                 hi = le16toh(ei->e2di_chksum_hi);
634                 provided |= hi << 16;
635         } else
636                 calculated &= 0xFFFF;
637
638         if (provided != calculated) {
639                 /*
640                  * If it is first time used dinode,
641                  * it is expected that it will be zeroed
642                  * and we will not return checksum error in this case.
643                  */
644                 if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
645                         return (0);
646
647                 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode csum");
648
649                 return (EIO);
650         }
651
652         return (0);
653 }
654
655 void
656 ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
657 {
658         struct m_ext2fs *fs;
659         uint32_t crc;
660
661         fs = ip->i_e2fs;
662
663         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
664                 return;
665
666         crc = ext2_ei_csum(ip, ei);
667
668         ei->e2di_chksum_lo = htole16(crc & 0xFFFF);
669         if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
670             le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END))
671                 ei->e2di_chksum_hi = htole16(crc >> 16);
672 }
673
674 static uint16_t
675 ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
676 {
677         size_t offset;
678         uint32_t csum32;
679         uint16_t crc, dummy_csum;
680
681         offset = offsetof(struct ext2_gd, ext4bgd_csum);
682
683         block_group = htole32(block_group);
684
685         if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
686                 csum32 = calculate_crc32c(fs->e2fs_csum_seed,
687                     (uint8_t *)&block_group, sizeof(block_group));
688                 csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
689                 dummy_csum = 0;
690                 csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
691                     sizeof(dummy_csum));
692                 offset += sizeof(dummy_csum);
693                 if (offset < le16toh(fs->e2fs->e3fs_desc_size))
694                         csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
695                             le16toh(fs->e2fs->e3fs_desc_size) - offset);
696
697                 crc = csum32 & 0xFFFF;
698                 return (htole16(crc));
699         } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
700                 crc = crc16(~0, fs->e2fs->e2fs_uuid,
701                     sizeof(fs->e2fs->e2fs_uuid));
702                 crc = crc16(crc, (uint8_t *)&block_group,
703                     sizeof(block_group));
704                 crc = crc16(crc, (uint8_t *)gd, offset);
705                 offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
706                 if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
707                     offset < le16toh(fs->e2fs->e3fs_desc_size))
708                         crc = crc16(crc, (uint8_t *)gd + offset,
709                             le16toh(fs->e2fs->e3fs_desc_size) - offset);
710                 return (htole16(crc));
711         }
712
713         return (0);
714 }
715
716 int
717 ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
718 {
719         unsigned int i;
720         int error = 0;
721
722         for (i = 0; i < fs->e2fs_gcount; i++) {
723                 if (fs->e2fs_gd[i].ext4bgd_csum !=
724                     ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
725                         printf(
726 "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
727                             devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
728                             ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
729                         error = EIO;
730                         break;
731                 }
732         }
733
734         return (error);
735 }
736
737 void
738 ext2_gd_csum_set(struct m_ext2fs *fs)
739 {
740         unsigned int i;
741
742         for (i = 0; i < fs->e2fs_gcount; i++)
743                 fs->e2fs_gd[i].ext4bgd_csum = ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
744 }