Add inital extents read-write support.

Approved by:    pfg (mentor)
MFC after:      6 months
RelNotes:       Yes

Differential Revision:    https://reviews.freebsd.org/D12087
This commit is contained in:
Fedor Uporov 2017-10-17 20:45:44 +00:00
parent b541ba195c
commit b394cd1e28
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324706
14 changed files with 1925 additions and 531 deletions

View File

@ -135,19 +135,20 @@ ext2_alloc(struct inode *ip, daddr_t lbn, e4fs_daddr_t bpref, int size,
* 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 *cred, struct vnode **vpp)
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 *cred, struct vnode **vpp)
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 indx, e2fs_daddr_t *bap,
* 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);
}
/*

View File

@ -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, struct ucred *cred,
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
*/

View File

@ -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;
error = ext4_ext_find_extent(ip, lbn, &path);
if (error)
return (error);
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;
}
ep = path.ep_ext;
*bnp = fsbtodb(fs, lbn - ep->e_blk +
(ep->e_start_lo | (daddr_t)ep->e_start_hi << 32));
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;
}
out:
if (path.ep_bp != NULL)
brelse(path.ep_bp);
ext4_ext_path_free(path);
return (error);
}

View File

@ -612,7 +612,7 @@ ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
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 attrnamespace,
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);

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,10 @@
#include <sys/types.h>
#define EXT4_EXT_MAGIC 0xf30a
#define EXT4_MAX_BLOCKS 0xffffffff
#define EXT_INIT_MAX_LEN (1UL << 15)
#define EXT4_MAX_LEN (EXT_INIT_MAX_LEN - 1)
#define EXT4_EXT_DEPTH_MAX 5
#define EXT4_EXT_CACHE_NO 0
#define EXT4_EXT_CACHE_GAP 1
@ -82,23 +86,41 @@ struct ext4_extent_cache {
* Save path to some extent.
*/
struct ext4_extent_path {
int index_count;
uint16_t ep_depth;
struct buf *ep_bp;
bool ep_is_sparse;
union {
struct ext4_extent ep_sparse_ext;
struct ext4_extent *ep_ext;
};
uint64_t ep_blk;
char *ep_data;
struct ext4_extent *ep_ext;
struct ext4_extent_index *ep_index;
struct ext4_extent_header *ep_header;
};
#define EXT_FIRST_EXTENT(hdr) ((struct ext4_extent *)(((char *)(hdr)) + \
sizeof(struct ext4_extent_header)))
#define EXT_FIRST_INDEX(hdr) ((struct ext4_extent_index *)(((char *)(hdr)) + \
sizeof(struct ext4_extent_header)))
#define EXT_LAST_EXTENT(hdr) (EXT_FIRST_EXTENT((hdr)) + (hdr)->eh_ecount - 1)
#define EXT_LAST_INDEX(hdr) (EXT_FIRST_INDEX((hdr)) + (hdr)->eh_ecount - 1)
#define EXT4_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext4_extent_header) + \
(sizeof(struct ext4_extent) * (hdr)->eh_max))
#define EXT_HAS_FREE_INDEX(path) \
((path)->ep_header->eh_ecount < (path)->ep_header->eh_max)
#define EXT_MAX_EXTENT(hdr) (EXT_FIRST_EXTENT(hdr) + ((hdr)->eh_max) - 1)
#define EXT_MAX_INDEX(hdr) (EXT_FIRST_INDEX((hdr)) + (hdr)->eh_max - 1)
struct inode;
struct m_ext2fs;
void ext4_ext_tree_init(struct inode *ip);
int ext4_ext_in_cache(struct inode *, daddr_t, struct ext4_extent *);
void ext4_ext_put_cache(struct inode *, struct ext4_extent *, int);
struct ext4_extent_path *
ext4_ext_find_extent(struct m_ext2fs *fs,
struct inode *, daddr_t, struct ext4_extent_path *);
int ext4_ext_find_extent(struct inode *, daddr_t, struct ext4_extent_path **);
void ext4_ext_path_free(struct ext4_extent_path *path);
int ext4_ext_remove_space(struct inode *ip, off_t length, int flags,
struct ucred *cred, struct thread *td);
int ext4_ext_get_blocks(struct inode *ip, int64_t iblock,
unsigned long max_blocks, struct ucred *cred, struct buf **bpp, int *allocate, uint32_t *);
#ifdef EXT2FS_DEBUG
void ext4_ext_print_extent_tree_status(struct inode * ip);
#endif
#endif /* !_FS_EXT2FS_EXT2_EXTENTS_H_ */

