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