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