xref: /freebsd/sys/fs/ext2fs/ext2_csum.c (revision c4aa9a026d9fd685bb417ae172875ce6d1e01e1b)
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 
157512f29d1SFedor Uporov static struct ext2fs_direct_tail *
158512f29d1SFedor Uporov ext2_get_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
159512f29d1SFedor Uporov {
160512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
161512f29d1SFedor Uporov 
162512f29d1SFedor Uporov 	tp = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
163512f29d1SFedor Uporov 	if (tp->e2dt_reserved_zero1 ||
164512f29d1SFedor Uporov 	    tp->e2dt_rec_len != sizeof(struct ext2fs_direct_tail) ||
165512f29d1SFedor Uporov 	    tp->e2dt_reserved_zero2 ||
166512f29d1SFedor Uporov 	    tp->e2dt_reserved_ft != EXT2_FT_DIR_CSUM)
167512f29d1SFedor Uporov 		return (NULL);
168512f29d1SFedor Uporov 
169512f29d1SFedor Uporov 	return (tp);
170512f29d1SFedor Uporov }
171512f29d1SFedor Uporov 
172512f29d1SFedor Uporov static uint32_t
173512f29d1SFedor Uporov ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
174512f29d1SFedor Uporov {
175512f29d1SFedor Uporov 	struct m_ext2fs *fs;
176512f29d1SFedor Uporov 	char *buf;
177512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
178512f29d1SFedor Uporov 
179512f29d1SFedor Uporov 	fs = ip->i_e2fs;
180512f29d1SFedor Uporov 
181512f29d1SFedor Uporov 	buf = (char *)ep;
182512f29d1SFedor Uporov 
183512f29d1SFedor Uporov 	inum = ip->i_number;
184512f29d1SFedor Uporov 	gen = ip->i_gen;
185512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
186512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
187512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
188512f29d1SFedor Uporov 
189512f29d1SFedor Uporov 	return (crc);
190512f29d1SFedor Uporov }
191512f29d1SFedor Uporov 
192512f29d1SFedor Uporov static int
193512f29d1SFedor Uporov ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
194512f29d1SFedor Uporov {
195512f29d1SFedor Uporov 	uint32_t calculated;
196512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
197512f29d1SFedor Uporov 
198512f29d1SFedor Uporov 	tp = ext2_get_dirent_tail(ip, ep);
199512f29d1SFedor Uporov 	if (tp == NULL)
200512f29d1SFedor Uporov 		return (0);
201512f29d1SFedor Uporov 
202512f29d1SFedor Uporov 	calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
203512f29d1SFedor Uporov 	if (calculated != tp->e2dt_checksum)
204512f29d1SFedor Uporov 		return (EIO);
205512f29d1SFedor Uporov 
206512f29d1SFedor Uporov 	return (0);
207512f29d1SFedor Uporov }
208512f29d1SFedor Uporov 
209512f29d1SFedor Uporov static struct ext2fs_htree_count *
210512f29d1SFedor Uporov ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset)
211512f29d1SFedor Uporov {
212512f29d1SFedor Uporov 	struct ext2fs_direct_2 *dp;
213512f29d1SFedor Uporov 	struct ext2fs_htree_root_info *root;
214512f29d1SFedor Uporov 	int count_offset;
215512f29d1SFedor Uporov 
216512f29d1SFedor Uporov 	if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs))
217512f29d1SFedor Uporov 		count_offset = 8;
218512f29d1SFedor Uporov 	else if (ep->e2d_reclen == 12) {
219512f29d1SFedor Uporov 		dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12);
220512f29d1SFedor Uporov 		if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12)
221512f29d1SFedor Uporov 			return (NULL);
222512f29d1SFedor Uporov 
223512f29d1SFedor Uporov 		root = (struct ext2fs_htree_root_info *)(((char *)dp + 12));
224512f29d1SFedor Uporov 		if (root->h_reserved1 ||
225512f29d1SFedor Uporov 		    root->h_info_len != sizeof(struct ext2fs_htree_root_info))
226512f29d1SFedor Uporov 			return (NULL);
227512f29d1SFedor Uporov 
228512f29d1SFedor Uporov 		count_offset = 32;
229512f29d1SFedor Uporov 	} else
230512f29d1SFedor Uporov 		return (NULL);
231512f29d1SFedor Uporov 
232512f29d1SFedor Uporov 	if (offset)
233512f29d1SFedor Uporov 		*offset = count_offset;
234512f29d1SFedor Uporov 
235512f29d1SFedor Uporov 	return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset));
236512f29d1SFedor Uporov }
237512f29d1SFedor Uporov 
238512f29d1SFedor Uporov static uint32_t
239512f29d1SFedor Uporov ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
240512f29d1SFedor Uporov     int count, struct ext2fs_htree_tail *tp)
241512f29d1SFedor Uporov {
242512f29d1SFedor Uporov 	struct m_ext2fs *fs;
243512f29d1SFedor Uporov 	char *buf;
244512f29d1SFedor Uporov 	int size;
245512f29d1SFedor Uporov 	uint32_t inum, old_csum, gen, crc;
246512f29d1SFedor Uporov 
247512f29d1SFedor Uporov 	fs = ip->i_e2fs;
248512f29d1SFedor Uporov 
249512f29d1SFedor Uporov 	buf = (char *)ep;
250512f29d1SFedor Uporov 
251512f29d1SFedor Uporov 	size = count_offset + (count * sizeof(struct ext2fs_htree_entry));
252512f29d1SFedor Uporov 	old_csum = tp->ht_checksum;
253512f29d1SFedor Uporov 	tp->ht_checksum = 0;
254512f29d1SFedor Uporov 
255512f29d1SFedor Uporov 	inum = ip->i_number;
256512f29d1SFedor Uporov 	gen = ip->i_gen;
257512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
258512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
259512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)buf, size);
260512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail));
261512f29d1SFedor Uporov 	tp->ht_checksum = old_csum;
262512f29d1SFedor Uporov 
263512f29d1SFedor Uporov 	return (crc);
264512f29d1SFedor Uporov }
265512f29d1SFedor Uporov 
266512f29d1SFedor Uporov static int
267512f29d1SFedor Uporov ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
268512f29d1SFedor Uporov {
269512f29d1SFedor Uporov 	uint32_t calculated;
270512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
271512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
272512f29d1SFedor Uporov 	int count_offset, limit, count;
273512f29d1SFedor Uporov 
274512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
275512f29d1SFedor Uporov 	if (cp == NULL)
276512f29d1SFedor Uporov 		return (0);
277512f29d1SFedor Uporov 
278512f29d1SFedor Uporov 	limit = cp->h_entries_max;
279512f29d1SFedor Uporov 	count = cp->h_entries_num;
280512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
281512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
282512f29d1SFedor Uporov 		return (EIO);
283512f29d1SFedor Uporov 
284512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
285512f29d1SFedor Uporov 	calculated = ext2_dx_csum(ip, ep,  count_offset, count, tp);
286512f29d1SFedor Uporov 
287512f29d1SFedor Uporov 	if (tp->ht_checksum != calculated)
288512f29d1SFedor Uporov 		return (EIO);
289512f29d1SFedor Uporov 
290512f29d1SFedor Uporov 	return (0);
291512f29d1SFedor Uporov }
292512f29d1SFedor Uporov 
293512f29d1SFedor Uporov int
294512f29d1SFedor Uporov ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
295512f29d1SFedor Uporov {
296512f29d1SFedor Uporov 	struct m_ext2fs *fs;
297512f29d1SFedor Uporov 	struct ext2fs_direct_2 *ep;
298512f29d1SFedor Uporov 	int error = 0;
299512f29d1SFedor Uporov 
300512f29d1SFedor Uporov 	fs = ip->i_e2fs;
301512f29d1SFedor Uporov 
302512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
303512f29d1SFedor Uporov 		return (error);
304512f29d1SFedor Uporov 
305512f29d1SFedor Uporov 	ep = (struct ext2fs_direct_2 *)bp->b_data;
306512f29d1SFedor Uporov 
307512f29d1SFedor Uporov 	if (ext2_get_dirent_tail(ip, ep) != NULL)
308512f29d1SFedor Uporov 		error = ext2_dirent_csum_verify(ip, ep);
309512f29d1SFedor Uporov 	else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
310512f29d1SFedor Uporov 		error = ext2_dx_csum_verify(ip, ep);
311512f29d1SFedor Uporov 
312512f29d1SFedor Uporov 	if (error)
313512f29d1SFedor Uporov 		printf("WARNING: bad directory csum detected, ip=%lu"
314512f29d1SFedor Uporov 		    " - run fsck\n", (unsigned long)ip->i_number);
315512f29d1SFedor Uporov 
316512f29d1SFedor Uporov 	return (error);
317512f29d1SFedor Uporov }
318512f29d1SFedor Uporov 
319512f29d1SFedor Uporov static void
320512f29d1SFedor Uporov ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
321512f29d1SFedor Uporov {
322512f29d1SFedor Uporov 	struct ext2fs_direct_tail *tp;
323512f29d1SFedor Uporov 
324512f29d1SFedor Uporov 	tp = ext2_get_dirent_tail(ip, ep);
325512f29d1SFedor Uporov 	if (tp == NULL)
326512f29d1SFedor Uporov 		return;
327512f29d1SFedor Uporov 
328512f29d1SFedor Uporov 	tp->e2dt_checksum =
329512f29d1SFedor Uporov 	    ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
330512f29d1SFedor Uporov }
331512f29d1SFedor Uporov 
332512f29d1SFedor Uporov static void
333512f29d1SFedor Uporov ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
334512f29d1SFedor Uporov {
335512f29d1SFedor Uporov 	struct ext2fs_htree_count *cp;
336512f29d1SFedor Uporov 	struct ext2fs_htree_tail *tp;
337512f29d1SFedor Uporov 	int count_offset, limit, count;
338512f29d1SFedor Uporov 
339512f29d1SFedor Uporov 	cp = ext2_get_dx_count(ip, ep, &count_offset);
340512f29d1SFedor Uporov 	if (cp == NULL)
341512f29d1SFedor Uporov 		return;
342512f29d1SFedor Uporov 
343512f29d1SFedor Uporov 	limit = cp->h_entries_max;
344512f29d1SFedor Uporov 	count = cp->h_entries_num;
345512f29d1SFedor Uporov 	if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) >
346512f29d1SFedor Uporov 	    ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail))
347512f29d1SFedor Uporov 		return;
348512f29d1SFedor Uporov 
349512f29d1SFedor Uporov 	tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit);
350512f29d1SFedor Uporov 	tp->ht_checksum = ext2_dx_csum(ip, ep,  count_offset, count, tp);
351512f29d1SFedor Uporov }
352512f29d1SFedor Uporov 
353512f29d1SFedor Uporov void
354512f29d1SFedor Uporov ext2_dir_blk_csum_set_mem(struct inode *ip, char *buf, int size)
355512f29d1SFedor Uporov {
356512f29d1SFedor Uporov 	struct m_ext2fs *fs;
357512f29d1SFedor Uporov 	struct ext2fs_direct_2 *ep;
358512f29d1SFedor Uporov 
359512f29d1SFedor Uporov 	fs = ip->i_e2fs;
360512f29d1SFedor Uporov 
361512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
362512f29d1SFedor Uporov 		return;
363512f29d1SFedor Uporov 
364512f29d1SFedor Uporov 	ep = (struct ext2fs_direct_2 *)buf;
365512f29d1SFedor Uporov 
366512f29d1SFedor Uporov 	if (ext2_htree_has_idx(ip)) {
367512f29d1SFedor Uporov 		if (ext2_get_dx_count(ip, ep, NULL) != NULL)
368512f29d1SFedor Uporov 			ext2_dx_csum_set(ip, ep);
369512f29d1SFedor Uporov 	} else {
370512f29d1SFedor Uporov 		if (ext2_get_dirent_tail(ip, ep) != NULL)
371512f29d1SFedor Uporov 			ext2_dirent_csum_set(ip, ep);
372512f29d1SFedor Uporov 	}
373512f29d1SFedor Uporov }
374512f29d1SFedor Uporov 
375512f29d1SFedor Uporov void
376512f29d1SFedor Uporov ext2_dir_blk_csum_set(struct inode *ip, struct buf *bp)
377512f29d1SFedor Uporov {
378512f29d1SFedor Uporov 
379512f29d1SFedor Uporov 	ext2_dir_blk_csum_set_mem(ip, bp->b_data, bp->b_bufsize);
380512f29d1SFedor Uporov }
381512f29d1SFedor Uporov 
382512f29d1SFedor Uporov static uint32_t
383512f29d1SFedor Uporov ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
384512f29d1SFedor Uporov {
385512f29d1SFedor Uporov 	struct m_ext2fs *fs;
386512f29d1SFedor Uporov 	size_t size;
387512f29d1SFedor Uporov 	uint32_t inum, gen, crc;
388512f29d1SFedor Uporov 
389512f29d1SFedor Uporov 	fs = ip->i_e2fs;
390512f29d1SFedor Uporov 
391512f29d1SFedor Uporov 	size = EXT4_EXTENT_TAIL_OFFSET(ehp) +
392512f29d1SFedor Uporov 	    offsetof(struct ext4_extent_tail, et_checksum);
393512f29d1SFedor Uporov 
394512f29d1SFedor Uporov 	inum = ip->i_number;
395512f29d1SFedor Uporov 	gen = ip->i_gen;
396512f29d1SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum));
397512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen));
398512f29d1SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ehp, size);
399512f29d1SFedor Uporov 
400512f29d1SFedor Uporov 	return (crc);
401512f29d1SFedor Uporov }
402512f29d1SFedor Uporov 
403512f29d1SFedor Uporov int
404512f29d1SFedor Uporov ext2_extent_blk_csum_verify(struct inode *ip, void *data)
405512f29d1SFedor Uporov {
406512f29d1SFedor Uporov 	struct m_ext2fs *fs;
407512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
408512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
409512f29d1SFedor Uporov 	uint32_t provided, calculated;
410512f29d1SFedor Uporov 
411512f29d1SFedor Uporov 	fs = ip->i_e2fs;
412512f29d1SFedor Uporov 
413512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
414512f29d1SFedor Uporov 		return (0);
415512f29d1SFedor Uporov 
416512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
417512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)ehp) +
418512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
419512f29d1SFedor Uporov 
420512f29d1SFedor Uporov 	provided = etp->et_checksum;
421512f29d1SFedor Uporov 	calculated = ext2_extent_blk_csum(ip, ehp);
422512f29d1SFedor Uporov 
423512f29d1SFedor Uporov 	if (provided != calculated) {
424512f29d1SFedor Uporov 		printf("WARNING: bad extent csum detected, ip=%lu - run fsck\n",
425512f29d1SFedor Uporov 		    (unsigned long)ip->i_number);
426512f29d1SFedor Uporov 		return (EIO);
427512f29d1SFedor Uporov 	}
428512f29d1SFedor Uporov 
429512f29d1SFedor Uporov 	return (0);
430512f29d1SFedor Uporov }
431512f29d1SFedor Uporov 
432512f29d1SFedor Uporov void
433512f29d1SFedor Uporov ext2_extent_blk_csum_set(struct inode *ip, void *data)
434512f29d1SFedor Uporov {
435512f29d1SFedor Uporov 	struct m_ext2fs *fs;
436512f29d1SFedor Uporov 	struct ext4_extent_header *ehp;
437512f29d1SFedor Uporov 	struct ext4_extent_tail *etp;
438512f29d1SFedor Uporov 
439512f29d1SFedor Uporov 	fs = ip->i_e2fs;
440512f29d1SFedor Uporov 
441512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
442512f29d1SFedor Uporov 		return;
443512f29d1SFedor Uporov 
444512f29d1SFedor Uporov 	ehp = (struct ext4_extent_header *)data;
445512f29d1SFedor Uporov 	etp = (struct ext4_extent_tail *)(((char *)data) +
446512f29d1SFedor Uporov 	    EXT4_EXTENT_TAIL_OFFSET(ehp));
447512f29d1SFedor Uporov 
448512f29d1SFedor Uporov 	etp->et_checksum = ext2_extent_blk_csum(ip,
449512f29d1SFedor Uporov 	    (struct ext4_extent_header *)data);
450512f29d1SFedor Uporov }
451512f29d1SFedor Uporov 
452512f29d1SFedor Uporov int
453512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
454512f29d1SFedor Uporov {
455512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
456512f29d1SFedor Uporov 
457512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
458512f29d1SFedor Uporov 		return (0);
459512f29d1SFedor Uporov 
460512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum;
461512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
462512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
463512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) {
464512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi;
465512f29d1SFedor Uporov 		provided |= (hi << 16);
466512f29d1SFedor Uporov 	} else
467512f29d1SFedor Uporov 		calculated &= 0xFFFF;
468512f29d1SFedor Uporov 
469512f29d1SFedor Uporov 	if (provided != calculated) {
470512f29d1SFedor Uporov 		printf("WARNING: bad inode bitmap csum detected, "
471512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
472512f29d1SFedor Uporov 		return (EIO);
473512f29d1SFedor Uporov 	}
474512f29d1SFedor Uporov 
475512f29d1SFedor Uporov 	return (0);
476512f29d1SFedor Uporov }
477512f29d1SFedor Uporov 
478512f29d1SFedor Uporov void
479512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
480512f29d1SFedor Uporov {
481512f29d1SFedor Uporov 	uint32_t csum;
482512f29d1SFedor Uporov 
483512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
484512f29d1SFedor Uporov 		return;
485512f29d1SFedor Uporov 
486512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data,
487512f29d1SFedor Uporov 	    fs->e2fs->e2fs_ipg / 8);
488512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF;
489512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END)
490512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16;
491512f29d1SFedor Uporov }
492512f29d1SFedor Uporov 
493512f29d1SFedor Uporov int
494512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp)
495512f29d1SFedor Uporov {
496512f29d1SFedor Uporov 	uint32_t hi, provided, calculated, size;
497512f29d1SFedor Uporov 
498512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
499512f29d1SFedor Uporov 		return (0);
500512f29d1SFedor Uporov 
501512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
502512f29d1SFedor Uporov 	provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum;
503512f29d1SFedor Uporov 	calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
504512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) {
505512f29d1SFedor Uporov 		hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi;
506512f29d1SFedor Uporov 		provided |= (hi << 16);
507512f29d1SFedor Uporov 	} else
508512f29d1SFedor Uporov 		calculated &= 0xFFFF;
509512f29d1SFedor Uporov 
510512f29d1SFedor Uporov 	if (provided != calculated) {
511512f29d1SFedor Uporov 		printf("WARNING: bad block bitmap csum detected, "
512512f29d1SFedor Uporov 		    "cg=%d - run fsck\n", cg);
513512f29d1SFedor Uporov 		return (EIO);
514512f29d1SFedor Uporov 	}
515512f29d1SFedor Uporov 
516512f29d1SFedor Uporov 	return (0);
517512f29d1SFedor Uporov }
518512f29d1SFedor Uporov 
519512f29d1SFedor Uporov void
520512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp)
521512f29d1SFedor Uporov {
522512f29d1SFedor Uporov 	uint32_t csum, size;
523512f29d1SFedor Uporov 
524512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
525512f29d1SFedor Uporov 		return;
526512f29d1SFedor Uporov 
527512f29d1SFedor Uporov 	size = fs->e2fs_fpg / 8;
528512f29d1SFedor Uporov 	csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size);
529512f29d1SFedor Uporov 	fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF;
530512f29d1SFedor Uporov 	if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
531512f29d1SFedor Uporov 		fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16;
532512f29d1SFedor Uporov }
533512f29d1SFedor Uporov 
534512f29d1SFedor Uporov static uint32_t
535512f29d1SFedor Uporov ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
536512f29d1SFedor Uporov {
537512f29d1SFedor Uporov 	struct m_ext2fs *fs;
538*c4aa9a02SFedor Uporov 	uint32_t inode_csum_seed, inum, gen, crc;
539*c4aa9a02SFedor Uporov 	uint16_t dummy_csum = 0;
540*c4aa9a02SFedor Uporov 	unsigned int offset, csum_size;
541512f29d1SFedor Uporov 
542512f29d1SFedor Uporov 	fs = ip->i_e2fs;
543*c4aa9a02SFedor Uporov 	offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo);
544*c4aa9a02SFedor Uporov 	csum_size = sizeof(dummy_csum);
545512f29d1SFedor Uporov 	inum = ip->i_number;
546512f29d1SFedor Uporov 	gen = ip->i_gen;
547*c4aa9a02SFedor Uporov 	crc = calculate_crc32c(fs->e2fs_csum_seed,
548*c4aa9a02SFedor Uporov 	    (uint8_t *)&inum, sizeof(inum));
549*c4aa9a02SFedor Uporov 	inode_csum_seed = calculate_crc32c(crc,
550*c4aa9a02SFedor Uporov 	    (uint8_t *)&gen, sizeof(gen));
551512f29d1SFedor Uporov 
552*c4aa9a02SFedor Uporov 	crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset);
553*c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size);
554*c4aa9a02SFedor Uporov 	offset += csum_size;
555*c4aa9a02SFedor Uporov 	crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
556*c4aa9a02SFedor Uporov 	    E2FS_REV0_INODE_SIZE - offset);
557512f29d1SFedor Uporov 
558*c4aa9a02SFedor Uporov 	if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) {
559*c4aa9a02SFedor Uporov 		offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi);
560*c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei +
561*c4aa9a02SFedor Uporov 		    E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE);
562*c4aa9a02SFedor Uporov 
563*c4aa9a02SFedor Uporov 		if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE &&
564*c4aa9a02SFedor Uporov 		    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
565*c4aa9a02SFedor Uporov 			crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum,
566*c4aa9a02SFedor Uporov 			    csum_size);
567*c4aa9a02SFedor Uporov 			offset += csum_size;
568*c4aa9a02SFedor Uporov 		}
569*c4aa9a02SFedor Uporov 
570*c4aa9a02SFedor Uporov 		crc = calculate_crc32c(crc, (uint8_t *)ei + offset,
571*c4aa9a02SFedor Uporov 		    EXT2_INODE_SIZE(fs) - offset);
572*c4aa9a02SFedor Uporov 	}
573512f29d1SFedor Uporov 
574512f29d1SFedor Uporov 	return (crc);
575512f29d1SFedor Uporov }
576512f29d1SFedor Uporov 
577512f29d1SFedor Uporov int
578512f29d1SFedor Uporov ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei)
579512f29d1SFedor Uporov {
580512f29d1SFedor Uporov 	struct m_ext2fs *fs;
581512f29d1SFedor Uporov 	const static struct ext2fs_dinode ei_zero;
582512f29d1SFedor Uporov 	uint32_t hi, provided, calculated;
583512f29d1SFedor Uporov 
584512f29d1SFedor Uporov 	fs = ip->i_e2fs;
585512f29d1SFedor Uporov 
586512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
587512f29d1SFedor Uporov 		return (0);
588512f29d1SFedor Uporov 
589512f29d1SFedor Uporov 	provided = ei->e2di_chksum_lo;
590512f29d1SFedor Uporov 	calculated = ext2_ei_csum(ip, ei);
591512f29d1SFedor Uporov 
592512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
593512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) {
594512f29d1SFedor Uporov 		hi = ei->e2di_chksum_hi;
595512f29d1SFedor Uporov 		provided |= hi << 16;
596512f29d1SFedor Uporov 	} else
597512f29d1SFedor Uporov 		calculated &= 0xFFFF;
598512f29d1SFedor Uporov 
599*c4aa9a02SFedor Uporov 	if (provided != calculated) {
600*c4aa9a02SFedor Uporov 		/*
601*c4aa9a02SFedor Uporov 		 * If it is first time used dinode,
602*c4aa9a02SFedor Uporov 		 * it is expected that it will be zeroed
603*c4aa9a02SFedor Uporov 		 * and we will not return checksum error in this case.
604*c4aa9a02SFedor Uporov 		 */
605*c4aa9a02SFedor Uporov 		if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode)))
606*c4aa9a02SFedor Uporov 			return (0);
607*c4aa9a02SFedor Uporov 
608512f29d1SFedor Uporov 		return (EIO);
609*c4aa9a02SFedor Uporov 	}
610512f29d1SFedor Uporov 
611512f29d1SFedor Uporov 	return (0);
612512f29d1SFedor Uporov }
613512f29d1SFedor Uporov 
614512f29d1SFedor Uporov void
615512f29d1SFedor Uporov ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei)
616512f29d1SFedor Uporov {
617512f29d1SFedor Uporov 	struct m_ext2fs *fs;
618512f29d1SFedor Uporov 	uint32_t crc;
619512f29d1SFedor Uporov 
620512f29d1SFedor Uporov 	fs = ip->i_e2fs;
621512f29d1SFedor Uporov 
622512f29d1SFedor Uporov 	if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
623512f29d1SFedor Uporov 		return;
624512f29d1SFedor Uporov 
625512f29d1SFedor Uporov 	crc = ext2_ei_csum(ip, ei);
626512f29d1SFedor Uporov 
627512f29d1SFedor Uporov 	ei->e2di_chksum_lo = crc & 0xFFFF;
628512f29d1SFedor Uporov 	if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE &&
629512f29d1SFedor Uporov 	    ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END))
630512f29d1SFedor Uporov 		ei->e2di_chksum_hi = crc >> 16;
631512f29d1SFedor Uporov }
632512f29d1SFedor Uporov 
633d23db91eSPedro F. Giffuni static uint16_t
634d23db91eSPedro F. Giffuni ext2_crc16(uint16_t crc, const void *buffer, unsigned int len)
635d23db91eSPedro F. Giffuni {
636d23db91eSPedro F. Giffuni 	const unsigned char *cp = buffer;
637d23db91eSPedro F. Giffuni 	/* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
638d23db91eSPedro F. Giffuni 	static uint16_t const crc16_table[256] = {
639d23db91eSPedro F. Giffuni 		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
640d23db91eSPedro F. Giffuni 		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
641d23db91eSPedro F. Giffuni 		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
642d23db91eSPedro F. Giffuni 		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
643d23db91eSPedro F. Giffuni 		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
644d23db91eSPedro F. Giffuni 		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
645d23db91eSPedro F. Giffuni 		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
646d23db91eSPedro F. Giffuni 		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
647d23db91eSPedro F. Giffuni 		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
648d23db91eSPedro F. Giffuni 		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
649d23db91eSPedro F. Giffuni 		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
650d23db91eSPedro F. Giffuni 		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
651d23db91eSPedro F. Giffuni 		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
652d23db91eSPedro F. Giffuni 		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
653d23db91eSPedro F. Giffuni 		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
654d23db91eSPedro F. Giffuni 		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
655d23db91eSPedro F. Giffuni 		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
656d23db91eSPedro F. Giffuni 		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
657d23db91eSPedro F. Giffuni 		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
658d23db91eSPedro F. Giffuni 		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
659d23db91eSPedro F. Giffuni 		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
660d23db91eSPedro F. Giffuni 		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
661d23db91eSPedro F. Giffuni 		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
662d23db91eSPedro F. Giffuni 		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
663d23db91eSPedro F. Giffuni 		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
664d23db91eSPedro F. Giffuni 		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
665d23db91eSPedro F. Giffuni 		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
666d23db91eSPedro F. Giffuni 		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
667d23db91eSPedro F. Giffuni 		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
668d23db91eSPedro F. Giffuni 		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
669d23db91eSPedro F. Giffuni 		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
670d23db91eSPedro F. Giffuni 		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
671d23db91eSPedro F. Giffuni 	};
672d23db91eSPedro F. Giffuni 
673d23db91eSPedro F. Giffuni 	while (len--)
674d23db91eSPedro F. Giffuni 		crc = (((crc >> 8) & 0xffU) ^
675d23db91eSPedro F. Giffuni 		    crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
676d23db91eSPedro F. Giffuni 	return crc;
677d23db91eSPedro F. Giffuni }
678d23db91eSPedro F. Giffuni 
679d23db91eSPedro F. Giffuni static uint16_t
680d23db91eSPedro F. Giffuni ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd)
681d23db91eSPedro F. Giffuni {
682d23db91eSPedro F. Giffuni 	size_t offset;
683512f29d1SFedor Uporov 	uint32_t csum32;
684512f29d1SFedor Uporov 	uint16_t crc, dummy_csum;
685d23db91eSPedro F. Giffuni 
686d23db91eSPedro F. Giffuni 	offset = offsetof(struct ext2_gd, ext4bgd_csum);
687d23db91eSPedro F. Giffuni 
688512f29d1SFedor Uporov 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) {
689512f29d1SFedor Uporov 		csum32 = calculate_crc32c(fs->e2fs_csum_seed,
690512f29d1SFedor Uporov 		    (uint8_t *)&block_group, sizeof(block_group));
691512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset);
692512f29d1SFedor Uporov 		dummy_csum = 0;
693512f29d1SFedor Uporov 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
694512f29d1SFedor Uporov 		    sizeof(dummy_csum));
695512f29d1SFedor Uporov 		offset += sizeof(dummy_csum);
696512f29d1SFedor Uporov 		if (offset < fs->e2fs->e3fs_desc_size)
697512f29d1SFedor Uporov 			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
698512f29d1SFedor Uporov 			    fs->e2fs->e3fs_desc_size - offset);
699512f29d1SFedor Uporov 
700512f29d1SFedor Uporov 		crc = csum32 & 0xFFFF;
701512f29d1SFedor Uporov 		return (crc);
702512f29d1SFedor Uporov 	} else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) {
703d23db91eSPedro F. Giffuni 		crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid,
704d23db91eSPedro F. Giffuni 		    sizeof(fs->e2fs->e2fs_uuid));
705d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)&block_group,
706d23db91eSPedro F. Giffuni 		    sizeof(block_group));
707d23db91eSPedro F. Giffuni 		crc = ext2_crc16(crc, (uint8_t *)gd, offset);
708d23db91eSPedro F. Giffuni 		offset += sizeof(gd->ext4bgd_csum); /* skip checksum */
709d23db91eSPedro F. Giffuni 		if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
710d23db91eSPedro F. Giffuni 		    offset < fs->e2fs->e3fs_desc_size)
711d23db91eSPedro F. Giffuni 			crc = ext2_crc16(crc, (uint8_t *)gd + offset,
712d23db91eSPedro F. Giffuni 			    fs->e2fs->e3fs_desc_size - offset);
713d23db91eSPedro F. Giffuni 		return (crc);
714d23db91eSPedro F. Giffuni 	}
715d23db91eSPedro F. Giffuni 
716d23db91eSPedro F. Giffuni 	return (0);
717d23db91eSPedro F. Giffuni }
718d23db91eSPedro F. Giffuni 
719d23db91eSPedro F. Giffuni int
720d23db91eSPedro F. Giffuni ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev)
721d23db91eSPedro F. Giffuni {
722d23db91eSPedro F. Giffuni 	unsigned int i;
723d23db91eSPedro F. Giffuni 	int error = 0;
724d23db91eSPedro F. Giffuni 
725d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++) {
726d23db91eSPedro F. Giffuni 		if (fs->e2fs_gd[i].ext4bgd_csum !=
727d23db91eSPedro F. Giffuni 		    ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) {
728d23db91eSPedro F. Giffuni 			printf(
729d23db91eSPedro F. Giffuni "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n",
730d23db91eSPedro F. Giffuni 			    devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum,
731d23db91eSPedro F. Giffuni 			    ext2_gd_csum(fs, i, &fs->e2fs_gd[i]));
732512f29d1SFedor Uporov 			error = EIO;
733d23db91eSPedro F. Giffuni 			break;
734d23db91eSPedro F. Giffuni 		}
735d23db91eSPedro F. Giffuni 	}
736d23db91eSPedro F. Giffuni 
737d23db91eSPedro F. Giffuni 	return (error);
738d23db91eSPedro F. Giffuni }
739d23db91eSPedro F. Giffuni 
740d23db91eSPedro F. Giffuni void
741d23db91eSPedro F. Giffuni ext2_gd_csum_set(struct m_ext2fs *fs)
742d23db91eSPedro F. Giffuni {
743d23db91eSPedro F. Giffuni 	unsigned int i;
744d23db91eSPedro F. Giffuni 
745d23db91eSPedro F. Giffuni 	for (i = 0; i < fs->e2fs_gcount; i++)
746d23db91eSPedro F. Giffuni 		    fs->e2fs_gd[i].ext4bgd_csum =
747d23db91eSPedro F. Giffuni 			ext2_gd_csum(fs, i, &fs->e2fs_gd[i]);
748d23db91eSPedro F. Giffuni }
749