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