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