Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/ext2fs/ext2_inode.c
Context not available. | |||||
#include <fs/ext2fs/ext2_extern.h> | #include <fs/ext2fs/ext2_extern.h> | ||||
#include <fs/ext2fs/ext2_extattr.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 | * Update the access, modified, and inode change times as specified by the | ||||
* IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. Write the inode | * IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. Write the inode | ||||
Context not available. | |||||
#define SINGLE 0 /* index of single indirect block */ | #define SINGLE 0 /* index of single indirect block */ | ||||
#define DOUBLE 1 /* index of double indirect block */ | #define DOUBLE 1 /* index of double indirect block */ | ||||
#define TRIPLE 2 /* index of triple 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 | * Truncate the inode oip to at most length size, freeing the | ||||
* disk blocks. | * disk blocks. | ||||
*/ | */ | ||||
int | static int | ||||
ext2_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred, | ext2_ind_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
struct vnode *ovp = vp; | struct vnode *ovp = vp; | ||||
Context not available. | |||||
bo = &ovp->v_bufobj; | bo = &ovp->v_bufobj; | ||||
#endif | #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; | fs = oip->i_e2fs; | ||||
osize = oip->i_size; | osize = oip->i_size; | ||||
/* | /* | ||||
Context not available. | |||||
return (allerror); | 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 | static int | ||||
ext2_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn, | ext2_ext_truncate(struct vnode *vp, off_t length, int flags, | ||||
daddr_t lastbn, int level, e4fs_daddr_t *countp) | struct ucred *cred, struct thread *td) | ||||
{ | { | ||||
struct vnode *ovp = vp; | |||||
int32_t lastblock; | |||||
struct m_ext2fs *fs; | |||||
struct inode *oip; | |||||
struct buf *bp; | struct buf *bp; | ||||
struct m_ext2fs *fs = ip->i_e2fs; | uint32_t lbn, offset; | ||||
struct vnode *vp; | int error, size; | ||||
e2fs_daddr_t *bap, *copy; | off_t osize; | ||||
int i, nblocks, error = 0, allerror = 0; | |||||
e2fs_lbn_t nb, nlbn, last; | |||||
e4fs_daddr_t blkcount, factor, blocksreleased = 0; | |||||
/* | oip = VTOI(ovp); | ||||
* Calculate index in current block of last | fs = oip->i_e2fs; | ||||
* block to be kept. -1 indicates the entire | osize = oip->i_size; | ||||
* block so we need not calculate the index. | |||||
*/ | if (osize < length) { | ||||
factor = 1; | if (length > oip->i_e2fs->e2fs_maxfilesize) { | ||||
for (i = SINGLE; i < level; i++) | return (EFBIG); | ||||
factor *= NINDIR(fs); | } | ||||
last = lastbn; | vnode_pager_setsize(ovp, length); | ||||
if (lastbn > 0) | offset = blkoff(fs, length - 1); | ||||
last /= factor; | lbn = lblkno(fs, length - 1); | ||||
nblocks = btodb(fs->e2fs_bsize); | flags |= BA_CLRBUF; | ||||
/* | error = ext2_balloc(oip, lbn, offset + 1, cred, &bp, flags); | ||||
* Get buffer of block pointers, zero those entries corresponding | if (error) { | ||||
* to blocks to be free'd, and update on disk copy first. Since | vnode_pager_setsize(vp, osize); | ||||
* double(triple) indirect before single(double) indirect, calls | return (error); | ||||
* to bmap on these blocks will fail. However, we already have | } | ||||
* the on disk address, so we have to set the b_blkno field | oip->i_size = length; | ||||
* explicitly instead of letting bread do everything for us. | if (bp->b_bufsize == fs->e2fs_bsize) | ||||
*/ | bp->b_flags |= B_CLUSTEROK; | ||||
vp = ITOV(ip); | if (flags & IO_SYNC) | ||||
bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0, 0); | bwrite(bp); | ||||
if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) { | else if (DOINGASYNC(ovp)) | ||||
bp->b_iocmd = BIO_READ; | bdwrite(bp); | ||||
if (bp->b_bcount > bp->b_bufsize) | else | ||||
panic("ext2_indirtrunc: bad buffer size"); | bawrite(bp); | ||||
bp->b_blkno = dbn; | oip->i_flag |= IN_CHANGE | IN_UPDATE; | ||||
vfs_busy_pages(bp, 0); | return (ext2_update(ovp, !DOINGASYNC(ovp))); | ||||
bp->b_iooffset = dbtob(bp->b_blkno); | |||||
bstrategy(bp); | |||||
error = bufwait(bp); | |||||
} | } | ||||
if (error) { | |||||
brelse(bp); | lastblock = (length + fs->e2fs_bsize - 1) / fs->e2fs_bsize; | ||||
*countp = 0; | error = ext4_ext_remove_space(oip, lastblock, flags, cred, td); | ||||
if (error) | |||||
return (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); | |||||
} else { | |||||
error = bwrite(bp); | |||||
if (error) | |||||
allerror = error; | |||||
} | |||||
bap = copy; | |||||
/* | offset = blkoff(fs, length); | ||||
* Recursively free totally unused blocks. | if (offset == 0) { | ||||
*/ | oip->i_size = length; | ||||
for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last; | } else { | ||||
i--, nlbn += factor) { | lbn = lblkno(fs, length); | ||||
nb = bap[i]; | flags |= BA_CLRBUF; | ||||
if (nb == 0) | error = ext2_balloc(oip, lbn, offset, cred, &bp, flags); | ||||
continue; | if (error) { | ||||
if (level > SINGLE) { | return (error); | ||||
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); | oip->i_size = length; | ||||
blocksreleased += nblocks; | 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); | |||||
} | } | ||||
/* | oip->i_size = osize; | ||||
* Recursively free last partial block. | error = vtruncbuf(ovp, cred, length, (int)fs->e2fs_bsize); | ||||
*/ | if (error) | ||||
if (level > SINGLE && lastbn >= 0) { | return (error); | ||||
last = lastbn % factor; | |||||
nb = bap[i]; | vnode_pager_setsize(ovp, length); | ||||
if (nb != 0) { | |||||
if ((error = ext2_indirtrunc(ip, nlbn, fsbtodb(fs, nb), | oip->i_size = length; | ||||
last, level - 1, &blkcount)) != 0) | oip->i_flag |= IN_CHANGE | IN_UPDATE; | ||||
allerror = error; | error = ext2_update(ovp, !DOINGASYNC(ovp)); | ||||
blocksreleased += blkcount; | |||||
} | 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); | if (ip->i_size == length) { | ||||
*countp = blocksreleased; | ip->i_flag |= IN_CHANGE | IN_UPDATE; | ||||
return (allerror); | 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); | |||||
} | } | ||||
/* | /* | ||||
Context not available. | |||||
if (ip->i_nlink <= 0) { | if (ip->i_nlink <= 0) { | ||||
ext2_extattr_free(ip); | ext2_extattr_free(ip); | ||||
error = ext2_truncate(vp, (off_t)0, 0, NOCRED, td); | 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; | mode = ip->i_mode; | ||||
ip->i_mode = 0; | ip->i_mode = 0; | ||||
ip->i_flag |= IN_CHANGE | IN_UPDATE; | ip->i_flag |= IN_CHANGE | IN_UPDATE; | ||||
Context not available. |