0b111bdfcb
Basic support for extents was implemented by Zheng Liu as part of his Google Summer of Code in 2010. This support is read-only at this time. In addition to extents we also support the huge_file extension for read-only purposes. This works nicely with the additional support for birthtime/nanosec timestamps and dir_index that have been added lately. The implementation may not work for all ext4 filesystems as it doesn't support some features that are being enabled by default on recent linux like flex_bg. Nevertheless, the feature should be very useful for migration or simple access in filesystems that have been converted from ext2/3 or don't use incompatible features. Special thanks to Zheng Liu for his dedication and continued work to support ext2 in FreeBSD. Submitted by: Zheng Liu (lz@) Reviewed by: Mike Ma, Christoph Mallon (previous version) Sponsored by: Google Inc. MFC after: 3 weeks
178 lines
4.6 KiB
C
178 lines
4.6 KiB
C
/*-
|
|
* Copyright (c) 2010 Zheng Liu <lz@freebsd.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/bio.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/conf.h>
|
|
|
|
#include <fs/ext2fs/ext2_mount.h>
|
|
#include <fs/ext2fs/fs.h>
|
|
#include <fs/ext2fs/inode.h>
|
|
#include <fs/ext2fs/ext2fs.h>
|
|
#include <fs/ext2fs/ext2_extents.h>
|
|
#include <fs/ext2fs/ext2_extern.h>
|
|
|
|
static void ext4_ext_binsearch_index(struct inode *ip, struct ext4_extent_path
|
|
*path, daddr_t lbn)
|
|
{
|
|
struct ext4_extent_header *ehp = path->ep_header;
|
|
struct ext4_extent_index *l, *r, *m;
|
|
|
|
l = (struct ext4_extent_index *)(char *)(ehp + 1);
|
|
r = (struct ext4_extent_index *)(char *)(ehp + 1) + ehp->eh_ecount - 1;
|
|
while (l <= r) {
|
|
m = l + (r - l) / 2;
|
|
if (lbn < m->ei_blk)
|
|
r = m - 1;
|
|
else
|
|
l = m + 1;
|
|
}
|
|
|
|
path->ep_index = l - 1;
|
|
}
|
|
|
|
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;
|
|
|
|
if (ehp->eh_ecount == 0)
|
|
return;
|
|
|
|
l = (struct ext4_extent *)(char *)(ehp + 1);
|
|
r = (struct ext4_extent *)(char *)(ehp + 1) + ehp->eh_ecount - 1;
|
|
while (l <= r) {
|
|
m = l + (r - l) / 2;
|
|
if (lbn < m->e_blk)
|
|
r = m - 1;
|
|
else
|
|
l = m + 1;
|
|
}
|
|
|
|
path->ep_ext = l - 1;
|
|
}
|
|
|
|
/*
|
|
* Find a block in ext4 extent cache.
|
|
*/
|
|
int
|
|
ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep)
|
|
{
|
|
struct ext4_extent_cache *ecp;
|
|
int ret = EXT4_EXT_CACHE_NO;
|
|
|
|
ecp = &ip->i_ext_cache;
|
|
|
|
/* cache is invalid */
|
|
if (ecp->ec_type == EXT4_EXT_CACHE_NO)
|
|
return (ret);
|
|
|
|
if (lbn >= ecp->ec_blk && lbn < ecp->ec_blk + ecp->ec_len) {
|
|
ep->e_blk = ecp->ec_blk;
|
|
ep->e_start_lo = ecp->ec_start & 0xffffffff;
|
|
ep->e_start_hi = ecp->ec_start >> 32 & 0xffff;
|
|
ep->e_len = ecp->ec_len;
|
|
ret = ecp->ec_type;
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Put an ext4_extent structure in ext4 cache.
|
|
*/
|
|
void
|
|
ext4_ext_put_cache(struct inode *ip, struct ext4_extent *ep, int type)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
struct vnode *vp;
|
|
struct ext4_extent_header *ehp;
|
|
uint16_t i;
|
|
int error, size;
|
|
daddr_t nblk;
|
|
|
|
vp = ITOV(ip);
|
|
ehp = (struct ext4_extent_header *)(char *)ip->i_db;
|
|
|
|
if (ehp->eh_magic != EXT4_EXT_MAGIC)
|
|
return (NULL);
|
|
|
|
path->ep_header = ehp;
|
|
|
|
for (i = ehp->eh_depth; i != 0; --i) {
|
|
ext4_ext_binsearch_index(ip, path, lbn);
|
|
path->ep_depth = 0;
|
|
path->ep_ext = NULL;
|
|
|
|
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;
|
|
}
|
|
error = bread(ip->i_devvp, fsbtodb(fs, nblk), size, NOCRED,
|
|
&path->ep_bp);
|
|
if (error) {
|
|
brelse(path->ep_bp);
|
|
path->ep_bp = NULL;
|
|
return (NULL);
|
|
}
|
|
ehp = (struct ext4_extent_header *)path->ep_bp->b_data;
|
|
path->ep_header = ehp;
|
|
}
|
|
|
|
path->ep_depth = i;
|
|
path->ep_ext = NULL;
|
|
path->ep_index = NULL;
|
|
|
|
ext4_ext_binsearch(ip, path, lbn);
|
|
return (path);
|
|
}
|