]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/ext2fs/ext2_csum.c
MFV r328231: 8897 zpool online -e fails assertion when run on non-leaf vdevs
[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_lo, old_hi;
539         uint32_t inum, gen, crc;
540
541         fs = ip->i_e2fs;
542
543         old_lo = ei->e2di_chksum_lo;
544         ei->e2di_chksum_lo = 0;
545         if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
546             ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
547                 old_hi = ei->e2di_chksum_hi;
548                 ei->e2di_chksum_hi = 0;
549         }
550
551         inum = ip->i_number;
552         gen = ip->i_gen;
553
554         crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
555         crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
556         crc = calculate_crc32c(crc, (uint8_t *)ei, fs->e2fs->e2fs_inode_size);
557
558         if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
559             ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
560                 ei->e2di_chksum_hi = old_hi;
561
562         return (crc);
563 }
564
565 int
566 ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
567 {
568         struct m_ext2fs *fs;
569         const static struct ext2fs_dinode ei_zero;
570         uint32_t hi, provided, calculated;
571
572         fs = ip->i_e2fs;
573
574         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
575                 return (0);
576
577         /* Check case, when dinode was not initialized */
578         if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
579                 return (0);
580
581         provided = ei->e2di_chksum_lo;
582         calculated = ext2_ei_csum(ip, ei);
583
584         if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
585             ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
586                 hi = ei->e2di_chksum_hi;
587                 provided |= hi << 16;
588         } else
589                 calculated &= 0xFFFF;
590
591         if (provided != calculated)
592                 return (EIO);
593
594         return (0);
595 }
596
597 void
598 ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
599 {
600         struct m_ext2fs *fs;
601         uint32_t crc;
602
603         fs = ip->i_e2fs;
604
605         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
606                 return;
607
608         crc = ext2_ei_csum(ip, ei);
609
610         ei->e2di_chksum_lo = crc & 0xFFFF;
611         if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
612             ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
613                 ei->e2di_chksum_hi = crc >> 16;
614 }
615
616 static uint16_t
617 ext2_crc16(uint16_t crc, const void *buffer, unsigned int len)
618 {
619         const unsigned char *cp = buffer;
620         /* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
621         static uint16_t const crc16_table[256] = {
622                 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
623                 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
624                 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
625                 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
626                 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
627                 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
628                 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
629                 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
630                 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
631                 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
632                 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
633                 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
634                 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
635                 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
636                 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
637                 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
638                 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
639                 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
640                 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
641                 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
642                 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
643                 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
644                 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
645                 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
646                 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
647                 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
648                 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
649                 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
650                 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
651                 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
652                 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
653                 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
654         };
655
656         while (len--)
657                 crc = (((crc >> 8) & 0xffU) ^
658                     crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
659         return crc;
660 }
661
662 static uint16_t
663 ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
664 {
665         size_t offset;
666         uint32_t csum32;
667         uint16_t crc, dummy_csum;
668
669         offset = offsetof(struct ext2_gd, ext4bgd_csum);
670
671         if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
672                 csum32 = calculate_crc32c(fs->e2fs_csum_seed,
673                     (uint8_t *)&block_group, sizeof(block_group));
674                 csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
675                 dummy_csum = 0;
676                 csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
677                     sizeof(dummy_csum));
678                 offset += sizeof(dummy_csum);
679                 if (offset < fs->e2fs->e3fs_desc_size)
680                         csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
681                             fs->e2fs->e3fs_desc_size - offset);
682
683                 crc = csum32 & 0xFFFF;
684                 return (crc);
685         } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
686                 crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid,
687                     sizeof(fs->e2fs->e2fs_uuid));
688                 crc = ext2_crc16(crc, (uint8_t *)&block_group,
689                     sizeof(block_group));
690                 crc = ext2_crc16(crc, (uint8_t *)gd, offset);
691                 offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
692                 if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
693                     offset < fs->e2fs->e3fs_desc_size)
694                         crc = ext2_crc16(crc, (uint8_t *)gd + offset,
695                             fs->e2fs->e3fs_desc_size - offset);
696                 return (crc);
697         }
698
699         return (0);
700 }
701
702 int
703 ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
704 {
705         unsigned int i;
706         int error = 0;
707
708         for (i = 0; i < fs->e2fs_gcount; i++) {
709                 if (fs->e2fs_gd[i].ext4bgd_csum !=
710                     ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
711                         printf(
712 "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
713                             devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
714                             ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
715                         error = EIO;
716                         break;
717                 }
718         }
719
720         return (error);
721 }
722
723 void
724 ext2_gd_csum_set(struct m_ext2fs *fs)
725 {
726         unsigned int i;
727
728         for (i = 0; i < fs->e2fs_gcount; i++)
729                     fs->e2fs_gd[i].ext4bgd_csum = 
730                         ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
731 }