xref: /freebsd/sys/fs/ext2fs/ext2_csum.c (revision e49d64a7a79310d1a10b9e3c190d68e596ab54d6)
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>
34d23db91eSPedro F. Giffuni #include <sys/stat.h>
35d23db91eSPedro F. Giffuni #include <sys/kernel.h>
36d23db91eSPedro F. Giffuni #include <sys/malloc.h>
37d23db91eSPedro F. Giffuni #include <sys/vnode.h>
38d23db91eSPedro F. Giffuni #include <sys/bio.h>
39d23db91eSPedro F. Giffuni #include <sys/buf.h>
40d23db91eSPedro F. Giffuni #include <sys/endian.h>
41d23db91eSPedro F. Giffuni #include <sys/conf.h>
42d23db91eSPedro F. Giffuni #include <sys/mount.h>
43d23db91eSPedro F. Giffuni 
44d23db91eSPedro F. Giffuni #include <fs/ext2fs/fs.h>
45d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2fs.h>
46512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dinode.h>
47d23db91eSPedro F. Giffuni #include <fs/ext2fs/inode.h>
48512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dir.h>
49512f29d1SFedor Uporov #include <fs/ext2fs/htree.h>
50512f29d1SFedor Uporov #include <fs/ext2fs/ext2_extattr.h>
51d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h>
52d23db91eSPedro F. Giffuni 
53512f29d1SFedor Uporov #define EXT2_BG_INODE_BITMAP_CSUM_HI_END	\
54512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \
55512f29d1SFedor Uporov 	 sizeof(uint16_t))
56512f29d1SFedor Uporov 
57512f29d1SFedor Uporov #define EXT2_INODE_CSUM_HI_EXTRA_END	\
58512f29d1SFedor Uporov 	(offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \
59512f29d1SFedor Uporov 	 E2FS_REV0_INODE_SIZE)
60512f29d1SFedor Uporov 
61512f29d1SFedor Uporov #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION	\
62512f29d1SFedor Uporov 	(offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \
63512f29d1SFedor Uporov 	 sizeof(uint16_t))
64512f29d1SFedor Uporov 
65512f29d1SFedor Uporov void
66512f29d1SFedor Uporov ext2_sb_csum_set_seed(struct m_ext2fs *fs)
67512f29d1SFedor Uporov {
68512f29d1SFedor Uporov 
69512f29d1SFedor Uporov 	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED))
70512f29d1SFedor Uporov 		fs->e2fs_csum_seed = fs->e2fs->e4fs_chksum_seed;
71512f29d1SFedor Uporov 	else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
72512f29d1SFedor Uporov 		fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid,
73512f29d1SFedor Uporov 		    sizeof(fs->e2fs->e2fs_uuid));
74512f29d1SFedor Uporov 	}
75512f29d1SFedor Uporov 	else
76512f29d1SFedor Uporov 		fs->e2fs_csum_seed = 0;
77512f29d1SFedor Uporov }
78512f29d1SFedor Uporov 
79512f29d1SFedor Uporov int
80512f29d1SFedor Uporov ext2_sb_csum_verify(struct m_ext2fs *fs)
81512f29d1SFedor Uporov {
82512f29d1SFedor Uporov 
83512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) {
84512f29d1SFedor Uporov 		printf(
85512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt);
86512f29d1SFedor Uporov 		return (EINVAL);
87512f29d1SFedor Uporov 	}
88512f29d1SFedor Uporov 	if (fs->e2fs->e4fs_sbchksum !=
89512f29d1SFedor Uporov 	    calculate_crc32c(~0, (const char *)fs->e2fs,
90512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum))) {
91512f29d1SFedor Uporov 		printf(
92512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n",
93512f29d1SFedor Uporov 		    fs->e2fs_fsmnt, fs->e2fs->e4fs_sbchksum, calculate_crc32c(~0,
94512f29d1SFedor Uporov 		    (const char *)fs->e2fs, offsetof(struct ext2fs, e4fs_sbchksum)));
95512f29d1SFedor Uporov 		return (EINVAL);
96512f29d1SFedor Uporov 	}
97512f29d1SFedor Uporov 
98512f29d1SFedor Uporov 	return (0);
99512f29d1SFedor Uporov }
100512f29d1SFedor Uporov 
101512f29d1SFedor Uporov void
102512f29d1SFedor Uporov ext2_sb_csum_set(struct m_ext2fs *fs)
103512f29d1SFedor Uporov {
104512f29d1SFedor Uporov 
105512f29d1SFedor Uporov 	fs->e2fs->e4fs_sbchksum = calculate_crc32c(~0, (const char *)fs->e2fs,
106512f29d1SFedor Uporov 	    offsetof(struct ext2fs, e4fs_sbchksum));
107512f29d1SFedor Uporov }
108512f29d1SFedor Uporov 
109512f29d1SFedor Uporov static uint32_t
110512f29d1SFedor Uporov ext2_extattr_blk_csum(struct inode *ip, uint64_t facl,
111512f29d1SFedor Uporov     struct ext2fs_extattr_header *header)
112512f29d1SFedor Uporov {
113512f29d1SFedor Uporov 	struct m_ext2fs *fs;
114512f29d1SFedor Uporov 	uint32_t crc, old_crc;
115512f29d1SFedor Uporov 
116512f29d1SFedor Uporov 	fs = ip->i_e2fs;
117512f29d1SFedor Uporov 
118512f29d1SFedor Uporov 	old_crc = header->h_checksum;
119512f29d1SFedor Uporov 
120512f29d1SFedor Uporov 	header->h_checksum = 0;
121512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl, sizeof(facl));
122512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)header, fs->e2fs_bsize);
123512f29d1SFedor Uporov 	header->h_checksum = old_crc;
124512f29d1SFedor Uporov 
125512f29d1SFedor Uporov 	return (crc);
126512f29d1SFedor Uporov }
127512f29d1SFedor Uporov 
128512f29d1SFedor Uporov int
129512f29d1SFedor Uporov ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp)
130512f29d1SFedor Uporov {
131512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
132512f29d1SFedor Uporov 
133512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
134512f29d1SFedor Uporov 
135512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) &&
136512f29d1SFedor Uporov 	    (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) {
137512f29d1SFedor Uporov 		printf("WARNING: bad extattr csum detected, ip=%lu - run fsck\n",
138512f29d1SFedor Uporov 		    (unsigned long)ip->i_number);
139512f29d1SFedor Uporov 		return (EIO);
140512f29d1SFedor Uporov 	}
141512f29d1SFedor Uporov 
142512f29d1SFedor Uporov 	return (0);
143512f29d1SFedor Uporov }
144512f29d1SFedor Uporov 
145512f29d1SFedor Uporov void
146512f29d1SFedor Uporov ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp)
147512f29d1SFedor Uporov {
148512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
149512f29d1SFedor Uporov 
150512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
151512f29d1SFedor Uporov 		return;
152512f29d1SFedor Uporov 
153512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
154512f29d1SFedor Uporov 	header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header);
155512f29d1SFedor Uporov }
156512f29d1SFedor Uporov 
1576d4a4ed7SFedor Uporov void
1586d4a4ed7SFedor Uporov ext2_init_dirent_tail(struct ext2fs_direct_tail *tp)
159512f29d1SFedor Uporov {
1606d4a4ed7SFedor Uporov 	memset(tp, 0, sizeof(struct ext2fs_direct_tail));
1616d4a4ed7SFedor Uporov 	tp->e2dt_rec_len = sizeof(struct ext2fs_direct_tail);
1626d4a4ed7SFedor Uporov 	tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM;
1636d4a4ed7SFedor Uporov }
164512f29d1SFedor Uporov 
165*e49d64a7SFedor Uporov int
166*e49d64a7SFedor Uporov ext2_is_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
167*e49d64a7SFedor Uporov {
168*e49d64a7SFedor Uporov 	struct m_ext2fs *fs;
169*e49d64a7SFedor Uporov 	struct ext2fs_direct_tail *tp;
170*e49d64a7SFedor Uporov 
171*e49d64a7SFedor Uporov 	fs = ip->i_e2fs;
172*e49d64a7SFedor Uporov 
173*e49d64a7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
174*e49d64a7SFedor Uporov 		return (0);
175*e49d64a7SFedor Uporov 
176*e49d64a7SFedor Uporov 	tp = (struct ext2fs_direct_tail *)ep;
177*e49d64a7SFedor Uporov 	if (tp->e2dt_reserved_zero1 == 0 &&
178*e49d64a7SFedor Uporov 	    tp->e2dt_rec_len == sizeof(struct ext2fs_direct_tail) &&
179*e49d64a7SFedor Uporov 	    tp->e2dt_reserved_zero2 == 0 &&
180*e49d64a7SFedor Uporov 	    tp->e2dt_reserved_ft == EXT2_FT_DIR_CSUM)
181*e49d64a7SFedor Uporov 		return (1);
182*e49d64a7SFedor Uporov 
183*e49d64a7SFedor Uporov 	return (0);
184*e49d64a7SFedor Uporov }
185*e49d64a7SFedor Uporov 
1866d4a4ed7SFedor Uporov struct ext2fs_direct_tail *
1876d4a4ed7SFedor Uporov ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
1886d4a4ed7SFedor Uporov {
1896d4a4ed7SFedor Uporov 	struct ext2fs_direct_2 *dep;
1906d4a4ed7SFedor Uporov 	void *top;
1916d4a4ed7SFedor Uporov 	unsigned int rec_len;
1926d4a4ed7SFedor Uporov 
1936d4a4ed7SFedor Uporov 	dep = ep;
1946d4a4ed7SFedor Uporov 	top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
1956d4a4ed7SFedor Uporov 	rec_len = dep->e2d_reclen;
1966d4a4ed7SFedor Uporov 
1976d4a4ed7SFedor Uporov 	while (rec_len && !(rec_len & 0x3)) {
1986d4a4ed7SFedor Uporov 		dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len);
1996d4a4ed7SFedor Uporov 		if ((void *)dep >= top)
2006d4a4ed7SFedor Uporov 			break;
2016d4a4ed7SFedor Uporov 		rec_len = dep->e2d_reclen;
2026d4a4ed7SFedor Uporov 	}
2036d4a4ed7SFedor Uporov 
2046d4a4ed7SFedor Uporov 	if (dep != top)
2056d4a4ed7SFedor Uporov 		return (NULL);
2066d4a4ed7SFedor Uporov 
207*e49d64a7SFedor Uporov 	if (ext2_is_dirent_tail(ip, dep))
208*e49d64a7SFedor Uporov 		return ((struct ext2fs_direct_tail *)dep);
209512f29d1SFedor Uporov 
210*e49d64a7SFedor Uporov 	return (NULL);
211512f29d1SFedor Uporov }
212512f29d1SFedor Uporov 
213512f29d1SFedor Uporov static uint32_t
214512f29d1SFedor Uporov ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
215512f29d1SFedor Uporov {
216512f29d1SFedor Uporov 	struct m_ext2fs *fs;
217512f29d1SFedor Uporov 	char *buf;
218512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
219512f29d1SFedor Uporov 
220512f29d1SFedor Uporov 	fs = ip->i_e2fs;
221512f29d1SFedor Uporov 
222512f29d1SFedor Uporov 	buf = (char *)ep;
223512f29d1SFedor Uporov 
224512f29d1SFedor Uporov 	inum = ip->i_number;
225512f29d1SFedor Uporov 	gen = ip->i_gen;
226512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
227512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
228512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
229512f29d1SFedor Uporov 
230512f29d1SFedor Uporov 	return (crc);
231512f29d1SFedor Uporov }
232512f29d1SFedor Uporov 
2336d4a4ed7SFedor Uporov int
234512f29d1SFedor Uporov ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
235512f29d1SFedor Uporov {
236512f29d1SFedor Uporov 	uint32_t calculated;
237512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
238512f29d1SFedor Uporov 
2396d4a4ed7SFedor Uporov 	tp = ext2_dirent_get_tail(ip, ep);
240512f29d1SFedor Uporov 	if (tp == NULL)
241512f29d1SFedor Uporov 		return (0);
242512f29d1SFedor Uporov 
243512f29d1SFedor Uporov 	calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
244512f29d1SFedor Uporov 	if (calculated != tp->e2dt_checksum)
245512f29d1SFedor Uporov 		return (EIO);
246512f29d1SFedor Uporov 
247512f29d1SFedor Uporov 	return (0);
248512f29d1SFedor Uporov }
249512f29d1SFedor Uporov 
250512f29d1SFedor Uporov static struct ext2fs_htree_count *
251512f29d1SFedor Uporov ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset)
252512f29d1SFedor Uporov {
253512f29d1SFedor Uporov 	struct ext2fs_direct_2 *dp;
254512f29d1SFedor Uporov 	struct ext2fs_htree_root_info *root;
255512f29d1SFedor Uporov 	int count_offset;
256512f29d1SFedor Uporov 
257512f29d1SFedor Uporov 	if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs))
258512f29d1SFedor Uporov 		count_offset = 8;
259512f29d1SFedor Uporov 	else if (ep->e2d_reclen == 12) {
260512f29d1SFedor Uporov 		dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12);
261512f29d1SFedor Uporov 		if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12)
262512f29d1SFedor Uporov 			return (NULL);
263512f29d1SFedor Uporov 
264512f29d1SFedor Uporov 		root = (struct ext2fs_htree_root_info *)(((char *)dp + 12));
265512f29d1SFedor Uporov 		if (root->h_reserved1 ||
266512f29d1SFedor Uporov 		    root->h_info_len != sizeof(struct ext2fs_htree_root_info))
267512f29d1SFedor Uporov 			return (NULL);
268512f29d1SFedor Uporov 
269512f29d1SFedor Uporov 		count_offset = 32;
270512f29d1SFedor Uporov 	} else
271512f29d1SFedor Uporov 		return (NULL);
272512f29d1SFedor Uporov 
273512f29d1SFedor Uporov 	if (offset)
274512f29d1SFedor Uporov 		*offset = count_offset;
275512f29d1SFedor Uporov 
276512f29d1SFedor Uporov 	return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset));
277512f29d1SFedor Uporov }
278512f29d1SFedor Uporov 
279512f29d1SFedor Uporov static uint32_t
280512f29d1SFedor Uporov ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
281512f29d1SFedor Uporov     int count, struct ext2fs_htree_tail *tp)
282512f29d1SFedor Uporov {
283512f29d1SFedor Uporov 	struct m_ext2fs *fs;
284512f29d1SFedor Uporov 	char *buf;
285512f29d1SFedor Uporov 	int size;
286512f29d1SFedor Uporov 	uint32_t inum, old_csum, gen, crc;
287512f29d1SFedor Uporov 
288512f29d1SFedor Uporov 	fs = ip->i_e2fs;
289512f29d1SFedor Uporov 
290512f29d1SFedor Uporov 	buf = (char *)ep;
291512f29d1SFedor Uporov 
292512f29d1SFedor Uporov 	size = count_offset + (count * sizeof(struct ext2fs_htree_entry));
293512f29d1SFedor Uporov 	old_csum = tp->ht_checksum;
294512f29d1SFedor Uporov 	tp->ht_checksum = 0;
295512f29d1SFedor Uporov 
296512f29d1SFedor Uporov 	inum = ip->i_number;
297512f29d1SFedor Uporov 	gen = ip->i_gen;
298512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
299512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
300512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
301512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail));
302512f29d1SFedor Uporov 	tp->ht_checksum = old_csum;
303512f29d1SFedor Uporov 
304512f29d1SFedor Uporov 	return (crc);
305512f29d1SFedor Uporov }
306512f29d1SFedor Uporov 
3076d4a4ed7SFedor Uporov int
308512f29d1SFedor Uporov ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
309512f29d1SFedor Uporov {
310512f29d1SFedor Uporov 	uint32_t calculated;
311512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
312512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
313512f29d1SFedor Uporov 	int count_offset, limit, count;
314512f29d1SFedor Uporov 
315512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
316512f29d1SFedor Uporov 	if (cp == NULL)
317512f29d1SFedor Uporov 		return (0);
318512f29d1SFedor Uporov 
319512f29d1SFedor Uporov 	limit = cp->h_entries_max;
320512f29d1SFedor Uporov 	count = cp->h_entries_num;
321512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
322512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
323512f29d1SFedor Uporov 		return (EIO);
324512f29d1SFedor Uporov 
325512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
326512f29d1SFedor Uporov 	calculated = ext2_dx_csum(ip, ep,  count_offset, count, tp);
327512f29d1SFedor Uporov 
328512f29d1SFedor Uporov 	if (tp->ht_checksum != calculated)
329512f29d1SFedor Uporov 		return (EIO);
330512f29d1SFedor Uporov 
331512f29d1SFedor Uporov 	return (0);
332512f29d1SFedor Uporov }
333512f29d1SFedor Uporov 
334512f29d1SFedor Uporov int
335512f29d1SFedor Uporov ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
336512f29d1SFedor Uporov {
337512f29d1SFedor Uporov 	struct m_ext2fs *fs;
338512f29d1SFedor Uporov 	struct ext2fs_direct_2 *ep;
339512f29d1SFedor Uporov 	int error = 0;
340512f29d1SFedor Uporov 
341512f29d1SFedor Uporov 	fs = ip->i_e2fs;
342512f29d1SFedor Uporov 
343512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
344512f29d1SFedor Uporov 		return (error);
345512f29d1SFedor Uporov 
346512f29d1SFedor Uporov 	ep = (struct ext2fs_direct_2 *)bp->b_data;
347512f29d1SFedor Uporov 
3486d4a4ed7SFedor Uporov 	if (ext2_dirent_get_tail(ip, ep) != NULL)
349512f29d1SFedor Uporov 		error = ext2_dirent_csum_verify(ip, ep);
350512f29d1SFedor Uporov 	else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
351512f29d1SFedor Uporov 		error = ext2_dx_csum_verify(ip, ep);
352512f29d1SFedor Uporov 
353512f29d1SFedor Uporov 	if (error)
354512f29d1SFedor Uporov 		printf("WARNING: bad directory csum detected, ip=%lu"
355512f29d1SFedor Uporov 		    " - run fsck\n", (unsigned long)ip->i_number);
356512f29d1SFedor Uporov 
357512f29d1SFedor Uporov 	return (error);
358512f29d1SFedor Uporov }
359512f29d1SFedor Uporov 
3606d4a4ed7SFedor Uporov void
361512f29d1SFedor Uporov ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
362512f29d1SFedor Uporov {
3636d4a4ed7SFedor Uporov 	struct m_ext2fs *fs;
364512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
365512f29d1SFedor Uporov 
3666d4a4ed7SFedor Uporov 	fs = ip->i_e2fs;
3676d4a4ed7SFedor Uporov 
3686d4a4ed7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
3696d4a4ed7SFedor Uporov 		return;
3706d4a4ed7SFedor Uporov 
3716d4a4ed7SFedor Uporov 	tp = ext2_dirent_get_tail(ip, ep);
372512f29d1SFedor Uporov 	if (tp == NULL)
373512f29d1SFedor Uporov 		return;
374512f29d1SFedor Uporov 
375512f29d1SFedor Uporov 	tp->e2dt_checksum =
376512f29d1SFedor Uporov 	    ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
377512f29d1SFedor Uporov }
378512f29d1SFedor Uporov 
3796d4a4ed7SFedor Uporov void
380512f29d1SFedor Uporov ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
381512f29d1SFedor Uporov {
3826d4a4ed7SFedor Uporov 	struct m_ext2fs *fs;
383512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
384512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
385512f29d1SFedor Uporov 	int count_offset, limit, count;
386512f29d1SFedor Uporov 
3876d4a4ed7SFedor Uporov 	fs = ip->i_e2fs;
3886d4a4ed7SFedor Uporov 
3896d4a4ed7SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
3906d4a4ed7SFedor Uporov 		return;
3916d4a4ed7SFedor Uporov 
392512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
393512f29d1SFedor Uporov 	if (cp == NULL)
394512f29d1SFedor Uporov 		return;
395512f29d1SFedor Uporov 
396512f29d1SFedor Uporov 	limit = cp->h_entries_max;
397512f29d1SFedor Uporov 	count = cp->h_entries_num;
398512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
399512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
400512f29d1SFedor Uporov 		return;
401512f29d1SFedor Uporov 
402512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
403512f29d1SFedor Uporov 	tp->ht_checksum = ext2_dx_csum(ip, ep,  count_offset, count, tp);
404512f29d1SFedor Uporov }
405512f29d1SFedor Uporov 
406512f29d1SFedor Uporov static uint32_t
407512f29d1SFedor Uporov ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
408512f29d1SFedor Uporov {
409512f29d1SFedor Uporov 	struct m_ext2fs *fs;
410512f29d1SFedor Uporov 	size_t size;
411512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
412512f29d1SFedor Uporov 
413512f29d1SFedor Uporov 	fs = ip->i_e2fs;
414512f29d1SFedor Uporov 
415512f29d1SFedor Uporov 	size = EXT4_EXTENT_TAIL_OFFSET(ehp) +
416512f29d1SFedor Uporov 	    offsetof(struct ext4_extent_tail, et_checksum);
417512f29d1SFedor Uporov 
418512f29d1SFedor Uporov 	inum = ip->i_number;
419512f29d1SFedor Uporov 	gen = ip->i_gen;
420512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
421512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
422512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ehp, size);
423512f29d1SFedor Uporov 
424512f29d1SFedor Uporov 	return (crc);
425512f29d1SFedor Uporov }
426512f29d1SFedor Uporov 
427512f29d1SFedor Uporov int
428512f29d1SFedor Uporov ext2_extent_blk_csum_verify(struct inode *ip, void *data)
429512f29d1SFedor Uporov {
430512f29d1SFedor Uporov 	struct m_ext2fs *fs;
431512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
432512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
433512f29d1SFedor Uporov 	uint32_t provided, calculated;
434512f29d1SFedor Uporov 
435512f29d1SFedor Uporov 	fs = ip->i_e2fs;
436512f29d1SFedor Uporov 
437512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
438512f29d1SFedor Uporov 		return (0);
439512f29d1SFedor Uporov 
440512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
441512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)ehp) +
442512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
443512f29d1SFedor Uporov 
444512f29d1SFedor Uporov 	provided = etp->et_checksum;
445512f29d1SFedor Uporov 	calculated = ext2_extent_blk_csum(ip, ehp);
446512f29d1SFedor Uporov 
447512f29d1SFedor Uporov 	if (provided != calculated) {
448512f29d1SFedor Uporov 		printf("WARNING: bad extent csum detected, ip=%lu - run fsck\n",
449512f29d1SFedor Uporov 		    (unsigned long)ip->i_number);
450512f29d1SFedor Uporov 		return (EIO);
451512f29d1SFedor Uporov 	}
452512f29d1SFedor Uporov 
453512f29d1SFedor Uporov 	return (0);
454512f29d1SFedor Uporov }
455512f29d1SFedor Uporov 
456512f29d1SFedor Uporov void
457512f29d1SFedor Uporov ext2_extent_blk_csum_set(struct inode *ip, void *data)
458512f29d1SFedor Uporov {
459512f29d1SFedor Uporov 	struct m_ext2fs *fs;
460512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
461512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
462512f29d1SFedor Uporov 
463512f29d1SFedor Uporov 	fs = ip->i_e2fs;
464512f29d1SFedor Uporov 
465512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
466512f29d1SFedor Uporov 		return;
467512f29d1SFedor Uporov 
468512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
469512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)data) +
470512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
471512f29d1SFedor Uporov 
472512f29d1SFedor Uporov 	etp->et_checksum = ext2_extent_blk_csum(ip,
473512f29d1SFedor Uporov 	    (struct ext4_extent_header *)data);
474512f29d1SFedor Uporov }
475512f29d1SFedor Uporov 
476512f29d1SFedor Uporov int
477512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
478512f29d1SFedor Uporov {
479512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
480512f29d1SFedor Uporov 
481512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
482512f29d1SFedor Uporov 		return (0);
483512f29d1SFedor Uporov 
484512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum;
485512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
486512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
487512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) {
488512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi;
489512f29d1SFedor Uporov 		provided |= (hi << 16);
490512f29d1SFedor Uporov 	} else
491512f29d1SFedor Uporov 		calculated &= 0xFFFF;
492512f29d1SFedor Uporov 
493512f29d1SFedor Uporov 	if (provided != calculated) {
494512f29d1SFedor Uporov 		printf("WARNING: bad inode bitmap csum detected, "
495512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
496512f29d1SFedor Uporov 		return (EIO);
497512f29d1SFedor Uporov 	}
498512f29d1SFedor Uporov 
499512f29d1SFedor Uporov 	return (0);
500512f29d1SFedor Uporov }
501512f29d1SFedor Uporov 
502512f29d1SFedor Uporov void
503512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
504512f29d1SFedor Uporov {
505512f29d1SFedor Uporov 	uint32_t csum;
506512f29d1SFedor Uporov 
507512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
508512f29d1SFedor Uporov 		return;
509512f29d1SFedor Uporov 
510512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
511512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
512512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF;
513512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END)
514512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16;
515512f29d1SFedor Uporov }
516512f29d1SFedor Uporov 
517512f29d1SFedor Uporov int
518512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
519512f29d1SFedor Uporov {
520512f29d1SFedor Uporov 	uint32_t hi, provided, calculated, size;
521512f29d1SFedor Uporov 
522512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
523512f29d1SFedor Uporov 		return (0);
524512f29d1SFedor Uporov 
525512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
526512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum;
527512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
528512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) {
529512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi;
530512f29d1SFedor Uporov 		provided |= (hi << 16);
531512f29d1SFedor Uporov 	} else
532512f29d1SFedor Uporov 		calculated &= 0xFFFF;
533512f29d1SFedor Uporov 
534512f29d1SFedor Uporov 	if (provided != calculated) {
535512f29d1SFedor Uporov 		printf("WARNING: bad block bitmap csum detected, "
536512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
537512f29d1SFedor Uporov 		return (EIO);
538512f29d1SFedor Uporov 	}
539512f29d1SFedor Uporov 
540512f29d1SFedor Uporov 	return (0);
541512f29d1SFedor Uporov }
542512f29d1SFedor Uporov 
543512f29d1SFedor Uporov void
544512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
545512f29d1SFedor Uporov {
546512f29d1SFedor Uporov 	uint32_t csum, size;
547512f29d1SFedor Uporov 
548512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
549512f29d1SFedor Uporov 		return;
550512f29d1SFedor Uporov 
551512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
552512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
553512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF;
554512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
555512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16;
556512f29d1SFedor Uporov }
557512f29d1SFedor Uporov 
558512f29d1SFedor Uporov static uint32_t
559512f29d1SFedor Uporov ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
560512f29d1SFedor Uporov {
561512f29d1SFedor Uporov 	struct m_ext2fs *fs;
562c4aa9a02SFedor Uporov 	uint32_t inode_csum_seed, inum, gen, crc;
563c4aa9a02SFedor Uporov 	uint16_t dummy_csum = 0;
564c4aa9a02SFedor Uporov 	unsigned int offset, csum_size;
565512f29d1SFedor Uporov 
566512f29d1SFedor Uporov 	fs = ip->i_e2fs;
567c4aa9a02SFedor Uporov 	offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo);
568c4aa9a02SFedor Uporov 	csum_size = sizeof(dummy_csum);
569512f29d1SFedor Uporov 	inum = ip->i_number;
570c4aa9a02SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed,
571c4aa9a02SFedor Uporov 	    (uint8_t *)&inum, sizeof(inum));
5726d4a4ed7SFedor Uporov 	gen = ip->i_gen;
573c4aa9a02SFedor Uporov 	inode_csum_seed = calculate_crc32c(crc,
574c4aa9a02SFedor Uporov 	    (uint8_t *)&gen, sizeof(gen));
575512f29d1SFedor Uporov 
576c4aa9a02SFedor Uporov 	crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset);
577c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size);
578c4aa9a02SFedor Uporov 	offset += csum_size;
579c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
580c4aa9a02SFedor Uporov 	    E2FS_REV0_INODE_SIZE - offset);
581512f29d1SFedor Uporov 
582c4aa9a02SFedor Uporov 	if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) {
583c4aa9a02SFedor Uporov 		offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi);
584c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei +
585c4aa9a02SFedor Uporov 		    E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE);
586c4aa9a02SFedor Uporov 
587c4aa9a02SFedor Uporov 		if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
588c4aa9a02SFedor Uporov 		    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
589c4aa9a02SFedor Uporov 			crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum,
590c4aa9a02SFedor Uporov 			    csum_size);
591c4aa9a02SFedor Uporov 			offset += csum_size;
592c4aa9a02SFedor Uporov 		}
593c4aa9a02SFedor Uporov 
594c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
595c4aa9a02SFedor Uporov 		    EXT2_INODE_SIZE(fs) - offset);
596c4aa9a02SFedor Uporov 	}
597512f29d1SFedor Uporov 
598512f29d1SFedor Uporov 	return (crc);
599512f29d1SFedor Uporov }
600512f29d1SFedor Uporov 
601512f29d1SFedor Uporov int
602512f29d1SFedor Uporov ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
603512f29d1SFedor Uporov {
604512f29d1SFedor Uporov 	struct m_ext2fs *fs;
605512f29d1SFedor Uporov 	const static struct ext2fs_dinode ei_zero;
606512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
607512f29d1SFedor Uporov 
608512f29d1SFedor Uporov 	fs = ip->i_e2fs;
609512f29d1SFedor Uporov 
610512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
611512f29d1SFedor Uporov 		return (0);
612512f29d1SFedor Uporov 
613512f29d1SFedor Uporov 	provided = ei->e2di_chksum_lo;
614512f29d1SFedor Uporov 	calculated = ext2_ei_csum(ip, ei);
615512f29d1SFedor Uporov 
616512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
617512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
618512f29d1SFedor Uporov 		hi = ei->e2di_chksum_hi;
619512f29d1SFedor Uporov 		provided |= hi << 16;
620512f29d1SFedor Uporov 	} else
621512f29d1SFedor Uporov 		calculated &= 0xFFFF;
622512f29d1SFedor Uporov 
623c4aa9a02SFedor Uporov 	if (provided != calculated) {
624c4aa9a02SFedor Uporov 		/*
625c4aa9a02SFedor Uporov 		 * If it is first time used dinode,
626c4aa9a02SFedor Uporov 		 * it is expected that it will be zeroed
627c4aa9a02SFedor Uporov 		 * and we will not return checksum error in this case.
628c4aa9a02SFedor Uporov 		 */
629c4aa9a02SFedor Uporov 		if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
630c4aa9a02SFedor Uporov 			return (0);
631c4aa9a02SFedor Uporov 
632512f29d1SFedor Uporov 		return (EIO);
633c4aa9a02SFedor Uporov 	}
634512f29d1SFedor Uporov 
635512f29d1SFedor Uporov 	return (0);
636512f29d1SFedor Uporov }
637512f29d1SFedor Uporov 
638512f29d1SFedor Uporov void
639512f29d1SFedor Uporov ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
640512f29d1SFedor Uporov {
641512f29d1SFedor Uporov 	struct m_ext2fs *fs;
642512f29d1SFedor Uporov 	uint32_t crc;
643512f29d1SFedor Uporov 
644512f29d1SFedor Uporov 	fs = ip->i_e2fs;
645512f29d1SFedor Uporov 
646512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
647512f29d1SFedor Uporov 		return;
648512f29d1SFedor Uporov 
649512f29d1SFedor Uporov 	crc = ext2_ei_csum(ip, ei);
650512f29d1SFedor Uporov 
651512f29d1SFedor Uporov 	ei->e2di_chksum_lo = crc & 0xFFFF;
652512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
653512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
654512f29d1SFedor Uporov 		ei->e2di_chksum_hi = crc >> 16;
655512f29d1SFedor Uporov }
656512f29d1SFedor Uporov 
657d23db91eSPedro F. Giffuni static uint16_t
658d23db91eSPedro F. Giffuni ext2_crc16(uint16_t crc, const void *buffer, unsigned int len)
659d23db91eSPedro F. Giffuni {
660d23db91eSPedro F. Giffuni 	const unsigned char *cp = buffer;
661d23db91eSPedro F. Giffuni 	/* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
662d23db91eSPedro F. Giffuni 	static uint16_t const crc16_table[256] = {
663d23db91eSPedro F. Giffuni 		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
664d23db91eSPedro F. Giffuni 		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
665d23db91eSPedro F. Giffuni 		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
666d23db91eSPedro F. Giffuni 		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
667d23db91eSPedro F. Giffuni 		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
668d23db91eSPedro F. Giffuni 		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
669d23db91eSPedro F. Giffuni 		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
670d23db91eSPedro F. Giffuni 		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
671d23db91eSPedro F. Giffuni 		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
672d23db91eSPedro F. Giffuni 		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
673d23db91eSPedro F. Giffuni 		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
674d23db91eSPedro F. Giffuni 		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
675d23db91eSPedro F. Giffuni 		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
676d23db91eSPedro F. Giffuni 		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
677d23db91eSPedro F. Giffuni 		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
678d23db91eSPedro F. Giffuni 		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
679d23db91eSPedro F. Giffuni 		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
680d23db91eSPedro F. Giffuni 		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
681d23db91eSPedro F. Giffuni 		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
682d23db91eSPedro F. Giffuni 		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
683d23db91eSPedro F. Giffuni 		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
684d23db91eSPedro F. Giffuni 		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
685d23db91eSPedro F. Giffuni 		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
686d23db91eSPedro F. Giffuni 		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
687d23db91eSPedro F. Giffuni 		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
688d23db91eSPedro F. Giffuni 		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
689d23db91eSPedro F. Giffuni 		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
690d23db91eSPedro F. Giffuni 		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
691d23db91eSPedro F. Giffuni 		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
692d23db91eSPedro F. Giffuni 		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
693d23db91eSPedro F. Giffuni 		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
694d23db91eSPedro F. Giffuni 		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
695d23db91eSPedro F. Giffuni 	};
696d23db91eSPedro F. Giffuni 
697d23db91eSPedro F. Giffuni 	while (len--)
698d23db91eSPedro F. Giffuni 		crc = (((crc >> 8) & 0xffU) ^
699d23db91eSPedro F. Giffuni 		    crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
700d23db91eSPedro F. Giffuni 	return crc;
701d23db91eSPedro F. Giffuni }
702d23db91eSPedro F. Giffuni 
703d23db91eSPedro F. Giffuni static uint16_t
704d23db91eSPedro F. Giffuni ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
705d23db91eSPedro F. Giffuni {
706d23db91eSPedro F. Giffuni 	size_t offset;
707512f29d1SFedor Uporov 	uint32_t csum32;
708512f29d1SFedor Uporov 	uint16_t crc, dummy_csum;
709d23db91eSPedro F. Giffuni 
710d23db91eSPedro F. Giffuni 	offset = offsetof(struct ext2_gd, ext4bgd_csum);
711d23db91eSPedro F. Giffuni 
712512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
713512f29d1SFedor Uporov 		csum32 = calculate_crc32c(fs->e2fs_csum_seed,
714512f29d1SFedor Uporov 		    (uint8_t *)&block_group, sizeof(block_group));
715512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
716512f29d1SFedor Uporov 		dummy_csum = 0;
717512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
718512f29d1SFedor Uporov 		    sizeof(dummy_csum));
719512f29d1SFedor Uporov 		offset += sizeof(dummy_csum);
720512f29d1SFedor Uporov 		if (offset < fs->e2fs->e3fs_desc_size)
721512f29d1SFedor Uporov 			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
722512f29d1SFedor Uporov 			    fs->e2fs->e3fs_desc_size - offset);
723512f29d1SFedor Uporov 
724512f29d1SFedor Uporov 		crc = csum32 & 0xFFFF;
725512f29d1SFedor Uporov 		return (crc);
726512f29d1SFedor Uporov 	} else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
727d23db91eSPedro F. Giffuni 		crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid,
728d23db91eSPedro F. Giffuni 		    sizeof(fs->e2fs->e2fs_uuid));
729d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)&block_group,
730d23db91eSPedro F. Giffuni 		    sizeof(block_group));
731d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)gd, offset);
732d23db91eSPedro F. Giffuni 		offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
733d23db91eSPedro F. Giffuni 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
734d23db91eSPedro F. Giffuni 		    offset < fs->e2fs->e3fs_desc_size)
735d23db91eSPedro F. Giffuni 			crc = ext2_crc16(crc, (uint8_t *)gd + offset,
736d23db91eSPedro F. Giffuni 			    fs->e2fs->e3fs_desc_size - offset);
737d23db91eSPedro F. Giffuni 		return (crc);
738d23db91eSPedro F. Giffuni 	}
739d23db91eSPedro F. Giffuni 
740d23db91eSPedro F. Giffuni 	return (0);
741d23db91eSPedro F. Giffuni }
742d23db91eSPedro F. Giffuni 
743d23db91eSPedro F. Giffuni int
744d23db91eSPedro F. Giffuni ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
745d23db91eSPedro F. Giffuni {
746d23db91eSPedro F. Giffuni 	unsigned int i;
747d23db91eSPedro F. Giffuni 	int error = 0;
748d23db91eSPedro F. Giffuni 
749d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++) {
750d23db91eSPedro F. Giffuni 		if (fs->e2fs_gd[i].ext4bgd_csum !=
751d23db91eSPedro F. Giffuni 		    ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
752d23db91eSPedro F. Giffuni 			printf(
753d23db91eSPedro F. Giffuni "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
754d23db91eSPedro F. Giffuni 			    devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
755d23db91eSPedro F. Giffuni 			    ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
756512f29d1SFedor Uporov 			error = EIO;
757d23db91eSPedro F. Giffuni 			break;
758d23db91eSPedro F. Giffuni 		}
759d23db91eSPedro F. Giffuni 	}
760d23db91eSPedro F. Giffuni 
761d23db91eSPedro F. Giffuni 	return (error);
762d23db91eSPedro F. Giffuni }
763d23db91eSPedro F. Giffuni 
764d23db91eSPedro F. Giffuni void
765d23db91eSPedro F. Giffuni ext2_gd_csum_set(struct m_ext2fs *fs)
766d23db91eSPedro F. Giffuni {
767d23db91eSPedro F. Giffuni 	unsigned int i;
768d23db91eSPedro F. Giffuni 
769d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++)
770d23db91eSPedro F. Giffuni 		    fs->e2fs_gd[i].ext4bgd_csum =
771d23db91eSPedro F. Giffuni 			ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
772d23db91eSPedro F. Giffuni }
773