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