1 1.3 christos /* $NetBSD: ext2fs_extents.c,v 1.3 2016/08/13 07:40:10 christos Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2010 Zheng Liu <lz (at) freebsd.org> 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * Redistribution and use in source and binary forms, with or without 8 1.1 christos * modification, are permitted provided that the following conditions 9 1.1 christos * are met: 10 1.1 christos * 1. Redistributions of source code must retain the above copyright 11 1.1 christos * notice, this list of conditions and the following disclaimer. 12 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 christos * notice, this list of conditions and the following disclaimer in the 14 1.1 christos * documentation and/or other materials provided with the distribution. 15 1.1 christos * 16 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 christos * SUCH DAMAGE. 27 1.1 christos * 28 1.1 christos * $FreeBSD: head/sys/fs/ext2fs/ext2_extents.c 295523 2016-02-11 15:27:14Z pfg $ 29 1.1 christos */ 30 1.1 christos 31 1.1 christos #include <sys/cdefs.h> 32 1.3 christos __KERNEL_RCSID(0, "$NetBSD: ext2fs_extents.c,v 1.3 2016/08/13 07:40:10 christos Exp $"); 33 1.1 christos 34 1.1 christos #include <sys/param.h> 35 1.1 christos #include <sys/systm.h> 36 1.1 christos #include <sys/resourcevar.h> 37 1.1 christos #include <sys/kernel.h> 38 1.1 christos #include <sys/file.h> 39 1.1 christos #include <sys/stat.h> 40 1.1 christos #include <sys/buf.h> 41 1.1 christos #include <sys/proc.h> 42 1.1 christos #include <sys/mount.h> 43 1.1 christos #include <sys/vnode.h> 44 1.1 christos #include <sys/signalvar.h> 45 1.1 christos #include <sys/kauth.h> 46 1.1 christos 47 1.1 christos #include <ufs/ufs/inode.h> 48 1.1 christos #include <ufs/ufs/ufsmount.h> 49 1.1 christos #include <ufs/ufs/ufs_extern.h> 50 1.1 christos 51 1.1 christos 52 1.1 christos #include <ufs/ext2fs/ext2fs.h> 53 1.1 christos #include <ufs/ext2fs/ext2fs_extents.h> 54 1.1 christos #include <ufs/ext2fs/ext2fs_extern.h> 55 1.1 christos 56 1.1 christos 57 1.1 christos 58 1.1 christos static bool 59 1.1 christos ext4_ext_binsearch_index(struct inode *ip, struct ext4_extent_path *path, 60 1.1 christos daddr_t lbn, daddr_t *first_lbn, daddr_t *last_lbn) 61 1.1 christos { 62 1.1 christos struct ext4_extent_header *ehp = path->ep_header; 63 1.1 christos struct ext4_extent_index *first, *last, *l, *r, *m; 64 1.1 christos 65 1.1 christos first = (struct ext4_extent_index *)(char *)(ehp + 1); 66 1.1 christos last = first + ehp->eh_ecount - 1; 67 1.1 christos l = first; 68 1.1 christos r = last; 69 1.1 christos while (l <= r) { 70 1.1 christos m = l + (r - l) / 2; 71 1.1 christos if (lbn < m->ei_blk) 72 1.1 christos r = m - 1; 73 1.1 christos else 74 1.1 christos l = m + 1; 75 1.1 christos } 76 1.1 christos 77 1.1 christos if (l == first) { 78 1.1 christos path->ep_sparse_ext.e_blk = *first_lbn; 79 1.1 christos path->ep_sparse_ext.e_len = first->ei_blk - *first_lbn; 80 1.1 christos path->ep_sparse_ext.e_start_hi = 0; 81 1.1 christos path->ep_sparse_ext.e_start_lo = 0; 82 1.1 christos path->ep_is_sparse = true; 83 1.2 christos return true; 84 1.1 christos } 85 1.1 christos path->ep_index = l - 1; 86 1.1 christos *first_lbn = path->ep_index->ei_blk; 87 1.1 christos if (path->ep_index < last) 88 1.1 christos *last_lbn = l->ei_blk - 1; 89 1.2 christos return false; 90 1.1 christos } 91 1.1 christos 92 1.1 christos static void 93 1.1 christos ext4_ext_binsearch(struct inode *ip, struct ext4_extent_path *path, daddr_t lbn, 94 1.1 christos daddr_t first_lbn, daddr_t last_lbn) 95 1.1 christos { 96 1.1 christos struct ext4_extent_header *ehp = path->ep_header; 97 1.1 christos struct ext4_extent *first, *l, *r, *m; 98 1.1 christos 99 1.1 christos if (ehp->eh_ecount == 0) 100 1.1 christos return; 101 1.1 christos 102 1.1 christos first = (struct ext4_extent *)(char *)(ehp + 1); 103 1.1 christos l = first; 104 1.1 christos r = first + ehp->eh_ecount - 1; 105 1.1 christos while (l <= r) { 106 1.1 christos m = l + (r - l) / 2; 107 1.1 christos if (lbn < m->e_blk) 108 1.1 christos r = m - 1; 109 1.1 christos else 110 1.1 christos l = m + 1; 111 1.1 christos } 112 1.1 christos 113 1.1 christos if (l == first) { 114 1.1 christos path->ep_sparse_ext.e_blk = first_lbn; 115 1.1 christos path->ep_sparse_ext.e_len = first->e_blk - first_lbn; 116 1.1 christos path->ep_sparse_ext.e_start_hi = 0; 117 1.1 christos path->ep_sparse_ext.e_start_lo = 0; 118 1.1 christos path->ep_is_sparse = true; 119 1.1 christos return; 120 1.1 christos } 121 1.1 christos path->ep_ext = l - 1; 122 1.1 christos if (path->ep_ext->e_blk + path->ep_ext->e_len <= lbn) { 123 1.1 christos path->ep_sparse_ext.e_blk = path->ep_ext->e_blk + 124 1.1 christos path->ep_ext->e_len; 125 1.1 christos if (l <= (first + ehp->eh_ecount - 1)) 126 1.1 christos path->ep_sparse_ext.e_len = l->e_blk - 127 1.1 christos path->ep_sparse_ext.e_blk; 128 1.1 christos else 129 1.1 christos path->ep_sparse_ext.e_len = last_lbn - 130 1.1 christos path->ep_sparse_ext.e_blk + 1; 131 1.1 christos path->ep_sparse_ext.e_start_hi = 0; 132 1.1 christos path->ep_sparse_ext.e_start_lo = 0; 133 1.1 christos path->ep_is_sparse = true; 134 1.1 christos } 135 1.1 christos } 136 1.1 christos 137 1.1 christos /* 138 1.1 christos * Find a block in ext4 extent cache. 139 1.1 christos */ 140 1.1 christos int 141 1.1 christos ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep) 142 1.1 christos { 143 1.1 christos struct ext4_extent_cache *ecp; 144 1.1 christos int ret = EXT4_EXT_CACHE_NO; 145 1.1 christos 146 1.1 christos ecp = &ip->inode_ext.e2fs.i_ext_cache; 147 1.1 christos 148 1.1 christos /* cache is invalid */ 149 1.1 christos if (ecp->ec_type == EXT4_EXT_CACHE_NO) 150 1.3 christos return ret; 151 1.1 christos 152 1.1 christos if (lbn >= ecp->ec_blk && lbn < ecp->ec_blk + ecp->ec_len) { 153 1.1 christos ep->e_blk = ecp->ec_blk; 154 1.1 christos ep->e_start_lo = ecp->ec_start & 0xffffffff; 155 1.1 christos ep->e_start_hi = ecp->ec_start >> 32 & 0xffff; 156 1.1 christos ep->e_len = ecp->ec_len; 157 1.1 christos ret = ecp->ec_type; 158 1.1 christos } 159 1.2 christos return ret; 160 1.1 christos } 161 1.1 christos 162 1.1 christos /* 163 1.1 christos * Put an ext4_extent structure in ext4 cache. 164 1.1 christos */ 165 1.1 christos void 166 1.1 christos ext4_ext_put_cache(struct inode *ip, struct ext4_extent *ep, int type) 167 1.1 christos { 168 1.1 christos struct ext4_extent_cache *ecp; 169 1.1 christos 170 1.1 christos ecp = &ip->inode_ext.e2fs.i_ext_cache; 171 1.1 christos ecp->ec_type = type; 172 1.1 christos ecp->ec_blk = ep->e_blk; 173 1.1 christos ecp->ec_len = ep->e_len; 174 1.1 christos ecp->ec_start = (daddr_t)ep->e_start_hi << 32 | ep->e_start_lo; 175 1.1 christos } 176 1.1 christos 177 1.1 christos /* 178 1.1 christos * Find an extent. 179 1.1 christos */ 180 1.1 christos struct ext4_extent_path * 181 1.1 christos ext4_ext_find_extent(struct m_ext2fs *fs, struct inode *ip, 182 1.1 christos daddr_t lbn, struct ext4_extent_path *path) 183 1.1 christos { 184 1.1 christos struct ext4_extent_header *ehp; 185 1.1 christos uint16_t i; 186 1.1 christos int error, size; 187 1.1 christos daddr_t nblk; 188 1.1 christos 189 1.1 christos ehp = (struct ext4_extent_header *)ip->i_din.e2fs_din->e2di_blocks; 190 1.1 christos 191 1.1 christos if (ehp->eh_magic != EXT4_EXT_MAGIC) 192 1.2 christos return NULL; 193 1.1 christos 194 1.1 christos path->ep_header = ehp; 195 1.1 christos 196 1.1 christos daddr_t first_lbn = 0; 197 1.1 christos daddr_t last_lbn = lblkno(ip->i_e2fs, ip->i_size); 198 1.1 christos 199 1.1 christos for (i = ehp->eh_depth; i != 0; --i) { 200 1.1 christos path->ep_depth = i; 201 1.1 christos path->ep_ext = NULL; 202 1.1 christos if (ext4_ext_binsearch_index(ip, path, lbn, &first_lbn, 203 1.1 christos &last_lbn)) { 204 1.2 christos return path; 205 1.1 christos } 206 1.1 christos 207 1.1 christos nblk = (daddr_t)path->ep_index->ei_leaf_hi << 32 | 208 1.1 christos path->ep_index->ei_leaf_lo; 209 1.1 christos size = blksize(fs, ip, nblk); 210 1.1 christos if (path->ep_bp != NULL) { 211 1.1 christos brelse(path->ep_bp, 0); 212 1.1 christos path->ep_bp = NULL; 213 1.1 christos } 214 1.1 christos error = bread(ip->i_devvp, fsbtodb(fs, nblk), size, 0, 215 1.1 christos &path->ep_bp); 216 1.1 christos if (error) { 217 1.1 christos brelse(path->ep_bp, 0); 218 1.1 christos path->ep_bp = NULL; 219 1.2 christos return NULL; 220 1.1 christos } 221 1.1 christos ehp = (struct ext4_extent_header *)path->ep_bp->b_data; 222 1.1 christos path->ep_header = ehp; 223 1.1 christos } 224 1.1 christos 225 1.1 christos path->ep_depth = i; 226 1.1 christos path->ep_ext = NULL; 227 1.1 christos path->ep_index = NULL; 228 1.1 christos path->ep_is_sparse = false; 229 1.1 christos 230 1.1 christos ext4_ext_binsearch(ip, path, lbn, first_lbn, last_lbn); 231 1.2 christos return path; 232 1.1 christos } 233