aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Tomasz Napierala <trasz@FreeBSD.org>2020-11-09 08:53:15 +0000
committerEdward Tomasz Napierala <trasz@FreeBSD.org>2020-11-09 08:53:15 +0000
commite3b1c847a4237ad990cab71427927ced34b47507 (patch)
tree3ca311db88d94e89d783fa9b1a145bd8dbf164b1
parent8b8af16875c788e63523dc574fb2e02b8728c651 (diff)
downloadsrc-e3b1c847a4237ad990cab71427927ced34b47507.tar.gz
src-e3b1c847a4237ad990cab71427927ced34b47507.zip
Make it possible to mount a fuse filesystem, such as squashfuse,
from a Linux binary. Should come handy for AppImages. Reviewed by: asomers MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D26959
Notes
Notes: svn path=/head/; revision=367517
-rw-r--r--sys/compat/linux/linux_file.c44
-rw-r--r--sys/fs/fuse/fuse_device.c12
-rw-r--r--sys/fs/fuse/fuse_ipc.h1
-rw-r--r--sys/fs/fuse/fuse_vfsops.c4
4 files changed, 53 insertions, 8 deletions
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c
index ebac7f916cd8..70cf0525f194 100644
--- a/sys/compat/linux/linux_file.c
+++ b/sys/compat/linux/linux_file.c
@@ -1181,13 +1181,15 @@ linux_pwritev(struct thread *td, struct linux_pwritev_args *uap)
int
linux_mount(struct thread *td, struct linux_mount_args *args)
{
- char fstypename[MFSNAMELEN];
- char *mntonname, *mntfromname;
+ struct mntarg *ma = NULL;
+ char *fstypename, *mntonname, *mntfromname, *data;
int error, fsflags;
+ fstypename = malloc(MNAMELEN, M_TEMP, M_WAITOK);
mntonname = malloc(MNAMELEN, M_TEMP, M_WAITOK);
mntfromname = malloc(MNAMELEN, M_TEMP, M_WAITOK);
- error = copyinstr(args->filesystemtype, fstypename, MFSNAMELEN - 1,
+ data = NULL;
+ error = copyinstr(args->filesystemtype, fstypename, MNAMELEN - 1,
NULL);
if (error != 0)
goto out;
@@ -1208,6 +1210,31 @@ linux_mount(struct thread *td, struct linux_mount_args *args)
strcpy(fstypename, "linprocfs");
} else if (strcmp(fstypename, "vfat") == 0) {
strcpy(fstypename, "msdosfs");
+ } else if (strcmp(fstypename, "fuse") == 0) {
+ char *fuse_options, *fuse_option, *fuse_name;
+
+ if (strcmp(mntfromname, "fuse") == 0)
+ strcpy(mntfromname, "/dev/fuse");
+
+ strcpy(fstypename, "fusefs");
+ data = malloc(MNAMELEN, M_TEMP, M_WAITOK);
+ error = copyinstr(args->data, data, MNAMELEN - 1, NULL);
+ if (error != 0)
+ goto out;
+
+ fuse_options = data;
+ while ((fuse_option = strsep(&fuse_options, ",")) != NULL) {
+ fuse_name = strsep(&fuse_option, "=");
+ if (fuse_name == NULL || fuse_option == NULL)
+ goto out;
+ ma = mount_arg(ma, fuse_name, fuse_option, -1);
+ }
+
+ /*
+ * The FUSE server uses Linux errno values instead of FreeBSD
+ * ones; add a flag to tell fuse(4) to do errno translation.
+ */
+ ma = mount_arg(ma, "linux_errnos", "1", -1);
}
fsflags = 0;
@@ -1225,14 +1252,15 @@ linux_mount(struct thread *td, struct linux_mount_args *args)
if (args->rwflag & LINUX_MS_REMOUNT)
fsflags |= MNT_UPDATE;
- error = kernel_vmount(fsflags,
- "fstype", fstypename,
- "fspath", mntonname,
- "from", mntfromname,
- NULL);
+ ma = mount_arg(ma, "fstype", fstypename, -1);
+ ma = mount_arg(ma, "fspath", mntonname, -1);
+ ma = mount_arg(ma, "from", mntfromname, -1);
+ error = kernel_mount(ma, fsflags);
out:
+ free(fstypename, M_TEMP);
free(mntonname, M_TEMP);
free(mntfromname, M_TEMP);
+ free(data, M_TEMP);
return (error);
}
diff --git a/sys/fs/fuse/fuse_device.c b/sys/fs/fuse/fuse_device.c
index 203da2c6f7ba..1698f962415f 100644
--- a/sys/fs/fuse/fuse_device.c
+++ b/sys/fs/fuse/fuse_device.c
@@ -89,6 +89,9 @@ __FBSDID("$FreeBSD$");
#include "fuse_internal.h"
#include "fuse_ipc.h"
+#include <compat/linux/linux_errno.h>
+#include <compat/linux/linux_errno.inc>
+
SDT_PROVIDER_DECLARE(fusefs);
/*
* Fuse trace probe:
@@ -451,6 +454,15 @@ fuse_device_write(struct cdev *dev, struct uio *uio, int ioflag)
if ((err = uiomove(&ohead, sizeof(struct fuse_out_header), uio)) != 0)
return (err);
+ if (data->linux_errnos != 0 && ohead.error != 0) {
+ err = -ohead.error;
+ if (err < 0 || err >= nitems(linux_to_bsd_errtbl))
+ return (EINVAL);
+
+ /* '-', because it will get flipped again below */
+ ohead.error = -linux_to_bsd_errtbl[err];
+ }
+
/*
* We check header information (which is redundant) and compare it
* with what we see. If we see some inconsistency we discard the
diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h
index 281a0f357359..980a52052218 100644
--- a/sys/fs/fuse/fuse_ipc.h
+++ b/sys/fs/fuse/fuse_ipc.h
@@ -217,6 +217,7 @@ struct fuse_data {
struct selinfo ks_rsel;
int daemon_timeout;
+ int linux_errnos;
unsigned time_gran;
uint64_t notimpl;
uint64_t mnt_flag;
diff --git a/sys/fs/fuse/fuse_vfsops.c b/sys/fs/fuse/fuse_vfsops.c
index ce7ffeda2498..6cfdb6c3d801 100644
--- a/sys/fs/fuse/fuse_vfsops.c
+++ b/sys/fs/fuse/fuse_vfsops.c
@@ -300,6 +300,7 @@ fuse_vfsop_mount(struct mount *mp)
uint64_t mntopts, __mntopts;
uint32_t max_read;
+ int linux_errnos;
int daemon_timeout;
int fd;
@@ -312,6 +313,7 @@ fuse_vfsop_mount(struct mount *mp)
subtype = NULL;
max_read = ~0;
+ linux_errnos = 0;
err = 0;
mntopts = 0;
__mntopts = 0;
@@ -337,6 +339,7 @@ fuse_vfsop_mount(struct mount *mp)
FUSE_FLAGOPT(intr, FSESS_INTR);
(void)vfs_scanopt(opts, "max_read=", "%u", &max_read);
+ (void)vfs_scanopt(opts, "linux_errnos", "%d", &linux_errnos);
if (vfs_scanopt(opts, "timeout=", "%u", &daemon_timeout) == 1) {
if (daemon_timeout < FUSE_MIN_DAEMON_TIMEOUT)
daemon_timeout = FUSE_MIN_DAEMON_TIMEOUT;
@@ -411,6 +414,7 @@ fuse_vfsop_mount(struct mount *mp)
data->dataflags |= mntopts;
data->max_read = max_read;
data->daemon_timeout = daemon_timeout;
+ data->linux_errnos = linux_errnos;
data->mnt_flag = mp->mnt_flag & MNT_UPDATEMASK;
FUSE_UNLOCK();