diff options
author | Philip Guenthe <guenther@cvs.openbsd.org> | 2012-07-11 12:39:21 +0000 |
---|---|---|
committer | Philip Guenthe <guenther@cvs.openbsd.org> | 2012-07-11 12:39:21 +0000 |
commit | c50d2a31964c3040622e25b55a6fd7a777283a3f (patch) | |
tree | 4d06e274aef8c7c009927f37d69e192b9718868f | |
parent | dcd93e0f82fbc4edfb9f7d89873dc721922d4735 (diff) |
If the current offset is strictly less than the process filesize
rlimit, then a write that would take it over the limit should be
clamped, making it a partial write.
ok beck@
-rw-r--r-- | sys/kern/vfs_vnops.c | 37 | ||||
-rw-r--r-- | sys/msdosfs/msdosfs_vnops.c | 22 | ||||
-rw-r--r-- | sys/nfs/nfs_bio.c | 39 | ||||
-rw-r--r-- | sys/sys/vnode.h | 3 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_readwrite.c | 22 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_vnops.c | 20 |
6 files changed, 86 insertions, 57 deletions
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index ca7421f5442..b7927924bff 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_vnops.c,v 1.70 2011/11/27 21:31:08 guenther Exp $ */ +/* $OpenBSD: vfs_vnops.c,v 1.71 2012/07/11 12:39:20 guenther Exp $ */ /* $NetBSD: vfs_vnops.c,v 1.20 1996/02/04 02:18:41 christos Exp $ */ /* @@ -45,6 +45,8 @@ #include <sys/stat.h> #include <sys/buf.h> #include <sys/proc.h> +#include <sys/resourcevar.h> +#include <sys/signalvar.h> #include <sys/mount.h> #include <sys/namei.h> #include <sys/vnode.h> @@ -223,6 +225,39 @@ vn_writechk(struct vnode *vp) } /* + * Check whether a write operation would exceed the file size rlimit + * for the process, if one should be applied for this operation. + * If a partial write should take place, the uio is adjusted and the + * amount by which the request would have exceeded the limit is returned + * via the 'overrun' argument. + */ +int +vn_fsizechk(struct vnode *vp, struct uio *uio, int ioflag, int *overrun) +{ + struct proc *p = uio->uio_procp; + + *overrun = 0; + if (vp->v_type == VREG && p != NULL && !(ioflag & IO_NOLIMIT)) { + rlim_t limit = p->p_rlimit[RLIMIT_FSIZE].rlim_cur; + + /* if already at or over the limit, send the signal and fail */ + if (uio->uio_offset >= limit) { + psignal(p, SIGXFSZ); + return (EFBIG); + } + + /* otherwise, clamp the write to stay under the limit */ + if (uio->uio_resid > limit - uio->uio_offset) { + *overrun = uio->uio_resid - (limit - uio->uio_offset); + uio->uio_resid = limit - uio->uio_offset; + } + } + + return (0); +} + + +/* * Mark a vnode as being the text image of a running process. */ void diff --git a/sys/msdosfs/msdosfs_vnops.c b/sys/msdosfs/msdosfs_vnops.c index 0bb791d3efd..2ebcf3f8362 100644 --- a/sys/msdosfs/msdosfs_vnops.c +++ b/sys/msdosfs/msdosfs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: msdosfs_vnops.c,v 1.81 2012/06/20 17:30:22 matthew Exp $ */ +/* $OpenBSD: msdosfs_vnops.c,v 1.82 2012/07/11 12:39:20 guenther Exp $ */ /* $NetBSD: msdosfs_vnops.c,v 1.63 1997/10/17 11:24:19 ws Exp $ */ /*- @@ -517,6 +517,7 @@ msdosfs_write(void *v) int n; int croffset; int resid; + int overrun; int extended = 0; uint32_t osize; int error = 0; @@ -525,7 +526,6 @@ msdosfs_write(void *v) struct buf *bp; int ioflag = ap->a_ioflag; struct uio *uio = ap->a_uio; - struct proc *p = uio->uio_procp; struct vnode *vp = ap->a_vp; struct vnode *thisvp; struct denode *dep = VTODE(vp); @@ -561,15 +561,9 @@ msdosfs_write(void *v) if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX) return (EFBIG); - /* - * If they've exceeded their filesize limit, tell them about it. - */ - if (p && - ((uio->uio_offset + uio->uio_resid) > - p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { - psignal(p, SIGXFSZ); - return (EFBIG); - } + /* do the filesize rlimit check */ + if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) + return (error); /* * If the offset we are starting the write at is beyond the end of @@ -579,7 +573,7 @@ msdosfs_write(void *v) */ if (uio->uio_offset > dep->de_FileSize) { if ((error = deextend(dep, uio->uio_offset, cred)) != 0) - return (error); + goto out; } /* @@ -704,6 +698,10 @@ errexit: } } else if (ioflag & IO_SYNC) error = deupdat(dep, 1); + +out: + /* correct the result for writes clamped by vn_fsizechk() */ + uio->uio_resid += overrun; return (error); } diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c index 2a48e32ed33..5c9b44b75d6 100644 --- a/sys/nfs/nfs_bio.c +++ b/sys/nfs/nfs_bio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nfs_bio.c,v 1.72 2010/08/07 03:50:02 krw Exp $ */ +/* $OpenBSD: nfs_bio.c,v 1.73 2012/07/11 12:39:20 guenther Exp $ */ /* $NetBSD: nfs_bio.c,v 1.25.4.2 1996/07/08 20:47:04 jtc Exp $ */ /* @@ -257,6 +257,7 @@ nfs_write(void *v) struct nfsmount *nmp = VFSTONFS(vp->v_mount); daddr64_t lbn, bn; int n, on, error = 0, extended = 0, wrotedta = 0, truncated = 0; + int overrun; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE) @@ -291,15 +292,10 @@ nfs_write(void *v) return (EINVAL); if (uio->uio_resid == 0) return (0); - /* - * Maybe this should be above the vnode op call, but so long as - * file servers have no limits, i don't think it matters - */ - if (p && uio->uio_offset + uio->uio_resid > - p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { - psignal(p, SIGXFSZ); - return (EFBIG); - } + + /* do the filesize rlimit check */ + if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) + return (error); /* * update the cache write creds for this node. @@ -329,8 +325,10 @@ nfs_write(void *v) bn = lbn * (biosize / DEV_BSIZE); again: bp = nfs_getcacheblk(vp, bn, biosize, p); - if (!bp) - return (EINTR); + if (!bp) { + error = EINTR; + goto out; + } np->n_flag |= NMODIFIED; if (uio->uio_offset + n > np->n_size) { np->n_size = uio->uio_offset + n; @@ -347,8 +345,10 @@ again: if (bp->b_dirtyend > 0 && (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) { bp->b_proc = p; - if (VOP_BWRITE(bp) == EINTR) - return (EINTR); + if (VOP_BWRITE(bp) == EINTR) { + error = EINTR; + goto out; + } goto again; } @@ -356,7 +356,7 @@ again: if (error) { bp->b_flags |= B_ERROR; brelse(bp); - return (error); + goto out; } if (bp->b_dirtyend > 0) { bp->b_dirtyoff = min(on, bp->b_dirtyoff); @@ -396,7 +396,7 @@ again: bp->b_proc = p; error = VOP_BWRITE(bp); if (error) - return (error); + goto out; } else if ((n + on) == biosize) { bp->b_proc = NULL; bp->b_flags |= B_ASYNC; @@ -406,11 +406,16 @@ again: } } while (uio->uio_resid > 0 && n > 0); +/*out: XXX belongs here??? */ if (wrotedta) VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0) | (truncated ? NOTE_TRUNCATE : 0)); - return (0); +out: + /* correct the result for writes clamped by vn_fsizechk() */ + uio->uio_resid += overrun; + + return (error); } /* diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index ff5c091428a..a56aba13641 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vnode.h,v 1.110 2011/12/09 16:14:54 nicm Exp $ */ +/* $OpenBSD: vnode.h,v 1.111 2012/07/11 12:39:20 guenther Exp $ */ /* $NetBSD: vnode.h,v 1.38 1996/02/29 20:59:05 cgd Exp $ */ /* @@ -651,6 +651,7 @@ int vn_stat(struct vnode *, struct stat *, struct proc *); int vn_statfile(struct file *, struct stat *, struct proc *); int vn_lock(struct vnode *, int, struct proc *); int vn_writechk(struct vnode *); +int vn_fsizechk(struct vnode *, struct uio *, int, int *); int vn_ioctl(struct file *, u_long, caddr_t, struct proc *); void vn_marktext(struct vnode *); diff --git a/sys/ufs/ext2fs/ext2fs_readwrite.c b/sys/ufs/ext2fs/ext2fs_readwrite.c index a03470b37a3..8e4060695d0 100644 --- a/sys/ufs/ext2fs/ext2fs_readwrite.c +++ b/sys/ufs/ext2fs/ext2fs_readwrite.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ext2fs_readwrite.c,v 1.23 2011/07/04 04:30:41 tedu Exp $ */ +/* $OpenBSD: ext2fs_readwrite.c,v 1.24 2012/07/11 12:39:20 guenther Exp $ */ /* $NetBSD: ext2fs_readwrite.c,v 1.16 2001/02/27 04:37:47 chs Exp $ */ /*- @@ -162,10 +162,9 @@ ext2fs_write(void *v) struct inode *ip; struct m_ext2fs *fs; struct buf *bp; - struct proc *p; int32_t lbn; off_t osize; - int blkoffset, error, flags, ioflag, resid, size, xfersize; + int blkoffset, error, flags, ioflag, resid, size, xfersize, overrun; ioflag = ap->a_ioflag; uio = ap->a_uio; @@ -207,17 +206,10 @@ ext2fs_write(void *v) (u_int64_t)uio->uio_offset + uio->uio_resid > ((u_int64_t)0x80000000 * fs->e2fs_bsize - 1)) return (EFBIG); - /* - * Maybe this should be above the vnode op call, but so long as - * file servers have no limits, I don't think it matters. - */ - 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); - } + + /* do the filesize rlimit check */ + if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) + return (error); resid = uio->uio_resid; osize = ext2fs_size(ip); @@ -282,5 +274,7 @@ ext2fs_write(void *v) } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) { error = ext2fs_update(ip, NULL, NULL, 1); } + /* correct the result for writes clamped by vn_fsizechk() */ + uio->uio_resid += overrun; return (error); } diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index 66a7cc0e43a..0d11c4bb5f2 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_vnops.c,v 1.67 2011/08/16 14:36:39 thib Exp $ */ +/* $OpenBSD: ffs_vnops.c,v 1.68 2012/07/11 12:39:20 guenther Exp $ */ /* $NetBSD: ffs_vnops.c,v 1.7 1996/05/11 18:27:24 mycroft Exp $ */ /* @@ -299,6 +299,7 @@ ffs_write(void *v) daddr64_t lbn; off_t osize; int blkoffset, error, extended, flags, ioflag, resid, size, xfersize; + int overrun; extern int num_indirdep, max_indirdep; extended = 0; @@ -340,17 +341,10 @@ ffs_write(void *v) if (uio->uio_offset < 0 || (u_int64_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize) return (EFBIG); - /* - * Maybe this should be above the vnode op call, but so long as - * file servers have no limits, I don't think it matters. - */ - p = uio->uio_procp; - if (vp->v_type == VREG && p && !(ioflag & IO_NOLIMIT) && - uio->uio_offset + uio->uio_resid > - p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { - psignal(p, SIGXFSZ); - return (EFBIG); - } + + /* do the filesize rlimit check */ + if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) + return (error); resid = uio->uio_resid; osize = DIP(ip, size); @@ -436,6 +430,8 @@ ffs_write(void *v) VOP_FSYNC(vp, NULL, MNT_WAIT, p); } } + /* correct the result for writes clamped by vn_fsizechk() */ + uio->uio_resid += overrun; return (error); } |