Module Name: src Committed By: riastradh Date: Fri Mar 27 17:27:56 UTC 2015
Modified Files: src/sys/ufs/chfs: chfs_vnops.c src/sys/ufs/ext2fs: ext2fs_extern.h ext2fs_lookup.c ext2fs_readwrite.c ext2fs_rename.c ext2fs_vfsops.c ext2fs_vnops.c src/sys/ufs/ffs: ffs_extern.h ffs_vfsops.c src/sys/ufs/lfs: lfs_extern.h lfs_rename.c lfs_vnops.c ulfs_extern.h ulfs_lookup.c ulfs_readwrite.c ulfs_vnops.c src/sys/ufs/ufs: ufs_extern.h ufs_lookup.c ufs_readwrite.c ufs_rename.c ufs_vnops.c ufsmount.h Log Message: Disentangle buffer-cached I/O from page-cached I/O in UFS. Page-cached I/O is used for regular files, and is initiated by VFS users such as userland and NFS. Buffer-cached I/O is used for directories and symlinks, and is issued only internally by UFS. New UFS routine ufs_bufio replaces vn_rdwr for internal use. ufs_bufio is implemented by new UFS operations uo_bufrd/uo_bufwr, which sit in ufs_readwrite.c alongside the VOP_READ/VOP_WRITE implementations. I preserved the code as much as possible and will leave further simplification for future commits. I kept the ulfs_readwrite.c copypasta close to ufs_readwrite.c in case we ever want to merge them back; likewise ext2fs_readwrite.c. No externally visible semantic change. All atf fs tests still pass. To generate a diff of this commit: cvs rdiff -u -r1.24 -r1.25 src/sys/ufs/chfs/chfs_vnops.c cvs rdiff -u -r1.47 -r1.48 src/sys/ufs/ext2fs/ext2fs_extern.h cvs rdiff -u -r1.77 -r1.78 src/sys/ufs/ext2fs/ext2fs_lookup.c cvs rdiff -u -r1.66 -r1.67 src/sys/ufs/ext2fs/ext2fs_readwrite.c cvs rdiff -u -r1.7 -r1.8 src/sys/ufs/ext2fs/ext2fs_rename.c cvs rdiff -u -r1.191 -r1.192 src/sys/ufs/ext2fs/ext2fs_vfsops.c cvs rdiff -u -r1.115 -r1.116 src/sys/ufs/ext2fs/ext2fs_vnops.c cvs rdiff -u -r1.81 -r1.82 src/sys/ufs/ffs/ffs_extern.h cvs rdiff -u -r1.325 -r1.326 src/sys/ufs/ffs/ffs_vfsops.c cvs rdiff -u -r1.101 -r1.102 src/sys/ufs/lfs/lfs_extern.h cvs rdiff -u -r1.7 -r1.8 src/sys/ufs/lfs/lfs_rename.c \ src/sys/ufs/lfs/ulfs_readwrite.c cvs rdiff -u -r1.269 -r1.270 src/sys/ufs/lfs/lfs_vnops.c cvs rdiff -u -r1.13 -r1.14 src/sys/ufs/lfs/ulfs_extern.h cvs rdiff -u -r1.21 -r1.22 src/sys/ufs/lfs/ulfs_lookup.c \ src/sys/ufs/lfs/ulfs_vnops.c cvs rdiff -u -r1.78 -r1.79 src/sys/ufs/ufs/ufs_extern.h cvs rdiff -u -r1.132 -r1.133 src/sys/ufs/ufs/ufs_lookup.c cvs rdiff -u -r1.107 -r1.108 src/sys/ufs/ufs/ufs_readwrite.c cvs rdiff -u -r1.11 -r1.12 src/sys/ufs/ufs/ufs_rename.c cvs rdiff -u -r1.225 -r1.226 src/sys/ufs/ufs/ufs_vnops.c cvs rdiff -u -r1.42 -r1.43 src/sys/ufs/ufs/ufsmount.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/ufs/chfs/chfs_vnops.c diff -u src/sys/ufs/chfs/chfs_vnops.c:1.24 src/sys/ufs/chfs/chfs_vnops.c:1.25 --- src/sys/ufs/chfs/chfs_vnops.c:1.24 Sun Jan 11 17:29:57 2015 +++ src/sys/ufs/chfs/chfs_vnops.c Fri Mar 27 17:27:55 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: chfs_vnops.c,v 1.24 2015/01/11 17:29:57 hannken Exp $ */ +/* $NetBSD: chfs_vnops.c,v 1.25 2015/03/27 17:27:55 riastradh Exp $ */ /*- * Copyright (c) 2010 Department of Software Engineering, @@ -1310,9 +1310,8 @@ chfs_symlink(void *v) uvm_vnp_setsize(vp, len); } else { - err = vn_rdwr(UIO_WRITE, vp, target, len, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED, cnp->cn_cred, - (size_t *)0, NULL); + err = ufs_bufio(UIO_WRITE, vp, target, len, (off_t)0, + IO_NODELOCKED, cnp->cn_cred, (size_t *)0, NULL); } out: @@ -1454,7 +1453,7 @@ chfs_readlink(void *v) return (0); } - return (VOP_READ(vp, uio, 0, cred)); + return (UFS_BUFRD(vp, uio, 0, cred)); } /* --------------------------------------------------------------------- */ Index: src/sys/ufs/ext2fs/ext2fs_extern.h diff -u src/sys/ufs/ext2fs/ext2fs_extern.h:1.47 src/sys/ufs/ext2fs/ext2fs_extern.h:1.48 --- src/sys/ufs/ext2fs/ext2fs_extern.h:1.47 Sun May 25 14:07:19 2014 +++ src/sys/ufs/ext2fs/ext2fs_extern.h Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ext2fs_extern.h,v 1.47 2014/05/25 14:07:19 hannken Exp $ */ +/* $NetBSD: ext2fs_extern.h,v 1.48 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -147,6 +147,8 @@ void ext2fs_set_inode_guid(struct inode /* ext2fs_readwrite.c */ int ext2fs_read(void *); int ext2fs_write(void *); +int ext2fs_bufrd(struct vnode *, struct uio *, int, kauth_cred_t); +int ext2fs_bufwr(struct vnode *, struct uio *, int, kauth_cred_t); /* ext2fs_vnops.c */ int ext2fs_create(void *); Index: src/sys/ufs/ext2fs/ext2fs_lookup.c diff -u src/sys/ufs/ext2fs/ext2fs_lookup.c:1.77 src/sys/ufs/ext2fs/ext2fs_lookup.c:1.78 --- src/sys/ufs/ext2fs/ext2fs_lookup.c:1.77 Tue Jun 3 19:30:29 2014 +++ src/sys/ufs/ext2fs/ext2fs_lookup.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ext2fs_lookup.c,v 1.77 2014/06/03 19:30:29 joerg Exp $ */ +/* $NetBSD: ext2fs_lookup.c,v 1.78 2015/03/27 17:27:56 riastradh Exp $ */ /* * Modified for NetBSD 1.2E @@ -48,7 +48,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ext2fs_lookup.c,v 1.77 2014/06/03 19:30:29 joerg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ext2fs_lookup.c,v 1.78 2015/03/27 17:27:56 riastradh Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -180,7 +180,7 @@ ext2fs_readdir(void *v) } aiov.iov_base = dirbuf; - error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); + error = UFS_BUFRD(ap->a_vp, &auio, 0, ap->a_cred); if (error == 0) { readcnt = e2fs_count - auio.uio_resid; for (dp = (struct ext2fs_direct *)dirbuf; @@ -952,8 +952,8 @@ ext2fs_dirempty(struct inode *ip, ino_t #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2) for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) { - error = vn_rdwr(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, off, - UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL); + error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, + off, IO_NODELOCKED, cred, &count, NULL); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. Index: src/sys/ufs/ext2fs/ext2fs_readwrite.c diff -u src/sys/ufs/ext2fs/ext2fs_readwrite.c:1.66 src/sys/ufs/ext2fs/ext2fs_readwrite.c:1.67 --- src/sys/ufs/ext2fs/ext2fs_readwrite.c:1.66 Sun Nov 9 18:23:28 2014 +++ src/sys/ufs/ext2fs/ext2fs_readwrite.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ext2fs_readwrite.c,v 1.66 2014/11/09 18:23:28 maxv Exp $ */ +/* $NetBSD: ext2fs_readwrite.c,v 1.67 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 1993 @@ -60,7 +60,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ext2fs_readwrite.c,v 1.66 2014/11/09 18:23:28 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ext2fs_readwrite.c,v 1.67 2015/03/27 17:27:56 riastradh Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -97,13 +97,9 @@ ext2fs_read(void *v) struct vnode *vp; struct inode *ip; struct uio *uio; - struct m_ext2fs *fs; - struct buf *bp; struct ufsmount *ump; vsize_t bytelen; - daddr_t lbn, nextlbn; - off_t bytesinfile; - long size, xfersize, blkoffset; + int advice; int error; vp = ap->a_vp; @@ -123,7 +119,10 @@ ext2fs_read(void *v) } else if (vp->v_type != VREG && vp->v_type != VDIR) panic("%s: type %d", "ext2fs_read", vp->v_type); #endif - fs = ip->i_e2fs; + /* XXX Eliminate me by refusing directory reads from userland. */ + if (vp->v_type == VDIR) + return ext2fs_bufrd(vp, uio, ap->a_ioflag, ap->a_cred); + if ((uint64_t)uio->uio_offset > ump->um_maxfilesize) return (EFBIG); if (uio->uio_resid == 0) @@ -131,22 +130,64 @@ ext2fs_read(void *v) if (uio->uio_offset >= ext2fs_size(ip)) goto out; - if (vp->v_type == VREG) { - const int advice = IO_ADV_DECODE(ap->a_ioflag); - - while (uio->uio_resid > 0) { - bytelen = MIN(ext2fs_size(ip) - uio->uio_offset, + KASSERT(vp->v_type == VREG); + advice = IO_ADV_DECODE(ap->a_ioflag); + while (uio->uio_resid > 0) { + bytelen = MIN(ext2fs_size(ip) - uio->uio_offset, uio->uio_resid); - if (bytelen == 0) - break; + if (bytelen == 0) + break; - error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, - UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); - if (error) - break; - } - goto out; + error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, + UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); + if (error) + break; + } + +out: + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { + ip->i_flag |= IN_ACCESS; + if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) + error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT); } + return (error); +} + +/* + * UFS op for reading via the buffer cache + */ +int +ext2fs_bufrd(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t cred) +{ + struct inode *ip; + struct ufsmount *ump; + struct m_ext2fs *fs; + struct buf *bp; + off_t bytesinfile; + daddr_t lbn, nextlbn; + long size, xfersize, blkoffset; + int error; + + KASSERT(uio->uio_rw == UIO_READ); + KASSERT(VOP_ISLOCKED(vp)); + KASSERT(vp->v_type == VDIR || vp->v_type == VLNK); + + ip = VTOI(vp); + ump = ip->i_ump; + fs = ip->i_e2fs; + error = 0; + + KASSERT(vp->v_type != VLNK || + ext2fs_size(ip) >= ump->um_maxsymlinklen); + KASSERT(vp->v_type != VLNK || ump->um_maxsymlinklen != 0 || + ext2fs_nblock(ip) != 0); + + if (uio->uio_offset > ump->um_maxfilesize) + return EFBIG; + if (uio->uio_resid == 0) + return 0; + if (uio->uio_offset >= ext2fs_size(ip)) + goto out; for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { bytesinfile = ext2fs_size(ip) - uio->uio_offset; @@ -196,7 +237,7 @@ ext2fs_read(void *v) out: if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { ip->i_flag |= IN_ACCESS; - if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) + if ((ioflag & IO_SYNC) == IO_SYNC) error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT); } return (error); @@ -218,11 +259,9 @@ ext2fs_write(void *v) struct uio *uio; struct inode *ip; struct m_ext2fs *fs; - struct buf *bp; struct ufsmount *ump; - daddr_t lbn; off_t osize; - int blkoffset, error, flags, ioflag, resid, xfersize; + int blkoffset, error, ioflag, resid; vsize_t bytelen; off_t oldoff = 0; /* XXX */ bool async; @@ -271,58 +310,124 @@ ext2fs_write(void *v) resid = uio->uio_resid; osize = ext2fs_size(ip); - if (vp->v_type == VREG) { - while (uio->uio_resid > 0) { - oldoff = uio->uio_offset; - blkoffset = ext2_blkoff(fs, uio->uio_offset); - bytelen = MIN(fs->e2fs_bsize - blkoffset, - uio->uio_resid); + KASSERT(vp->v_type == VREG); + while (uio->uio_resid > 0) { + oldoff = uio->uio_offset; + blkoffset = ext2_blkoff(fs, uio->uio_offset); + bytelen = MIN(fs->e2fs_bsize - blkoffset, uio->uio_resid); - if (vp->v_size < oldoff + bytelen) { - uvm_vnp_setwritesize(vp, oldoff + bytelen); - } - error = ufs_balloc_range(vp, uio->uio_offset, - bytelen, ap->a_cred, 0); - if (error) - break; - error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, - UBC_WRITE | UBC_UNMAP_FLAG(vp)); - if (error) - break; + if (vp->v_size < oldoff + bytelen) { + uvm_vnp_setwritesize(vp, oldoff + bytelen); + } + error = ufs_balloc_range(vp, uio->uio_offset, bytelen, + ap->a_cred, 0); + if (error) + break; + error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, + UBC_WRITE | UBC_UNMAP_FLAG(vp)); + if (error) + break; + + /* + * update UVM's notion of the size now that we've + * copied the data into the vnode's pages. + */ - /* - * update UVM's notion of the size now that we've - * copied the data into the vnode's pages. - */ - - if (vp->v_size < uio->uio_offset) { - uvm_vnp_setsize(vp, uio->uio_offset); - extended = 1; - } - - /* - * flush what we just wrote if necessary. - * XXXUBC simplistic async flushing. - */ - - if (!async && oldoff >> 16 != uio->uio_offset >> 16) { - mutex_enter(vp->v_interlock); - error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16, - (uio->uio_offset >> 16) << 16, - PGO_CLEANIT | PGO_LAZY); - } + if (vp->v_size < uio->uio_offset) { + uvm_vnp_setsize(vp, uio->uio_offset); + extended = 1; } - if (error == 0 && ioflag & IO_SYNC) { + + /* + * flush what we just wrote if necessary. + * XXXUBC simplistic async flushing. + */ + + if (!async && oldoff >> 16 != uio->uio_offset >> 16) { mutex_enter(vp->v_interlock); - error = VOP_PUTPAGES(vp, trunc_page(oldoff), - round_page(ext2_blkroundup(fs, uio->uio_offset)), - PGO_CLEANIT | PGO_SYNCIO); + error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16, + (uio->uio_offset >> 16) << 16, + PGO_CLEANIT | PGO_LAZY); } + } + if (error == 0 && ioflag & IO_SYNC) { + mutex_enter(vp->v_interlock); + error = VOP_PUTPAGES(vp, trunc_page(oldoff), + round_page(ext2_blkroundup(fs, uio->uio_offset)), + PGO_CLEANIT | PGO_SYNCIO); + } - goto out; + /* + * If we successfully wrote any data, and we are not the superuser + * we clear the setuid and setgid bits as a precaution against + * tampering. + */ + ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + ip->i_flag |= IN_ACCESS; + if (resid > uio->uio_resid && ap->a_cred) { + if (ip->i_e2fs_mode & ISUID) { + if (kauth_authorize_vnode(ap->a_cred, + KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) + ip->i_e2fs_mode &= ISUID; + } + + if (ip->i_e2fs_mode & ISGID) { + if (kauth_authorize_vnode(ap->a_cred, + KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) + ip->i_e2fs_mode &= ~ISGID; + } } + if (resid > uio->uio_resid) + VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); + if (error) { + (void) ext2fs_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) == IO_SYNC) + error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT); + KASSERT(vp->v_size == ext2fs_size(ip)); + return (error); +} + +/* + * UFS op for writing via the buffer cache + */ +int +ext2fs_bufwr(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t cred) +{ + struct inode *ip; + struct ufsmount *ump; + struct m_ext2fs *fs; + struct buf *bp; + int flags; + off_t osize; + daddr_t lbn; + int resid, blkoffset, xfersize; + int extended = 0; + int error; + + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(vp->v_type == VDIR || vp->v_type == VLNK); + KASSERT(vp->v_type != VDIR || ISSET(ioflag, IO_SYNC)); + KASSERT(uio->uio_rw == UIO_WRITE); + + ip = VTOI(vp); + ump = ip->i_ump; + fs = ip->i_e2fs; + error = 0; + + if (uio->uio_offset < 0 || + uio->uio_resid > ump->um_maxfilesize || + uio->uio_offset > (ump->um_maxfilesize - uio->uio_resid)) + return EFBIG; + if (uio->uio_resid == 0) + return 0; flags = ioflag & IO_SYNC ? B_SYNC : 0; + resid = uio->uio_resid; + osize = ext2fs_size(ip); + for (error = 0; uio->uio_resid > 0;) { lbn = ext2_lblkno(fs, uio->uio_offset); blkoffset = ext2_blkoff(fs, uio->uio_offset); @@ -331,8 +436,8 @@ ext2fs_write(void *v) flags |= B_CLRBUF; else flags &= ~B_CLRBUF; - error = ext2fs_balloc(ip, - lbn, blkoffset + xfersize, ap->a_cred, &bp, flags); + error = ext2fs_balloc(ip, lbn, blkoffset + xfersize, cred, &bp, + flags); if (error) break; if (ext2fs_size(ip) < uio->uio_offset + xfersize) { @@ -367,20 +472,18 @@ ext2fs_write(void *v) * we clear the setuid and setgid bits as a precaution against * tampering. */ - -out: ip->i_flag |= IN_CHANGE | IN_UPDATE; if (vp->v_mount->mnt_flag & MNT_RELATIME) ip->i_flag |= IN_ACCESS; - if (resid > uio->uio_resid && ap->a_cred) { + if (resid > uio->uio_resid && cred) { if (ip->i_e2fs_mode & ISUID) { - if (kauth_authorize_vnode(ap->a_cred, + if (kauth_authorize_vnode(cred, KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) ip->i_e2fs_mode &= ISUID; } if (ip->i_e2fs_mode & ISGID) { - if (kauth_authorize_vnode(ap->a_cred, + if (kauth_authorize_vnode(cred, KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) ip->i_e2fs_mode &= ~ISGID; } @@ -388,7 +491,7 @@ out: if (resid > uio->uio_resid) VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); if (error) { - (void) ext2fs_truncate(vp, osize, ioflag & IO_SYNC, ap->a_cred); + (void) ext2fs_truncate(vp, osize, ioflag & IO_SYNC, cred); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } else if (resid > uio->uio_resid && (ioflag & IO_SYNC) == IO_SYNC) Index: src/sys/ufs/ext2fs/ext2fs_rename.c diff -u src/sys/ufs/ext2fs/ext2fs_rename.c:1.7 src/sys/ufs/ext2fs/ext2fs_rename.c:1.8 --- src/sys/ufs/ext2fs/ext2fs_rename.c:1.7 Sun May 25 13:46:58 2014 +++ src/sys/ufs/ext2fs/ext2fs_rename.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ext2fs_rename.c,v 1.7 2014/05/25 13:46:58 hannken Exp $ */ +/* $NetBSD: ext2fs_rename.c,v 1.8 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ext2fs_rename.c,v 1.7 2014/05/25 13:46:58 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ext2fs_rename.c,v 1.8 2015/03/27 17:27:56 riastradh Exp $"); #include <sys/param.h> #include <sys/buf.h> @@ -892,8 +892,8 @@ ext2fs_read_dotdot(struct vnode *vp, kau KASSERT(ino_ret != NULL); KASSERT(vp->v_type == VDIR); - error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); + error = ufs_bufio(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, + IO_NODELOCKED, cred, NULL, NULL); if (error) return error; @@ -924,8 +924,8 @@ ext2fs_rename_replace_dotdot(struct vnod VTOI(fdvp)->i_e2fs_nlink--; VTOI(fdvp)->i_flag |= IN_CHANGE; - error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); + error = ufs_bufio(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, + IO_NODELOCKED, cred, NULL, NULL); if (error) return error; @@ -944,8 +944,8 @@ ext2fs_rename_replace_dotdot(struct vnod dirbuf.dotdot_ino = h2fs32(VTOI(tdvp)->i_number); /* XXX WTF? Why not check error? */ - (void)vn_rdwr(UIO_WRITE, vp, &dirbuf, sizeof dirbuf, (off_t)0, - UIO_SYSSPACE, (IO_NODELOCKED | IO_SYNC), cred, NULL, NULL); + (void)ufs_bufio(UIO_WRITE, vp, &dirbuf, sizeof dirbuf, (off_t)0, + (IO_NODELOCKED | IO_SYNC), cred, NULL, NULL); return 0; } Index: src/sys/ufs/ext2fs/ext2fs_vfsops.c diff -u src/sys/ufs/ext2fs/ext2fs_vfsops.c:1.191 src/sys/ufs/ext2fs/ext2fs_vfsops.c:1.192 --- src/sys/ufs/ext2fs/ext2fs_vfsops.c:1.191 Tue Mar 17 09:39:29 2015 +++ src/sys/ufs/ext2fs/ext2fs_vfsops.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ext2fs_vfsops.c,v 1.191 2015/03/17 09:39:29 hannken Exp $ */ +/* $NetBSD: ext2fs_vfsops.c,v 1.192 2015/03/27 17:27:56 riastradh Exp $ */ /* * Copyright (c) 1989, 1991, 1993, 1994 @@ -60,7 +60,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ext2fs_vfsops.c,v 1.191 2015/03/17 09:39:29 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ext2fs_vfsops.c,v 1.192 2015/03/27 17:27:56 riastradh Exp $"); #if defined(_KERNEL_OPT) #include "opt_compat_netbsd.h" @@ -156,6 +156,8 @@ static const struct genfs_ops ext2fs_gen static const struct ufs_ops ext2fs_ufsops = { .uo_itimes = ext2fs_itimes, .uo_update = ext2fs_update, + .uo_bufrd = ext2fs_bufrd, + .uo_bufwr = ext2fs_bufwr, }; /* Fill in the inode uid/gid from ext2 halves. */ Index: src/sys/ufs/ext2fs/ext2fs_vnops.c diff -u src/sys/ufs/ext2fs/ext2fs_vnops.c:1.115 src/sys/ufs/ext2fs/ext2fs_vnops.c:1.116 --- src/sys/ufs/ext2fs/ext2fs_vnops.c:1.115 Sun Nov 9 18:23:28 2014 +++ src/sys/ufs/ext2fs/ext2fs_vnops.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ext2fs_vnops.c,v 1.115 2014/11/09 18:23:28 maxv Exp $ */ +/* $NetBSD: ext2fs_vnops.c,v 1.116 2015/03/27 17:27:56 riastradh Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1993 @@ -65,7 +65,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ext2fs_vnops.c,v 1.115 2014/11/09 18:23:28 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ext2fs_vnops.c,v 1.116 2015/03/27 17:27:56 riastradh Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -727,9 +727,9 @@ ext2fs_mkdir(void *v) dirtemplate.dotdot_type = EXT2_FT_DIR; } dirtemplate.dotdot_name[0] = dirtemplate.dotdot_name[1] = '.'; - error = vn_rdwr(UIO_WRITE, tvp, (void *)&dirtemplate, - sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, - IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (size_t *)0, NULL); + error = ufs_bufio(UIO_WRITE, tvp, (void *)&dirtemplate, + sizeof (dirtemplate), (off_t)0, IO_NODELOCKED|IO_SYNC, + cnp->cn_cred, (size_t *)0, NULL); if (error) { dp->i_e2fs_nlink--; dp->i_flag |= IN_CHANGE; @@ -895,9 +895,8 @@ ext2fs_symlink(void *v) ip->i_flag |= IN_ACCESS; uvm_vnp_setsize(vp, len); } else - error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, - (size_t *)0, NULL); + error = ufs_bufio(UIO_WRITE, vp, ap->a_target, len, (off_t)0, + IO_NODELOCKED, ap->a_cnp->cn_cred, (size_t *)0, NULL); bad: VOP_UNLOCK(vp); if (error) @@ -927,7 +926,7 @@ ext2fs_readlink(void *v) uiomove((char *)ip->i_din.e2fs_din->e2di_shortlink, isize, ap->a_uio); return (0); } - return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); + return (UFS_BUFRD(vp, ap->a_uio, 0, ap->a_cred)); } /* Index: src/sys/ufs/ffs/ffs_extern.h diff -u src/sys/ufs/ffs/ffs_extern.h:1.81 src/sys/ufs/ffs/ffs_extern.h:1.82 --- src/sys/ufs/ffs/ffs_extern.h:1.81 Tue Mar 17 09:39:29 2015 +++ src/sys/ufs/ffs/ffs_extern.h Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_extern.h,v 1.81 2015/03/17 09:39:29 hannken Exp $ */ +/* $NetBSD: ffs_extern.h,v 1.82 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -127,6 +127,10 @@ int ffs_cgupdate(struct ufsmount *, int) /* ffs_vnops.c */ int ffs_read(void *); int ffs_write(void *); +int ffs_bufio(enum uio_rw, struct vnode *, void *, size_t, off_t, int, + kauth_cred_t, size_t *, struct lwp *); +int ffs_bufrd(struct vnode *, struct uio *, int, kauth_cred_t); +int ffs_bufwr(struct vnode *, struct uio *, int, kauth_cred_t); int ffs_fsync(void *); int ffs_spec_fsync(void *); int ffs_reclaim(void *); Index: src/sys/ufs/ffs/ffs_vfsops.c diff -u src/sys/ufs/ffs/ffs_vfsops.c:1.325 src/sys/ufs/ffs/ffs_vfsops.c:1.326 --- src/sys/ufs/ffs/ffs_vfsops.c:1.325 Tue Mar 17 09:39:29 2015 +++ src/sys/ufs/ffs/ffs_vfsops.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_vfsops.c,v 1.325 2015/03/17 09:39:29 hannken Exp $ */ +/* $NetBSD: ffs_vfsops.c,v 1.326 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -61,7 +61,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.325 2015/03/17 09:39:29 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.326 2015/03/27 17:27:56 riastradh Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -182,6 +182,8 @@ static const struct ufs_ops ffs_ufsops = .uo_truncate = ffs_truncate, .uo_balloc = ffs_balloc, .uo_snapgone = ffs_snapgone, + .uo_bufrd = ffs_bufrd, + .uo_bufwr = ffs_bufwr, }; static int Index: src/sys/ufs/lfs/lfs_extern.h diff -u src/sys/ufs/lfs/lfs_extern.h:1.101 src/sys/ufs/lfs/lfs_extern.h:1.102 --- src/sys/ufs/lfs/lfs_extern.h:1.101 Tue Mar 18 18:20:44 2014 +++ src/sys/ufs/lfs/lfs_extern.h Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: lfs_extern.h,v 1.101 2014/03/18 18:20:44 riastradh Exp $ */ +/* $NetBSD: lfs_extern.h,v 1.102 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc. @@ -272,6 +272,9 @@ int lfs_write (void *); int lfs_getpages (void *); int lfs_putpages (void *); +int lfs_bufrd(struct vnode *, struct uio *, int, kauth_cred_t); +int lfs_bufwr(struct vnode *, struct uio *, int, kauth_cred_t); + extern int lfs_mount_type; extern int (**lfs_vnodeop_p)(void *); extern int (**lfs_specop_p)(void *); Index: src/sys/ufs/lfs/lfs_rename.c diff -u src/sys/ufs/lfs/lfs_rename.c:1.7 src/sys/ufs/lfs/lfs_rename.c:1.8 --- src/sys/ufs/lfs/lfs_rename.c:1.7 Sat May 17 07:08:35 2014 +++ src/sys/ufs/lfs/lfs_rename.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: lfs_rename.c,v 1.7 2014/05/17 07:08:35 dholland Exp $ */ +/* $NetBSD: lfs_rename.c,v 1.8 2015/03/27 17:27:56 riastradh Exp $ */ /* from NetBSD: ufs_rename.c,v 1.6 2013/01/22 09:39:18 dholland Exp */ /*- @@ -89,7 +89,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: lfs_rename.c,v 1.7 2014/05/17 07:08:35 dholland Exp $"); +__KERNEL_RCSID(0, "$NetBSD: lfs_rename.c,v 1.8 2015/03/27 17:27:56 riastradh Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -629,8 +629,8 @@ ulfs_read_dotdot(struct vnode *vp, kauth KASSERT(ino_ret != NULL); KASSERT(vp->v_type == VDIR); - error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); + error = ulfs_bufio(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, + IO_NODELOCKED, cred, NULL, NULL); if (error) return error; Index: src/sys/ufs/lfs/ulfs_readwrite.c diff -u src/sys/ufs/lfs/ulfs_readwrite.c:1.7 src/sys/ufs/lfs/ulfs_readwrite.c:1.8 --- src/sys/ufs/lfs/ulfs_readwrite.c:1.7 Thu Oct 17 21:01:08 2013 +++ src/sys/ufs/lfs/ulfs_readwrite.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ulfs_readwrite.c,v 1.7 2013/10/17 21:01:08 christos Exp $ */ +/* $NetBSD: ulfs_readwrite.c,v 1.8 2015/03/27 17:27:56 riastradh Exp $ */ /* from NetBSD: ufs_readwrite.c,v 1.105 2013/01/22 09:39:18 dholland Exp */ /*- @@ -33,7 +33,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: ulfs_readwrite.c,v 1.7 2013/10/17 21:01:08 christos Exp $"); +__KERNEL_RCSID(1, "$NetBSD: ulfs_readwrite.c,v 1.8 2015/03/27 17:27:56 riastradh Exp $"); #ifdef LFS_READWRITE #define FS struct lfs @@ -42,6 +42,8 @@ __KERNEL_RCSID(1, "$NetBSD: ulfs_readwri #define READ_S "lfs_read" #define WRITE lfs_write #define WRITE_S "lfs_write" +#define BUFRD lfs_bufrd +#define BUFWR lfs_bufwr #define fs_bsize lfs_bsize #define fs_bmask lfs_bmask #else @@ -51,6 +53,8 @@ __KERNEL_RCSID(1, "$NetBSD: ulfs_readwri #define READ_S "ffs_read" #define WRITE ffs_write #define WRITE_S "ffs_write" +#define BUFRD ffs_bufrd +#define BUFWR ffs_bufwr #endif /* @@ -69,14 +73,9 @@ READ(void *v) struct vnode *vp; struct inode *ip; struct uio *uio; - struct buf *bp; FS *fs; vsize_t bytelen; - daddr_t lbn, nextlbn; - off_t bytesinfile; - long size, xfersize, blkoffset; - int error, ioflag; - bool usepc = false; + int error, ioflag, advice; vp = ap->a_vp; ip = VTOI(vp); @@ -89,13 +88,17 @@ READ(void *v) if (uio->uio_rw != UIO_READ) panic("%s: mode", READ_S); - if (vp->v_type == VLNK) { - if (ip->i_size < fs->um_maxsymlinklen || - (fs->um_maxsymlinklen == 0 && DIP(ip, blocks) == 0)) - panic("%s: short symlink", READ_S); - } else if (vp->v_type != VREG && vp->v_type != VDIR) + if (vp->v_type != VREG && vp->v_type != VDIR) panic("%s: type %d", READ_S, vp->v_type); #endif + /* XXX Eliminate me by refusing directory reads from userland. */ + if (vp->v_type == VDIR) + return BUFRD(vp, uio, ioflag, ap->a_cred); +#ifdef LFS_READWRITE + /* XXX Eliminate me by using ufs_bufio in lfs. */ + if (vp->v_type == VREG && ip->i_number == LFS_IFILE_INUM) + return BUFRD(vp, uio, ioflag, ap->a_cred); +#endif if ((u_int64_t)uio->uio_offset > fs->um_maxfilesize) return (EFBIG); if (uio->uio_resid == 0) @@ -111,30 +114,76 @@ READ(void *v) if (uio->uio_offset >= ip->i_size) goto out; -#ifdef LFS_READWRITE - usepc = (vp->v_type == VREG && ip->i_number != LFS_IFILE_INUM); -#else /* !LFS_READWRITE */ - usepc = vp->v_type == VREG; -#endif /* !LFS_READWRITE */ - if (usepc) { - const int advice = IO_ADV_DECODE(ap->a_ioflag); + KASSERT(vp->v_type == VREG); + advice = IO_ADV_DECODE(ap->a_ioflag); + while (uio->uio_resid > 0) { + if (ioflag & IO_DIRECT) { + genfs_directio(vp, uio, ioflag); + } + bytelen = MIN(ip->i_size - uio->uio_offset, uio->uio_resid); + if (bytelen == 0) + break; + error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, + UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); + if (error) + break; + } - while (uio->uio_resid > 0) { - if (ioflag & IO_DIRECT) { - genfs_directio(vp, uio, ioflag); - } - bytelen = MIN(ip->i_size - uio->uio_offset, - uio->uio_resid); - if (bytelen == 0) - break; - error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, - UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); - if (error) - break; + out: + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { + ip->i_flag |= IN_ACCESS; + if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) { + error = lfs_update(vp, NULL, NULL, UPDATE_WAIT); } - goto out; } + fstrans_done(vp->v_mount); + return (error); +} + +/* + * UFS op for reading via the buffer cache + */ +int +BUFRD(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t cred) +{ + struct inode *ip; + FS *fs; + struct buf *bp; + daddr_t lbn, nextlbn; + off_t bytesinfile; + long size, xfersize, blkoffset; + int error; + + KASSERT(VOP_ISLOCKED(vp)); + KASSERT(vp->v_type == VDIR || vp->v_type == VLNK || + vp->v_type == VREG); + KASSERT(uio->uio_rw == UIO_READ); + + ip = VTOI(vp); + fs = ip->I_FS; + error = 0; + + KASSERT(vp->v_type != VLNK || ip->i_size < fs->um_maxsymlinklen); + KASSERT(vp->v_type != VLNK || fs->um_maxsymlinklen != 0 || + DIP(ip, blocks) == 0); + KASSERT(vp->v_type != VREG || vp == fs->lfs_ivnode); + KASSERT(vp->v_type != VREG || ip->i_number == LFS_IFILE_INUM); + + if (uio->uio_offset > fs->um_maxfilesize) + return EFBIG; + if (uio->uio_resid == 0) + return 0; + +#ifndef LFS_READWRITE + KASSERT(!ISSET(ip->i_flags, (SF_SNAPSHOT | SF_SNAPINVAL))); +#endif + + fstrans_start(vp->v_mount, FSTRANS_SHARED); + + if (uio->uio_offset >= ip->i_size) + goto out; + for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { bytesinfile = ip->i_size - uio->uio_offset; if (bytesinfile <= 0) @@ -180,7 +229,7 @@ READ(void *v) out: if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { ip->i_flag |= IN_ACCESS; - if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) { + if ((ioflag & IO_SYNC) == IO_SYNC) { error = lfs_update(vp, NULL, NULL, UPDATE_WAIT); } } @@ -205,19 +254,13 @@ WRITE(void *v) struct uio *uio; struct inode *ip; FS *fs; - struct buf *bp; kauth_cred_t cred; - daddr_t lbn; off_t osize, origoff, oldoff, preallocoff, endallocoff, nsize; - int blkoffset, error, flags, ioflag, resid, size, xfersize; + int blkoffset, error, flags, ioflag, resid; int aflag; int extended=0; vsize_t bytelen; bool async; - bool usepc = false; -#ifdef LFS_READWRITE - bool need_unreserve = false; -#endif cred = ap->a_cred; ioflag = ap->a_ioflag; @@ -237,12 +280,6 @@ WRITE(void *v) uio->uio_offset = ip->i_size; if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size) return (EPERM); - /* FALLTHROUGH */ - case VLNK: - break; - case VDIR: - if ((ioflag & IO_SYNC) == 0) - panic("%s: nonsync dir write", WRITE_S); break; default: panic("%s: type", WRITE_S); @@ -270,15 +307,13 @@ WRITE(void *v) osize = ip->i_size; error = 0; - usepc = vp->v_type == VREG; + KASSERT(vp->v_type == VREG); #ifdef LFS_READWRITE async = true; lfs_availwait(fs, lfs_btofsb(fs, uio->uio_resid)); lfs_check(vp, LFS_UNUSED_LBN, 0); #endif /* !LFS_READWRITE */ - if (!usepc) - goto bcache; preallocoff = round_page(lfs_blkroundup(fs, MAX(osize, uio->uio_offset))); aflag = ioflag & IO_SYNC ? B_SYNC : 0; @@ -412,9 +447,105 @@ WRITE(void *v) round_page(lfs_blkroundup(fs, uio->uio_offset)), PGO_CLEANIT | PGO_SYNCIO | PGO_JOURNALLOCKED); } - goto out; - bcache: + /* + * If we successfully wrote any data, and we are not the superuser + * we clear the setuid and setgid bits as a precaution against + * tampering. + */ +out: + ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + ip->i_flag |= IN_ACCESS; + if (resid > uio->uio_resid && ap->a_cred) { + if (ip->i_mode & ISUID) { + if (kauth_authorize_vnode(ap->a_cred, + KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) { + ip->i_mode &= ~ISUID; + DIP_ASSIGN(ip, mode, ip->i_mode); + } + } + + if (ip->i_mode & ISGID) { + if (kauth_authorize_vnode(ap->a_cred, + KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) { + ip->i_mode &= ~ISGID; + DIP_ASSIGN(ip, mode, ip->i_mode); + } + } + } + if (resid > uio->uio_resid) + VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); + if (error) { + (void) lfs_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) == IO_SYNC) { + error = lfs_update(vp, NULL, NULL, UPDATE_WAIT); + } else { + /* nothing */ + } + KASSERT(vp->v_size == ip->i_size); + fstrans_done(vp->v_mount); + + return (error); +} + +/* + * UFS op for writing via the buffer cache + */ +int +BUFWR(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t cred) +{ + struct inode *ip; + FS *fs; + int flags; + struct buf *bp; + off_t osize, origoff; + int resid, xfersize, size, blkoffset; + daddr_t lbn; + int extended=0; + int error; +#ifdef LFS_READWRITE + bool need_unreserve = false; +#endif + + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(vp->v_type == VDIR || vp->v_type == VLNK); + KASSERT(vp->v_type != VDIR || ISSET(ioflag, IO_SYNC)); + KASSERT(uio->uio_rw == UIO_WRITE); + + ip = VTOI(vp); + fs = ip->I_FS; + + KASSERT(vp->v_size == ip->i_size); + + if (uio->uio_offset < 0 || + uio->uio_resid > fs->um_maxfilesize || + uio->uio_offset > (fs->um_maxfilesize - uio->uio_resid)) + return EFBIG; +#ifdef LFS_READWRITE + KASSERT(vp != fs->lfs_ivnode); +#endif + if (uio->uio_resid == 0) + return 0; + + fstrans_start(vp->v_mount, FSTRANS_SHARED); + + flags = ioflag & IO_SYNC ? B_SYNC : 0; + origoff = uio->uio_offset; + resid = uio->uio_resid; + osize = ip->i_size; + error = 0; + + KASSERT(vp->v_type != VREG); + +#ifdef LFS_READWRITE + lfs_availwait(fs, lfs_btofsb(fs, uio->uio_resid)); + lfs_check(vp, LFS_UNUSED_LBN, 0); +#endif /* !LFS_READWRITE */ + + /* XXX Should never have cached pages here. */ mutex_enter(vp->v_interlock); VOP_PUTPAGES(vp, trunc_page(origoff), round_page(origoff + resid), PGO_CLEANIT | PGO_FREE | PGO_SYNCIO | PGO_JOURNALLOCKED); @@ -434,8 +565,8 @@ WRITE(void *v) break; need_unreserve = true; #endif - error = lfs_balloc(vp, uio->uio_offset, xfersize, - ap->a_cred, flags, &bp); + error = lfs_balloc(vp, uio->uio_offset, xfersize, cred, flags, + &bp); if (error) break; @@ -488,13 +619,12 @@ WRITE(void *v) * we clear the setuid and setgid bits as a precaution against * tampering. */ -out: ip->i_flag |= IN_CHANGE | IN_UPDATE; if (vp->v_mount->mnt_flag & MNT_RELATIME) ip->i_flag |= IN_ACCESS; - if (resid > uio->uio_resid && ap->a_cred) { + if (resid > uio->uio_resid && cred) { if (ip->i_mode & ISUID) { - if (kauth_authorize_vnode(ap->a_cred, + if (kauth_authorize_vnode(cred, KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) { ip->i_mode &= ~ISUID; DIP_ASSIGN(ip, mode, ip->i_mode); @@ -502,7 +632,7 @@ out: } if (ip->i_mode & ISGID) { - if (kauth_authorize_vnode(ap->a_cred, + if (kauth_authorize_vnode(cred, KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) { ip->i_mode &= ~ISGID; DIP_ASSIGN(ip, mode, ip->i_mode); @@ -512,7 +642,7 @@ out: if (resid > uio->uio_resid) VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); if (error) { - (void) lfs_truncate(vp, osize, ioflag & IO_SYNC, ap->a_cred); + (void) lfs_truncate(vp, osize, ioflag & IO_SYNC, cred); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } else if (resid > uio->uio_resid && (ioflag & IO_SYNC) == IO_SYNC) { Index: src/sys/ufs/lfs/lfs_vnops.c diff -u src/sys/ufs/lfs/lfs_vnops.c:1.269 src/sys/ufs/lfs/lfs_vnops.c:1.270 --- src/sys/ufs/lfs/lfs_vnops.c:1.269 Fri Jul 25 08:20:53 2014 +++ src/sys/ufs/lfs/lfs_vnops.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: lfs_vnops.c,v 1.269 2014/07/25 08:20:53 dholland Exp $ */ +/* $NetBSD: lfs_vnops.c,v 1.270 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc. @@ -125,7 +125,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: lfs_vnops.c,v 1.269 2014/07/25 08:20:53 dholland Exp $"); +__KERNEL_RCSID(0, "$NetBSD: lfs_vnops.c,v 1.270 2015/03/27 17:27:56 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_compat_netbsd.h" @@ -691,9 +691,9 @@ lfs_symlink(void *v) if ((*vpp)->v_mount->mnt_flag & MNT_RELATIME) ip->i_flag |= IN_ACCESS; } else { - error = vn_rdwr(UIO_WRITE, *vpp, ap->a_target, len, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED | IO_JOURNALLOCKED, - ap->a_cnp->cn_cred, NULL, NULL); + error = ulfs_bufio(UIO_WRITE, *vpp, ap->a_target, len, (off_t)0, + IO_NODELOCKED | IO_JOURNALLOCKED, ap->a_cnp->cn_cred, NULL, + NULL); } VOP_UNLOCK(*vpp); Index: src/sys/ufs/lfs/ulfs_extern.h diff -u src/sys/ufs/lfs/ulfs_extern.h:1.13 src/sys/ufs/lfs/ulfs_extern.h:1.14 --- src/sys/ufs/lfs/ulfs_extern.h:1.13 Sun May 25 13:49:13 2014 +++ src/sys/ufs/lfs/ulfs_extern.h Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ulfs_extern.h,v 1.13 2014/05/25 13:49:13 hannken Exp $ */ +/* $NetBSD: ulfs_extern.h,v 1.14 2015/03/27 17:27:56 riastradh Exp $ */ /* from NetBSD: ufs_extern.h,v 1.72 2012/05/09 00:21:18 riastradh Exp */ /*- @@ -170,6 +170,8 @@ int ulfs_makeinode(int, struct vnode *, struct vnode **, struct componentname *); int ulfs_gop_alloc(struct vnode *, off_t, off_t, int, kauth_cred_t); void ulfs_gop_markupdate(struct vnode *, int); +int ulfs_bufio(enum uio_rw, struct vnode *, void *, size_t, off_t, int, + kauth_cred_t, size_t *, struct lwp *); /* * Snapshot function prototypes. Index: src/sys/ufs/lfs/ulfs_lookup.c diff -u src/sys/ufs/lfs/ulfs_lookup.c:1.21 src/sys/ufs/lfs/ulfs_lookup.c:1.22 --- src/sys/ufs/lfs/ulfs_lookup.c:1.21 Tue Jun 3 19:30:30 2014 +++ src/sys/ufs/lfs/ulfs_lookup.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ulfs_lookup.c,v 1.21 2014/06/03 19:30:30 joerg Exp $ */ +/* $NetBSD: ulfs_lookup.c,v 1.22 2015/03/27 17:27:56 riastradh Exp $ */ /* from NetBSD: ufs_lookup.c,v 1.122 2013/01/22 09:39:18 dholland Exp */ /* @@ -38,7 +38,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ulfs_lookup.c,v 1.21 2014/06/03 19:30:30 joerg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ulfs_lookup.c,v 1.22 2015/03/27 17:27:56 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_lfs.h" @@ -1216,8 +1216,8 @@ ulfs_dirempty(struct inode *ip, ino_t pa for (off = 0; off < ip->i_size; off += ulfs_rw16(dp->d_reclen, needswap)) { - error = vn_rdwr(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, off, - UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL); + error = ulfs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, + off, IO_NODELOCKED, cred, &count, NULL); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. Index: src/sys/ufs/lfs/ulfs_vnops.c diff -u src/sys/ufs/lfs/ulfs_vnops.c:1.21 src/sys/ufs/lfs/ulfs_vnops.c:1.22 --- src/sys/ufs/lfs/ulfs_vnops.c:1.21 Sat May 17 07:09:09 2014 +++ src/sys/ufs/lfs/ulfs_vnops.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ulfs_vnops.c,v 1.21 2014/05/17 07:09:09 dholland Exp $ */ +/* $NetBSD: ulfs_vnops.c,v 1.22 2015/03/27 17:27:56 riastradh Exp $ */ /* from NetBSD: ufs_vnops.c,v 1.213 2013/06/08 05:47:02 kardel Exp */ /*- @@ -67,7 +67,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ulfs_vnops.c,v 1.21 2014/05/17 07:09:09 dholland Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ulfs_vnops.c,v 1.22 2015/03/27 17:27:56 riastradh Exp $"); #if defined(_KERNEL_OPT) #include "opt_lfs.h" @@ -934,7 +934,7 @@ ulfs_readlink(void *v) uiomove((char *)SHORTLINK(ip), isize, ap->a_uio); return (0); } - return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); + return (lfs_bufrd(vp, ap->a_uio, 0, ap->a_cred)); } /* @@ -1320,3 +1320,49 @@ ulfs_gop_markupdate(struct vnode *vp, in ip->i_flag |= mask; } } + +int +ulfs_bufio(enum uio_rw rw, struct vnode *vp, void *buf, size_t len, off_t off, + int ioflg, kauth_cred_t cred, size_t *aresid, struct lwp *l) +{ + struct iovec iov; + struct uio uio; + int error; + + /* XXX Remove me -- all callers should be locked. */ + if (!ISSET(ioflg, IO_NODELOCKED)) { + if (rw == UIO_READ) + vn_lock(vp, LK_SHARED | LK_RETRY); + else /* UIO_WRITE */ + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + } + + iov.iov_base = buf; + iov.iov_len = len; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = len; + uio.uio_offset = off; + uio.uio_rw = rw; + UIO_SETUP_SYSSPACE(&uio); + + switch (rw) { + case UIO_READ: + error = lfs_bufrd(vp, &uio, ioflg, cred); + break; + case UIO_WRITE: + error = lfs_bufwr(vp, &uio, ioflg, cred); + break; + default: + panic("invalid uio rw: %d", (int)rw); + } + + if (aresid) + *aresid = uio.uio_resid; + else if (uio.uio_resid && error == 0) + error = EIO; + + if (!ISSET(ioflg, IO_NODELOCKED)) + VOP_UNLOCK(vp); + return error; +} Index: src/sys/ufs/ufs/ufs_extern.h diff -u src/sys/ufs/ufs/ufs_extern.h:1.78 src/sys/ufs/ufs/ufs_extern.h:1.79 --- src/sys/ufs/ufs/ufs_extern.h:1.78 Tue Mar 17 09:39:29 2015 +++ src/sys/ufs/ufs/ufs_extern.h Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_extern.h,v 1.78 2015/03/17 09:39:29 hannken Exp $ */ +/* $NetBSD: ufs_extern.h,v 1.79 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -186,6 +186,8 @@ void ufs_vinit(struct mount *, int (**)( int (**)(void *), struct vnode **); int ufs_gop_alloc(struct vnode *, off_t, off_t, int, kauth_cred_t); void ufs_gop_markupdate(struct vnode *, int); +int ufs_bufio(enum uio_rw, struct vnode *, void *, size_t, off_t, int, + kauth_cred_t, size_t *, struct lwp *); __END_DECLS Index: src/sys/ufs/ufs/ufs_lookup.c diff -u src/sys/ufs/ufs/ufs_lookup.c:1.132 src/sys/ufs/ufs/ufs_lookup.c:1.133 --- src/sys/ufs/ufs/ufs_lookup.c:1.132 Tue Jun 3 19:30:30 2014 +++ src/sys/ufs/ufs/ufs_lookup.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_lookup.c,v 1.132 2014/06/03 19:30:30 joerg Exp $ */ +/* $NetBSD: ufs_lookup.c,v 1.133 2015/03/27 17:27:56 riastradh Exp $ */ /* * Copyright (c) 1989, 1993 @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c,v 1.132 2014/06/03 19:30:30 joerg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c,v 1.133 2015/03/27 17:27:56 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_ffs.h" @@ -1182,8 +1182,8 @@ ufs_dirempty(struct inode *ip, ino_t par for (off = 0; off < ip->i_size; off += ufs_rw16(dp->d_reclen, needswap)) { - error = vn_rdwr(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, off, - UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL); + error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, + off, IO_NODELOCKED, cred, &count, NULL); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. Index: src/sys/ufs/ufs/ufs_readwrite.c diff -u src/sys/ufs/ufs/ufs_readwrite.c:1.107 src/sys/ufs/ufs/ufs_readwrite.c:1.108 --- src/sys/ufs/ufs/ufs_readwrite.c:1.107 Sun Jun 23 07:28:37 2013 +++ src/sys/ufs/ufs/ufs_readwrite.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_readwrite.c,v 1.107 2013/06/23 07:28:37 dholland Exp $ */ +/* $NetBSD: ufs_readwrite.c,v 1.108 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 1993 @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: ufs_readwrite.c,v 1.107 2013/06/23 07:28:37 dholland Exp $"); +__KERNEL_RCSID(1, "$NetBSD: ufs_readwrite.c,v 1.108 2015/03/27 17:27:56 riastradh Exp $"); #ifdef LFS_READWRITE #define FS struct lfs @@ -41,6 +41,8 @@ __KERNEL_RCSID(1, "$NetBSD: ufs_readwrit #define READ_S "lfs_read" #define WRITE lfs_write #define WRITE_S "lfs_write" +#define BUFRD lfs_bufrd +#define BUFWR lfs_bufwr #define fs_bsize lfs_bsize #define fs_bmask lfs_bmask #define UFS_WAPBL_BEGIN(mp) 0 @@ -58,6 +60,8 @@ __KERNEL_RCSID(1, "$NetBSD: ufs_readwrit #define READ_S "ffs_read" #define WRITE ffs_write #define WRITE_S "ffs_write" +#define BUFRD ffs_bufrd +#define BUFWR ffs_bufwr #define ufs_blkoff ffs_blkoff #define ufs_blksize ffs_blksize #define ufs_lblkno ffs_lblkno @@ -82,14 +86,8 @@ READ(void *v) struct inode *ip; struct uio *uio; struct ufsmount *ump; - struct buf *bp; - FS *fs; vsize_t bytelen; - daddr_t lbn, nextlbn; - off_t bytesinfile; - long size, xfersize, blkoffset; - int error, ioflag; - bool usepc = false; + int error, ioflag, advice; vp = ap->a_vp; ip = VTOI(vp); @@ -102,14 +100,17 @@ READ(void *v) if (uio->uio_rw != UIO_READ) panic("%s: mode", READ_S); - if (vp->v_type == VLNK) { - if (ip->i_size < ump->um_maxsymlinklen || - (ump->um_maxsymlinklen == 0 && DIP(ip, blocks) == 0)) - panic("%s: short symlink", READ_S); - } else if (vp->v_type != VREG && vp->v_type != VDIR) + if (vp->v_type != VREG && vp->v_type != VDIR) panic("%s: type %d", READ_S, vp->v_type); #endif - fs = ip->I_FS; + /* XXX Eliminate me by refusing directory reads from userland. */ + if (vp->v_type == VDIR) + return BUFRD(vp, uio, ioflag, ap->a_cred); +#ifdef LFS_READWRITE + /* XXX Eliminate me by using ufs_bufio in lfs. */ + if (vp->v_type == VREG && ip->i_number == LFS_IFILE_INUM) + return BUFRD(vp, uio, ioflag, ap->a_cred); +#endif if ((u_int64_t)uio->uio_offset > ump->um_maxfilesize) return (EFBIG); if (uio->uio_resid == 0) @@ -125,30 +126,81 @@ READ(void *v) if (uio->uio_offset >= ip->i_size) goto out; -#ifdef LFS_READWRITE - usepc = (vp->v_type == VREG && ip->i_number != LFS_IFILE_INUM); -#else /* !LFS_READWRITE */ - usepc = vp->v_type == VREG; -#endif /* !LFS_READWRITE */ - if (usepc) { - const int advice = IO_ADV_DECODE(ap->a_ioflag); + KASSERT(vp->v_type == VREG); + advice = IO_ADV_DECODE(ap->a_ioflag); + while (uio->uio_resid > 0) { + if (ioflag & IO_DIRECT) { + genfs_directio(vp, uio, ioflag); + } + bytelen = MIN(ip->i_size - uio->uio_offset, uio->uio_resid); + if (bytelen == 0) + break; + error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, + UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); + if (error) + break; + } - while (uio->uio_resid > 0) { - if (ioflag & IO_DIRECT) { - genfs_directio(vp, uio, ioflag); + out: + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { + ip->i_flag |= IN_ACCESS; + if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) { + error = UFS_WAPBL_BEGIN(vp->v_mount); + if (error) { + fstrans_done(vp->v_mount); + return error; } - bytelen = MIN(ip->i_size - uio->uio_offset, - uio->uio_resid); - if (bytelen == 0) - break; - error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, - UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); - if (error) - break; + error = UFS_UPDATE(vp, NULL, NULL, UPDATE_WAIT); + UFS_WAPBL_END(vp->v_mount); } - goto out; } + fstrans_done(vp->v_mount); + return (error); +} + +/* + * UFS op for reading via the buffer cache + */ +int +BUFRD(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t cred) +{ + struct inode *ip; + struct ufsmount *ump; + FS *fs; + struct buf *bp; + daddr_t lbn, nextlbn; + off_t bytesinfile; + long size, xfersize, blkoffset; + int error; + + KASSERT(VOP_ISLOCKED(vp)); + KASSERT(vp->v_type == VDIR || vp->v_type == VLNK); + KASSERT(uio->uio_rw == UIO_READ); + + ip = VTOI(vp); + ump = ip->i_ump; + fs = ip->I_FS; + error = 0; + + KASSERT(vp->v_type != VLNK || ip->i_size >= ump->um_maxsymlinklen); + KASSERT(vp->v_type != VLNK || ump->um_maxsymlinklen != 0 || + DIP(ip, blocks) == 0); + + if (uio->uio_offset > ump->um_maxfilesize) + return EFBIG; + if (uio->uio_resid == 0) + return 0; + +#ifndef LFS_READWRITE + KASSERT(!ISSET(ip->i_flags, (SF_SNAPSHOT | SF_SNAPINVAL))); +#endif + + fstrans_start(vp->v_mount, FSTRANS_SHARED); + + if (uio->uio_offset >= ip->i_size) + goto out; + for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { bytesinfile = ip->i_size - uio->uio_offset; if (bytesinfile <= 0) @@ -194,7 +246,7 @@ READ(void *v) out: if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { ip->i_flag |= IN_ACCESS; - if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) { + if ((ioflag & IO_SYNC) == IO_SYNC) { error = UFS_WAPBL_BEGIN(vp->v_mount); if (error) { fstrans_done(vp->v_mount); @@ -225,19 +277,13 @@ WRITE(void *v) struct uio *uio; struct inode *ip; FS *fs; - struct buf *bp; kauth_cred_t cred; - daddr_t lbn; off_t osize, origoff, oldoff, preallocoff, endallocoff, nsize; - int blkoffset, error, flags, ioflag, resid, size, xfersize; + int blkoffset, error, flags, ioflag, resid; int aflag; int extended=0; vsize_t bytelen; bool async; - bool usepc = false; -#ifdef LFS_READWRITE - bool need_unreserve = false; -#endif struct ufsmount *ump; cred = ap->a_cred; @@ -259,12 +305,6 @@ WRITE(void *v) uio->uio_offset = ip->i_size; if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size) return (EPERM); - /* FALLTHROUGH */ - case VLNK: - break; - case VDIR: - if ((ioflag & IO_SYNC) == 0) - panic("%s: nonsync dir write", WRITE_S); break; default: panic("%s: type", WRITE_S); @@ -292,7 +332,7 @@ WRITE(void *v) osize = ip->i_size; error = 0; - usepc = vp->v_type == VREG; + KASSERT(vp->v_type == VREG); if ((ioflag & IO_JOURNALLOCKED) == 0) { error = UFS_WAPBL_BEGIN(vp->v_mount); @@ -307,8 +347,6 @@ WRITE(void *v) lfs_availwait(fs, btofsb(fs, uio->uio_resid)); lfs_check(vp, LFS_UNUSED_LBN, 0); #endif /* !LFS_READWRITE */ - if (!usepc) - goto bcache; preallocoff = round_page(ufs_blkroundup(fs, MAX(osize, uio->uio_offset))); aflag = ioflag & IO_SYNC ? B_SYNC : 0; @@ -440,9 +478,110 @@ WRITE(void *v) round_page(ufs_blkroundup(fs, uio->uio_offset)), PGO_CLEANIT | PGO_SYNCIO | PGO_JOURNALLOCKED); } - goto out; - bcache: + /* + * If we successfully wrote any data, and we are not the superuser + * we clear the setuid and setgid bits as a precaution against + * tampering. + */ +out: + ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + ip->i_flag |= IN_ACCESS; + if (resid > uio->uio_resid && ap->a_cred) { + if (ip->i_mode & ISUID) { + if (kauth_authorize_vnode(ap->a_cred, + KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) { + ip->i_mode &= ~ISUID; + DIP_ASSIGN(ip, mode, ip->i_mode); + } + } + + if (ip->i_mode & ISGID) { + if (kauth_authorize_vnode(ap->a_cred, + KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) { + ip->i_mode &= ~ISGID; + DIP_ASSIGN(ip, mode, ip->i_mode); + } + } + } + if (resid > uio->uio_resid) + VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); + if (error) { + (void) UFS_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) == IO_SYNC) + error = UFS_UPDATE(vp, NULL, NULL, UPDATE_WAIT); + else + UFS_WAPBL_UPDATE(vp, NULL, NULL, 0); + KASSERT(vp->v_size == ip->i_size); + if ((ioflag & IO_JOURNALLOCKED) == 0) + UFS_WAPBL_END(vp->v_mount); + fstrans_done(vp->v_mount); + + return (error); +} + +/* + * UFS op for writing via the buffer cache + */ +int +BUFWR(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t cred) +{ + struct inode *ip; + struct ufsmount *ump; + FS *fs; + int flags; + struct buf *bp; + off_t osize, origoff; + int resid, xfersize, size, blkoffset; + daddr_t lbn; + int extended=0; + int error; +#ifdef LFS_READWRITE + bool need_unreserve = false; +#endif + + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(vp->v_type == VDIR || vp->v_type == VLNK); + KASSERT(vp->v_type != VDIR || ISSET(ioflag, IO_SYNC)); + KASSERT(uio->uio_rw == UIO_WRITE); + + ip = VTOI(vp); + ump = ip->i_ump; + fs = ip->I_FS; + + KASSERT(vp->v_size == ip->i_size); + + if (uio->uio_offset < 0 || + uio->uio_resid > ump->um_maxfilesize || + uio->uio_offset > (ump->um_maxfilesize - uio->uio_resid)) + return EFBIG; +#ifdef LFS_READWRITE + KASSERT(vp != fs->lfs_ivnode); +#endif + if (uio->uio_resid == 0) + return 0; + + fstrans_start(vp->v_mount, FSTRANS_SHARED); + + flags = ioflag & IO_SYNC ? B_SYNC : 0; + origoff = uio->uio_offset; + resid = uio->uio_resid; + osize = ip->i_size; + error = 0; + + KASSERT(vp->v_type != VREG); + KASSERT(ISSET(ioflag, IO_JOURNALLOCKED)); + UFS_WAPBL_JLOCK_ASSERT(vp->v_mount); + +#ifdef LFS_READWRITE + lfs_availwait(fs, btofsb(fs, uio->uio_resid)); + lfs_check(vp, LFS_UNUSED_LBN, 0); +#endif /* !LFS_READWRITE */ + + /* XXX Should never have pages cached here. */ mutex_enter(vp->v_interlock); VOP_PUTPAGES(vp, trunc_page(origoff), round_page(origoff + resid), PGO_CLEANIT | PGO_FREE | PGO_SYNCIO | PGO_JOURNALLOCKED); @@ -462,8 +601,8 @@ WRITE(void *v) break; need_unreserve = true; #endif - error = UFS_BALLOC(vp, uio->uio_offset, xfersize, - ap->a_cred, flags, &bp); + error = UFS_BALLOC(vp, uio->uio_offset, xfersize, cred, flags, + &bp); if (error) break; @@ -516,13 +655,12 @@ WRITE(void *v) * we clear the setuid and setgid bits as a precaution against * tampering. */ -out: ip->i_flag |= IN_CHANGE | IN_UPDATE; if (vp->v_mount->mnt_flag & MNT_RELATIME) ip->i_flag |= IN_ACCESS; - if (resid > uio->uio_resid && ap->a_cred) { + if (resid > uio->uio_resid && cred) { if (ip->i_mode & ISUID) { - if (kauth_authorize_vnode(ap->a_cred, + if (kauth_authorize_vnode(cred, KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) { ip->i_mode &= ~ISUID; DIP_ASSIGN(ip, mode, ip->i_mode); @@ -530,7 +668,7 @@ out: } if (ip->i_mode & ISGID) { - if (kauth_authorize_vnode(ap->a_cred, + if (kauth_authorize_vnode(cred, KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) { ip->i_mode &= ~ISGID; DIP_ASSIGN(ip, mode, ip->i_mode); @@ -540,7 +678,7 @@ out: if (resid > uio->uio_resid) VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); if (error) { - (void) UFS_TRUNCATE(vp, osize, ioflag & IO_SYNC, ap->a_cred); + (void) UFS_TRUNCATE(vp, osize, ioflag & IO_SYNC, cred); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } else if (resid > uio->uio_resid && (ioflag & IO_SYNC) == IO_SYNC) Index: src/sys/ufs/ufs/ufs_rename.c diff -u src/sys/ufs/ufs/ufs_rename.c:1.11 src/sys/ufs/ufs/ufs_rename.c:1.12 --- src/sys/ufs/ufs/ufs_rename.c:1.11 Sun May 25 13:45:39 2014 +++ src/sys/ufs/ufs/ufs_rename.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_rename.c,v 1.11 2014/05/25 13:45:39 hannken Exp $ */ +/* $NetBSD: ufs_rename.c,v 1.12 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ufs_rename.c,v 1.11 2014/05/25 13:45:39 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ufs_rename.c,v 1.12 2015/03/27 17:27:56 riastradh Exp $"); #include <sys/param.h> #include <sys/buf.h> @@ -874,8 +874,8 @@ ufs_read_dotdot(struct vnode *vp, kauth_ KASSERT(ino_ret != NULL); KASSERT(vp->v_type == VDIR); - error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); + error = ufs_bufio(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, + IO_NODELOCKED, cred, NULL, NULL); if (error) return error; Index: src/sys/ufs/ufs/ufs_vnops.c diff -u src/sys/ufs/ufs/ufs_vnops.c:1.225 src/sys/ufs/ufs/ufs_vnops.c:1.226 --- src/sys/ufs/ufs/ufs_vnops.c:1.225 Tue Mar 17 09:39:29 2015 +++ src/sys/ufs/ufs/ufs_vnops.c Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_vnops.c,v 1.225 2015/03/17 09:39:29 hannken Exp $ */ +/* $NetBSD: ufs_vnops.c,v 1.226 2015/03/27 17:27:56 riastradh Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -66,7 +66,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.225 2015/03/17 09:39:29 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.226 2015/03/27 17:27:56 riastradh Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -1210,9 +1210,9 @@ ufs_symlink(void *v) ip->i_flag |= IN_ACCESS; UFS_WAPBL_UPDATE(vp, NULL, NULL, 0); } else - error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED | IO_JOURNALLOCKED, - ap->a_cnp->cn_cred, NULL, NULL); + error = ufs_bufio(UIO_WRITE, vp, ap->a_target, len, (off_t)0, + IO_NODELOCKED | IO_JOURNALLOCKED, ap->a_cnp->cn_cred, NULL, + NULL); UFS_WAPBL_END1(ap->a_dvp->v_mount, ap->a_dvp); VOP_UNLOCK(vp); if (error) @@ -1279,7 +1279,7 @@ ufs_readdir(void *v) cdbuf = kmem_alloc(cdbufsz, KM_SLEEP); aiov.iov_base = cdbuf; aiov.iov_len = rcount; - error = VOP_READ(vp, &auio, 0, ap->a_cred); + error = UFS_BUFRD(vp, &auio, 0, ap->a_cred); if (error != 0) { kmem_free(cdbuf, cdbufsz); return error; @@ -1399,7 +1399,7 @@ ufs_readlink(void *v) uiomove((char *)SHORTLINK(ip), isize, ap->a_uio); return (0); } - return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); + return (UFS_BUFRD(vp, ap->a_uio, 0, ap->a_cred)); } /* @@ -1887,3 +1887,49 @@ ufs_gop_markupdate(struct vnode *vp, int ip->i_flag |= mask; } } + +int +ufs_bufio(enum uio_rw rw, struct vnode *vp, void *buf, size_t len, off_t off, + int ioflg, kauth_cred_t cred, size_t *aresid, struct lwp *l) +{ + struct iovec iov; + struct uio uio; + int error; + + /* XXX Remove me -- all callers should be locked. */ + if (!ISSET(ioflg, IO_NODELOCKED)) { + if (rw == UIO_READ) + vn_lock(vp, LK_SHARED | LK_RETRY); + else /* UIO_WRITE */ + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + } + + iov.iov_base = buf; + iov.iov_len = len; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = len; + uio.uio_offset = off; + uio.uio_rw = rw; + UIO_SETUP_SYSSPACE(&uio); + + switch (rw) { + case UIO_READ: + error = UFS_BUFRD(vp, &uio, ioflg, cred); + break; + case UIO_WRITE: + error = UFS_BUFWR(vp, &uio, ioflg, cred); + break; + default: + panic("invalid uio rw: %d", (int)rw); + } + + if (aresid) + *aresid = uio.uio_resid; + else if (uio.uio_resid && error == 0) + error = EIO; + + if (!ISSET(ioflg, IO_NODELOCKED)) + VOP_UNLOCK(vp); + return error; +} Index: src/sys/ufs/ufs/ufsmount.h diff -u src/sys/ufs/ufs/ufsmount.h:1.42 src/sys/ufs/ufs/ufsmount.h:1.43 --- src/sys/ufs/ufs/ufsmount.h:1.42 Tue Mar 17 09:39:29 2015 +++ src/sys/ufs/ufs/ufsmount.h Fri Mar 27 17:27:56 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ufsmount.h,v 1.42 2015/03/17 09:39:29 hannken Exp $ */ +/* $NetBSD: ufsmount.h,v 1.43 2015/03/27 17:27:56 riastradh Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1993 @@ -137,6 +137,8 @@ struct ufs_ops { int (*uo_balloc)(struct vnode *, off_t, int, kauth_cred_t, int, struct buf **); void (*uo_snapgone)(struct vnode *); + int (*uo_bufrd)(struct vnode *, struct uio *, int, kauth_cred_t); + int (*uo_bufwr)(struct vnode *, struct uio *, int, kauth_cred_t); }; #define UFS_OPS(vp) (VFSTOUFS((vp)->v_mount)->um_ops) @@ -151,6 +153,10 @@ struct ufs_ops { (*UFS_OPS(vp)->uo_balloc)((vp), (off), (size), (cr), (flags), (bpp)) #define UFS_SNAPGONE(vp) \ (*UFS_OPS(vp)->uo_snapgone)((vp)) +#define UFS_BUFRD(vp, uio, ioflag, cred) \ + (*UFS_OPS(vp)->uo_bufrd)((vp), (uio), (ioflag), (cred)) +#define UFS_BUFWR(vp, uio, ioflag, cred) \ + (*UFS_OPS(vp)->uo_bufwr)((vp), (uio), (ioflag), (cred)) /* UFS-specific flags */ #define UFS_NEEDSWAP 0x01 /* filesystem metadata need byte-swapping */