From owner-svn-src-all@freebsd.org Tue Oct 17 20:45:45 2017 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id DEC21E472FE; Tue, 17 Oct 2017 20:45:45 +0000 (UTC) (envelope-from fsu@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id ADD3D82FD3; Tue, 17 Oct 2017 20:45:45 +0000 (UTC) (envelope-from fsu@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v9HKjiPT035519; Tue, 17 Oct 2017 20:45:44 GMT (envelope-from fsu@FreeBSD.org) Received: (from fsu@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v9HKjiHD035513; Tue, 17 Oct 2017 20:45:44 GMT (envelope-from fsu@FreeBSD.org) Message-Id: <201710172045.v9HKjiHD035513@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: fsu set sender to fsu@FreeBSD.org using -f From: Fedor Uporov Date: Tue, 17 Oct 2017 20:45:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r324706 - head/sys/fs/ext2fs X-SVN-Group: head X-SVN-Commit-Author: fsu X-SVN-Commit-Paths: head/sys/fs/ext2fs X-SVN-Commit-Revision: 324706 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 17 Oct 2017 20:45:46 -0000 Author: fsu Date: Tue Oct 17 20:45:44 2017 New Revision: 324706 URL: https://svnweb.freebsd.org/changeset/base/324706 Log: Add inital extents read-write support. Approved by: pfg (mentor) MFC after: 6 months RelNotes: Yes Differential Revision: https://reviews.freebsd.org/D12087 Modified: head/sys/fs/ext2fs/ext2_alloc.c head/sys/fs/ext2fs/ext2_balloc.c head/sys/fs/ext2fs/ext2_bmap.c head/sys/fs/ext2fs/ext2_extattr.c head/sys/fs/ext2fs/ext2_extents.c head/sys/fs/ext2fs/ext2_extents.h head/sys/fs/ext2fs/ext2_extern.h head/sys/fs/ext2fs/ext2_inode.c head/sys/fs/ext2fs/ext2_inode_cnv.c head/sys/fs/ext2fs/ext2_subr.c head/sys/fs/ext2fs/ext2_vfsops.c head/sys/fs/ext2fs/ext2_vnops.c head/sys/fs/ext2fs/ext2fs.h head/sys/fs/ext2fs/inode.h Modified: head/sys/fs/ext2fs/ext2_alloc.c ============================================================================== --- head/sys/fs/ext2fs/ext2_alloc.c Tue Oct 17 20:37:31 2017 (r324705) +++ head/sys/fs/ext2fs/ext2_alloc.c Tue Oct 17 20:45:44 2017 (r324706) @@ -135,19 +135,20 @@ nospace: * Allocate EA's block for inode. */ daddr_t -ext2_allocfacl(struct inode *ip) +ext2_alloc_meta(struct inode *ip) { struct m_ext2fs *fs; - daddr_t facl; + daddr_t blk; fs = ip->i_e2fs; EXT2_LOCK(ip->i_ump); - facl = ext2_alloccg(ip, ino_to_cg(fs, ip->i_number), 0, fs->e2fs_bsize); - if (0 == facl) + blk = ext2_hashalloc(ip, ino_to_cg(fs, ip->i_number), 0, fs->e2fs_bsize, + ext2_alloccg); + if (0 == blk) EXT2_UNLOCK(ip->i_ump); - return (facl); + return (blk); } /* @@ -200,7 +201,7 @@ ext2_reallocblks(struct vop_reallocblks_args *ap) fs = ip->i_e2fs; ump = ip->i_ump; - if (fs->e2fs_contigsumsize <= 0) + if (fs->e2fs_contigsumsize <= 0 || ip->i_flag & IN_E4EXTENTS) return (ENOSPC); buflist = ap->a_buflist; @@ -375,7 +376,7 @@ ext2_valloc(struct vnode *pvp, int mode, struct ucred struct inode *ip; struct ext2mount *ump; ino_t ino, ipref; - int i, error, cg; + int error, cg; *vpp = NULL; pip = VTOI(pvp); @@ -421,11 +422,12 @@ ext2_valloc(struct vnode *pvp, int mode, struct ucred ip->i_blocks = 0; ip->i_mode = 0; ip->i_flags = 0; - /* now we want to make sure that the block pointers are zeroed out */ - for (i = 0; i < EXT2_NDADDR; i++) - ip->i_db[i] = 0; - for (i = 0; i < EXT2_NIADDR; i++) - ip->i_ib[i] = 0; + if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_EXTENTS) + && (S_ISREG(mode) || S_ISDIR(mode))) + ext4_ext_tree_init(ip); + else + memset(ip->i_data, 0, sizeof(ip->i_data)); + /* * Set up a new generation number for this inode. @@ -575,8 +577,11 @@ e4fs_daddr_t ext2_blkpref(struct inode *ip, e2fs_lbn_t lbn, int indx, e2fs_daddr_t *bap, e2fs_daddr_t blocknr) { + struct m_ext2fs *fs; int tmp; + fs = ip->i_e2fs; + mtx_assert(EXT2_MTX(ip->i_ump), MA_OWNED); /* @@ -599,10 +604,9 @@ ext2_blkpref(struct inode *ip, e2fs_lbn_t lbn, int ind * Else lets fall back to the blocknr or, if there is none, follow * the rule that a block should be allocated near its inode. */ - return blocknr ? blocknr : + return (blocknr ? blocknr : (e2fs_daddr_t)(ip->i_block_group * - EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) + - ip->i_e2fs->e2fs->e2fs_first_dblock; + EXT2_BLOCKS_PER_GROUP(fs)) + fs->e2fs->e2fs_first_dblock); } /* Modified: head/sys/fs/ext2fs/ext2_balloc.c ============================================================================== --- head/sys/fs/ext2fs/ext2_balloc.c Tue Oct 17 20:37:31 2017 (r324705) +++ head/sys/fs/ext2fs/ext2_balloc.c Tue Oct 17 20:45:44 2017 (r324706) @@ -51,6 +51,76 @@ #include #include +static int +ext2_ext_balloc(struct inode *ip, uint32_t lbn, int size, + struct ucred *cred, struct buf **bpp, int flags) +{ + struct m_ext2fs *fs; + struct buf *bp = NULL; + struct vnode *vp = ITOV(ip); + uint32_t nb; + int osize, nsize, blks, error, allocated; + + fs = ip->i_e2fs; + blks = howmany(size, fs->e2fs_bsize); + + error = ext4_ext_get_blocks(ip, lbn, blks, cred, NULL, &allocated, &nb); + if (error) + return (error); + + if (allocated) { + if (ip->i_size < (lbn + 1) * fs->e2fs_bsize) + nsize = fragroundup(fs, size); + else + nsize = fs->e2fs_bsize; + + bp = getblk(vp, lbn, nsize, 0, 0, 0); + if(!bp) + return (EIO); + + bp->b_blkno = fsbtodb(fs, nb); + if (flags & BA_CLRBUF) + vfs_bio_clrbuf(bp); + } else { + if (ip->i_size >= (lbn + 1) * fs->e2fs_bsize) { + + error = bread(vp, lbn, fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + bp->b_blkno = fsbtodb(fs, nb); + *bpp = bp; + return (0); + } + + /* + * Consider need to reallocate a fragment. + */ + osize = fragroundup(fs, blkoff(fs, ip->i_size)); + nsize = fragroundup(fs, size); + if (nsize <= osize) { + error = bread(vp, lbn, osize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + bp->b_blkno = fsbtodb(fs, nb); + } else { + error = bread(vp, lbn, fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + bp->b_blkno = fsbtodb(fs, nb); + } + } + + *bpp = bp; + + return (error); +} + /* * Balloc defines the structure of filesystem storage * by allocating the physical blocks on a device given @@ -84,6 +154,10 @@ ext2_balloc(struct inode *ip, e2fs_lbn_t lbn, int size ip->i_next_alloc_block++; ip->i_next_alloc_goal++; } + + if (ip->i_flag & IN_E4EXTENTS) + return (ext2_ext_balloc(ip, lbn, size, cred, bpp, flags)); + /* * The first EXT2_NDADDR blocks are direct blocks */ Modified: head/sys/fs/ext2fs/ext2_bmap.c ============================================================================== --- head/sys/fs/ext2fs/ext2_bmap.c Tue Oct 17 20:37:31 2017 (r324705) +++ head/sys/fs/ext2fs/ext2_bmap.c Tue Oct 17 20:45:44 2017 (r324706) @@ -53,8 +53,6 @@ #include #include -static int ext4_bmapext(struct vnode *, int32_t, int64_t *, int *, int *); - /* * Bmap converts the logical block number of a file to its physical block * number on the disk. The conversion is done by using the logical block @@ -89,55 +87,52 @@ ext2_bmap(struct vop_bmap_args *ap) * Convert the logical block number of a file to its physical block number * on the disk within ext4 extents. */ -static int +int ext4_bmapext(struct vnode *vp, int32_t bn, int64_t *bnp, int *runp, int *runb) { struct inode *ip; struct m_ext2fs *fs; + struct ext4_extent_header *ehp; struct ext4_extent *ep; - struct ext4_extent_path path = {.ep_bp = NULL}; + struct ext4_extent_path *path = NULL; daddr_t lbn; - int error; + int error, depth; ip = VTOI(vp); fs = ip->i_e2fs; lbn = bn; + ehp = (struct ext4_extent_header *)ip->i_data; + depth = ehp->eh_depth; + *bnp = -1; if (runp != NULL) *runp = 0; if (runb != NULL) *runb = 0; - error = 0; - ext4_ext_find_extent(fs, ip, lbn, &path); - if (path.ep_is_sparse) { - *bnp = -1; - if (runp != NULL) - *runp = path.ep_sparse_ext.e_len - - (lbn - path.ep_sparse_ext.e_blk) - 1; - if (runb != NULL) - *runb = lbn - path.ep_sparse_ext.e_blk; - } else { - if (path.ep_ext == NULL) { - error = EIO; - goto out; - } - ep = path.ep_ext; - *bnp = fsbtodb(fs, lbn - ep->e_blk + - (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32)); + error = ext4_ext_find_extent(ip, lbn, &path); + if (error) + return (error); - if (*bnp == 0) - *bnp = -1; - - if (runp != NULL) - *runp = ep->e_len - (lbn - ep->e_blk) - 1; - if (runb != NULL) - *runb = lbn - ep->e_blk; + ep = path[depth].ep_ext; + if(ep) { + if (lbn < ep->e_blk) { + if (runp != NULL) + *runp = ep->e_blk - lbn - 1; + } else if (ep->e_blk <= lbn && lbn < ep->e_blk + ep->e_len) { + *bnp = fsbtodb(fs, lbn - ep->e_blk + + (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32)); + if (runp != NULL) + *runp = ep->e_len - (lbn - ep->e_blk) - 1; + if (runb != NULL) + *runb = lbn - ep->e_blk; + } else { + if (runb != NULL) + *runb = ep->e_blk + lbn - ep->e_len; + } } -out: - if (path.ep_bp != NULL) - brelse(path.ep_bp); + ext4_ext_path_free(path); return (error); } Modified: head/sys/fs/ext2fs/ext2_extattr.c ============================================================================== --- head/sys/fs/ext2fs/ext2_extattr.c Tue Oct 17 20:37:31 2017 (r324705) +++ head/sys/fs/ext2fs/ext2_extattr.c Tue Oct 17 20:45:44 2017 (r324706) @@ -612,7 +612,7 @@ ext2_extattr_block_clone(struct inode *ip, struct buf if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1) return (EINVAL); - facl = ext2_allocfacl(ip); + facl = ext2_alloc_meta(ip); if (!facl) return (ENOSPC); @@ -1137,7 +1137,7 @@ ext2_extattr_block_set(struct inode *ip, int attrnames return (ENOSPC); /* Allocate block, fill EA header and insert entry */ - ip->i_facl = ext2_allocfacl(ip); + ip->i_facl = ext2_alloc_meta(ip); if (0 == ip->i_facl) return (ENOSPC); Modified: head/sys/fs/ext2fs/ext2_extents.c ============================================================================== --- head/sys/fs/ext2fs/ext2_extents.c Tue Oct 17 20:37:31 2017 (r324705) +++ head/sys/fs/ext2fs/ext2_extents.c Tue Oct 17 20:45:44 2017 (r324706) @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -43,87 +44,163 @@ #include #include -static bool -ext4_ext_binsearch_index(struct inode *ip, struct ext4_extent_path *path, - daddr_t lbn, daddr_t *first_lbn, daddr_t *last_lbn){ - struct ext4_extent_header *ehp = path->ep_header; - struct ext4_extent_index *first, *last, *l, *r, *m; +static MALLOC_DEFINE(M_EXT2EXTENTS, "ext2_extents", "EXT2 extents"); - first = (struct ext4_extent_index *)(char *)(ehp + 1); - last = first + ehp->eh_ecount - 1; - l = first; - r = last; - while (l <= r) { - m = l + (r - l) / 2; - if (lbn < m->ei_blk) - r = m - 1; - else - l = m + 1; - } +#ifdef EXT2FS_DEBUG +static void +ext4_ext_print_extent(struct ext4_extent *ep) +{ - if (l == first) { - path->ep_sparse_ext.e_blk = *first_lbn; - path->ep_sparse_ext.e_len = first->ei_blk - *first_lbn; - path->ep_sparse_ext.e_start_hi = 0; - path->ep_sparse_ext.e_start_lo = 0; - path->ep_is_sparse = true; - return (true); - } - path->ep_index = l - 1; - *first_lbn = path->ep_index->ei_blk; - if (path->ep_index < last) - *last_lbn = l->ei_blk - 1; - return (false); + printf(" ext %p => (blk %u len %u start %lu)\n", + ep, ep->e_blk, ep->e_len, + (uint64_t)ep->e_start_hi << 32 | ep->e_start_lo); } +static void ext4_ext_print_header(struct inode *ip, struct ext4_extent_header *ehp); + static void -ext4_ext_binsearch(struct inode *ip, struct ext4_extent_path *path, daddr_t lbn, - daddr_t first_lbn, daddr_t last_lbn) +ext4_ext_print_index(struct inode *ip, struct ext4_extent_index *ex, int do_walk) { - struct ext4_extent_header *ehp = path->ep_header; - struct ext4_extent *first, *l, *r, *m; + struct m_ext2fs *fs; + struct buf *bp; + int error; - if (ehp->eh_ecount == 0) - return; + fs = ip->i_e2fs; - first = (struct ext4_extent *)(char *)(ehp + 1); - l = first; - r = first + ehp->eh_ecount - 1; - while (l <= r) { - m = l + (r - l) / 2; - if (lbn < m->e_blk) - r = m - 1; - else - l = m + 1; - } + printf(" index %p => (blk %u pblk %lu)\n", + ex, ex->ei_blk, (uint64_t)ex->ei_leaf_hi << 32 | ex->ei_leaf_lo); - if (l == first) { - path->ep_sparse_ext.e_blk = first_lbn; - path->ep_sparse_ext.e_len = first->e_blk - first_lbn; - path->ep_sparse_ext.e_start_hi = 0; - path->ep_sparse_ext.e_start_lo = 0; - path->ep_is_sparse = true; + if(!do_walk) return; + + if ((error = bread(ip->i_devvp, + fsbtodb(fs, ((uint64_t)ex->ei_leaf_hi << 32 | ex->ei_leaf_lo)), + (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { + brelse(bp); + return; } - path->ep_ext = l - 1; - if (path->ep_ext->e_blk + path->ep_ext->e_len <= lbn) { - path->ep_sparse_ext.e_blk = path->ep_ext->e_blk + - path->ep_ext->e_len; - if (l <= (first + ehp->eh_ecount - 1)) - path->ep_sparse_ext.e_len = l->e_blk - - path->ep_sparse_ext.e_blk; + + ext4_ext_print_header(ip, (struct ext4_extent_header *)bp->b_data); + + brelse(bp); + +} + +static void +ext4_ext_print_header(struct inode *ip, struct ext4_extent_header *ehp) +{ + int i; + + printf("header %p => (magic 0x%x entries %d max %d depth %d gen %d)\n", + ehp, ehp->eh_magic, ehp->eh_ecount, ehp->eh_max, ehp->eh_depth, + ehp->eh_gen); + + for (i = 0; i < ehp->eh_ecount; i++) + if (ehp->eh_depth != 0) + ext4_ext_print_index(ip, + (struct ext4_extent_index *)(ehp + 1 + i), 1); else - path->ep_sparse_ext.e_len = last_lbn - - path->ep_sparse_ext.e_blk + 1; - path->ep_sparse_ext.e_start_hi = 0; - path->ep_sparse_ext.e_start_lo = 0; - path->ep_is_sparse = true; + ext4_ext_print_extent((struct ext4_extent *)(ehp + 1 + i)); +} + +static void +ext4_ext_print_path(struct inode *ip, struct ext4_extent_path *path) +{ + int k, l; + + l = path->ep_depth + + printf("ip=%d, Path:\n", ip->i_number); + for (k = 0; k <= l; k++, path++) { + if (path->ep_index) { + ext4_ext_print_index(ip, path->ep_index, 0); + } else if (path->ep_ext) { + ext4_ext_print_extent(path->ep_ext); + } } } -/* - * Find a block in ext4 extent cache. - */ +void +ext4_ext_print_extent_tree_status(struct inode * ip) +{ + struct m_ext2fs *fs; + struct ext4_extent_header *ehp; + + fs = ip->i_e2fs; + ehp = (struct ext4_extent_header *)(char *)ip->i_db; + + printf("Extent status:ip=%d\n", ip->i_number); + if (!(ip->i_flag & IN_E4EXTENTS)) + return; + + ext4_ext_print_header(ip, ehp); + + return; +} +#endif + +static inline struct ext4_extent_header * +ext4_ext_inode_header(struct inode *ip) +{ + + return ((struct ext4_extent_header *)ip->i_db); +} + +static inline struct ext4_extent_header * +ext4_ext_block_header(char *bdata) +{ + + return ((struct ext4_extent_header *)bdata); +} + +static inline unsigned short +ext4_ext_inode_depth(struct inode *ip) +{ + struct ext4_extent_header *ehp; + + ehp = (struct ext4_extent_header *)ip->i_data; + return (ehp->eh_depth); +} + +static inline e4fs_daddr_t +ext4_ext_index_pblock(struct ext4_extent_index *index) +{ + e4fs_daddr_t blk; + + blk = index->ei_leaf_lo; + blk |= (e4fs_daddr_t)index->ei_leaf_hi << 32; + + return (blk); +} + +static inline void +ext4_index_store_pblock(struct ext4_extent_index *index, e4fs_daddr_t pb) +{ + + index->ei_leaf_lo = pb & 0xffffffff; + index->ei_leaf_hi = (pb >> 32) & 0xffff; +} + + +static inline e4fs_daddr_t +ext4_ext_extent_pblock(struct ext4_extent *extent) +{ + e4fs_daddr_t blk; + + blk = extent->e_start_lo; + blk |= (e4fs_daddr_t)extent->e_start_hi << 32; + + return (blk); +} + +static inline void +ext4_ext_store_pblock(struct ext4_extent *ex, e4fs_daddr_t pb) +{ + + ex->e_start_lo = pb & 0xffffffff; + ex->e_start_hi = (pb >> 32) & 0xffff; +} + int ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep) { @@ -131,8 +208,6 @@ ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struc int ret = EXT4_EXT_CACHE_NO; ecp = &ip->i_ext_cache; - - /* cache is invalid */ if (ecp->ec_type == EXT4_EXT_CACHE_NO) return (ret); @@ -146,74 +221,1367 @@ ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struc return (ret); } -/* - * Put an ext4_extent structure in ext4 cache. - */ +static int +ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh) +{ + struct m_ext2fs *fs; + char *error_msg; + + fs = ip->i_e2fs; + + if (eh->eh_magic != EXT4_EXT_MAGIC) { + error_msg = "invalid magic"; + goto corrupted; + } + if (eh->eh_max == 0) { + error_msg = "invalid eh_max"; + goto corrupted; + } + if (eh->eh_ecount > eh->eh_max) { + error_msg = "invalid eh_entries"; + goto corrupted; + } + + return (0); + +corrupted: + ext2_fserr(fs, ip->i_uid, error_msg); + return (EIO); +} + +static void +ext4_ext_binsearch_index(struct ext4_extent_path *path, int blk) +{ + struct ext4_extent_header *eh; + struct ext4_extent_index *r, *l, *m; + + eh = path->ep_header; + + KASSERT(eh->eh_ecount <= eh->eh_max && eh->eh_ecount > 0, + ("ext4_ext_binsearch_index: bad args")); + + l = EXT_FIRST_INDEX(eh) + 1; + r = EXT_FIRST_INDEX(eh) + eh->eh_ecount - 1; + while (l <= r) { + m = l + (r - l) / 2; + if (blk < m->ei_blk) + r = m - 1; + else + l = m + 1; + } + + path->ep_index = l - 1; +} + +static void +ext4_ext_binsearch_ext(struct ext4_extent_path *path, int blk) +{ + struct ext4_extent_header *eh; + struct ext4_extent *r, *l, *m; + + eh = path->ep_header; + + KASSERT(eh->eh_ecount <= eh->eh_max, + ("ext4_ext_binsearch_ext: bad args")); + + if (eh->eh_ecount == 0) + return; + + l = EXT_FIRST_EXTENT(eh) + 1; + r = EXT_FIRST_EXTENT(eh) + eh->eh_ecount - 1; + + while (l <= r) { + m = l + (r - l) / 2; + if (blk < m->e_blk) + r = m - 1; + else + l = m + 1; + } + + path->ep_ext = l - 1; +} + +static int +ext4_ext_fill_path_bdata(struct ext4_extent_path *path, + struct buf *bp, uint64_t blk) +{ + + KASSERT(path->ep_data == NULL, + ("ext4_ext_fill_path_bdata: bad ep_data")); + + path->ep_data = malloc(bp->b_bufsize, M_EXT2EXTENTS, M_WAITOK); + if (!path->ep_data) + return (ENOMEM); + + memcpy(path->ep_data, bp->b_data, bp->b_bufsize); + path->ep_blk = blk; + + return (0); +} + +static void +ext4_ext_fill_path_buf(struct ext4_extent_path *path, struct buf *bp) +{ + + KASSERT(path->ep_data != NULL, + ("ext4_ext_fill_path_buf: bad ep_data")); + + memcpy(bp->b_data, path->ep_data, bp->b_bufsize); +} + +static void +ext4_ext_drop_refs(struct ext4_extent_path *path) +{ + int depth, i; + + if (!path) + return; + + depth = path->ep_depth; + for (i = 0; i <= depth; i++, path++) + if (path->ep_data) { + free(path->ep_data, M_EXT2EXTENTS); + path->ep_data = NULL; + } +} + void -ext4_ext_put_cache(struct inode *ip, struct ext4_extent *ep, int type) +ext4_ext_path_free(struct ext4_extent_path *path) { - struct ext4_extent_cache *ecp; - ecp = &ip->i_ext_cache; - ecp->ec_type = type; - ecp->ec_blk = ep->e_blk; - ecp->ec_len = ep->e_len; - ecp->ec_start = (daddr_t)ep->e_start_hi << 32 | ep->e_start_lo; + if (!path) + return; + + ext4_ext_drop_refs(path); + free(path, M_EXT2EXTENTS); } -/* - * Find an extent. - */ -struct ext4_extent_path * -ext4_ext_find_extent(struct m_ext2fs *fs, struct inode *ip, - daddr_t lbn, struct ext4_extent_path *path) +int +ext4_ext_find_extent(struct inode *ip, daddr_t block, + struct ext4_extent_path **ppath) { + struct m_ext2fs *fs; + struct ext4_extent_header *eh; + struct ext4_extent_path *path; + struct buf *bp; + uint64_t blk; + int error, depth, i, ppos, alloc; + + fs = ip->i_e2fs; + eh = ext4_ext_inode_header(ip); + depth = ext4_ext_inode_depth(ip); + ppos = 0; + alloc = 0; + + error = ext4_ext_check_header(ip, eh); + if (error) + return (error); + + if (!ppath) + return (EINVAL); + + path = *ppath; + if (!path) { + path = malloc(EXT4_EXT_DEPTH_MAX * + sizeof(struct ext4_extent_path), + M_EXT2EXTENTS, M_WAITOK | M_ZERO); + if (!path) + return (ENOMEM); + + *ppath = path; + alloc = 1; + } + + path[0].ep_header = eh; + path[0].ep_data = NULL; + + /* Walk through the tree. */ + i = depth; + while (i) { + ext4_ext_binsearch_index(&path[ppos], block); + blk = ext4_ext_index_pblock(path[ppos].ep_index); + path[ppos].ep_depth = i; + path[ppos].ep_ext = NULL; + + error = bread(ip->i_devvp, fsbtodb(ip->i_e2fs, blk), + ip->i_e2fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + goto error; + } + + ppos++; + if (ppos > depth) { + ext2_fserr(fs, ip->i_uid, + "ppos > depth => extent corrupted"); + error = EIO; + brelse(bp); + goto error; + } + + ext4_ext_fill_path_bdata(&path[ppos], bp, blk); + brelse(bp); + + eh = ext4_ext_block_header(path[ppos].ep_data); + error = ext4_ext_check_header(ip, eh); + if (error) + goto error; + + path[ppos].ep_header = eh; + + i--; + } + + error = ext4_ext_check_header(ip, eh); + if (error) + goto error; + + /* Find extent. */ + path[ppos].ep_depth = i; + path[ppos].ep_header = eh; + path[ppos].ep_ext = NULL; + path[ppos].ep_index = NULL; + ext4_ext_binsearch_ext(&path[ppos], block); + return (0); + +error: + ext4_ext_drop_refs(path); + if (alloc) + free(path, M_EXT2EXTENTS); + + *ppath = NULL; + + return (error); +} + +static inline int +ext4_ext_space_root(struct inode *ip) +{ + int size; + + size = sizeof(ip->i_data); + size -= sizeof(struct ext4_extent_header); + size /= sizeof(struct ext4_extent); + + return (size); +} + +static inline int +ext4_ext_space_block(struct inode *ip) +{ + struct m_ext2fs *fs; + int size; + + fs = ip->i_e2fs; + + size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent); + + return (size); +} + +static inline int +ext4_ext_space_block_index(struct inode *ip) +{ + struct m_ext2fs *fs; + int size; + + fs = ip->i_e2fs; + + size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent_index); + + return (size); +} + +void +ext4_ext_tree_init(struct inode *ip) +{ struct ext4_extent_header *ehp; - uint16_t i; - int error, size; - daddr_t nblk; - ehp = (struct ext4_extent_header *)(char *)ip->i_db; + ip->i_flag |= IN_E4EXTENTS; - if (ehp->eh_magic != EXT4_EXT_MAGIC) - return (NULL); + memset(ip->i_data, 0, EXT2_NDADDR + EXT2_NIADDR); + ehp = (struct ext4_extent_header *)ip->i_data; + ehp->eh_magic = EXT4_EXT_MAGIC; + ehp->eh_max = ext4_ext_space_root(ip); + ip->i_ext_cache.ec_type = EXT4_EXT_CACHE_NO; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + ext2_update(ip->i_vnode, 1); +} - path->ep_header = ehp; +static inline void +ext4_ext_put_in_cache(struct inode *ip, uint32_t blk, + uint32_t len, uint32_t start, int type) +{ - daddr_t first_lbn = 0; - daddr_t last_lbn = lblkno(ip->i_e2fs, ip->i_size); + KASSERT(len != 0, ("ext4_ext_put_in_cache: bad input")); - for (i = ehp->eh_depth; i != 0; --i) { - path->ep_depth = i; - path->ep_ext = NULL; - if (ext4_ext_binsearch_index(ip, path, lbn, &first_lbn, - &last_lbn)) { - return (path); + ip->i_ext_cache.ec_type = type; + ip->i_ext_cache.ec_blk = blk; + ip->i_ext_cache.ec_len = len; + ip->i_ext_cache.ec_start = start; +} + +static e4fs_daddr_t +ext4_ext_blkpref(struct inode *ip, struct ext4_extent_path *path, + e4fs_daddr_t block) +{ + struct m_ext2fs *fs; + struct ext4_extent *ex; + e4fs_daddr_t bg_start; + int depth; + + fs = ip->i_e2fs; + + if (path) { + depth = path->ep_depth; + ex = path[depth].ep_ext; + if (ex) { + e4fs_daddr_t pblk = ext4_ext_extent_pblock(ex); + e2fs_daddr_t blk = ex->e_blk; + + if (block > blk) + return (pblk + (block - blk)); + else + return (pblk - (blk - block)); } - nblk = (daddr_t)path->ep_index->ei_leaf_hi << 32 | - path->ep_index->ei_leaf_lo; - size = blksize(fs, ip, nblk); - if (path->ep_bp != NULL) { - brelse(path->ep_bp); - path->ep_bp = NULL; + /* Try to get block from index itself. */ + if (path[depth].ep_data) + return (path[depth].ep_blk); + } + + /* Use inode's group. */ + bg_start = (ip->i_block_group * EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) + + fs->e2fs->e2fs_first_dblock; + + return (bg_start + block); +} + +static int inline +ext4_can_extents_be_merged(struct ext4_extent *ex1, + struct ext4_extent *ex2) +{ + + if (ex1->e_blk + ex1->e_len != ex2->e_blk) + return (0); + + if (ex1->e_len + ex2->e_len > EXT4_MAX_LEN) + return (0); + + if (ext4_ext_extent_pblock(ex1) + ex1->e_len == + ext4_ext_extent_pblock(ex2)) + return (1); + + return (0); +} + +static unsigned +ext4_ext_next_leaf_block(struct inode *ip, struct ext4_extent_path *path) +{ + int depth = path->ep_depth; + + /* Empty tree */ + if (depth == 0) + return (EXT4_MAX_BLOCKS); + + /* Go to indexes. */ + depth--; + + while (depth >= 0) { + if (path[depth].ep_index != + EXT_LAST_INDEX(path[depth].ep_header)) + return (path[depth].ep_index[1].ei_blk); + + depth--; + } + + return (EXT4_MAX_BLOCKS); +} + +static int +ext4_ext_dirty(struct inode *ip, struct ext4_extent_path *path) +{ + struct m_ext2fs *fs; + struct buf *bp; + uint64_t blk; + int error; + + fs = ip->i_e2fs; + + if (!path) + return (EINVAL); + + if (path->ep_data) { + blk = path->ep_blk; + bp = getblk(ip->i_devvp, fsbtodb(fs, blk), + fs->e2fs_bsize, 0, 0, 0); + if (!bp) + return (EIO); + ext4_ext_fill_path_buf(path, bp); + error = bwrite(bp); + } else { + ip->i_flag |= IN_CHANGE | IN_UPDATE; + error = ext2_update(ip->i_vnode, 1); + } + + return (error); +} + +static int +ext4_ext_insert_index(struct inode *ip, struct ext4_extent_path *path, + uint32_t lblk, e4fs_daddr_t blk) +{ + struct m_ext2fs *fs; + struct ext4_extent_index *idx; + int len; + + fs = ip->i_e2fs; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***