#define BLKSIZE(a, b, c) blksize(a, b, c)
#define FS struct fs
#define I_FS i_fs
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_pager.h>
#include <vm/vm_map.h>
#include <vm/vnode_pager.h>
#include <sys/event.h>
#include <sys/vmmeter.h>
#include <sys/sysctl.h>
#include <vm/vm_page2.h>
#define VN_KNOTE(vp, b) KNOTE(&vp->v_pollinfo.vpi_kqinfo.ki_note, (b))
SYSCTL_DECL(_vfs_ffs);
int
ffs_read(struct vop_read_args *ap)
{
struct vnode *vp;
struct inode *ip;
struct uio *uio;
FS *fs;
struct buf *bp;
off_t bytesinfile;
int xfersize, blkoffset;
int error, orig_resid;
int seqcount;
int ioflag;
vp = ap->a_vp;
seqcount = ap->a_ioflag >> IO_SEQSHIFT;
ip = VTOI(vp);
uio = ap->a_uio;
ioflag = ap->a_ioflag;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("ffs_read: mode");
if (vp->v_type == VLNK) {
if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
panic("ffs_read: short symlink");
} else if (vp->v_type != VREG && vp->v_type != VDIR)
panic("ffs_read: type %d", vp->v_type);
#endif
fs = ip->I_FS;
if ((uint64_t)uio->uio_offset > fs->fs_maxfilesize)
return (EFBIG);
orig_resid = uio->uio_resid;
if (orig_resid <= 0)
return (0);
bytesinfile = ip->i_size - uio->uio_offset;
if (bytesinfile <= 0) {
if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
ip->i_flag |= IN_ACCESS;
return 0;
}
for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
break;
error = ffs_blkatoff_ra(vp, uio->uio_offset, NULL,
&bp, seqcount);
if (error)
break;
if (ioflag & IO_DIRECT)
bp->b_flags |= B_DIRECT;
KASSERT(bp->b_resid == 0, ("bp->b_resid != 0"));
blkoffset = blkoff(fs, uio->uio_offset);
xfersize = bp->b_bufsize - blkoffset;
if (xfersize > uio->uio_resid)
xfersize = uio->uio_resid;
if (xfersize > bytesinfile)
xfersize = bytesinfile;
if (xfersize <= 0) {
panic("ufs_readwrite: impossible xfersize: %d",
xfersize);
}
error = uiomovebp(bp, bp->b_data + blkoffset, xfersize, uio);
if (error)
break;
if ((ioflag & (IO_VMIO|IO_DIRECT)) &&
(LIST_FIRST(&bp->b_dep) == NULL)) {
bp->b_flags |= B_RELBUF;
brelse(bp);
} else {
bqrelse(bp);
}
}
if (bp != NULL) {
if ((ioflag & (IO_VMIO|IO_DIRECT)) &&
(LIST_FIRST(&bp->b_dep) == NULL)) {
bp->b_flags |= B_RELBUF;
brelse(bp);
} else {
bqrelse(bp);
}
}
if ((error == 0 || uio->uio_resid != orig_resid) &&
(vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
ip->i_flag |= IN_ACCESS;
return (error);
}
int
ffs_write(struct vop_write_args *ap)
{
struct vnode *vp;
struct uio *uio;
struct inode *ip;
FS *fs;
struct buf *bp;
ufs_daddr_t lbn;
off_t osize;
off_t nsize;
int seqcount;
int blkoffset, error, extended, flags, ioflag, resid, size, xfersize;
struct thread *td;
extended = 0;
seqcount = ap->a_ioflag >> IO_SEQSHIFT;
ioflag = ap->a_ioflag;
uio = ap->a_uio;
vp = ap->a_vp;
ip = VTOI(vp);
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_WRITE)
panic("ffs_write: mode");
#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:
panic("ffs_write: dir write");
break;
default:
panic("ffs_write: type %p %d (%d,%d)", vp, (int)vp->v_type,
(int)uio->uio_offset,
(int)uio->uio_resid
);
}
fs = ip->I_FS;
if (uio->uio_offset < 0 ||
(uint64_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize) {
return (EFBIG);
}
td = uio->uio_td;
if (vp->v_type == VREG && td && td->td_proc &&
uio->uio_offset + uio->uio_resid >
td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
return (EFBIG);
}
resid = uio->uio_resid;
osize = ip->i_size;
if (seqcount > B_SEQMAX)
flags = B_SEQMAX << B_SEQSHIFT;
else
flags = seqcount << B_SEQSHIFT;
if ((ioflag & IO_SYNC) && !DOINGASYNC(vp))
flags |= B_SYNC;
if (uio->uio_segflg == UIO_NOCOPY)
ip->i_flag |= IN_NOCOPYWRITE;
else
vclrflags(vp, VLASTWRITETS);
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 (uio->uio_offset + xfersize > ip->i_size) {
nsize = uio->uio_offset + xfersize;
nvnode_pager_setsize(vp, nsize,
blkoffresize(fs, nsize), blkoff(fs, nsize));
}
#if 0
if (uio->uio_segflg == UIO_NOCOPY) {
error = ffs_blkatoff(vp, uio->uio_offset, NULL, &bp);
if (error)
break;
bqrelse(bp);
}
#endif
if (xfersize < fs->fs_bsize || uio->uio_segflg == UIO_NOCOPY)
flags |= B_CLRBUF;
else
flags &= ~B_CLRBUF;
error = VOP_BALLOC(vp, uio->uio_offset, xfersize,
ap->a_cred, flags, &bp);
if (error != 0)
break;
if ((bp->b_flags & B_CACHE) == 0 && (flags & B_CLRBUF) == 0)
vfs_bio_clrbuf(bp);
if (ioflag & IO_DIRECT)
bp->b_flags |= B_DIRECT;
if ((ioflag & (IO_SYNC|IO_INVAL)) == (IO_SYNC|IO_INVAL))
bp->b_flags |= B_NOCACHE;
if (uio->uio_offset + xfersize > ip->i_size) {
ip->i_size = uio->uio_offset + xfersize;
extended = 1;
}
size = BLKSIZE(fs, ip, lbn) - bp->b_resid;
if (size < xfersize)
xfersize = size;
error = uiomovebp(bp, bp->b_data + blkoffset, xfersize, uio);
if ((ioflag & (IO_VMIO|IO_DIRECT)) &&
(LIST_FIRST(&bp->b_dep) == NULL)) {
bp->b_flags |= B_RELBUF;
}
if (ioflag & IO_SYNC) {
(void)bwrite(bp);
} else if (vm_paging_severe() ||
buf_dirty_count_severe() ||
(ioflag & IO_ASYNC)) {
bp->b_flags |= B_CLUSTEROK;
bawrite(bp);
} else if (xfersize + blkoffset == fs->fs_bsize) {
if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) {
bp->b_flags |= B_CLUSTEROK;
cluster_write(bp, (off_t)ip->i_size, fs->fs_bsize, seqcount);
} else {
bawrite(bp);
}
} else if (ioflag & IO_DIRECT) {
bp->b_flags |= B_CLUSTEROK;
bawrite(bp);
} else {
bp->b_flags |= B_CLUSTEROK;
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 | (extended ? NOTE_EXTEND : 0));
if (error) {
if (ioflag & IO_UNIT) {
(void)ffs_truncate(vp, osize, ioflag & IO_SYNC,
ap->a_cred);
uio->uio_offset -= resid - uio->uio_resid;
uio->uio_resid = resid;
}
} else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
error = ffs_update(vp, 1);
} else {
ufs_itimes(vp);
}
if (uio->uio_segflg == UIO_NOCOPY)
ip->i_flag &= ~IN_NOCOPYWRITE;
return (error);
}