diff options
author | kib <kib@FreeBSD.org> | 2013-08-21 17:36:01 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2013-08-21 17:36:01 +0000 |
commit | 6a459eb27c6a215b645e8673b5275f94003510a4 (patch) | |
tree | 6d3150c62239e19e8c4f26b32d07e284cd50fd1d /sys/kern/vfs_vnops.c | |
parent | a3e8f2c6dc03b8fa8727dd48f5055f20459d8b18 (diff) | |
download | FreeBSD-src-6a459eb27c6a215b645e8673b5275f94003510a4.zip FreeBSD-src-6a459eb27c6a215b645e8673b5275f94003510a4.tar.gz |
Make the seek a method of the struct fileops.
Tested by: pho
Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'sys/kern/vfs_vnops.c')
-rw-r--r-- | sys/kern/vfs_vnops.c | 71 |
1 files changed, 71 insertions, 0 deletions
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); +} |