public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: linux-fsdevel <linux-fsdevel@vger.kernel.org>,
	linux-ext4 <linux-ext4@vger.kernel.org>,
	fuse-devel <fuse-devel@lists.linux.dev>
Cc: Miklos Szeredi <miklos@szeredi.hu>,
	Bernd Schubert <bernd@bsbernd.com>,
	Joanne Koong <joannelkoong@gmail.com>,
	Theodore Ts'o <tytso@mit.edu>, Neal Gompa <neal@gompa.dev>,
	Amir Goldstein <amir73il@gmail.com>,
	Christian Brauner <brauner@kernel.org>,
	demiobenour@gmail.com
Subject: [RFC PATCH 1/4] fusefatfs: enable fuse systemd service mode
Date: Wed, 22 Apr 2026 16:29:50 -0700	[thread overview]
Message-ID: <20260422232950.GG7739@frogsfrogsfrogs> (raw)
In-Reply-To: <20260422231518.GA7717@frogsfrogsfrogs>

From: Darrick J. Wong <djwong@kernel.org>

Enable use of fusefatfs as a contained systemd service.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
 CMakeLists.txt        |   18 +++++++
 config.h.in           |    2 +
 diskio.c              |    3 +
 fusefatfs.c           |  124 ++++++++++++++++++++++++++++++++++++++++++++++---
 fusefatfs.socket.in   |   17 +++++++
 fusefatfs@.service.in |  102 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 259 insertions(+), 7 deletions(-)
 create mode 100644 fusefatfs.socket.in
 create mode 100644 fusefatfs@.service.in

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e7d70ec85b748..473d1c451d0810 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,24 @@ pkg_check_modules(FUSE fuse3)
 if(NOT FUSE_FOUND)
 	pkg_check_modules(FUSE REQUIRED fuse)
 endif()
+pkg_get_variable(FUSE3_SERVICE_SOCKET_DIR fuse3 service_socket_dir)
+pkg_get_variable(FUSE3_SERVICE_SOCKET_PERMS fuse3 service_socket_perms)
+pkg_check_modules(SYSTEMD systemd)
+if (SYSTEMD_FOUND)
+	pkg_get_variable(SYSTEMD_SYSTEM_UNIT_DIR systemd systemd_system_unit_dir)
+endif()
+IF ( (NOT "${FUSE3_SERVICE_SOCKET_DIR}" STREQUAL "") AND (NOT "${SYSTEMD_SYSTEM_UNIT_DIR}" STREQUAL "") )
+	message(STATUS "Found libfuse3 service socket dir: ${FUSE3_SERVICE_SOCKET_DIR}")
+	message(STATUS "Found libfuse3 service socket perms: ${FUSE3_SERVICE_SOCKET_PERMS}")
+	set(DEFINE_HAVE_FUSE_SERVICE "#define HAVE_FUSE_SERVICE")
+	configure_file(fusefatfs.socket.in ${CMAKE_BINARY_DIR}/fusefatfs.socket @ONLY)
+	configure_file(fusefatfs@.service.in ${CMAKE_BINARY_DIR}/fusefatfs@.service @ONLY)
+	message(STATUS "Found systemd system unit dir: ${SYSTEMD_SYSTEM_UNIT_DIR}")
+	install(FILES ${CMAKE_BINARY_DIR}/fusefatfs.socket
+		DESTINATION ${CMAKE_INSTALL_PREFIX}${SYSTEMD_SYSTEM_UNIT_DIR})
+	install(FILES ${CMAKE_BINARY_DIR}/fusefatfs@.service
+		DESTINATION ${CMAKE_INSTALL_PREFIX}${SYSTEMD_SYSTEM_UNIT_DIR})
+endif()
 string(REGEX REPLACE "\\..*" "" FUSE_VERSION ${FUSE_VERSION})
 
 set(CMAKE_REQUIRED_DEFINITIONS -D_FILE_OFFSET_BITS=64)
diff --git a/config.h.in b/config.h.in
index 7f916d685c1e42..e6d2e8c82b7d0c 100644
--- a/config.h.in
+++ b/config.h.in
@@ -4,5 +4,7 @@
 #define PROGNAME "@CMAKE_PROJECT_NAME@"
 #define VERSION "@CMAKE_PROJECT_VERSION@"
 
+@DEFINE_HAVE_FUSE_SERVICE@
+
 #endif
 