View File

@ -51,7 +51,7 @@ struct vnode;
int ext2_add_entry(struct vnode *, struct ext2fs_direct_2 *);
int ext2_alloc(struct inode *, daddr_t, e4fs_daddr_t, int,
struct ucred *, e4fs_daddr_t *);
daddr_t ext2_allocfacl(struct inode *ip);
daddr_t ext2_alloc_meta(struct inode *ip);
int ext2_balloc(struct inode *,
e2fs_lbn_t, int, struct ucred *, struct buf **, int);
int ext2_blkatoff(struct vnode *, off_t, char **, struct buf **);
@ -60,6 +60,7 @@ e4fs_daddr_t ext2_blkpref(struct inode *, e2fs_lbn_t, int, e2fs_daddr_t *,
e2fs_daddr_t);
int ext2_bmap(struct vop_bmap_args *);
int ext2_bmaparray(struct vnode *, daddr_t, daddr_t *, int *, int *);
int ext4_bmapext(struct vnode *, int32_t, int64_t *, int *, int *);
void ext2_clusteracct(struct m_ext2fs *, char *, int, daddr_t, int);
void ext2_dirbad(struct inode *ip, doff_t offset, char *how);
void ext2_fserr(struct m_ext2fs *, uid_t, char *);
@ -101,6 +102,7 @@ int ext2_search_dirblock(struct inode *, void *, int *, const char *, int,
int *, doff_t *, doff_t *, doff_t *, struct ext2fs_searchslot *);
int ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev);
void ext2_gd_csum_set(struct m_ext2fs *fs);
void ext2_fserr(struct m_ext2fs *, uid_t, char *);
/* Flags to low-level allocation routines.

View File

@ -55,9 +55,6 @@
#include <fs/ext2fs/ext2_extern.h>
#include <fs/ext2fs/ext2_extattr.h>
static int ext2_indirtrunc(struct inode *, daddr_t, daddr_t,
daddr_t, int, e4fs_daddr_t *);
/*
* Update the access, modified, and inode change times as specified by the
* IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. Write the inode
@ -107,12 +104,123 @@ ext2_update(struct vnode *vp, int waitfor)
#define SINGLE 0 /* index of single indirect block */
#define DOUBLE 1 /* index of double indirect block */
#define TRIPLE 2 /* index of triple indirect block */
/*
* Release blocks associated with the inode ip and stored in the indirect
* block bn. Blocks are free'd in LIFO order up to (but not including)
* lastbn. If level is greater than SINGLE, the block is an indirect block
* and recursive calls to indirtrunc must be used to cleanse other indirect
* blocks.
*
* NB: triple indirect blocks are untested.
*/
static int
ext2_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn,
daddr_t lastbn, int level, e4fs_daddr_t *countp)
{
struct buf *bp;
struct m_ext2fs *fs = ip->i_e2fs;
struct vnode *vp;
e2fs_daddr_t *bap, *copy;
int i, nblocks, error = 0, allerror = 0;
e2fs_lbn_t nb, nlbn, last;
e4fs_daddr_t blkcount, factor, blocksreleased = 0;
/*
* Calculate index in current block of last
* block to be kept. -1 indicates the entire
* block so we need not calculate the index.
*/
factor = 1;
for (i = SINGLE; i < level; i++)
factor *= NINDIR(fs);
last = lastbn;
if (lastbn > 0)
last /= factor;
nblocks = btodb(fs->e2fs_bsize);
/*
* Get buffer of block pointers, zero those entries corresponding
* to blocks to be free'd, and update on disk copy first. Since
* double(triple) indirect before single(double) indirect, calls
* to bmap on these blocks will fail. However, we already have
* the on disk address, so we have to set the b_blkno field
* explicitly instead of letting bread do everything for us.
*/
vp = ITOV(ip);
bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0, 0);
if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) {
bp->b_iocmd = BIO_READ;
if (bp->b_bcount > bp->b_bufsize)
panic("ext2_indirtrunc: bad buffer size");
bp->b_blkno = dbn;
vfs_busy_pages(bp, 0);
bp->b_iooffset = dbtob(bp->b_blkno);
bstrategy(bp);
error = bufwait(bp);
}
if (error) {
brelse(bp);
*countp = 0;
return (error);
}
bap = (e2fs_daddr_t *)bp->b_data;
copy = malloc(fs->e2fs_bsize, M_TEMP, M_WAITOK);
bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->e2fs_bsize);
bzero((caddr_t)&bap[last + 1],
(NINDIR(fs) - (last + 1)) * sizeof(e2fs_daddr_t));
if (last == -1)
bp->b_flags |= B_INVAL;
if (DOINGASYNC(vp)) {
bdwrite(bp);
} else {
error = bwrite(bp);
if (error)
allerror = error;
}
bap = copy;
/*
* Recursively free totally unused blocks.
*/
for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
i--, nlbn += factor) {
nb = bap[i];
if (nb == 0)
continue;
if (level > SINGLE) {
if ((error = ext2_indirtrunc(ip, nlbn,
fsbtodb(fs, nb), (int32_t)-1, level - 1, &blkcount)) != 0)
allerror = error;
blocksreleased += blkcount;
}
ext2_blkfree(ip, nb, fs->e2fs_bsize);
blocksreleased += nblocks;
}
/*
* Recursively free last partial block.
*/
if (level > SINGLE && lastbn >= 0) {
last = lastbn % factor;
nb = bap[i];
if (nb != 0) {
if ((error = ext2_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
last, level - 1, &blkcount)) != 0)
allerror = error;
blocksreleased += blkcount;
}
}
free(copy, M_TEMP);
*countp = blocksreleased;
return (allerror);
}
/*
* Truncate the inode oip to at most length size, freeing the
* disk blocks.
*/
int
ext2_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred,
static int
ext2_ind_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred,
struct thread *td)
{
struct vnode *ovp = vp;
@ -136,26 +244,6 @@ ext2_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred,
bo = &ovp->v_bufobj;
#endif
ASSERT_VOP_LOCKED(vp, "ext2_truncate");
if (length < 0)
return (EINVAL);
if (ovp->v_type == VLNK &&
oip->i_size < ovp->v_mount->mnt_maxsymlinklen) {
#ifdef INVARIANTS
if (length != 0)
panic("ext2_truncate: partial truncate of symlink");
#endif
bzero((char *)&oip->i_shortlink, (u_int)oip->i_size);
oip->i_size = 0;
oip->i_flag |= IN_CHANGE | IN_UPDATE;
return (ext2_update(ovp, 1));
}
if (oip->i_size == length) {
oip->i_flag |= IN_CHANGE | IN_UPDATE;
return (ext2_update(ovp, 0));
}
fs = oip->i_e2fs;
osize = oip->i_size;
/*
@ -365,115 +453,131 @@ ext2_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred,
return (allerror);
}
/*
* Release blocks associated with the inode ip and stored in the indirect
* block bn. Blocks are free'd in LIFO order up to (but not including)
* lastbn. If level is greater than SINGLE, the block is an indirect block
* and recursive calls to indirtrunc must be used to cleanse other indirect
* blocks.
*
* NB: triple indirect blocks are untested.
*/
static int
ext2_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn,
daddr_t lastbn, int level, e4fs_daddr_t *countp)
ext2_ext_truncate(struct vnode *vp, off_t length, int flags,
struct ucred *cred, struct thread *td)
{
struct vnode *ovp = vp;
int32_t lastblock;
struct m_ext2fs *fs;
struct inode *oip;
struct buf *bp;
struct m_ext2fs *fs = ip->i_e2fs;
struct vnode *vp;
e2fs_daddr_t *bap, *copy;
int i, nblocks, error = 0, allerror = 0;
e2fs_lbn_t nb, nlbn, last;
e4fs_daddr_t blkcount, factor, blocksreleased = 0;
uint32_t lbn, offset;
int error, size;
off_t osize;
/*
* Calculate index in current block of last
* block to be kept. -1 indicates the entire
* block so we need not calculate the index.
*/
factor = 1;
for (i = SINGLE; i < level; i++)
factor *= NINDIR(fs);
last = lastbn;
if (lastbn > 0)
last /= factor;
nblocks = btodb(fs->e2fs_bsize);
/*
* Get buffer of block pointers, zero those entries corresponding
* to blocks to be free'd, and update on disk copy first. Since
* double(triple) indirect before single(double) indirect, calls
* to bmap on these blocks will fail. However, we already have
* the on disk address, so we have to set the b_blkno field
* explicitly instead of letting bread do everything for us.
*/
vp = ITOV(ip);
bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0, 0);
if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) {
bp->b_iocmd = BIO_READ;
if (bp->b_bcount > bp->b_bufsize)
panic("ext2_indirtrunc: bad buffer size");
bp->b_blkno = dbn;
vfs_busy_pages(bp, 0);
bp->b_iooffset = dbtob(bp->b_blkno);
bstrategy(bp);
error = bufwait(bp);
oip = VTOI(ovp);
fs = oip->i_e2fs;
osize = oip->i_size;
if (osize < length) {
if (length > oip->i_e2fs->e2fs_maxfilesize) {
return (EFBIG);
}
vnode_pager_setsize(ovp, length);
offset = blkoff(fs, length - 1);
lbn = lblkno(fs, length - 1);
flags |= BA_CLRBUF;
error = ext2_balloc(oip, lbn, offset + 1, cred, &bp, flags);
if (error) {
vnode_pager_setsize(vp, osize);
return (error);
}
oip->i_size = length;
if (bp->b_bufsize == fs->e2fs_bsize)
bp->b_flags |= B_CLUSTEROK;
if (flags & IO_SYNC)
bwrite(bp);
else if (DOINGASYNC(ovp))
bdwrite(bp);
else
bawrite(bp);
oip->i_flag |= IN_CHANGE | IN_UPDATE;
return (ext2_update(ovp, !DOINGASYNC(ovp)));
}
if (error) {
brelse(bp);
*countp = 0;
lastblock = (length + fs->e2fs_bsize - 1) / fs->e2fs_bsize;
error = ext4_ext_remove_space(oip, lastblock, flags, cred, td);
if (error)
return (error);
}
bap = (e2fs_daddr_t *)bp->b_data;
copy = malloc(fs->e2fs_bsize, M_TEMP, M_WAITOK);
bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->e2fs_bsize);
bzero((caddr_t)&bap[last + 1],
(NINDIR(fs) - (last + 1)) * sizeof(e2fs_daddr_t));
if (last == -1)
bp->b_flags |= B_INVAL;
if (DOINGASYNC(vp)) {
bdwrite(bp);
offset = blkoff(fs, length);
if (offset == 0) {
oip->i_size = length;
} else {
error = bwrite(bp);
if (error)
allerror = error;
}
bap = copy;
/*
* Recursively free totally unused blocks.
*/
for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
i--, nlbn += factor) {
nb = bap[i];
if (nb == 0)
continue;
if (level > SINGLE) {
if ((error = ext2_indirtrunc(ip, nlbn,
fsbtodb(fs, nb), (int32_t)-1, level - 1, &blkcount)) != 0)
allerror = error;
blocksreleased += blkcount;
lbn = lblkno(fs, length);
flags |= BA_CLRBUF;
error = ext2_balloc(oip, lbn, offset, cred, &bp, flags);
if (error) {
return (error);
}
ext2_blkfree(ip, nb, fs->e2fs_bsize);
blocksreleased += nblocks;
oip->i_size = length;
size = blksize(fs, oip, lbn);
bzero((char *)bp->b_data + offset, (u_int)(size - offset));
allocbuf(bp, size);
if (bp->b_bufsize == fs->e2fs_bsize)
bp->b_flags |= B_CLUSTEROK;
if (flags & IO_SYNC)
bwrite(bp);
else if (DOINGASYNC(ovp))
bdwrite(bp);
else
bawrite(bp);
}
/*
* Recursively free last partial block.
*/
if (level > SINGLE && lastbn >= 0) {
last = lastbn % factor;
nb = bap[i];
if (nb != 0) {
if ((error = ext2_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
last, level - 1, &blkcount)) != 0)
allerror = error;
blocksreleased += blkcount;
}
oip->i_size = osize;
error = vtruncbuf(ovp, cred, length, (int)fs->e2fs_bsize);
if (error)
return (error);
vnode_pager_setsize(ovp, length);
oip->i_size = length;
oip->i_flag |= IN_CHANGE | IN_UPDATE;
error = ext2_update(ovp, !DOINGASYNC(ovp));
return (error);
}
/*
* Truncate the inode ip to at most length size, freeing the
* disk blocks.
*/
int
ext2_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred,
struct thread *td)
{
struct inode *ip;
int error;
ASSERT_VOP_LOCKED(vp, "ext2_truncate");
if (length < 0)
return (EINVAL);
ip = VTOI(vp);
if (vp->v_type == VLNK &&
ip->i_size < vp->v_mount->mnt_maxsymlinklen) {
#ifdef INVARIANTS
if (length != 0)
panic("ext2_truncate: partial truncate of symlink");
#endif
bzero((char *)&ip->i_shortlink, (u_int)ip->i_size);
ip->i_size = 0;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
return (ext2_update(vp, 1));
}
free(copy, M_TEMP);
*countp = blocksreleased;
return (allerror);
if (ip->i_size == length) {
ip->i_flag |= IN_CHANGE | IN_UPDATE;
return (ext2_update(vp, 0));
}
if (ip->i_flag & IN_E4EXTENTS)
error = ext2_ext_truncate(vp, length, flags, cred, td);
else
error = ext2_ind_truncate(vp, length, flags, cred, td);
return (error);
}
/*
@ -495,7 +599,8 @@ ext2_inactive(struct vop_inactive_args *ap)
if (ip->i_nlink <= 0) {
ext2_extattr_free(ip);
error = ext2_truncate(vp, (off_t)0, 0, NOCRED, td);
ip->i_rdev = 0;
if (!(ip->i_flag & IN_E4EXTENTS))
ip->i_rdev = 0;
mode = ip->i_mode;
ip->i_mode = 0;
ip->i_flag |= IN_CHANGE | IN_UPDATE;

View File

@ -51,7 +51,7 @@ ext2_print_inode(struct inode *in)
printf("Inode: %5ju", (uintmax_t)in->i_number);
printf( /* "Inode: %5d" */
" Type: %10s Mode: 0x%o Flags: 0x%x Version: %d acl: 0x%llx\n",
" Type: %10s Mode: 0x%o Flags: 0x%x Version: %d acl: 0x%lx\n",
"n/a", in->i_mode, in->i_flags, in->i_gen, in->i_facl);
printf("User: %5u Group: %5u Size: %ju\n",
in->i_uid, in->i_gid, (uintmax_t)in->i_size);
@ -62,19 +62,22 @@ ext2_print_inode(struct inode *in)
printf("mtime: 0x%x", in->i_mtime);
if (E2DI_HAS_XTIME(in))
printf("crtime %#x ", in->i_birthtime);
printf("BLOCKS:");
for (i = 0; i < (in->i_blocks <= 24 ? (in->i_blocks + 1) / 2 : 12); i++)
printf(" %d", in->i_db[i]);
printf("\n");
printf("Extents:\n");
ehp = (struct ext4_extent_header *)in->i_db;
printf("Header (magic 0x%x entries %d max %d depth %d gen %d)\n",
ehp->eh_magic, ehp->eh_ecount, ehp->eh_max, ehp->eh_depth,
ehp->eh_gen);
ep = (struct ext4_extent *)(char *)(ehp + 1);
printf("Index (blk %d len %d start_lo %d start_hi %d)\n", ep->e_blk,
ep->e_len, ep->e_start_lo, ep->e_start_hi);
printf("\n");
if (in->i_flag & IN_E4EXTENTS) {
printf("Extents:\n");
ehp = (struct ext4_extent_header *)in->i_db;
printf("Header (magic 0x%x entries %d max %d depth %d gen %d)\n",
ehp->eh_magic, ehp->eh_ecount, ehp->eh_max, ehp->eh_depth,
ehp->eh_gen);
ep = (struct ext4_extent *)(char *)(ehp + 1);
printf("Index (blk %d len %d start_lo %d start_hi %d)\n", ep->e_blk,
ep->e_len, ep->e_start_lo, ep->e_start_hi);
printf("\n");
} else {
printf("BLOCKS:");
for (i = 0; i < (in->i_blocks <= 24 ? (in->i_blocks + 1) / 2 : 12); i++)
printf(" %d", in->i_db[i]);
printf("\n");
}
}
#endif /* EXT2FS_DEBUG */
@ -84,8 +87,6 @@ ext2_print_inode(struct inode *in)
void
ext2_ei2i(struct ext2fs_dinode *ei, struct inode *ip)
{
int i;
ip->i_nlink = ei->e2di_nlink;
/*
* Godmar thinks - if the link count is zero, then the inode is
@ -127,10 +128,7 @@ ext2_ei2i(struct ext2fs_dinode *ei, struct inode *ip)
ip->i_uid |= (uint32_t)ei->e2di_uid_high << 16;
ip->i_gid |= (uint32_t)ei->e2di_gid_high << 16;
for (i = 0; i < EXT2_NDADDR; i++)
ip->i_db[i] = ei->e2di_blocks[i];
for (i = 0; i < EXT2_NIADDR; i++)
ip->i_ib[i] = ei->e2di_blocks[EXT2_NDIR_BLOCKS + i];
memcpy(ip->i_data, ei->e2di_blocks, sizeof(ei->e2di_blocks));
}
/*
@ -140,7 +138,6 @@ int
ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei)
{
struct m_ext2fs *fs;
int i;
fs = ip->i_e2fs;
ei->e2di_mode = ip->i_mode;
@ -190,10 +187,7 @@ ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei)
ei->e2di_gid = ip->i_gid & 0xffff;
ei->e2di_gid_high = ip->i_gid >> 16 & 0xffff;
for (i = 0; i < EXT2_NDADDR; i++)
ei->e2di_blocks[i] = ip->i_db[i];
for (i = 0; i < EXT2_NIADDR; i++)
ei->e2di_blocks[EXT2_NDIR_BLOCKS + i] = ip->i_ib[i];
memcpy(ei->e2di_blocks, ip->i_data, sizeof(ei->e2di_blocks));
return (0);
}

View File

@ -66,63 +66,22 @@ ext2_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp)
struct m_ext2fs *fs;
struct buf *bp;
e2fs_lbn_t lbn;
int bsize, error;
daddr_t newblk;
struct ext4_extent *ep;
struct ext4_extent_path path;
int error, bsize;
ip = VTOI(vp);
fs = ip->i_e2fs;
lbn = lblkno(fs, offset);
bsize = blksize(fs, ip, lbn);
*bpp = NULL;
/*
* IN_E4EXTENTS requires special treatment as we can otherwise fall
* back to the normal path.
*/
if (!(ip->i_flag & IN_E4EXTENTS))
goto normal;
memset(&path, 0, sizeof(path));
if (ext4_ext_find_extent(fs, ip, lbn, &path) == NULL)
goto normal;
ep = path.ep_ext;
if (ep == NULL)
goto normal;
newblk = lbn - ep->e_blk +
(ep->e_start_lo | (daddr_t)ep->e_start_hi << 32);
if (path.ep_bp != NULL) {
brelse(path.ep_bp);
path.ep_bp = NULL;
}
error = bread(ip->i_devvp, fsbtodb(fs, newblk), bsize, NOCRED, &bp);
if (error != 0) {
if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) {
brelse(bp);
return (error);
}
if (res)
*res = (char *)bp->b_data + blkoff(fs, offset);
/*
* If IN_E4EXTENTS is enabled we would get a wrong offset so
* reset b_offset here.
*/
bp->b_offset = lbn * bsize;
*bpp = bp;
return (0);
normal:
if (*bpp == NULL) {
if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) {
brelse(bp);
return (error);
}
if (res)
*res = (char *)bp->b_data + blkoff(fs, offset);
*bpp = bp;
}
*bpp = bp;
return (0);
}

