1d7511a40SPedro F. Giffuni /*- 2d63027b6SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3d63027b6SPedro F. Giffuni * 4d7511a40SPedro F. Giffuni * Copyright (c) 2010 Zheng Liu <lz@freebsd.org> 5d7511a40SPedro F. Giffuni * All rights reserved. 6d7511a40SPedro F. Giffuni * 7d7511a40SPedro F. Giffuni * Redistribution and use in source and binary forms, with or without 8d7511a40SPedro F. Giffuni * modification, are permitted provided that the following conditions 9d7511a40SPedro F. Giffuni * are met: 10d7511a40SPedro F. Giffuni * 1. Redistributions of source code must retain the above copyright 11d7511a40SPedro F. Giffuni * notice, this list of conditions and the following disclaimer. 12d7511a40SPedro F. Giffuni * 2. Redistributions in binary form must reproduce the above copyright 13d7511a40SPedro F. Giffuni * notice, this list of conditions and the following disclaimer in the 14d7511a40SPedro F. Giffuni * documentation and/or other materials provided with the distribution. 15d7511a40SPedro F. Giffuni * 16d7511a40SPedro F. Giffuni * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17d7511a40SPedro F. Giffuni * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18d7511a40SPedro F. Giffuni * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19d7511a40SPedro F. Giffuni * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20d7511a40SPedro F. Giffuni * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21d7511a40SPedro F. Giffuni * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22d7511a40SPedro F. Giffuni * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23d7511a40SPedro F. Giffuni * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24d7511a40SPedro F. Giffuni * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25d7511a40SPedro F. Giffuni * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26d7511a40SPedro F. Giffuni * SUCH DAMAGE. 27d7511a40SPedro F. Giffuni * 28d7511a40SPedro F. Giffuni * $FreeBSD$ 29d7511a40SPedro F. Giffuni */ 30d7511a40SPedro F. Giffuni 31d7511a40SPedro F. Giffuni #include <sys/param.h> 32d7511a40SPedro F. Giffuni #include <sys/systm.h> 33d7511a40SPedro F. Giffuni #include <sys/types.h> 34d7511a40SPedro F. Giffuni #include <sys/kernel.h> 35d7511a40SPedro F. Giffuni #include <sys/malloc.h> 36d7511a40SPedro F. Giffuni #include <sys/vnode.h> 37d7511a40SPedro F. Giffuni #include <sys/bio.h> 38d7511a40SPedro F. Giffuni #include <sys/buf.h> 39cd3acfe7SFedor Uporov #include <sys/endian.h> 40d7511a40SPedro F. Giffuni #include <sys/conf.h> 4184b89556SFedor Uporov #include <sys/sdt.h> 42b394cd1eSFedor Uporov #include <sys/stat.h> 43d7511a40SPedro F. Giffuni 44d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2_mount.h> 45d7511a40SPedro F. Giffuni #include <fs/ext2fs/fs.h> 46d7511a40SPedro F. Giffuni #include <fs/ext2fs/inode.h> 47d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2fs.h> 48d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2_extents.h> 49d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h> 50d7511a40SPedro F. Giffuni 5184b89556SFedor Uporov SDT_PROVIDER_DECLARE(ext2fs); 5284b89556SFedor Uporov /* 5384b89556SFedor Uporov * ext2fs trace probe: 5484b89556SFedor Uporov * arg0: verbosity. Higher numbers give more verbose messages 5584b89556SFedor Uporov * arg1: Textual message 5684b89556SFedor Uporov */ 5784b89556SFedor Uporov SDT_PROBE_DEFINE2(ext2fs, , trace, extents, "int", "char*"); 5884b89556SFedor Uporov 59b394cd1eSFedor Uporov static MALLOC_DEFINE(M_EXT2EXTENTS, "ext2_extents", "EXT2 extents"); 60d7511a40SPedro F. Giffuni 6184b89556SFedor Uporov #ifdef EXT2FS_PRINT_EXTENTS 625679656eSFedor Uporov static const bool print_extents_walk = true; 63b394cd1eSFedor Uporov 64*f1d5e2c8SFedor Uporov static int ext4_ext_check_header(struct inode *, struct ext4_extent_header *, 65*f1d5e2c8SFedor Uporov int); 66*f1d5e2c8SFedor Uporov static int ext4_ext_walk_header(struct inode *, struct ext4_extent_header *, 67*f1d5e2c8SFedor Uporov int); 685679656eSFedor Uporov static inline e4fs_daddr_t ext4_ext_index_pblock(struct ext4_extent_index *); 695679656eSFedor Uporov static inline e4fs_daddr_t ext4_ext_extent_pblock(struct ext4_extent *); 705679656eSFedor Uporov 715679656eSFedor Uporov static int 725679656eSFedor Uporov ext4_ext_blk_check(struct inode *ip, e4fs_daddr_t blk) 735679656eSFedor Uporov { 745679656eSFedor Uporov struct m_ext2fs *fs; 755679656eSFedor Uporov 765679656eSFedor Uporov fs = ip->i_e2fs; 775679656eSFedor Uporov 785679656eSFedor Uporov if (blk < fs->e2fs->e2fs_first_dblock || blk >= fs->e2fs_bcount) 795679656eSFedor Uporov return (EIO); 805679656eSFedor Uporov 815679656eSFedor Uporov return (0); 82d7511a40SPedro F. Giffuni } 83d7511a40SPedro F. Giffuni 845679656eSFedor Uporov static int 85*f1d5e2c8SFedor Uporov ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, int depth, 86*f1d5e2c8SFedor Uporov bool do_walk) 87b394cd1eSFedor Uporov { 88b394cd1eSFedor Uporov struct m_ext2fs *fs; 89b394cd1eSFedor Uporov struct buf *bp; 905679656eSFedor Uporov e4fs_daddr_t blk; 91b394cd1eSFedor Uporov int error; 92b394cd1eSFedor Uporov 93b394cd1eSFedor Uporov fs = ip->i_e2fs; 94b394cd1eSFedor Uporov 955679656eSFedor Uporov if (print_extents_walk) 965679656eSFedor Uporov printf(" index %p => (blk %u pblk %ju)\n", ex, 97*f1d5e2c8SFedor Uporov le32toh(ex->ei_blk), 98*f1d5e2c8SFedor Uporov (uint64_t)le16toh(ex->ei_leaf_hi) << 32 | 99cd3acfe7SFedor Uporov le32toh(ex->ei_leaf_lo)); 100b394cd1eSFedor Uporov 101b394cd1eSFedor Uporov if(!do_walk) 1025679656eSFedor Uporov return (0); 1035679656eSFedor Uporov 1045679656eSFedor Uporov blk = ext4_ext_index_pblock(ex); 1055679656eSFedor Uporov error = ext4_ext_blk_check(ip, blk); 1065679656eSFedor Uporov if (error) 1075679656eSFedor Uporov return (error); 108b394cd1eSFedor Uporov 109b394cd1eSFedor Uporov if ((error = bread(ip->i_devvp, 1105679656eSFedor Uporov fsbtodb(fs, blk), (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 111b394cd1eSFedor Uporov brelse(bp); 1125679656eSFedor Uporov return (error); 11378f6ea54SPedro F. Giffuni } 114b394cd1eSFedor Uporov 115*f1d5e2c8SFedor Uporov error = ext4_ext_walk_header(ip, 116*f1d5e2c8SFedor Uporov (struct ext4_extent_header *)bp->b_data, depth); 117b394cd1eSFedor Uporov 118b394cd1eSFedor Uporov brelse(bp); 119b394cd1eSFedor Uporov 1205679656eSFedor Uporov return (error); 121d7511a40SPedro F. Giffuni } 122d7511a40SPedro F. Giffuni 1235679656eSFedor Uporov static int 1245679656eSFedor Uporov ext4_ext_walk_extent(struct inode *ip, struct ext4_extent *ep) 125d7511a40SPedro F. Giffuni { 1265679656eSFedor Uporov e4fs_daddr_t blk; 1275679656eSFedor Uporov int error; 128d7511a40SPedro F. Giffuni 1295679656eSFedor Uporov blk = ext4_ext_extent_pblock(ep); 1305679656eSFedor Uporov error = ext4_ext_blk_check(ip, blk); 1315679656eSFedor Uporov if (error) 1325679656eSFedor Uporov return (error); 133b394cd1eSFedor Uporov 1345679656eSFedor Uporov if (print_extents_walk) 1355679656eSFedor Uporov printf(" ext %p => (blk %u len %u start %ju)\n", 1365679656eSFedor Uporov ep, le32toh(ep->e_blk), le16toh(ep->e_len), 1375679656eSFedor Uporov (uint64_t)blk); 1385679656eSFedor Uporov 1395679656eSFedor Uporov return (0); 140b394cd1eSFedor Uporov } 141b394cd1eSFedor Uporov 1425679656eSFedor Uporov static int 143*f1d5e2c8SFedor Uporov ext4_ext_walk_header(struct inode *ip, struct ext4_extent_header *eh, int depth) 1445679656eSFedor Uporov { 1455679656eSFedor Uporov int i, error = 0; 1465679656eSFedor Uporov 147*f1d5e2c8SFedor Uporov error = ext4_ext_check_header(ip, eh, depth); 1485679656eSFedor Uporov if (error) 1495679656eSFedor Uporov return (error); 1505679656eSFedor Uporov 1515679656eSFedor Uporov if (print_extents_walk) 1525679656eSFedor Uporov printf("header %p => (entries %d max %d depth %d gen %d)\n", 1535679656eSFedor Uporov eh, le16toh(eh->eh_ecount), 154*f1d5e2c8SFedor Uporov le16toh(eh->eh_max), le16toh(eh->eh_depth), 155*f1d5e2c8SFedor Uporov le32toh(eh->eh_gen)); 1565679656eSFedor Uporov 1575679656eSFedor Uporov for (i = 0; i < le16toh(eh->eh_ecount) && error == 0; i++) 1585679656eSFedor Uporov if (eh->eh_depth != 0) 1595679656eSFedor Uporov error = ext4_ext_walk_index(ip, 160*f1d5e2c8SFedor Uporov (struct ext4_extent_index *)(eh + 1 + i), depth - 1, 161*f1d5e2c8SFedor Uporov true); 1625679656eSFedor Uporov else 163*f1d5e2c8SFedor Uporov error = ext4_ext_walk_extent(ip, 164*f1d5e2c8SFedor Uporov (struct ext4_extent *)(eh + 1 + i)); 165b394cd1eSFedor Uporov 1665679656eSFedor Uporov return (error); 1675679656eSFedor Uporov } 1685679656eSFedor Uporov 1695679656eSFedor Uporov int 1705679656eSFedor Uporov ext4_ext_walk(struct inode *ip) 171b394cd1eSFedor Uporov { 172b394cd1eSFedor Uporov struct ext4_extent_header *ehp; 173b394cd1eSFedor Uporov 1745679656eSFedor Uporov ehp = (struct ext4_extent_header *)ip->i_db; 175b394cd1eSFedor Uporov 1765679656eSFedor Uporov if (print_extents_walk) 177e06e5241SFedor Uporov printf("Extent status:ip=%ju\n", ip->i_number); 1785679656eSFedor Uporov 179b394cd1eSFedor Uporov if (!(ip->i_flag & IN_E4EXTENTS)) 1805679656eSFedor Uporov return (0); 181d7511a40SPedro F. Giffuni 182*f1d5e2c8SFedor Uporov return (ext4_ext_walk_header(ip, ehp, 0)); 183*f1d5e2c8SFedor Uporov } 184*f1d5e2c8SFedor Uporov 185*f1d5e2c8SFedor Uporov static int 186*f1d5e2c8SFedor Uporov ext4_ext_print_path(struct inode *ip, struct ext4_extent_path *path) 187*f1d5e2c8SFedor Uporov { 188*f1d5e2c8SFedor Uporov int k, depth, error = 0; 189*f1d5e2c8SFedor Uporov 190*f1d5e2c8SFedor Uporov depth = path->ep_depth; 191*f1d5e2c8SFedor Uporov 192*f1d5e2c8SFedor Uporov if (print_extents_walk) 193*f1d5e2c8SFedor Uporov printf("ip=%ju, Path:\n", ip->i_number); 194*f1d5e2c8SFedor Uporov 195*f1d5e2c8SFedor Uporov for (k = 0; k <= depth && error == 0; k++, path++) { 196*f1d5e2c8SFedor Uporov if (path->ep_index) { 197*f1d5e2c8SFedor Uporov error = ext4_ext_walk_index(ip, path->ep_index, 198*f1d5e2c8SFedor Uporov depth - 1, false); 199*f1d5e2c8SFedor Uporov } else if (path->ep_ext) { 200*f1d5e2c8SFedor Uporov error = ext4_ext_walk_extent(ip, path->ep_ext); 201*f1d5e2c8SFedor Uporov } 202*f1d5e2c8SFedor Uporov } 203*f1d5e2c8SFedor Uporov 204*f1d5e2c8SFedor Uporov return (error); 205e813d9d7SPedro F. Giffuni } 206b394cd1eSFedor Uporov #endif 207b394cd1eSFedor Uporov 208b394cd1eSFedor Uporov static inline struct ext4_extent_header * 209b394cd1eSFedor Uporov ext4_ext_inode_header(struct inode *ip) 210b394cd1eSFedor Uporov { 211b394cd1eSFedor Uporov 212b394cd1eSFedor Uporov return ((struct ext4_extent_header *)ip->i_db); 213d7511a40SPedro F. Giffuni } 214d7511a40SPedro F. Giffuni 215b394cd1eSFedor Uporov static inline struct ext4_extent_header * 216b394cd1eSFedor Uporov ext4_ext_block_header(char *bdata) 217b394cd1eSFedor Uporov { 218b394cd1eSFedor Uporov 219b394cd1eSFedor Uporov return ((struct ext4_extent_header *)bdata); 220b394cd1eSFedor Uporov } 221b394cd1eSFedor Uporov 222b394cd1eSFedor Uporov static inline unsigned short 223b394cd1eSFedor Uporov ext4_ext_inode_depth(struct inode *ip) 224b394cd1eSFedor Uporov { 225b394cd1eSFedor Uporov struct ext4_extent_header *ehp; 226b394cd1eSFedor Uporov 227b394cd1eSFedor Uporov ehp = (struct ext4_extent_header *)ip->i_data; 228cd3acfe7SFedor Uporov return (le16toh(ehp->eh_depth)); 229b394cd1eSFedor Uporov } 230b394cd1eSFedor Uporov 231b394cd1eSFedor Uporov static inline e4fs_daddr_t 232b394cd1eSFedor Uporov ext4_ext_index_pblock(struct ext4_extent_index *index) 233b394cd1eSFedor Uporov { 234b394cd1eSFedor Uporov e4fs_daddr_t blk; 235b394cd1eSFedor Uporov 236cd3acfe7SFedor Uporov blk = le32toh(index->ei_leaf_lo); 237cd3acfe7SFedor Uporov blk |= (e4fs_daddr_t)le16toh(index->ei_leaf_hi) << 32; 238b394cd1eSFedor Uporov 239b394cd1eSFedor Uporov return (blk); 240b394cd1eSFedor Uporov } 241b394cd1eSFedor Uporov 242b394cd1eSFedor Uporov static inline void 243b394cd1eSFedor Uporov ext4_index_store_pblock(struct ext4_extent_index *index, e4fs_daddr_t pb) 244b394cd1eSFedor Uporov { 245b394cd1eSFedor Uporov 246cd3acfe7SFedor Uporov index->ei_leaf_lo = htole32(pb & 0xffffffff); 247cd3acfe7SFedor Uporov index->ei_leaf_hi = htole16((pb >> 32) & 0xffff); 248b394cd1eSFedor Uporov } 249b394cd1eSFedor Uporov 250b394cd1eSFedor Uporov static inline e4fs_daddr_t 251b394cd1eSFedor Uporov ext4_ext_extent_pblock(struct ext4_extent *extent) 252b394cd1eSFedor Uporov { 253b394cd1eSFedor Uporov e4fs_daddr_t blk; 254b394cd1eSFedor Uporov 255cd3acfe7SFedor Uporov blk = le32toh(extent->e_start_lo); 256cd3acfe7SFedor Uporov blk |= (e4fs_daddr_t)le16toh(extent->e_start_hi) << 32; 257b394cd1eSFedor Uporov 258b394cd1eSFedor Uporov return (blk); 259b394cd1eSFedor Uporov } 260b394cd1eSFedor Uporov 261b394cd1eSFedor Uporov static inline void 262b394cd1eSFedor Uporov ext4_ext_store_pblock(struct ext4_extent *ex, e4fs_daddr_t pb) 263b394cd1eSFedor Uporov { 264b394cd1eSFedor Uporov 265cd3acfe7SFedor Uporov ex->e_start_lo = htole32(pb & 0xffffffff); 266cd3acfe7SFedor Uporov ex->e_start_hi = htole16((pb >> 32) & 0xffff); 267b394cd1eSFedor Uporov } 268b394cd1eSFedor Uporov 269d7511a40SPedro F. Giffuni int 270d7511a40SPedro F. Giffuni ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep) 271d7511a40SPedro F. Giffuni { 272d7511a40SPedro F. Giffuni struct ext4_extent_cache *ecp; 273d7511a40SPedro F. Giffuni int ret = EXT4_EXT_CACHE_NO; 274d7511a40SPedro F. Giffuni 275d7511a40SPedro F. Giffuni ecp = &ip->i_ext_cache; 276d7511a40SPedro F. Giffuni if (ecp->ec_type == EXT4_EXT_CACHE_NO) 277d7511a40SPedro F. Giffuni return (ret); 278d7511a40SPedro F. Giffuni 279d7511a40SPedro F. Giffuni if (lbn >= ecp->ec_blk && lbn < ecp->ec_blk + ecp->ec_len) { 280cd3acfe7SFedor Uporov ep->e_blk = htole32(ecp->ec_blk); 281cd3acfe7SFedor Uporov ep->e_start_lo = htole32(ecp->ec_start & 0xffffffff); 282cd3acfe7SFedor Uporov ep->e_start_hi = htole16(ecp->ec_start >> 32 & 0xffff); 283cd3acfe7SFedor Uporov ep->e_len = htole16(ecp->ec_len); 284d7511a40SPedro F. Giffuni ret = ecp->ec_type; 285d7511a40SPedro F. Giffuni } 286d7511a40SPedro F. Giffuni return (ret); 287d7511a40SPedro F. Giffuni } 288d7511a40SPedro F. Giffuni 289*f1d5e2c8SFedor Uporov static inline int 290*f1d5e2c8SFedor Uporov ext4_ext_space_root(struct inode *ip) 291*f1d5e2c8SFedor Uporov { 292*f1d5e2c8SFedor Uporov int size; 293*f1d5e2c8SFedor Uporov 294*f1d5e2c8SFedor Uporov size = sizeof(ip->i_data); 295*f1d5e2c8SFedor Uporov size -= sizeof(struct ext4_extent_header); 296*f1d5e2c8SFedor Uporov size /= sizeof(struct ext4_extent); 297*f1d5e2c8SFedor Uporov 298*f1d5e2c8SFedor Uporov return (size); 299*f1d5e2c8SFedor Uporov } 300*f1d5e2c8SFedor Uporov 301*f1d5e2c8SFedor Uporov static inline int 302*f1d5e2c8SFedor Uporov ext4_ext_space_block(struct inode *ip) 303*f1d5e2c8SFedor Uporov { 304*f1d5e2c8SFedor Uporov struct m_ext2fs *fs; 305*f1d5e2c8SFedor Uporov int size; 306*f1d5e2c8SFedor Uporov 307*f1d5e2c8SFedor Uporov fs = ip->i_e2fs; 308*f1d5e2c8SFedor Uporov 309*f1d5e2c8SFedor Uporov size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / 310*f1d5e2c8SFedor Uporov sizeof(struct ext4_extent); 311*f1d5e2c8SFedor Uporov 312*f1d5e2c8SFedor Uporov return (size); 313*f1d5e2c8SFedor Uporov } 314*f1d5e2c8SFedor Uporov 315*f1d5e2c8SFedor Uporov static inline int 316*f1d5e2c8SFedor Uporov ext4_ext_space_root_idx(struct inode *ip) 317*f1d5e2c8SFedor Uporov { 318*f1d5e2c8SFedor Uporov int size; 319*f1d5e2c8SFedor Uporov 320*f1d5e2c8SFedor Uporov size = sizeof(ip->i_data); 321*f1d5e2c8SFedor Uporov size -= sizeof(struct ext4_extent_header); 322*f1d5e2c8SFedor Uporov size /= sizeof(struct ext4_extent_index); 323*f1d5e2c8SFedor Uporov 324*f1d5e2c8SFedor Uporov return (size); 325*f1d5e2c8SFedor Uporov } 326*f1d5e2c8SFedor Uporov 327*f1d5e2c8SFedor Uporov static inline int 328*f1d5e2c8SFedor Uporov ext4_ext_space_block_idx(struct inode *ip) 329*f1d5e2c8SFedor Uporov { 330*f1d5e2c8SFedor Uporov struct m_ext2fs *fs; 331*f1d5e2c8SFedor Uporov int size; 332*f1d5e2c8SFedor Uporov 333*f1d5e2c8SFedor Uporov fs = ip->i_e2fs; 334*f1d5e2c8SFedor Uporov 335*f1d5e2c8SFedor Uporov size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / 336*f1d5e2c8SFedor Uporov sizeof(struct ext4_extent_index); 337*f1d5e2c8SFedor Uporov 338*f1d5e2c8SFedor Uporov return (size); 339*f1d5e2c8SFedor Uporov } 340*f1d5e2c8SFedor Uporov 341b394cd1eSFedor Uporov static int 342*f1d5e2c8SFedor Uporov ext4_ext_max_entries(struct inode *ip, int depth) 343*f1d5e2c8SFedor Uporov { 344*f1d5e2c8SFedor Uporov 345*f1d5e2c8SFedor Uporov if (depth == ext4_ext_inode_depth(ip)) { 346*f1d5e2c8SFedor Uporov if (depth == 0) 347*f1d5e2c8SFedor Uporov return (ext4_ext_space_root(ip)); 348*f1d5e2c8SFedor Uporov else 349*f1d5e2c8SFedor Uporov return (ext4_ext_space_root_idx(ip)); 350*f1d5e2c8SFedor Uporov } else { 351*f1d5e2c8SFedor Uporov if (depth == 0) 352*f1d5e2c8SFedor Uporov return (ext4_ext_space_block(ip)); 353*f1d5e2c8SFedor Uporov else 354*f1d5e2c8SFedor Uporov return (ext4_ext_space_block_idx(ip)); 355*f1d5e2c8SFedor Uporov } 356*f1d5e2c8SFedor Uporov } 357*f1d5e2c8SFedor Uporov 358*f1d5e2c8SFedor Uporov static inline uint16_t 359*f1d5e2c8SFedor Uporov ext4_ext_get_actual_len(struct ext4_extent *ext) 360*f1d5e2c8SFedor Uporov { 361*f1d5e2c8SFedor Uporov 362*f1d5e2c8SFedor Uporov return (le16toh(ext->e_len) <= EXT_INIT_MAX_LEN ? 363*f1d5e2c8SFedor Uporov le16toh(ext->e_len) : (le16toh(ext->e_len) - EXT_INIT_MAX_LEN)); 364*f1d5e2c8SFedor Uporov } 365*f1d5e2c8SFedor Uporov 366*f1d5e2c8SFedor Uporov 367*f1d5e2c8SFedor Uporov static int 368*f1d5e2c8SFedor Uporov ext4_inode_block_validate(struct inode *ip, e4fs_daddr_t start_blk, 369*f1d5e2c8SFedor Uporov unsigned int count) 370*f1d5e2c8SFedor Uporov { 371*f1d5e2c8SFedor Uporov struct m_ext2fs *fs; 372*f1d5e2c8SFedor Uporov 373*f1d5e2c8SFedor Uporov fs = ip->i_e2fs; 374*f1d5e2c8SFedor Uporov 375*f1d5e2c8SFedor Uporov if ((start_blk <= le32toh(fs->e2fs->e2fs_first_dblock)) || 376*f1d5e2c8SFedor Uporov (start_blk + count < start_blk) || 377*f1d5e2c8SFedor Uporov (start_blk + count > fs->e2fs_bcount)) 378*f1d5e2c8SFedor Uporov return (EIO); 379*f1d5e2c8SFedor Uporov 380*f1d5e2c8SFedor Uporov return (0); 381*f1d5e2c8SFedor Uporov } 382*f1d5e2c8SFedor Uporov 383*f1d5e2c8SFedor Uporov static int 384*f1d5e2c8SFedor Uporov ext4_validate_extent(struct inode *ip, struct ext4_extent *ext) 385*f1d5e2c8SFedor Uporov { 386*f1d5e2c8SFedor Uporov e4fs_daddr_t blk = ext4_ext_extent_pblock(ext); 387*f1d5e2c8SFedor Uporov uint32_t lblk = le32toh(ext->e_blk); 388*f1d5e2c8SFedor Uporov int len = ext4_ext_get_actual_len(ext); 389*f1d5e2c8SFedor Uporov 390*f1d5e2c8SFedor Uporov if (lblk + len <= lblk) 391*f1d5e2c8SFedor Uporov return (EIO); 392*f1d5e2c8SFedor Uporov 393*f1d5e2c8SFedor Uporov return (ext4_inode_block_validate(ip, blk, len)); 394*f1d5e2c8SFedor Uporov } 395*f1d5e2c8SFedor Uporov 396*f1d5e2c8SFedor Uporov static int 397*f1d5e2c8SFedor Uporov ext4_validate_extent_idx(struct inode *ip, struct ext4_extent_index *ext_idx) 398*f1d5e2c8SFedor Uporov { 399*f1d5e2c8SFedor Uporov e4fs_daddr_t blk = ext4_ext_index_pblock(ext_idx); 400*f1d5e2c8SFedor Uporov 401*f1d5e2c8SFedor Uporov return (ext4_inode_block_validate(ip, blk, 1)); 402*f1d5e2c8SFedor Uporov } 403*f1d5e2c8SFedor Uporov 404*f1d5e2c8SFedor Uporov static int 405*f1d5e2c8SFedor Uporov ext4_validate_extent_entries(struct inode *ip, struct ext4_extent_header *eh, 406*f1d5e2c8SFedor Uporov int depth) 407*f1d5e2c8SFedor Uporov { 408*f1d5e2c8SFedor Uporov unsigned int count; 409*f1d5e2c8SFedor Uporov 410*f1d5e2c8SFedor Uporov count = le16toh(eh->eh_ecount); 411*f1d5e2c8SFedor Uporov if (count == 0) 412*f1d5e2c8SFedor Uporov return (0); 413*f1d5e2c8SFedor Uporov 414*f1d5e2c8SFedor Uporov if (depth == 0) { 415*f1d5e2c8SFedor Uporov struct ext4_extent *ext = EXT_FIRST_EXTENT(eh); 416*f1d5e2c8SFedor Uporov uint32_t lblk = 0; 417*f1d5e2c8SFedor Uporov uint32_t prev = 0; 418*f1d5e2c8SFedor Uporov int len = 0; 419*f1d5e2c8SFedor Uporov while (count) { 420*f1d5e2c8SFedor Uporov /* leaf entries */ 421*f1d5e2c8SFedor Uporov if (ext4_validate_extent(ip, ext)) 422*f1d5e2c8SFedor Uporov return (EIO); 423*f1d5e2c8SFedor Uporov 424*f1d5e2c8SFedor Uporov /* Check for overlapping extents */ 425*f1d5e2c8SFedor Uporov lblk = le32toh(ext->e_blk); 426*f1d5e2c8SFedor Uporov len = ext4_ext_get_actual_len(ext); 427*f1d5e2c8SFedor Uporov if ((lblk <= prev) && prev) 428*f1d5e2c8SFedor Uporov return (EIO); 429*f1d5e2c8SFedor Uporov 430*f1d5e2c8SFedor Uporov ext++; 431*f1d5e2c8SFedor Uporov count--; 432*f1d5e2c8SFedor Uporov prev = lblk + len - 1; 433*f1d5e2c8SFedor Uporov } 434*f1d5e2c8SFedor Uporov } else { 435*f1d5e2c8SFedor Uporov struct ext4_extent_index *ext_idx = EXT_FIRST_INDEX(eh); 436*f1d5e2c8SFedor Uporov while (count) { 437*f1d5e2c8SFedor Uporov if (ext4_validate_extent_idx(ip, ext_idx)) 438*f1d5e2c8SFedor Uporov return (EIO); 439*f1d5e2c8SFedor Uporov 440*f1d5e2c8SFedor Uporov ext_idx++; 441*f1d5e2c8SFedor Uporov count--; 442*f1d5e2c8SFedor Uporov } 443*f1d5e2c8SFedor Uporov } 444*f1d5e2c8SFedor Uporov 445*f1d5e2c8SFedor Uporov return (0); 446*f1d5e2c8SFedor Uporov } 447*f1d5e2c8SFedor Uporov 448*f1d5e2c8SFedor Uporov static int 449*f1d5e2c8SFedor Uporov ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh, 450*f1d5e2c8SFedor Uporov int depth) 451d7511a40SPedro F. Giffuni { 452b394cd1eSFedor Uporov char *error_msg; 453d7511a40SPedro F. Giffuni 454cd3acfe7SFedor Uporov if (le16toh(eh->eh_magic) != EXT4_EXT_MAGIC) { 45584b89556SFedor Uporov error_msg = "header: invalid magic"; 456b394cd1eSFedor Uporov goto corrupted; 457b394cd1eSFedor Uporov } 458*f1d5e2c8SFedor Uporov if (le16toh(eh->eh_depth) != depth || 459*f1d5e2c8SFedor Uporov le16toh(eh->eh_depth) > EXT4_EXT_DEPTH_MAX) 460*f1d5e2c8SFedor Uporov { 461*f1d5e2c8SFedor Uporov error_msg = "header: invalid eh_depth"; 462*f1d5e2c8SFedor Uporov goto corrupted; 463*f1d5e2c8SFedor Uporov } 464b394cd1eSFedor Uporov if (eh->eh_max == 0) { 46584b89556SFedor Uporov error_msg = "header: invalid eh_max"; 466b394cd1eSFedor Uporov goto corrupted; 467b394cd1eSFedor Uporov } 468*f1d5e2c8SFedor Uporov if (le16toh(eh->eh_max) > ext4_ext_max_entries(ip, depth)) { 469*f1d5e2c8SFedor Uporov error_msg = "header: too large eh_max"; 470*f1d5e2c8SFedor Uporov goto corrupted; 471*f1d5e2c8SFedor Uporov } 472cd3acfe7SFedor Uporov if (le16toh(eh->eh_ecount) > le16toh(eh->eh_max)) { 47384b89556SFedor Uporov error_msg = "header: invalid eh_entries"; 474b394cd1eSFedor Uporov goto corrupted; 475d7511a40SPedro F. Giffuni } 476*f1d5e2c8SFedor Uporov if (le16toh(eh->eh_depth) > EXT4_EXT_DEPTH_MAX) { 477be60d8f2SNeel Chauhan error_msg = "header: invalid eh_depth"; 478be60d8f2SNeel Chauhan goto corrupted; 479be60d8f2SNeel Chauhan } 480*f1d5e2c8SFedor Uporov if (ext4_validate_extent_entries(ip, eh, depth)) { 481*f1d5e2c8SFedor Uporov error_msg = "header: invalid extent entries"; 482*f1d5e2c8SFedor Uporov goto corrupted; 483*f1d5e2c8SFedor Uporov } 484d7511a40SPedro F. Giffuni 485b394cd1eSFedor Uporov return (0); 486b394cd1eSFedor Uporov 487b394cd1eSFedor Uporov corrupted: 48884b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, error_msg); 489b394cd1eSFedor Uporov return (EIO); 490b394cd1eSFedor Uporov } 491b394cd1eSFedor Uporov 492b394cd1eSFedor Uporov static void 493b394cd1eSFedor Uporov ext4_ext_binsearch_index(struct ext4_extent_path *path, int blk) 494b394cd1eSFedor Uporov { 495b394cd1eSFedor Uporov struct ext4_extent_header *eh; 496b394cd1eSFedor Uporov struct ext4_extent_index *r, *l, *m; 497b394cd1eSFedor Uporov 498b394cd1eSFedor Uporov eh = path->ep_header; 499b394cd1eSFedor Uporov 500cd3acfe7SFedor Uporov KASSERT(le16toh(eh->eh_ecount) <= le16toh(eh->eh_max) && 501cd3acfe7SFedor Uporov le16toh(eh->eh_ecount) > 0, 502b394cd1eSFedor Uporov ("ext4_ext_binsearch_index: bad args")); 503b394cd1eSFedor Uporov 504b394cd1eSFedor Uporov l = EXT_FIRST_INDEX(eh) + 1; 505cd3acfe7SFedor Uporov r = EXT_FIRST_INDEX(eh) + le16toh(eh->eh_ecount) - 1; 506b394cd1eSFedor Uporov while (l <= r) { 507b394cd1eSFedor Uporov m = l + (r - l) / 2; 508cd3acfe7SFedor Uporov if (blk < le32toh(m->ei_blk)) 509b394cd1eSFedor Uporov r = m - 1; 510b394cd1eSFedor Uporov else 511b394cd1eSFedor Uporov l = m + 1; 512b394cd1eSFedor Uporov } 513b394cd1eSFedor Uporov 514b394cd1eSFedor Uporov path->ep_index = l - 1; 515b394cd1eSFedor Uporov } 516b394cd1eSFedor Uporov 517b394cd1eSFedor Uporov static void 518b394cd1eSFedor Uporov ext4_ext_binsearch_ext(struct ext4_extent_path *path, int blk) 519b394cd1eSFedor Uporov { 520b394cd1eSFedor Uporov struct ext4_extent_header *eh; 521b394cd1eSFedor Uporov struct ext4_extent *r, *l, *m; 522b394cd1eSFedor Uporov 523b394cd1eSFedor Uporov eh = path->ep_header; 524b394cd1eSFedor Uporov 525cd3acfe7SFedor Uporov KASSERT(le16toh(eh->eh_ecount) <= le16toh(eh->eh_max), 526b394cd1eSFedor Uporov ("ext4_ext_binsearch_ext: bad args")); 527b394cd1eSFedor Uporov 528b394cd1eSFedor Uporov if (eh->eh_ecount == 0) 529b394cd1eSFedor Uporov return; 530b394cd1eSFedor Uporov 531b394cd1eSFedor Uporov l = EXT_FIRST_EXTENT(eh) + 1; 532cd3acfe7SFedor Uporov r = EXT_FIRST_EXTENT(eh) + le16toh(eh->eh_ecount) - 1; 533b394cd1eSFedor Uporov 534b394cd1eSFedor Uporov while (l <= r) { 535b394cd1eSFedor Uporov m = l + (r - l) / 2; 536cd3acfe7SFedor Uporov if (blk < le32toh(m->e_blk)) 537b394cd1eSFedor Uporov r = m - 1; 538b394cd1eSFedor Uporov else 539b394cd1eSFedor Uporov l = m + 1; 540b394cd1eSFedor Uporov } 541b394cd1eSFedor Uporov 542b394cd1eSFedor Uporov path->ep_ext = l - 1; 543b394cd1eSFedor Uporov } 544b394cd1eSFedor Uporov 545b394cd1eSFedor Uporov static int 546b394cd1eSFedor Uporov ext4_ext_fill_path_bdata(struct ext4_extent_path *path, 547b394cd1eSFedor Uporov struct buf *bp, uint64_t blk) 548b394cd1eSFedor Uporov { 549b394cd1eSFedor Uporov 550b394cd1eSFedor Uporov KASSERT(path->ep_data == NULL, 551b394cd1eSFedor Uporov ("ext4_ext_fill_path_bdata: bad ep_data")); 552b394cd1eSFedor Uporov 553b394cd1eSFedor Uporov path->ep_data = malloc(bp->b_bufsize, M_EXT2EXTENTS, M_WAITOK); 554b394cd1eSFedor Uporov memcpy(path->ep_data, bp->b_data, bp->b_bufsize); 555b394cd1eSFedor Uporov path->ep_blk = blk; 556b394cd1eSFedor Uporov 557b394cd1eSFedor Uporov return (0); 558b394cd1eSFedor Uporov } 559b394cd1eSFedor Uporov 560b394cd1eSFedor Uporov static void 561b394cd1eSFedor Uporov ext4_ext_fill_path_buf(struct ext4_extent_path *path, struct buf *bp) 562b394cd1eSFedor Uporov { 563b394cd1eSFedor Uporov 564b394cd1eSFedor Uporov KASSERT(path->ep_data != NULL, 565b394cd1eSFedor Uporov ("ext4_ext_fill_path_buf: bad ep_data")); 566b394cd1eSFedor Uporov 567b394cd1eSFedor Uporov memcpy(bp->b_data, path->ep_data, bp->b_bufsize); 568b394cd1eSFedor Uporov } 569b394cd1eSFedor Uporov 570b394cd1eSFedor Uporov static void 571b394cd1eSFedor Uporov ext4_ext_drop_refs(struct ext4_extent_path *path) 572b394cd1eSFedor Uporov { 573b394cd1eSFedor Uporov int depth, i; 574b394cd1eSFedor Uporov 575b394cd1eSFedor Uporov if (!path) 576b394cd1eSFedor Uporov return; 577b394cd1eSFedor Uporov 578b394cd1eSFedor Uporov depth = path->ep_depth; 579b394cd1eSFedor Uporov for (i = 0; i <= depth; i++, path++) 580b394cd1eSFedor Uporov if (path->ep_data) { 581b394cd1eSFedor Uporov free(path->ep_data, M_EXT2EXTENTS); 582b394cd1eSFedor Uporov path->ep_data = NULL; 583b394cd1eSFedor Uporov } 584b394cd1eSFedor Uporov } 585b394cd1eSFedor Uporov 586b394cd1eSFedor Uporov void 587b394cd1eSFedor Uporov ext4_ext_path_free(struct ext4_extent_path *path) 588b394cd1eSFedor Uporov { 589b394cd1eSFedor Uporov 590b394cd1eSFedor Uporov if (!path) 591b394cd1eSFedor Uporov return; 592b394cd1eSFedor Uporov 593b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 594b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 595b394cd1eSFedor Uporov } 596b394cd1eSFedor Uporov 597b394cd1eSFedor Uporov int 598b394cd1eSFedor Uporov ext4_ext_find_extent(struct inode *ip, daddr_t block, 599b394cd1eSFedor Uporov struct ext4_extent_path **ppath) 600b394cd1eSFedor Uporov { 601b394cd1eSFedor Uporov struct ext4_extent_header *eh; 602b394cd1eSFedor Uporov struct ext4_extent_path *path; 603b394cd1eSFedor Uporov struct buf *bp; 604b394cd1eSFedor Uporov uint64_t blk; 605b394cd1eSFedor Uporov int error, depth, i, ppos, alloc; 606b394cd1eSFedor Uporov 607b394cd1eSFedor Uporov eh = ext4_ext_inode_header(ip); 608b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 609b394cd1eSFedor Uporov ppos = 0; 610b394cd1eSFedor Uporov alloc = 0; 611b394cd1eSFedor Uporov 612*f1d5e2c8SFedor Uporov error = ext4_ext_check_header(ip, eh, depth); 613b394cd1eSFedor Uporov if (error) 614b394cd1eSFedor Uporov return (error); 615b394cd1eSFedor Uporov 6163fcbb8c0SFedor Uporov if (ppath == NULL) 617b394cd1eSFedor Uporov return (EINVAL); 618b394cd1eSFedor Uporov 619b394cd1eSFedor Uporov path = *ppath; 6203fcbb8c0SFedor Uporov if (path == NULL) { 621b394cd1eSFedor Uporov path = malloc(EXT4_EXT_DEPTH_MAX * 622b394cd1eSFedor Uporov sizeof(struct ext4_extent_path), 623b394cd1eSFedor Uporov M_EXT2EXTENTS, M_WAITOK | M_ZERO); 624b394cd1eSFedor Uporov *ppath = path; 625b394cd1eSFedor Uporov alloc = 1; 626b394cd1eSFedor Uporov } 627b394cd1eSFedor Uporov 628b394cd1eSFedor Uporov path[0].ep_header = eh; 629b394cd1eSFedor Uporov path[0].ep_data = NULL; 630b394cd1eSFedor Uporov 631b394cd1eSFedor Uporov /* Walk through the tree. */ 632b394cd1eSFedor Uporov i = depth; 633b394cd1eSFedor Uporov while (i) { 634b394cd1eSFedor Uporov ext4_ext_binsearch_index(&path[ppos], block); 635b394cd1eSFedor Uporov blk = ext4_ext_index_pblock(path[ppos].ep_index); 636b394cd1eSFedor Uporov path[ppos].ep_depth = i; 637b394cd1eSFedor Uporov path[ppos].ep_ext = NULL; 638b394cd1eSFedor Uporov 639b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(ip->i_e2fs, blk), 640b394cd1eSFedor Uporov ip->i_e2fs->e2fs_bsize, NOCRED, &bp); 641b394cd1eSFedor Uporov if (error) { 642b394cd1eSFedor Uporov goto error; 643b394cd1eSFedor Uporov } 644b394cd1eSFedor Uporov 645b394cd1eSFedor Uporov ppos++; 646b394cd1eSFedor Uporov if (ppos > depth) { 64784b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 648b394cd1eSFedor Uporov "ppos > depth => extent corrupted"); 649b394cd1eSFedor Uporov error = EIO; 650b394cd1eSFedor Uporov brelse(bp); 651b394cd1eSFedor Uporov goto error; 652b394cd1eSFedor Uporov } 653b394cd1eSFedor Uporov 654b394cd1eSFedor Uporov ext4_ext_fill_path_bdata(&path[ppos], bp, blk); 655f0a993bbSFedor Uporov bqrelse(bp); 656b394cd1eSFedor Uporov 657b394cd1eSFedor Uporov eh = ext4_ext_block_header(path[ppos].ep_data); 658*f1d5e2c8SFedor Uporov if (ext4_ext_check_header(ip, eh, i - 1) || 659512f29d1SFedor Uporov ext2_extent_blk_csum_verify(ip, path[ppos].ep_data)) { 660512f29d1SFedor Uporov error = EIO; 661b394cd1eSFedor Uporov goto error; 662512f29d1SFedor Uporov } 663b394cd1eSFedor Uporov 664b394cd1eSFedor Uporov path[ppos].ep_header = eh; 665b394cd1eSFedor Uporov 666b394cd1eSFedor Uporov i--; 667b394cd1eSFedor Uporov } 668b394cd1eSFedor Uporov 669*f1d5e2c8SFedor Uporov error = ext4_ext_check_header(ip, eh, 0); 670b394cd1eSFedor Uporov if (error) 671b394cd1eSFedor Uporov goto error; 672b394cd1eSFedor Uporov 673b394cd1eSFedor Uporov /* Find extent. */ 674b394cd1eSFedor Uporov path[ppos].ep_depth = i; 675b394cd1eSFedor Uporov path[ppos].ep_header = eh; 676b394cd1eSFedor Uporov path[ppos].ep_ext = NULL; 677b394cd1eSFedor Uporov path[ppos].ep_index = NULL; 678b394cd1eSFedor Uporov ext4_ext_binsearch_ext(&path[ppos], block); 679b394cd1eSFedor Uporov return (0); 680b394cd1eSFedor Uporov 681b394cd1eSFedor Uporov error: 682b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 683b394cd1eSFedor Uporov if (alloc) 684b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 685b394cd1eSFedor Uporov 686b394cd1eSFedor Uporov *ppath = NULL; 687b394cd1eSFedor Uporov 688b394cd1eSFedor Uporov return (error); 689b394cd1eSFedor Uporov } 690b394cd1eSFedor Uporov 691b394cd1eSFedor Uporov static inline int 692b394cd1eSFedor Uporov ext4_ext_space_block_index(struct inode *ip) 693b394cd1eSFedor Uporov { 694b394cd1eSFedor Uporov struct m_ext2fs *fs; 695b394cd1eSFedor Uporov int size; 696b394cd1eSFedor Uporov 697b394cd1eSFedor Uporov fs = ip->i_e2fs; 698b394cd1eSFedor Uporov 699b394cd1eSFedor Uporov size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / 700b394cd1eSFedor Uporov sizeof(struct ext4_extent_index); 701b394cd1eSFedor Uporov 702b394cd1eSFedor Uporov return (size); 703b394cd1eSFedor Uporov } 704b394cd1eSFedor Uporov 705b394cd1eSFedor Uporov void 706b394cd1eSFedor Uporov ext4_ext_tree_init(struct inode *ip) 707d7511a40SPedro F. Giffuni { 708d7511a40SPedro F. Giffuni struct ext4_extent_header *ehp; 709d7511a40SPedro F. Giffuni 710b394cd1eSFedor Uporov ip->i_flag |= IN_E4EXTENTS; 711d7511a40SPedro F. Giffuni 712b394cd1eSFedor Uporov memset(ip->i_data, 0, EXT2_NDADDR + EXT2_NIADDR); 713b394cd1eSFedor Uporov ehp = (struct ext4_extent_header *)ip->i_data; 714cd3acfe7SFedor Uporov ehp->eh_magic = htole16(EXT4_EXT_MAGIC); 715cd3acfe7SFedor Uporov ehp->eh_max = htole16(ext4_ext_space_root(ip)); 716b394cd1eSFedor Uporov ip->i_ext_cache.ec_type = EXT4_EXT_CACHE_NO; 717b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 718b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 71978f6ea54SPedro F. Giffuni } 720d7511a40SPedro F. Giffuni 721b394cd1eSFedor Uporov static inline void 722b394cd1eSFedor Uporov ext4_ext_put_in_cache(struct inode *ip, uint32_t blk, 723b394cd1eSFedor Uporov uint32_t len, uint32_t start, int type) 724b394cd1eSFedor Uporov { 725b394cd1eSFedor Uporov 726b394cd1eSFedor Uporov KASSERT(len != 0, ("ext4_ext_put_in_cache: bad input")); 727b394cd1eSFedor Uporov 728b394cd1eSFedor Uporov ip->i_ext_cache.ec_type = type; 729b394cd1eSFedor Uporov ip->i_ext_cache.ec_blk = blk; 730b394cd1eSFedor Uporov ip->i_ext_cache.ec_len = len; 731b394cd1eSFedor Uporov ip->i_ext_cache.ec_start = start; 732d7511a40SPedro F. Giffuni } 733b394cd1eSFedor Uporov 734b394cd1eSFedor Uporov static e4fs_daddr_t 735b394cd1eSFedor Uporov ext4_ext_blkpref(struct inode *ip, struct ext4_extent_path *path, 736b394cd1eSFedor Uporov e4fs_daddr_t block) 737b394cd1eSFedor Uporov { 738b394cd1eSFedor Uporov struct m_ext2fs *fs; 739b394cd1eSFedor Uporov struct ext4_extent *ex; 740b394cd1eSFedor Uporov e4fs_daddr_t bg_start; 741b394cd1eSFedor Uporov int depth; 742b394cd1eSFedor Uporov 743b394cd1eSFedor Uporov fs = ip->i_e2fs; 744b394cd1eSFedor Uporov 745b394cd1eSFedor Uporov if (path) { 746b394cd1eSFedor Uporov depth = path->ep_depth; 747b394cd1eSFedor Uporov ex = path[depth].ep_ext; 748b394cd1eSFedor Uporov if (ex) { 749b394cd1eSFedor Uporov e4fs_daddr_t pblk = ext4_ext_extent_pblock(ex); 750cd3acfe7SFedor Uporov e2fs_daddr_t blk = le32toh(ex->e_blk); 751b394cd1eSFedor Uporov 752b394cd1eSFedor Uporov if (block > blk) 753b394cd1eSFedor Uporov return (pblk + (block - blk)); 754b394cd1eSFedor Uporov else 755b394cd1eSFedor Uporov return (pblk - (blk - block)); 756b394cd1eSFedor Uporov } 757b394cd1eSFedor Uporov 758b394cd1eSFedor Uporov /* Try to get block from index itself. */ 759b394cd1eSFedor Uporov if (path[depth].ep_data) 760b394cd1eSFedor Uporov return (path[depth].ep_blk); 761b394cd1eSFedor Uporov } 762b394cd1eSFedor Uporov 763b394cd1eSFedor Uporov /* Use inode's group. */ 764b394cd1eSFedor Uporov bg_start = (ip->i_block_group * EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) + 765cd3acfe7SFedor Uporov le32toh(fs->e2fs->e2fs_first_dblock); 766b394cd1eSFedor Uporov 767b394cd1eSFedor Uporov return (bg_start + block); 768b394cd1eSFedor Uporov } 769b394cd1eSFedor Uporov 770b394cd1eSFedor Uporov static int inline 771b394cd1eSFedor Uporov ext4_can_extents_be_merged(struct ext4_extent *ex1, 772b394cd1eSFedor Uporov struct ext4_extent *ex2) 773b394cd1eSFedor Uporov { 774b394cd1eSFedor Uporov 775cd3acfe7SFedor Uporov if (le32toh(ex1->e_blk) + le16toh(ex1->e_len) != le32toh(ex2->e_blk)) 776b394cd1eSFedor Uporov return (0); 777b394cd1eSFedor Uporov 778cd3acfe7SFedor Uporov if (le16toh(ex1->e_len) + le16toh(ex2->e_len) > EXT4_MAX_LEN) 779b394cd1eSFedor Uporov return (0); 780b394cd1eSFedor Uporov 781cd3acfe7SFedor Uporov if (ext4_ext_extent_pblock(ex1) + le16toh(ex1->e_len) == 782b394cd1eSFedor Uporov ext4_ext_extent_pblock(ex2)) 783b394cd1eSFedor Uporov return (1); 784b394cd1eSFedor Uporov 785b394cd1eSFedor Uporov return (0); 786b394cd1eSFedor Uporov } 787b394cd1eSFedor Uporov 788b394cd1eSFedor Uporov static unsigned 789b394cd1eSFedor Uporov ext4_ext_next_leaf_block(struct inode *ip, struct ext4_extent_path *path) 790b394cd1eSFedor Uporov { 791b394cd1eSFedor Uporov int depth = path->ep_depth; 792b394cd1eSFedor Uporov 793b394cd1eSFedor Uporov /* Empty tree */ 794b394cd1eSFedor Uporov if (depth == 0) 795b394cd1eSFedor Uporov return (EXT4_MAX_BLOCKS); 796b394cd1eSFedor Uporov 797b394cd1eSFedor Uporov /* Go to indexes. */ 798b394cd1eSFedor Uporov depth--; 799b394cd1eSFedor Uporov 800b394cd1eSFedor Uporov while (depth >= 0) { 801b394cd1eSFedor Uporov if (path[depth].ep_index != 802b394cd1eSFedor Uporov EXT_LAST_INDEX(path[depth].ep_header)) 803cd3acfe7SFedor Uporov return (le32toh(path[depth].ep_index[1].ei_blk)); 804b394cd1eSFedor Uporov 805b394cd1eSFedor Uporov depth--; 806b394cd1eSFedor Uporov } 807b394cd1eSFedor Uporov 808b394cd1eSFedor Uporov return (EXT4_MAX_BLOCKS); 809b394cd1eSFedor Uporov } 810b394cd1eSFedor Uporov 811b394cd1eSFedor Uporov static int 812b394cd1eSFedor Uporov ext4_ext_dirty(struct inode *ip, struct ext4_extent_path *path) 813b394cd1eSFedor Uporov { 814b394cd1eSFedor Uporov struct m_ext2fs *fs; 815b394cd1eSFedor Uporov struct buf *bp; 816b394cd1eSFedor Uporov uint64_t blk; 817b394cd1eSFedor Uporov int error; 818b394cd1eSFedor Uporov 819b394cd1eSFedor Uporov fs = ip->i_e2fs; 820b394cd1eSFedor Uporov 821b394cd1eSFedor Uporov if (!path) 822b394cd1eSFedor Uporov return (EINVAL); 823b394cd1eSFedor Uporov 824b394cd1eSFedor Uporov if (path->ep_data) { 825b394cd1eSFedor Uporov blk = path->ep_blk; 826b394cd1eSFedor Uporov bp = getblk(ip->i_devvp, fsbtodb(fs, blk), 827b394cd1eSFedor Uporov fs->e2fs_bsize, 0, 0, 0); 828b394cd1eSFedor Uporov if (!bp) 829b394cd1eSFedor Uporov return (EIO); 830b394cd1eSFedor Uporov ext4_ext_fill_path_buf(path, bp); 831512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 832b394cd1eSFedor Uporov error = bwrite(bp); 833b394cd1eSFedor Uporov } else { 834b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 835b394cd1eSFedor Uporov error = ext2_update(ip->i_vnode, 1); 836b394cd1eSFedor Uporov } 837b394cd1eSFedor Uporov 838b394cd1eSFedor Uporov return (error); 839b394cd1eSFedor Uporov } 840b394cd1eSFedor Uporov 841b394cd1eSFedor Uporov static int 842b394cd1eSFedor Uporov ext4_ext_insert_index(struct inode *ip, struct ext4_extent_path *path, 843b394cd1eSFedor Uporov uint32_t lblk, e4fs_daddr_t blk) 844b394cd1eSFedor Uporov { 845b394cd1eSFedor Uporov struct ext4_extent_index *idx; 846b394cd1eSFedor Uporov int len; 847b394cd1eSFedor Uporov 848cd3acfe7SFedor Uporov if (lblk == le32toh(path->ep_index->ei_blk)) { 84984b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 850b394cd1eSFedor Uporov "lblk == index blk => extent corrupted"); 851b394cd1eSFedor Uporov return (EIO); 852b394cd1eSFedor Uporov } 853b394cd1eSFedor Uporov 854cd3acfe7SFedor Uporov if (le16toh(path->ep_header->eh_ecount) >= 855cd3acfe7SFedor Uporov le16toh(path->ep_header->eh_max)) { 85684b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 857b394cd1eSFedor Uporov "ecout > maxcount => extent corrupted"); 858b394cd1eSFedor Uporov return (EIO); 859b394cd1eSFedor Uporov } 860b394cd1eSFedor Uporov 861cd3acfe7SFedor Uporov if (lblk > le32toh(path->ep_index->ei_blk)) { 862b394cd1eSFedor Uporov /* Insert after. */ 863b394cd1eSFedor Uporov idx = path->ep_index + 1; 864b394cd1eSFedor Uporov } else { 865b394cd1eSFedor Uporov /* Insert before. */ 866b394cd1eSFedor Uporov idx = path->ep_index; 867b394cd1eSFedor Uporov } 868b394cd1eSFedor Uporov 869b394cd1eSFedor Uporov len = EXT_LAST_INDEX(path->ep_header) - idx + 1; 870b394cd1eSFedor Uporov if (len > 0) 871b394cd1eSFedor Uporov memmove(idx + 1, idx, len * sizeof(struct ext4_extent_index)); 872b394cd1eSFedor Uporov 873b394cd1eSFedor Uporov if (idx > EXT_MAX_INDEX(path->ep_header)) { 87484b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 875b394cd1eSFedor Uporov "index is out of range => extent corrupted"); 876b394cd1eSFedor Uporov return (EIO); 877b394cd1eSFedor Uporov } 878b394cd1eSFedor Uporov 879cd3acfe7SFedor Uporov idx->ei_blk = htole32(lblk); 880b394cd1eSFedor Uporov ext4_index_store_pblock(idx, blk); 881cd3acfe7SFedor Uporov path->ep_header->eh_ecount = 882cd3acfe7SFedor Uporov htole16(le16toh(path->ep_header->eh_ecount) + 1); 883b394cd1eSFedor Uporov 884b394cd1eSFedor Uporov return (ext4_ext_dirty(ip, path)); 885b394cd1eSFedor Uporov } 886b394cd1eSFedor Uporov 887b394cd1eSFedor Uporov static e4fs_daddr_t 888b394cd1eSFedor Uporov ext4_ext_alloc_meta(struct inode *ip) 889b394cd1eSFedor Uporov { 890b394cd1eSFedor Uporov e4fs_daddr_t blk = ext2_alloc_meta(ip); 891b394cd1eSFedor Uporov if (blk) { 892b394cd1eSFedor Uporov ip->i_blocks += btodb(ip->i_e2fs->e2fs_bsize); 893b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 894b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 895b394cd1eSFedor Uporov } 896b394cd1eSFedor Uporov 897b394cd1eSFedor Uporov return (blk); 898b394cd1eSFedor Uporov } 899b394cd1eSFedor Uporov 900b394cd1eSFedor Uporov static void 901b394cd1eSFedor Uporov ext4_ext_blkfree(struct inode *ip, uint64_t blk, int count, int flags) 902b394cd1eSFedor Uporov { 903b394cd1eSFedor Uporov struct m_ext2fs *fs; 904b394cd1eSFedor Uporov int i, blocksreleased; 905b394cd1eSFedor Uporov 906b394cd1eSFedor Uporov fs = ip->i_e2fs; 907b394cd1eSFedor Uporov blocksreleased = count; 908b394cd1eSFedor Uporov 909b394cd1eSFedor Uporov for(i = 0; i < count; i++) 910b394cd1eSFedor Uporov ext2_blkfree(ip, blk + i, fs->e2fs_bsize); 911b394cd1eSFedor Uporov 912b394cd1eSFedor Uporov if (ip->i_blocks >= blocksreleased) 913b394cd1eSFedor Uporov ip->i_blocks -= (btodb(fs->e2fs_bsize)*blocksreleased); 914b394cd1eSFedor Uporov else 915b394cd1eSFedor Uporov ip->i_blocks = 0; 916b394cd1eSFedor Uporov 917b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 918b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 919b394cd1eSFedor Uporov } 920b394cd1eSFedor Uporov 921b394cd1eSFedor Uporov static int 922b394cd1eSFedor Uporov ext4_ext_split(struct inode *ip, struct ext4_extent_path *path, 923b394cd1eSFedor Uporov struct ext4_extent *newext, int at) 924b394cd1eSFedor Uporov { 925b394cd1eSFedor Uporov struct m_ext2fs *fs; 926b394cd1eSFedor Uporov struct buf *bp; 927b394cd1eSFedor Uporov int depth = ext4_ext_inode_depth(ip); 928b394cd1eSFedor Uporov struct ext4_extent_header *neh; 929b394cd1eSFedor Uporov struct ext4_extent_index *fidx; 930b394cd1eSFedor Uporov struct ext4_extent *ex; 931b394cd1eSFedor Uporov int i = at, k, m, a; 932b394cd1eSFedor Uporov e4fs_daddr_t newblk, oldblk; 933b394cd1eSFedor Uporov uint32_t border; 934b394cd1eSFedor Uporov e4fs_daddr_t *ablks = NULL; 935b394cd1eSFedor Uporov int error = 0; 936b394cd1eSFedor Uporov 937b394cd1eSFedor Uporov fs = ip->i_e2fs; 938b394cd1eSFedor Uporov bp = NULL; 939b394cd1eSFedor Uporov 940b394cd1eSFedor Uporov /* 941b394cd1eSFedor Uporov * We will split at current extent for now. 942b394cd1eSFedor Uporov */ 943b394cd1eSFedor Uporov if (path[depth].ep_ext > EXT_MAX_EXTENT(path[depth].ep_header)) { 94484b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 945b394cd1eSFedor Uporov "extent is out of range => extent corrupted"); 946b394cd1eSFedor Uporov return (EIO); 947b394cd1eSFedor Uporov } 948b394cd1eSFedor Uporov 949b394cd1eSFedor Uporov if (path[depth].ep_ext != EXT_MAX_EXTENT(path[depth].ep_header)) 950cd3acfe7SFedor Uporov border = le32toh(path[depth].ep_ext[1].e_blk); 951b394cd1eSFedor Uporov else 952cd3acfe7SFedor Uporov border = le32toh(newext->e_blk); 953b394cd1eSFedor Uporov 954b394cd1eSFedor Uporov /* Allocate new blocks. */ 955b394cd1eSFedor Uporov ablks = malloc(sizeof(e4fs_daddr_t) * depth, 956b394cd1eSFedor Uporov M_EXT2EXTENTS, M_WAITOK | M_ZERO); 957b394cd1eSFedor Uporov for (a = 0; a < depth - at; a++) { 958b394cd1eSFedor Uporov newblk = ext4_ext_alloc_meta(ip); 959b394cd1eSFedor Uporov if (newblk == 0) 960b394cd1eSFedor Uporov goto cleanup; 961b394cd1eSFedor Uporov ablks[a] = newblk; 962b394cd1eSFedor Uporov } 963b394cd1eSFedor Uporov 964b394cd1eSFedor Uporov newblk = ablks[--a]; 965b394cd1eSFedor Uporov bp = getblk(ip->i_devvp, fsbtodb(fs, newblk), fs->e2fs_bsize, 0, 0, 0); 966b394cd1eSFedor Uporov if (!bp) { 967b394cd1eSFedor Uporov error = EIO; 968b394cd1eSFedor Uporov goto cleanup; 969b394cd1eSFedor Uporov } 970b394cd1eSFedor Uporov 971b394cd1eSFedor Uporov neh = ext4_ext_block_header(bp->b_data); 972b394cd1eSFedor Uporov neh->eh_ecount = 0; 973cd3acfe7SFedor Uporov neh->eh_max = le16toh(ext4_ext_space_block(ip)); 974cd3acfe7SFedor Uporov neh->eh_magic = le16toh(EXT4_EXT_MAGIC); 975b394cd1eSFedor Uporov neh->eh_depth = 0; 976b394cd1eSFedor Uporov ex = EXT_FIRST_EXTENT(neh); 977b394cd1eSFedor Uporov 978cd3acfe7SFedor Uporov if (le16toh(path[depth].ep_header->eh_ecount) != 979cd3acfe7SFedor Uporov le16toh(path[depth].ep_header->eh_max)) { 98084b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 981b394cd1eSFedor Uporov "extents count out of range => extent corrupted"); 982b394cd1eSFedor Uporov error = EIO; 983b394cd1eSFedor Uporov goto cleanup; 984b394cd1eSFedor Uporov } 985b394cd1eSFedor Uporov 986b394cd1eSFedor Uporov /* Start copy from next extent. */ 987b394cd1eSFedor Uporov m = 0; 988b394cd1eSFedor Uporov path[depth].ep_ext++; 989b394cd1eSFedor Uporov while (path[depth].ep_ext <= EXT_MAX_EXTENT(path[depth].ep_header)) { 990b394cd1eSFedor Uporov path[depth].ep_ext++; 991b394cd1eSFedor Uporov m++; 992b394cd1eSFedor Uporov } 993b394cd1eSFedor Uporov if (m) { 994b394cd1eSFedor Uporov memmove(ex, path[depth].ep_ext - m, 995b394cd1eSFedor Uporov sizeof(struct ext4_extent) * m); 996cd3acfe7SFedor Uporov neh->eh_ecount = htole16(le16toh(neh->eh_ecount) + m); 997b394cd1eSFedor Uporov } 998b394cd1eSFedor Uporov 999512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 1000b394cd1eSFedor Uporov bwrite(bp); 1001b394cd1eSFedor Uporov bp = NULL; 1002b394cd1eSFedor Uporov 1003b394cd1eSFedor Uporov /* Fix old leaf. */ 1004b394cd1eSFedor Uporov if (m) { 1005b394cd1eSFedor Uporov path[depth].ep_header->eh_ecount = 1006cd3acfe7SFedor Uporov htole16(le16toh(path[depth].ep_header->eh_ecount) - m); 1007b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + depth); 1008b394cd1eSFedor Uporov } 1009b394cd1eSFedor Uporov 1010b394cd1eSFedor Uporov /* Create intermediate indexes. */ 1011b394cd1eSFedor Uporov k = depth - at - 1; 1012b394cd1eSFedor Uporov KASSERT(k >= 0, ("ext4_ext_split: negative k")); 1013b394cd1eSFedor Uporov 1014b394cd1eSFedor Uporov /* Insert new index into current index block. */ 1015b394cd1eSFedor Uporov i = depth - 1; 1016b394cd1eSFedor Uporov while (k--) { 1017b394cd1eSFedor Uporov oldblk = newblk; 1018b394cd1eSFedor Uporov newblk = ablks[--a]; 1019b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(fs, newblk), 1020b394cd1eSFedor Uporov (int)fs->e2fs_bsize, NOCRED, &bp); 1021d7511a40SPedro F. Giffuni if (error) { 1022b394cd1eSFedor Uporov goto cleanup; 1023b394cd1eSFedor Uporov } 1024b394cd1eSFedor Uporov 1025b394cd1eSFedor Uporov neh = (struct ext4_extent_header *)bp->b_data; 1026cd3acfe7SFedor Uporov neh->eh_ecount = htole16(1); 1027cd3acfe7SFedor Uporov neh->eh_magic = htole16(EXT4_EXT_MAGIC); 1028cd3acfe7SFedor Uporov neh->eh_max = htole16(ext4_ext_space_block_index(ip)); 1029cd3acfe7SFedor Uporov neh->eh_depth = htole16(depth - i); 1030b394cd1eSFedor Uporov fidx = EXT_FIRST_INDEX(neh); 1031cd3acfe7SFedor Uporov fidx->ei_blk = htole32(border); 1032b394cd1eSFedor Uporov ext4_index_store_pblock(fidx, oldblk); 1033b394cd1eSFedor Uporov 1034b394cd1eSFedor Uporov m = 0; 1035b394cd1eSFedor Uporov path[i].ep_index++; 1036b394cd1eSFedor Uporov while (path[i].ep_index <= EXT_MAX_INDEX(path[i].ep_header)) { 1037b394cd1eSFedor Uporov path[i].ep_index++; 1038b394cd1eSFedor Uporov m++; 1039b394cd1eSFedor Uporov } 1040b394cd1eSFedor Uporov if (m) { 1041b394cd1eSFedor Uporov memmove(++fidx, path[i].ep_index - m, 1042b394cd1eSFedor Uporov sizeof(struct ext4_extent_index) * m); 1043cd3acfe7SFedor Uporov neh->eh_ecount = htole16(le16toh(neh->eh_ecount) + m); 1044b394cd1eSFedor Uporov } 1045b394cd1eSFedor Uporov 1046512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 1047b394cd1eSFedor Uporov bwrite(bp); 1048b394cd1eSFedor Uporov bp = NULL; 1049b394cd1eSFedor Uporov 1050b394cd1eSFedor Uporov /* Fix old index. */ 1051b394cd1eSFedor Uporov if (m) { 1052b394cd1eSFedor Uporov path[i].ep_header->eh_ecount = 1053cd3acfe7SFedor Uporov htole16(le16toh(path[i].ep_header->eh_ecount) - m); 1054b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + i); 1055b394cd1eSFedor Uporov } 1056b394cd1eSFedor Uporov 1057b394cd1eSFedor Uporov i--; 1058b394cd1eSFedor Uporov } 1059b394cd1eSFedor Uporov 1060b394cd1eSFedor Uporov error = ext4_ext_insert_index(ip, path + at, border, newblk); 1061b394cd1eSFedor Uporov 1062b394cd1eSFedor Uporov cleanup: 1063b394cd1eSFedor Uporov if (bp) 1064b394cd1eSFedor Uporov brelse(bp); 1065b394cd1eSFedor Uporov 1066b394cd1eSFedor Uporov if (error) { 1067b394cd1eSFedor Uporov for (i = 0; i < depth; i++) { 1068b394cd1eSFedor Uporov if (!ablks[i]) 1069b394cd1eSFedor Uporov continue; 1070b394cd1eSFedor Uporov ext4_ext_blkfree(ip, ablks[i], 1, 0); 1071b394cd1eSFedor Uporov } 1072b394cd1eSFedor Uporov } 1073b394cd1eSFedor Uporov 1074b394cd1eSFedor Uporov free(ablks, M_EXT2EXTENTS); 1075b394cd1eSFedor Uporov 1076b394cd1eSFedor Uporov return (error); 1077b394cd1eSFedor Uporov } 1078b394cd1eSFedor Uporov 1079b394cd1eSFedor Uporov static int 1080b394cd1eSFedor Uporov ext4_ext_grow_indepth(struct inode *ip, struct ext4_extent_path *path, 1081b394cd1eSFedor Uporov struct ext4_extent *newext) 1082b394cd1eSFedor Uporov { 1083b394cd1eSFedor Uporov struct m_ext2fs *fs; 1084b394cd1eSFedor Uporov struct ext4_extent_path *curpath; 1085b394cd1eSFedor Uporov struct ext4_extent_header *neh; 1086b394cd1eSFedor Uporov struct buf *bp; 1087b394cd1eSFedor Uporov e4fs_daddr_t newblk; 1088b394cd1eSFedor Uporov int error = 0; 1089b394cd1eSFedor Uporov 1090b394cd1eSFedor Uporov fs = ip->i_e2fs; 1091b394cd1eSFedor Uporov curpath = path; 1092b394cd1eSFedor Uporov 1093b394cd1eSFedor Uporov newblk = ext4_ext_alloc_meta(ip); 1094b394cd1eSFedor Uporov if (newblk == 0) 1095b394cd1eSFedor Uporov return (error); 1096b394cd1eSFedor Uporov 1097b394cd1eSFedor Uporov bp = getblk(ip->i_devvp, fsbtodb(fs, newblk), fs->e2fs_bsize, 0, 0, 0); 1098b394cd1eSFedor Uporov if (!bp) 1099b394cd1eSFedor Uporov return (EIO); 1100b394cd1eSFedor Uporov 1101b394cd1eSFedor Uporov /* Move top-level index/leaf into new block. */ 1102b394cd1eSFedor Uporov memmove(bp->b_data, curpath->ep_header, sizeof(ip->i_data)); 1103b394cd1eSFedor Uporov 1104b394cd1eSFedor Uporov /* Set size of new block */ 1105b394cd1eSFedor Uporov neh = ext4_ext_block_header(bp->b_data); 1106cd3acfe7SFedor Uporov neh->eh_magic = htole16(EXT4_EXT_MAGIC); 1107b394cd1eSFedor Uporov 1108b394cd1eSFedor Uporov if (ext4_ext_inode_depth(ip)) 1109cd3acfe7SFedor Uporov neh->eh_max = htole16(ext4_ext_space_block_index(ip)); 1110b394cd1eSFedor Uporov else 1111cd3acfe7SFedor Uporov neh->eh_max = htole16(ext4_ext_space_block(ip)); 1112b394cd1eSFedor Uporov 1113512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 1114b394cd1eSFedor Uporov error = bwrite(bp); 1115b394cd1eSFedor Uporov if (error) 1116b394cd1eSFedor Uporov goto out; 1117b394cd1eSFedor Uporov 1118b394cd1eSFedor Uporov bp = NULL; 1119b394cd1eSFedor Uporov 1120cd3acfe7SFedor Uporov curpath->ep_header->eh_magic = htole16(EXT4_EXT_MAGIC); 1121cd3acfe7SFedor Uporov curpath->ep_header->eh_max = htole16(ext4_ext_space_root(ip)); 1122cd3acfe7SFedor Uporov curpath->ep_header->eh_ecount = htole16(1); 1123b394cd1eSFedor Uporov curpath->ep_index = EXT_FIRST_INDEX(curpath->ep_header); 1124b394cd1eSFedor Uporov curpath->ep_index->ei_blk = EXT_FIRST_EXTENT(path[0].ep_header)->e_blk; 1125b394cd1eSFedor Uporov ext4_index_store_pblock(curpath->ep_index, newblk); 1126b394cd1eSFedor Uporov 1127b394cd1eSFedor Uporov neh = ext4_ext_inode_header(ip); 1128cd3acfe7SFedor Uporov neh->eh_depth = htole16(path->ep_depth + 1); 1129b394cd1eSFedor Uporov ext4_ext_dirty(ip, curpath); 1130b394cd1eSFedor Uporov out: 1131b394cd1eSFedor Uporov brelse(bp); 1132b394cd1eSFedor Uporov 1133b394cd1eSFedor Uporov return (error); 1134b394cd1eSFedor Uporov } 1135b394cd1eSFedor Uporov 1136b394cd1eSFedor Uporov static int 1137b394cd1eSFedor Uporov ext4_ext_create_new_leaf(struct inode *ip, struct ext4_extent_path *path, 1138b394cd1eSFedor Uporov struct ext4_extent *newext) 1139b394cd1eSFedor Uporov { 1140b394cd1eSFedor Uporov struct ext4_extent_path *curpath; 1141b394cd1eSFedor Uporov int depth, i, error; 1142b394cd1eSFedor Uporov 1143b394cd1eSFedor Uporov repeat: 1144b394cd1eSFedor Uporov i = depth = ext4_ext_inode_depth(ip); 1145b394cd1eSFedor Uporov 1146b394cd1eSFedor Uporov /* Look for free index entry int the tree */ 1147b394cd1eSFedor Uporov curpath = path + depth; 1148b394cd1eSFedor Uporov while (i > 0 && !EXT_HAS_FREE_INDEX(curpath)) { 1149b394cd1eSFedor Uporov i--; 1150b394cd1eSFedor Uporov curpath--; 1151b394cd1eSFedor Uporov } 1152b394cd1eSFedor Uporov 1153b394cd1eSFedor Uporov /* 1154b394cd1eSFedor Uporov * We use already allocated block for index block, 1155b394cd1eSFedor Uporov * so subsequent data blocks should be contiguous. 1156b394cd1eSFedor Uporov */ 1157b394cd1eSFedor Uporov if (EXT_HAS_FREE_INDEX(curpath)) { 1158b394cd1eSFedor Uporov error = ext4_ext_split(ip, path, newext, i); 1159b394cd1eSFedor Uporov if (error) 1160b394cd1eSFedor Uporov goto out; 1161b394cd1eSFedor Uporov 1162b394cd1eSFedor Uporov /* Refill path. */ 1163b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 1164cd3acfe7SFedor Uporov error = ext4_ext_find_extent(ip, le32toh(newext->e_blk), &path); 1165b394cd1eSFedor Uporov if (error) 1166b394cd1eSFedor Uporov goto out; 1167b394cd1eSFedor Uporov } else { 1168b394cd1eSFedor Uporov /* Tree is full, do grow in depth. */ 1169b394cd1eSFedor Uporov error = ext4_ext_grow_indepth(ip, path, newext); 1170b394cd1eSFedor Uporov if (error) 1171b394cd1eSFedor Uporov goto out; 1172b394cd1eSFedor Uporov 1173b394cd1eSFedor Uporov /* Refill path. */ 1174b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 1175cd3acfe7SFedor Uporov error = ext4_ext_find_extent(ip, le32toh(newext->e_blk), &path); 1176b394cd1eSFedor Uporov if (error) 1177b394cd1eSFedor Uporov goto out; 1178b394cd1eSFedor Uporov 1179b394cd1eSFedor Uporov /* Check and split tree if required. */ 1180b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1181cd3acfe7SFedor Uporov if (le16toh(path[depth].ep_header->eh_ecount) == 1182cd3acfe7SFedor Uporov le16toh(path[depth].ep_header->eh_max)) 1183b394cd1eSFedor Uporov goto repeat; 1184b394cd1eSFedor Uporov } 1185b394cd1eSFedor Uporov 1186b394cd1eSFedor Uporov out: 1187b394cd1eSFedor Uporov return (error); 1188b394cd1eSFedor Uporov } 1189b394cd1eSFedor Uporov 1190b394cd1eSFedor Uporov static int 1191b394cd1eSFedor Uporov ext4_ext_correct_indexes(struct inode *ip, struct ext4_extent_path *path) 1192b394cd1eSFedor Uporov { 1193b394cd1eSFedor Uporov struct ext4_extent_header *eh; 1194b394cd1eSFedor Uporov struct ext4_extent *ex; 1195b394cd1eSFedor Uporov int32_t border; 1196b394cd1eSFedor Uporov int depth, k; 1197b394cd1eSFedor Uporov 1198b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1199b394cd1eSFedor Uporov eh = path[depth].ep_header; 1200b394cd1eSFedor Uporov ex = path[depth].ep_ext; 1201b394cd1eSFedor Uporov 1202b394cd1eSFedor Uporov if (ex == NULL || eh == NULL) 1203b394cd1eSFedor Uporov return (EIO); 1204b394cd1eSFedor Uporov 1205b394cd1eSFedor Uporov if (!depth) 1206b394cd1eSFedor Uporov return (0); 1207b394cd1eSFedor Uporov 1208b394cd1eSFedor Uporov /* We will correct tree if first leaf got modified only. */ 1209b394cd1eSFedor Uporov if (ex != EXT_FIRST_EXTENT(eh)) 1210b394cd1eSFedor Uporov return (0); 1211b394cd1eSFedor Uporov 1212b394cd1eSFedor Uporov k = depth - 1; 1213cd3acfe7SFedor Uporov border = le32toh(path[depth].ep_ext->e_blk); 1214cd3acfe7SFedor Uporov path[k].ep_index->ei_blk = htole32(border); 1215b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + k); 1216b394cd1eSFedor Uporov while (k--) { 1217b394cd1eSFedor Uporov /* Change all left-side indexes. */ 1218b394cd1eSFedor Uporov if (path[k+1].ep_index != EXT_FIRST_INDEX(path[k+1].ep_header)) 1219b394cd1eSFedor Uporov break; 1220b394cd1eSFedor Uporov 1221cd3acfe7SFedor Uporov path[k].ep_index->ei_blk = htole32(border); 1222b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + k); 1223b394cd1eSFedor Uporov } 1224b394cd1eSFedor Uporov 1225b394cd1eSFedor Uporov return (0); 1226b394cd1eSFedor Uporov } 1227b394cd1eSFedor Uporov 1228b394cd1eSFedor Uporov static int 1229b394cd1eSFedor Uporov ext4_ext_insert_extent(struct inode *ip, struct ext4_extent_path *path, 1230b394cd1eSFedor Uporov struct ext4_extent *newext) 1231b394cd1eSFedor Uporov { 1232b394cd1eSFedor Uporov struct ext4_extent_header * eh; 1233b394cd1eSFedor Uporov struct ext4_extent *ex, *nex, *nearex; 1234b394cd1eSFedor Uporov struct ext4_extent_path *npath; 1235b394cd1eSFedor Uporov int depth, len, error, next; 1236b394cd1eSFedor Uporov 1237b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1238b394cd1eSFedor Uporov ex = path[depth].ep_ext; 1239b394cd1eSFedor Uporov npath = NULL; 1240b394cd1eSFedor Uporov 1241cd3acfe7SFedor Uporov if (htole16(newext->e_len) == 0 || path[depth].ep_header == NULL) 1242b394cd1eSFedor Uporov return (EINVAL); 1243b394cd1eSFedor Uporov 1244b394cd1eSFedor Uporov /* Insert block into found extent. */ 1245b394cd1eSFedor Uporov if (ex && ext4_can_extents_be_merged(ex, newext)) { 1246cd3acfe7SFedor Uporov ex->e_len = htole16(le16toh(ex->e_len) + le16toh(newext->e_len)); 1247b394cd1eSFedor Uporov eh = path[depth].ep_header; 1248b394cd1eSFedor Uporov nearex = ex; 1249b394cd1eSFedor Uporov goto merge; 1250b394cd1eSFedor Uporov } 1251b394cd1eSFedor Uporov 1252b394cd1eSFedor Uporov repeat: 1253b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1254b394cd1eSFedor Uporov eh = path[depth].ep_header; 1255cd3acfe7SFedor Uporov if (le16toh(eh->eh_ecount) < le16toh(eh->eh_max)) 1256b394cd1eSFedor Uporov goto has_space; 1257b394cd1eSFedor Uporov 1258b394cd1eSFedor Uporov /* Try next leaf */ 1259b394cd1eSFedor Uporov nex = EXT_LAST_EXTENT(eh); 1260b394cd1eSFedor Uporov next = ext4_ext_next_leaf_block(ip, path); 1261cd3acfe7SFedor Uporov if (le32toh(newext->e_blk) > le32toh(nex->e_blk) && next != 1262cd3acfe7SFedor Uporov EXT4_MAX_BLOCKS) { 1263b394cd1eSFedor Uporov KASSERT(npath == NULL, 1264b394cd1eSFedor Uporov ("ext4_ext_insert_extent: bad path")); 1265b394cd1eSFedor Uporov 1266b394cd1eSFedor Uporov error = ext4_ext_find_extent(ip, next, &npath); 1267b394cd1eSFedor Uporov if (error) 1268b394cd1eSFedor Uporov goto cleanup; 1269b394cd1eSFedor Uporov 1270b394cd1eSFedor Uporov if (npath->ep_depth != path->ep_depth) { 1271b394cd1eSFedor Uporov error = EIO; 1272b394cd1eSFedor Uporov goto cleanup; 1273b394cd1eSFedor Uporov } 1274b394cd1eSFedor Uporov 1275b394cd1eSFedor Uporov eh = npath[depth].ep_header; 1276cd3acfe7SFedor Uporov if (le16toh(eh->eh_ecount) < le16toh(eh->eh_max)) { 1277b394cd1eSFedor Uporov path = npath; 1278b394cd1eSFedor Uporov goto repeat; 1279b394cd1eSFedor Uporov } 1280b394cd1eSFedor Uporov } 1281b394cd1eSFedor Uporov 1282b394cd1eSFedor Uporov /* 1283b394cd1eSFedor Uporov * There is no free space in the found leaf, 1284b394cd1eSFedor Uporov * try to add a new leaf to the tree. 1285b394cd1eSFedor Uporov */ 1286b394cd1eSFedor Uporov error = ext4_ext_create_new_leaf(ip, path, newext); 1287b394cd1eSFedor Uporov if (error) 1288b394cd1eSFedor Uporov goto cleanup; 1289b394cd1eSFedor Uporov 1290b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1291b394cd1eSFedor Uporov eh = path[depth].ep_header; 1292b394cd1eSFedor Uporov 1293b394cd1eSFedor Uporov has_space: 1294b394cd1eSFedor Uporov nearex = path[depth].ep_ext; 1295b394cd1eSFedor Uporov if (!nearex) { 1296b394cd1eSFedor Uporov /* Create new extent in the leaf. */ 1297b394cd1eSFedor Uporov path[depth].ep_ext = EXT_FIRST_EXTENT(eh); 1298cd3acfe7SFedor Uporov } else if (le32toh(newext->e_blk) > le32toh(nearex->e_blk)) { 1299b394cd1eSFedor Uporov if (nearex != EXT_LAST_EXTENT(eh)) { 1300b394cd1eSFedor Uporov len = EXT_MAX_EXTENT(eh) - nearex; 1301b394cd1eSFedor Uporov len = (len - 1) * sizeof(struct ext4_extent); 1302b394cd1eSFedor Uporov len = len < 0 ? 0 : len; 1303b394cd1eSFedor Uporov memmove(nearex + 2, nearex + 1, len); 1304b394cd1eSFedor Uporov } 1305b394cd1eSFedor Uporov path[depth].ep_ext = nearex + 1; 1306b394cd1eSFedor Uporov } else { 1307b394cd1eSFedor Uporov len = (EXT_MAX_EXTENT(eh) - nearex) * sizeof(struct ext4_extent); 1308b394cd1eSFedor Uporov len = len < 0 ? 0 : len; 1309b394cd1eSFedor Uporov memmove(nearex + 1, nearex, len); 1310b394cd1eSFedor Uporov path[depth].ep_ext = nearex; 1311b394cd1eSFedor Uporov } 1312b394cd1eSFedor Uporov 1313cd3acfe7SFedor Uporov eh->eh_ecount = htole16(le16toh(eh->eh_ecount) + 1); 1314b394cd1eSFedor Uporov nearex = path[depth].ep_ext; 1315b394cd1eSFedor Uporov nearex->e_blk = newext->e_blk; 1316b394cd1eSFedor Uporov nearex->e_start_lo = newext->e_start_lo; 1317b394cd1eSFedor Uporov nearex->e_start_hi = newext->e_start_hi; 1318b394cd1eSFedor Uporov nearex->e_len = newext->e_len; 1319b394cd1eSFedor Uporov 1320b394cd1eSFedor Uporov merge: 1321b394cd1eSFedor Uporov /* Try to merge extents to the right. */ 1322b394cd1eSFedor Uporov while (nearex < EXT_LAST_EXTENT(eh)) { 1323b394cd1eSFedor Uporov if (!ext4_can_extents_be_merged(nearex, nearex + 1)) 1324b394cd1eSFedor Uporov break; 1325b394cd1eSFedor Uporov 1326b394cd1eSFedor Uporov /* Merge with next extent. */ 1327cd3acfe7SFedor Uporov nearex->e_len = htole16(le16toh(nearex->e_len) + 1328cd3acfe7SFedor Uporov le16toh(nearex[1].e_len)); 1329b394cd1eSFedor Uporov if (nearex + 1 < EXT_LAST_EXTENT(eh)) { 1330b394cd1eSFedor Uporov len = (EXT_LAST_EXTENT(eh) - nearex - 1) * 1331b394cd1eSFedor Uporov sizeof(struct ext4_extent); 1332b394cd1eSFedor Uporov memmove(nearex + 1, nearex + 2, len); 1333b394cd1eSFedor Uporov } 1334b394cd1eSFedor Uporov 1335cd3acfe7SFedor Uporov eh->eh_ecount = htole16(le16toh(eh->eh_ecount) - 1); 1336cd3acfe7SFedor Uporov KASSERT(le16toh(eh->eh_ecount) != 0, 1337b394cd1eSFedor Uporov ("ext4_ext_insert_extent: bad ecount")); 1338b394cd1eSFedor Uporov } 1339b394cd1eSFedor Uporov 1340b394cd1eSFedor Uporov /* 1341b394cd1eSFedor Uporov * Try to merge extents to the left, 1342b394cd1eSFedor Uporov * start from inexes correction. 1343b394cd1eSFedor Uporov */ 1344b394cd1eSFedor Uporov error = ext4_ext_correct_indexes(ip, path); 1345b394cd1eSFedor Uporov if (error) 1346b394cd1eSFedor Uporov goto cleanup; 1347b394cd1eSFedor Uporov 1348b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + depth); 1349b394cd1eSFedor Uporov 1350b394cd1eSFedor Uporov cleanup: 1351b394cd1eSFedor Uporov if (npath) { 1352b394cd1eSFedor Uporov ext4_ext_drop_refs(npath); 1353b394cd1eSFedor Uporov free(npath, M_EXT2EXTENTS); 1354b394cd1eSFedor Uporov } 1355b394cd1eSFedor Uporov 1356b394cd1eSFedor Uporov ip->i_ext_cache.ec_type = EXT4_EXT_CACHE_NO; 1357b394cd1eSFedor Uporov return (error); 1358b394cd1eSFedor Uporov } 1359b394cd1eSFedor Uporov 1360b394cd1eSFedor Uporov static e4fs_daddr_t 1361b394cd1eSFedor Uporov ext4_new_blocks(struct inode *ip, daddr_t lbn, e4fs_daddr_t pref, 1362b394cd1eSFedor Uporov struct ucred *cred, unsigned long *count, int *perror) 1363b394cd1eSFedor Uporov { 1364b394cd1eSFedor Uporov struct m_ext2fs *fs; 1365b394cd1eSFedor Uporov e4fs_daddr_t newblk; 1366b394cd1eSFedor Uporov 1367b394cd1eSFedor Uporov /* 1368b394cd1eSFedor Uporov * We will allocate only single block for now. 1369b394cd1eSFedor Uporov */ 1370b394cd1eSFedor Uporov if (*count > 1) 1371b394cd1eSFedor Uporov return (0); 1372b394cd1eSFedor Uporov 1373f86f5cd4SPedro F. Giffuni fs = ip->i_e2fs; 1374b394cd1eSFedor Uporov EXT2_LOCK(ip->i_ump); 1375b394cd1eSFedor Uporov *perror = ext2_alloc(ip, lbn, pref, (int)fs->e2fs_bsize, cred, &newblk); 1376b394cd1eSFedor Uporov if (*perror) 1377b394cd1eSFedor Uporov return (0); 1378b394cd1eSFedor Uporov 1379b394cd1eSFedor Uporov if (newblk) { 1380b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 1381b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 1382b394cd1eSFedor Uporov } 1383b394cd1eSFedor Uporov 1384b394cd1eSFedor Uporov return (newblk); 1385b394cd1eSFedor Uporov } 1386b394cd1eSFedor Uporov 1387b394cd1eSFedor Uporov int 1388b394cd1eSFedor Uporov ext4_ext_get_blocks(struct inode *ip, e4fs_daddr_t iblk, 1389b394cd1eSFedor Uporov unsigned long max_blocks, struct ucred *cred, struct buf **bpp, 13903acd9182SFedor Uporov int *pallocated, daddr_t *nb) 1391b394cd1eSFedor Uporov { 1392b394cd1eSFedor Uporov struct m_ext2fs *fs; 1393b394cd1eSFedor Uporov struct buf *bp = NULL; 1394b394cd1eSFedor Uporov struct ext4_extent_path *path; 1395b394cd1eSFedor Uporov struct ext4_extent newex, *ex; 1396b394cd1eSFedor Uporov e4fs_daddr_t bpref, newblk = 0; 1397b394cd1eSFedor Uporov unsigned long allocated = 0; 1398b394cd1eSFedor Uporov int error = 0, depth; 1399b394cd1eSFedor Uporov 1400b394cd1eSFedor Uporov if(bpp) 1401b394cd1eSFedor Uporov *bpp = NULL; 1402f86f5cd4SPedro F. Giffuni *pallocated = 0; 1403b394cd1eSFedor Uporov 1404b394cd1eSFedor Uporov /* Check cache. */ 1405f86f5cd4SPedro F. Giffuni path = NULL; 1406b394cd1eSFedor Uporov if ((bpref = ext4_ext_in_cache(ip, iblk, &newex))) { 1407b394cd1eSFedor Uporov if (bpref == EXT4_EXT_CACHE_IN) { 1408b394cd1eSFedor Uporov /* Block is already allocated. */ 1409cd3acfe7SFedor Uporov newblk = iblk - le32toh(newex.e_blk) + 1410b394cd1eSFedor Uporov ext4_ext_extent_pblock(&newex); 1411cd3acfe7SFedor Uporov allocated = le16toh(newex.e_len) - (iblk - le32toh(newex.e_blk)); 1412b394cd1eSFedor Uporov goto out; 1413b394cd1eSFedor Uporov } else { 1414b394cd1eSFedor Uporov error = EIO; 1415b394cd1eSFedor Uporov goto out2; 1416b394cd1eSFedor Uporov } 1417b394cd1eSFedor Uporov } 1418b394cd1eSFedor Uporov 1419b394cd1eSFedor Uporov error = ext4_ext_find_extent(ip, iblk, &path); 1420b394cd1eSFedor Uporov if (error) { 1421b394cd1eSFedor Uporov goto out2; 1422b394cd1eSFedor Uporov } 1423b394cd1eSFedor Uporov 1424b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1425b394cd1eSFedor Uporov if (path[depth].ep_ext == NULL && depth != 0) { 1426b394cd1eSFedor Uporov error = EIO; 1427b394cd1eSFedor Uporov goto out2; 1428b394cd1eSFedor Uporov } 1429b394cd1eSFedor Uporov 1430b394cd1eSFedor Uporov if ((ex = path[depth].ep_ext)) { 1431cd3acfe7SFedor Uporov uint64_t lblk = le32toh(ex->e_blk); 1432cd3acfe7SFedor Uporov uint16_t e_len = le16toh(ex->e_len); 1433b394cd1eSFedor Uporov e4fs_daddr_t e_start = ext4_ext_extent_pblock(ex); 1434b394cd1eSFedor Uporov 1435b394cd1eSFedor Uporov if (e_len > EXT4_MAX_LEN) 1436b394cd1eSFedor Uporov goto out2; 1437b394cd1eSFedor Uporov 1438b394cd1eSFedor Uporov /* If we found extent covers block, simply return it. */ 1439b394cd1eSFedor Uporov if (iblk >= lblk && iblk < lblk + e_len) { 1440b394cd1eSFedor Uporov newblk = iblk - lblk + e_start; 1441b394cd1eSFedor Uporov allocated = e_len - (iblk - lblk); 1442b394cd1eSFedor Uporov ext4_ext_put_in_cache(ip, lblk, e_len, 1443b394cd1eSFedor Uporov e_start, EXT4_EXT_CACHE_IN); 1444b394cd1eSFedor Uporov goto out; 1445b394cd1eSFedor Uporov } 1446b394cd1eSFedor Uporov } 1447b394cd1eSFedor Uporov 1448b394cd1eSFedor Uporov /* Allocate the new block. */ 1449b394cd1eSFedor Uporov if (S_ISREG(ip->i_mode) && (!ip->i_next_alloc_block)) { 1450b394cd1eSFedor Uporov ip->i_next_alloc_goal = 0; 1451b394cd1eSFedor Uporov } 1452b394cd1eSFedor Uporov 1453b394cd1eSFedor Uporov bpref = ext4_ext_blkpref(ip, path, iblk); 1454b394cd1eSFedor Uporov allocated = max_blocks; 1455b394cd1eSFedor Uporov newblk = ext4_new_blocks(ip, iblk, bpref, cred, &allocated, &error); 1456b394cd1eSFedor Uporov if (!newblk) 1457b394cd1eSFedor Uporov goto out2; 1458b394cd1eSFedor Uporov 1459b394cd1eSFedor Uporov /* Try to insert new extent into found leaf and return. */ 1460cd3acfe7SFedor Uporov newex.e_blk = htole32(iblk); 1461b394cd1eSFedor Uporov ext4_ext_store_pblock(&newex, newblk); 1462cd3acfe7SFedor Uporov newex.e_len = htole16(allocated); 1463b394cd1eSFedor Uporov error = ext4_ext_insert_extent(ip, path, &newex); 1464b394cd1eSFedor Uporov if (error) 1465b394cd1eSFedor Uporov goto out2; 1466b394cd1eSFedor Uporov 1467b394cd1eSFedor Uporov newblk = ext4_ext_extent_pblock(&newex); 1468b394cd1eSFedor Uporov ext4_ext_put_in_cache(ip, iblk, allocated, newblk, EXT4_EXT_CACHE_IN); 1469b394cd1eSFedor Uporov *pallocated = 1; 1470b394cd1eSFedor Uporov 1471b394cd1eSFedor Uporov out: 1472b394cd1eSFedor Uporov if (allocated > max_blocks) 1473b394cd1eSFedor Uporov allocated = max_blocks; 1474b394cd1eSFedor Uporov 1475b394cd1eSFedor Uporov if (bpp) 1476b394cd1eSFedor Uporov { 1477f86f5cd4SPedro F. Giffuni fs = ip->i_e2fs; 1478b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(fs, newblk), 1479b394cd1eSFedor Uporov fs->e2fs_bsize, cred, &bp); 1480b394cd1eSFedor Uporov if (error) { 1481b394cd1eSFedor Uporov brelse(bp); 1482b394cd1eSFedor Uporov } else { 1483b394cd1eSFedor Uporov *bpp = bp; 1484b394cd1eSFedor Uporov } 1485b394cd1eSFedor Uporov } 1486b394cd1eSFedor Uporov 1487b394cd1eSFedor Uporov out2: 1488b394cd1eSFedor Uporov if (path) { 1489b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 1490b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 1491b394cd1eSFedor Uporov } 1492b394cd1eSFedor Uporov 1493b394cd1eSFedor Uporov if (nb) 1494b394cd1eSFedor Uporov *nb = newblk; 1495b394cd1eSFedor Uporov 1496b394cd1eSFedor Uporov return (error); 1497b394cd1eSFedor Uporov } 1498b394cd1eSFedor Uporov 1499b394cd1eSFedor Uporov static inline struct ext4_extent_header * 1500b394cd1eSFedor Uporov ext4_ext_header(struct inode *ip) 1501b394cd1eSFedor Uporov { 1502b394cd1eSFedor Uporov 1503f86f5cd4SPedro F. Giffuni return ((struct ext4_extent_header *)ip->i_db); 1504b394cd1eSFedor Uporov } 1505b394cd1eSFedor Uporov 1506b394cd1eSFedor Uporov static int 1507b394cd1eSFedor Uporov ext4_remove_blocks(struct inode *ip, struct ext4_extent *ex, 1508b394cd1eSFedor Uporov unsigned long from, unsigned long to) 1509b394cd1eSFedor Uporov { 1510b394cd1eSFedor Uporov unsigned long num, start; 1511b394cd1eSFedor Uporov 1512cd3acfe7SFedor Uporov if (from >= le32toh(ex->e_blk) && 1513cd3acfe7SFedor Uporov to == le32toh(ex->e_blk) + ext4_ext_get_actual_len(ex) - 1) { 1514b394cd1eSFedor Uporov /* Tail cleanup. */ 1515cd3acfe7SFedor Uporov num = le32toh(ex->e_blk) + ext4_ext_get_actual_len(ex) - from; 1516b394cd1eSFedor Uporov start = ext4_ext_extent_pblock(ex) + 1517b394cd1eSFedor Uporov ext4_ext_get_actual_len(ex) - num; 1518b394cd1eSFedor Uporov ext4_ext_blkfree(ip, start, num, 0); 1519b394cd1eSFedor Uporov } 1520b394cd1eSFedor Uporov 1521b394cd1eSFedor Uporov return (0); 1522b394cd1eSFedor Uporov } 1523b394cd1eSFedor Uporov 1524b394cd1eSFedor Uporov static int 1525b394cd1eSFedor Uporov ext4_ext_rm_index(struct inode *ip, struct ext4_extent_path *path) 1526b394cd1eSFedor Uporov { 1527b394cd1eSFedor Uporov e4fs_daddr_t leaf; 1528b394cd1eSFedor Uporov 1529b394cd1eSFedor Uporov /* Free index block. */ 1530b394cd1eSFedor Uporov path--; 1531b394cd1eSFedor Uporov leaf = ext4_ext_index_pblock(path->ep_index); 1532b394cd1eSFedor Uporov KASSERT(path->ep_header->eh_ecount != 0, 1533b394cd1eSFedor Uporov ("ext4_ext_rm_index: bad ecount")); 1534cd3acfe7SFedor Uporov path->ep_header->eh_ecount = 1535cd3acfe7SFedor Uporov htole16(le16toh(path->ep_header->eh_ecount) - 1); 1536b394cd1eSFedor Uporov ext4_ext_dirty(ip, path); 1537b394cd1eSFedor Uporov ext4_ext_blkfree(ip, leaf, 1, 0); 1538b394cd1eSFedor Uporov return (0); 1539b394cd1eSFedor Uporov } 1540b394cd1eSFedor Uporov 1541b394cd1eSFedor Uporov static int 1542b394cd1eSFedor Uporov ext4_ext_rm_leaf(struct inode *ip, struct ext4_extent_path *path, 1543b394cd1eSFedor Uporov uint64_t start) 1544b394cd1eSFedor Uporov { 1545b394cd1eSFedor Uporov struct ext4_extent_header *eh; 1546f86f5cd4SPedro F. Giffuni struct ext4_extent *ex; 1547b394cd1eSFedor Uporov unsigned int a, b, block, num; 1548b394cd1eSFedor Uporov unsigned long ex_blk; 1549b394cd1eSFedor Uporov unsigned short ex_len; 1550f86f5cd4SPedro F. Giffuni int depth; 1551b394cd1eSFedor Uporov int error, correct_index; 1552b394cd1eSFedor Uporov 1553b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1554b394cd1eSFedor Uporov if (!path[depth].ep_header) { 1555b394cd1eSFedor Uporov if (path[depth].ep_data == NULL) 1556b394cd1eSFedor Uporov return (EINVAL); 1557b394cd1eSFedor Uporov path[depth].ep_header = 1558b394cd1eSFedor Uporov (struct ext4_extent_header* )path[depth].ep_data; 1559b394cd1eSFedor Uporov } 1560b394cd1eSFedor Uporov 1561b394cd1eSFedor Uporov eh = path[depth].ep_header; 1562b394cd1eSFedor Uporov if (!eh) { 156384b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 1564f86f5cd4SPedro F. Giffuni "bad header => extent corrupted"); 1565b394cd1eSFedor Uporov return (EIO); 1566b394cd1eSFedor Uporov } 1567b394cd1eSFedor Uporov 1568b394cd1eSFedor Uporov ex = EXT_LAST_EXTENT(eh); 1569cd3acfe7SFedor Uporov ex_blk = le32toh(ex->e_blk); 1570b394cd1eSFedor Uporov ex_len = ext4_ext_get_actual_len(ex); 1571b394cd1eSFedor Uporov 1572f86f5cd4SPedro F. Giffuni error = 0; 1573f86f5cd4SPedro F. Giffuni correct_index = 0; 1574b394cd1eSFedor Uporov while (ex >= EXT_FIRST_EXTENT(eh) && ex_blk + ex_len > start) { 1575b394cd1eSFedor Uporov path[depth].ep_ext = ex; 1576b394cd1eSFedor Uporov a = ex_blk > start ? ex_blk : start; 1577b394cd1eSFedor Uporov b = (uint64_t)ex_blk + ex_len - 1 < 1578b394cd1eSFedor Uporov EXT4_MAX_BLOCKS ? ex_blk + ex_len - 1 : EXT4_MAX_BLOCKS; 1579b394cd1eSFedor Uporov 1580b394cd1eSFedor Uporov if (a != ex_blk && b != ex_blk + ex_len - 1) 1581b394cd1eSFedor Uporov return (EINVAL); 1582b394cd1eSFedor Uporov else if (a != ex_blk) { 1583b394cd1eSFedor Uporov /* Remove tail of the extent. */ 1584b394cd1eSFedor Uporov block = ex_blk; 1585b394cd1eSFedor Uporov num = a - block; 1586b394cd1eSFedor Uporov } else if (b != ex_blk + ex_len - 1) { 1587b394cd1eSFedor Uporov /* Remove head of the extent, not implemented. */ 1588b394cd1eSFedor Uporov return (EINVAL); 1589b394cd1eSFedor Uporov } else { 1590b394cd1eSFedor Uporov /* Remove whole extent. */ 1591b394cd1eSFedor Uporov block = ex_blk; 1592b394cd1eSFedor Uporov num = 0; 1593b394cd1eSFedor Uporov } 1594b394cd1eSFedor Uporov 15953fcbb8c0SFedor Uporov if (ex == EXT_FIRST_EXTENT(eh)) 1596b394cd1eSFedor Uporov correct_index = 1; 1597b394cd1eSFedor Uporov 1598b394cd1eSFedor Uporov error = ext4_remove_blocks(ip, ex, a, b); 1599b394cd1eSFedor Uporov if (error) 1600b394cd1eSFedor Uporov goto out; 1601b394cd1eSFedor Uporov 1602b394cd1eSFedor Uporov if (num == 0) { 1603b394cd1eSFedor Uporov ext4_ext_store_pblock(ex, 0); 1604cd3acfe7SFedor Uporov eh->eh_ecount = htole16(le16toh(eh->eh_ecount) - 1); 1605b394cd1eSFedor Uporov } 1606b394cd1eSFedor Uporov 1607cd3acfe7SFedor Uporov ex->e_blk = htole32(block); 1608cd3acfe7SFedor Uporov ex->e_len = htole16(num); 1609b394cd1eSFedor Uporov 1610b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + depth); 1611b394cd1eSFedor Uporov 1612b394cd1eSFedor Uporov ex--; 1613cd3acfe7SFedor Uporov ex_blk = htole32(ex->e_blk); 1614b394cd1eSFedor Uporov ex_len = ext4_ext_get_actual_len(ex); 1615b394cd1eSFedor Uporov }; 1616b394cd1eSFedor Uporov 1617cd3acfe7SFedor Uporov if (correct_index && le16toh(eh->eh_ecount)) 1618b394cd1eSFedor Uporov error = ext4_ext_correct_indexes(ip, path); 1619b394cd1eSFedor Uporov 1620b394cd1eSFedor Uporov /* 1621b394cd1eSFedor Uporov * If this leaf is free, we should 1622b394cd1eSFedor Uporov * remove it from index block above. 1623b394cd1eSFedor Uporov */ 1624cd3acfe7SFedor Uporov if (error == 0 && eh->eh_ecount == 0 && 1625cd3acfe7SFedor Uporov path[depth].ep_data != NULL) 1626b394cd1eSFedor Uporov error = ext4_ext_rm_index(ip, path + depth); 1627b394cd1eSFedor Uporov 1628b394cd1eSFedor Uporov out: 1629b394cd1eSFedor Uporov return (error); 1630b394cd1eSFedor Uporov } 1631b394cd1eSFedor Uporov 1632b394cd1eSFedor Uporov static struct buf * 1633b394cd1eSFedor Uporov ext4_read_extent_tree_block(struct inode *ip, e4fs_daddr_t pblk, 1634b394cd1eSFedor Uporov int depth, int flags) 1635b394cd1eSFedor Uporov { 1636b394cd1eSFedor Uporov struct m_ext2fs *fs; 1637b394cd1eSFedor Uporov struct ext4_extent_header *eh; 1638b394cd1eSFedor Uporov struct buf *bp; 1639b394cd1eSFedor Uporov int error; 1640b394cd1eSFedor Uporov 1641b394cd1eSFedor Uporov fs = ip->i_e2fs; 1642b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(fs, pblk), 1643b394cd1eSFedor Uporov fs->e2fs_bsize, NOCRED, &bp); 1644b394cd1eSFedor Uporov if (error) { 1645d7511a40SPedro F. Giffuni return (NULL); 1646d7511a40SPedro F. Giffuni } 1647b394cd1eSFedor Uporov 1648b394cd1eSFedor Uporov eh = ext4_ext_block_header(bp->b_data); 1649cd3acfe7SFedor Uporov if (le16toh(eh->eh_depth) != depth) { 165084b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 165184b89556SFedor Uporov "unexpected eh_depth"); 1652b394cd1eSFedor Uporov goto err; 1653d7511a40SPedro F. Giffuni } 1654d7511a40SPedro F. Giffuni 1655*f1d5e2c8SFedor Uporov error = ext4_ext_check_header(ip, eh, depth); 1656b394cd1eSFedor Uporov if (error) 1657b394cd1eSFedor Uporov goto err; 1658d7511a40SPedro F. Giffuni 1659b394cd1eSFedor Uporov return (bp); 1660b394cd1eSFedor Uporov 1661b394cd1eSFedor Uporov err: 1662b394cd1eSFedor Uporov brelse(bp); 1663b394cd1eSFedor Uporov return (NULL); 1664b394cd1eSFedor Uporov 1665b394cd1eSFedor Uporov } 1666b394cd1eSFedor Uporov 1667b394cd1eSFedor Uporov static int inline 1668b394cd1eSFedor Uporov ext4_ext_more_to_rm(struct ext4_extent_path *path) 1669b394cd1eSFedor Uporov { 1670b394cd1eSFedor Uporov 1671b394cd1eSFedor Uporov KASSERT(path->ep_index != NULL, 1672b394cd1eSFedor Uporov ("ext4_ext_more_to_rm: bad index from path")); 1673b394cd1eSFedor Uporov 1674b394cd1eSFedor Uporov if (path->ep_index < EXT_FIRST_INDEX(path->ep_header)) 1675b394cd1eSFedor Uporov return (0); 1676b394cd1eSFedor Uporov 1677cd3acfe7SFedor Uporov if (le16toh(path->ep_header->eh_ecount) == path->index_count) 1678b394cd1eSFedor Uporov return (0); 1679b394cd1eSFedor Uporov 1680b394cd1eSFedor Uporov return (1); 1681b394cd1eSFedor Uporov } 1682b394cd1eSFedor Uporov 1683b394cd1eSFedor Uporov int 1684b394cd1eSFedor Uporov ext4_ext_remove_space(struct inode *ip, off_t length, int flags, 1685b394cd1eSFedor Uporov struct ucred *cred, struct thread *td) 1686b394cd1eSFedor Uporov { 1687b394cd1eSFedor Uporov struct buf *bp; 1688b394cd1eSFedor Uporov struct ext4_extent_header *ehp; 1689b394cd1eSFedor Uporov struct ext4_extent_path *path; 1690b394cd1eSFedor Uporov int depth; 1691b394cd1eSFedor Uporov int i, error; 1692b394cd1eSFedor Uporov 1693b394cd1eSFedor Uporov ehp = (struct ext4_extent_header *)ip->i_db; 1694b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1695b394cd1eSFedor Uporov 1696*f1d5e2c8SFedor Uporov error = ext4_ext_check_header(ip, ehp, depth); 1697b394cd1eSFedor Uporov if(error) 1698b394cd1eSFedor Uporov return (error); 1699b394cd1eSFedor Uporov 1700b394cd1eSFedor Uporov path = malloc(sizeof(struct ext4_extent_path) * (depth + 1), 1701b394cd1eSFedor Uporov M_EXT2EXTENTS, M_WAITOK | M_ZERO); 1702b394cd1eSFedor Uporov path[0].ep_header = ehp; 1703b394cd1eSFedor Uporov path[0].ep_depth = depth; 1704f86f5cd4SPedro F. Giffuni i = 0; 1705f86f5cd4SPedro F. Giffuni while (error == 0 && i >= 0) { 1706b394cd1eSFedor Uporov if (i == depth) { 1707b394cd1eSFedor Uporov /* This is leaf. */ 1708b394cd1eSFedor Uporov error = ext4_ext_rm_leaf(ip, path, length); 1709b394cd1eSFedor Uporov if (error) 1710b394cd1eSFedor Uporov break; 1711b394cd1eSFedor Uporov free(path[i].ep_data, M_EXT2EXTENTS); 1712b394cd1eSFedor Uporov path[i].ep_data = NULL; 1713b394cd1eSFedor Uporov i--; 1714b394cd1eSFedor Uporov continue; 1715b394cd1eSFedor Uporov } 1716b394cd1eSFedor Uporov 1717b394cd1eSFedor Uporov /* This is index. */ 1718b394cd1eSFedor Uporov if (!path[i].ep_header) 1719b394cd1eSFedor Uporov path[i].ep_header = 1720b394cd1eSFedor Uporov (struct ext4_extent_header *)path[i].ep_data; 1721b394cd1eSFedor Uporov 1722b394cd1eSFedor Uporov if (!path[i].ep_index) { 1723b394cd1eSFedor Uporov /* This level hasn't touched yet. */ 1724b394cd1eSFedor Uporov path[i].ep_index = EXT_LAST_INDEX(path[i].ep_header); 1725cd3acfe7SFedor Uporov path[i].index_count = 1726cd3acfe7SFedor Uporov le16toh(path[i].ep_header->eh_ecount) + 1; 1727b394cd1eSFedor Uporov } else { 1728b394cd1eSFedor Uporov /* We've already was here, see at next index. */ 1729b394cd1eSFedor Uporov path[i].ep_index--; 1730b394cd1eSFedor Uporov } 1731b394cd1eSFedor Uporov 1732b394cd1eSFedor Uporov if (ext4_ext_more_to_rm(path + i)) { 1733b394cd1eSFedor Uporov memset(path + i + 1, 0, sizeof(*path)); 1734b394cd1eSFedor Uporov bp = ext4_read_extent_tree_block(ip, 1735b394cd1eSFedor Uporov ext4_ext_index_pblock(path[i].ep_index), 1736b394cd1eSFedor Uporov path[0].ep_depth - (i + 1), 0); 1737b394cd1eSFedor Uporov if (!bp) { 1738b394cd1eSFedor Uporov error = EIO; 1739b394cd1eSFedor Uporov break; 1740b394cd1eSFedor Uporov } 1741b394cd1eSFedor Uporov 1742b394cd1eSFedor Uporov ext4_ext_fill_path_bdata(&path[i+1], bp, 1743b394cd1eSFedor Uporov ext4_ext_index_pblock(path[i].ep_index)); 1744b394cd1eSFedor Uporov brelse(bp); 1745cd3acfe7SFedor Uporov path[i].index_count = 1746cd3acfe7SFedor Uporov le16toh(path[i].ep_header->eh_ecount); 1747b394cd1eSFedor Uporov i++; 1748b394cd1eSFedor Uporov } else { 1749b394cd1eSFedor Uporov if (path[i].ep_header->eh_ecount == 0 && i > 0) { 1750b394cd1eSFedor Uporov /* Index is empty, remove it. */ 1751b394cd1eSFedor Uporov error = ext4_ext_rm_index(ip, path + i); 1752b394cd1eSFedor Uporov } 1753b394cd1eSFedor Uporov free(path[i].ep_data, M_EXT2EXTENTS); 1754b394cd1eSFedor Uporov path[i].ep_data = NULL; 1755b394cd1eSFedor Uporov i--; 1756b394cd1eSFedor Uporov } 1757b394cd1eSFedor Uporov } 1758b394cd1eSFedor Uporov 1759b394cd1eSFedor Uporov if (path->ep_header->eh_ecount == 0) { 1760b394cd1eSFedor Uporov /* 1761b394cd1eSFedor Uporov * Truncate the tree to zero. 1762b394cd1eSFedor Uporov */ 1763b394cd1eSFedor Uporov ext4_ext_header(ip)->eh_depth = 0; 1764cd3acfe7SFedor Uporov ext4_ext_header(ip)->eh_max = htole16(ext4_ext_space_root(ip)); 1765b394cd1eSFedor Uporov ext4_ext_dirty(ip, path); 1766b394cd1eSFedor Uporov } 1767b394cd1eSFedor Uporov 1768b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 1769b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 1770b394cd1eSFedor Uporov 1771b3f46656SFedor Uporov ip->i_ext_cache.ec_type = EXT4_EXT_CACHE_NO; 1772b394cd1eSFedor Uporov return (error); 1773d7511a40SPedro F. Giffuni } 1774