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> 43f89d2072SXin LI #include <sys/gsb_crc32.h> 44*b344bd3aSVal Packett #include <sys/crc16.h> 45d23db91eSPedro F. Giffuni #include <sys/mount.h> 46d23db91eSPedro F. Giffuni 47d23db91eSPedro F. Giffuni #include <fs/ext2fs/fs.h> 48d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2fs.h> 49512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dinode.h> 50d23db91eSPedro F. Giffuni #include <fs/ext2fs/inode.h> 51512f29d1SFedor Uporov #include <fs/ext2fs/ext2_dir.h> 52512f29d1SFedor Uporov #include <fs/ext2fs/htree.h> 53512f29d1SFedor Uporov #include <fs/ext2fs/ext2_extattr.h> 54d23db91eSPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h> 55d23db91eSPedro F. Giffuni 56ebc94b66SFedor Uporov SDT_PROVIDER_DECLARE(ext2fs); 57ebc94b66SFedor Uporov /* 58ebc94b66SFedor Uporov * ext2fs trace probe: 59ebc94b66SFedor Uporov * arg0: verbosity. Higher numbers give more verbose messages 60ebc94b66SFedor Uporov * arg1: Textual message 61ebc94b66SFedor Uporov */ 62ebc94b66SFedor Uporov SDT_PROBE_DEFINE2(ext2fs, , trace, csum, "int", "char*"); 63ebc94b66SFedor Uporov 64512f29d1SFedor Uporov #define EXT2_BG_INODE_BITMAP_CSUM_HI_END \ 65512f29d1SFedor Uporov (offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \ 66512f29d1SFedor Uporov sizeof(uint16_t)) 67512f29d1SFedor Uporov 68512f29d1SFedor Uporov #define EXT2_INODE_CSUM_HI_EXTRA_END \ 69512f29d1SFedor Uporov (offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \ 70512f29d1SFedor Uporov E2FS_REV0_INODE_SIZE) 71512f29d1SFedor Uporov 72512f29d1SFedor Uporov #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \ 73512f29d1SFedor Uporov (offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \ 74512f29d1SFedor Uporov sizeof(uint16_t)) 75512f29d1SFedor Uporov 76512f29d1SFedor Uporov void 77512f29d1SFedor Uporov ext2_sb_csum_set_seed(struct m_ext2fs *fs) 78512f29d1SFedor Uporov { 79512f29d1SFedor Uporov 80512f29d1SFedor Uporov if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED)) 81cd3acfe7SFedor Uporov fs->e2fs_csum_seed = le32toh(fs->e2fs->e4fs_chksum_seed); 82512f29d1SFedor Uporov else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { 83512f29d1SFedor Uporov fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid, 84512f29d1SFedor Uporov sizeof(fs->e2fs->e2fs_uuid)); 85512f29d1SFedor Uporov } 86512f29d1SFedor Uporov else 87512f29d1SFedor Uporov fs->e2fs_csum_seed = 0; 88512f29d1SFedor Uporov } 89512f29d1SFedor Uporov 90512f29d1SFedor Uporov int 91512f29d1SFedor Uporov ext2_sb_csum_verify(struct m_ext2fs *fs) 92512f29d1SFedor Uporov { 93512f29d1SFedor Uporov 94512f29d1SFedor Uporov if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) { 95512f29d1SFedor Uporov printf( 96512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt); 97512f29d1SFedor Uporov return (EINVAL); 98512f29d1SFedor Uporov } 99cd3acfe7SFedor Uporov if (le32toh(fs->e2fs->e4fs_sbchksum) != 100512f29d1SFedor Uporov calculate_crc32c(~0, (const char *)fs->e2fs, 101512f29d1SFedor Uporov offsetof(struct ext2fs, e4fs_sbchksum))) { 102512f29d1SFedor Uporov printf( 103512f29d1SFedor Uporov "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n", 104cd3acfe7SFedor Uporov fs->e2fs_fsmnt, le32toh(fs->e2fs->e4fs_sbchksum), 105cd3acfe7SFedor Uporov calculate_crc32c(~0, (const char *)fs->e2fs, 106cd3acfe7SFedor Uporov offsetof(struct ext2fs, e4fs_sbchksum))); 107512f29d1SFedor Uporov return (EINVAL); 108512f29d1SFedor Uporov } 109512f29d1SFedor Uporov 110512f29d1SFedor Uporov return (0); 111512f29d1SFedor Uporov } 112512f29d1SFedor Uporov 113512f29d1SFedor Uporov void 114512f29d1SFedor Uporov ext2_sb_csum_set(struct m_ext2fs *fs) 115512f29d1SFedor Uporov { 116512f29d1SFedor Uporov 117cd3acfe7SFedor Uporov fs->e2fs->e4fs_sbchksum = 118cd3acfe7SFedor Uporov htole32(calculate_crc32c(~0, (const char *)fs->e2fs, 119cd3acfe7SFedor Uporov offsetof(struct ext2fs, e4fs_sbchksum))); 120512f29d1SFedor Uporov } 121512f29d1SFedor Uporov 122512f29d1SFedor Uporov static uint32_t 123512f29d1SFedor Uporov ext2_extattr_blk_csum(struct inode *ip, uint64_t facl, 124512f29d1SFedor Uporov struct ext2fs_extattr_header *header) 125512f29d1SFedor Uporov { 126512f29d1SFedor Uporov struct m_ext2fs *fs; 127cd3acfe7SFedor Uporov uint32_t crc, dummy_crc = 0; 128cd3acfe7SFedor Uporov uint64_t facl_bn = htole64(facl); 129cd3acfe7SFedor Uporov int offset = offsetof(struct ext2fs_extattr_header, h_checksum); 130512f29d1SFedor Uporov 131512f29d1SFedor Uporov fs = ip->i_e2fs; 132512f29d1SFedor Uporov 133cd3acfe7SFedor Uporov crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl_bn, 134cd3acfe7SFedor Uporov sizeof(facl_bn)); 135cd3acfe7SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)header, offset); 136cd3acfe7SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)&dummy_crc, 137cd3acfe7SFedor Uporov sizeof(dummy_crc)); 138cd3acfe7SFedor Uporov offset += sizeof(dummy_crc); 139cd3acfe7SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)header + offset, 140cd3acfe7SFedor Uporov fs->e2fs_bsize - offset); 141512f29d1SFedor Uporov 142cd3acfe7SFedor Uporov return (htole32(crc)); 143512f29d1SFedor Uporov } 144512f29d1SFedor Uporov 145512f29d1SFedor Uporov int 146512f29d1SFedor Uporov ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp) 147512f29d1SFedor Uporov { 148512f29d1SFedor Uporov struct ext2fs_extattr_header *header; 149512f29d1SFedor Uporov 150512f29d1SFedor Uporov header = (struct ext2fs_extattr_header *)bp->b_data; 151512f29d1SFedor Uporov 152512f29d1SFedor Uporov if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) && 153512f29d1SFedor Uporov (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) { 154ebc94b66SFedor Uporov SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extattr csum detected"); 155512f29d1SFedor Uporov return (EIO); 156512f29d1SFedor Uporov } 157512f29d1SFedor Uporov 158512f29d1SFedor Uporov return (0); 159512f29d1SFedor Uporov } 160512f29d1SFedor Uporov 161512f29d1SFedor Uporov void 162512f29d1SFedor Uporov ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp) 163512f29d1SFedor Uporov { 164512f29d1SFedor Uporov struct ext2fs_extattr_header *header; 165512f29d1SFedor Uporov 166512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 167512f29d1SFedor Uporov return; 168512f29d1SFedor Uporov 169512f29d1SFedor Uporov header = (struct ext2fs_extattr_header *)bp->b_data; 170512f29d1SFedor Uporov header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header); 171512f29d1SFedor Uporov } 172512f29d1SFedor Uporov 1736d4a4ed7SFedor Uporov void 1746d4a4ed7SFedor Uporov ext2_init_dirent_tail(struct ext2fs_direct_tail *tp) 175512f29d1SFedor Uporov { 1766d4a4ed7SFedor Uporov memset(tp, 0, sizeof(struct ext2fs_direct_tail)); 177cd3acfe7SFedor Uporov tp->e2dt_rec_len = le16toh(sizeof(struct ext2fs_direct_tail)); 1786d4a4ed7SFedor Uporov tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM; 1796d4a4ed7SFedor Uporov } 180512f29d1SFedor Uporov 181e49d64a7SFedor Uporov int 182e49d64a7SFedor Uporov ext2_is_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep) 183e49d64a7SFedor Uporov { 184e49d64a7SFedor Uporov struct m_ext2fs *fs; 185e49d64a7SFedor Uporov struct ext2fs_direct_tail *tp; 186e49d64a7SFedor Uporov 187e49d64a7SFedor Uporov fs = ip->i_e2fs; 188e49d64a7SFedor Uporov 189e49d64a7SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 190e49d64a7SFedor Uporov return (0); 191e49d64a7SFedor Uporov 192e49d64a7SFedor Uporov tp = (struct ext2fs_direct_tail *)ep; 193e49d64a7SFedor Uporov if (tp->e2dt_reserved_zero1 == 0 && 194cd3acfe7SFedor Uporov le16toh(tp->e2dt_rec_len) == sizeof(struct ext2fs_direct_tail) && 195e49d64a7SFedor Uporov tp->e2dt_reserved_zero2 == 0 && 196e49d64a7SFedor Uporov tp->e2dt_reserved_ft == EXT2_FT_DIR_CSUM) 197e49d64a7SFedor Uporov return (1); 198e49d64a7SFedor Uporov 199e49d64a7SFedor Uporov return (0); 200e49d64a7SFedor Uporov } 201e49d64a7SFedor Uporov 2026d4a4ed7SFedor Uporov struct ext2fs_direct_tail * 2036d4a4ed7SFedor Uporov ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep) 2046d4a4ed7SFedor Uporov { 2056d4a4ed7SFedor Uporov struct ext2fs_direct_2 *dep; 2066d4a4ed7SFedor Uporov void *top; 2076d4a4ed7SFedor Uporov unsigned int rec_len; 2086d4a4ed7SFedor Uporov 2096d4a4ed7SFedor Uporov dep = ep; 2106d4a4ed7SFedor Uporov top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize); 211cd3acfe7SFedor Uporov rec_len = le16toh(dep->e2d_reclen); 2126d4a4ed7SFedor Uporov 2136d4a4ed7SFedor Uporov while (rec_len && !(rec_len & 0x3)) { 2146d4a4ed7SFedor Uporov dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len); 2156d4a4ed7SFedor Uporov if ((void *)dep >= top) 2166d4a4ed7SFedor Uporov break; 217cd3acfe7SFedor Uporov rec_len = le16toh(dep->e2d_reclen); 2186d4a4ed7SFedor Uporov } 2196d4a4ed7SFedor Uporov 2206d4a4ed7SFedor Uporov if (dep != top) 2216d4a4ed7SFedor Uporov return (NULL); 2226d4a4ed7SFedor Uporov 223e49d64a7SFedor Uporov if (ext2_is_dirent_tail(ip, dep)) 224e49d64a7SFedor Uporov return ((struct ext2fs_direct_tail *)dep); 225512f29d1SFedor Uporov 226e49d64a7SFedor Uporov return (NULL); 227512f29d1SFedor Uporov } 228512f29d1SFedor Uporov 229512f29d1SFedor Uporov static uint32_t 230512f29d1SFedor Uporov ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size) 231512f29d1SFedor Uporov { 232512f29d1SFedor Uporov struct m_ext2fs *fs; 233512f29d1SFedor Uporov char *buf; 234512f29d1SFedor Uporov uint32_t inum, gen, crc; 235512f29d1SFedor Uporov 236512f29d1SFedor Uporov fs = ip->i_e2fs; 237512f29d1SFedor Uporov 238512f29d1SFedor Uporov buf = (char *)ep; 239512f29d1SFedor Uporov 240cd3acfe7SFedor Uporov inum = htole32(ip->i_number); 241cd3acfe7SFedor Uporov gen = htole32(ip->i_gen); 242512f29d1SFedor Uporov crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 243512f29d1SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 244512f29d1SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)buf, size); 245512f29d1SFedor Uporov 246512f29d1SFedor Uporov return (crc); 247512f29d1SFedor Uporov } 248512f29d1SFedor Uporov 2496d4a4ed7SFedor Uporov int 250512f29d1SFedor Uporov ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 251512f29d1SFedor Uporov { 252512f29d1SFedor Uporov uint32_t calculated; 253512f29d1SFedor Uporov struct ext2fs_direct_tail *tp; 254512f29d1SFedor Uporov 2556d4a4ed7SFedor Uporov tp = ext2_dirent_get_tail(ip, ep); 256512f29d1SFedor Uporov if (tp == NULL) 257512f29d1SFedor Uporov return (0); 258512f29d1SFedor Uporov 259512f29d1SFedor Uporov calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); 260cd3acfe7SFedor Uporov if (calculated != le32toh(tp->e2dt_checksum)) 261512f29d1SFedor Uporov return (EIO); 262512f29d1SFedor Uporov 263512f29d1SFedor Uporov return (0); 264512f29d1SFedor Uporov } 265512f29d1SFedor Uporov 266512f29d1SFedor Uporov static struct ext2fs_htree_count * 267512f29d1SFedor Uporov ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset) 268512f29d1SFedor Uporov { 269512f29d1SFedor Uporov struct ext2fs_direct_2 *dp; 270512f29d1SFedor Uporov struct ext2fs_htree_root_info *root; 271512f29d1SFedor Uporov int count_offset; 272512f29d1SFedor Uporov 273cd3acfe7SFedor Uporov if (le16toh(ep->e2d_reclen) == EXT2_BLOCK_SIZE(ip->i_e2fs)) 274512f29d1SFedor Uporov count_offset = 8; 275cd3acfe7SFedor Uporov else if (le16toh(ep->e2d_reclen) == 12) { 276512f29d1SFedor Uporov dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12); 277cd3acfe7SFedor Uporov if (le16toh(dp->e2d_reclen) != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12) 278512f29d1SFedor Uporov return (NULL); 279512f29d1SFedor Uporov 280512f29d1SFedor Uporov root = (struct ext2fs_htree_root_info *)(((char *)dp + 12)); 281512f29d1SFedor Uporov if (root->h_reserved1 || 282512f29d1SFedor Uporov root->h_info_len != sizeof(struct ext2fs_htree_root_info)) 283512f29d1SFedor Uporov return (NULL); 284512f29d1SFedor Uporov 285512f29d1SFedor Uporov count_offset = 32; 286512f29d1SFedor Uporov } else 287512f29d1SFedor Uporov return (NULL); 288512f29d1SFedor Uporov 289512f29d1SFedor Uporov if (offset) 290512f29d1SFedor Uporov *offset = count_offset; 291512f29d1SFedor Uporov 292512f29d1SFedor Uporov return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset)); 293512f29d1SFedor Uporov } 294512f29d1SFedor Uporov 295512f29d1SFedor Uporov static uint32_t 296512f29d1SFedor Uporov ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset, 297512f29d1SFedor Uporov int count, struct ext2fs_htree_tail *tp) 298512f29d1SFedor Uporov { 299512f29d1SFedor Uporov struct m_ext2fs *fs; 300512f29d1SFedor Uporov char *buf; 301512f29d1SFedor Uporov int size; 302512f29d1SFedor Uporov uint32_t inum, old_csum, gen, crc; 303512f29d1SFedor Uporov 304512f29d1SFedor Uporov fs = ip->i_e2fs; 305512f29d1SFedor Uporov 306512f29d1SFedor Uporov buf = (char *)ep; 307512f29d1SFedor Uporov 308512f29d1SFedor Uporov size = count_offset + (count * sizeof(struct ext2fs_htree_entry)); 309512f29d1SFedor Uporov old_csum = tp->ht_checksum; 310512f29d1SFedor Uporov tp->ht_checksum = 0; 311512f29d1SFedor Uporov 312cd3acfe7SFedor Uporov inum = htole32(ip->i_number); 313cd3acfe7SFedor Uporov gen = htole32(ip->i_gen); 314512f29d1SFedor Uporov crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 315512f29d1SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 316512f29d1SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)buf, size); 317512f29d1SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail)); 318512f29d1SFedor Uporov tp->ht_checksum = old_csum; 319512f29d1SFedor Uporov 320cd3acfe7SFedor Uporov return htole32(crc); 321512f29d1SFedor Uporov } 322512f29d1SFedor Uporov 3236d4a4ed7SFedor Uporov int 324512f29d1SFedor Uporov ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 325512f29d1SFedor Uporov { 326512f29d1SFedor Uporov uint32_t calculated; 327512f29d1SFedor Uporov struct ext2fs_htree_count *cp; 328512f29d1SFedor Uporov struct ext2fs_htree_tail *tp; 329512f29d1SFedor Uporov int count_offset, limit, count; 330512f29d1SFedor Uporov 331512f29d1SFedor Uporov cp = ext2_get_dx_count(ip, ep, &count_offset); 332512f29d1SFedor Uporov if (cp == NULL) 333512f29d1SFedor Uporov return (0); 334512f29d1SFedor Uporov 335cd3acfe7SFedor Uporov limit = le16toh(cp->h_entries_max); 336cd3acfe7SFedor Uporov count = le16toh(cp->h_entries_num); 337512f29d1SFedor Uporov if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 338512f29d1SFedor Uporov ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 339512f29d1SFedor Uporov return (EIO); 340512f29d1SFedor Uporov 341512f29d1SFedor Uporov tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 342512f29d1SFedor Uporov calculated = ext2_dx_csum(ip, ep, count_offset, count, tp); 343512f29d1SFedor Uporov 344512f29d1SFedor Uporov if (tp->ht_checksum != calculated) 345512f29d1SFedor Uporov return (EIO); 346512f29d1SFedor Uporov 347512f29d1SFedor Uporov return (0); 348512f29d1SFedor Uporov } 349512f29d1SFedor Uporov 350512f29d1SFedor Uporov int 351512f29d1SFedor Uporov ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp) 352512f29d1SFedor Uporov { 353512f29d1SFedor Uporov struct m_ext2fs *fs; 354512f29d1SFedor Uporov struct ext2fs_direct_2 *ep; 355512f29d1SFedor Uporov int error = 0; 356512f29d1SFedor Uporov 357512f29d1SFedor Uporov fs = ip->i_e2fs; 358512f29d1SFedor Uporov 359512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 360512f29d1SFedor Uporov return (error); 361512f29d1SFedor Uporov 362512f29d1SFedor Uporov ep = (struct ext2fs_direct_2 *)bp->b_data; 363512f29d1SFedor Uporov 3646d4a4ed7SFedor Uporov if (ext2_dirent_get_tail(ip, ep) != NULL) 365512f29d1SFedor Uporov error = ext2_dirent_csum_verify(ip, ep); 366512f29d1SFedor Uporov else if (ext2_get_dx_count(ip, ep, NULL) != NULL) 367512f29d1SFedor Uporov error = ext2_dx_csum_verify(ip, ep); 368512f29d1SFedor Uporov 369512f29d1SFedor Uporov if (error) 370ebc94b66SFedor Uporov SDT_PROBE2(ext2fs, , trace, csum, 1, "bad directory csum detected"); 371512f29d1SFedor Uporov 372512f29d1SFedor Uporov return (error); 373512f29d1SFedor Uporov } 374512f29d1SFedor Uporov 3756d4a4ed7SFedor Uporov void 376512f29d1SFedor Uporov ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 377512f29d1SFedor Uporov { 3786d4a4ed7SFedor Uporov struct m_ext2fs *fs; 379512f29d1SFedor Uporov struct ext2fs_direct_tail *tp; 380512f29d1SFedor Uporov 3816d4a4ed7SFedor Uporov fs = ip->i_e2fs; 3826d4a4ed7SFedor Uporov 3836d4a4ed7SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 3846d4a4ed7SFedor Uporov return; 3856d4a4ed7SFedor Uporov 3866d4a4ed7SFedor Uporov tp = ext2_dirent_get_tail(ip, ep); 387512f29d1SFedor Uporov if (tp == NULL) 388512f29d1SFedor Uporov return; 389512f29d1SFedor Uporov 390512f29d1SFedor Uporov tp->e2dt_checksum = 391cd3acfe7SFedor Uporov htole32(ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep)); 392512f29d1SFedor Uporov } 393512f29d1SFedor Uporov 3946d4a4ed7SFedor Uporov void 395512f29d1SFedor Uporov ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 396512f29d1SFedor Uporov { 3976d4a4ed7SFedor Uporov struct m_ext2fs *fs; 398512f29d1SFedor Uporov struct ext2fs_htree_count *cp; 399512f29d1SFedor Uporov struct ext2fs_htree_tail *tp; 400512f29d1SFedor Uporov int count_offset, limit, count; 401512f29d1SFedor Uporov 4026d4a4ed7SFedor Uporov fs = ip->i_e2fs; 4036d4a4ed7SFedor Uporov 4046d4a4ed7SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 4056d4a4ed7SFedor Uporov return; 4066d4a4ed7SFedor Uporov 407512f29d1SFedor Uporov cp = ext2_get_dx_count(ip, ep, &count_offset); 408512f29d1SFedor Uporov if (cp == NULL) 409512f29d1SFedor Uporov return; 410512f29d1SFedor Uporov 411cd3acfe7SFedor Uporov limit = le16toh(cp->h_entries_max); 412cd3acfe7SFedor Uporov count = le16toh(cp->h_entries_num); 413512f29d1SFedor Uporov if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 414512f29d1SFedor Uporov ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 415512f29d1SFedor Uporov return; 416512f29d1SFedor Uporov 417512f29d1SFedor Uporov tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 418512f29d1SFedor Uporov tp->ht_checksum = ext2_dx_csum(ip, ep, count_offset, count, tp); 419512f29d1SFedor Uporov } 420512f29d1SFedor Uporov 421512f29d1SFedor Uporov static uint32_t 422512f29d1SFedor Uporov ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp) 423512f29d1SFedor Uporov { 424512f29d1SFedor Uporov struct m_ext2fs *fs; 425512f29d1SFedor Uporov size_t size; 426512f29d1SFedor Uporov uint32_t inum, gen, crc; 427512f29d1SFedor Uporov 428512f29d1SFedor Uporov fs = ip->i_e2fs; 429512f29d1SFedor Uporov 430512f29d1SFedor Uporov size = EXT4_EXTENT_TAIL_OFFSET(ehp) + 431512f29d1SFedor Uporov offsetof(struct ext4_extent_tail, et_checksum); 432512f29d1SFedor Uporov 433cd3acfe7SFedor Uporov inum = htole32(ip->i_number); 434cd3acfe7SFedor Uporov gen = htole32(ip->i_gen); 435512f29d1SFedor Uporov crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 436512f29d1SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 437512f29d1SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)ehp, size); 438512f29d1SFedor Uporov 439512f29d1SFedor Uporov return (crc); 440512f29d1SFedor Uporov } 441512f29d1SFedor Uporov 442512f29d1SFedor Uporov int 443512f29d1SFedor Uporov ext2_extent_blk_csum_verify(struct inode *ip, void *data) 444512f29d1SFedor Uporov { 445512f29d1SFedor Uporov struct m_ext2fs *fs; 446512f29d1SFedor Uporov struct ext4_extent_header *ehp; 447512f29d1SFedor Uporov struct ext4_extent_tail *etp; 448512f29d1SFedor Uporov uint32_t provided, calculated; 449512f29d1SFedor Uporov 450512f29d1SFedor Uporov fs = ip->i_e2fs; 451512f29d1SFedor Uporov 452512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 453512f29d1SFedor Uporov return (0); 454512f29d1SFedor Uporov 455512f29d1SFedor Uporov ehp = (struct ext4_extent_header *)data; 456512f29d1SFedor Uporov etp = (struct ext4_extent_tail *)(((char *)ehp) + 457512f29d1SFedor Uporov EXT4_EXTENT_TAIL_OFFSET(ehp)); 458512f29d1SFedor Uporov 459cd3acfe7SFedor Uporov provided = le32toh(etp->et_checksum); 460512f29d1SFedor Uporov calculated = ext2_extent_blk_csum(ip, ehp); 461512f29d1SFedor Uporov 462512f29d1SFedor Uporov if (provided != calculated) { 463ebc94b66SFedor Uporov SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extent csum detected"); 464512f29d1SFedor Uporov return (EIO); 465512f29d1SFedor Uporov } 466512f29d1SFedor Uporov 467512f29d1SFedor Uporov return (0); 468512f29d1SFedor Uporov } 469512f29d1SFedor Uporov 470512f29d1SFedor Uporov void 471512f29d1SFedor Uporov ext2_extent_blk_csum_set(struct inode *ip, void *data) 472512f29d1SFedor Uporov { 473512f29d1SFedor Uporov struct m_ext2fs *fs; 474512f29d1SFedor Uporov struct ext4_extent_header *ehp; 475512f29d1SFedor Uporov struct ext4_extent_tail *etp; 476512f29d1SFedor Uporov 477512f29d1SFedor Uporov fs = ip->i_e2fs; 478512f29d1SFedor Uporov 479512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 480512f29d1SFedor Uporov return; 481512f29d1SFedor Uporov 482512f29d1SFedor Uporov ehp = (struct ext4_extent_header *)data; 483512f29d1SFedor Uporov etp = (struct ext4_extent_tail *)(((char *)data) + 484512f29d1SFedor Uporov EXT4_EXTENT_TAIL_OFFSET(ehp)); 485512f29d1SFedor Uporov 486cd3acfe7SFedor Uporov etp->et_checksum = htole32(ext2_extent_blk_csum(ip, 487cd3acfe7SFedor Uporov (struct ext4_extent_header *)data)); 488512f29d1SFedor Uporov } 489512f29d1SFedor Uporov 490512f29d1SFedor Uporov int 491512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 492512f29d1SFedor Uporov { 493512f29d1SFedor Uporov uint32_t hi, provided, calculated; 494512f29d1SFedor Uporov 495512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 496512f29d1SFedor Uporov return (0); 497512f29d1SFedor Uporov 498cd3acfe7SFedor Uporov provided = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum); 499512f29d1SFedor Uporov calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 500cd3acfe7SFedor Uporov fs->e2fs_ipg / 8); 501cd3acfe7SFedor Uporov if (le16toh(fs->e2fs->e3fs_desc_size) >= 502cd3acfe7SFedor Uporov EXT2_BG_INODE_BITMAP_CSUM_HI_END) { 503cd3acfe7SFedor Uporov hi = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi); 504512f29d1SFedor Uporov provided |= (hi << 16); 505512f29d1SFedor Uporov } else 506512f29d1SFedor Uporov calculated &= 0xFFFF; 507512f29d1SFedor Uporov 508512f29d1SFedor Uporov if (provided != calculated) { 509ebc94b66SFedor Uporov SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode bitmap csum detected"); 510512f29d1SFedor Uporov return (EIO); 511512f29d1SFedor Uporov } 512512f29d1SFedor Uporov 513512f29d1SFedor Uporov return (0); 514512f29d1SFedor Uporov } 515512f29d1SFedor Uporov 516512f29d1SFedor Uporov void 517512f29d1SFedor Uporov ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 518512f29d1SFedor Uporov { 519512f29d1SFedor Uporov uint32_t csum; 520512f29d1SFedor Uporov 521512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 522512f29d1SFedor Uporov return; 523512f29d1SFedor Uporov 524512f29d1SFedor Uporov csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 525cd3acfe7SFedor Uporov fs->e2fs_ipg / 8); 526cd3acfe7SFedor Uporov fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = htole16(csum & 0xFFFF); 527cd3acfe7SFedor Uporov if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) 528cd3acfe7SFedor Uporov fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = htole16(csum >> 16); 529512f29d1SFedor Uporov } 530512f29d1SFedor Uporov 531512f29d1SFedor Uporov int 532512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 533512f29d1SFedor Uporov { 534512f29d1SFedor Uporov uint32_t hi, provided, calculated, size; 535512f29d1SFedor Uporov 536512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 537512f29d1SFedor Uporov return (0); 538512f29d1SFedor Uporov 539512f29d1SFedor Uporov size = fs->e2fs_fpg / 8; 540cd3acfe7SFedor Uporov provided = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum); 541512f29d1SFedor Uporov calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 542cd3acfe7SFedor Uporov if (le16toh(fs->e2fs->e3fs_desc_size) >= 543cd3acfe7SFedor Uporov EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) { 544cd3acfe7SFedor Uporov hi = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi); 545512f29d1SFedor Uporov provided |= (hi << 16); 546512f29d1SFedor Uporov } else 547512f29d1SFedor Uporov calculated &= 0xFFFF; 548512f29d1SFedor Uporov 549512f29d1SFedor Uporov if (provided != calculated) { 550ebc94b66SFedor Uporov SDT_PROBE2(ext2fs, , trace, csum, 1, "bad block bitmap csum detected"); 551512f29d1SFedor Uporov return (EIO); 552512f29d1SFedor Uporov } 553512f29d1SFedor Uporov 554512f29d1SFedor Uporov return (0); 555512f29d1SFedor Uporov } 556512f29d1SFedor Uporov 557512f29d1SFedor Uporov void 558512f29d1SFedor Uporov ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 559512f29d1SFedor Uporov { 560512f29d1SFedor Uporov uint32_t csum, size; 561512f29d1SFedor Uporov 562512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 563512f29d1SFedor Uporov return; 564512f29d1SFedor Uporov 565512f29d1SFedor Uporov size = fs->e2fs_fpg / 8; 566512f29d1SFedor Uporov csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 567cd3acfe7SFedor Uporov fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = htole16(csum & 0xFFFF); 568cd3acfe7SFedor Uporov if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) 569cd3acfe7SFedor Uporov fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = htole16(csum >> 16); 570512f29d1SFedor Uporov } 571512f29d1SFedor Uporov 572512f29d1SFedor Uporov static uint32_t 573512f29d1SFedor Uporov ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei) 574512f29d1SFedor Uporov { 575512f29d1SFedor Uporov struct m_ext2fs *fs; 576c4aa9a02SFedor Uporov uint32_t inode_csum_seed, inum, gen, crc; 577c4aa9a02SFedor Uporov uint16_t dummy_csum = 0; 578c4aa9a02SFedor Uporov unsigned int offset, csum_size; 579512f29d1SFedor Uporov 580512f29d1SFedor Uporov fs = ip->i_e2fs; 581c4aa9a02SFedor Uporov offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo); 582c4aa9a02SFedor Uporov csum_size = sizeof(dummy_csum); 583cd3acfe7SFedor Uporov inum = htole32(ip->i_number); 584c4aa9a02SFedor Uporov crc = calculate_crc32c(fs->e2fs_csum_seed, 585c4aa9a02SFedor Uporov (uint8_t *)&inum, sizeof(inum)); 586cd3acfe7SFedor Uporov gen = htole32(ip->i_gen); 587c4aa9a02SFedor Uporov inode_csum_seed = calculate_crc32c(crc, 588c4aa9a02SFedor Uporov (uint8_t *)&gen, sizeof(gen)); 589512f29d1SFedor Uporov 590c4aa9a02SFedor Uporov crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset); 591c4aa9a02SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size); 592c4aa9a02SFedor Uporov offset += csum_size; 593c4aa9a02SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)ei + offset, 594c4aa9a02SFedor Uporov E2FS_REV0_INODE_SIZE - offset); 595512f29d1SFedor Uporov 596c4aa9a02SFedor Uporov if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) { 597c4aa9a02SFedor Uporov offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi); 598c4aa9a02SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)ei + 599c4aa9a02SFedor Uporov E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE); 600c4aa9a02SFedor Uporov 601c4aa9a02SFedor Uporov if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE && 602cd3acfe7SFedor Uporov le16toh(ei->e2di_extra_isize) >= 603cd3acfe7SFedor Uporov EXT2_INODE_CSUM_HI_EXTRA_END)) { 604c4aa9a02SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, 605c4aa9a02SFedor Uporov csum_size); 606c4aa9a02SFedor Uporov offset += csum_size; 607c4aa9a02SFedor Uporov } 608c4aa9a02SFedor Uporov 609c4aa9a02SFedor Uporov crc = calculate_crc32c(crc, (uint8_t *)ei + offset, 610c4aa9a02SFedor Uporov EXT2_INODE_SIZE(fs) - offset); 611c4aa9a02SFedor Uporov } 612512f29d1SFedor Uporov 613512f29d1SFedor Uporov return (crc); 614512f29d1SFedor Uporov } 615512f29d1SFedor Uporov 616512f29d1SFedor Uporov int 617512f29d1SFedor Uporov ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei) 618512f29d1SFedor Uporov { 619512f29d1SFedor Uporov struct m_ext2fs *fs; 620512f29d1SFedor Uporov const static struct ext2fs_dinode ei_zero; 621512f29d1SFedor Uporov uint32_t hi, provided, calculated; 622512f29d1SFedor Uporov 623512f29d1SFedor Uporov fs = ip->i_e2fs; 624512f29d1SFedor Uporov 625512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 626512f29d1SFedor Uporov return (0); 627512f29d1SFedor Uporov 628cd3acfe7SFedor Uporov provided = le16toh(ei->e2di_chksum_lo); 629512f29d1SFedor Uporov calculated = ext2_ei_csum(ip, ei); 630512f29d1SFedor Uporov 631512f29d1SFedor Uporov if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 632cd3acfe7SFedor Uporov le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END)) { 633cd3acfe7SFedor Uporov hi = le16toh(ei->e2di_chksum_hi); 634512f29d1SFedor Uporov provided |= hi << 16; 635512f29d1SFedor Uporov } else 636512f29d1SFedor Uporov calculated &= 0xFFFF; 637512f29d1SFedor Uporov 638c4aa9a02SFedor Uporov if (provided != calculated) { 639c4aa9a02SFedor Uporov /* 640c4aa9a02SFedor Uporov * If it is first time used dinode, 641c4aa9a02SFedor Uporov * it is expected that it will be zeroed 642c4aa9a02SFedor Uporov * and we will not return checksum error in this case. 643c4aa9a02SFedor Uporov */ 644c4aa9a02SFedor Uporov if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode))) 645c4aa9a02SFedor Uporov return (0); 646c4aa9a02SFedor Uporov 647ebc94b66SFedor Uporov SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode csum"); 648daa2d62dSFedor Uporov 649512f29d1SFedor Uporov return (EIO); 650c4aa9a02SFedor Uporov } 651512f29d1SFedor Uporov 652512f29d1SFedor Uporov return (0); 653512f29d1SFedor Uporov } 654512f29d1SFedor Uporov 655512f29d1SFedor Uporov void 656512f29d1SFedor Uporov ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei) 657512f29d1SFedor Uporov { 658512f29d1SFedor Uporov struct m_ext2fs *fs; 659512f29d1SFedor Uporov uint32_t crc; 660512f29d1SFedor Uporov 661512f29d1SFedor Uporov fs = ip->i_e2fs; 662512f29d1SFedor Uporov 663512f29d1SFedor Uporov if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 664512f29d1SFedor Uporov return; 665512f29d1SFedor Uporov 666512f29d1SFedor Uporov crc = ext2_ei_csum(ip, ei); 667512f29d1SFedor Uporov 668cd3acfe7SFedor Uporov ei->e2di_chksum_lo = htole16(crc & 0xFFFF); 669512f29d1SFedor Uporov if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 670cd3acfe7SFedor Uporov le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END)) 671cd3acfe7SFedor Uporov ei->e2di_chksum_hi = htole16(crc >> 16); 672512f29d1SFedor Uporov } 673512f29d1SFedor Uporov 674d23db91eSPedro F. Giffuni static uint16_t 675d23db91eSPedro F. Giffuni ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd) 676d23db91eSPedro F. Giffuni { 677d23db91eSPedro F. Giffuni size_t offset; 678512f29d1SFedor Uporov uint32_t csum32; 679512f29d1SFedor Uporov uint16_t crc, dummy_csum; 680d23db91eSPedro F. Giffuni 681d23db91eSPedro F. Giffuni offset = offsetof(struct ext2_gd, ext4bgd_csum); 682d23db91eSPedro F. Giffuni 683cd3acfe7SFedor Uporov block_group = htole32(block_group); 684cd3acfe7SFedor Uporov 685512f29d1SFedor Uporov if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { 686512f29d1SFedor Uporov csum32 = calculate_crc32c(fs->e2fs_csum_seed, 687512f29d1SFedor Uporov (uint8_t *)&block_group, sizeof(block_group)); 688512f29d1SFedor Uporov csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset); 689512f29d1SFedor Uporov dummy_csum = 0; 690512f29d1SFedor Uporov csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum, 691512f29d1SFedor Uporov sizeof(dummy_csum)); 692512f29d1SFedor Uporov offset += sizeof(dummy_csum); 693cd3acfe7SFedor Uporov if (offset < le16toh(fs->e2fs->e3fs_desc_size)) 694512f29d1SFedor Uporov csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset, 695cd3acfe7SFedor Uporov le16toh(fs->e2fs->e3fs_desc_size) - offset); 696512f29d1SFedor Uporov 697512f29d1SFedor Uporov crc = csum32 & 0xFFFF; 698cd3acfe7SFedor Uporov return (htole16(crc)); 699512f29d1SFedor Uporov } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { 700*b344bd3aSVal Packett crc = crc16(~0, fs->e2fs->e2fs_uuid, 701d23db91eSPedro F. Giffuni sizeof(fs->e2fs->e2fs_uuid)); 702*b344bd3aSVal Packett crc = crc16(crc, (uint8_t *)&block_group, 703d23db91eSPedro F. Giffuni sizeof(block_group)); 704*b344bd3aSVal Packett crc = crc16(crc, (uint8_t *)gd, offset); 705d23db91eSPedro F. Giffuni offset += sizeof(gd->ext4bgd_csum); /* skip checksum */ 706d23db91eSPedro F. Giffuni if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) && 707cd3acfe7SFedor Uporov offset < le16toh(fs->e2fs->e3fs_desc_size)) 708*b344bd3aSVal Packett crc = crc16(crc, (uint8_t *)gd + offset, 709cd3acfe7SFedor Uporov le16toh(fs->e2fs->e3fs_desc_size) - offset); 710cd3acfe7SFedor Uporov return (htole16(crc)); 711d23db91eSPedro F. Giffuni } 712d23db91eSPedro F. Giffuni 713d23db91eSPedro F. Giffuni return (0); 714d23db91eSPedro F. Giffuni } 715d23db91eSPedro F. Giffuni 716d23db91eSPedro F. Giffuni int 717d23db91eSPedro F. Giffuni ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev) 718d23db91eSPedro F. Giffuni { 719d23db91eSPedro F. Giffuni unsigned int i; 720d23db91eSPedro F. Giffuni int error = 0; 721d23db91eSPedro F. Giffuni 722d23db91eSPedro F. Giffuni for (i = 0; i < fs->e2fs_gcount; i++) { 723d23db91eSPedro F. Giffuni if (fs->e2fs_gd[i].ext4bgd_csum != 724d23db91eSPedro F. Giffuni ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) { 725d23db91eSPedro F. Giffuni printf( 726d23db91eSPedro F. Giffuni "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n", 727d23db91eSPedro F. Giffuni devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum, 728d23db91eSPedro F. Giffuni ext2_gd_csum(fs, i, &fs->e2fs_gd[i])); 729512f29d1SFedor Uporov error = EIO; 730d23db91eSPedro F. Giffuni break; 731d23db91eSPedro F. Giffuni } 732d23db91eSPedro F. Giffuni } 733d23db91eSPedro F. Giffuni 734d23db91eSPedro F. Giffuni return (error); 735d23db91eSPedro F. Giffuni } 736d23db91eSPedro F. Giffuni 737d23db91eSPedro F. Giffuni void 738d23db91eSPedro F. Giffuni ext2_gd_csum_set(struct m_ext2fs *fs) 739d23db91eSPedro F. Giffuni { 740d23db91eSPedro F. Giffuni unsigned int i; 741d23db91eSPedro F. Giffuni 742d23db91eSPedro F. Giffuni for (i = 0; i < fs->e2fs_gcount; i++) 743cd3acfe7SFedor Uporov fs->e2fs_gd[i].ext4bgd_csum = ext2_gd_csum(fs, i, &fs->e2fs_gd[i]); 744d23db91eSPedro F. Giffuni } 745