summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_vnops.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2013-08-21 17:36:01 +0000
committerkib <kib@FreeBSD.org>2013-08-21 17:36:01 +0000
commit6a459eb27c6a215b645e8673b5275f94003510a4 (patch)
tree6d3150c62239e19e8c4f26b32d07e284cd50fd1d /sys/kern/vfs_vnops.c
parenta3e8f2c6dc03b8fa8727dd48f5055f20459d8b18 (diff)
downloadFreeBSD-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.c71
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);
+}
OpenPOWER on IntegriCloud