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