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