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 <fs/ext2fs/ext2_extern.h>
 #include <fs/ext2fs/ext2_mount.h>
 
+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 <fs/ext2fs/ext2_extern.h>
 #include <fs/ext2fs/ext2_mount.h>
 
-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 <sys/bio.h>
 #include <sys/buf.h>
 #include <sys/conf.h>
+#include <sys/stat.h>
 
 #include <fs/ext2fs/ext2_mount.h>
 #include <fs/ext2fs/fs.h>
@@ -43,87 +44,163 @@
 #include <fs/ext2fs/ext2_extents.h>
 #include <fs/ext2fs/ext2_extern.h>
 
-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 ***
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to