From 6787bc2ec83bf20a601e834517fbf1a42af42a4b Mon Sep 17 00:00:00 2001 From: Damjan Jovanovic Date: Wed, 6 Jan 2016 19:05:37 +0200 Subject: [PATCH] Implement reading sparse files from EXT4 filesystems. --- sys/fs/ext2fs/ext2_bmap.c | 20 ++++++++++-------- sys/fs/ext2fs/ext2_extents.c | 28 ++++++++++++++++++++++--- sys/fs/ext2fs/ext2_extents.h | 2 ++ sys/fs/ext2fs/ext2_vnops.c | 49 +++++++++++++++++++++++++++++--------------- 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/sys/fs/ext2fs/ext2_bmap.c b/sys/fs/ext2fs/ext2_bmap.c index 7d4a880..c742feb 100644 --- a/sys/fs/ext2fs/ext2_bmap.c +++ b/sys/fs/ext2fs/ext2_bmap.c @@ -112,15 +112,19 @@ ext4_bmapext(struct vnode *vp, int32_t bn, int64_t *bnp, int *runp, int *runb) *runb = 0; ext4_ext_find_extent(fs, ip, lbn, &path); - ep = path.ep_ext; - if (ep == NULL) - ret = EIO; + if (path.ep_is_sparse) + *bnp = -1; else { - *bnp = fsbtodb(fs, lbn - ep->e_blk + - (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32)); - - if (*bnp == 0) - *bnp = -1; + ep = path.ep_ext; + if (ep == NULL) + ret = EIO; + else { + *bnp = fsbtodb(fs, lbn - ep->e_blk + + (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32)); + + if (*bnp == 0) + *bnp = -1; + } } if (path.ep_bp != NULL) { diff --git a/sys/fs/ext2fs/ext2_extents.c b/sys/fs/ext2fs/ext2_extents.c index 68704bb..4e077ff 100644 --- a/sys/fs/ext2fs/ext2_extents.c +++ b/sys/fs/ext2fs/ext2_extents.c @@ -66,13 +66,14 @@ static void ext4_ext_binsearch(struct inode *ip, struct ext4_extent_path *path, daddr_t lbn) { struct ext4_extent_header *ehp = path->ep_header; - struct ext4_extent *l, *r, *m; + struct ext4_extent *first, *l, *r, *m; if (ehp->eh_ecount == 0) return; - l = (struct ext4_extent *)(char *)(ehp + 1); - r = (struct ext4_extent *)(char *)(ehp + 1) + ehp->eh_ecount - 1; + 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) @@ -81,7 +82,27 @@ ext4_ext_binsearch(struct inode *ip, struct ext4_extent_path *path, daddr_t lbn) l = m + 1; } + if (l == first) + { + path->ep_sparse_ext.e_blk = lbn; + path->ep_sparse_ext.e_len = first->e_blk - lbn; + path->ep_sparse_ext.e_start_hi = 0; + path->ep_sparse_ext.e_start_lo = 0; + path->ep_is_sparse = 1; + return; + } path->ep_ext = l - 1; + if (path->ep_ext->e_blk + path->ep_ext->e_len <= lbn) + { + path->ep_sparse_ext.e_blk = lbn; + if (l <= (first + ehp->eh_ecount - 1)) + path->ep_sparse_ext.e_len = l->e_blk - lbn; + else + path->ep_sparse_ext.e_len = 1; // FIXME: where does it end? + path->ep_sparse_ext.e_start_hi = 0; + path->ep_sparse_ext.e_start_lo = 0; + path->ep_is_sparse = 1; + } } /* @@ -169,6 +190,7 @@ ext4_ext_find_extent(struct m_ext2fs *fs, struct inode *ip, path->ep_depth = i; path->ep_ext = NULL; path->ep_index = NULL; + path->ep_is_sparse = 0; ext4_ext_binsearch(ip, path, lbn); return (path); diff --git a/sys/fs/ext2fs/ext2_extents.h b/sys/fs/ext2fs/ext2_extents.h index 94ded83..856ceec 100644 --- a/sys/fs/ext2fs/ext2_extents.h +++ b/sys/fs/ext2fs/ext2_extents.h @@ -84,6 +84,8 @@ struct ext4_extent_cache { struct ext4_extent_path { uint16_t ep_depth; struct buf *ep_bp; + int ep_is_sparse; + struct ext4_extent ep_sparse_ext; /* either ep_sparse_ext (if ep_is_sparse) or ep_ext is valid */ struct ext4_extent *ep_ext; struct ext4_extent_index *ep_index; struct ext4_extent_header *ep_header; diff --git a/sys/fs/ext2fs/ext2_vnops.c b/sys/fs/ext2fs/ext2_vnops.c index 5b8990ee..f7fed62 100644 --- a/sys/fs/ext2fs/ext2_vnops.c +++ b/sys/fs/ext2fs/ext2_vnops.c @@ -1787,6 +1787,7 @@ ext2_ioctl(struct vop_ioctl_args *ap) static int ext4_ext_read(struct vop_read_args *ap) { + static unsigned char zeroes[4096]; struct vnode *vp; struct inode *ip; struct uio *uio; @@ -1831,11 +1832,14 @@ ext4_ext_read(struct vop_read_args *ap) switch (cache_type) { case EXT4_EXT_CACHE_NO: ext4_ext_find_extent(fs, ip, lbn, &path); - ep = path.ep_ext; + if (path.ep_is_sparse) + ep = &path.ep_sparse_ext; + else + ep = path.ep_ext; if (ep == NULL) return (EIO); - ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN); + ext4_ext_put_cache(ip, ep, path.ep_is_sparse ? EXT4_EXT_CACHE_GAP : EXT4_EXT_CACHE_IN); newblk = lbn - ep->e_blk + (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32); @@ -1848,7 +1852,7 @@ ext4_ext_read(struct vop_read_args *ap) case EXT4_EXT_CACHE_GAP: /* block has not been allocated yet */ - return (0); + break; case EXT4_EXT_CACHE_IN: newblk = lbn - nex.e_blk + (nex.e_start_lo | @@ -1859,24 +1863,35 @@ ext4_ext_read(struct vop_read_args *ap) panic("%s: invalid cache type", __func__); } - error = bread(ip->i_devvp, fsbtodb(fs, newblk), size, NOCRED, &bp); - if (error) { - brelse(bp); - return (error); + if (cache_type == EXT4_EXT_CACHE_GAP || (cache_type == EXT4_EXT_CACHE_NO && path.ep_is_sparse)) + { + if (xfersize > sizeof(zeroes)) + xfersize = sizeof(zeroes); + error = uiomove(zeroes, (int)xfersize, uio); + if (error) + return (error); } + else + { + error = bread(ip->i_devvp, fsbtodb(fs, newblk), size, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } - size -= bp->b_resid; - if (size < xfersize) { - if (size == 0) { - bqrelse(bp); - break; + size -= bp->b_resid; + if (size < xfersize) { + if (size == 0) { + bqrelse(bp); + break; + } + xfersize = size; } - xfersize = size; + error = uiomove(bp->b_data + blkoffset, (int)xfersize, uio); + bqrelse(bp); + if (error) + return (error); } - error = uiomove(bp->b_data + blkoffset, (int)xfersize, uio); - bqrelse(bp); - if (error) - return (error); } return (0); -- 2.6.3