{"id":2231576,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2231576/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-ext4/patch/177758363748.1314717.15016129531266257713.stgit@frogsfrogsfrogs/","project":{"id":8,"url":"http://patchwork.ozlabs.org/api/1.1/projects/8/?format=json","name":"Linux ext4 filesystem development","link_name":"linux-ext4","list_id":"linux-ext4.vger.kernel.org","list_email":"linux-ext4@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null},"msgid":"<177758363748.1314717.15016129531266257713.stgit@frogsfrogsfrogs>","date":"2026-04-30T21:18:09","name":"[12/13] example/service: create a sample systemd service for a high-level fuse server","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"d2693af7d03d23783cf2da3cd808ce872e66880e","submitter":{"id":77032,"url":"http://patchwork.ozlabs.org/api/1.1/people/77032/?format=json","name":"Darrick J. Wong","email":"djwong@kernel.org"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-ext4/patch/177758363748.1314717.15016129531266257713.stgit@frogsfrogsfrogs/mbox/","series":[{"id":502386,"url":"http://patchwork.ozlabs.org/api/1.1/series/502386/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-ext4/list/?series=502386","date":"2026-04-30T21:15:17","name":"[01/13] Refactor mount code / move common functions to mount_util.c","version":1,"mbox":"http://patchwork.ozlabs.org/series/502386/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2231576/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2231576/checks/","tags":{},"headers":{"Return-Path":"\n <SRS0=qFhI=C5=vger.kernel.org=linux-ext4+bounces-16264-patchwork-incoming=ozlabs.org@ozlabs.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-ext4@vger.kernel.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","patchwork-incoming@ozlabs.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=RSkhYwcG;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=ozlabs.org\n (client-ip=2404:9400:2221:ea00::3; helo=mail.ozlabs.org;\n envelope-from=srs0=qfhi=c5=vger.kernel.org=linux-ext4+bounces-16264-patchwork-incoming=ozlabs.org@ozlabs.org;\n receiver=patchwork.ozlabs.org)","gandalf.ozlabs.org;\n arc=pass smtp.remote-ip=172.234.253.10 arc.chain=subspace.kernel.org","gandalf.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=kernel.org","gandalf.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=RSkhYwcG;\n\tdkim-atps=neutral","gandalf.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-ext4+bounces-16264-patchwork-incoming=ozlabs.org@vger.kernel.org;\n receiver=ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=\"RSkhYwcG\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201"],"Received":["from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g66WJ1jKfz1yHZ\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 01 May 2026 07:19:40 +1000 (AEST)","from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3])\n\tby gandalf.ozlabs.org (Postfix) with ESMTP id 4g66WJ0yJFz4wc4\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 01 May 2026 07:19:40 +1000 (AEST)","by gandalf.ozlabs.org (Postfix)\n\tid 4g66WJ0tmxz4wck; Fri, 01 May 2026 07:19:40 +1000 (AEST)","from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby gandalf.ozlabs.org (Postfix) with ESMTPS id 4g66WD5NCpz4wc4\n\tfor <patchwork-incoming@ozlabs.org>; Fri, 01 May 2026 07:19:36 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 2320C301BCCA\n\tfor <patchwork-incoming@ozlabs.org>; Thu, 30 Apr 2026 21:18:11 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 65BE8376BD6;\n\tThu, 30 Apr 2026 21:18:10 +0000 (UTC)","from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org\n [10.30.226.201])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 274493AB276;\n\tThu, 30 Apr 2026 21:18:09 +0000 (UTC)","by smtp.kernel.org (Postfix) with ESMTPSA id B5ECBC2BCB3;\n\tThu, 30 Apr 2026 21:18:09 +0000 (UTC)"],"ARC-Seal":["i=2; a=rsa-sha256; d=ozlabs.org; s=201707; t=1777583980; cv=pass;\n\tb=MzVwMfxnNketG2rQNeX6G9OEgjRfgY+Wjsy7sijB9B+sW4VFq7Ljecky5v++KvYjQfOfQpyXikFlMdSeYSgmT+ovrQCnY/6fzpwBAEQku8KdCHY77xaQxsGbEvY33LTNT2iSGv1UHjSCp0f7MCzaYPCSqr8AdK1hp5Emg5hcFT+wZrMIHod14kXFfbC3eZhf5xK9wYMgLSH+pzpAPYQ+J9CVBtOucB0fyfefL6Vj1JQnhoiRpzyYcJG5b4vdqRMhxB4gCduOdxTL3eEAFtkP/gp9a1w06amkG0iGPYPdQCCXHYwKYwNF0ywXrqiFy7ElEiRwB5I9FDRXnug6UhCLCg==","i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1777583890; cv=none;\n b=CNlUFA5Aqcuz3W505QTMI4yX02YlJ8XMU9NMjZTUaVd4HCLPyXaF1k3ij4XzDdoO+pypTCFt7epipcw7hvVNaqullFqUfhcuL4g10uFMe5ltmQc+UBtSGvr01pox3/JXB0eFXHpzT8/5NRg+DPnuOSMKJQy6PQJOEh19igr+wjk="],"ARC-Message-Signature":["i=2; a=rsa-sha256; d=ozlabs.org; s=201707;\n\tt=1777583980; c=relaxed/relaxed;\n\tbh=rZjeVGA5j28LB7+j5xrf+MrcKlGEnIuURG5rRf0J3ks=;\n\th=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References:\n\t MIME-Version:Content-Type;\n b=cj8gUA6GPi4IpK5xDHlbbVmSav0NtE5+Xv+F0lEX8/9Pg90hkB9L1Ncjfd0KFVIMt0A5qgG3AVsuwmzBRjHTrxFnyYOsZuUjtXBLweaqauWRFj6RaM5oDyMnyFvoU55rlf6WbBsDPOoXSKJBc7PQN/XQPK+tLgTBX/bJEVwuPdGOSEOWeVB/MDFWOC9HzrcWin0ubtFEXIeKk/ilStYVV5NxjMZew9n8jWcZIFfe9LYQZtxV28+u77UeOWd8ME0EfREaxlV1Etuh4sBL9ghCy0BimjwI1Loay4XwxKqIR736CMXhSzQXtVp5CM1hrfvwM9+h1quXM3OwvLUxVSU4mQ==","i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1777583890; c=relaxed/simple;\n\tbh=j6nXSMHdW57X+7AE1hEnds7Y3Z2E3FI7sRzdiPSFlxA=;\n\th=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References:\n\t MIME-Version:Content-Type;\n b=OqUMO6A5k6kuLIhDH65lruiemevtrAJjNAT0+9N6iFHR3C3U6sxQPWVozqpjrR7bGxvOhHEp1K1aSWRB6d5Bo5beWsVQfJST0sZs+XNbJozlsK8fp0VW5VVOIbSwDMPOIuLCORqtH8I6PU0uFfwBc0INDd6x1OB7lRqlh1GisQI="],"ARC-Authentication-Results":["i=2; gandalf.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=kernel.org;\n dkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=RSkhYwcG; dkim-atps=neutral;\n spf=pass (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-ext4+bounces-16264-patchwork-incoming=ozlabs.org@vger.kernel.org;\n receiver=ozlabs.org) smtp.mailfrom=vger.kernel.org","i=1; smtp.subspace.kernel.org;\n dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=RSkhYwcG; arc=none smtp.client-ip=10.30.226.201"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;\n\ts=k20201202; t=1777583889;\n\tbh=j6nXSMHdW57X+7AE1hEnds7Y3Z2E3FI7sRzdiPSFlxA=;\n\th=Date:Subject:From:To:Cc:In-Reply-To:References:From;\n\tb=RSkhYwcG5VOan26+y3iV3fr6gMIqv9EFJYabMGe/IBtmD2vb4x99QqmbGaEv0CR3K\n\t kcEc5M+H8w/nCE5fU6/+ukypVDPCcIOACg4AfI2B0ZpvopDYzwUQCuTAj9oohXXHtZ\n\t FoOCIjpyx521yUWJPg5u8tqmsK8t1rlcjpfGD5r4QlbbK3AB9+s7MVSzRqUSoP3UVF\n\t 4ACBAVy9K4CIfNBWz0Qf7YqoeiwKDqXpmAwz3Y1NB1M16Fz7WqahaeAS8beSE2sgKs\n\t FoLLX/O2oxlKAJvrYtIN+cP8UVGAMt+ksYLA7rUQoM9qxdXrc2bBKgO7Qk21QTv7TE\n\t vonKhS3oMoLEQ==","Date":"Thu, 30 Apr 2026 14:18:09 -0700","Subject":"[PATCH 12/13] example/service: create a sample systemd service for a\n high-level fuse server","From":"\"Darrick J. Wong\" <djwong@kernel.org>","To":"bernd@bsbernd.com, djwong@kernel.org","Cc":"linux-fsdevel@vger.kernel.org, fuse-devel@lists.linux.dev,\n linux-ext4@vger.kernel.org, miklos@szeredi.hu, neal@gompa.dev,\n joannelkoong@gmail.com","Message-ID":"<177758363748.1314717.15016129531266257713.stgit@frogsfrogsfrogs>","In-Reply-To":"<177758363484.1314717.11777978893472254088.stgit@frogsfrogsfrogs>","References":"<177758363484.1314717.11777978893472254088.stgit@frogsfrogsfrogs>","Precedence":"bulk","X-Mailing-List":"linux-ext4@vger.kernel.org","List-Id":"<linux-ext4.vger.kernel.org>","List-Subscribe":"<mailto:linux-ext4+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-ext4+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","X-Spam-Status":"No, score=-1.2 required=5.0 tests=ARC_SIGNED,ARC_VALID,\n\tDKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DMARC_PASS,\n\tMAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=disabled\n\tversion=4.0.1","X-Spam-Checker-Version":"SpamAssassin 4.0.1 (2024-03-25) on gandalf.ozlabs.org"},"content":"From: Darrick J. Wong <djwong@kernel.org>\n\nCreate a simple high-level fuse server that can be run as a systemd\nservice.\n\nSigned-off-by: \"Darrick J. Wong\" <djwong@kernel.org>\n---\n example/single_file.h        |   38 ++++++\n example/meson.build          |    6 +\n example/service_hl.c         |  240 ++++++++++++++++++++++++++++++++++++\n example/service_hl.socket.in |   15 ++\n example/service_hl@.service  |  102 +++++++++++++++\n example/service_ll.c         |    1 \n example/single_file.c        |  280 +++++++++++++++++++++++++++++++++++++++---\n 7 files changed, 665 insertions(+), 17 deletions(-)\n create mode 100644 example/service_hl.c\n create mode 100644 example/service_hl.socket.in\n create mode 100644 example/service_hl@.service","diff":"diff --git a/example/single_file.h b/example/single_file.h\nindex 6e5d3e7c975385..290dd6051ed6f5 100644\n--- a/example/single_file.h\n+++ b/example/single_file.h\n@@ -128,6 +128,7 @@ ssize_t single_file_pread(char *buf, size_t count, off_t pos);\n \n /* low-level fuse operation handlers */\n \n+#ifdef USE_SINGLE_FILE_LL_API\n bool is_single_file_child(fuse_ino_t parent, const char *name);\n bool is_single_file_ino(fuse_ino_t ino);\n \n@@ -153,5 +154,42 @@ void single_file_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,\n \n int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,\n \t\t      off_t off, size_t maxsize);\n+#endif\n+\n+/* high-level fuse operation handlers */\n+\n+#ifdef USE_SINGLE_FILE_HL_API\n+bool is_single_open_file_path(const struct fuse_file_info *fi, const char *name);\n+\n+int single_file_hl_readdir(const char *path, void *buf, fuse_fill_dir_t filler,\n+\t\t\t   off_t offset, struct fuse_file_info *fi,\n+\t\t\t   enum fuse_readdir_flags flags);\n+\n+int single_file_hl_statfs(const char *path, struct statvfs *buf);\n+\n+int single_file_hl_statx(const char *path, int statx_flags, int statx_mask,\n+\t\t\t struct statx *stx, struct fuse_file_info *fi);\n+\n+int single_file_hl_getattr(const char *path, struct stat *stbuf,\n+\t\t\t   struct fuse_file_info *fi);\n+int single_file_hl_chmod(const char *path, mode_t mode,\n+\t\t\t struct fuse_file_info *fi);\n+int single_file_hl_utimens(const char *path, const struct timespec ctv[2],\n+\t\t\t   struct fuse_file_info *fi);\n+int single_file_hl_chown(const char *path, uid_t owner, gid_t group,\n+\t\t\t struct fuse_file_info *fi);\n+int single_file_hl_truncate(const char *path, off_t len,\n+\t\t\t    struct fuse_file_info *fi);\n+\n+int single_file_hl_opendir(const char *path, struct fuse_file_info *fi);\n+int single_file_hl_open(const char *path, struct fuse_file_info *fi);\n+\n+int single_file_hl_fsync(const char *path, int datasync,\n+\t\t\t struct fuse_file_info *fi);\n+#endif\n+\n+#if !defined(USE_SINGLE_FILE_LL_API) && !defined(USE_SINGLE_FILE_HL_API)\n+# warning USE_SINGLE_FILE_[HL]L_API not defined!\n+#endif\n \n #endif /* FUSE_SINGLE_FILE_H_ */\ndiff --git a/example/meson.build b/example/meson.build\nindex e948f6ba74fdfa..19a383f7cd2c74 100644\n--- a/example/meson.build\n+++ b/example/meson.build\n@@ -19,6 +19,12 @@ if platform.endswith('linux')\n     configure_file(input: 'service_ll.socket.in',\n                    output: 'service_ll.socket',\n                    configuration: private_cfg)\n+\n+    single_file_examples += [ 'service_hl' ]\n+    configure_file(input: 'service_hl.socket.in',\n+                   output: 'service_hl.socket',\n+                   configuration: private_cfg)\n+\n endif\n \n threaded_examples = [ 'notify_inval_inode',\ndiff --git a/example/service_hl.c b/example/service_hl.c\nnew file mode 100644\nindex 00000000000000..ea041f670f2ec5\n--- /dev/null\n+++ b/example/service_hl.c\n@@ -0,0 +1,240 @@\n+/*\n+ * FUSE: Filesystem in Userspace\n+ * Copyright (C) 2026 Oracle.\n+ *\n+ * This program can be distributed under the terms of the GNU GPLv2.\n+ * See the file GPL2.txt.\n+ */\n+\n+/** @file\n+ *\n+ * Minimal example filesystem using high-level API and systemd service API.\n+ *\n+ * - Shows how to build a high level FUSE filesystem server that can be managed\n+ *   by systemd\n+ * - Enables on-demand filesystem mounting via socket activation\n+ * - Demonstrates requesting resources from the mount-caller's environment\n+ * - Allows running FUSE servers with minimal privileges; isolated mount,\n+ *   network, and pid namespaces; and a separate uid/gid (unlike traditional\n+ *   FUSE which needs mount permissions and runs in the caller's environment)\n+ *\n+ * Compile with:\n+ *\n+ *     gcc -Wall single_file.c service_hl.c `pkg-config fuse3 --cflags --libs` -o service_hl\n+ *\n+ * Note: If the pkg-config command fails due to the absence of the fuse3.pc\n+ *     file, you should configure the path to the fuse3.pc file in the\n+ *     PKG_CONFIG_PATH variable.\n+ *\n+ * Change the ExecStart line in service_hl@.service:\n+ *\n+ *     ExecStart=/path/to/service_hl\n+ *\n+ * to point to the actual path of the service_hl binary.\n+ *\n+ * Finally, install the service_hl@.service and service_hl.socket files to the\n+ * systemd service directory, usually /run/systemd/system.  Run these commands\n+ * to activate:\n+ *\n+ *     systemctl daemon-reload\n+ *     systemctl start service_hl.socket\n+ *\n+ * Then mount with:\n+ *\n+ *     mount -t fuse.service_hl /dev/sda /mnt\n+ *\n+ * ## Source code ##\n+ * \\include service_hl.c\n+ * \\include service_hl.socket\n+ * \\include service_hl@.service\n+ * \\include single_file.c\n+ * \\include single_file.h\n+ */\n+\n+#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 19)\n+\n+#ifndef _GNU_SOURCE\n+#define _GNU_SOURCE\n+#endif\n+\n+#include <fuse.h>\n+#include <fuse_service.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <unistd.h>\n+#include <assert.h>\n+#include <pthread.h>\n+#include <stddef.h>\n+#include <sys/ioctl.h>\n+#include <sys/stat.h>\n+#include <linux/fs.h>\n+#include <linux/stat.h>\n+#define USE_SINGLE_FILE_HL_API\n+#include \"single_file.h\"\n+\n+struct service_hl {\n+\tchar *device;\n+\tstruct fuse_service *service;\n+\n+\t/* really booleans */\n+\tint debug;\n+};\n+\n+static struct service_hl hl = { };\n+\n+static void *service_hl_init(struct fuse_conn_info *conn,\n+\t\t\tstruct fuse_config *cfg)\n+{\n+\t(void) conn;\n+\tcfg->kernel_cache = 1;\n+\n+\treturn NULL;\n+}\n+\n+static int service_hl_read(const char *path, char *buf, size_t count,\n+\t\t\t   off_t pos, struct fuse_file_info *fi)\n+{\n+\tif (!is_single_open_file_path(fi, path))\n+\t\treturn -EIO;\n+\n+\tif (hl.debug)\n+\t\tfprintf(stderr, \"%s: pos 0x%llx count 0x%llx\\n\",\n+\t\t\t__func__,\n+\t\t\t(unsigned long long)pos,\n+\t\t\t(unsigned long long)count);\n+\n+\tif (!single_file.allow_dio && fi->direct_io)\n+\t\treturn -ENOSYS;\n+\n+\tsingle_file_check_read(pos, &count);\n+\n+\tif (!count)\n+\t\treturn 0;\n+\n+\treturn single_file_pread(buf, count, pos);\n+}\n+\n+static int service_hl_write(const char *path, const char *buf, size_t count,\n+\t\t\t    off_t pos, struct fuse_file_info *fi)\n+{\n+\tint ret;\n+\n+\tif (!is_single_open_file_path(fi, path))\n+\t\treturn -EIO;\n+\n+\tif (hl.debug)\n+\t\tfprintf(stderr, \"%s: pos 0x%llx count 0x%llx\\n\",\n+\t\t\t__func__,\n+\t\t\t(unsigned long long)pos,\n+\t\t\t(unsigned long long)count);\n+\n+\tif (!single_file.allow_dio && fi->direct_io)\n+\t\treturn -ENOSYS;\n+\n+\tret = single_file_check_write(pos, &count);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (!count)\n+\t\treturn 0;\n+\n+\treturn single_file_pwrite(buf, count, pos);\n+}\n+\n+static const struct fuse_operations service_hl_oper = {\n+\t.getattr\t= single_file_hl_getattr,\n+\t.readdir\t= single_file_hl_readdir,\n+\t.open\t\t= single_file_hl_open,\n+\t.opendir\t= single_file_hl_opendir,\n+\t.statfs\t\t= single_file_hl_statfs,\n+\t.chmod\t\t= single_file_hl_chmod,\n+\t.utimens\t= single_file_hl_utimens,\n+\t.fsync\t\t= single_file_hl_fsync,\n+\t.chown\t\t= single_file_hl_chown,\n+\t.truncate\t= single_file_hl_truncate,\n+\t.statx\t\t= single_file_hl_statx,\n+\n+\t.init\t\t= service_hl_init,\n+\t.read\t\t= service_hl_read,\n+\t.write\t\t= service_hl_write,\n+};\n+\n+#define SERVICE_HL_OPT(t, p, v) { t, offsetof(struct service_hl, p), v }\n+\n+static struct fuse_opt service_hl_opts[] = {\n+\tSERVICE_HL_OPT(\"debug\",\t\tdebug,\t\t\t1),\n+\tSINGLE_FILE_OPT_KEYS,\n+\tFUSE_OPT_END\n+};\n+\n+static int service_hl_opt_proc(void *data, const char *arg, int key,\n+\t\t\t       struct fuse_args *outargs)\n+{\n+\tint ret = single_file_opt_proc(data, arg, key, outargs);\n+\n+\tif (ret < 1)\n+\t\treturn ret;\n+\n+\tswitch (key) {\n+\tcase FUSE_OPT_KEY_NONOPT:\n+\t\tif (!hl.device) {\n+\t\t\thl.device = strdup(arg);\n+\t\t\treturn 0;\n+\t\t}\n+\t\treturn 1;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tstruct fuse_args args = FUSE_ARGS_INIT(argc, argv);\n+\tint ret = 1;\n+\n+\tif (fuse_service_accept(&hl.service))\n+\t\tgoto err_args;\n+\n+\tif (!fuse_service_accepted(hl.service))\n+\t\tgoto err_args;\n+\n+\tif (fuse_service_append_args(hl.service, &args))\n+\t\tgoto err_service;\n+\n+\tif (fuse_opt_parse(&args, &hl, service_hl_opts, service_hl_opt_proc))\n+\t\tgoto err_service;\n+\n+\tif (!hl.device) {\n+\t\tprintf(\"usage: %s [options] <device> <mountpoint>\\n\", argv[0]);\n+\t\tprintf(\"       %s --help\\n\", argv[0]);\n+\t\tgoto err_service;\n+\t}\n+\n+\tif (single_file_service_open(hl.service, hl.device))\n+\t\tgoto err_service;\n+\n+\tif (fuse_service_finish_file_requests(hl.service))\n+\t\tgoto err_singlefile;\n+\n+\tif (single_file_configure(hl.device, NULL))\n+\t\tgoto err_singlefile;\n+\n+\tfuse_service_expect_mount_format(hl.service, S_IFDIR);\n+\n+\tret = fuse_service_main(hl.service, &args, &service_hl_oper, NULL);\n+\n+err_singlefile:\n+\tsingle_file_close();\n+err_service:\n+\tfree(hl.device);\n+\tfuse_service_send_goodbye(hl.service, ret);\n+\tfuse_service_destroy(&hl.service);\n+err_args:\n+\tfuse_opt_free_args(&args);\n+\treturn fuse_service_exit(ret);\n+}\ndiff --git a/example/service_hl.socket.in b/example/service_hl.socket.in\nnew file mode 100644\nindex 00000000000000..46035d6c315b8d\n--- /dev/null\n+++ b/example/service_hl.socket.in\n@@ -0,0 +1,15 @@\n+# SPDX-License-Identifier: GPL-2.0-or-later\n+#\n+# Copyright (C) 2026 Oracle.  All Rights Reserved.\n+# Author: Darrick J. Wong <djwong@kernel.org>\n+[Unit]\n+Description=Socket for service_hl Service\n+\n+[Socket]\n+ListenSequentialPacket=@FUSE_SERVICE_SOCKET_DIR_RAW@/service_hl\n+Accept=yes\n+SocketMode=@FUSE_SERVICE_SOCKET_PERMS@\n+RemoveOnStop=yes\n+\n+[Install]\n+WantedBy=sockets.target\ndiff --git a/example/service_hl@.service b/example/service_hl@.service\nnew file mode 100644\nindex 00000000000000..883b9c649cbc90\n--- /dev/null\n+++ b/example/service_hl@.service\n@@ -0,0 +1,102 @@\n+# SPDX-License-Identifier: GPL-2.0-or-later\n+#\n+# Copyright (C) 2026 Oracle.  All Rights Reserved.\n+# Author: Darrick J. Wong <djwong@kernel.org>\n+[Unit]\n+Description=service_hl Sample Fuse Service\n+\n+# Don't leave failed units behind, systemd does not clean them up!\n+CollectMode=inactive-or-failed\n+\n+[Service]\n+Type=exec\n+ExecStart=/path/to/service_hl\n+\n+# Try to capture core dumps\n+LimitCORE=infinity\n+\n+SyslogIdentifier=%N\n+\n+# No realtime CPU scheduling\n+RestrictRealtime=true\n+\n+# Don't let us see anything in the regular system, and don't run as root\n+DynamicUser=true\n+ProtectSystem=strict\n+ProtectHome=true\n+PrivateTmp=true\n+PrivateDevices=true\n+PrivateUsers=true\n+\n+# No network access\n+PrivateNetwork=true\n+ProtectHostname=true\n+RestrictAddressFamilies=none\n+IPAddressDeny=any\n+\n+# Don't let the program mess with the kernel configuration at all\n+ProtectKernelLogs=true\n+ProtectKernelModules=true\n+ProtectKernelTunables=true\n+ProtectControlGroups=true\n+ProtectProc=invisible\n+RestrictNamespaces=true\n+RestrictFileSystems=\n+\n+# Hide everything in /proc, even /proc/mounts\n+ProcSubset=pid\n+\n+# Only allow the default personality Linux\n+LockPersonality=true\n+\n+# No writable memory pages\n+MemoryDenyWriteExecute=true\n+\n+# Don't let our mounts leak out to the host\n+PrivateMounts=true\n+\n+# Restrict system calls to the native arch and only enough to get things going\n+SystemCallArchitectures=native\n+SystemCallFilter=@system-service\n+SystemCallFilter=~@privileged\n+SystemCallFilter=~@resources\n+\n+SystemCallFilter=~@clock\n+SystemCallFilter=~@cpu-emulation\n+SystemCallFilter=~@debug\n+SystemCallFilter=~@module\n+SystemCallFilter=~@reboot\n+SystemCallFilter=~@swap\n+\n+SystemCallFilter=~@mount\n+\n+# libfuse io_uring wants to pin cores and memory\n+SystemCallFilter=mbind\n+SystemCallFilter=sched_setaffinity\n+\n+# Leave a breadcrumb if we get whacked by the system call filter\n+SystemCallErrorNumber=EL3RST\n+\n+# Log to the kernel dmesg, just like an in-kernel filesystem driver\n+StandardOutput=append:/dev/ttyprintk\n+StandardError=append:/dev/ttyprintk\n+\n+# Run with no capabilities at all\n+CapabilityBoundingSet=\n+AmbientCapabilities=\n+NoNewPrivileges=true\n+\n+# We don't create files\n+UMask=7777\n+\n+# No access to hardware /dev files at all\n+ProtectClock=true\n+DevicePolicy=closed\n+\n+# Don't mess with set[ug]id anything.\n+RestrictSUIDSGID=true\n+\n+# Don't let OOM kills of processes in this containment group kill the whole\n+# service, because we don't want filesystem drivers to go down.\n+OOMPolicy=continue\n+OOMScoreAdjust=-1000\ndiff --git a/example/service_ll.c b/example/service_ll.c\nindex d045176443d4e3..33a8bd48bc1215 100644\n--- a/example/service_ll.c\n+++ b/example/service_ll.c\n@@ -67,6 +67,7 @@\n #include <unistd.h>\n #include <assert.h>\n #include <pthread.h>\n+#define USE_SINGLE_FILE_LL_API\n #include \"single_file.h\"\n \n struct service_ll {\ndiff --git a/example/single_file.c b/example/single_file.c\nindex 9b6f76504686b5..a962232d576e17 100644\n--- a/example/single_file.c\n+++ b/example/single_file.c\n@@ -27,7 +27,10 @@\n #define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 19))\n \n #include \"fuse_lowlevel.h\"\n+#include \"fuse.h\"\n #include \"fuse_service.h\"\n+#define USE_SINGLE_FILE_LL_API\n+#define USE_SINGLE_FILE_HL_API\n #include \"single_file.h\"\n \n #define min(x, y) ((x) < (y) ? (x) : (y))\n@@ -60,6 +63,23 @@ struct single_file single_file = {\n \t.lock = PTHREAD_MUTEX_INITIALIZER,\n };\n \n+static fuse_ino_t single_file_path_to_ino(const char *path)\n+{\n+\tif (strcmp(path, \"/\") == 0)\n+\t\treturn FUSE_ROOT_ID;\n+\tif (strcmp(path + 1, single_file_name) == 0)\n+\t\treturn SINGLE_FILE_INO;\n+\treturn 0;\n+}\n+\n+static fuse_ino_t single_open_file_path_to_ino(const struct fuse_file_info *fi,\n+\t\t\t\t\t       const char *path)\n+{\n+\tif (fi)\n+\t\treturn fi->fh;\n+\treturn single_file_path_to_ino(path);\n+}\n+\n static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,\n \t\t       fuse_ino_t ino)\n {\n@@ -95,6 +115,13 @@ bool is_single_file_ino(fuse_ino_t ino)\n \treturn ino == SINGLE_FILE_INO;\n }\n \n+bool is_single_open_file_path(const struct fuse_file_info *fi, const char *name)\n+{\n+\tif (fi)\n+\t\treturn is_single_file_ino(fi->fh);\n+\treturn name[0] == '/' && strcmp(name + 1, single_file_name) == 0;\n+}\n+\n void single_file_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,\n \t\t\t    off_t off, struct fuse_file_info *fi)\n {\n@@ -121,6 +148,37 @@ void single_file_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,\n \tfree(b.p);\n }\n \n+int single_file_hl_readdir(const char *path, void *buf, fuse_fill_dir_t filler,\n+\t\t\t   off_t offset, struct fuse_file_info *fi,\n+\t\t\t   enum fuse_readdir_flags flags)\n+{\n+\tstruct stat stbuf;\n+\tfuse_ino_t ino = single_open_file_path_to_ino(fi, path);\n+\n+\tmemset(&stbuf, 0, sizeof(stbuf));\n+\n+\t(void) offset;\n+\t(void) flags;\n+\n+\tswitch (ino) {\n+\tcase FUSE_ROOT_ID:\n+\t\tbreak;\n+\tcase SINGLE_FILE_INO:\n+\t\treturn -ENOTDIR;\n+\tdefault:\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tstbuf.st_ino = FUSE_ROOT_ID;\n+\tfiller(buf, \".\", &stbuf, 0, FUSE_FILL_DIR_DEFAULTS);\n+\tfiller(buf, \"..\", &stbuf, 0, FUSE_FILL_DIR_DEFAULTS);\n+\n+\tstbuf.st_ino = SINGLE_FILE_INO;\n+\tfiller(buf, single_file_name, &stbuf, 0, FUSE_FILL_DIR_DEFAULTS);\n+\n+\treturn 0;\n+}\n+\n static bool sf_stat(fuse_ino_t ino, struct single_file_stat *llstat)\n {\n \tstruct fuse_entry_param *entry = &llstat->entry;\n@@ -248,40 +306,82 @@ void single_file_ll_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,\n \telse\n \t\tfuse_reply_statx(req, 0, &stx, 0.0);\n }\n+\n+int single_file_hl_statx(const char *path, int statx_flags, int statx_mask,\n+\t\t\t struct statx *stx, struct fuse_file_info *fi)\n+{\n+\tfuse_ino_t ino = single_open_file_path_to_ino(fi, path);\n+\tbool filled;\n+\n+\tif (!ino)\n+\t\treturn -ENOENT;\n+\n+\tif ((statx_flags & AT_STATX_FORCE_SYNC) && is_single_file_ino(ino) &&\n+\t    single_file.backing_fd >= 0) {\n+\t\tint ret = fsync(single_file.backing_fd);\n+\n+\t\tif (ret)\n+\t\t\treturn -errno;\n+\t}\n+\n+\tpthread_mutex_lock(&single_file.lock);\n+\tfilled = sf_statx(ino, statx_mask, stx);\n+\tpthread_mutex_unlock(&single_file.lock);\n+\n+\treturn filled ? 0 : -ENOENT;\n+}\n #else\n void single_file_ll_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,\n \t\t\t  struct fuse_file_info *fi)\n {\n \tfuse_reply_err(req, ENOSYS);\n }\n+\n+int single_file_hl_statx(const char *path, int statx_flags, int statx_mask,\n+\t\t\t struct statx *stx, struct fuse_file_info *fi)\n+{\n+\treturn -ENOSYS;\n+}\n #endif /* STATX_BASIC_STATS */\n \n+static void single_file_statfs(struct statvfs *buf)\n+{\n+\tpthread_mutex_lock(&single_file.lock);\n+\tbuf->f_bsize = single_file.blocksize;\n+\tbuf->f_frsize = 0;\n+\n+\tbuf->f_blocks = single_file.blocks;\n+\tbuf->f_bfree = 0;\n+\tbuf->f_bavail = 0;\n+\tbuf->f_files = 1;\n+\tbuf->f_ffree = 0;\n+\tbuf->f_favail = 0;\n+\tbuf->f_fsid = 0x50C00L;\n+\tbuf->f_flag = 0;\n+\tif (single_file.ro)\n+\t\tbuf->f_flag |= ST_RDONLY;\n+\tbuf->f_namemax = 255;\n+\tpthread_mutex_unlock(&single_file.lock);\n+}\n+\n void single_file_ll_statfs(fuse_req_t req, fuse_ino_t ino)\n {\n \tstruct statvfs buf;\n \n \t(void)ino;\n \n-\tpthread_mutex_lock(&single_file.lock);\n-\tbuf.f_bsize = single_file.blocksize;\n-\tbuf.f_frsize = 0;\n-\n-\tbuf.f_blocks = single_file.blocks;\n-\tbuf.f_bfree = 0;\n-\tbuf.f_bavail = 0;\n-\tbuf.f_files = 1;\n-\tbuf.f_ffree = 0;\n-\tbuf.f_favail = 0;\n-\tbuf.f_fsid = 0x50C00L;\n-\tbuf.f_flag = 0;\n-\tif (single_file.ro)\n-\t\tbuf.f_flag |= ST_RDONLY;\n-\tbuf.f_namemax = 255;\n-\tpthread_mutex_unlock(&single_file.lock);\n-\n+\tsingle_file_statfs(&buf);\n \tfuse_reply_statfs(req, &buf);\n }\n \n+int single_file_hl_statfs(const char *path, struct statvfs *buf)\n+{\n+\t(void)path;\n+\n+\tsingle_file_statfs(buf);\n+\treturn 0;\n+}\n+\n void single_file_ll_getattr(fuse_req_t req, fuse_ino_t ino,\n \t\t\t    struct fuse_file_info *fi)\n {\n@@ -301,6 +401,28 @@ void single_file_ll_getattr(fuse_req_t req, fuse_ino_t ino,\n \t\t\t\tllstat.entry.attr_timeout);\n }\n \n+int single_file_hl_getattr(const char *path, struct stat *stbuf,\n+\t\t\t   struct fuse_file_info *fi)\n+{\n+\tstruct single_file_stat llstat;\n+\tfuse_ino_t ino = single_open_file_path_to_ino(fi, path);\n+\tbool filled;\n+\n+\tif (!ino)\n+\t\treturn -ENOENT;\n+\n+\tmemset(&llstat, 0, sizeof(llstat));\n+\tpthread_mutex_lock(&single_file.lock);\n+\tfilled = sf_stat(ino, &llstat);\n+\tpthread_mutex_unlock(&single_file.lock);\n+\n+\tif (!filled)\n+\t\treturn -ENOENT;\n+\n+\tmemcpy(stbuf, &llstat.entry.attr, sizeof(*stbuf));\n+\treturn 0;\n+}\n+\n static void get_now(struct timespec *now)\n {\n #ifdef CLOCK_REALTIME\n@@ -353,6 +475,82 @@ void single_file_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,\n \tfuse_reply_err(req, EPERM);\n }\n \n+int single_file_hl_chmod(const char *path, mode_t mode,\n+\t\t\t struct fuse_file_info *fi)\n+{\n+\tfuse_ino_t ino = single_open_file_path_to_ino(fi, path);\n+\n+\tif (!ino)\n+\t\treturn -ENOENT;\n+\tif (ino != SINGLE_FILE_INO)\n+\t\treturn -EPERM;\n+\tif (single_file.ro)\n+\t\treturn -EPERM;\n+\n+\tpthread_mutex_lock(&single_file.lock);\n+\tsingle_file.mode = (single_file.mode & S_IFMT) | (mode & ~S_IFMT);\n+\tget_now(&single_file.ctime);\n+\tpthread_mutex_unlock(&single_file.lock);\n+\n+\treturn 0;\n+}\n+\n+static void set_time(const struct timespec *ctv, struct timespec *tv)\n+{\n+\tswitch (ctv->tv_nsec) {\n+\tcase UTIME_OMIT:\n+\t\treturn;\n+\tcase UTIME_NOW:\n+\t\tget_now(tv);\n+\t\tbreak;\n+\tdefault:\n+\t\tmemcpy(tv, ctv, sizeof(*tv));\n+\t\tbreak;\n+\t}\n+}\n+\n+int single_file_hl_utimens(const char *path, const struct timespec ctv[2],\n+\t\t\t   struct fuse_file_info *fi)\n+{\n+\tfuse_ino_t ino = single_open_file_path_to_ino(fi, path);\n+\n+\tif (!ino)\n+\t\treturn -ENOENT;\n+\tif (ino != SINGLE_FILE_INO)\n+\t\treturn -EPERM;\n+\tif (single_file.ro)\n+\t\treturn -EPERM;\n+\n+\tpthread_mutex_lock(&single_file.lock);\n+\tset_time(&ctv[0], &single_file.atime);\n+\tset_time(&ctv[1], &single_file.mtime);\n+\tget_now(&single_file.ctime);\n+\tpthread_mutex_unlock(&single_file.lock);\n+\n+\treturn 0;\n+}\n+\n+int single_file_hl_chown(const char *path, uid_t owner, gid_t group,\n+\t\t\t struct fuse_file_info *fi)\n+{\n+\t(void)path;\n+\t(void)owner;\n+\t(void)group;\n+\t(void)fi;\n+\n+\treturn -EPERM;\n+}\n+\n+int single_file_hl_truncate(const char *path, off_t len,\n+\t\t\t    struct fuse_file_info *fi)\n+{\n+\t(void)path;\n+\t(void)len;\n+\t(void)fi;\n+\n+\treturn -EPERM;\n+}\n+\n void single_file_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)\n {\n \tstruct single_file_stat llstat;\n@@ -384,6 +582,34 @@ void single_file_ll_open(fuse_req_t req, fuse_ino_t ino,\n \t\tfuse_reply_open(req, fi);\n }\n \n+int single_file_hl_opendir(const char *path, struct fuse_file_info *fi)\n+{\n+\tfuse_ino_t ino = single_file_path_to_ino(path);\n+\n+\tif (!ino)\n+\t\treturn -ENOENT;\n+\tif (ino == SINGLE_FILE_INO)\n+\t\treturn -ENOTDIR;\n+\n+\tfi->fh = ino;\n+\treturn 0;\n+}\n+\n+int single_file_hl_open(const char *path, struct fuse_file_info *fi)\n+{\n+\tfuse_ino_t ino = single_file_path_to_ino(path);\n+\n+\tif (!ino)\n+\t\treturn -ENOENT;\n+\tif (ino != SINGLE_FILE_INO)\n+\t\treturn -EISDIR;\n+\tif (single_file.ro && (fi->flags & O_ACCMODE) != O_RDONLY)\n+\t\treturn -EROFS;\n+\n+\tfi->fh = ino;\n+\treturn 0;\n+}\n+\n void single_file_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,\n \t\t\t  struct fuse_file_info *fi)\n {\n@@ -401,6 +627,26 @@ void single_file_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,\n \tfuse_reply_err(req, ret);\n }\n \n+int single_file_hl_fsync(const char *path, int datasync,\n+\t\t\t struct fuse_file_info *fi)\n+{\n+\tfuse_ino_t ino = single_open_file_path_to_ino(fi, path);\n+\n+\t(void)datasync;\n+\n+\tif (!ino)\n+\t\treturn -ENOENT;\n+\n+\tif (ino == SINGLE_FILE_INO) {\n+\t\tint ret = fsync(single_file.backing_fd);\n+\n+\t\tif (ret)\n+\t\t\treturn -errno;\n+\t}\n+\n+\treturn 0;\n+}\n+\n unsigned long long parse_num_blocks(const char *arg, int log_block_size)\n {\n \tchar *p;\n","prefixes":["12/13"]}