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:
parent
b541ba195c
commit
b394cd1e28
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324706
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
@ -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_ */
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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))
|
||||
|
@ -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 */
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user