#define BLKSIZE(a, b, c) blksize(a, b, c)
#define FS struct fs
#define I_FS i_fs
#define PGRD ffs_pgrd
#define PGRD_S "ffs_pgrd"
#define PGWR ffs_pgwr
#define PGWR_S "ffs_pgwr"
ffs_read(ap)
struct vop_read_args *ap;
{
register struct vnode *vp;
register struct inode *ip;
register struct uio *uio;
register FS *fs;
struct buf *bp = (struct buf *)0;
ufs_daddr_t lbn, nextlbn;
off_t bytesinfile;
long size, xfersize, blkoffset;
int devBlockSize=0;
int error;
u_short mode;
#if REV_ENDIAN_FS
int rev_endian=0;
#endif
vp = ap->a_vp;
ip = VTOI(vp);
mode = ip->i_mode;
uio = ap->a_uio;
#if REV_ENDIAN_FS
rev_endian=(vp->v_mount->mnt_flag & MNT_REVEND);
#endif
#if DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("ffs_read: invalid uio_rw = %x", uio->uio_rw);
if (vp->v_type == VLNK) {
if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
panic("ffs_read: short symlink = %d", ip->i_size);
} else if (vp->v_type != VREG && vp->v_type != VDIR)
panic("ffs_read: invalid v_type = %x", vp->v_type);
#endif
fs = ip->I_FS;
if (uio->uio_offset < 0)
return (EINVAL);
if (uio->uio_offset > fs->fs_maxfilesize)
return (EFBIG);
VOP_DEVBLOCKSIZE(ip->i_devvp, &devBlockSize);
if (UBCISVALID(vp)) {
error = cluster_read(vp, uio, (off_t)ip->i_size,
devBlockSize, 0);
} else {
for (error = 0, bp = NULL; uio->uio_resid > 0;
bp = NULL) {
if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
break;
lbn = lblkno(fs, uio->uio_offset);
nextlbn = lbn + 1;
size = BLKSIZE(fs, ip, lbn);
blkoffset = blkoff(fs, uio->uio_offset);
xfersize = fs->fs_bsize - blkoffset;
if (uio->uio_resid < xfersize)
xfersize = uio->uio_resid;
if (bytesinfile < xfersize)
xfersize = bytesinfile;
if (lblktosize(fs, nextlbn) >= ip->i_size)
error = bread(vp, lbn, size, NOCRED, &bp);
else if (lbn - 1 == vp->v_lastr && !(vp->v_flag & VRAOFF)) {
int nextsize = BLKSIZE(fs, ip, nextlbn);
error = breadn(vp, lbn,
size, &nextlbn, &nextsize, 1, NOCRED, &bp);
} else
error = bread(vp, lbn, size, NOCRED, &bp);
if (error)
break;
vp->v_lastr = lbn;
size -= bp->b_resid;
if (size < xfersize) {
if (size == 0)
break;
xfersize = size;
}
#if REV_ENDIAN_FS
if (rev_endian && S_ISDIR(mode)) {
byte_swap_dir_block_in((char *)bp->b_data + blkoffset, xfersize);
}
#endif
if (error =
uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio)) {
#if REV_ENDIAN_FS
if (rev_endian && S_ISDIR(mode)) {
byte_swap_dir_block_in((char *)bp->b_data + blkoffset, xfersize);
}
#endif
break;
}
#if REV_ENDIAN_FS
if (rev_endian && S_ISDIR(mode)) {
byte_swap_dir_out((char *)bp->b_data + blkoffset, xfersize);
}
#endif
if (S_ISREG(mode) && (xfersize + blkoffset == fs->fs_bsize ||
uio->uio_offset == ip->i_size))
bp->b_flags |= B_AGE;
brelse(bp);
}
}
if (bp != NULL)
brelse(bp);
ip->i_flag |= IN_ACCESS;
return (error);
}
ffs_write(ap)
struct vop_write_args *ap;
{
register struct vnode *vp;
register struct uio *uio;
register struct inode *ip;
register FS *fs;
struct buf *bp;
struct proc *p;
ufs_daddr_t lbn;
off_t osize;
int blkoffset, flags, ioflag, resid, rsd, size, xfersize;
int devBlockSize=0;
int save_error=0, save_size=0;
int blkalloc = 0;
int error = 0;
int file_extended = 0;
int doingdirectory = 0;
#if REV_ENDIAN_FS
int rev_endian=0;
#endif
ioflag = ap->a_ioflag;
uio = ap->a_uio;
vp = ap->a_vp;
ip = VTOI(vp);
#if REV_ENDIAN_FS
rev_endian=(vp->v_mount->mnt_flag & MNT_REVEND);
#endif
#if DIAGNOSTIC
if (uio->uio_rw != UIO_WRITE)
panic("ffs_write: uio_rw = %x\n", uio->uio_rw);
#endif
switch (vp->v_type) {
case VREG:
if (ioflag & IO_APPEND)
uio->uio_offset = ip->i_size;
if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size)
return (EPERM);
case VLNK:
break;
case VDIR:
doingdirectory = 1;
if ((ioflag & IO_SYNC) == 0)
panic("ffs_write: nonsync dir write");
break;
default:
panic("ffs_write: invalid v_type=%x", vp->v_type);
}
fs = ip->I_FS;
if (uio->uio_offset < 0 ||
(u_int64_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize)
return (EFBIG);
if (uio->uio_resid == 0)
return (0);
VOP_DEVBLOCKSIZE(ip->i_devvp, &devBlockSize);
p = uio->uio_procp;
if (vp->v_type == VREG && p &&
uio->uio_offset + uio->uio_resid >
p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
psignal(p, SIGXFSZ);
return (EFBIG);
}
resid = uio->uio_resid;
osize = ip->i_size;
flags = 0;
if ((ioflag & IO_SYNC) && !((vp)->v_mount->mnt_flag & MNT_ASYNC))
flags = B_SYNC;
if (UBCISVALID(vp)) {
off_t filesize;
off_t endofwrite;
off_t local_offset;
off_t head_offset;
int local_flags;
int first_block;
int fboff;
int fblk;
int loopcount;
endofwrite = uio->uio_offset + uio->uio_resid;
if (endofwrite > ip->i_size) {
filesize = endofwrite;
file_extended = 1;
} else
filesize = ip->i_size;
head_offset = ip->i_size;
rsd = uio->uio_resid;
local_offset = uio->uio_offset;
local_flags = 0;
if ((ioflag & IO_SYNC) && !((vp)->v_mount->mnt_flag & MNT_ASYNC))
local_flags = B_SYNC;
local_flags |= B_NOBUFF;
first_block = 1;
fboff = 0;
fblk = 0;
loopcount = 0;
for (error = 0; rsd > 0;) {
blkalloc = 0;
lbn = lblkno(fs, local_offset);
blkoffset = blkoff(fs, local_offset);
xfersize = fs->fs_bsize - blkoffset;
if (first_block)
fboff = blkoffset;
if (rsd < xfersize)
xfersize = rsd;
if (fs->fs_bsize > xfersize)
local_flags |= B_CLRBUF;
else
local_flags &= ~B_CLRBUF;
error = ffs_balloc(ip,
lbn, blkoffset + xfersize, ap->a_cred,
&bp, local_flags, &blkalloc);
if (error)
break;
if (first_block) {
fblk = blkalloc;
first_block = 0;
}
loopcount++;
rsd -= xfersize;
local_offset += (off_t)xfersize;
if (local_offset > ip->i_size)
ip->i_size = local_offset;
}
if(error) {
save_error = error;
save_size = rsd;
uio->uio_resid -= rsd;
if (file_extended)
filesize -= rsd;
}
flags = ioflag & IO_SYNC ? IO_SYNC : 0;
if((error == 0) && fblk && fboff) {
if( fblk > fs->fs_bsize)
panic("ffs_balloc : allocated more than bsize(head)");
head_offset = uio->uio_offset - (off_t)fboff ;
flags |= IO_HEADZEROFILL;
}
if((error == 0) && blkalloc && ((blkalloc - xfersize) > 0)) {
if( blkalloc > fs->fs_bsize)
panic("ffs_balloc : allocated more than bsize(tail)");
local_offset += (blkalloc - xfersize);
if (loopcount == 1) {
local_offset -= fboff;
}
flags |= IO_TAILZEROFILL;
}
error = cluster_write(vp, uio, osize, filesize, head_offset, local_offset, devBlockSize, flags);
if (uio->uio_offset > osize) {
if (error && ((ioflag & IO_UNIT)==0))
(void)VOP_TRUNCATE(vp, uio->uio_offset,
ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
ip->i_size = uio->uio_offset;
ubc_setsize(vp, (off_t)ip->i_size);
}
if(save_error) {
uio->uio_resid += save_size;
if(!error)
error = save_error;
}
ip->i_flag |= IN_CHANGE | IN_UPDATE;
} else {
flags = 0;
if ((ioflag & IO_SYNC) && !((vp)->v_mount->mnt_flag & MNT_ASYNC))
flags = B_SYNC;
for (error = 0; uio->uio_resid > 0;) {
lbn = lblkno(fs, uio->uio_offset);
blkoffset = blkoff(fs, uio->uio_offset);
xfersize = fs->fs_bsize - blkoffset;
if (uio->uio_resid < xfersize)
xfersize = uio->uio_resid;
if (fs->fs_bsize > xfersize)
flags |= B_CLRBUF;
else
flags &= ~B_CLRBUF;
error = ffs_balloc(ip,
lbn, blkoffset + xfersize, ap->a_cred, &bp, flags, 0);
if (error)
break;
if (uio->uio_offset + xfersize > ip->i_size) {
ip->i_size = uio->uio_offset + xfersize;
if (UBCISVALID(vp))
ubc_setsize(vp, (u_long)ip->i_size);
}
size = BLKSIZE(fs, ip, lbn) - bp->b_resid;
if (size < xfersize)
xfersize = size;
error =
uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
#if REV_ENDIAN_FS
if (rev_endian && S_ISDIR(ip->i_mode)) {
byte_swap_dir_out((char *)bp->b_data + blkoffset, xfersize);
}
#endif
if (doingdirectory == 0 && (ioflag & IO_SYNC))
(void)bwrite(bp);
else if (xfersize + blkoffset == fs->fs_bsize) {
bp->b_flags |= B_AGE;
bdwrite(bp);
}
else
bdwrite(bp);
if (error || xfersize == 0)
break;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
}
}
if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
ip->i_mode &= ~(ISUID | ISGID);
if (resid > uio->uio_resid)
VN_KNOTE(vp, NOTE_WRITE | (file_extended ? NOTE_EXTEND : 0));
if (error) {
if (ioflag & IO_UNIT) {
(void)VOP_TRUNCATE(vp, osize,
ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
uio->uio_offset -= resid - uio->uio_resid;
uio->uio_resid = resid;
}
} else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
error = VOP_UPDATE(vp, (struct timeval *)&time,
(struct timeval *)&time, 1);
return (error);
}
ffs_pagein(ap)
struct vop_pagein_args *ap;
{
register struct vnode *vp = ap->a_vp;
upl_t pl = ap->a_pl;
size_t size= ap->a_size;
off_t f_offset = ap->a_f_offset;
vm_offset_t pl_offset = ap->a_pl_offset;
int flags = ap->a_flags;
register struct inode *ip;
int devBlockSize=0;
int error;
ip = VTOI(vp);
if (UBCINVALID(vp))
panic("ffs_pagein: Not a VREG: vp=%x", vp);
if (UBCINFOMISSING(vp))
panic("ffs_pagein: No mapping: vp=%x", vp);
#if DIAGNOSTIC
if (vp->v_type == VLNK) {
if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
panic("%s: short symlink", "ffs_pagein");
} else if (vp->v_type != VREG && vp->v_type != VDIR)
panic("%s: type %d", "ffs_pagein", vp->v_type);
#endif
VOP_DEVBLOCKSIZE(ip->i_devvp, &devBlockSize);
error = cluster_pagein(vp, pl, pl_offset, f_offset, size,
(off_t)ip->i_size, devBlockSize, flags);
return (error);
}
ffs_pageout(ap)
struct vop_pageout_args *ap;
{
register struct vnode *vp = ap->a_vp;
upl_t pl = ap->a_pl;
size_t size= ap->a_size;
off_t f_offset = ap->a_f_offset;
vm_offset_t pl_offset = ap->a_pl_offset;
int flags = ap->a_flags;
register struct inode *ip;
register FS *fs;
int error ;
int devBlockSize=0;
size_t xfer_size = 0;
int local_flags=0;
off_t local_offset;
int resid, blkoffset;
size_t xsize, lsize;
daddr_t lbn;
int save_error =0, save_size=0;
vm_offset_t lupl_offset;
int nocommit = flags & UPL_NOCOMMIT;
struct buf *bp;
ip = VTOI(vp);
if (UBCINVALID(vp))
panic("ffs_pageout: Not a VREG: vp=%x", vp);
if (UBCINFOMISSING(vp))
panic("ffs_pageout: No mapping: vp=%x", vp);
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
if (!nocommit)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_FREE_ON_EMPTY);
return (EROFS);
}
fs = ip->I_FS;
if (f_offset < 0 || f_offset >= ip->i_size) {
if (!nocommit)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_FREE_ON_EMPTY);
return (EINVAL);
}
if (f_offset + size > ip->i_size)
xfer_size = ip->i_size - f_offset;
else
xfer_size = size;
VOP_DEVBLOCKSIZE(ip->i_devvp, &devBlockSize);
if (xfer_size & (PAGE_SIZE - 1)) {
xfer_size = (xfer_size + (devBlockSize - 1)) & ~(devBlockSize - 1);
}
local_flags = 0;
resid = xfer_size;
local_offset = f_offset;
for (error = 0; resid > 0;) {
lbn = lblkno(fs, local_offset);
blkoffset = blkoff(fs, local_offset);
xsize = fs->fs_bsize - blkoffset;
if (resid < xsize)
xsize = resid;
error = ffs_blkalloc(ip,
lbn, blkoffset + xsize, ap->a_cred,
local_flags);
if (error)
break;
resid -= xsize;
local_offset += (off_t)xsize;
}
if (error) {
save_size = resid;
save_error = error;
xfer_size -= save_size;
}
error = cluster_pageout(vp, pl, pl_offset, f_offset, round_page_32(xfer_size), ip->i_size, devBlockSize, flags);
if(save_error) {
lupl_offset = size - save_size;
resid = round_page_32(save_size);
if (!nocommit)
ubc_upl_abort_range(pl, lupl_offset, resid,
UPL_ABORT_FREE_ON_EMPTY);
if(!error)
error= save_error;
}
return (error);
}