diff --git a/diskio.c b/diskio.c
index 122f93f3316e66..ca83a102e49b78 100644
--- a/diskio.c
+++ b/diskio.c
@@ -41,6 +41,9 @@ DSTATUS disk_initialize (
 	struct fftab *drv = fftab_get(pdrv);
 	if (!drv) return STA_NOINIT;
 
+	if (drv->fd >= 0)
+		return RES_OK;
+
 	if (drv->flags & FFFF_RDONLY)
 		drv->fd = open(drv->path, O_RDONLY);
 	else
diff --git a/fusefatfs.c b/fusefatfs.c
index 248f5c3a8a37c8..376f6cd1c338dd 100644
--- a/fusefatfs.c
+++ b/fusefatfs.c
@@ -20,7 +20,7 @@
 #define FUSE_USE_VERSION 29
 #define FUSE3_ONLY(...)
 #else
-#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 14)
+#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 19)
 #define FUSE3_ONLY(...) __VA_ARGS__
 #endif
 
@@ -37,6 +37,11 @@
 #include <fftable.h>
 #include <config.h>
 
+#ifdef HAVE_FUSE_SERVICE
+# include <sys/mount.h>
+# include <fuse_service.h>
+#endif
+
 int fuse_reentrant_tag = 0;
 
 #if FF_DEFINED == 80286
@@ -52,6 +57,96 @@ static pthread_mutex_t fff_mutex = PTHREAD_MUTEX_INITIALIZER;
 #define mutex_out() pthread_mutex_unlock(&fff_mutex)
 #define mutex_out_return(RETVAL) do {mutex_out(); return(RETVAL); } while (0)
 
+#ifdef HAVE_FUSE_SERVICE
+static struct fuse_service *service;
+static int bdev_fd = -1;
+
+static inline bool fff_is_service(void)
+{
+	return fuse_service_accepted(service);
+}
+
+static int fff_service_connect(struct fuse_args *args)
+{
+	int ret;
+
+	ret = fuse_service_accept(&service);
+	if (ret)
+		return ret;
+
+	if (fuse_service_accepted(service))
+		return fuse_service_append_args(service, args);
+
+	return 0;
+}
+
+static int fff_service_get_config(const char *device, bool ro,
+				  struct stat *sbuf)
+{
+	int open_flags = O_EXCL;
+	int fd;
+	int ret;
+
+	if (ro)
+		open_flags |= O_RDONLY;
+	else
+		open_flags |= O_SYNC | O_RDWR;
+
+	ret = fuse_service_request_file(service, device, open_flags, 0, 0);
+	if (ret)
+		return ret;
+
+	ret = fuse_service_receive_file(service, device, &fd);
+	if (ret)
+		return ret;
+
+	if (fd < 0) {
+		fprintf(stderr, "%s opening device: %s.\n", device,
+			   strerror(-fd));
+		return -1;
+	}
+	bdev_fd = fd;
+
+	ret = fuse_service_finish_file_requests(service);
+	if (ret)
+		return ret;
+
+	return fstat(bdev_fd, sbuf);
+}
+
+static void fff_service_assign_bdev(struct fftab *ffentry)
+{
+	if (fff_is_service())
+		ffentry->fd = bdev_fd;
+}
+
+static int fff_service_main(struct fuse_args *args,
+			    const struct fuse_operations *ops,
+			    struct fftab *data)
+{
+	fuse_service_expect_mount_format(service, S_IFDIR);
+	return fuse_service_main(service, args, ops, data);
+}
+
+static int fff_service_finish(int exitcode)
+{
+	if (!fff_is_service())
+		return exitcode;
+
+	fuse_service_send_goodbye(service, exitcode);
+	fuse_service_destroy(&service);
+
+	return fuse_service_exit(exitcode);
+}
+#else
+# define fff_is_service(...)			(false)
+# define fff_service_connect(...)		(0)
+# define fff_service_get_config(...)		(EOPNOTSUPP)
+# define fff_service_assign_bdev(...)		((void)0)
+# define fff_service_main(...)			(1)
+# define fff_service_finish(ret)		(ret)
+#endif /* HAVE_FUSE_SERVICE */
+
 #define fffpath(index, path) \
   *fffpath; \
   ssize_t __fffpathlen = (index == 0) ? 0 : strlen(path) + 3; \
@@ -423,6 +518,9 @@ static struct fftab *fff_init(const char *source, int codepage, int flags) {
 	if (index >= 0) {
 		struct fftab *ffentry = fftab_get(index);
 		char sdrv[12];
+
+		fff_service_assign_bdev(ffentry);
+
 		snprintf(sdrv, 12, "%d:", index);
 		FRESULT fres = f_mount(&ffentry->fs, sdrv, 1);
 		if (fres != FR_OK) {
@@ -531,10 +629,10 @@ fff_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
 			return 1;
 		case FUSE_OPT_KEY_NONOPT:
 			if (!options->source) {
-				options->source = arg;
+				options->source = strdup(arg);
 				return 0;
 			} else if(!options->mountpoint) {
-				options->mountpoint = arg;
+				options->mountpoint = strdup(arg);
 				return 1;
 			} else
 				return -1;
@@ -565,6 +663,11 @@ int main(int argc, char *argv[])
 	int flags = 0;
 	struct stat sbuf;
 	putenv("TZ=UTC0");
+
+	err = fff_service_connect(&args);
+	if (err)
+		exit(1);
+
 	if (fuse_opt_parse(&args, &options, fff_opts, fff_opt_proc) == -1) {
 		fuse_opt_free_args(&args);
 		return -1;
@@ -585,7 +688,11 @@ int main(int argc, char *argv[])
 		goto returnerr;
 	}
 
-	if (stat(options.source, &sbuf) < 0) {
+	if (fff_is_service())
+		err = fff_service_get_config(options.source, options.ro, &sbuf);
+	else
+		err = stat(options.source, &sbuf);
+	if (err < 0) {
 		fprintf(stderr, "%s: %s\n", options.source, strerror(errno));
 		goto returnerr;
 	}
@@ -600,12 +707,15 @@ int main(int argc, char *argv[])
 		fprintf(stderr, "Fuse init error\n");
 		goto returnerr;
 	}
-	err = fuse_main(args.argc, args.argv, &fusefat_ops, ffentry);
+	if (fff_is_service())
+		err = fff_service_main(&args, &fusefat_ops, ffentry);
+	else
+		err = fuse_main(args.argc, args.argv, &fusefat_ops, ffentry);
 	fff_destroy(ffentry);
 	fuse_opt_free_args(&args);
 	if (err) fprintf(stderr, "Fuse error %d\n", err);
-	return err;
+	return fff_service_finish(err);
 returnerr:
 	fuse_opt_free_args(&args);
-	return -1;
+	return fff_service_finish(-1);
 }
diff --git a/fusefatfs.socket.in b/fusefatfs.socket.in
new file mode 100644
index 00000000000000..3512b3570d6178
--- /dev/null
+++ b/fusefatfs.socket.in
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2026 Oracle.  All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+[Unit]
+Description=Socket for fusefatfs Service
+
+[Socket]
+ListenSequentialPacket=@FUSE3_SERVICE_SOCKET_DIR@/vfat
+ListenSequentialPacket=@FUSE3_SERVICE_SOCKET_DIR@/msdos
+ListenSequentialPacket=@FUSE3_SERVICE_SOCKET_DIR@/fat
+Accept=yes
+SocketMode=@FUSE3_SERVICE_SOCKET_PERMS@
+RemoveOnStop=yes
+
+[Install]
+WantedBy=sockets.target
diff --git a/fusefatfs@.service.in b/fusefatfs@.service.in
new file mode 100644
index 00000000000000..ac7c4d6cdad93a
--- /dev/null
+++ b/fusefatfs@.service.in
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2026 Oracle.  All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+[Unit]
+Description=fusefatfs Service
+
+# Don't leave failed units behind, systemd does not clean them up!
+CollectMode=inactive-or-failed
+
+[Service]
+Type=exec
+ExecStart=/@CMAKE_INSTALL_BINDIR@/fusefatfs
+
+# Try to capture core dumps
+LimitCORE=infinity
+
+SyslogIdentifier=%N
+
+# No realtime CPU scheduling
+RestrictRealtime=true
+
+# Don't let us see anything in the regular system, and don't run as root
+DynamicUser=true
+ProtectSystem=strict
+ProtectHome=true
+PrivateTmp=true
+PrivateDevices=true
+PrivateUsers=true
+
+# No network access
+PrivateNetwork=true
+ProtectHostname=true
+RestrictAddressFamilies=none
+IPAddressDeny=any
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+RestrictFileSystems=
+
+# Hide everything in /proc, even /proc/mounts
+ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+
+SystemCallFilter=~@clock
+SystemCallFilter=~@cpu-emulation
+SystemCallFilter=~@debug
+SystemCallFilter=~@module
+SystemCallFilter=~@reboot
+SystemCallFilter=~@swap
+
+SystemCallFilter=~@mount
+
+# libfuse io_uring wants to pin cores and memory
+SystemCallFilter=mbind
+SystemCallFilter=sched_setaffinity
+
+# Leave a breadcrumb if we get whacked by the system call filter
+SystemCallErrorNumber=EL3RST
+
+# Log to the kernel dmesg, just like an in-kernel ext4 driver
+StandardOutput=append:/dev/ttyprintk
+StandardError=append:/dev/ttyprintk
+
+# Run with no capabilities at all
+CapabilityBoundingSet=
+AmbientCapabilities=
+NoNewPrivileges=true
+
+# fuse4fs doesn't create files
+UMask=7777
+
+# No access to hardware /dev files at all
+ProtectClock=true
+DevicePolicy=closed
+
+# Don't mess with set[ug]id anything.
+RestrictSUIDSGID=true
+
+# Don't let OOM kills of processes in this containment group kill the whole
+# service, because we don't want filesystem drivers to go down.
+OOMPolicy=continue
+OOMScoreAdjust=-1000

  parent reply	other threads:[~2026-04-22 23:29 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-22 23:15 [PATCHBOMB v5] fuse/libfuse/e2fsprogs/etc: containerize ext4 for safer operation Darrick J. Wong
2026-04-22 23:18 ` [PATCHSET v5] libfuse: run fuse servers as a contained service Darrick J. Wong
2026-04-22 23:19   ` [PATCH 01/13] Refactor mount code / move common functions to mount_util.c Darrick J. Wong
2026-04-22 23:19   ` [PATCH 02/13] mount_service: add systemd socket service mounting helper Darrick J. Wong
2026-04-22 23:20   ` [PATCH 03/13] mount_service: create high level fuse helpers Darrick J. Wong
2026-04-22 23:20   ` [PATCH 04/13] mount_service: use the new mount api for the mount service Darrick J. Wong
2026-04-22 23:20   ` [PATCH 05/13] mount_service: update mtab after a successful mount Darrick J. Wong
2026-04-22 23:20   ` [PATCH 06/13] util: hoist the fuse.conf parsing and setuid mode enforcement code Darrick J. Wong
2026-04-22 23:21   ` [PATCH 07/13] util: fix checkpatch complaints in fuser_conf.[ch] Darrick J. Wong
2026-04-22 23:21   ` [PATCH 08/13] mount_service: enable unprivileged users in a similar manner as fusermount Darrick J. Wong
2026-04-22 23:21   ` [PATCH 09/13] mount.fuse3: integrate systemd service startup Darrick J. Wong
2026-04-22 23:21   ` [PATCH 10/13] mount_service: allow installation as a setuid program Darrick J. Wong
2026-04-22 23:22   ` [PATCH 11/13] example/service_ll: create a sample systemd service fuse server Darrick J. Wong
2026-04-22 23:22   ` [PATCH 12/13] example/service: create a sample systemd service for a high-level " Darrick J. Wong
2026-04-22 23:22   ` [PATCH 13/13] nullfs: support fuse systemd service mode Darrick J. Wong
2026-04-22 23:19 ` [PATCHSET v5 2/2] fuse4fs: run servers as a contained service Darrick J. Wong
2026-04-22 23:23   ` [PATCH 01/10] libext2fs: make it possible to extract the fd from an IO manager Darrick J. Wong
2026-04-22 23:24   ` [PATCH 02/10] libext2fs: fix checking for valid fds in mmp.c Darrick J. Wong
2026-04-22 23:24   ` [PATCH 03/10] unix_io: allow passing /dev/fd/XXX paths to the unixfd IO manager Darrick J. Wong
2026-04-22 23:24   ` [PATCH 04/10] libext2fs: fix MMP code to work with " Darrick J. Wong
2026-04-22 23:24   ` [PATCH 05/10] libext2fs: bump libfuse API version to 3.19 Darrick J. Wong
2026-04-22 23:25   ` [PATCH 06/10] fuse4fs: hoist some code out of fuse4fs_main Darrick J. Wong
2026-04-22 23:25   ` [PATCH 07/10] fuse4fs: enable safe service mode Darrick J. Wong
2026-04-22 23:25   ` [PATCH 08/10] fuse4fs: set proc title when in fuse " Darrick J. Wong
2026-04-22 23:25   ` [PATCH 09/10] fuse4fs: make MMP work correctly in safe " Darrick J. Wong
2026-04-22 23:26   ` [PATCH 10/10] debian: update packaging for fuse4fs service Darrick J. Wong
2026-04-22 23:29 ` Darrick J. Wong [this message]
2026-04-22 23:30 ` [RFC PATCH 2/4] exfat: enable fuse systemd service mode Darrick J. Wong
2026-04-22 23:32 ` [RFC PATCH 3/4] fuseiso: enable " Darrick J. Wong
2026-04-22 23:32 ` [RFC PATCH 4/4] httpdirfs: enable fuse " Darrick J. Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260422232950.GG7739@frogsfrogsfrogs \
    --to=djwong@kernel.org \
    --cc=amir73il@gmail.com \
    --cc=bernd@bsbernd.com \
    --cc=brauner@kernel.org \
    --cc=demiobenour@gmail.com \
    --cc=fuse-devel@lists.linux.dev \
    --cc=joannelkoong@gmail.com \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=miklos@szeredi.hu \
    --cc=neal@gompa.dev \
    --cc=tytso@mit.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox