summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Guenthe <guenther@cvs.openbsd.org>2012-07-11 12:39:21 +0000
committerPhilip Guenthe <guenther@cvs.openbsd.org>2012-07-11 12:39:21 +0000
commitc50d2a31964c3040622e25b55a6fd7a777283a3f (patch)
tree4d06e274aef8c7c009927f37d69e192b9718868f
parentdcd93e0f82fbc4edfb9f7d89873dc721922d4735 (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.c37
-rw-r--r--sys/msdosfs/msdosfs_vnops.c22
-rw-r--r--sys/nfs/nfs_bio.c39
-rw-r--r--sys/sys/vnode.h3
-rw-r--r--sys/ufs/ext2fs/ext2fs_readwrite.c22
-rw-r--r--sys/ufs/ffs/ffs_vnops.c20
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);
}