View File

@ -992,6 +992,7 @@ ext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
}
#ifdef EXT2FS_DEBUG
ext2_print_inode(ip);
ext4_ext_print_extent_tree_status(ip);
#endif
bqrelse(bp);

View File

@ -91,8 +91,6 @@
static int ext2_makeinode(int mode, struct vnode *, struct vnode **, struct componentname *);
static void ext2_itimes_locked(struct vnode *);
static int ext4_ext_read(struct vop_read_args *);
static int ext2_ind_read(struct vop_read_args *);
static vop_access_t ext2_access;
static int ext2_chmod(struct vnode *, int, struct ucred *, struct thread *);
@ -630,7 +628,8 @@ ext2_mknod(struct vop_mknod_args *ap)
* Want to be able to use this to make badblock
* inodes, so don't truncate the dev number.
*/
ip->i_rdev = vap->va_rdev;
if (!(ip->i_flag & IN_E4EXTENTS))
ip->i_rdev = vap->va_rdev;
}
/*
* Remove inode, then reload it through VFS_VGET so it is
@ -1542,7 +1541,12 @@ ext2_strategy(struct vop_strategy_args *ap)
if (vp->v_type == VBLK || vp->v_type == VCHR)
panic("ext2_strategy: spec");
if (bp->b_blkno == bp->b_lblkno) {
error = ext2_bmaparray(vp, bp->b_lblkno, &blkno, NULL, NULL);
if (VTOI(ap->a_vp)->i_flag & IN_E4EXTENTS)
error = ext4_bmapext(vp, bp->b_lblkno, &blkno, NULL, NULL);
else
error = ext2_bmaparray(vp, bp->b_lblkno, &blkno, NULL, NULL);
bp->b_blkno = blkno;
if (error) {
bp->b_error = error;
@ -1987,28 +1991,6 @@ ext2_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
*/
static int
ext2_read(struct vop_read_args *ap)
{
struct vnode *vp;
struct inode *ip;
int error;
vp = ap->a_vp;
ip = VTOI(vp);
/* EXT4_EXT_LOCK(ip); */
if (ip->i_flag & IN_E4EXTENTS)
error = ext4_ext_read(ap);
else
error = ext2_ind_read(ap);
/* EXT4_EXT_UNLOCK(ip); */
return (error);
}
/*
* Vnode op for reading.
*/
static int
ext2_ind_read(struct vop_read_args *ap)
{
struct vnode *vp;
struct inode *ip;
@ -2130,122 +2112,6 @@ ext2_ioctl(struct vop_ioctl_args *ap)
}
}
/*
* this function handles ext4 extents block mapping
*/
static int
ext4_ext_read(struct vop_read_args *ap)
{
static unsigned char zeroes[EXT2_MAX_BLOCK_SIZE];
struct vnode *vp;
struct inode *ip;
struct uio *uio;
struct m_ext2fs *fs;
struct buf *bp;
struct ext4_extent nex, *ep;
struct ext4_extent_path path;
daddr_t lbn, newblk;
off_t bytesinfile;
int cache_type;
ssize_t orig_resid;
int error;
long size, xfersize, blkoffset;
vp = ap->a_vp;
ip = VTOI(vp);
uio = ap->a_uio;
memset(&path, 0, sizeof(path));
orig_resid = uio->uio_resid;
KASSERT(orig_resid >= 0, ("%s: uio->uio_resid < 0", __func__));
if (orig_resid == 0)
return (0);
KASSERT(uio->uio_offset >= 0, ("%s: uio->uio_offset < 0", __func__));
fs = ip->i_e2fs;
if (uio->uio_offset < ip->i_size && uio->uio_offset >= fs->e2fs_maxfilesize)
return (EOVERFLOW);
while (uio->uio_resid > 0) {
if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
break;
lbn = lblkno(fs, uio->uio_offset);
size = blksize(fs, ip, lbn);
blkoffset = blkoff(fs, uio->uio_offset);
xfersize = fs->e2fs_fsize - blkoffset;
xfersize = MIN(xfersize, uio->uio_resid);
xfersize = MIN(xfersize, bytesinfile);
/* get block from ext4 extent cache */
cache_type = ext4_ext_in_cache(ip, lbn, &nex);
switch (cache_type) {
case EXT4_EXT_CACHE_NO:
ext4_ext_find_extent(fs, ip, lbn, &path);
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,
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);
if (path.ep_bp != NULL) {
brelse(path.ep_bp);
path.ep_bp = NULL;
}
break;
case EXT4_EXT_CACHE_GAP:
/* block has not been allocated yet */
break;
case EXT4_EXT_CACHE_IN:
newblk = lbn - nex.e_blk + (nex.e_start_lo |
(daddr_t)nex.e_start_hi << 32);
break;
default:
panic("%s: invalid cache type", __func__);
}
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, 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;
}
xfersize = size;
}
error = uiomove(bp->b_data + blkoffset, xfersize, uio);
bqrelse(bp);
if (error)
return (error);
}
}
return (0);
}
/*
* Vnode op for writing.
*/

View File

@ -385,7 +385,6 @@ struct ext2_gd {
/*
* Macro-instructions used to manage several block sizes
*/
#define EXT2_MAX_BLOCK_SIZE 4096
#define EXT2_MIN_BLOCK_LOG_SIZE 10
#define EXT2_BLOCK_SIZE(s) ((s)->e2fs_bsize)
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(uint32_t))

View File

@ -107,8 +107,13 @@ struct inode {
uint32_t i_gen; /* Generation number. */
uint64_t i_facl; /* EA block number. */
uint32_t i_flags; /* Status flags (chflags). */
uint32_t i_db[EXT2_NDADDR]; /* Direct disk blocks. */
uint32_t i_ib[EXT2_NIADDR]; /* Indirect disk blocks. */
union {
struct {
uint32_t i_db[EXT2_NDADDR]; /* Direct disk blocks. */
uint32_t i_ib[EXT2_NIADDR]; /* Indirect disk blocks. */
};
uint32_t i_data[EXT2_NDADDR + EXT2_NIADDR];
};
struct ext4_extent_cache i_ext_cache; /* cache for ext4 extent */
};