From f8a246b9791d1450cf4945cc7b38f651a3a456ee Mon Sep 17 00:00:00 2001 From: jhb Date: Mon, 7 Jan 2008 20:05:19 +0000 Subject: 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 --- sys/dev/streams/streams.c | 1 + sys/fs/devfs/devfs_vnops.c | 8 +++++ sys/fs/fifofs/fifo_vnops.c | 9 +++++ sys/kern/kern_descrip.c | 8 +++++ sys/kern/kern_event.c | 11 ++++++ sys/kern/sys_generic.c | 67 +++++++++++++++++++++++++++++++++- sys/kern/sys_pipe.c | 14 ++++++++ sys/kern/sys_socket.c | 9 +++++ sys/kern/uipc_mqueue.c | 9 +++++ sys/kern/vfs_syscalls.c | 90 ---------------------------------------------- sys/kern/vfs_vnops.c | 49 +++++++++++++++++++++++++ sys/opencrypto/cryptodev.c | 14 ++++++++ sys/sys/file.h | 16 +++++++++ sys/sys/syscallsubr.h | 1 + 14 files changed, 215 insertions(+), 91 deletions(-) diff --git a/sys/dev/streams/streams.c b/sys/dev/streams/streams.c index 55df9e5..5b97f85 100644 --- a/sys/dev/streams/streams.c +++ b/sys/dev/streams/streams.c @@ -90,6 +90,7 @@ static struct cdev *dt_ptm, *dt_arp, *dt_icmp, *dt_ip, *dt_tcp, *dt_udp, static struct fileops svr4_netops = { .fo_read = soo_read, .fo_write = soo_write, + .fo_truncate = soo_truncate, .fo_ioctl = soo_ioctl, .fo_poll = soo_poll, .fo_kqfilter = soo_kqfilter, diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 269f2c3..7c753f1 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -1278,6 +1278,13 @@ devfs_symlink(struct vop_symlink_args *ap) return (devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, td)); } +static int +devfs_truncate_f(struct file *fp, off_t length, struct ucred *cred, struct thread *td) +{ + + return (vnops.fo_truncate(fp, length, cred, td)); +} + /* ARGSUSED */ static int devfs_write_f(struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct thread *td) @@ -1322,6 +1329,7 @@ dev2udev(struct cdev *x) static struct fileops devfs_ops_f = { .fo_read = devfs_read_f, .fo_write = devfs_write_f, + .fo_truncate = devfs_truncate_f, .fo_ioctl = devfs_ioctl_f, .fo_poll = devfs_poll_f, .fo_kqfilter = devfs_kqfilter_f, diff --git a/sys/fs/fifofs/fifo_vnops.c b/sys/fs/fifofs/fifo_vnops.c index 78718db..9b0ad1e 100644 --- a/sys/fs/fifofs/fifo_vnops.c +++ b/sys/fs/fifofs/fifo_vnops.c @@ -61,10 +61,12 @@ static fo_poll_t fifo_poll_f; static fo_kqfilter_t fifo_kqfilter_f; static fo_stat_t fifo_stat_f; static fo_close_t fifo_close_f; +static fo_truncate_t fifo_truncate_f; struct fileops fifo_ops_f = { .fo_read = fifo_read_f, .fo_write = fifo_write_f, + .fo_truncate = fifo_truncate_f, .fo_ioctl = fifo_ioctl_f, .fo_poll = fifo_poll_f, .fo_kqfilter = fifo_kqfilter_f, @@ -724,6 +726,13 @@ fifo_stat_f(struct file *fp, struct stat *sb, struct ucred *cred, struct thread } static int +fifo_truncate_f(struct file *fp, off_t length, struct ucred *cred, struct thread *td) +{ + + return (vnops.fo_truncate(fp, length, cred, td)); +} + +static int fifo_write_f(struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct thread *td) { struct fifoinfo *fip; 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 #include #include +#include #include #include #include @@ -69,7 +70,7 @@ __FBSDID("$FreeBSD$"); #include #endif -#include +#include 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 diff --git a/sys/opencrypto/cryptodev.c b/sys/opencrypto/cryptodev.c index c9fc6d2..fe7372b 100644 --- a/sys/opencrypto/cryptodev.c +++ b/sys/opencrypto/cryptodev.c @@ -86,6 +86,8 @@ struct fcrypt { static int cryptof_rw(struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct thread *); +static int cryptof_truncate(struct file *, off_t, struct ucred *, + struct thread *); static int cryptof_ioctl(struct file *, u_long, void *, struct ucred *, struct thread *); static int cryptof_poll(struct file *, int, struct ucred *, struct thread *); @@ -97,6 +99,7 @@ static int cryptof_close(struct file *, struct thread *); static struct fileops cryptofops = { .fo_read = cryptof_rw, .fo_write = cryptof_rw, + .fo_truncate = cryptof_truncate, .fo_ioctl = cryptof_ioctl, .fo_poll = cryptof_poll, .fo_kqfilter = cryptof_kqfilter, @@ -129,6 +132,17 @@ cryptof_rw( return (EIO); } +static int +cryptof_truncate( + struct file *fp, + off_t length, + struct ucred *active_cred, + struct thread *td) +{ + + return (EINVAL); +} + /* * Check a crypto identifier to see if it requested * a software device/driver. This can be done either diff --git a/sys/sys/file.h b/sys/sys/file.h index c5f4afb..757c884 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -69,6 +69,8 @@ typedef int fo_rdwr_t(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td); #define FOF_OFFSET 1 /* Use the offset in uio argument */ +typedef int fo_truncate_t(struct file *fp, off_t length, + struct ucred *active_cred, struct thread *td); typedef int fo_ioctl_t(struct file *fp, u_long com, void *data, struct ucred *active_cred, struct thread *td); typedef int fo_poll_t(struct file *fp, int events, @@ -82,6 +84,7 @@ typedef int fo_flags_t; struct fileops { fo_rdwr_t *fo_read; fo_rdwr_t *fo_write; + fo_truncate_t *fo_truncate; fo_ioctl_t *fo_ioctl; fo_poll_t *fo_poll; fo_kqfilter_t *fo_kqfilter; @@ -175,6 +178,7 @@ int _fdrop(struct file *fp, struct thread *td); */ fo_rdwr_t soo_read; fo_rdwr_t soo_write; +fo_truncate_t soo_truncate; fo_ioctl_t soo_ioctl; fo_poll_t soo_poll; fo_kqfilter_t soo_kqfilter; @@ -195,6 +199,7 @@ void fputsock(struct socket *sp); static __inline fo_rdwr_t fo_read; static __inline fo_rdwr_t fo_write; +static __inline fo_truncate_t fo_truncate; static __inline fo_ioctl_t fo_ioctl; static __inline fo_poll_t fo_poll; static __inline fo_kqfilter_t fo_kqfilter; @@ -226,6 +231,17 @@ fo_write(fp, uio, active_cred, flags, td) } static __inline int +fo_truncate(fp, length, active_cred, td) + struct file *fp; + off_t length; + struct ucred *active_cred; + struct thread *td; +{ + + return ((*fp->f_ops->fo_truncate)(fp, length, active_cred, td)); +} + +static __inline int fo_ioctl(fp, com, data, active_cred, td) struct file *fp; u_long com; diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 1cba85d..b8d6113 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -82,6 +82,7 @@ int kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg); int kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf); int kern_fstat(struct thread *td, int fd, struct stat *sbp); int kern_fstatfs(struct thread *td, int fd, struct statfs *buf); +int kern_ftruncate(struct thread *td, int fd, off_t length); int kern_futimes(struct thread *td, int fd, struct timeval *tptr, enum uio_seg tptrseg); int kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize, -- cgit v1.1