xref: /freebsd/sys/fs/ext2fs/ext2_csum.c (revision f89d2072795407d7c3afff865b988e021c1451a2)
1d23db91eSPedro F. Giffuni /*-
27abc09cdSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37abc09cdSPedro F. Giffuni  *
4d23db91eSPedro F. Giffuni  * Copyright (c) 2017, Fedor Uporov
5d23db91eSPedro F. Giffuni  * All rights reserved.
6d23db91eSPedro F. Giffuni  *
7d23db91eSPedro F. Giffuni  * Redistribution and use in source and binary forms, with or without
8d23db91eSPedro F. Giffuni  * modification, are permitted provided that the following conditions
9d23db91eSPedro F. Giffuni  * are met:
10d23db91eSPedro F. Giffuni  * 1. Redistributions of source code must retain the above copyright
11d23db91eSPedro F. Giffuni  *    notice, this list of conditions and the following disclaimer.
12d23db91eSPedro F. Giffuni  * 2. Redistributions in binary form must reproduce the above copyright
13d23db91eSPedro F. Giffuni  *    notice, this list of conditions and the following disclaimer in the
14d23db91eSPedro F. Giffuni  *    documentation and/or other materials provided with the distribution.
15d23db91eSPedro F. Giffuni  *
16d23db91eSPedro F. Giffuni  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17d23db91eSPedro F. Giffuni  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d23db91eSPedro F. Giffuni  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d23db91eSPedro F. Giffuni  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20d23db91eSPedro F. Giffuni  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d23db91eSPedro F. Giffuni  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d23db91eSPedro F. Giffuni  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d23db91eSPedro F. Giffuni  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d23db91eSPedro F. Giffuni  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d23db91eSPedro F. Giffuni  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d23db91eSPedro F. Giffuni  * SUCH DAMAGE.
27d23db91eSPedro F. Giffuni  *
28d23db91eSPedro F. Giffuni  * $FreeBSD$
29d23db91eSPedro F. Giffuni  */
30d23db91eSPedro F. Giffuni 
31d23db91eSPedro F. Giffuni #include <sys/param.h>
32d23db91eSPedro F. Giffuni #include <sys/systm.h>
33d23db91eSPedro F. Giffuni #include <sys/types.h>
34ebc94b66SFedor Uporov #include <sys/sdt.h>
35d23db91eSPedro F. Giffuni #include <sys/stat.h>
36d23db91eSPedro F. Giffuni #include <sys/kernel.h>
37d23db91eSPedro F. Giffuni #include <sys/malloc.h>
38d23db91eSPedro F. Giffuni #include <sys/vnode.h>
39d23db91eSPedro F. Giffuni #include <sys/bio.h>
40d23db91eSPedro F. Giffuni #include <sys/buf.h>
41d23db91eSPedro F. Giffuni #include <sys/endian.h>
42d23db91eSPedro F. Giffuni #include <sys/conf.h>
43*f89d2072SXin LI #include <sys/gsb_crc32.h>
44d23db91eSPedro F. Giffuni #include <sys/mount.h>
45d23db91eSPedro F. Giffuni 
46d23db91eSPedro F. Giffuni #include <fs/ext2fs/fs.h>
47d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2fs.h>
48512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dinode.h>
49d23db91eSPedro F. Giffuni #include <fs/ext2fs/inode.h>
50512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dir.h>
51512f29d1SFedor Uporov #include <fs/ext2fs/htree.h>
52512f29d1SFedor Uporov #include <fs/ext2fs/ext2_extattr.h>
53d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h>
54d23db91eSPedro F. Giffuni 
55ebc94b66SFedor Uporov SDT_PROVIDER_DECLARE(ext2fs);
56ebc94b66SFedor Uporov /*
57ebc94b66SFedor Uporov  * ext2fs trace probe:
58ebc94b66SFedor Uporov  * arg0: verbosity. Higher numbers give more verbose messages
59ebc94b66SFedor Uporov  * arg1: Textual message
60ebc94b66SFedor Uporov  */
61ebc94b66SFedor Uporov SDT_PROBE_DEFINE2(ext2fs, , trace, csum, "int", "char*");
62ebc94b66SFedor Uporov 
63512f29d1SFedor Uporov #define EXT2_BG_INODE_BITMAP_CSUM_HI_END	\
64512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \
65512f29d1SFedor Uporov 	 sizeof(uint16_t))
66512f29d1SFedor Uporov 
67512f29d1SFedor Uporov #define EXT2_INODE_CSUM_HI_EXTRA_END	\
68512f29d1SFedor Uporov 	(offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \
69512f29d1SFedor Uporov 	 E2FS_REV0_INODE_SIZE)
70512f29d1SFedor Uporov 
71512f29d1SFedor Uporov #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION	\
72512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \
73512f29d1SFedor Uporov 	 sizeof(uint16_t))
74512f29d1SFedor Uporov 
75512f29d1SFedor Uporov void
76512f29d1SFedor Uporov ext2_sb_csum_set_seed(struct m_ext2fs *fs)
77512f29d1SFedor Uporov {
78512f29d1SFedor Uporov 
79512f29d1SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED))
80512f29d1SFedor Uporov 		fs->e2fs_csum_seed = fs->e2fs->e4fs_chksum_seed;
81512f29d1SFedor Uporov 	else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
82512f29d1SFedor Uporov 		fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid,
83512f29d1SFedor Uporov 		    sizeof(fs->e2fs->e2fs_uuid));
84512f29d1SFedor Uporov 	}
85512f29d1SFedor Uporov 	else
86512f29d1SFedor Uporov 		fs->e2fs_csum_seed = 0;
87512f29d1SFedor Uporov }
88512f29d1SFedor Uporov 
89512f29d1SFedor Uporov int
90512f29d1SFedor Uporov ext2_sb_csum_verify(struct m_ext2fs *fs)
91512f29d1SFedor Uporov {
92512f29d1SFedor Uporov 
93512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) {
94512f29d1SFedor Uporov 		printf(
95512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt);
96512f29d1SFedor Uporov 		return (EINVAL);
97512f29d1SFedor Uporov 	}
98512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_sbchksum !=
99512f29d1SFedor Uporov 	    calculate_crc32c(~0, (const char *)fs->e2fs,
100512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum))) {
101512f29d1SFedor Uporov 		printf(
102512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n",
103512f29d1SFedor Uporov 		    fs->e2fs_fsmnt, fs->e2fs->e4fs_sbchksum, calculate_crc32c(~0,
104512f29d1SFedor Uporov 		    (const char *)fs->e2fs, offsetof(struct ext2fs, e4fs_sbchksum)));
105512f29d1SFedor Uporov 		return (EINVAL);
106512f29d1SFedor Uporov 	}
107512f29d1SFedor Uporov 
108512f29d1SFedor Uporov 	return (0);
109512f29d1SFedor Uporov }
110512f29d1SFedor Uporov 
111512f29d1SFedor Uporov void
112512f29d1SFedor Uporov ext2_sb_csum_set(struct m_ext2fs *fs)
113512f29d1SFedor Uporov {
114512f29d1SFedor Uporov 
115512f29d1SFedor Uporov 	fs->e2fs->e4fs_sbchksum = calculate_crc32c(~0, (const char *)fs->e2fs,
116512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum));
117512f29d1SFedor Uporov }
118512f29d1SFedor Uporov 
119512f29d1SFedor Uporov static uint32_t
120512f29d1SFedor Uporov ext2_extattr_blk_csum(struct inode *ip, uint64_t facl,
121512f29d1SFedor Uporov     struct ext2fs_extattr_header *header)
122512f29d1SFedor Uporov {
123512f29d1SFedor Uporov 	struct m_ext2fs *fs;
124512f29d1SFedor Uporov 	uint32_t crc, old_crc;
125512f29d1SFedor Uporov 
126512f29d1SFedor Uporov 	fs = ip->i_e2fs;
127512f29d1SFedor Uporov 
128512f29d1SFedor Uporov 	old_crc = header->h_checksum;
129512f29d1SFedor Uporov 
130512f29d1SFedor Uporov 	header->h_checksum = 0;
131512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl, sizeof(facl));
132512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)header, fs->e2fs_bsize);
133512f29d1SFedor Uporov 	header->h_checksum = old_crc;
134512f29d1SFedor Uporov 
135512f29d1SFedor Uporov 	return (crc);
136512f29d1SFedor Uporov }
137512f29d1SFedor Uporov 
138512f29d1SFedor Uporov int
139512f29d1SFedor Uporov ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp)
140512f29d1SFedor Uporov {
141512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
142512f29d1SFedor Uporov 
143512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
144512f29d1SFedor Uporov 
145512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) &&
146512f29d1SFedor Uporov 	    (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) {
147ebc94b66SFedor Uporov 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extattr csum detected");
148512f29d1SFedor Uporov 		return (EIO);
149512f29d1SFedor Uporov 	}
150512f29d1SFedor Uporov 
151512f29d1SFedor Uporov 	return (0);
152512f29d1SFedor Uporov }
153512f29d1SFedor Uporov 
154512f29d1SFedor Uporov void
155512f29d1SFedor Uporov ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp)
156512f29d1SFedor Uporov {
157512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
158512f29d1SFedor Uporov 
159512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
160512f29d1SFedor Uporov 		return;
161512f29d1SFedor Uporov 
162512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
163512f29d1SFedor Uporov 	header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header);
164512f29d1SFedor Uporov }
165512f29d1SFedor Uporov 
1666d4a4ed7SFedor Uporov void
1676d4a4ed7SFedor Uporov ext2_init_dirent_tail(struct ext2fs_direct_tail *tp)
168512f29d1SFedor Uporov {
1696d4a4ed7SFedor Uporov 	memset(tp, 0, sizeof(struct ext2fs_direct_tail));
1706d4a4ed7SFedor Uporov 	tp->e2dt_rec_len = sizeof(struct ext2fs_direct_tail);
1716d4a4ed7SFedor Uporov 	tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM;
1726d4a4ed7SFedor Uporov }
173512f29d1SFedor Uporov 
174e49d64a7SFedor Uporov int
175e49d64a7SFedor Uporov ext2_is_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
176e49d64a7SFedor Uporov {
177e49d64a7SFedor Uporov 	struct m_ext2fs *fs;
178e49d64a7SFedor Uporov 	struct ext2fs_direct_tail *tp;
179e49d64a7SFedor Uporov 
180e49d64a7SFedor Uporov 	fs = ip->i_e2fs;
181e49d64a7SFedor Uporov 
182e49d64a7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
183e49d64a7SFedor Uporov 		return (0);
184e49d64a7SFedor Uporov 
185e49d64a7SFedor Uporov 	tp = (struct ext2fs_direct_tail *)ep;
186e49d64a7SFedor Uporov 	if (tp->e2dt_reserved_zero1 == 0 &&
187e49d64a7SFedor Uporov 	    tp->e2dt_rec_len == sizeof(struct ext2fs_direct_tail) &&
188e49d64a7SFedor Uporov 	    tp->e2dt_reserved_zero2 == 0 &&
189e49d64a7SFedor Uporov 	    tp->e2dt_reserved_ft == EXT2_FT_DIR_CSUM)
190e49d64a7SFedor Uporov 		return (1);
191e49d64a7SFedor Uporov 
192e49d64a7SFedor Uporov 	return (0);
193e49d64a7SFedor Uporov }
194e49d64a7SFedor Uporov 
1956d4a4ed7SFedor Uporov struct ext2fs_direct_tail *
1966d4a4ed7SFedor Uporov ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
1976d4a4ed7SFedor Uporov {
1986d4a4ed7SFedor Uporov 	struct ext2fs_direct_2 *dep;
1996d4a4ed7SFedor Uporov 	void *top;
2006d4a4ed7SFedor Uporov 	unsigned int rec_len;
2016d4a4ed7SFedor Uporov 
2026d4a4ed7SFedor Uporov 	dep = ep;
2036d4a4ed7SFedor Uporov 	top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
2046d4a4ed7SFedor Uporov 	rec_len = dep->e2d_reclen;
2056d4a4ed7SFedor Uporov 
2066d4a4ed7SFedor Uporov 	while (rec_len && !(rec_len & 0x3)) {
2076d4a4ed7SFedor Uporov 		dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len);
2086d4a4ed7SFedor Uporov 		if ((void *)dep >= top)
2096d4a4ed7SFedor Uporov 			break;
2106d4a4ed7SFedor Uporov 		rec_len = dep->e2d_reclen;
2116d4a4ed7SFedor Uporov 	}
2126d4a4ed7SFedor Uporov 
2136d4a4ed7SFedor Uporov 	if (dep != top)
2146d4a4ed7SFedor Uporov 		return (NULL);
2156d4a4ed7SFedor Uporov 
216e49d64a7SFedor Uporov 	if (ext2_is_dirent_tail(ip, dep))
217e49d64a7SFedor Uporov 		return ((struct ext2fs_direct_tail *)dep);
218512f29d1SFedor Uporov 
219e49d64a7SFedor Uporov 	return (NULL);
220512f29d1SFedor Uporov }
221512f29d1SFedor Uporov 
222512f29d1SFedor Uporov static uint32_t
223512f29d1SFedor Uporov ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
224512f29d1SFedor Uporov {
225512f29d1SFedor Uporov 	struct m_ext2fs *fs;
226512f29d1SFedor Uporov 	char *buf;
227512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
228512f29d1SFedor Uporov 
229512f29d1SFedor Uporov 	fs = ip->i_e2fs;
230512f29d1SFedor Uporov 
231512f29d1SFedor Uporov 	buf = (char *)ep;
232512f29d1SFedor Uporov 
233512f29d1SFedor Uporov 	inum = ip->i_number;
234512f29d1SFedor Uporov 	gen = ip->i_gen;
235512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
236512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
237512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
238512f29d1SFedor Uporov 
239512f29d1SFedor Uporov 	return (crc);
240512f29d1SFedor Uporov }
241512f29d1SFedor Uporov 
2426d4a4ed7SFedor Uporov int
243512f29d1SFedor Uporov ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
244512f29d1SFedor Uporov {
245512f29d1SFedor Uporov 	uint32_t calculated;
246512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
247512f29d1SFedor Uporov 
2486d4a4ed7SFedor Uporov 	tp = ext2_dirent_get_tail(ip, ep);
249512f29d1SFedor Uporov 	if (tp == NULL)
250512f29d1SFedor Uporov 		return (0);
251512f29d1SFedor Uporov 
252512f29d1SFedor Uporov 	calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
253512f29d1SFedor Uporov 	if (calculated != tp->e2dt_checksum)
254512f29d1SFedor Uporov 		return (EIO);
255512f29d1SFedor Uporov 
256512f29d1SFedor Uporov 	return (0);
257512f29d1SFedor Uporov }
258512f29d1SFedor Uporov 
259512f29d1SFedor Uporov static struct ext2fs_htree_count *
260512f29d1SFedor Uporov ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset)
261512f29d1SFedor Uporov {
262512f29d1SFedor Uporov 	struct ext2fs_direct_2 *dp;
263512f29d1SFedor Uporov 	struct ext2fs_htree_root_info *root;
264512f29d1SFedor Uporov 	int count_offset;
265512f29d1SFedor Uporov 
266512f29d1SFedor Uporov 	if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs))
267512f29d1SFedor Uporov 		count_offset = 8;
268512f29d1SFedor Uporov 	else if (ep->e2d_reclen == 12) {
269512f29d1SFedor Uporov 		dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12);
270512f29d1SFedor Uporov 		if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12)
271512f29d1SFedor Uporov 			return (NULL);
272512f29d1SFedor Uporov 
273512f29d1SFedor Uporov 		root = (struct ext2fs_htree_root_info *)(((char *)dp + 12));
274512f29d1SFedor Uporov 		if (root->h_reserved1 ||
275512f29d1SFedor Uporov 		    root->h_info_len != sizeof(struct ext2fs_htree_root_info))
276512f29d1SFedor Uporov 			return (NULL);
277512f29d1SFedor Uporov 
278512f29d1SFedor Uporov 		count_offset = 32;
279512f29d1SFedor Uporov 	} else
280512f29d1SFedor Uporov 		return (NULL);
281512f29d1SFedor Uporov 
282512f29d1SFedor Uporov 	if (offset)
283512f29d1SFedor Uporov 		*offset = count_offset;
284512f29d1SFedor Uporov 
285512f29d1SFedor Uporov 	return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset));
286512f29d1SFedor Uporov }
287512f29d1SFedor Uporov 
288512f29d1SFedor Uporov static uint32_t
289512f29d1SFedor Uporov ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
290512f29d1SFedor Uporov     int count, struct ext2fs_htree_tail *tp)
291512f29d1SFedor Uporov {
292512f29d1SFedor Uporov 	struct m_ext2fs *fs;
293512f29d1SFedor Uporov 	char *buf;
294512f29d1SFedor Uporov 	int size;
295512f29d1SFedor Uporov 	uint32_t inum, old_csum, gen, crc;
296512f29d1SFedor Uporov 
297512f29d1SFedor Uporov 	fs = ip->i_e2fs;
298512f29d1SFedor Uporov 
299512f29d1SFedor Uporov 	buf = (char *)ep;
300512f29d1SFedor Uporov 
301512f29d1SFedor Uporov 	size = count_offset + (count * sizeof(struct ext2fs_htree_entry));
302512f29d1SFedor Uporov 	old_csum = tp->ht_checksum;
303512f29d1SFedor Uporov 	tp->ht_checksum = 0;
304512f29d1SFedor Uporov 
305512f29d1SFedor Uporov 	inum = ip->i_number;
306512f29d1SFedor Uporov 	gen = ip->i_gen;
307512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
308512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
309512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
310512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail));
311512f29d1SFedor Uporov 	tp->ht_checksum = old_csum;
312512f29d1SFedor Uporov 
313512f29d1SFedor Uporov 	return (crc);
314512f29d1SFedor Uporov }
315512f29d1SFedor Uporov 
3166d4a4ed7SFedor Uporov int
317512f29d1SFedor Uporov ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
318512f29d1SFedor Uporov {
319512f29d1SFedor Uporov 	uint32_t calculated;
320512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
321512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
322512f29d1SFedor Uporov 	int count_offset, limit, count;
323512f29d1SFedor Uporov 
324512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
325512f29d1SFedor Uporov 	if (cp == NULL)
326512f29d1SFedor Uporov 		return (0);
327512f29d1SFedor Uporov 
328512f29d1SFedor Uporov 	limit = cp->h_entries_max;
329512f29d1SFedor Uporov 	count = cp->h_entries_num;
330512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
331512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
332512f29d1SFedor Uporov 		return (EIO);
333512f29d1SFedor Uporov 
334512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
335512f29d1SFedor Uporov 	calculated = ext2_dx_csum(ip, ep,  count_offset, count, tp);
336512f29d1SFedor Uporov 
337512f29d1SFedor Uporov 	if (tp->ht_checksum != calculated)
338512f29d1SFedor Uporov 		return (EIO);
339512f29d1SFedor Uporov 
340512f29d1SFedor Uporov 	return (0);
341512f29d1SFedor Uporov }
342512f29d1SFedor Uporov 
343512f29d1SFedor Uporov int
344512f29d1SFedor Uporov ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
345512f29d1SFedor Uporov {
346512f29d1SFedor Uporov 	struct m_ext2fs *fs;
347512f29d1SFedor Uporov 	struct ext2fs_direct_2 *ep;
348512f29d1SFedor Uporov 	int error = 0;
349512f29d1SFedor Uporov 
350512f29d1SFedor Uporov 	fs = ip->i_e2fs;
351512f29d1SFedor Uporov 
352512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
353512f29d1SFedor Uporov 		return (error);
354512f29d1SFedor Uporov 
355512f29d1SFedor Uporov 	ep = (struct ext2fs_direct_2 *)bp->b_data;
356512f29d1SFedor Uporov 
3576d4a4ed7SFedor Uporov 	if (ext2_dirent_get_tail(ip, ep) != NULL)
358512f29d1SFedor Uporov 		error = ext2_dirent_csum_verify(ip, ep);
359512f29d1SFedor Uporov 	else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
360512f29d1SFedor Uporov 		error = ext2_dx_csum_verify(ip, ep);
361512f29d1SFedor Uporov 
362512f29d1SFedor Uporov 	if (error)
363ebc94b66SFedor Uporov 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad directory csum detected");
364512f29d1SFedor Uporov 
365512f29d1SFedor Uporov 	return (error);
366512f29d1SFedor Uporov }
367512f29d1SFedor Uporov 
3686d4a4ed7SFedor Uporov void
369512f29d1SFedor Uporov ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
370512f29d1SFedor Uporov {
3716d4a4ed7SFedor Uporov 	struct m_ext2fs *fs;
372512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
373512f29d1SFedor Uporov 
3746d4a4ed7SFedor Uporov 	fs = ip->i_e2fs;
3756d4a4ed7SFedor Uporov 
3766d4a4ed7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
3776d4a4ed7SFedor Uporov 		return;
3786d4a4ed7SFedor Uporov 
3796d4a4ed7SFedor Uporov 	tp = ext2_dirent_get_tail(ip, ep);
380512f29d1SFedor Uporov 	if (tp == NULL)
381512f29d1SFedor Uporov 		return;
382512f29d1SFedor Uporov 
383512f29d1SFedor Uporov 	tp->e2dt_checksum =
384512f29d1SFedor Uporov 	    ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
385512f29d1SFedor Uporov }
386512f29d1SFedor Uporov 
3876d4a4ed7SFedor Uporov void
388512f29d1SFedor Uporov ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
389512f29d1SFedor Uporov {
3906d4a4ed7SFedor Uporov 	struct m_ext2fs *fs;
391512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
392512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
393512f29d1SFedor Uporov 	int count_offset, limit, count;
394512f29d1SFedor Uporov 
3956d4a4ed7SFedor Uporov 	fs = ip->i_e2fs;
3966d4a4ed7SFedor Uporov 
3976d4a4ed7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
3986d4a4ed7SFedor Uporov 		return;
3996d4a4ed7SFedor Uporov 
400512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
401512f29d1SFedor Uporov 	if (cp == NULL)
402512f29d1SFedor Uporov 		return;
403512f29d1SFedor Uporov 
404512f29d1SFedor Uporov 	limit = cp->h_entries_max;
405512f29d1SFedor Uporov 	count = cp->h_entries_num;
406512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
407512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
408512f29d1SFedor Uporov 		return;
409512f29d1SFedor Uporov 
410512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
411512f29d1SFedor Uporov 	tp->ht_checksum = ext2_dx_csum(ip, ep,  count_offset, count, tp);
412512f29d1SFedor Uporov }
413512f29d1SFedor Uporov 
414512f29d1SFedor Uporov static uint32_t
415512f29d1SFedor Uporov ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
416512f29d1SFedor Uporov {
417512f29d1SFedor Uporov 	struct m_ext2fs *fs;
418512f29d1SFedor Uporov 	size_t size;
419512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
420512f29d1SFedor Uporov 
421512f29d1SFedor Uporov 	fs = ip->i_e2fs;
422512f29d1SFedor Uporov 
423512f29d1SFedor Uporov 	size = EXT4_EXTENT_TAIL_OFFSET(ehp) +
424512f29d1SFedor Uporov 	    offsetof(struct ext4_extent_tail, et_checksum);
425512f29d1SFedor Uporov 
426512f29d1SFedor Uporov 	inum = ip->i_number;
427512f29d1SFedor Uporov 	gen = ip->i_gen;
428512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
429512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
430512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ehp, size);
431512f29d1SFedor Uporov 
432512f29d1SFedor Uporov 	return (crc);
433512f29d1SFedor Uporov }
434512f29d1SFedor Uporov 
435512f29d1SFedor Uporov int
436512f29d1SFedor Uporov ext2_extent_blk_csum_verify(struct inode *ip, void *data)
437512f29d1SFedor Uporov {
438512f29d1SFedor Uporov 	struct m_ext2fs *fs;
439512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
440512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
441512f29d1SFedor Uporov 	uint32_t provided, calculated;
442512f29d1SFedor Uporov 
443512f29d1SFedor Uporov 	fs = ip->i_e2fs;
444512f29d1SFedor Uporov 
445512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
446512f29d1SFedor Uporov 		return (0);
447512f29d1SFedor Uporov 
448512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
449512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)ehp) +
450512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
451512f29d1SFedor Uporov 
452512f29d1SFedor Uporov 	provided = etp->et_checksum;
453512f29d1SFedor Uporov 	calculated = ext2_extent_blk_csum(ip, ehp);
454512f29d1SFedor Uporov 
455512f29d1SFedor Uporov 	if (provided != calculated) {
456ebc94b66SFedor Uporov 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extent csum detected");
457512f29d1SFedor Uporov 		return (EIO);
458512f29d1SFedor Uporov 	}
459512f29d1SFedor Uporov 
460512f29d1SFedor Uporov 	return (0);
461512f29d1SFedor Uporov }
462512f29d1SFedor Uporov 
463512f29d1SFedor Uporov void
464512f29d1SFedor Uporov ext2_extent_blk_csum_set(struct inode *ip, void *data)
465512f29d1SFedor Uporov {
466512f29d1SFedor Uporov 	struct m_ext2fs *fs;
467512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
468512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
469512f29d1SFedor Uporov 
470512f29d1SFedor Uporov 	fs = ip->i_e2fs;
471512f29d1SFedor Uporov 
472512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
473512f29d1SFedor Uporov 		return;
474512f29d1SFedor Uporov 
475512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
476512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)data) +
477512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
478512f29d1SFedor Uporov 
479512f29d1SFedor Uporov 	etp->et_checksum = ext2_extent_blk_csum(ip,
480512f29d1SFedor Uporov 	    (struct ext4_extent_header *)data);
481512f29d1SFedor Uporov }
482512f29d1SFedor Uporov 
483512f29d1SFedor Uporov int
484512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
485512f29d1SFedor Uporov {
486512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
487512f29d1SFedor Uporov 
488512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
489512f29d1SFedor Uporov 		return (0);
490512f29d1SFedor Uporov 
491512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum;
492512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
493512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
494512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) {
495512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi;
496512f29d1SFedor Uporov 		provided |= (hi << 16);
497512f29d1SFedor Uporov 	} else
498512f29d1SFedor Uporov 		calculated &= 0xFFFF;
499512f29d1SFedor Uporov 
500512f29d1SFedor Uporov 	if (provided != calculated) {
501ebc94b66SFedor Uporov 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode bitmap csum detected");
502512f29d1SFedor Uporov 		return (EIO);
503512f29d1SFedor Uporov 	}
504512f29d1SFedor Uporov 
505512f29d1SFedor Uporov 	return (0);
506512f29d1SFedor Uporov }
507512f29d1SFedor Uporov 
508512f29d1SFedor Uporov void
509512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
510512f29d1SFedor Uporov {
511512f29d1SFedor Uporov 	uint32_t csum;
512512f29d1SFedor Uporov 
513512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
514512f29d1SFedor Uporov 		return;
515512f29d1SFedor Uporov 
516512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
517512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
518512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF;
519512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END)
520512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16;
521512f29d1SFedor Uporov }
522512f29d1SFedor Uporov 
523512f29d1SFedor Uporov int
524512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
525512f29d1SFedor Uporov {
526512f29d1SFedor Uporov 	uint32_t hi, provided, calculated, size;
527512f29d1SFedor Uporov 
528512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
529512f29d1SFedor Uporov 		return (0);
530512f29d1SFedor Uporov 
531512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
532512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum;
533512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
534512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) {
535512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi;
536512f29d1SFedor Uporov 		provided |= (hi << 16);
537512f29d1SFedor Uporov 	} else
538512f29d1SFedor Uporov 		calculated &= 0xFFFF;
539512f29d1SFedor Uporov 
540512f29d1SFedor Uporov 	if (provided != calculated) {
541ebc94b66SFedor Uporov 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad block bitmap csum detected");
542512f29d1SFedor Uporov 		return (EIO);
543512f29d1SFedor Uporov 	}
544512f29d1SFedor Uporov 
545512f29d1SFedor Uporov 	return (0);
546512f29d1SFedor Uporov }
547512f29d1SFedor Uporov 
548512f29d1SFedor Uporov void
549512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
550512f29d1SFedor Uporov {
551512f29d1SFedor Uporov 	uint32_t csum, size;
552512f29d1SFedor Uporov 
553512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
554512f29d1SFedor Uporov 		return;
555512f29d1SFedor Uporov 
556512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
557512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
558512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF;
559512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
560512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16;
561512f29d1SFedor Uporov }
562512f29d1SFedor Uporov 
563512f29d1SFedor Uporov static uint32_t
564512f29d1SFedor Uporov ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
565512f29d1SFedor Uporov {
566512f29d1SFedor Uporov 	struct m_ext2fs *fs;
567c4aa9a02SFedor Uporov 	uint32_t inode_csum_seed, inum, gen, crc;
568c4aa9a02SFedor Uporov 	uint16_t dummy_csum = 0;
569c4aa9a02SFedor Uporov 	unsigned int offset, csum_size;
570512f29d1SFedor Uporov 
571512f29d1SFedor Uporov 	fs = ip->i_e2fs;
572c4aa9a02SFedor Uporov 	offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo);
573c4aa9a02SFedor Uporov 	csum_size = sizeof(dummy_csum);
574512f29d1SFedor Uporov 	inum = ip->i_number;
575c4aa9a02SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed,
576c4aa9a02SFedor Uporov 	    (uint8_t *)&inum, sizeof(inum));
5776d4a4ed7SFedor Uporov 	gen = ip->i_gen;
578c4aa9a02SFedor Uporov 	inode_csum_seed = calculate_crc32c(crc,
579c4aa9a02SFedor Uporov 	    (uint8_t *)&gen, sizeof(gen));
580512f29d1SFedor Uporov 
581c4aa9a02SFedor Uporov 	crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset);
582c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size);
583c4aa9a02SFedor Uporov 	offset += csum_size;
584c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
585c4aa9a02SFedor Uporov 	    E2FS_REV0_INODE_SIZE - offset);
586512f29d1SFedor Uporov 
587c4aa9a02SFedor Uporov 	if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) {
588c4aa9a02SFedor Uporov 		offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi);
589c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei +
590c4aa9a02SFedor Uporov 		    E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE);
591c4aa9a02SFedor Uporov 
592c4aa9a02SFedor Uporov 		if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
593c4aa9a02SFedor Uporov 		    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
594c4aa9a02SFedor Uporov 			crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum,
595c4aa9a02SFedor Uporov 			    csum_size);
596c4aa9a02SFedor Uporov 			offset += csum_size;
597c4aa9a02SFedor Uporov 		}
598c4aa9a02SFedor Uporov 
599c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
600c4aa9a02SFedor Uporov 		    EXT2_INODE_SIZE(fs) - offset);
601c4aa9a02SFedor Uporov 	}
602512f29d1SFedor Uporov 
603512f29d1SFedor Uporov 	return (crc);
604512f29d1SFedor Uporov }
605512f29d1SFedor Uporov 
606512f29d1SFedor Uporov int
607512f29d1SFedor Uporov ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
608512f29d1SFedor Uporov {
609512f29d1SFedor Uporov 	struct m_ext2fs *fs;
610512f29d1SFedor Uporov 	const static struct ext2fs_dinode ei_zero;
611512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
612512f29d1SFedor Uporov 
613512f29d1SFedor Uporov 	fs = ip->i_e2fs;
614512f29d1SFedor Uporov 
615512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
616512f29d1SFedor Uporov 		return (0);
617512f29d1SFedor Uporov 
618512f29d1SFedor Uporov 	provided = ei->e2di_chksum_lo;
619512f29d1SFedor Uporov 	calculated = ext2_ei_csum(ip, ei);
620512f29d1SFedor Uporov 
621512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
622512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
623512f29d1SFedor Uporov 		hi = ei->e2di_chksum_hi;
624512f29d1SFedor Uporov 		provided |= hi << 16;
625512f29d1SFedor Uporov 	} else
626512f29d1SFedor Uporov 		calculated &= 0xFFFF;
627512f29d1SFedor Uporov 
628c4aa9a02SFedor Uporov 	if (provided != calculated) {
629c4aa9a02SFedor Uporov 		/*
630c4aa9a02SFedor Uporov 		 * If it is first time used dinode,
631c4aa9a02SFedor Uporov 		 * it is expected that it will be zeroed
632c4aa9a02SFedor Uporov 		 * and we will not return checksum error in this case.
633c4aa9a02SFedor Uporov 		 */
634c4aa9a02SFedor Uporov 		if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
635c4aa9a02SFedor Uporov 			return (0);
636c4aa9a02SFedor Uporov 
637ebc94b66SFedor Uporov 		SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode csum");
638daa2d62dSFedor Uporov 
639512f29d1SFedor Uporov 		return (EIO);
640c4aa9a02SFedor Uporov 	}
641512f29d1SFedor Uporov 
642512f29d1SFedor Uporov 	return (0);
643512f29d1SFedor Uporov }
644512f29d1SFedor Uporov 
645512f29d1SFedor Uporov void
646512f29d1SFedor Uporov ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
647512f29d1SFedor Uporov {
648512f29d1SFedor Uporov 	struct m_ext2fs *fs;
649512f29d1SFedor Uporov 	uint32_t crc;
650512f29d1SFedor Uporov 
651512f29d1SFedor Uporov 	fs = ip->i_e2fs;
652512f29d1SFedor Uporov 
653512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
654512f29d1SFedor Uporov 		return;
655512f29d1SFedor Uporov 
656512f29d1SFedor Uporov 	crc = ext2_ei_csum(ip, ei);
657512f29d1SFedor Uporov 
658512f29d1SFedor Uporov 	ei->e2di_chksum_lo = crc & 0xFFFF;
659512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
660512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
661512f29d1SFedor Uporov 		ei->e2di_chksum_hi = crc >> 16;
662512f29d1SFedor Uporov }
663512f29d1SFedor Uporov 
664d23db91eSPedro F. Giffuni static uint16_t
665d23db91eSPedro F. Giffuni ext2_crc16(uint16_t crc, const void *buffer, unsigned int len)
666d23db91eSPedro F. Giffuni {
667d23db91eSPedro F. Giffuni 	const unsigned char *cp = buffer;
668d23db91eSPedro F. Giffuni 	/* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
669d23db91eSPedro F. Giffuni 	static uint16_t const crc16_table[256] = {
670d23db91eSPedro F. Giffuni 		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
671d23db91eSPedro F. Giffuni 		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
672d23db91eSPedro F. Giffuni 		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
673d23db91eSPedro F. Giffuni 		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
674d23db91eSPedro F. Giffuni 		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
675d23db91eSPedro F. Giffuni 		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
676d23db91eSPedro F. Giffuni 		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
677d23db91eSPedro F. Giffuni 		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
678d23db91eSPedro F. Giffuni 		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
679d23db91eSPedro F. Giffuni 		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
680d23db91eSPedro F. Giffuni 		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
681d23db91eSPedro F. Giffuni 		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
682d23db91eSPedro F. Giffuni 		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
683d23db91eSPedro F. Giffuni 		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
684d23db91eSPedro F. Giffuni 		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
685d23db91eSPedro F. Giffuni 		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
686d23db91eSPedro F. Giffuni 		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
687d23db91eSPedro F. Giffuni 		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
688d23db91eSPedro F. Giffuni 		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
689d23db91eSPedro F. Giffuni 		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
690d23db91eSPedro F. Giffuni 		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
691d23db91eSPedro F. Giffuni 		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
692d23db91eSPedro F. Giffuni 		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
693d23db91eSPedro F. Giffuni 		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
694d23db91eSPedro F. Giffuni 		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
695d23db91eSPedro F. Giffuni 		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
696d23db91eSPedro F. Giffuni 		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
697d23db91eSPedro F. Giffuni 		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
698d23db91eSPedro F. Giffuni 		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
699d23db91eSPedro F. Giffuni 		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
700d23db91eSPedro F. Giffuni 		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
701d23db91eSPedro F. Giffuni 		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
702d23db91eSPedro F. Giffuni 	};
703d23db91eSPedro F. Giffuni 
704d23db91eSPedro F. Giffuni 	while (len--)
705d23db91eSPedro F. Giffuni 		crc = (((crc >> 8) & 0xffU) ^
706d23db91eSPedro F. Giffuni 		    crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
707d23db91eSPedro F. Giffuni 	return crc;
708d23db91eSPedro F. Giffuni }
709d23db91eSPedro F. Giffuni 
710d23db91eSPedro F. Giffuni static uint16_t
711d23db91eSPedro F. Giffuni ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
712d23db91eSPedro F. Giffuni {
713d23db91eSPedro F. Giffuni 	size_t offset;
714512f29d1SFedor Uporov 	uint32_t csum32;
715512f29d1SFedor Uporov 	uint16_t crc, dummy_csum;
716d23db91eSPedro F. Giffuni 
717d23db91eSPedro F. Giffuni 	offset = offsetof(struct ext2_gd, ext4bgd_csum);
718d23db91eSPedro F. Giffuni 
719512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
720512f29d1SFedor Uporov 		csum32 = calculate_crc32c(fs->e2fs_csum_seed,
721512f29d1SFedor Uporov 		    (uint8_t *)&block_group, sizeof(block_group));
722512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
723512f29d1SFedor Uporov 		dummy_csum = 0;
724512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
725512f29d1SFedor Uporov 		    sizeof(dummy_csum));
726512f29d1SFedor Uporov 		offset += sizeof(dummy_csum);
727512f29d1SFedor Uporov 		if (offset < fs->e2fs->e3fs_desc_size)
728512f29d1SFedor Uporov 			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
729512f29d1SFedor Uporov 			    fs->e2fs->e3fs_desc_size - offset);
730512f29d1SFedor Uporov 
731512f29d1SFedor Uporov 		crc = csum32 & 0xFFFF;
732512f29d1SFedor Uporov 		return (crc);
733512f29d1SFedor Uporov 	} else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
734d23db91eSPedro F. Giffuni 		crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid,
735d23db91eSPedro F. Giffuni 		    sizeof(fs->e2fs->e2fs_uuid));
736d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)&block_group,
737d23db91eSPedro F. Giffuni 		    sizeof(block_group));
738d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)gd, offset);
739d23db91eSPedro F. Giffuni 		offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
740d23db91eSPedro F. Giffuni 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
741d23db91eSPedro F. Giffuni 		    offset < fs->e2fs->e3fs_desc_size)
742d23db91eSPedro F. Giffuni 			crc = ext2_crc16(crc, (uint8_t *)gd + offset,
743d23db91eSPedro F. Giffuni 			    fs->e2fs->e3fs_desc_size - offset);
744d23db91eSPedro F. Giffuni 		return (crc);
745d23db91eSPedro F. Giffuni 	}
746d23db91eSPedro F. Giffuni 
747d23db91eSPedro F. Giffuni 	return (0);
748d23db91eSPedro F. Giffuni }
749d23db91eSPedro F. Giffuni 
750d23db91eSPedro F. Giffuni int
751d23db91eSPedro F. Giffuni ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
752d23db91eSPedro F. Giffuni {
753d23db91eSPedro F. Giffuni 	unsigned int i;
754d23db91eSPedro F. Giffuni 	int error = 0;
755d23db91eSPedro F. Giffuni 
756d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++) {
757d23db91eSPedro F. Giffuni 		if (fs->e2fs_gd[i].ext4bgd_csum !=
758d23db91eSPedro F. Giffuni 		    ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
759d23db91eSPedro F. Giffuni 			printf(
760d23db91eSPedro F. Giffuni "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
761d23db91eSPedro F. Giffuni 			    devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
762d23db91eSPedro F. Giffuni 			    ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
763512f29d1SFedor Uporov 			error = EIO;
764d23db91eSPedro F. Giffuni 			break;
765d23db91eSPedro F. Giffuni 		}
766d23db91eSPedro F. Giffuni 	}
767d23db91eSPedro F. Giffuni 
768d23db91eSPedro F. Giffuni 	return (error);
769d23db91eSPedro F. Giffuni }
770d23db91eSPedro F. Giffuni 
771d23db91eSPedro F. Giffuni void
772d23db91eSPedro F. Giffuni ext2_gd_csum_set(struct m_ext2fs *fs)
773d23db91eSPedro F. Giffuni {
774d23db91eSPedro F. Giffuni 	unsigned int i;
775d23db91eSPedro F. Giffuni 
776d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++)
777d23db91eSPedro F. Giffuni 		    fs->e2fs_gd[i].ext4bgd_csum =
778d23db91eSPedro F. Giffuni 			ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
779d23db91eSPedro F. Giffuni }
780