diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c index 25278c8..f518209 100644 --- a/sys/kern/vfs_default.c +++ b/sys/kern/vfs_default.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ static int get_next_dirent(struct vnode *vp, struct dirent **dpp, struct thread *td); static int dirent_exists(struct vnode *vp, const char *dirname, struct thread *td); +static vop_ioctl_t vop_stdioctl; #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) @@ -107,7 +109,7 @@ struct vop_vector default_vnodeops = { .vop_getpages = vop_stdgetpages, .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, @@ -1064,6 +1066,74 @@ vop_stdunp_detach(struct vop_unp_detach_args *ap) return (0); } +static int +vop_stdioctl(struct vop_ioctl_args *ap) +{ + struct vnode *vp; + struct vattr va; + daddr_t bn, bnp; + uint64_t bsize; + off_t off, noff; + int error; + + vp = ap->a_vp; + error = ENOTTY; + + switch (ap->a_command) { + case FIOSEEKDATA: + case FIOSEEKHOLE: + off = *(off_t *)ap->a_data; + if (vn_lock(vp, LK_SHARED) != 0) { + error = EBADF; + break; + } + if (vp->v_type != VREG) { + error = ENOTTY; + goto holeend; + } + error = VOP_GETATTR(vp, &va, ap->a_cred); + if (error != 0) + goto holeend; + if (off >= va.va_size) { + error = ENXIO; + goto holeend; + } + bsize = vp->v_mount->mnt_stat.f_iosize; + for (bn = off / bsize, noff = off; noff < va.va_size; + bn++, noff += bsize) { + error = VOP_BMAP(vp, bn, NULL, &bnp, NULL, NULL); + if (error == EOPNOTSUPP) { + error = ENOTTY; + goto holeend; + } + if (bnp == -1 && ap->a_command == FIOSEEKHOLE) { + noff = bn * bsize; + if (noff < off) + noff = off; + goto holeend; + } else if (bnp != -1 && ap->a_command == FIOSEEKDATA) { + noff = bn * bsize; + if (noff < off) + noff = off; + goto holeend; + } + } + if (noff > va.va_size) + noff = va.va_size; + /* noff == va.va_size */ + if (ap->a_command == FIOSEEKDATA) + error = ESRCH; +holeend: + VOP_UNLOCK(vp, 0); + if (error == 0) + *(off_t *)ap->a_data = noff; + break; + } + return (error); +} + + + /* * vfs default ops * used to fill the vfs function table to get reasonable default return values.