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
This commit is contained in:
John Baldwin 2008-01-07 20:05:19 +00:00
parent 0460d3516d
commit e46502943a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=175140
14 changed files with 215 additions and 91 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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,
@ -723,6 +725,13 @@ fifo_stat_f(struct file *fp, struct stat *sb, struct ucred *cred, struct thread
return (vnops.fo_stat(fp, sb, cred, td));
}
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)
{

View File

@ -2759,6 +2759,13 @@ badfo_readwrite(struct file *fp, struct uio *uio, struct ucred *active_cred, int
return (EBADF);
}
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,

View File

@ -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,
@ -1322,6 +1324,15 @@ kqueue_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
return (ENXIO);
}
/*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,

View File

@ -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;

View File

@ -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.
*/

View File

@ -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,
@ -109,6 +110,14 @@ soo_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
return (error);
}
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)

View File

@ -2316,6 +2316,14 @@ mqf_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
return (EOPNOTSUPP);
}
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,

View File

@ -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 */

View File

@ -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,
@ -606,6 +608,53 @@ vn_write(fp, uio, active_cred, flags, td)
return (error);
}
/*
* 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.
*/

View File

@ -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

View File

@ -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;
@ -225,6 +230,17 @@ fo_write(fp, uio, active_cred, flags, td)
return ((*fp->f_ops->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;

View File

@ -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,