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> 39d7511a40SPedro F. Giffuni #include <sys/conf.h> 40*84b89556SFedor Uporov #include <sys/sdt.h> 41b394cd1eSFedor Uporov #include <sys/stat.h> 42d7511a40SPedro F. Giffuni 43d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2_mount.h> 44d7511a40SPedro F. Giffuni #include <fs/ext2fs/fs.h> 45d7511a40SPedro F. Giffuni #include <fs/ext2fs/inode.h> 46d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2fs.h> 47d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2_extents.h> 48d7511a40SPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h> 49d7511a40SPedro F. Giffuni 50*84b89556SFedor Uporov SDT_PROVIDER_DECLARE(ext2fs); 51*84b89556SFedor Uporov /* 52*84b89556SFedor Uporov * ext2fs trace probe: 53*84b89556SFedor Uporov * arg0: verbosity. Higher numbers give more verbose messages 54*84b89556SFedor Uporov * arg1: Textual message 55*84b89556SFedor Uporov */ 56*84b89556SFedor Uporov SDT_PROBE_DEFINE2(ext2fs, , trace, extents, "int", "char*"); 57*84b89556SFedor Uporov 58b394cd1eSFedor Uporov static MALLOC_DEFINE(M_EXT2EXTENTS, "ext2_extents", "EXT2 extents"); 59d7511a40SPedro F. Giffuni 60*84b89556SFedor Uporov #ifdef EXT2FS_PRINT_EXTENTS 61b394cd1eSFedor Uporov static void 62b394cd1eSFedor Uporov ext4_ext_print_extent(struct ext4_extent *ep) 63b394cd1eSFedor Uporov { 64b394cd1eSFedor Uporov 65e06e5241SFedor Uporov printf(" ext %p => (blk %u len %u start %ju)\n", 66b394cd1eSFedor Uporov ep, ep->e_blk, ep->e_len, 67b394cd1eSFedor Uporov (uint64_t)ep->e_start_hi << 32 | ep->e_start_lo); 68d7511a40SPedro F. Giffuni } 69d7511a40SPedro F. Giffuni 70b394cd1eSFedor Uporov static void ext4_ext_print_header(struct inode *ip, struct ext4_extent_header *ehp); 71b394cd1eSFedor Uporov 72b394cd1eSFedor Uporov static void 73b394cd1eSFedor Uporov ext4_ext_print_index(struct inode *ip, struct ext4_extent_index *ex, int do_walk) 74b394cd1eSFedor Uporov { 75b394cd1eSFedor Uporov struct m_ext2fs *fs; 76b394cd1eSFedor Uporov struct buf *bp; 77b394cd1eSFedor Uporov int error; 78b394cd1eSFedor Uporov 79b394cd1eSFedor Uporov fs = ip->i_e2fs; 80b394cd1eSFedor Uporov 81e06e5241SFedor Uporov printf(" index %p => (blk %u pblk %ju)\n", 82b394cd1eSFedor Uporov ex, ex->ei_blk, (uint64_t)ex->ei_leaf_hi << 32 | ex->ei_leaf_lo); 83b394cd1eSFedor Uporov 84b394cd1eSFedor Uporov if(!do_walk) 85b394cd1eSFedor Uporov return; 86b394cd1eSFedor Uporov 87b394cd1eSFedor Uporov if ((error = bread(ip->i_devvp, 88b394cd1eSFedor Uporov fsbtodb(fs, ((uint64_t)ex->ei_leaf_hi << 32 | ex->ei_leaf_lo)), 89b394cd1eSFedor Uporov (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 90b394cd1eSFedor Uporov brelse(bp); 91b394cd1eSFedor Uporov return; 9278f6ea54SPedro F. Giffuni } 93b394cd1eSFedor Uporov 94b394cd1eSFedor Uporov ext4_ext_print_header(ip, (struct ext4_extent_header *)bp->b_data); 95b394cd1eSFedor Uporov 96b394cd1eSFedor Uporov brelse(bp); 97b394cd1eSFedor Uporov 98d7511a40SPedro F. Giffuni } 99d7511a40SPedro F. Giffuni 100d7511a40SPedro F. Giffuni static void 101b394cd1eSFedor Uporov ext4_ext_print_header(struct inode *ip, struct ext4_extent_header *ehp) 102d7511a40SPedro F. Giffuni { 103b394cd1eSFedor Uporov int i; 104d7511a40SPedro F. Giffuni 105b394cd1eSFedor Uporov printf("header %p => (magic 0x%x entries %d max %d depth %d gen %d)\n", 106b394cd1eSFedor Uporov ehp, ehp->eh_magic, ehp->eh_ecount, ehp->eh_max, ehp->eh_depth, 107b394cd1eSFedor Uporov ehp->eh_gen); 108b394cd1eSFedor Uporov 109b394cd1eSFedor Uporov for (i = 0; i < ehp->eh_ecount; i++) 110b394cd1eSFedor Uporov if (ehp->eh_depth != 0) 111b394cd1eSFedor Uporov ext4_ext_print_index(ip, 112b394cd1eSFedor Uporov (struct ext4_extent_index *)(ehp + 1 + i), 1); 113b394cd1eSFedor Uporov else 114b394cd1eSFedor Uporov ext4_ext_print_extent((struct ext4_extent *)(ehp + 1 + i)); 115b394cd1eSFedor Uporov } 116b394cd1eSFedor Uporov 117b394cd1eSFedor Uporov static void 118b394cd1eSFedor Uporov ext4_ext_print_path(struct inode *ip, struct ext4_extent_path *path) 119b394cd1eSFedor Uporov { 120b394cd1eSFedor Uporov int k, l; 121b394cd1eSFedor Uporov 122e06e5241SFedor Uporov l = path->ep_depth; 123b394cd1eSFedor Uporov 124e06e5241SFedor Uporov printf("ip=%ju, Path:\n", ip->i_number); 125b394cd1eSFedor Uporov for (k = 0; k <= l; k++, path++) { 126b394cd1eSFedor Uporov if (path->ep_index) { 127b394cd1eSFedor Uporov ext4_ext_print_index(ip, path->ep_index, 0); 128b394cd1eSFedor Uporov } else if (path->ep_ext) { 129b394cd1eSFedor Uporov ext4_ext_print_extent(path->ep_ext); 130b394cd1eSFedor Uporov } 131b394cd1eSFedor Uporov } 132b394cd1eSFedor Uporov } 133b394cd1eSFedor Uporov 134b394cd1eSFedor Uporov void 135b394cd1eSFedor Uporov ext4_ext_print_extent_tree_status(struct inode *ip) 136b394cd1eSFedor Uporov { 137b394cd1eSFedor Uporov struct ext4_extent_header *ehp; 138b394cd1eSFedor Uporov 139b394cd1eSFedor Uporov ehp = (struct ext4_extent_header *)(char *)ip->i_db; 140b394cd1eSFedor Uporov 141e06e5241SFedor Uporov printf("Extent status:ip=%ju\n", ip->i_number); 142b394cd1eSFedor Uporov if (!(ip->i_flag & IN_E4EXTENTS)) 143d7511a40SPedro F. Giffuni return; 144d7511a40SPedro F. Giffuni 145b394cd1eSFedor Uporov ext4_ext_print_header(ip, ehp); 146d7511a40SPedro F. Giffuni 147e813d9d7SPedro F. Giffuni return; 148e813d9d7SPedro F. Giffuni } 149b394cd1eSFedor Uporov #endif 150b394cd1eSFedor Uporov 151b394cd1eSFedor Uporov static inline struct ext4_extent_header * 152b394cd1eSFedor Uporov ext4_ext_inode_header(struct inode *ip) 153b394cd1eSFedor Uporov { 154b394cd1eSFedor Uporov 155b394cd1eSFedor Uporov return ((struct ext4_extent_header *)ip->i_db); 156d7511a40SPedro F. Giffuni } 157d7511a40SPedro F. Giffuni 158b394cd1eSFedor Uporov static inline struct ext4_extent_header * 159b394cd1eSFedor Uporov ext4_ext_block_header(char *bdata) 160b394cd1eSFedor Uporov { 161b394cd1eSFedor Uporov 162b394cd1eSFedor Uporov return ((struct ext4_extent_header *)bdata); 163b394cd1eSFedor Uporov } 164b394cd1eSFedor Uporov 165b394cd1eSFedor Uporov static inline unsigned short 166b394cd1eSFedor Uporov ext4_ext_inode_depth(struct inode *ip) 167b394cd1eSFedor Uporov { 168b394cd1eSFedor Uporov struct ext4_extent_header *ehp; 169b394cd1eSFedor Uporov 170b394cd1eSFedor Uporov ehp = (struct ext4_extent_header *)ip->i_data; 171b394cd1eSFedor Uporov return (ehp->eh_depth); 172b394cd1eSFedor Uporov } 173b394cd1eSFedor Uporov 174b394cd1eSFedor Uporov static inline e4fs_daddr_t 175b394cd1eSFedor Uporov ext4_ext_index_pblock(struct ext4_extent_index *index) 176b394cd1eSFedor Uporov { 177b394cd1eSFedor Uporov e4fs_daddr_t blk; 178b394cd1eSFedor Uporov 179b394cd1eSFedor Uporov blk = index->ei_leaf_lo; 180b394cd1eSFedor Uporov blk |= (e4fs_daddr_t)index->ei_leaf_hi << 32; 181b394cd1eSFedor Uporov 182b394cd1eSFedor Uporov return (blk); 183b394cd1eSFedor Uporov } 184b394cd1eSFedor Uporov 185b394cd1eSFedor Uporov static inline void 186b394cd1eSFedor Uporov ext4_index_store_pblock(struct ext4_extent_index *index, e4fs_daddr_t pb) 187b394cd1eSFedor Uporov { 188b394cd1eSFedor Uporov 189b394cd1eSFedor Uporov index->ei_leaf_lo = pb & 0xffffffff; 190b394cd1eSFedor Uporov index->ei_leaf_hi = (pb >> 32) & 0xffff; 191b394cd1eSFedor Uporov } 192b394cd1eSFedor Uporov 193b394cd1eSFedor Uporov 194b394cd1eSFedor Uporov static inline e4fs_daddr_t 195b394cd1eSFedor Uporov ext4_ext_extent_pblock(struct ext4_extent *extent) 196b394cd1eSFedor Uporov { 197b394cd1eSFedor Uporov e4fs_daddr_t blk; 198b394cd1eSFedor Uporov 199b394cd1eSFedor Uporov blk = extent->e_start_lo; 200b394cd1eSFedor Uporov blk |= (e4fs_daddr_t)extent->e_start_hi << 32; 201b394cd1eSFedor Uporov 202b394cd1eSFedor Uporov return (blk); 203b394cd1eSFedor Uporov } 204b394cd1eSFedor Uporov 205b394cd1eSFedor Uporov static inline void 206b394cd1eSFedor Uporov ext4_ext_store_pblock(struct ext4_extent *ex, e4fs_daddr_t pb) 207b394cd1eSFedor Uporov { 208b394cd1eSFedor Uporov 209b394cd1eSFedor Uporov ex->e_start_lo = pb & 0xffffffff; 210b394cd1eSFedor Uporov ex->e_start_hi = (pb >> 32) & 0xffff; 211b394cd1eSFedor Uporov } 212b394cd1eSFedor Uporov 213d7511a40SPedro F. Giffuni int 214d7511a40SPedro F. Giffuni ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep) 215d7511a40SPedro F. Giffuni { 216d7511a40SPedro F. Giffuni struct ext4_extent_cache *ecp; 217d7511a40SPedro F. Giffuni int ret = EXT4_EXT_CACHE_NO; 218d7511a40SPedro F. Giffuni 219d7511a40SPedro F. Giffuni ecp = &ip->i_ext_cache; 220d7511a40SPedro F. Giffuni if (ecp->ec_type == EXT4_EXT_CACHE_NO) 221d7511a40SPedro F. Giffuni return (ret); 222d7511a40SPedro F. Giffuni 223d7511a40SPedro F. Giffuni if (lbn >= ecp->ec_blk && lbn < ecp->ec_blk + ecp->ec_len) { 224d7511a40SPedro F. Giffuni ep->e_blk = ecp->ec_blk; 225d7511a40SPedro F. Giffuni ep->e_start_lo = ecp->ec_start & 0xffffffff; 226d7511a40SPedro F. Giffuni ep->e_start_hi = ecp->ec_start >> 32 & 0xffff; 227d7511a40SPedro F. Giffuni ep->e_len = ecp->ec_len; 228d7511a40SPedro F. Giffuni ret = ecp->ec_type; 229d7511a40SPedro F. Giffuni } 230d7511a40SPedro F. Giffuni return (ret); 231d7511a40SPedro F. Giffuni } 232d7511a40SPedro F. Giffuni 233b394cd1eSFedor Uporov static int 234b394cd1eSFedor Uporov ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh) 235d7511a40SPedro F. Giffuni { 236b394cd1eSFedor Uporov struct m_ext2fs *fs; 237b394cd1eSFedor Uporov char *error_msg; 238d7511a40SPedro F. Giffuni 239b394cd1eSFedor Uporov fs = ip->i_e2fs; 240b394cd1eSFedor Uporov 241b394cd1eSFedor Uporov if (eh->eh_magic != EXT4_EXT_MAGIC) { 242*84b89556SFedor Uporov error_msg = "header: invalid magic"; 243b394cd1eSFedor Uporov goto corrupted; 244b394cd1eSFedor Uporov } 245b394cd1eSFedor Uporov if (eh->eh_max == 0) { 246*84b89556SFedor Uporov error_msg = "header: invalid eh_max"; 247b394cd1eSFedor Uporov goto corrupted; 248b394cd1eSFedor Uporov } 249b394cd1eSFedor Uporov if (eh->eh_ecount > eh->eh_max) { 250*84b89556SFedor Uporov error_msg = "header: invalid eh_entries"; 251b394cd1eSFedor Uporov goto corrupted; 252d7511a40SPedro F. Giffuni } 253d7511a40SPedro F. Giffuni 254b394cd1eSFedor Uporov return (0); 255b394cd1eSFedor Uporov 256b394cd1eSFedor Uporov corrupted: 257*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, error_msg); 258b394cd1eSFedor Uporov return (EIO); 259b394cd1eSFedor Uporov } 260b394cd1eSFedor Uporov 261b394cd1eSFedor Uporov static void 262b394cd1eSFedor Uporov ext4_ext_binsearch_index(struct ext4_extent_path *path, int blk) 263b394cd1eSFedor Uporov { 264b394cd1eSFedor Uporov struct ext4_extent_header *eh; 265b394cd1eSFedor Uporov struct ext4_extent_index *r, *l, *m; 266b394cd1eSFedor Uporov 267b394cd1eSFedor Uporov eh = path->ep_header; 268b394cd1eSFedor Uporov 269b394cd1eSFedor Uporov KASSERT(eh->eh_ecount <= eh->eh_max && eh->eh_ecount > 0, 270b394cd1eSFedor Uporov ("ext4_ext_binsearch_index: bad args")); 271b394cd1eSFedor Uporov 272b394cd1eSFedor Uporov l = EXT_FIRST_INDEX(eh) + 1; 273b394cd1eSFedor Uporov r = EXT_FIRST_INDEX(eh) + eh->eh_ecount - 1; 274b394cd1eSFedor Uporov while (l <= r) { 275b394cd1eSFedor Uporov m = l + (r - l) / 2; 276b394cd1eSFedor Uporov if (blk < m->ei_blk) 277b394cd1eSFedor Uporov r = m - 1; 278b394cd1eSFedor Uporov else 279b394cd1eSFedor Uporov l = m + 1; 280b394cd1eSFedor Uporov } 281b394cd1eSFedor Uporov 282b394cd1eSFedor Uporov path->ep_index = l - 1; 283b394cd1eSFedor Uporov } 284b394cd1eSFedor Uporov 285b394cd1eSFedor Uporov static void 286b394cd1eSFedor Uporov ext4_ext_binsearch_ext(struct ext4_extent_path *path, int blk) 287b394cd1eSFedor Uporov { 288b394cd1eSFedor Uporov struct ext4_extent_header *eh; 289b394cd1eSFedor Uporov struct ext4_extent *r, *l, *m; 290b394cd1eSFedor Uporov 291b394cd1eSFedor Uporov eh = path->ep_header; 292b394cd1eSFedor Uporov 293b394cd1eSFedor Uporov KASSERT(eh->eh_ecount <= eh->eh_max, 294b394cd1eSFedor Uporov ("ext4_ext_binsearch_ext: bad args")); 295b394cd1eSFedor Uporov 296b394cd1eSFedor Uporov if (eh->eh_ecount == 0) 297b394cd1eSFedor Uporov return; 298b394cd1eSFedor Uporov 299b394cd1eSFedor Uporov l = EXT_FIRST_EXTENT(eh) + 1; 300b394cd1eSFedor Uporov r = EXT_FIRST_EXTENT(eh) + eh->eh_ecount - 1; 301b394cd1eSFedor Uporov 302b394cd1eSFedor Uporov while (l <= r) { 303b394cd1eSFedor Uporov m = l + (r - l) / 2; 304b394cd1eSFedor Uporov if (blk < m->e_blk) 305b394cd1eSFedor Uporov r = m - 1; 306b394cd1eSFedor Uporov else 307b394cd1eSFedor Uporov l = m + 1; 308b394cd1eSFedor Uporov } 309b394cd1eSFedor Uporov 310b394cd1eSFedor Uporov path->ep_ext = l - 1; 311b394cd1eSFedor Uporov } 312b394cd1eSFedor Uporov 313b394cd1eSFedor Uporov static int 314b394cd1eSFedor Uporov ext4_ext_fill_path_bdata(struct ext4_extent_path *path, 315b394cd1eSFedor Uporov struct buf *bp, uint64_t blk) 316b394cd1eSFedor Uporov { 317b394cd1eSFedor Uporov 318b394cd1eSFedor Uporov KASSERT(path->ep_data == NULL, 319b394cd1eSFedor Uporov ("ext4_ext_fill_path_bdata: bad ep_data")); 320b394cd1eSFedor Uporov 321b394cd1eSFedor Uporov path->ep_data = malloc(bp->b_bufsize, M_EXT2EXTENTS, M_WAITOK); 322b394cd1eSFedor Uporov if (!path->ep_data) 323b394cd1eSFedor Uporov return (ENOMEM); 324b394cd1eSFedor Uporov 325b394cd1eSFedor Uporov memcpy(path->ep_data, bp->b_data, bp->b_bufsize); 326b394cd1eSFedor Uporov path->ep_blk = blk; 327b394cd1eSFedor Uporov 328b394cd1eSFedor Uporov return (0); 329b394cd1eSFedor Uporov } 330b394cd1eSFedor Uporov 331b394cd1eSFedor Uporov static void 332b394cd1eSFedor Uporov ext4_ext_fill_path_buf(struct ext4_extent_path *path, struct buf *bp) 333b394cd1eSFedor Uporov { 334b394cd1eSFedor Uporov 335b394cd1eSFedor Uporov KASSERT(path->ep_data != NULL, 336b394cd1eSFedor Uporov ("ext4_ext_fill_path_buf: bad ep_data")); 337b394cd1eSFedor Uporov 338b394cd1eSFedor Uporov memcpy(bp->b_data, path->ep_data, bp->b_bufsize); 339b394cd1eSFedor Uporov } 340b394cd1eSFedor Uporov 341b394cd1eSFedor Uporov static void 342b394cd1eSFedor Uporov ext4_ext_drop_refs(struct ext4_extent_path *path) 343b394cd1eSFedor Uporov { 344b394cd1eSFedor Uporov int depth, i; 345b394cd1eSFedor Uporov 346b394cd1eSFedor Uporov if (!path) 347b394cd1eSFedor Uporov return; 348b394cd1eSFedor Uporov 349b394cd1eSFedor Uporov depth = path->ep_depth; 350b394cd1eSFedor Uporov for (i = 0; i <= depth; i++, path++) 351b394cd1eSFedor Uporov if (path->ep_data) { 352b394cd1eSFedor Uporov free(path->ep_data, M_EXT2EXTENTS); 353b394cd1eSFedor Uporov path->ep_data = NULL; 354b394cd1eSFedor Uporov } 355b394cd1eSFedor Uporov } 356b394cd1eSFedor Uporov 357b394cd1eSFedor Uporov void 358b394cd1eSFedor Uporov ext4_ext_path_free(struct ext4_extent_path *path) 359b394cd1eSFedor Uporov { 360b394cd1eSFedor Uporov 361b394cd1eSFedor Uporov if (!path) 362b394cd1eSFedor Uporov return; 363b394cd1eSFedor Uporov 364b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 365b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 366b394cd1eSFedor Uporov } 367b394cd1eSFedor Uporov 368b394cd1eSFedor Uporov int 369b394cd1eSFedor Uporov ext4_ext_find_extent(struct inode *ip, daddr_t block, 370b394cd1eSFedor Uporov struct ext4_extent_path **ppath) 371b394cd1eSFedor Uporov { 372b394cd1eSFedor Uporov struct m_ext2fs *fs; 373b394cd1eSFedor Uporov struct ext4_extent_header *eh; 374b394cd1eSFedor Uporov struct ext4_extent_path *path; 375b394cd1eSFedor Uporov struct buf *bp; 376b394cd1eSFedor Uporov uint64_t blk; 377b394cd1eSFedor Uporov int error, depth, i, ppos, alloc; 378b394cd1eSFedor Uporov 379b394cd1eSFedor Uporov fs = ip->i_e2fs; 380b394cd1eSFedor Uporov eh = ext4_ext_inode_header(ip); 381b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 382b394cd1eSFedor Uporov ppos = 0; 383b394cd1eSFedor Uporov alloc = 0; 384b394cd1eSFedor Uporov 385b394cd1eSFedor Uporov error = ext4_ext_check_header(ip, eh); 386b394cd1eSFedor Uporov if (error) 387b394cd1eSFedor Uporov return (error); 388b394cd1eSFedor Uporov 3893fcbb8c0SFedor Uporov if (ppath == NULL) 390b394cd1eSFedor Uporov return (EINVAL); 391b394cd1eSFedor Uporov 392b394cd1eSFedor Uporov path = *ppath; 3933fcbb8c0SFedor Uporov if (path == NULL) { 394b394cd1eSFedor Uporov path = malloc(EXT4_EXT_DEPTH_MAX * 395b394cd1eSFedor Uporov sizeof(struct ext4_extent_path), 396b394cd1eSFedor Uporov M_EXT2EXTENTS, M_WAITOK | M_ZERO); 397b394cd1eSFedor Uporov if (!path) 398b394cd1eSFedor Uporov return (ENOMEM); 399b394cd1eSFedor Uporov 400b394cd1eSFedor Uporov *ppath = path; 401b394cd1eSFedor Uporov alloc = 1; 402b394cd1eSFedor Uporov } 403b394cd1eSFedor Uporov 404b394cd1eSFedor Uporov path[0].ep_header = eh; 405b394cd1eSFedor Uporov path[0].ep_data = NULL; 406b394cd1eSFedor Uporov 407b394cd1eSFedor Uporov /* Walk through the tree. */ 408b394cd1eSFedor Uporov i = depth; 409b394cd1eSFedor Uporov while (i) { 410b394cd1eSFedor Uporov ext4_ext_binsearch_index(&path[ppos], block); 411b394cd1eSFedor Uporov blk = ext4_ext_index_pblock(path[ppos].ep_index); 412b394cd1eSFedor Uporov path[ppos].ep_depth = i; 413b394cd1eSFedor Uporov path[ppos].ep_ext = NULL; 414b394cd1eSFedor Uporov 415b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(ip->i_e2fs, blk), 416b394cd1eSFedor Uporov ip->i_e2fs->e2fs_bsize, NOCRED, &bp); 417b394cd1eSFedor Uporov if (error) { 418b394cd1eSFedor Uporov brelse(bp); 419b394cd1eSFedor Uporov goto error; 420b394cd1eSFedor Uporov } 421b394cd1eSFedor Uporov 422b394cd1eSFedor Uporov ppos++; 423b394cd1eSFedor Uporov if (ppos > depth) { 424*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 425b394cd1eSFedor Uporov "ppos > depth => extent corrupted"); 426b394cd1eSFedor Uporov error = EIO; 427b394cd1eSFedor Uporov brelse(bp); 428b394cd1eSFedor Uporov goto error; 429b394cd1eSFedor Uporov } 430b394cd1eSFedor Uporov 431b394cd1eSFedor Uporov ext4_ext_fill_path_bdata(&path[ppos], bp, blk); 432f0a993bbSFedor Uporov bqrelse(bp); 433b394cd1eSFedor Uporov 434b394cd1eSFedor Uporov eh = ext4_ext_block_header(path[ppos].ep_data); 435512f29d1SFedor Uporov if (ext4_ext_check_header(ip, eh) || 436512f29d1SFedor Uporov ext2_extent_blk_csum_verify(ip, path[ppos].ep_data)) { 437512f29d1SFedor Uporov error = EIO; 438b394cd1eSFedor Uporov goto error; 439512f29d1SFedor Uporov } 440b394cd1eSFedor Uporov 441b394cd1eSFedor Uporov path[ppos].ep_header = eh; 442b394cd1eSFedor Uporov 443b394cd1eSFedor Uporov i--; 444b394cd1eSFedor Uporov } 445b394cd1eSFedor Uporov 446b394cd1eSFedor Uporov error = ext4_ext_check_header(ip, eh); 447b394cd1eSFedor Uporov if (error) 448b394cd1eSFedor Uporov goto error; 449b394cd1eSFedor Uporov 450b394cd1eSFedor Uporov /* Find extent. */ 451b394cd1eSFedor Uporov path[ppos].ep_depth = i; 452b394cd1eSFedor Uporov path[ppos].ep_header = eh; 453b394cd1eSFedor Uporov path[ppos].ep_ext = NULL; 454b394cd1eSFedor Uporov path[ppos].ep_index = NULL; 455b394cd1eSFedor Uporov ext4_ext_binsearch_ext(&path[ppos], block); 456b394cd1eSFedor Uporov return (0); 457b394cd1eSFedor Uporov 458b394cd1eSFedor Uporov error: 459b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 460b394cd1eSFedor Uporov if (alloc) 461b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 462b394cd1eSFedor Uporov 463b394cd1eSFedor Uporov *ppath = NULL; 464b394cd1eSFedor Uporov 465b394cd1eSFedor Uporov return (error); 466b394cd1eSFedor Uporov } 467b394cd1eSFedor Uporov 468b394cd1eSFedor Uporov static inline int 469b394cd1eSFedor Uporov ext4_ext_space_root(struct inode *ip) 470b394cd1eSFedor Uporov { 471b394cd1eSFedor Uporov int size; 472b394cd1eSFedor Uporov 473b394cd1eSFedor Uporov size = sizeof(ip->i_data); 474b394cd1eSFedor Uporov size -= sizeof(struct ext4_extent_header); 475b394cd1eSFedor Uporov size /= sizeof(struct ext4_extent); 476b394cd1eSFedor Uporov 477b394cd1eSFedor Uporov return (size); 478b394cd1eSFedor Uporov } 479b394cd1eSFedor Uporov 480b394cd1eSFedor Uporov static inline int 481b394cd1eSFedor Uporov ext4_ext_space_block(struct inode *ip) 482b394cd1eSFedor Uporov { 483b394cd1eSFedor Uporov struct m_ext2fs *fs; 484b394cd1eSFedor Uporov int size; 485b394cd1eSFedor Uporov 486b394cd1eSFedor Uporov fs = ip->i_e2fs; 487b394cd1eSFedor Uporov 488b394cd1eSFedor Uporov size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / 489b394cd1eSFedor Uporov sizeof(struct ext4_extent); 490b394cd1eSFedor Uporov 491b394cd1eSFedor Uporov return (size); 492b394cd1eSFedor Uporov } 493b394cd1eSFedor Uporov 494b394cd1eSFedor Uporov static inline int 495b394cd1eSFedor Uporov ext4_ext_space_block_index(struct inode *ip) 496b394cd1eSFedor Uporov { 497b394cd1eSFedor Uporov struct m_ext2fs *fs; 498b394cd1eSFedor Uporov int size; 499b394cd1eSFedor Uporov 500b394cd1eSFedor Uporov fs = ip->i_e2fs; 501b394cd1eSFedor Uporov 502b394cd1eSFedor Uporov size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / 503b394cd1eSFedor Uporov sizeof(struct ext4_extent_index); 504b394cd1eSFedor Uporov 505b394cd1eSFedor Uporov return (size); 506b394cd1eSFedor Uporov } 507b394cd1eSFedor Uporov 508b394cd1eSFedor Uporov void 509b394cd1eSFedor Uporov ext4_ext_tree_init(struct inode *ip) 510d7511a40SPedro F. Giffuni { 511d7511a40SPedro F. Giffuni struct ext4_extent_header *ehp; 512d7511a40SPedro F. Giffuni 513b394cd1eSFedor Uporov ip->i_flag |= IN_E4EXTENTS; 514d7511a40SPedro F. Giffuni 515b394cd1eSFedor Uporov memset(ip->i_data, 0, EXT2_NDADDR + EXT2_NIADDR); 516b394cd1eSFedor Uporov ehp = (struct ext4_extent_header *)ip->i_data; 517b394cd1eSFedor Uporov ehp->eh_magic = EXT4_EXT_MAGIC; 518b394cd1eSFedor Uporov ehp->eh_max = ext4_ext_space_root(ip); 519b394cd1eSFedor Uporov ip->i_ext_cache.ec_type = EXT4_EXT_CACHE_NO; 520b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 521b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 52278f6ea54SPedro F. Giffuni } 523d7511a40SPedro F. Giffuni 524b394cd1eSFedor Uporov static inline void 525b394cd1eSFedor Uporov ext4_ext_put_in_cache(struct inode *ip, uint32_t blk, 526b394cd1eSFedor Uporov uint32_t len, uint32_t start, int type) 527b394cd1eSFedor Uporov { 528b394cd1eSFedor Uporov 529b394cd1eSFedor Uporov KASSERT(len != 0, ("ext4_ext_put_in_cache: bad input")); 530b394cd1eSFedor Uporov 531b394cd1eSFedor Uporov ip->i_ext_cache.ec_type = type; 532b394cd1eSFedor Uporov ip->i_ext_cache.ec_blk = blk; 533b394cd1eSFedor Uporov ip->i_ext_cache.ec_len = len; 534b394cd1eSFedor Uporov ip->i_ext_cache.ec_start = start; 535d7511a40SPedro F. Giffuni } 536b394cd1eSFedor Uporov 537b394cd1eSFedor Uporov static e4fs_daddr_t 538b394cd1eSFedor Uporov ext4_ext_blkpref(struct inode *ip, struct ext4_extent_path *path, 539b394cd1eSFedor Uporov e4fs_daddr_t block) 540b394cd1eSFedor Uporov { 541b394cd1eSFedor Uporov struct m_ext2fs *fs; 542b394cd1eSFedor Uporov struct ext4_extent *ex; 543b394cd1eSFedor Uporov e4fs_daddr_t bg_start; 544b394cd1eSFedor Uporov int depth; 545b394cd1eSFedor Uporov 546b394cd1eSFedor Uporov fs = ip->i_e2fs; 547b394cd1eSFedor Uporov 548b394cd1eSFedor Uporov if (path) { 549b394cd1eSFedor Uporov depth = path->ep_depth; 550b394cd1eSFedor Uporov ex = path[depth].ep_ext; 551b394cd1eSFedor Uporov if (ex) { 552b394cd1eSFedor Uporov e4fs_daddr_t pblk = ext4_ext_extent_pblock(ex); 553b394cd1eSFedor Uporov e2fs_daddr_t blk = ex->e_blk; 554b394cd1eSFedor Uporov 555b394cd1eSFedor Uporov if (block > blk) 556b394cd1eSFedor Uporov return (pblk + (block - blk)); 557b394cd1eSFedor Uporov else 558b394cd1eSFedor Uporov return (pblk - (blk - block)); 559b394cd1eSFedor Uporov } 560b394cd1eSFedor Uporov 561b394cd1eSFedor Uporov /* Try to get block from index itself. */ 562b394cd1eSFedor Uporov if (path[depth].ep_data) 563b394cd1eSFedor Uporov return (path[depth].ep_blk); 564b394cd1eSFedor Uporov } 565b394cd1eSFedor Uporov 566b394cd1eSFedor Uporov /* Use inode's group. */ 567b394cd1eSFedor Uporov bg_start = (ip->i_block_group * EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) + 568b394cd1eSFedor Uporov fs->e2fs->e2fs_first_dblock; 569b394cd1eSFedor Uporov 570b394cd1eSFedor Uporov return (bg_start + block); 571b394cd1eSFedor Uporov } 572b394cd1eSFedor Uporov 573b394cd1eSFedor Uporov static int inline 574b394cd1eSFedor Uporov ext4_can_extents_be_merged(struct ext4_extent *ex1, 575b394cd1eSFedor Uporov struct ext4_extent *ex2) 576b394cd1eSFedor Uporov { 577b394cd1eSFedor Uporov 578b394cd1eSFedor Uporov if (ex1->e_blk + ex1->e_len != ex2->e_blk) 579b394cd1eSFedor Uporov return (0); 580b394cd1eSFedor Uporov 581b394cd1eSFedor Uporov if (ex1->e_len + ex2->e_len > EXT4_MAX_LEN) 582b394cd1eSFedor Uporov return (0); 583b394cd1eSFedor Uporov 584b394cd1eSFedor Uporov if (ext4_ext_extent_pblock(ex1) + ex1->e_len == 585b394cd1eSFedor Uporov ext4_ext_extent_pblock(ex2)) 586b394cd1eSFedor Uporov return (1); 587b394cd1eSFedor Uporov 588b394cd1eSFedor Uporov return (0); 589b394cd1eSFedor Uporov } 590b394cd1eSFedor Uporov 591b394cd1eSFedor Uporov static unsigned 592b394cd1eSFedor Uporov ext4_ext_next_leaf_block(struct inode *ip, struct ext4_extent_path *path) 593b394cd1eSFedor Uporov { 594b394cd1eSFedor Uporov int depth = path->ep_depth; 595b394cd1eSFedor Uporov 596b394cd1eSFedor Uporov /* Empty tree */ 597b394cd1eSFedor Uporov if (depth == 0) 598b394cd1eSFedor Uporov return (EXT4_MAX_BLOCKS); 599b394cd1eSFedor Uporov 600b394cd1eSFedor Uporov /* Go to indexes. */ 601b394cd1eSFedor Uporov depth--; 602b394cd1eSFedor Uporov 603b394cd1eSFedor Uporov while (depth >= 0) { 604b394cd1eSFedor Uporov if (path[depth].ep_index != 605b394cd1eSFedor Uporov EXT_LAST_INDEX(path[depth].ep_header)) 606b394cd1eSFedor Uporov return (path[depth].ep_index[1].ei_blk); 607b394cd1eSFedor Uporov 608b394cd1eSFedor Uporov depth--; 609b394cd1eSFedor Uporov } 610b394cd1eSFedor Uporov 611b394cd1eSFedor Uporov return (EXT4_MAX_BLOCKS); 612b394cd1eSFedor Uporov } 613b394cd1eSFedor Uporov 614b394cd1eSFedor Uporov static int 615b394cd1eSFedor Uporov ext4_ext_dirty(struct inode *ip, struct ext4_extent_path *path) 616b394cd1eSFedor Uporov { 617b394cd1eSFedor Uporov struct m_ext2fs *fs; 618b394cd1eSFedor Uporov struct buf *bp; 619b394cd1eSFedor Uporov uint64_t blk; 620b394cd1eSFedor Uporov int error; 621b394cd1eSFedor Uporov 622b394cd1eSFedor Uporov fs = ip->i_e2fs; 623b394cd1eSFedor Uporov 624b394cd1eSFedor Uporov if (!path) 625b394cd1eSFedor Uporov return (EINVAL); 626b394cd1eSFedor Uporov 627b394cd1eSFedor Uporov if (path->ep_data) { 628b394cd1eSFedor Uporov blk = path->ep_blk; 629b394cd1eSFedor Uporov bp = getblk(ip->i_devvp, fsbtodb(fs, blk), 630b394cd1eSFedor Uporov fs->e2fs_bsize, 0, 0, 0); 631b394cd1eSFedor Uporov if (!bp) 632b394cd1eSFedor Uporov return (EIO); 633b394cd1eSFedor Uporov ext4_ext_fill_path_buf(path, bp); 634512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 635b394cd1eSFedor Uporov error = bwrite(bp); 636b394cd1eSFedor Uporov } else { 637b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 638b394cd1eSFedor Uporov error = ext2_update(ip->i_vnode, 1); 639b394cd1eSFedor Uporov } 640b394cd1eSFedor Uporov 641b394cd1eSFedor Uporov return (error); 642b394cd1eSFedor Uporov } 643b394cd1eSFedor Uporov 644b394cd1eSFedor Uporov static int 645b394cd1eSFedor Uporov ext4_ext_insert_index(struct inode *ip, struct ext4_extent_path *path, 646b394cd1eSFedor Uporov uint32_t lblk, e4fs_daddr_t blk) 647b394cd1eSFedor Uporov { 648b394cd1eSFedor Uporov struct m_ext2fs *fs; 649b394cd1eSFedor Uporov struct ext4_extent_index *idx; 650b394cd1eSFedor Uporov int len; 651b394cd1eSFedor Uporov 652b394cd1eSFedor Uporov fs = ip->i_e2fs; 653b394cd1eSFedor Uporov 654b394cd1eSFedor Uporov if (lblk == path->ep_index->ei_blk) { 655*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 656b394cd1eSFedor Uporov "lblk == index blk => extent corrupted"); 657b394cd1eSFedor Uporov return (EIO); 658b394cd1eSFedor Uporov } 659b394cd1eSFedor Uporov 660b394cd1eSFedor Uporov if (path->ep_header->eh_ecount >= path->ep_header->eh_max) { 661*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 662b394cd1eSFedor Uporov "ecout > maxcount => extent corrupted"); 663b394cd1eSFedor Uporov return (EIO); 664b394cd1eSFedor Uporov } 665b394cd1eSFedor Uporov 666b394cd1eSFedor Uporov if (lblk > path->ep_index->ei_blk) { 667b394cd1eSFedor Uporov /* Insert after. */ 668b394cd1eSFedor Uporov idx = path->ep_index + 1; 669b394cd1eSFedor Uporov } else { 670b394cd1eSFedor Uporov /* Insert before. */ 671b394cd1eSFedor Uporov idx = path->ep_index; 672b394cd1eSFedor Uporov } 673b394cd1eSFedor Uporov 674b394cd1eSFedor Uporov len = EXT_LAST_INDEX(path->ep_header) - idx + 1; 675b394cd1eSFedor Uporov if (len > 0) 676b394cd1eSFedor Uporov memmove(idx + 1, idx, len * sizeof(struct ext4_extent_index)); 677b394cd1eSFedor Uporov 678b394cd1eSFedor Uporov if (idx > EXT_MAX_INDEX(path->ep_header)) { 679*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 680b394cd1eSFedor Uporov "index is out of range => extent corrupted"); 681b394cd1eSFedor Uporov return (EIO); 682b394cd1eSFedor Uporov } 683b394cd1eSFedor Uporov 684b394cd1eSFedor Uporov idx->ei_blk = lblk; 685b394cd1eSFedor Uporov ext4_index_store_pblock(idx, blk); 686b394cd1eSFedor Uporov path->ep_header->eh_ecount++; 687b394cd1eSFedor Uporov 688b394cd1eSFedor Uporov return (ext4_ext_dirty(ip, path)); 689b394cd1eSFedor Uporov } 690b394cd1eSFedor Uporov 691b394cd1eSFedor Uporov static e4fs_daddr_t 692b394cd1eSFedor Uporov ext4_ext_alloc_meta(struct inode *ip) 693b394cd1eSFedor Uporov { 694b394cd1eSFedor Uporov e4fs_daddr_t blk = ext2_alloc_meta(ip); 695b394cd1eSFedor Uporov if (blk) { 696b394cd1eSFedor Uporov ip->i_blocks += btodb(ip->i_e2fs->e2fs_bsize); 697b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 698b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 699b394cd1eSFedor Uporov } 700b394cd1eSFedor Uporov 701b394cd1eSFedor Uporov return (blk); 702b394cd1eSFedor Uporov } 703b394cd1eSFedor Uporov 704b394cd1eSFedor Uporov static void 705b394cd1eSFedor Uporov ext4_ext_blkfree(struct inode *ip, uint64_t blk, int count, int flags) 706b394cd1eSFedor Uporov { 707b394cd1eSFedor Uporov struct m_ext2fs *fs; 708b394cd1eSFedor Uporov int i, blocksreleased; 709b394cd1eSFedor Uporov 710b394cd1eSFedor Uporov fs = ip->i_e2fs; 711b394cd1eSFedor Uporov blocksreleased = count; 712b394cd1eSFedor Uporov 713b394cd1eSFedor Uporov for(i = 0; i < count; i++) 714b394cd1eSFedor Uporov ext2_blkfree(ip, blk + i, fs->e2fs_bsize); 715b394cd1eSFedor Uporov 716b394cd1eSFedor Uporov if (ip->i_blocks >= blocksreleased) 717b394cd1eSFedor Uporov ip->i_blocks -= (btodb(fs->e2fs_bsize)*blocksreleased); 718b394cd1eSFedor Uporov else 719b394cd1eSFedor Uporov ip->i_blocks = 0; 720b394cd1eSFedor Uporov 721b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 722b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 723b394cd1eSFedor Uporov } 724b394cd1eSFedor Uporov 725b394cd1eSFedor Uporov static int 726b394cd1eSFedor Uporov ext4_ext_split(struct inode *ip, struct ext4_extent_path *path, 727b394cd1eSFedor Uporov struct ext4_extent *newext, int at) 728b394cd1eSFedor Uporov { 729b394cd1eSFedor Uporov struct m_ext2fs *fs; 730b394cd1eSFedor Uporov struct buf *bp; 731b394cd1eSFedor Uporov int depth = ext4_ext_inode_depth(ip); 732b394cd1eSFedor Uporov struct ext4_extent_header *neh; 733b394cd1eSFedor Uporov struct ext4_extent_index *fidx; 734b394cd1eSFedor Uporov struct ext4_extent *ex; 735b394cd1eSFedor Uporov int i = at, k, m, a; 736b394cd1eSFedor Uporov e4fs_daddr_t newblk, oldblk; 737b394cd1eSFedor Uporov uint32_t border; 738b394cd1eSFedor Uporov e4fs_daddr_t *ablks = NULL; 739b394cd1eSFedor Uporov int error = 0; 740b394cd1eSFedor Uporov 741b394cd1eSFedor Uporov fs = ip->i_e2fs; 742b394cd1eSFedor Uporov bp = NULL; 743b394cd1eSFedor Uporov 744b394cd1eSFedor Uporov /* 745b394cd1eSFedor Uporov * We will split at current extent for now. 746b394cd1eSFedor Uporov */ 747b394cd1eSFedor Uporov if (path[depth].ep_ext > EXT_MAX_EXTENT(path[depth].ep_header)) { 748*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 749b394cd1eSFedor Uporov "extent is out of range => extent corrupted"); 750b394cd1eSFedor Uporov return (EIO); 751b394cd1eSFedor Uporov } 752b394cd1eSFedor Uporov 753b394cd1eSFedor Uporov if (path[depth].ep_ext != EXT_MAX_EXTENT(path[depth].ep_header)) 754b394cd1eSFedor Uporov border = path[depth].ep_ext[1].e_blk; 755b394cd1eSFedor Uporov else 756b394cd1eSFedor Uporov border = newext->e_blk; 757b394cd1eSFedor Uporov 758b394cd1eSFedor Uporov /* Allocate new blocks. */ 759b394cd1eSFedor Uporov ablks = malloc(sizeof(e4fs_daddr_t) * depth, 760b394cd1eSFedor Uporov M_EXT2EXTENTS, M_WAITOK | M_ZERO); 761b394cd1eSFedor Uporov if (!ablks) 762b394cd1eSFedor Uporov return (ENOMEM); 763b394cd1eSFedor Uporov for (a = 0; a < depth - at; a++) { 764b394cd1eSFedor Uporov newblk = ext4_ext_alloc_meta(ip); 765b394cd1eSFedor Uporov if (newblk == 0) 766b394cd1eSFedor Uporov goto cleanup; 767b394cd1eSFedor Uporov ablks[a] = newblk; 768b394cd1eSFedor Uporov } 769b394cd1eSFedor Uporov 770b394cd1eSFedor Uporov newblk = ablks[--a]; 771b394cd1eSFedor Uporov bp = getblk(ip->i_devvp, fsbtodb(fs, newblk), fs->e2fs_bsize, 0, 0, 0); 772b394cd1eSFedor Uporov if (!bp) { 773b394cd1eSFedor Uporov error = EIO; 774b394cd1eSFedor Uporov goto cleanup; 775b394cd1eSFedor Uporov } 776b394cd1eSFedor Uporov 777b394cd1eSFedor Uporov neh = ext4_ext_block_header(bp->b_data); 778b394cd1eSFedor Uporov neh->eh_ecount = 0; 779b394cd1eSFedor Uporov neh->eh_max = ext4_ext_space_block(ip); 780b394cd1eSFedor Uporov neh->eh_magic = EXT4_EXT_MAGIC; 781b394cd1eSFedor Uporov neh->eh_depth = 0; 782b394cd1eSFedor Uporov ex = EXT_FIRST_EXTENT(neh); 783b394cd1eSFedor Uporov 784b394cd1eSFedor Uporov if (path[depth].ep_header->eh_ecount != path[depth].ep_header->eh_max) { 785*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 786b394cd1eSFedor Uporov "extents count out of range => extent corrupted"); 787b394cd1eSFedor Uporov error = EIO; 788b394cd1eSFedor Uporov goto cleanup; 789b394cd1eSFedor Uporov } 790b394cd1eSFedor Uporov 791b394cd1eSFedor Uporov /* Start copy from next extent. */ 792b394cd1eSFedor Uporov m = 0; 793b394cd1eSFedor Uporov path[depth].ep_ext++; 794b394cd1eSFedor Uporov while (path[depth].ep_ext <= EXT_MAX_EXTENT(path[depth].ep_header)) { 795b394cd1eSFedor Uporov path[depth].ep_ext++; 796b394cd1eSFedor Uporov m++; 797b394cd1eSFedor Uporov } 798b394cd1eSFedor Uporov if (m) { 799b394cd1eSFedor Uporov memmove(ex, path[depth].ep_ext - m, 800b394cd1eSFedor Uporov sizeof(struct ext4_extent) * m); 801b394cd1eSFedor Uporov neh->eh_ecount = neh->eh_ecount + m; 802b394cd1eSFedor Uporov } 803b394cd1eSFedor Uporov 804512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 805b394cd1eSFedor Uporov bwrite(bp); 806b394cd1eSFedor Uporov bp = NULL; 807b394cd1eSFedor Uporov 808b394cd1eSFedor Uporov /* Fix old leaf. */ 809b394cd1eSFedor Uporov if (m) { 810b394cd1eSFedor Uporov path[depth].ep_header->eh_ecount = 811b394cd1eSFedor Uporov path[depth].ep_header->eh_ecount - m; 812b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + depth); 813b394cd1eSFedor Uporov } 814b394cd1eSFedor Uporov 815b394cd1eSFedor Uporov /* Create intermediate indexes. */ 816b394cd1eSFedor Uporov k = depth - at - 1; 817b394cd1eSFedor Uporov KASSERT(k >= 0, ("ext4_ext_split: negative k")); 818b394cd1eSFedor Uporov 819b394cd1eSFedor Uporov /* Insert new index into current index block. */ 820b394cd1eSFedor Uporov i = depth - 1; 821b394cd1eSFedor Uporov while (k--) { 822b394cd1eSFedor Uporov oldblk = newblk; 823b394cd1eSFedor Uporov newblk = ablks[--a]; 824b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(fs, newblk), 825b394cd1eSFedor Uporov (int)fs->e2fs_bsize, NOCRED, &bp); 826d7511a40SPedro F. Giffuni if (error) { 827b394cd1eSFedor Uporov brelse(bp); 828b394cd1eSFedor Uporov goto cleanup; 829b394cd1eSFedor Uporov } 830b394cd1eSFedor Uporov 831b394cd1eSFedor Uporov neh = (struct ext4_extent_header *)bp->b_data; 832b394cd1eSFedor Uporov neh->eh_ecount = 1; 833b394cd1eSFedor Uporov neh->eh_magic = EXT4_EXT_MAGIC; 834b394cd1eSFedor Uporov neh->eh_max = ext4_ext_space_block_index(ip); 835b394cd1eSFedor Uporov neh->eh_depth = depth - i; 836b394cd1eSFedor Uporov fidx = EXT_FIRST_INDEX(neh); 837b394cd1eSFedor Uporov fidx->ei_blk = border; 838b394cd1eSFedor Uporov ext4_index_store_pblock(fidx, oldblk); 839b394cd1eSFedor Uporov 840b394cd1eSFedor Uporov m = 0; 841b394cd1eSFedor Uporov path[i].ep_index++; 842b394cd1eSFedor Uporov while (path[i].ep_index <= EXT_MAX_INDEX(path[i].ep_header)) { 843b394cd1eSFedor Uporov path[i].ep_index++; 844b394cd1eSFedor Uporov m++; 845b394cd1eSFedor Uporov } 846b394cd1eSFedor Uporov if (m) { 847b394cd1eSFedor Uporov memmove(++fidx, path[i].ep_index - m, 848b394cd1eSFedor Uporov sizeof(struct ext4_extent_index) * m); 849b394cd1eSFedor Uporov neh->eh_ecount = neh->eh_ecount + m; 850b394cd1eSFedor Uporov } 851b394cd1eSFedor Uporov 852512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 853b394cd1eSFedor Uporov bwrite(bp); 854b394cd1eSFedor Uporov bp = NULL; 855b394cd1eSFedor Uporov 856b394cd1eSFedor Uporov /* Fix old index. */ 857b394cd1eSFedor Uporov if (m) { 858b394cd1eSFedor Uporov path[i].ep_header->eh_ecount = 859b394cd1eSFedor Uporov path[i].ep_header->eh_ecount - m; 860b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + i); 861b394cd1eSFedor Uporov } 862b394cd1eSFedor Uporov 863b394cd1eSFedor Uporov i--; 864b394cd1eSFedor Uporov } 865b394cd1eSFedor Uporov 866b394cd1eSFedor Uporov error = ext4_ext_insert_index(ip, path + at, border, newblk); 867b394cd1eSFedor Uporov 868b394cd1eSFedor Uporov cleanup: 869b394cd1eSFedor Uporov if (bp) 870b394cd1eSFedor Uporov brelse(bp); 871b394cd1eSFedor Uporov 872b394cd1eSFedor Uporov if (error) { 873b394cd1eSFedor Uporov for (i = 0; i < depth; i++) { 874b394cd1eSFedor Uporov if (!ablks[i]) 875b394cd1eSFedor Uporov continue; 876b394cd1eSFedor Uporov ext4_ext_blkfree(ip, ablks[i], 1, 0); 877b394cd1eSFedor Uporov } 878b394cd1eSFedor Uporov } 879b394cd1eSFedor Uporov 880b394cd1eSFedor Uporov free(ablks, M_EXT2EXTENTS); 881b394cd1eSFedor Uporov 882b394cd1eSFedor Uporov return (error); 883b394cd1eSFedor Uporov } 884b394cd1eSFedor Uporov 885b394cd1eSFedor Uporov static int 886b394cd1eSFedor Uporov ext4_ext_grow_indepth(struct inode *ip, struct ext4_extent_path *path, 887b394cd1eSFedor Uporov struct ext4_extent *newext) 888b394cd1eSFedor Uporov { 889b394cd1eSFedor Uporov struct m_ext2fs *fs; 890b394cd1eSFedor Uporov struct ext4_extent_path *curpath; 891b394cd1eSFedor Uporov struct ext4_extent_header *neh; 892b394cd1eSFedor Uporov struct buf *bp; 893b394cd1eSFedor Uporov e4fs_daddr_t newblk; 894b394cd1eSFedor Uporov int error = 0; 895b394cd1eSFedor Uporov 896b394cd1eSFedor Uporov fs = ip->i_e2fs; 897b394cd1eSFedor Uporov curpath = path; 898b394cd1eSFedor Uporov 899b394cd1eSFedor Uporov newblk = ext4_ext_alloc_meta(ip); 900b394cd1eSFedor Uporov if (newblk == 0) 901b394cd1eSFedor Uporov return (error); 902b394cd1eSFedor Uporov 903b394cd1eSFedor Uporov bp = getblk(ip->i_devvp, fsbtodb(fs, newblk), fs->e2fs_bsize, 0, 0, 0); 904b394cd1eSFedor Uporov if (!bp) 905b394cd1eSFedor Uporov return (EIO); 906b394cd1eSFedor Uporov 907b394cd1eSFedor Uporov /* Move top-level index/leaf into new block. */ 908b394cd1eSFedor Uporov memmove(bp->b_data, curpath->ep_header, sizeof(ip->i_data)); 909b394cd1eSFedor Uporov 910b394cd1eSFedor Uporov /* Set size of new block */ 911b394cd1eSFedor Uporov neh = ext4_ext_block_header(bp->b_data); 912b394cd1eSFedor Uporov neh->eh_magic = EXT4_EXT_MAGIC; 913b394cd1eSFedor Uporov 914b394cd1eSFedor Uporov if (ext4_ext_inode_depth(ip)) 915b394cd1eSFedor Uporov neh->eh_max = ext4_ext_space_block_index(ip); 916b394cd1eSFedor Uporov else 917b394cd1eSFedor Uporov neh->eh_max = ext4_ext_space_block(ip); 918b394cd1eSFedor Uporov 919512f29d1SFedor Uporov ext2_extent_blk_csum_set(ip, bp->b_data); 920b394cd1eSFedor Uporov error = bwrite(bp); 921b394cd1eSFedor Uporov if (error) 922b394cd1eSFedor Uporov goto out; 923b394cd1eSFedor Uporov 924b394cd1eSFedor Uporov bp = NULL; 925b394cd1eSFedor Uporov 926b394cd1eSFedor Uporov curpath->ep_header->eh_magic = EXT4_EXT_MAGIC; 927b394cd1eSFedor Uporov curpath->ep_header->eh_max = ext4_ext_space_root(ip); 928b394cd1eSFedor Uporov curpath->ep_header->eh_ecount = 1; 929b394cd1eSFedor Uporov curpath->ep_index = EXT_FIRST_INDEX(curpath->ep_header); 930b394cd1eSFedor Uporov curpath->ep_index->ei_blk = EXT_FIRST_EXTENT(path[0].ep_header)->e_blk; 931b394cd1eSFedor Uporov ext4_index_store_pblock(curpath->ep_index, newblk); 932b394cd1eSFedor Uporov 933b394cd1eSFedor Uporov neh = ext4_ext_inode_header(ip); 934b394cd1eSFedor Uporov neh->eh_depth = path->ep_depth + 1; 935b394cd1eSFedor Uporov ext4_ext_dirty(ip, curpath); 936b394cd1eSFedor Uporov out: 937b394cd1eSFedor Uporov brelse(bp); 938b394cd1eSFedor Uporov 939b394cd1eSFedor Uporov return (error); 940b394cd1eSFedor Uporov } 941b394cd1eSFedor Uporov 942b394cd1eSFedor Uporov static int 943b394cd1eSFedor Uporov ext4_ext_create_new_leaf(struct inode *ip, struct ext4_extent_path *path, 944b394cd1eSFedor Uporov struct ext4_extent *newext) 945b394cd1eSFedor Uporov { 946b394cd1eSFedor Uporov struct ext4_extent_path *curpath; 947b394cd1eSFedor Uporov int depth, i, error; 948b394cd1eSFedor Uporov 949b394cd1eSFedor Uporov repeat: 950b394cd1eSFedor Uporov i = depth = ext4_ext_inode_depth(ip); 951b394cd1eSFedor Uporov 952b394cd1eSFedor Uporov /* Look for free index entry int the tree */ 953b394cd1eSFedor Uporov curpath = path + depth; 954b394cd1eSFedor Uporov while (i > 0 && !EXT_HAS_FREE_INDEX(curpath)) { 955b394cd1eSFedor Uporov i--; 956b394cd1eSFedor Uporov curpath--; 957b394cd1eSFedor Uporov } 958b394cd1eSFedor Uporov 959b394cd1eSFedor Uporov /* 960b394cd1eSFedor Uporov * We use already allocated block for index block, 961b394cd1eSFedor Uporov * so subsequent data blocks should be contiguous. 962b394cd1eSFedor Uporov */ 963b394cd1eSFedor Uporov if (EXT_HAS_FREE_INDEX(curpath)) { 964b394cd1eSFedor Uporov error = ext4_ext_split(ip, path, newext, i); 965b394cd1eSFedor Uporov if (error) 966b394cd1eSFedor Uporov goto out; 967b394cd1eSFedor Uporov 968b394cd1eSFedor Uporov /* Refill path. */ 969b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 970b394cd1eSFedor Uporov error = ext4_ext_find_extent(ip, newext->e_blk, &path); 971b394cd1eSFedor Uporov if (error) 972b394cd1eSFedor Uporov goto out; 973b394cd1eSFedor Uporov } else { 974b394cd1eSFedor Uporov /* Tree is full, do grow in depth. */ 975b394cd1eSFedor Uporov error = ext4_ext_grow_indepth(ip, path, newext); 976b394cd1eSFedor Uporov if (error) 977b394cd1eSFedor Uporov goto out; 978b394cd1eSFedor Uporov 979b394cd1eSFedor Uporov /* Refill path. */ 980b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 981b394cd1eSFedor Uporov error = ext4_ext_find_extent(ip, newext->e_blk, &path); 982b394cd1eSFedor Uporov if (error) 983b394cd1eSFedor Uporov goto out; 984b394cd1eSFedor Uporov 985b394cd1eSFedor Uporov /* Check and split tree if required. */ 986b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 987b394cd1eSFedor Uporov if (path[depth].ep_header->eh_ecount == 988b394cd1eSFedor Uporov path[depth].ep_header->eh_max) 989b394cd1eSFedor Uporov goto repeat; 990b394cd1eSFedor Uporov } 991b394cd1eSFedor Uporov 992b394cd1eSFedor Uporov out: 993b394cd1eSFedor Uporov return (error); 994b394cd1eSFedor Uporov } 995b394cd1eSFedor Uporov 996b394cd1eSFedor Uporov static int 997b394cd1eSFedor Uporov ext4_ext_correct_indexes(struct inode *ip, struct ext4_extent_path *path) 998b394cd1eSFedor Uporov { 999b394cd1eSFedor Uporov struct ext4_extent_header *eh; 1000b394cd1eSFedor Uporov struct ext4_extent *ex; 1001b394cd1eSFedor Uporov int32_t border; 1002b394cd1eSFedor Uporov int depth, k; 1003b394cd1eSFedor Uporov 1004b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1005b394cd1eSFedor Uporov eh = path[depth].ep_header; 1006b394cd1eSFedor Uporov ex = path[depth].ep_ext; 1007b394cd1eSFedor Uporov 1008b394cd1eSFedor Uporov if (ex == NULL || eh == NULL) 1009b394cd1eSFedor Uporov return (EIO); 1010b394cd1eSFedor Uporov 1011b394cd1eSFedor Uporov if (!depth) 1012b394cd1eSFedor Uporov return (0); 1013b394cd1eSFedor Uporov 1014b394cd1eSFedor Uporov /* We will correct tree if first leaf got modified only. */ 1015b394cd1eSFedor Uporov if (ex != EXT_FIRST_EXTENT(eh)) 1016b394cd1eSFedor Uporov return (0); 1017b394cd1eSFedor Uporov 1018b394cd1eSFedor Uporov k = depth - 1; 1019b394cd1eSFedor Uporov border = path[depth].ep_ext->e_blk; 1020b394cd1eSFedor Uporov path[k].ep_index->ei_blk = border; 1021b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + k); 1022b394cd1eSFedor Uporov while (k--) { 1023b394cd1eSFedor Uporov /* Change all left-side indexes. */ 1024b394cd1eSFedor Uporov if (path[k+1].ep_index != EXT_FIRST_INDEX(path[k+1].ep_header)) 1025b394cd1eSFedor Uporov break; 1026b394cd1eSFedor Uporov 1027b394cd1eSFedor Uporov path[k].ep_index->ei_blk = border; 1028b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + k); 1029b394cd1eSFedor Uporov } 1030b394cd1eSFedor Uporov 1031b394cd1eSFedor Uporov return (0); 1032b394cd1eSFedor Uporov } 1033b394cd1eSFedor Uporov 1034b394cd1eSFedor Uporov static int 1035b394cd1eSFedor Uporov ext4_ext_insert_extent(struct inode *ip, struct ext4_extent_path *path, 1036b394cd1eSFedor Uporov struct ext4_extent *newext) 1037b394cd1eSFedor Uporov { 1038b394cd1eSFedor Uporov struct ext4_extent_header * eh; 1039b394cd1eSFedor Uporov struct ext4_extent *ex, *nex, *nearex; 1040b394cd1eSFedor Uporov struct ext4_extent_path *npath; 1041b394cd1eSFedor Uporov int depth, len, error, next; 1042b394cd1eSFedor Uporov 1043b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1044b394cd1eSFedor Uporov ex = path[depth].ep_ext; 1045b394cd1eSFedor Uporov npath = NULL; 1046b394cd1eSFedor Uporov 1047b394cd1eSFedor Uporov if (newext->e_len == 0 || path[depth].ep_header == NULL) 1048b394cd1eSFedor Uporov return (EINVAL); 1049b394cd1eSFedor Uporov 1050b394cd1eSFedor Uporov /* Insert block into found extent. */ 1051b394cd1eSFedor Uporov if (ex && ext4_can_extents_be_merged(ex, newext)) { 1052b394cd1eSFedor Uporov ex->e_len = ex->e_len + newext->e_len; 1053b394cd1eSFedor Uporov eh = path[depth].ep_header; 1054b394cd1eSFedor Uporov nearex = ex; 1055b394cd1eSFedor Uporov goto merge; 1056b394cd1eSFedor Uporov } 1057b394cd1eSFedor Uporov 1058b394cd1eSFedor Uporov repeat: 1059b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1060b394cd1eSFedor Uporov eh = path[depth].ep_header; 1061b394cd1eSFedor Uporov if (eh->eh_ecount < eh->eh_max) 1062b394cd1eSFedor Uporov goto has_space; 1063b394cd1eSFedor Uporov 1064b394cd1eSFedor Uporov /* Try next leaf */ 1065b394cd1eSFedor Uporov nex = EXT_LAST_EXTENT(eh); 1066b394cd1eSFedor Uporov next = ext4_ext_next_leaf_block(ip, path); 1067b394cd1eSFedor Uporov if (newext->e_blk > nex->e_blk && next != EXT4_MAX_BLOCKS) { 1068b394cd1eSFedor Uporov KASSERT(npath == NULL, 1069b394cd1eSFedor Uporov ("ext4_ext_insert_extent: bad path")); 1070b394cd1eSFedor Uporov 1071b394cd1eSFedor Uporov error = ext4_ext_find_extent(ip, next, &npath); 1072b394cd1eSFedor Uporov if (error) 1073b394cd1eSFedor Uporov goto cleanup; 1074b394cd1eSFedor Uporov 1075b394cd1eSFedor Uporov if (npath->ep_depth != path->ep_depth) { 1076b394cd1eSFedor Uporov error = EIO; 1077b394cd1eSFedor Uporov goto cleanup; 1078b394cd1eSFedor Uporov } 1079b394cd1eSFedor Uporov 1080b394cd1eSFedor Uporov eh = npath[depth].ep_header; 1081b394cd1eSFedor Uporov if (eh->eh_ecount < eh->eh_max) { 1082b394cd1eSFedor Uporov path = npath; 1083b394cd1eSFedor Uporov goto repeat; 1084b394cd1eSFedor Uporov } 1085b394cd1eSFedor Uporov } 1086b394cd1eSFedor Uporov 1087b394cd1eSFedor Uporov /* 1088b394cd1eSFedor Uporov * There is no free space in the found leaf, 1089b394cd1eSFedor Uporov * try to add a new leaf to the tree. 1090b394cd1eSFedor Uporov */ 1091b394cd1eSFedor Uporov error = ext4_ext_create_new_leaf(ip, path, newext); 1092b394cd1eSFedor Uporov if (error) 1093b394cd1eSFedor Uporov goto cleanup; 1094b394cd1eSFedor Uporov 1095b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1096b394cd1eSFedor Uporov eh = path[depth].ep_header; 1097b394cd1eSFedor Uporov 1098b394cd1eSFedor Uporov has_space: 1099b394cd1eSFedor Uporov nearex = path[depth].ep_ext; 1100b394cd1eSFedor Uporov if (!nearex) { 1101b394cd1eSFedor Uporov /* Create new extent in the leaf. */ 1102b394cd1eSFedor Uporov path[depth].ep_ext = EXT_FIRST_EXTENT(eh); 1103b394cd1eSFedor Uporov } else if (newext->e_blk > nearex->e_blk) { 1104b394cd1eSFedor Uporov if (nearex != EXT_LAST_EXTENT(eh)) { 1105b394cd1eSFedor Uporov len = EXT_MAX_EXTENT(eh) - nearex; 1106b394cd1eSFedor Uporov len = (len - 1) * sizeof(struct ext4_extent); 1107b394cd1eSFedor Uporov len = len < 0 ? 0 : len; 1108b394cd1eSFedor Uporov memmove(nearex + 2, nearex + 1, len); 1109b394cd1eSFedor Uporov } 1110b394cd1eSFedor Uporov path[depth].ep_ext = nearex + 1; 1111b394cd1eSFedor Uporov } else { 1112b394cd1eSFedor Uporov len = (EXT_MAX_EXTENT(eh) - nearex) * sizeof(struct ext4_extent); 1113b394cd1eSFedor Uporov len = len < 0 ? 0 : len; 1114b394cd1eSFedor Uporov memmove(nearex + 1, nearex, len); 1115b394cd1eSFedor Uporov path[depth].ep_ext = nearex; 1116b394cd1eSFedor Uporov } 1117b394cd1eSFedor Uporov 1118b394cd1eSFedor Uporov eh->eh_ecount = eh->eh_ecount + 1; 1119b394cd1eSFedor Uporov nearex = path[depth].ep_ext; 1120b394cd1eSFedor Uporov nearex->e_blk = newext->e_blk; 1121b394cd1eSFedor Uporov nearex->e_start_lo = newext->e_start_lo; 1122b394cd1eSFedor Uporov nearex->e_start_hi = newext->e_start_hi; 1123b394cd1eSFedor Uporov nearex->e_len = newext->e_len; 1124b394cd1eSFedor Uporov 1125b394cd1eSFedor Uporov merge: 1126b394cd1eSFedor Uporov /* Try to merge extents to the right. */ 1127b394cd1eSFedor Uporov while (nearex < EXT_LAST_EXTENT(eh)) { 1128b394cd1eSFedor Uporov if (!ext4_can_extents_be_merged(nearex, nearex + 1)) 1129b394cd1eSFedor Uporov break; 1130b394cd1eSFedor Uporov 1131b394cd1eSFedor Uporov /* Merge with next extent. */ 1132b394cd1eSFedor Uporov nearex->e_len = nearex->e_len + nearex[1].e_len; 1133b394cd1eSFedor Uporov if (nearex + 1 < EXT_LAST_EXTENT(eh)) { 1134b394cd1eSFedor Uporov len = (EXT_LAST_EXTENT(eh) - nearex - 1) * 1135b394cd1eSFedor Uporov sizeof(struct ext4_extent); 1136b394cd1eSFedor Uporov memmove(nearex + 1, nearex + 2, len); 1137b394cd1eSFedor Uporov } 1138b394cd1eSFedor Uporov 1139b394cd1eSFedor Uporov eh->eh_ecount = eh->eh_ecount - 1; 1140b394cd1eSFedor Uporov KASSERT(eh->eh_ecount != 0, 1141b394cd1eSFedor Uporov ("ext4_ext_insert_extent: bad ecount")); 1142b394cd1eSFedor Uporov } 1143b394cd1eSFedor Uporov 1144b394cd1eSFedor Uporov /* 1145b394cd1eSFedor Uporov * Try to merge extents to the left, 1146b394cd1eSFedor Uporov * start from inexes correction. 1147b394cd1eSFedor Uporov */ 1148b394cd1eSFedor Uporov error = ext4_ext_correct_indexes(ip, path); 1149b394cd1eSFedor Uporov if (error) 1150b394cd1eSFedor Uporov goto cleanup; 1151b394cd1eSFedor Uporov 1152b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + depth); 1153b394cd1eSFedor Uporov 1154b394cd1eSFedor Uporov cleanup: 1155b394cd1eSFedor Uporov if (npath) { 1156b394cd1eSFedor Uporov ext4_ext_drop_refs(npath); 1157b394cd1eSFedor Uporov free(npath, M_EXT2EXTENTS); 1158b394cd1eSFedor Uporov } 1159b394cd1eSFedor Uporov 1160b394cd1eSFedor Uporov ip->i_ext_cache.ec_type = EXT4_EXT_CACHE_NO; 1161b394cd1eSFedor Uporov return (error); 1162b394cd1eSFedor Uporov } 1163b394cd1eSFedor Uporov 1164b394cd1eSFedor Uporov static e4fs_daddr_t 1165b394cd1eSFedor Uporov ext4_new_blocks(struct inode *ip, daddr_t lbn, e4fs_daddr_t pref, 1166b394cd1eSFedor Uporov struct ucred *cred, unsigned long *count, int *perror) 1167b394cd1eSFedor Uporov { 1168b394cd1eSFedor Uporov struct m_ext2fs *fs; 1169b394cd1eSFedor Uporov e4fs_daddr_t newblk; 1170b394cd1eSFedor Uporov 1171b394cd1eSFedor Uporov /* 1172b394cd1eSFedor Uporov * We will allocate only single block for now. 1173b394cd1eSFedor Uporov */ 1174b394cd1eSFedor Uporov if (*count > 1) 1175b394cd1eSFedor Uporov return (0); 1176b394cd1eSFedor Uporov 1177f86f5cd4SPedro F. Giffuni fs = ip->i_e2fs; 1178b394cd1eSFedor Uporov EXT2_LOCK(ip->i_ump); 1179b394cd1eSFedor Uporov *perror = ext2_alloc(ip, lbn, pref, (int)fs->e2fs_bsize, cred, &newblk); 1180b394cd1eSFedor Uporov if (*perror) 1181b394cd1eSFedor Uporov return (0); 1182b394cd1eSFedor Uporov 1183b394cd1eSFedor Uporov if (newblk) { 1184b394cd1eSFedor Uporov ip->i_flag |= IN_CHANGE | IN_UPDATE; 1185b394cd1eSFedor Uporov ext2_update(ip->i_vnode, 1); 1186b394cd1eSFedor Uporov } 1187b394cd1eSFedor Uporov 1188b394cd1eSFedor Uporov return (newblk); 1189b394cd1eSFedor Uporov } 1190b394cd1eSFedor Uporov 1191b394cd1eSFedor Uporov int 1192b394cd1eSFedor Uporov ext4_ext_get_blocks(struct inode *ip, e4fs_daddr_t iblk, 1193b394cd1eSFedor Uporov unsigned long max_blocks, struct ucred *cred, struct buf **bpp, 11943acd9182SFedor Uporov int *pallocated, daddr_t *nb) 1195b394cd1eSFedor Uporov { 1196b394cd1eSFedor Uporov struct m_ext2fs *fs; 1197b394cd1eSFedor Uporov struct buf *bp = NULL; 1198b394cd1eSFedor Uporov struct ext4_extent_path *path; 1199b394cd1eSFedor Uporov struct ext4_extent newex, *ex; 1200b394cd1eSFedor Uporov e4fs_daddr_t bpref, newblk = 0; 1201b394cd1eSFedor Uporov unsigned long allocated = 0; 1202b394cd1eSFedor Uporov int error = 0, depth; 1203b394cd1eSFedor Uporov 1204b394cd1eSFedor Uporov if(bpp) 1205b394cd1eSFedor Uporov *bpp = NULL; 1206f86f5cd4SPedro F. Giffuni *pallocated = 0; 1207b394cd1eSFedor Uporov 1208b394cd1eSFedor Uporov /* Check cache. */ 1209f86f5cd4SPedro F. Giffuni path = NULL; 1210b394cd1eSFedor Uporov if ((bpref = ext4_ext_in_cache(ip, iblk, &newex))) { 1211b394cd1eSFedor Uporov if (bpref == EXT4_EXT_CACHE_IN) { 1212b394cd1eSFedor Uporov /* Block is already allocated. */ 1213b394cd1eSFedor Uporov newblk = iblk - newex.e_blk + 1214b394cd1eSFedor Uporov ext4_ext_extent_pblock(&newex); 1215b394cd1eSFedor Uporov allocated = newex.e_len - (iblk - newex.e_blk); 1216b394cd1eSFedor Uporov goto out; 1217b394cd1eSFedor Uporov } else { 1218b394cd1eSFedor Uporov error = EIO; 1219b394cd1eSFedor Uporov goto out2; 1220b394cd1eSFedor Uporov } 1221b394cd1eSFedor Uporov } 1222b394cd1eSFedor Uporov 1223b394cd1eSFedor Uporov error = ext4_ext_find_extent(ip, iblk, &path); 1224b394cd1eSFedor Uporov if (error) { 1225b394cd1eSFedor Uporov goto out2; 1226b394cd1eSFedor Uporov } 1227b394cd1eSFedor Uporov 1228b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1229b394cd1eSFedor Uporov if (path[depth].ep_ext == NULL && depth != 0) { 1230b394cd1eSFedor Uporov error = EIO; 1231b394cd1eSFedor Uporov goto out2; 1232b394cd1eSFedor Uporov } 1233b394cd1eSFedor Uporov 1234b394cd1eSFedor Uporov if ((ex = path[depth].ep_ext)) { 1235b394cd1eSFedor Uporov uint64_t lblk = ex->e_blk; 1236b394cd1eSFedor Uporov uint16_t e_len = ex->e_len; 1237b394cd1eSFedor Uporov e4fs_daddr_t e_start = ext4_ext_extent_pblock(ex); 1238b394cd1eSFedor Uporov 1239b394cd1eSFedor Uporov if (e_len > EXT4_MAX_LEN) 1240b394cd1eSFedor Uporov goto out2; 1241b394cd1eSFedor Uporov 1242b394cd1eSFedor Uporov /* If we found extent covers block, simply return it. */ 1243b394cd1eSFedor Uporov if (iblk >= lblk && iblk < lblk + e_len) { 1244b394cd1eSFedor Uporov newblk = iblk - lblk + e_start; 1245b394cd1eSFedor Uporov allocated = e_len - (iblk - lblk); 1246b394cd1eSFedor Uporov ext4_ext_put_in_cache(ip, lblk, e_len, 1247b394cd1eSFedor Uporov e_start, EXT4_EXT_CACHE_IN); 1248b394cd1eSFedor Uporov goto out; 1249b394cd1eSFedor Uporov } 1250b394cd1eSFedor Uporov } 1251b394cd1eSFedor Uporov 1252b394cd1eSFedor Uporov /* Allocate the new block. */ 1253b394cd1eSFedor Uporov if (S_ISREG(ip->i_mode) && (!ip->i_next_alloc_block)) { 1254b394cd1eSFedor Uporov ip->i_next_alloc_goal = 0; 1255b394cd1eSFedor Uporov } 1256b394cd1eSFedor Uporov 1257b394cd1eSFedor Uporov bpref = ext4_ext_blkpref(ip, path, iblk); 1258b394cd1eSFedor Uporov allocated = max_blocks; 1259b394cd1eSFedor Uporov newblk = ext4_new_blocks(ip, iblk, bpref, cred, &allocated, &error); 1260b394cd1eSFedor Uporov if (!newblk) 1261b394cd1eSFedor Uporov goto out2; 1262b394cd1eSFedor Uporov 1263b394cd1eSFedor Uporov /* Try to insert new extent into found leaf and return. */ 1264b394cd1eSFedor Uporov newex.e_blk = iblk; 1265b394cd1eSFedor Uporov ext4_ext_store_pblock(&newex, newblk); 1266b394cd1eSFedor Uporov newex.e_len = allocated; 1267b394cd1eSFedor Uporov error = ext4_ext_insert_extent(ip, path, &newex); 1268b394cd1eSFedor Uporov if (error) 1269b394cd1eSFedor Uporov goto out2; 1270b394cd1eSFedor Uporov 1271b394cd1eSFedor Uporov newblk = ext4_ext_extent_pblock(&newex); 1272b394cd1eSFedor Uporov ext4_ext_put_in_cache(ip, iblk, allocated, newblk, EXT4_EXT_CACHE_IN); 1273b394cd1eSFedor Uporov *pallocated = 1; 1274b394cd1eSFedor Uporov 1275b394cd1eSFedor Uporov out: 1276b394cd1eSFedor Uporov if (allocated > max_blocks) 1277b394cd1eSFedor Uporov allocated = max_blocks; 1278b394cd1eSFedor Uporov 1279b394cd1eSFedor Uporov if (bpp) 1280b394cd1eSFedor Uporov { 1281f86f5cd4SPedro F. Giffuni fs = ip->i_e2fs; 1282b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(fs, newblk), 1283b394cd1eSFedor Uporov fs->e2fs_bsize, cred, &bp); 1284b394cd1eSFedor Uporov if (error) { 1285b394cd1eSFedor Uporov brelse(bp); 1286b394cd1eSFedor Uporov } else { 1287b394cd1eSFedor Uporov *bpp = bp; 1288b394cd1eSFedor Uporov } 1289b394cd1eSFedor Uporov } 1290b394cd1eSFedor Uporov 1291b394cd1eSFedor Uporov out2: 1292b394cd1eSFedor Uporov if (path) { 1293b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 1294b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 1295b394cd1eSFedor Uporov } 1296b394cd1eSFedor Uporov 1297b394cd1eSFedor Uporov if (nb) 1298b394cd1eSFedor Uporov *nb = newblk; 1299b394cd1eSFedor Uporov 1300b394cd1eSFedor Uporov return (error); 1301b394cd1eSFedor Uporov } 1302b394cd1eSFedor Uporov 1303b394cd1eSFedor Uporov static inline uint16_t 1304b394cd1eSFedor Uporov ext4_ext_get_actual_len(struct ext4_extent *ext) 1305b394cd1eSFedor Uporov { 1306b394cd1eSFedor Uporov 1307b394cd1eSFedor Uporov return (ext->e_len <= EXT_INIT_MAX_LEN ? 1308b394cd1eSFedor Uporov ext->e_len : (ext->e_len - EXT_INIT_MAX_LEN)); 1309b394cd1eSFedor Uporov } 1310b394cd1eSFedor Uporov 1311b394cd1eSFedor Uporov static inline struct ext4_extent_header * 1312b394cd1eSFedor Uporov ext4_ext_header(struct inode *ip) 1313b394cd1eSFedor Uporov { 1314b394cd1eSFedor Uporov 1315f86f5cd4SPedro F. Giffuni return ((struct ext4_extent_header *)ip->i_db); 1316b394cd1eSFedor Uporov } 1317b394cd1eSFedor Uporov 1318b394cd1eSFedor Uporov static int 1319b394cd1eSFedor Uporov ext4_remove_blocks(struct inode *ip, struct ext4_extent *ex, 1320b394cd1eSFedor Uporov unsigned long from, unsigned long to) 1321b394cd1eSFedor Uporov { 1322b394cd1eSFedor Uporov unsigned long num, start; 1323b394cd1eSFedor Uporov 1324b394cd1eSFedor Uporov if (from >= ex->e_blk && 1325b394cd1eSFedor Uporov to == ex->e_blk + ext4_ext_get_actual_len(ex) - 1) { 1326b394cd1eSFedor Uporov /* Tail cleanup. */ 1327b394cd1eSFedor Uporov num = ex->e_blk + ext4_ext_get_actual_len(ex) - from; 1328b394cd1eSFedor Uporov start = ext4_ext_extent_pblock(ex) + 1329b394cd1eSFedor Uporov ext4_ext_get_actual_len(ex) - num; 1330b394cd1eSFedor Uporov ext4_ext_blkfree(ip, start, num, 0); 1331b394cd1eSFedor Uporov } 1332b394cd1eSFedor Uporov 1333b394cd1eSFedor Uporov return (0); 1334b394cd1eSFedor Uporov } 1335b394cd1eSFedor Uporov 1336b394cd1eSFedor Uporov static int 1337b394cd1eSFedor Uporov ext4_ext_rm_index(struct inode *ip, struct ext4_extent_path *path) 1338b394cd1eSFedor Uporov { 1339b394cd1eSFedor Uporov e4fs_daddr_t leaf; 1340b394cd1eSFedor Uporov 1341b394cd1eSFedor Uporov /* Free index block. */ 1342b394cd1eSFedor Uporov path--; 1343b394cd1eSFedor Uporov leaf = ext4_ext_index_pblock(path->ep_index); 1344b394cd1eSFedor Uporov KASSERT(path->ep_header->eh_ecount != 0, 1345b394cd1eSFedor Uporov ("ext4_ext_rm_index: bad ecount")); 1346b394cd1eSFedor Uporov path->ep_header->eh_ecount--; 1347b394cd1eSFedor Uporov ext4_ext_dirty(ip, path); 1348b394cd1eSFedor Uporov ext4_ext_blkfree(ip, leaf, 1, 0); 1349b394cd1eSFedor Uporov return (0); 1350b394cd1eSFedor Uporov } 1351b394cd1eSFedor Uporov 1352b394cd1eSFedor Uporov static int 1353b394cd1eSFedor Uporov ext4_ext_rm_leaf(struct inode *ip, struct ext4_extent_path *path, 1354b394cd1eSFedor Uporov uint64_t start) 1355b394cd1eSFedor Uporov { 1356b394cd1eSFedor Uporov struct ext4_extent_header *eh; 1357f86f5cd4SPedro F. Giffuni struct ext4_extent *ex; 1358b394cd1eSFedor Uporov unsigned int a, b, block, num; 1359b394cd1eSFedor Uporov unsigned long ex_blk; 1360b394cd1eSFedor Uporov unsigned short ex_len; 1361f86f5cd4SPedro F. Giffuni int depth; 1362b394cd1eSFedor Uporov int error, correct_index; 1363b394cd1eSFedor Uporov 1364b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1365b394cd1eSFedor Uporov if (!path[depth].ep_header) { 1366b394cd1eSFedor Uporov if (path[depth].ep_data == NULL) 1367b394cd1eSFedor Uporov return (EINVAL); 1368b394cd1eSFedor Uporov path[depth].ep_header = 1369b394cd1eSFedor Uporov (struct ext4_extent_header* )path[depth].ep_data; 1370b394cd1eSFedor Uporov } 1371b394cd1eSFedor Uporov 1372b394cd1eSFedor Uporov eh = path[depth].ep_header; 1373b394cd1eSFedor Uporov if (!eh) { 1374*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 1375f86f5cd4SPedro F. Giffuni "bad header => extent corrupted"); 1376b394cd1eSFedor Uporov return (EIO); 1377b394cd1eSFedor Uporov } 1378b394cd1eSFedor Uporov 1379b394cd1eSFedor Uporov ex = EXT_LAST_EXTENT(eh); 1380b394cd1eSFedor Uporov ex_blk = ex->e_blk; 1381b394cd1eSFedor Uporov ex_len = ext4_ext_get_actual_len(ex); 1382b394cd1eSFedor Uporov 1383f86f5cd4SPedro F. Giffuni error = 0; 1384f86f5cd4SPedro F. Giffuni correct_index = 0; 1385b394cd1eSFedor Uporov while (ex >= EXT_FIRST_EXTENT(eh) && ex_blk + ex_len > start) { 1386b394cd1eSFedor Uporov path[depth].ep_ext = ex; 1387b394cd1eSFedor Uporov a = ex_blk > start ? ex_blk : start; 1388b394cd1eSFedor Uporov b = (uint64_t)ex_blk + ex_len - 1 < 1389b394cd1eSFedor Uporov EXT4_MAX_BLOCKS ? ex_blk + ex_len - 1 : EXT4_MAX_BLOCKS; 1390b394cd1eSFedor Uporov 1391b394cd1eSFedor Uporov if (a != ex_blk && b != ex_blk + ex_len - 1) 1392b394cd1eSFedor Uporov return (EINVAL); 1393b394cd1eSFedor Uporov else if (a != ex_blk) { 1394b394cd1eSFedor Uporov /* Remove tail of the extent. */ 1395b394cd1eSFedor Uporov block = ex_blk; 1396b394cd1eSFedor Uporov num = a - block; 1397b394cd1eSFedor Uporov } else if (b != ex_blk + ex_len - 1) { 1398b394cd1eSFedor Uporov /* Remove head of the extent, not implemented. */ 1399b394cd1eSFedor Uporov return (EINVAL); 1400b394cd1eSFedor Uporov } else { 1401b394cd1eSFedor Uporov /* Remove whole extent. */ 1402b394cd1eSFedor Uporov block = ex_blk; 1403b394cd1eSFedor Uporov num = 0; 1404b394cd1eSFedor Uporov } 1405b394cd1eSFedor Uporov 14063fcbb8c0SFedor Uporov if (ex == EXT_FIRST_EXTENT(eh)) 1407b394cd1eSFedor Uporov correct_index = 1; 1408b394cd1eSFedor Uporov 1409b394cd1eSFedor Uporov error = ext4_remove_blocks(ip, ex, a, b); 1410b394cd1eSFedor Uporov if (error) 1411b394cd1eSFedor Uporov goto out; 1412b394cd1eSFedor Uporov 1413b394cd1eSFedor Uporov if (num == 0) { 1414b394cd1eSFedor Uporov ext4_ext_store_pblock(ex, 0); 1415b394cd1eSFedor Uporov eh->eh_ecount--; 1416b394cd1eSFedor Uporov } 1417b394cd1eSFedor Uporov 1418b394cd1eSFedor Uporov ex->e_blk = block; 1419b394cd1eSFedor Uporov ex->e_len = num; 1420b394cd1eSFedor Uporov 1421b394cd1eSFedor Uporov ext4_ext_dirty(ip, path + depth); 1422b394cd1eSFedor Uporov 1423b394cd1eSFedor Uporov ex--; 1424b394cd1eSFedor Uporov ex_blk = ex->e_blk; 1425b394cd1eSFedor Uporov ex_len = ext4_ext_get_actual_len(ex); 1426b394cd1eSFedor Uporov }; 1427b394cd1eSFedor Uporov 1428b394cd1eSFedor Uporov if (correct_index && eh->eh_ecount) 1429b394cd1eSFedor Uporov error = ext4_ext_correct_indexes(ip, path); 1430b394cd1eSFedor Uporov 1431b394cd1eSFedor Uporov /* 1432b394cd1eSFedor Uporov * If this leaf is free, we should 1433b394cd1eSFedor Uporov * remove it from index block above. 1434b394cd1eSFedor Uporov */ 1435b394cd1eSFedor Uporov if (error == 0 && eh->eh_ecount == 0 && path[depth].ep_data != NULL) 1436b394cd1eSFedor Uporov error = ext4_ext_rm_index(ip, path + depth); 1437b394cd1eSFedor Uporov 1438b394cd1eSFedor Uporov out: 1439b394cd1eSFedor Uporov return (error); 1440b394cd1eSFedor Uporov } 1441b394cd1eSFedor Uporov 1442b394cd1eSFedor Uporov static struct buf * 1443b394cd1eSFedor Uporov ext4_read_extent_tree_block(struct inode *ip, e4fs_daddr_t pblk, 1444b394cd1eSFedor Uporov int depth, int flags) 1445b394cd1eSFedor Uporov { 1446b394cd1eSFedor Uporov struct m_ext2fs *fs; 1447b394cd1eSFedor Uporov struct ext4_extent_header *eh; 1448b394cd1eSFedor Uporov struct buf *bp; 1449b394cd1eSFedor Uporov int error; 1450b394cd1eSFedor Uporov 1451b394cd1eSFedor Uporov fs = ip->i_e2fs; 1452b394cd1eSFedor Uporov error = bread(ip->i_devvp, fsbtodb(fs, pblk), 1453b394cd1eSFedor Uporov fs->e2fs_bsize, NOCRED, &bp); 1454b394cd1eSFedor Uporov if (error) { 1455b394cd1eSFedor Uporov brelse(bp); 1456d7511a40SPedro F. Giffuni return (NULL); 1457d7511a40SPedro F. Giffuni } 1458b394cd1eSFedor Uporov 1459b394cd1eSFedor Uporov eh = ext4_ext_block_header(bp->b_data); 1460b394cd1eSFedor Uporov if (eh->eh_depth != depth) { 1461*84b89556SFedor Uporov SDT_PROBE2(ext2fs, , trace, extents, 1, 1462*84b89556SFedor Uporov "unexpected eh_depth"); 1463b394cd1eSFedor Uporov goto err; 1464d7511a40SPedro F. Giffuni } 1465d7511a40SPedro F. Giffuni 1466b394cd1eSFedor Uporov error = ext4_ext_check_header(ip, eh); 1467b394cd1eSFedor Uporov if (error) 1468b394cd1eSFedor Uporov goto err; 1469d7511a40SPedro F. Giffuni 1470b394cd1eSFedor Uporov return (bp); 1471b394cd1eSFedor Uporov 1472b394cd1eSFedor Uporov err: 1473b394cd1eSFedor Uporov brelse(bp); 1474b394cd1eSFedor Uporov return (NULL); 1475b394cd1eSFedor Uporov 1476b394cd1eSFedor Uporov } 1477b394cd1eSFedor Uporov 1478b394cd1eSFedor Uporov static int inline 1479b394cd1eSFedor Uporov ext4_ext_more_to_rm(struct ext4_extent_path *path) 1480b394cd1eSFedor Uporov { 1481b394cd1eSFedor Uporov 1482b394cd1eSFedor Uporov KASSERT(path->ep_index != NULL, 1483b394cd1eSFedor Uporov ("ext4_ext_more_to_rm: bad index from path")); 1484b394cd1eSFedor Uporov 1485b394cd1eSFedor Uporov if (path->ep_index < EXT_FIRST_INDEX(path->ep_header)) 1486b394cd1eSFedor Uporov return (0); 1487b394cd1eSFedor Uporov 1488b394cd1eSFedor Uporov if (path->ep_header->eh_ecount == path->index_count) 1489b394cd1eSFedor Uporov return (0); 1490b394cd1eSFedor Uporov 1491b394cd1eSFedor Uporov return (1); 1492b394cd1eSFedor Uporov } 1493b394cd1eSFedor Uporov 1494b394cd1eSFedor Uporov int 1495b394cd1eSFedor Uporov ext4_ext_remove_space(struct inode *ip, off_t length, int flags, 1496b394cd1eSFedor Uporov struct ucred *cred, struct thread *td) 1497b394cd1eSFedor Uporov { 1498b394cd1eSFedor Uporov struct buf *bp; 1499b394cd1eSFedor Uporov struct ext4_extent_header *ehp; 1500b394cd1eSFedor Uporov struct ext4_extent_path *path; 1501b394cd1eSFedor Uporov int depth; 1502b394cd1eSFedor Uporov int i, error; 1503b394cd1eSFedor Uporov 1504b394cd1eSFedor Uporov ehp = (struct ext4_extent_header *)ip->i_db; 1505b394cd1eSFedor Uporov depth = ext4_ext_inode_depth(ip); 1506b394cd1eSFedor Uporov 1507b394cd1eSFedor Uporov error = ext4_ext_check_header(ip, ehp); 1508b394cd1eSFedor Uporov if(error) 1509b394cd1eSFedor Uporov return (error); 1510b394cd1eSFedor Uporov 1511b394cd1eSFedor Uporov path = malloc(sizeof(struct ext4_extent_path) * (depth + 1), 1512b394cd1eSFedor Uporov M_EXT2EXTENTS, M_WAITOK | M_ZERO); 1513b394cd1eSFedor Uporov if (!path) 1514b394cd1eSFedor Uporov return (ENOMEM); 1515b394cd1eSFedor Uporov 1516b394cd1eSFedor Uporov path[0].ep_header = ehp; 1517b394cd1eSFedor Uporov path[0].ep_depth = depth; 1518f86f5cd4SPedro F. Giffuni i = 0; 1519f86f5cd4SPedro F. Giffuni while (error == 0 && i >= 0) { 1520b394cd1eSFedor Uporov if (i == depth) { 1521b394cd1eSFedor Uporov /* This is leaf. */ 1522b394cd1eSFedor Uporov error = ext4_ext_rm_leaf(ip, path, length); 1523b394cd1eSFedor Uporov if (error) 1524b394cd1eSFedor Uporov break; 1525b394cd1eSFedor Uporov free(path[i].ep_data, M_EXT2EXTENTS); 1526b394cd1eSFedor Uporov path[i].ep_data = NULL; 1527b394cd1eSFedor Uporov i--; 1528b394cd1eSFedor Uporov continue; 1529b394cd1eSFedor Uporov } 1530b394cd1eSFedor Uporov 1531b394cd1eSFedor Uporov /* This is index. */ 1532b394cd1eSFedor Uporov if (!path[i].ep_header) 1533b394cd1eSFedor Uporov path[i].ep_header = 1534b394cd1eSFedor Uporov (struct ext4_extent_header *)path[i].ep_data; 1535b394cd1eSFedor Uporov 1536b394cd1eSFedor Uporov if (!path[i].ep_index) { 1537b394cd1eSFedor Uporov /* This level hasn't touched yet. */ 1538b394cd1eSFedor Uporov path[i].ep_index = EXT_LAST_INDEX(path[i].ep_header); 1539b394cd1eSFedor Uporov path[i].index_count = path[i].ep_header->eh_ecount + 1; 1540b394cd1eSFedor Uporov } else { 1541b394cd1eSFedor Uporov /* We've already was here, see at next index. */ 1542b394cd1eSFedor Uporov path[i].ep_index--; 1543b394cd1eSFedor Uporov } 1544b394cd1eSFedor Uporov 1545b394cd1eSFedor Uporov if (ext4_ext_more_to_rm(path + i)) { 1546b394cd1eSFedor Uporov memset(path + i + 1, 0, sizeof(*path)); 1547b394cd1eSFedor Uporov bp = ext4_read_extent_tree_block(ip, 1548b394cd1eSFedor Uporov ext4_ext_index_pblock(path[i].ep_index), 1549b394cd1eSFedor Uporov path[0].ep_depth - (i + 1), 0); 1550b394cd1eSFedor Uporov if (!bp) { 1551b394cd1eSFedor Uporov error = EIO; 1552b394cd1eSFedor Uporov break; 1553b394cd1eSFedor Uporov } 1554b394cd1eSFedor Uporov 1555b394cd1eSFedor Uporov ext4_ext_fill_path_bdata(&path[i+1], bp, 1556b394cd1eSFedor Uporov ext4_ext_index_pblock(path[i].ep_index)); 1557b394cd1eSFedor Uporov brelse(bp); 1558b394cd1eSFedor Uporov path[i].index_count = path[i].ep_header->eh_ecount; 1559b394cd1eSFedor Uporov i++; 1560b394cd1eSFedor Uporov } else { 1561b394cd1eSFedor Uporov if (path[i].ep_header->eh_ecount == 0 && i > 0) { 1562b394cd1eSFedor Uporov /* Index is empty, remove it. */ 1563b394cd1eSFedor Uporov error = ext4_ext_rm_index(ip, path + i); 1564b394cd1eSFedor Uporov } 1565b394cd1eSFedor Uporov free(path[i].ep_data, M_EXT2EXTENTS); 1566b394cd1eSFedor Uporov path[i].ep_data = NULL; 1567b394cd1eSFedor Uporov i--; 1568b394cd1eSFedor Uporov } 1569b394cd1eSFedor Uporov } 1570b394cd1eSFedor Uporov 1571b394cd1eSFedor Uporov if (path->ep_header->eh_ecount == 0) { 1572b394cd1eSFedor Uporov /* 1573b394cd1eSFedor Uporov * Truncate the tree to zero. 1574b394cd1eSFedor Uporov */ 1575b394cd1eSFedor Uporov ext4_ext_header(ip)->eh_depth = 0; 1576b394cd1eSFedor Uporov ext4_ext_header(ip)->eh_max = ext4_ext_space_root(ip); 1577b394cd1eSFedor Uporov ext4_ext_dirty(ip, path); 1578b394cd1eSFedor Uporov } 1579b394cd1eSFedor Uporov 1580b394cd1eSFedor Uporov ext4_ext_drop_refs(path); 1581b394cd1eSFedor Uporov free(path, M_EXT2EXTENTS); 1582b394cd1eSFedor Uporov 1583b394cd1eSFedor Uporov return (error); 1584d7511a40SPedro F. Giffuni } 1585