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