mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
mount_service: add systemd socket service mounting helper
Create a mount helper program that can start a fuse server that runs as a socket-based systemd service, and a new libfuse module to wrap all the details of communicating between the mount helper and the containerized fuse server. This enables untrusted ext4 mounts via systemd service containers, which avoids the problem of malicious filesystems compromising the integrity of the running kernel through memory corruption. In theory this could also be supported via inetd and clones, though the author hasn't found one that supports AF_UNIX sockets. Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
This commit is contained in:
committed by
Bernd Schubert
parent
0d7e725415
commit
5d8b9a39a1
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* FUSE: Filesystem in Userspace
|
||||
* Copyright (C) 2025-2026 Oracle.
|
||||
* Author: Darrick J. Wong <djwong@kernel.org>
|
||||
*
|
||||
* This program can be distributed under the terms of the GNU LGPLv2.
|
||||
* See the file LGPL2.txt.
|
||||
*/
|
||||
#ifndef FUSE_SERVICE_H_
|
||||
#define FUSE_SERVICE_H_
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Low level API
|
||||
*
|
||||
* IMPORTANT: you should define FUSE_USE_VERSION before including this
|
||||
* header. To use the newest API define it to 319 (recommended for any
|
||||
* new application).
|
||||
*/
|
||||
|
||||
#ifndef FUSE_USE_VERSION
|
||||
#error FUSE_USE_VERSION not defined
|
||||
#endif
|
||||
|
||||
#include "fuse_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if FUSE_MAKE_VERSION(3, 19) <= FUSE_USE_VERSION
|
||||
|
||||
struct fuse_service;
|
||||
|
||||
/**
|
||||
* Accept a socket created by mount.service for information exchange.
|
||||
*
|
||||
* @param sfp pointer to pointer to a service context. The pointer will always
|
||||
* be initialized by this function; use fuse_service_accepted to
|
||||
* find out if the fuse server is actually running as a service.
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_accept(struct fuse_service **sfp);
|
||||
|
||||
/**
|
||||
* Has the fuse server accepted a service context?
|
||||
*
|
||||
* @param sf service context
|
||||
* @return true if it has, false if not
|
||||
*/
|
||||
static inline bool fuse_service_accepted(struct fuse_service *sf)
|
||||
{
|
||||
return sf != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will the mount service helper accept the allow_other option?
|
||||
*
|
||||
* @param sf service context
|
||||
* @return true if it has, false if not
|
||||
*/
|
||||
bool fuse_service_can_allow_other(struct fuse_service *sf);
|
||||
|
||||
/**
|
||||
* Release all resources associated with the service context.
|
||||
*
|
||||
* @param sfp service context
|
||||
*/
|
||||
void fuse_service_release(struct fuse_service *sf);
|
||||
|
||||
/**
|
||||
* Destroy a service context and release all resources
|
||||
*
|
||||
* @param sfp pointer to pointer to a service context
|
||||
*/
|
||||
void fuse_service_destroy(struct fuse_service **sfp);
|
||||
|
||||
/**
|
||||
* Append the command line arguments from the mount service helper to an
|
||||
* existing fuse_args structure. The fuse_args should have been initialized
|
||||
* with the argc and argv passed to main().
|
||||
*
|
||||
* @param sfp service context
|
||||
* @param args arguments to modify (input+output)
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_append_args(struct fuse_service *sf, struct fuse_args *args);
|
||||
|
||||
/**
|
||||
* Generate the effective fuse server command line from the args structure.
|
||||
* The args structure should be the outcome from fuse_service_append_args.
|
||||
* The resulting string is suitable for setproctitle and must be freed by the
|
||||
* callre.
|
||||
*
|
||||
* @param argc argument count passed to main()
|
||||
* @param argv argument vector passed to main()
|
||||
* @param args fuse args structure
|
||||
* @return effective command line string, or NULL
|
||||
*/
|
||||
char *fuse_service_cmdline(int argc, char *argv[], struct fuse_args *args);
|
||||
|
||||
struct fuse_cmdline_opts;
|
||||
|
||||
/**
|
||||
* Utility function to parse common options for simple file systems
|
||||
* using the low-level API. A help text that describes the available
|
||||
* options can be printed with `fuse_cmdline_help`. A single
|
||||
* non-option argument is treated as the mountpoint. Multiple
|
||||
* non-option arguments will result in an error.
|
||||
*
|
||||
* If neither -o subtype= or -o fsname= options are given, a new
|
||||
* subtype option will be added and set to the basename of the program
|
||||
* (the fsname will remain unset, and then defaults to "fuse").
|
||||
*
|
||||
* Known options will be removed from *args*, unknown options will
|
||||
* remain. The mountpoint will not be checked here; that is the job of
|
||||
* mount.service.
|
||||
*
|
||||
* @param args argument vector (input+output)
|
||||
* @param opts output argument for parsed options
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int fuse_service_parse_cmdline_opts(struct fuse_args *args,
|
||||
struct fuse_cmdline_opts *opts);
|
||||
|
||||
/**
|
||||
* Don't complain if this file cannot be opened.
|
||||
*/
|
||||
#define FUSE_SERVICE_REQUEST_FILE_QUIET (1U << 0)
|
||||
|
||||
/**
|
||||
* Ask the mount.service helper to open a file on behalf of the fuse server.
|
||||
*
|
||||
* @param sf service context
|
||||
* @param path the path to file
|
||||
* @param open_flags O_ flags
|
||||
* @param create_mode mode with which to create the file
|
||||
* @param request_flags set of FUSE_SERVICE_REQUEST_* flags
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_request_file(struct fuse_service *sf, const char *path,
|
||||
int open_flags, mode_t create_mode,
|
||||
unsigned int request_flags);
|
||||
|
||||
/**
|
||||
* Ask the mount.service helper to open a block device on behalf of the fuse
|
||||
* server.
|
||||
*
|
||||
* @param sf service context
|
||||
* @param path the path to file
|
||||
* @param open_flags O_ flags
|
||||
* @param create_mode mode with which to create the file
|
||||
* @param request_flags set of FUSE_SERVICE_REQUEST_* flags
|
||||
* @param block_size set the block device block size to this value
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_request_blockdev(struct fuse_service *sf, const char *path,
|
||||
int open_flags, mode_t create_mode,
|
||||
unsigned int request_flags,
|
||||
unsigned int block_size);
|
||||
|
||||
/**
|
||||
* Receive a file previously requested.
|
||||
*
|
||||
* @param sf service context
|
||||
* @param path to file
|
||||
* @fdp pointer to file descriptor, which will be set a non-negative file
|
||||
* descriptor value on success, or negative errno on failure
|
||||
* @return 0 on success, or negative errno on socket communication failure
|
||||
*/
|
||||
int fuse_service_receive_file(struct fuse_service *sf,
|
||||
const char *path, int *fdp);
|
||||
|
||||
/**
|
||||
* Prevent the mount.service server from sending us any more open files.
|
||||
*
|
||||
* @param sf service context
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_finish_file_requests(struct fuse_service *sf);
|
||||
|
||||
/**
|
||||
* Require that the filesystem mount point have the expected file format
|
||||
* (S_IFDIR/S_IFREG). Can be overridden when calling
|
||||
* fuse_service_session_mount.
|
||||
*
|
||||
* @param sf service context
|
||||
* @param expected_fmt expected mode (S_IFDIR/S_IFREG) for mount point, or 0
|
||||
* to skip checks
|
||||
*/
|
||||
void fuse_service_expect_mount_format(struct fuse_service *sf,
|
||||
mode_t expected_fmt);
|
||||
|
||||
/**
|
||||
* Bind a FUSE file system to the fuse session inside a fuse service process,
|
||||
* then ask the mount.service helper to mount the filesystem for us. The fuse
|
||||
* client will begin sending requests to the fuse server immediately after
|
||||
* this. Do not call fuse_daemonize() when running as a fuse service.
|
||||
*
|
||||
* @param sf service context
|
||||
* @param se fuse session
|
||||
* @param expected_fmt expected mode (S_IFDIR/S_IFREG) for mount point, or 0
|
||||
* to skip checks
|
||||
* @param opts command line options
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_session_mount(struct fuse_service *sf, struct fuse_session *se,
|
||||
mode_t expected_fmt,
|
||||
struct fuse_cmdline_opts *opts);
|
||||
|
||||
/**
|
||||
* Ask the mount helper to unmount th e filesystem.
|
||||
*
|
||||
* @param sf service context
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_session_unmount(struct fuse_service *sf);
|
||||
|
||||
/**
|
||||
* Bid farewell to the mount.service helper. It is still necessary to call
|
||||
* fuse_service_destroy after this.
|
||||
*
|
||||
* @param sf service context
|
||||
* @param exitcode fuse server process exit status
|
||||
* @return 0 on success, or negative errno on failure
|
||||
*/
|
||||
int fuse_service_send_goodbye(struct fuse_service *sf, int exitcode);
|
||||
|
||||
/**
|
||||
* Exit routine for a fuse server running as a systemd service.
|
||||
*
|
||||
* @param ret 0 for success, nonzero for service failure.
|
||||
* @return a value to be passed to exit() or returned from main
|
||||
*/
|
||||
int fuse_service_exit(int ret);
|
||||
|
||||
#endif /* FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 19) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FUSE_SERVICE_H_ */
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* FUSE: Filesystem in Userspace
|
||||
* Copyright (C) 2025-2026 Oracle.
|
||||
* Author: Darrick J. Wong <djwong@kernel.org>
|
||||
*
|
||||
* This program can be distributed under the terms of the GNU LGPLv2.
|
||||
* See the file LGPL2.txt.
|
||||
*/
|
||||
#ifndef FUSE_SERVICE_PRIV_H_
|
||||
#define FUSE_SERVICE_PRIV_H_
|
||||
|
||||
/* All numeric fields are network order (big-endian) when going across the socket */
|
||||
|
||||
struct fuse_service_memfd_arg {
|
||||
uint32_t pos;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
struct fuse_service_memfd_argv {
|
||||
uint32_t magic;
|
||||
uint32_t argc;
|
||||
};
|
||||
|
||||
#define FUSE_SERVICE_MAX_CMD_SIZE (65536)
|
||||
|
||||
#define FUSE_SERVICE_ARGS_MAGIC 0x41524753 /* ARGS */
|
||||
|
||||
/* mount.service sends a hello to the server and it replies */
|
||||
#define FUSE_SERVICE_HELLO_CMD 0x53414654 /* SAFT */
|
||||
#define FUSE_SERVICE_HELLO_REPLY 0x4c415354 /* LAST */
|
||||
|
||||
/* fuse servers send commands to mount.service */
|
||||
#define FUSE_SERVICE_OPEN_CMD 0x4f50454e /* OPEN */
|
||||
#define FUSE_SERVICE_OPEN_BDEV_CMD 0x42444556 /* BDEV */
|
||||
#define FUSE_SERVICE_FSOPEN_CMD 0x54595045 /* TYPE */
|
||||
#define FUSE_SERVICE_SOURCE_CMD 0x4e414d45 /* NAME */
|
||||
#define FUSE_SERVICE_MNTOPTS_CMD 0x4f505453 /* OPTS */
|
||||
#define FUSE_SERVICE_MNTPT_CMD 0x4d4e5450 /* MNTP */
|
||||
#define FUSE_SERVICE_MOUNT_CMD 0x444f4954 /* DOIT */
|
||||
#define FUSE_SERVICE_UNMOUNT_CMD 0x554d4e54 /* UMNT */
|
||||
#define FUSE_SERVICE_BYE_CMD 0x42594545 /* BYEE */
|
||||
|
||||
/* mount.service sends replies to the fuse server */
|
||||
#define FUSE_SERVICE_OPEN_REPLY 0x46494c45 /* FILE */
|
||||
#define FUSE_SERVICE_SIMPLE_REPLY 0x5245504c /* REPL */
|
||||
|
||||
struct fuse_service_packet {
|
||||
uint32_t magic; /* FUSE_SERVICE_*_{CMD,REPLY} */
|
||||
};
|
||||
|
||||
#define FUSE_SERVICE_PROTO (1)
|
||||
#define FUSE_SERVICE_MIN_PROTO (1)
|
||||
#define FUSE_SERVICE_MAX_PROTO (1)
|
||||
|
||||
#define FUSE_SERVICE_FLAG_ALLOW_OTHER (1U << 0)
|
||||
|
||||
#define FUSE_SERVICE_FLAGS (FUSE_SERVICE_FLAG_ALLOW_OTHER)
|
||||
|
||||
struct fuse_service_hello {
|
||||
struct fuse_service_packet p;
|
||||
uint16_t min_version;
|
||||
uint16_t max_version;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static inline bool check_null_endbyte(const void *p, size_t psz)
|
||||
{
|
||||
return *((const char *)p + psz - 1) == 0;
|
||||
}
|
||||
|
||||
struct fuse_service_hello_reply {
|
||||
struct fuse_service_packet p;
|
||||
uint16_t version;
|
||||
uint16_t padding;
|
||||
};
|
||||
|
||||
struct fuse_service_simple_reply {
|
||||
struct fuse_service_packet p;
|
||||
uint32_t error; /* positive errno */
|
||||
};
|
||||
|
||||
struct fuse_service_requested_file {
|
||||
struct fuse_service_packet p;
|
||||
uint32_t error; /* positive errno */
|
||||
char path[];
|
||||
};
|
||||
|
||||
static inline size_t sizeof_fuse_service_requested_file(size_t pathlen)
|
||||
{
|
||||
return sizeof(struct fuse_service_requested_file) + pathlen + 1;
|
||||
}
|
||||
|
||||
#define FUSE_SERVICE_FSOPEN_FUSEBLK (1U << 0)
|
||||
#define FUSE_SERVICE_FSOPEN_FLAGS (FUSE_SERVICE_FSOPEN_FUSEBLK)
|
||||
|
||||
struct fuse_service_fsopen_command {
|
||||
struct fuse_service_packet p;
|
||||
uint32_t fsopen_flags;
|
||||
};
|
||||
|
||||
#define FUSE_SERVICE_OPEN_QUIET (1U << 0)
|
||||
#define FUSE_SERVICE_OPEN_FLAGS (FUSE_SERVICE_OPEN_QUIET)
|
||||
|
||||
struct fuse_service_open_command {
|
||||
struct fuse_service_packet p;
|
||||
uint32_t open_flags;
|
||||
uint32_t create_mode;
|
||||
uint32_t request_flags;
|
||||
uint32_t block_size;
|
||||
char path[];
|
||||
};
|
||||
|
||||
static inline size_t sizeof_fuse_service_open_command(size_t pathlen)
|
||||
{
|
||||
return sizeof(struct fuse_service_open_command) + pathlen + 1;
|
||||
}
|
||||
|
||||
struct fuse_service_string_command {
|
||||
struct fuse_service_packet p;
|
||||
char value[];
|
||||
};
|
||||
|
||||
static inline size_t sizeof_fuse_service_string_command(size_t len)
|
||||
{
|
||||
return sizeof(struct fuse_service_string_command) + len + 1;
|
||||
}
|
||||
|
||||
struct fuse_service_mountpoint_command {
|
||||
struct fuse_service_packet p;
|
||||
uint16_t expected_fmt;
|
||||
uint16_t padding;
|
||||
char value[];
|
||||
};
|
||||
|
||||
static inline size_t sizeof_fuse_service_mountpoint_command(size_t len)
|
||||
{
|
||||
return sizeof(struct fuse_service_mountpoint_command) + len + 1;
|
||||
}
|
||||
|
||||
struct fuse_service_bye_command {
|
||||
struct fuse_service_packet p;
|
||||
uint32_t exitcode;
|
||||
};
|
||||
|
||||
struct fuse_service_mount_command {
|
||||
struct fuse_service_packet p;
|
||||
uint32_t ms_flags;
|
||||
};
|
||||
|
||||
struct fuse_service_unmount_command {
|
||||
struct fuse_service_packet p;
|
||||
};
|
||||
|
||||
int fuse_parse_cmdline_service(struct fuse_args *args,
|
||||
struct fuse_cmdline_opts *opts);
|
||||
|
||||
#define FUSE_SERVICE_ARGV "argv"
|
||||
#define FUSE_SERVICE_FUSEDEV "fusedev"
|
||||
|
||||
#endif /* FUSE_SERVICE_PRIV_H_ */
|
||||
@@ -2,4 +2,8 @@ libfuse_headers = [ 'fuse.h', 'fuse_common.h', 'fuse_lowlevel.h',
|
||||
'fuse_opt.h', 'cuse_lowlevel.h', 'fuse_log.h',
|
||||
'fuse_daemonize.h' ]
|
||||
|
||||
if private_cfg.get('HAVE_SERVICEMOUNT', false)
|
||||
libfuse_headers += [ 'fuse_service.h' ]
|
||||
endif
|
||||
|
||||
install_headers(libfuse_headers, subdir: 'fuse3')
|
||||
|
||||
Reference in New Issue
Block a user