diff options
-rw-r--r-- | sys/fs/devfs/devfs_vnops.c | 1 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 68 | ||||
-rw-r--r-- | sys/kern/vfs_vnops.c | 71 | ||||
-rw-r--r-- | sys/sys/file.h | 11 |
4 files changed, 86 insertions, 65 deletions
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 468e76b..711a7ea 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -1697,6 +1697,7 @@ static struct fileops devfs_ops_f = { .fo_chmod = vn_chmod, .fo_chown = vn_chown, .fo_sendfile = vn_sendfile, + .fo_seek = vn_seek, .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE }; diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 9acf195..2877ad2 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1879,77 +1879,15 @@ sys_lseek(td, uap) int whence; } */ *uap; { - struct ucred *cred = td->td_ucred; struct file *fp; - struct vnode *vp; - struct vattr vattr; - off_t foffset, offset, size; - int error, noneg; + int error; AUDIT_ARG_FD(uap->fd); if ((error = fget(td, uap->fd, CAP_SEEK, &fp)) != 0) return (error); - if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) { - fdrop(fp, td); - return (ESPIPE); - } - vp = fp->f_vnode; - foffset = foffset_lock(fp, 0); - noneg = (vp->v_type != VCHR); - offset = uap->offset; - switch (uap->whence) { - case L_INCR: - if (noneg && - (foffset < 0 || - (offset > 0 && foffset > OFF_MAX - offset))) { - error = EOVERFLOW; - break; - } - offset += foffset; - break; - case L_XTND: - vn_lock(vp, LK_SHARED | LK_RETRY); - error = VOP_GETATTR(vp, &vattr, cred); - VOP_UNLOCK(vp, 0); - if (error) - break; - - /* - * If the file references a disk device, then fetch - * the media size and use that to determine the ending - * offset. - */ - if (vattr.va_size == 0 && vp->v_type == VCHR && - fo_ioctl(fp, DIOCGMEDIASIZE, &size, cred, td) == 0) - vattr.va_size = size; - if (noneg && - (vattr.va_size > OFF_MAX || - (offset > 0 && vattr.va_size > OFF_MAX - offset))) { - error = EOVERFLOW; - break; - } - offset += vattr.va_size; - break; - case L_SET: - break; - case SEEK_DATA: - error = fo_ioctl(fp, FIOSEEKDATA, &offset, cred, td); - break; - case SEEK_HOLE: - error = fo_ioctl(fp, FIOSEEKHOLE, &offset, cred, td); - break; - default: - error = EINVAL; - } - if (error == 0 && noneg && offset < 0) - error = EINVAL; - if (error != 0) - goto drop; - VFS_KNOTE_UNLOCKED(vp, 0); - *(off_t *)(td->td_retval) = offset; -drop: + error = (fp->f_ops->fo_flags & DFLAG_SEEKABLE) != 0 ? + fo_seek(fp, uap->offset, uap->whence, td) : ESPIPE; fdrop(fp, td); - foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0); return (error); } diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 59b8995..c53030a 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/disk.h> #include <sys/fcntl.h> #include <sys/file.h> #include <sys/kdb.h> @@ -101,6 +102,7 @@ struct fileops vnops = { .fo_chmod = vn_chmod, .fo_chown = vn_chown, .fo_sendfile = vn_sendfile, + .fo_seek = vn_seek, .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE }; @@ -2010,3 +2012,72 @@ unlock: *off = noff; return (error); } + +int +vn_seek(struct file *fp, off_t offset, int whence, struct thread *td) +{ + struct ucred *cred; + struct vnode *vp; + struct vattr vattr; + off_t foffset, size; + int error, noneg; + + cred = td->td_ucred; + vp = fp->f_vnode; + foffset = foffset_lock(fp, 0); + noneg = (vp->v_type != VCHR); + error = 0; + switch (whence) { + case L_INCR: + if (noneg && + (foffset < 0 || + (offset > 0 && foffset > OFF_MAX - offset))) { + error = EOVERFLOW; + break; + } + offset += foffset; + break; + case L_XTND: + vn_lock(vp, LK_SHARED | LK_RETRY); + error = VOP_GETATTR(vp, &vattr, cred); + VOP_UNLOCK(vp, 0); + if (error) + break; + + /* + * If the file references a disk device, then fetch + * the media size and use that to determine the ending + * offset. + */ + if (vattr.va_size == 0 && vp->v_type == VCHR && + fo_ioctl(fp, DIOCGMEDIASIZE, &size, cred, td) == 0) + vattr.va_size = size; + if (noneg && + (vattr.va_size > OFF_MAX || + (offset > 0 && vattr.va_size > OFF_MAX - offset))) { + error = EOVERFLOW; + break; + } + offset += vattr.va_size; + break; + case L_SET: + break; + case SEEK_DATA: + error = fo_ioctl(fp, FIOSEEKDATA, &offset, cred, td); + break; + case SEEK_HOLE: + error = fo_ioctl(fp, FIOSEEKHOLE, &offset, cred, td); + break; + default: + error = EINVAL; + } + if (error == 0 && noneg && offset < 0) + error = EINVAL; + if (error != 0) + goto drop; + VFS_KNOTE_UNLOCKED(vp, 0); + *(off_t *)(td->td_retval) = offset; +drop: + foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0); + return (error); +} diff --git a/sys/sys/file.h b/sys/sys/file.h index cc4e900..72c512f 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -108,6 +108,8 @@ typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid, typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, int kflags, struct thread *td); +typedef int fo_seek_t(struct file *fp, off_t offset, int whence, + struct thread *td); typedef int fo_flags_t; struct fileops { @@ -122,6 +124,7 @@ struct fileops { fo_chmod_t *fo_chmod; fo_chown_t *fo_chown; fo_sendfile_t *fo_sendfile; + fo_seek_t *fo_seek; fo_flags_t fo_flags; /* DFLAG_* below */ }; @@ -242,6 +245,7 @@ fo_chown_t invfo_chown; fo_sendfile_t invfo_sendfile; fo_sendfile_t vn_sendfile; +fo_seek_t vn_seek; void finit(struct file *, u_int, short, void *, struct fileops *); int fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp); @@ -370,6 +374,13 @@ fo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, nbytes, sent, flags, kflags, td)); } +static __inline int +fo_seek(struct file *fp, off_t offset, int whence, struct thread *td) +{ + + return ((*fp->f_ops->fo_seek)(fp, offset, whence, td)); +} + #endif /* _KERNEL */ #endif /* !SYS_FILE_H */ |