diff options
author | jhb <jhb@FreeBSD.org> | 2008-01-07 20:05:19 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2008-01-07 20:05:19 +0000 |
commit | f8a246b9791d1450cf4945cc7b38f651a3a456ee (patch) | |
tree | 4cf9280569e048ce55d457d60918586c5cf023e5 /sys/kern | |
parent | 1b130ab3271ae64d5c2979889a661e27bbe422d9 (diff) | |
download | FreeBSD-src-f8a246b9791d1450cf4945cc7b38f651a3a456ee.zip FreeBSD-src-f8a246b9791d1450cf4945cc7b38f651a3a456ee.tar.gz |
Make ftruncate a 'struct file' operation rather than a vnode operation.
This makes it possible to support ftruncate() on non-vnode file types in
the future.
- 'struct fileops' grows a 'fo_truncate' method to handle an ftruncate() on
a given file descriptor.
- ftruncate() moves to kern/sys_generic.c and now just fetches a file
object and invokes fo_truncate().
- The vnode-specific portions of ftruncate() move to vn_truncate() in
vfs_vnops.c which implements fo_truncate() for vnode file types.
- Non-vnode file types return EINVAL in their fo_truncate() method.
Submitted by: rwatson
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_descrip.c | 8 | ||||
-rw-r--r-- | sys/kern/kern_event.c | 11 | ||||
-rw-r--r-- | sys/kern/sys_generic.c | 67 | ||||
-rw-r--r-- | sys/kern/sys_pipe.c | 14 | ||||
-rw-r--r-- | sys/kern/sys_socket.c | 9 | ||||
-rw-r--r-- | sys/kern/uipc_mqueue.c | 9 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 90 | ||||
-rw-r--r-- | sys/kern/vfs_vnops.c | 49 |
8 files changed, 166 insertions, 91 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index d68854e..24438dd 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -2760,6 +2760,13 @@ badfo_readwrite(struct file *fp, struct uio *uio, struct ucred *active_cred, int } static int +badfo_truncate(struct file *fp, off_t length, struct ucred *active_cred, struct thread *td) +{ + + return (EINVAL); +} + +static int badfo_ioctl(struct file *fp, u_long com, void *data, struct ucred *active_cred, struct thread *td) { @@ -2797,6 +2804,7 @@ badfo_close(struct file *fp, struct thread *td) struct fileops badfileops = { .fo_read = badfo_readwrite, .fo_write = badfo_readwrite, + .fo_truncate = badfo_truncate, .fo_ioctl = badfo_ioctl, .fo_poll = badfo_poll, .fo_kqfilter = badfo_kqfilter, diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c index b5d01d0..171a150 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -105,6 +105,7 @@ static void kqueue_fo_release(int filt); static fo_rdwr_t kqueue_read; static fo_rdwr_t kqueue_write; +static fo_truncate_t kqueue_truncate; static fo_ioctl_t kqueue_ioctl; static fo_poll_t kqueue_poll; static fo_kqfilter_t kqueue_kqfilter; @@ -114,6 +115,7 @@ static fo_close_t kqueue_close; static struct fileops kqueueops = { .fo_read = kqueue_read, .fo_write = kqueue_write, + .fo_truncate = kqueue_truncate, .fo_ioctl = kqueue_ioctl, .fo_poll = kqueue_poll, .fo_kqfilter = kqueue_kqfilter, @@ -1324,6 +1326,15 @@ kqueue_write(struct file *fp, struct uio *uio, struct ucred *active_cred, /*ARGSUSED*/ static int +kqueue_truncate(struct file *fp, off_t length, struct ucred *active_cred, + struct thread *td) +{ + + return (EINVAL); +} + +/*ARGSUSED*/ +static int kqueue_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index 9c800f3..90946ea 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socketvar.h> #include <sys/uio.h> #include <sys/kernel.h> +#include <sys/ktr.h> #include <sys/limits.h> #include <sys/malloc.h> #include <sys/poll.h> @@ -69,7 +70,7 @@ __FBSDID("$FreeBSD$"); #include <sys/ktrace.h> #endif -#include <sys/ktr.h> +#include <security/audit/audit.h> static MALLOC_DEFINE(M_IOCTLOPS, "ioctlops", "ioctl data buffer"); static MALLOC_DEFINE(M_SELECT, "select", "select() buffer"); @@ -544,6 +545,70 @@ dofilewrite(td, fd, fp, auio, offset, flags) return (error); } +/* + * Truncate a file given a file descriptor. + * + * Can't use fget_write() here, since must return EINVAL and not EBADF if the + * descriptor isn't writable. + */ +int +kern_ftruncate(td, fd, length) + struct thread *td; + int fd; + off_t length; +{ + struct file *fp; + int error; + + AUDIT_ARG(fd, fd); + if (length < 0) + return (EINVAL); + error = fget(td, fd, &fp); + if (error) + return (error); + AUDIT_ARG(file, td->td_proc, fp); + if (!(fp->f_flag & FWRITE)) { + fdrop(fp, td); + return (EINVAL); + } + error = fo_truncate(fp, length, td->td_ucred, td); + fdrop(fp, td); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct ftruncate_args { + int fd; + int pad; + off_t length; +}; +#endif +int +ftruncate(td, uap) + struct thread *td; + struct ftruncate_args *uap; +{ + + return (kern_ftruncate(td, uap->fd, uap->length)); +} + +#if defined(COMPAT_43) +#ifndef _SYS_SYSPROTO_H_ +struct oftruncate_args { + int fd; + long length; +}; +#endif +int +oftruncate(td, uap) + struct thread *td; + struct oftruncate_args *uap; +{ + + return (kern_ftruncate(td, uap->fd, uap->length)); +} +#endif /* COMPAT_43 */ + #ifndef _SYS_SYSPROTO_H_ struct ioctl_args { int fd; diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index 27ecf80..457f30b 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -140,6 +140,7 @@ __FBSDID("$FreeBSD$"); */ static fo_rdwr_t pipe_read; static fo_rdwr_t pipe_write; +static fo_truncate_t pipe_truncate; static fo_ioctl_t pipe_ioctl; static fo_poll_t pipe_poll; static fo_kqfilter_t pipe_kqfilter; @@ -149,6 +150,7 @@ static fo_close_t pipe_close; static struct fileops pipeops = { .fo_read = pipe_read, .fo_write = pipe_write, + .fo_truncate = pipe_truncate, .fo_ioctl = pipe_ioctl, .fo_poll = pipe_poll, .fo_kqfilter = pipe_kqfilter, @@ -1230,6 +1232,18 @@ pipe_write(fp, uio, active_cred, flags, td) return (error); } +/* ARGSUSED */ +static int +pipe_truncate(fp, length, active_cred, td) + struct file *fp; + off_t length; + struct ucred *active_cred; + struct thread *td; +{ + + return (EINVAL); +} + /* * we implement a very minimal set of ioctls for compatibility with sockets. */ diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c index 7e4547c..8e6b5f2 100644 --- a/sys/kern/sys_socket.c +++ b/sys/kern/sys_socket.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); struct fileops socketops = { .fo_read = soo_read, .fo_write = soo_write, + .fo_truncate = soo_truncate, .fo_ioctl = soo_ioctl, .fo_poll = soo_poll, .fo_kqfilter = soo_kqfilter, @@ -110,6 +111,14 @@ soo_write(struct file *fp, struct uio *uio, struct ucred *active_cred, } int +soo_truncate(struct file *fp, off_t length, struct ucred *active_cred, + struct thread *td) +{ + + return (EINVAL); +} + +int soo_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c index 8fe34bc..2d2a477 100644 --- a/sys/kern/uipc_mqueue.c +++ b/sys/kern/uipc_mqueue.c @@ -2317,6 +2317,14 @@ mqf_write(struct file *fp, struct uio *uio, struct ucred *active_cred, } static int +mqf_truncate(struct file *fp, off_t length, struct ucred *active_cred, + struct thread *td) +{ + + return (EINVAL); +} + +static int mqf_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { @@ -2433,6 +2441,7 @@ filt_mqwrite(struct knote *kn, long hint) static struct fileops mqueueops = { .fo_read = mqf_read, .fo_write = mqf_write, + .fo_truncate = mqf_truncate, .fo_ioctl = mqf_ioctl, .fo_poll = mqf_poll, .fo_kqfilter = mqf_kqfilter, diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 0e42ea3..483651e 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -3086,68 +3086,6 @@ kern_truncate(struct thread *td, char *path, enum uio_seg pathseg, off_t length) return (error); } -/* - * Truncate a file given a file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct ftruncate_args { - int fd; - int pad; - off_t length; -}; -#endif -int -ftruncate(td, uap) - struct thread *td; - register struct ftruncate_args /* { - int fd; - int pad; - off_t length; - } */ *uap; -{ - struct mount *mp; - struct vattr vattr; - struct vnode *vp; - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, uap->fd); - if (uap->length < 0) - return(EINVAL); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - if ((fp->f_flag & FWRITE) == 0) { - fdrop(fp, td); - return (EINVAL); - } - vp = fp->f_vnode; - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - goto drop; - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, vp, ARG_VNODE1); - if (vp->v_type == VDIR) - error = EISDIR; -#ifdef MAC - else if ((error = mac_vnode_check_write(td->td_ucred, fp->f_cred, - vp))) { - } -#endif - else if ((error = vn_writechk(vp)) == 0) { - VATTR_NULL(&vattr); - vattr.va_size = uap->length; - error = VOP_SETATTR(vp, &vattr, fp->f_cred, td); - } - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); -drop: - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} - #if defined(COMPAT_43) /* * Truncate a file given its path name. @@ -3176,34 +3114,6 @@ otruncate(td, uap) nuap.length = uap->length; return (truncate(td, &nuap)); } - -/* - * Truncate a file given a file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct oftruncate_args { - int fd; - long length; -}; -#endif -int -oftruncate(td, uap) - struct thread *td; - register struct oftruncate_args /* { - int fd; - long length; - } */ *uap; -{ - struct ftruncate_args /* { - int fd; - int pad; - off_t length; - } */ nuap; - - nuap.fd = uap->fd; - nuap.length = uap->length; - return (ftruncate(td, &nuap)); -} #endif /* COMPAT_43 */ /* Versions with the pad argument */ diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 4826b08..10898e5 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$"); static fo_rdwr_t vn_read; static fo_rdwr_t vn_write; +static fo_truncate_t vn_truncate; static fo_ioctl_t vn_ioctl; static fo_poll_t vn_poll; static fo_kqfilter_t vn_kqfilter; @@ -75,6 +76,7 @@ static fo_close_t vn_closefile; struct fileops vnops = { .fo_read = vn_read, .fo_write = vn_write, + .fo_truncate = vn_truncate, .fo_ioctl = vn_ioctl, .fo_poll = vn_poll, .fo_kqfilter = vn_kqfilter, @@ -607,6 +609,53 @@ unlock: } /* + * File table truncate routine. + */ +static int +vn_truncate(fp, length, active_cred, td) + struct file *fp; + off_t length; + struct ucred *active_cred; + struct thread *td; +{ + struct vattr vattr; + struct mount *mp; + struct vnode *vp; + int vfslocked; + int error; + + vp = fp->f_vnode; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); + if (error) { + VFS_UNLOCK_GIANT(vfslocked); + return (error); + } + VOP_LEASE(vp, td, active_cred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + if (vp->v_type == VDIR) { + error = EISDIR; + goto out; + } +#ifdef MAC + error = mac_vnode_check_write(active_cred, fp->f_cred, vp); + if (error) + goto out; +#endif + error = vn_writechk(vp); + if (error == 0) { + VATTR_NULL(&vattr); + vattr.va_size = length; + error = VOP_SETATTR(vp, &vattr, fp->f_cred, td); + } +out: + VOP_UNLOCK(vp, 0, td); + vn_finished_write(mp); + VFS_UNLOCK_GIANT(vfslocked); + return (error); +} + +/* * File table vnode stat routine. */ static int |