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