All of lore.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: 51+ 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-28 18:08     ` Darrick J. Wong
2026-04-29 15:23       ` 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-26 20:42     ` Bernd Schubert
2026-04-27 14:40       ` 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-28 18:10     ` 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-26 21:28     ` Bernd Schubert
2026-04-27 14:51       ` 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-26 21:04     ` Bernd Schubert
2026-04-27 15:04       ` Darrick J. Wong
2026-04-26 21:21     ` Bernd Schubert
2026-04-27 15:13       ` Darrick J. Wong
2026-04-22 23:22   ` [PATCH 13/13] nullfs: support fuse systemd service mode Darrick J. Wong
2026-04-26 16:35   ` [PATCHSET v5] libfuse: run fuse servers as a contained service Bernd Schubert
2026-04-26 16:56     ` Darrick J. Wong
2026-04-26 19:35       ` Bernd Schubert
2026-04-26 20:23         ` Bernd Schubert
2026-04-22 23:19 ` [PATCHSET 1/2] libext2fs: fix some missed fsync calls Darrick J. Wong
2026-04-22 23:23   ` [PATCH 1/3] libext2fs: always fsync the device when flushing the cache Darrick J. Wong
2026-04-22 23:23   ` [PATCH 2/3] libext2fs: always fsync the device when closing the unix IO manager Darrick J. Wong
2026-04-22 23:23   ` [PATCH 3/3] libext2fs: only fsync the unix fd if we wrote to the device 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
2026-04-23  8:44 ` [PATCHBOMB v5] fuse/libfuse/e2fsprogs/etc: containerize ext4 for safer operation Amir Goldstein
2026-04-23 14:50   ` 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.