summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/fs/devfs/devfs_vnops.c1
-rw-r--r--sys/kern/vfs_syscalls.c68
-rw-r--r--sys/kern/vfs_vnops.c71
-rw-r--r--sys/sys/file.h11
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 */
OpenPOWER on IntegriCloud