svn commit: r324706 - head/sys/fs/ext2fs

Fedor Uporov fsu at FreeBSD.org
Tue Oct 17 20:45:46 UTC 2017


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 ***


More information about the svn-src-head mailing list