aboutsummaryrefslogtreecommitdiff
path: root/sys/fs
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2020-12-29 00:26:31 +0000
committerAlan Somers <asomers@FreeBSD.org>2020-12-31 15:51:47 +0000
commit37df9d3bba8577fcdd63382ff5a4a5cbb4aa55b4 (patch)
tree2b5a984b9aac87453992bc94972f122ce38fb372 /sys/fs
parent68de3bb59f64a37f3473517a56c2767e652c36b8 (diff)
fusefs: update FUSE protocol to 7.24 and implement FUSE_LSEEK
FUSE_LSEEK reports holes on fuse file systems, and is used for example by bsdtar. MFC after: 2 weeks Relnotes: yes Reviewed by: cem Differential Revision: https://reviews.freebsd.org/D27804
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/fuse/fuse_internal.c9
-rw-r--r--sys/fs/fuse/fuse_ipc.c8
-rw-r--r--sys/fs/fuse/fuse_ipc.h37
-rw-r--r--sys/fs/fuse/fuse_kernel.h17
-rw-r--r--sys/fs/fuse/fuse_vfsops.c2
-rw-r--r--sys/fs/fuse/fuse_vnops.c141
6 files changed, 193 insertions, 21 deletions
diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c
index 79365b8802df..2faad7cd8651 100644
--- a/sys/fs/fuse/fuse_internal.c
+++ b/sys/fs/fuse/fuse_internal.c
@@ -219,7 +219,7 @@ fuse_internal_access(struct vnode *vp,
SDT_PROBE0(fusefs, , internal, access_vadmin);
}
- if (!fsess_isimpl(mp, FUSE_ACCESS))
+ if (fsess_not_impl(mp, FUSE_ACCESS))
return 0;
if ((mode & (VWRITE | VAPPEND)) != 0)
@@ -337,14 +337,14 @@ fuse_internal_fsync(struct vnode *vp,
int op = FUSE_FSYNC;
int err = 0;
- if (!fsess_isimpl(vnode_mount(vp),
+ if (fsess_not_impl(vnode_mount(vp),
(vnode_vtype(vp) == VDIR ? FUSE_FSYNCDIR : FUSE_FSYNC))) {
return 0;
}
if (vnode_isdir(vp))
op = FUSE_FSYNCDIR;
- if (!fsess_isimpl(mp, op))
+ if (fsess_not_impl(mp, op))
return 0;
fdisp_init(&fdi, sizeof(*ffsi));
@@ -1051,6 +1051,9 @@ fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
else
data->cache_mode = FUSE_CACHE_WT;
+ if (!fuse_libabi_geq(data, 7, 24))
+ fsess_set_notimpl(data->mp, FUSE_LSEEK);
+
out:
if (err) {
fdata_set_dead(data);
diff --git a/sys/fs/fuse/fuse_ipc.c b/sys/fs/fuse/fuse_ipc.c
index 776a4ecd11d7..d3738da26b34 100644
--- a/sys/fs/fuse/fuse_ipc.c
+++ b/sys/fs/fuse/fuse_ipc.c
@@ -230,7 +230,7 @@ fuse_interrupt_send(struct fuse_ticket *otick, int err)
* If the fuse daemon doesn't support interrupts, then there's
* nothing more that we can do
*/
- if (!fsess_isimpl(data->mp, FUSE_INTERRUPT))
+ if (fsess_not_impl(data->mp, FUSE_INTERRUPT))
return;
/*
@@ -423,7 +423,7 @@ fticket_wait_answer(struct fuse_ticket *ftick)
struct fuse_data *data = ftick->tk_data;
bool interrupted = false;
- if (fsess_isimpl(ftick->tk_data->mp, FUSE_INTERRUPT) &&
+ if (fsess_maybe_impl(ftick->tk_data->mp, FUSE_INTERRUPT) &&
data->dataflags & FSESS_INTR) {
SIGEMPTYSET(blockedset);
} else {
@@ -851,6 +851,10 @@ fuse_body_audit(struct fuse_ticket *ftick, size_t blen)
err = (blen == 0) ? 0 : EINVAL;
break;
+ case FUSE_LSEEK:
+ err = (blen == sizeof(struct fuse_lseek_out)) ? 0 : EINVAL;
+ break;
+
default:
panic("FUSE: opcodes out of sync (%d)\n", opcode);
}
diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h
index c8a665abded7..2ed75b3319e3 100644
--- a/sys/fs/fuse/fuse_ipc.h
+++ b/sys/fs/fuse/fuse_ipc.h
@@ -212,7 +212,15 @@ struct fuse_data {
int daemon_timeout;
int linux_errnos;
unsigned time_gran;
+ /* A bitmask of FUSE RPCs that are not implemented by the server */
uint64_t notimpl;
+ /*
+ * A bitmask of FUSE RPCs that are implemented by the server.
+ * If an operation is not present in either notimpl or isimpl, then it
+ * may be implemented by the server, but the kernel doesn't know for
+ * sure.
+ */
+ uint64_t isimpl;
uint64_t mnt_flag;
enum fuse_data_cache_mode cache_mode;
};
@@ -240,13 +248,40 @@ fuse_get_mpdata(struct mount *mp)
}
static inline bool
-fsess_isimpl(struct mount *mp, int opcode)
+fsess_is_impl(struct mount *mp, int opcode)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ return ((data->isimpl & (1ULL << opcode)) != 0);
+
+}
+
+static inline bool
+fsess_maybe_impl(struct mount *mp, int opcode)
{
struct fuse_data *data = fuse_get_mpdata(mp);
return ((data->notimpl & (1ULL << opcode)) == 0);
}
+
+static inline bool
+fsess_not_impl(struct mount *mp, int opcode)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ return ((data->notimpl & (1ULL << opcode)) != 0);
+
+}
+
+static inline void
+fsess_set_impl(struct mount *mp, int opcode)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ data->isimpl |= (1ULL << opcode);
+}
+
static inline void
fsess_set_notimpl(struct mount *mp, int opcode)
{
diff --git a/sys/fs/fuse/fuse_kernel.h b/sys/fs/fuse/fuse_kernel.h
index fa3c63417f19..6e97b04a733f 100644
--- a/sys/fs/fuse/fuse_kernel.h
+++ b/sys/fs/fuse/fuse_kernel.h
@@ -102,6 +102,9 @@
* - add ctime and ctimensec to fuse_setattr_in
* - add FUSE_RENAME2 request
* - add FUSE_NO_OPEN_SUPPORT flag
+ *
+ * 7.24
+ * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
*/
#ifndef _FUSE_FUSE_KERNEL_H
@@ -117,7 +120,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 23
+#define FUSE_KERNEL_MINOR_VERSION 24
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -340,6 +343,7 @@ enum fuse_opcode {
FUSE_FALLOCATE = 43,
FUSE_READDIRPLUS = 44,
FUSE_RENAME2 = 45,
+ FUSE_LSEEK = 46,
#ifdef linux
/* CUSE specific operations */
@@ -751,4 +755,15 @@ struct fuse_notify_retrieve_in {
uint64_t dummy4;
};
+struct fuse_lseek_in {
+ uint64_t fh;
+ uint64_t offset;
+ uint32_t whence;
+ uint32_t padding;
+};
+
+struct fuse_lseek_out {
+ uint64_t offset;
+};
+
#endif /* _FUSE_FUSE_KERNEL_H */
diff --git a/sys/fs/fuse/fuse_vfsops.c b/sys/fs/fuse/fuse_vfsops.c
index 04d273127ade..7f47f8800994 100644
--- a/sys/fs/fuse/fuse_vfsops.c
+++ b/sys/fs/fuse/fuse_vfsops.c
@@ -501,7 +501,7 @@ fuse_vfsop_unmount(struct mount *mp, int mntflags)
if (fdata_get_dead(data)) {
goto alreadydead;
}
- if (fsess_isimpl(mp, FUSE_DESTROY)) {
+ if (fsess_maybe_impl(mp, FUSE_DESTROY)) {
fdisp_init(&fdi, 0);
fdisp_make(&fdi, FUSE_DESTROY, mp, 0, td, NULL);
diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
index fde673d1f5f3..efac7e041cf6 100644
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/conf.h>
+#include <sys/filio.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/queue.h>
@@ -136,6 +137,7 @@ static vop_fsync_t fuse_vnop_fsync;
static vop_getattr_t fuse_vnop_getattr;
static vop_getextattr_t fuse_vnop_getextattr;
static vop_inactive_t fuse_vnop_inactive;
+static vop_ioctl_t fuse_vnop_ioctl;
static vop_link_t fuse_vnop_link;
static vop_listextattr_t fuse_vnop_listextattr;
static vop_lookup_t fuse_vnop_lookup;
@@ -190,11 +192,7 @@ struct vop_vector fuse_vnops = {
.vop_getattr = fuse_vnop_getattr,
.vop_getextattr = fuse_vnop_getextattr,
.vop_inactive = fuse_vnop_inactive,
- /*
- * TODO: implement vop_ioctl after upgrading to protocol 7.16.
- * FUSE_IOCTL was added in 7.11, but 32-bit compat is broken until
- * 7.16.
- */
+ .vop_ioctl = fuse_vnop_ioctl,
.vop_link = fuse_vnop_link,
.vop_listextattr = fuse_vnop_listextattr,
.vop_lookup = fuse_vnop_lookup,
@@ -284,7 +282,7 @@ fuse_flush(struct vnode *vp, struct ucred *cred, pid_t pid, int fflag)
struct mount *mp = vnode_mount(vp);
int err;
- if (!fsess_isimpl(vnode_mount(vp), FUSE_FLUSH))
+ if (fsess_not_impl(vnode_mount(vp), FUSE_FLUSH))
return 0;
err = fuse_filehandle_getrw(vp, fflag, &fufh, cred, pid);
@@ -318,6 +316,42 @@ fuse_fifo_close(struct vop_close_args *ap)
return (fifo_specops.vop_close(ap));
}
+/* Send FUSE_LSEEK for this node */
+static int
+fuse_vnop_do_lseek(struct vnode *vp, struct thread *td, struct ucred *cred,
+ pid_t pid, off_t *offp, int whence)
+{
+ struct fuse_dispatcher fdi;
+ struct fuse_filehandle *fufh;
+ struct fuse_lseek_in *flsi;
+ struct fuse_lseek_out *flso;
+ struct mount *mp = vnode_mount(vp);
+ int err;
+
+ MPASS(VOP_ISLOCKED(vp));
+
+ err = fuse_filehandle_getrw(vp, FREAD, &fufh, cred, pid);
+ if (err)
+ return (err);
+ fdisp_init(&fdi, sizeof(*flsi));
+ fdisp_make_vp(&fdi, FUSE_LSEEK, vp, td, cred);
+ flsi = fdi.indata;
+ flsi->fh = fufh->fh_id;
+ flsi->offset = *offp;
+ flsi->whence = whence;
+ err = fdisp_wait_answ(&fdi);
+ if (err == ENOSYS) {
+ fsess_set_notimpl(mp, FUSE_LSEEK);
+ } else if (err == 0) {
+ fsess_set_impl(mp, FUSE_LSEEK);
+ flso = fdi.answ;
+ *offp = flso->offset;
+ }
+ fdisp_destroy(&fdi);
+
+ return (err);
+}
+
/*
struct vnop_access_args {
struct vnode *a_vp;
@@ -516,7 +550,7 @@ fuse_vnop_bmap(struct vop_bmap_args *ap)
*runp = 0;
}
- if (fsess_isimpl(mp, FUSE_BMAP)) {
+ if (fsess_maybe_impl(mp, FUSE_BMAP)) {
fdisp_init(&fdi, sizeof(*fbi));
fdisp_make_vp(&fdi, FUSE_BMAP, vp, td, td->td_ucred);
fbi = fdi.indata;
@@ -652,7 +686,7 @@ fuse_vnop_create(struct vop_create_args *ap)
if (vap->va_type != VREG)
return (EINVAL);
- if (!fsess_isimpl(mp, FUSE_CREATE) || vap->va_type == VSOCK) {
+ if (fsess_not_impl(mp, FUSE_CREATE) || vap->va_type == VSOCK) {
/* Fallback to FUSE_MKNOD/FUSE_OPEN */
fdisp_make_mknod_for_fallback(fdip, cnp, dvp, parentnid, td,
cred, mode, &op);
@@ -884,6 +918,56 @@ fuse_vnop_inactive(struct vop_inactive_args *ap)
}
/*
+ struct vnop_ioctl_args {
+ struct vnode *a_vp;
+ u_long a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ };
+*/
+static int
+fuse_vnop_ioctl(struct vop_ioctl_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct mount *mp = vnode_mount(vp);
+ struct ucred *cred = ap->a_cred;
+ off_t *offp;
+ pid_t pid = ap->a_td->td_proc->p_pid;
+ int err;
+
+ switch (ap->a_command) {
+ case FIOSEEKDATA:
+ case FIOSEEKHOLE:
+ /* Call FUSE_LSEEK, if we can, or fall back to vop_stdioctl */
+ if (fsess_maybe_impl(mp, FUSE_LSEEK)) {
+ int whence;
+
+ offp = ap->a_data;
+ if (ap->a_command == FIOSEEKDATA)
+ whence = SEEK_DATA;
+ else
+ whence = SEEK_HOLE;
+
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ err = fuse_vnop_do_lseek(vp, ap->a_td, cred, pid, offp,
+ whence);
+ VOP_UNLOCK(vp);
+ }
+ if (fsess_not_impl(mp, FUSE_LSEEK))
+ err = vop_stdioctl(ap);
+ break;
+ default:
+ /* TODO: implement FUSE_IOCTL */
+ err = ENOTTY;
+ break;
+ }
+ return (err);
+}
+
+
+/*
struct vnop_link_args {
struct vnode *a_tdvp;
struct vnode *a_vp;
@@ -1337,6 +1421,8 @@ fuse_vnop_open(struct vop_open_args *ap)
static int
fuse_vnop_pathconf(struct vop_pathconf_args *ap)
{
+ struct vnode *vp = ap->a_vp;
+ struct mount *mp;
switch (ap->a_name) {
case _PC_FILESIZEBITS:
@@ -1354,6 +1440,35 @@ fuse_vnop_pathconf(struct vop_pathconf_args *ap)
case _PC_NO_TRUNC:
*ap->a_retval = 1;
return (0);
+ case _PC_MIN_HOLE_SIZE:
+ /*
+ * The FUSE protocol provides no mechanism for a server to
+ * report _PC_MIN_HOLE_SIZE. It's a protocol bug. Instead,
+ * return EINVAL if the server does not support FUSE_LSEEK, or
+ * 1 if it does.
+ */
+ mp = vnode_mount(vp);
+ if (!fsess_is_impl(mp, FUSE_LSEEK) &&
+ !fsess_not_impl(mp, FUSE_LSEEK)) {
+ off_t offset = 0;
+
+ /* Issue a FUSE_LSEEK to find out if it's implemented */
+ fuse_vnop_do_lseek(vp, curthread, curthread->td_ucred,
+ curthread->td_proc->p_pid, &offset, SEEK_DATA);
+ }
+
+ if (fsess_is_impl(mp, FUSE_LSEEK)) {
+ *ap->a_retval = 1;
+ return (0);
+ } else {
+ /*
+ * Probably FUSE_LSEEK is not implemented. It might
+ * be, if the FUSE_LSEEK above returned an error like
+ * EACCES, but in that case we can't tell, so it's
+ * safest to report EINVAL anyway.
+ */
+ return (EINVAL);
+ }
default:
return (vop_stdpathconf(ap));
}
@@ -2035,7 +2150,7 @@ fuse_vnop_getextattr(struct vop_getextattr_args *ap)
if (fuse_isdeadfs(vp))
return (ENXIO);
- if (!fsess_isimpl(mp, FUSE_GETXATTR))
+ if (fsess_not_impl(mp, FUSE_GETXATTR))
return EOPNOTSUPP;
err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
@@ -2121,7 +2236,7 @@ fuse_vnop_setextattr(struct vop_setextattr_args *ap)
if (fuse_isdeadfs(vp))
return (ENXIO);
- if (!fsess_isimpl(mp, FUSE_SETXATTR))
+ if (fsess_not_impl(mp, FUSE_SETXATTR))
return EOPNOTSUPP;
if (vfs_isrdonly(mp))
@@ -2133,7 +2248,7 @@ fuse_vnop_setextattr(struct vop_setextattr_args *ap)
* If we got here as fallback from VOP_DELETEEXTATTR, then
* return EOPNOTSUPP.
*/
- if (!fsess_isimpl(mp, FUSE_REMOVEXATTR))
+ if (fsess_not_impl(mp, FUSE_REMOVEXATTR))
return (EOPNOTSUPP);
else
return (EINVAL);
@@ -2286,7 +2401,7 @@ fuse_vnop_listextattr(struct vop_listextattr_args *ap)
if (fuse_isdeadfs(vp))
return (ENXIO);
- if (!fsess_isimpl(mp, FUSE_LISTXATTR))
+ if (fsess_not_impl(mp, FUSE_LISTXATTR))
return EOPNOTSUPP;
err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
@@ -2409,7 +2524,7 @@ fuse_vnop_deleteextattr(struct vop_deleteextattr_args *ap)
if (fuse_isdeadfs(vp))
return (ENXIO);
- if (!fsess_isimpl(mp, FUSE_REMOVEXATTR))
+ if (fsess_not_impl(mp, FUSE_REMOVEXATTR))
return EOPNOTSUPP;
if (vfs_isrdonly(mp))