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
next prev 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