Add a vop_stdioctl() that performs a trivial FIOSEEKDATA/FIOSEEKHOLE.

Without this patch, when an application performed lseek(SEEK_DATA/SEEK_HOLE)
on a file in a file system that does not have its own VOP_IOCTL(), the
lseek(2) fails with errno ENOTTY. This didn't seem appropriate, since
ENOTTY is not listed as an error return by either the lseek(2) man page
nor the POSIX draft for lseek(2).
A discussion on freebsd-current@ seemed to indicate that implementing
a trivial algorithm that returns the offset argument for FIOSEEKDATA and
returns the file's size for FIOSEEKHOLE was the preferred fix.
http://docs.FreeBSD.org/cgi/mid.cgi?CAOtMX2iiQdv1+15e1N_r7V6aCx_VqAJCTP1AW+qs3Yg7sPg9wA
The Linux kernel appears to implement this trivial algorithm as well.

This patch adds a vop_stdioctl() that implements this trivial algorithm.
It returns errors consistent with vn_bmap_seekhole() and, as such, will
still return ENOTTY for non-regular files.

I have proposed a separate patch that maps errors not described by the
lseek(2) man page nor POSIX draft to EINVAL. This patch is under separate
review.

Reviewed by:	kib
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D21299
This commit is contained in:
Rick Macklem 2019-08-19 00:29:05 +00:00
parent a5e5548c88
commit 2e1b32c0e3

View File

@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/event.h>
#include <sys/filio.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
@ -86,6 +87,7 @@ static int vop_stdadd_writecount(struct vop_add_writecount_args *ap);
static int vop_stdcopy_file_range(struct vop_copy_file_range_args *ap);
static int vop_stdfdatasync(struct vop_fdatasync_args *ap);
static int vop_stdgetpages_async(struct vop_getpages_async_args *ap);
static int vop_stdioctl(struct vop_ioctl_args *ap);
/*
* This vnode table stores what we want to do if the filesystem doesn't
@ -118,7 +120,7 @@ struct vop_vector default_vnodeops = {
.vop_getpages_async = vop_stdgetpages_async,
.vop_getwritemount = vop_stdgetwritemount,
.vop_inactive = VOP_NULL,
.vop_ioctl = VOP_ENOTTY,
.vop_ioctl = vop_stdioctl,
.vop_kqfilter = vop_stdkqfilter,
.vop_islocked = vop_stdislocked,
.vop_lock1 = vop_stdlock,
@ -1155,6 +1157,41 @@ vop_stdadd_writecount(struct vop_add_writecount_args *ap)
return (error);
}
static int
vop_stdioctl(struct vop_ioctl_args *ap)
{
struct vnode *vp;
struct vattr va;
off_t *offp;
int error;
switch (ap->a_command) {
case FIOSEEKDATA:
case FIOSEEKHOLE:
vp = ap->a_vp;
error = vn_lock(vp, LK_SHARED);
if (error != 0)
return (EBADF);
if (vp->v_type == VREG)
error = VOP_GETATTR(vp, &va, ap->a_cred);
else
error = ENOTTY;
if (error == 0) {
offp = ap->a_data;
if (*offp < 0 || *offp >= va.va_size)
error = ENXIO;
else if (ap->a_command == FIOSEEKHOLE)
*offp = va.va_size;
}
VOP_UNLOCK(vp, 0);
break;
default:
error = ENOTTY;
break;
}
return (error);
}
/*
* vfs default ops
* used to fill the vfs function table to get reasonable default return values.