mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
mount.fuse3: integrate systemd service startup
Teach mount.fuse3 how to start fuse via systemd service, if present. 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
96702c5b80
commit
f127c2de34
@@ -7,12 +7,20 @@ fuservicemount3 \- mount a FUSE filesystem that runs as a system socket service
|
||||
.B mountpoint
|
||||
.BI -t " fstype"
|
||||
[
|
||||
.I options
|
||||
.BI -o " options"
|
||||
]
|
||||
|
||||
.B fuservicemount3
|
||||
.BI -t " fstype"
|
||||
.B --check
|
||||
|
||||
.SH DESCRIPTION
|
||||
Mount a filesystem using a FUSE server that runs as a socket service.
|
||||
These servers can be contained using the platform's service management
|
||||
framework.
|
||||
|
||||
The second form checks if there is a FUSE service available for the given
|
||||
filesystem type.
|
||||
.SH "AUTHORS"
|
||||
.LP
|
||||
The author of the fuse socket service code is Darrick J. Wong <djwong@kernel.org>.
|
||||
|
||||
+2
-1
@@ -83,7 +83,8 @@ private_cfg.set('FUSE_SERVICE_SOCKET_PERMS', service_socket_perms)
|
||||
# Test for presence of some functions
|
||||
test_funcs = [ 'fork', 'fstatat', 'openat', 'readlinkat', 'pipe2',
|
||||
'splice', 'vmsplice', 'posix_fallocate', 'fdatasync',
|
||||
'utimensat', 'copy_file_range', 'fallocate', 'fspacectl' ]
|
||||
'utimensat', 'copy_file_range', 'fallocate', 'fspacectl',
|
||||
'faccessat' ]
|
||||
foreach func : test_funcs
|
||||
private_cfg.set('HAVE_' + func.to_upper(),
|
||||
cc.has_function(func, prefix: include_default, args: args_default))
|
||||
|
||||
@@ -9,10 +9,57 @@
|
||||
* This program wraps the mounting of FUSE filesystems that run in systemd
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "fuse_config.h"
|
||||
#include "mount_service.h"
|
||||
|
||||
static int check_service(const char *fstype)
|
||||
{
|
||||
if (!fstype) {
|
||||
fprintf(stderr,
|
||||
"fuservicemount: expected fs type for --check\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return mount_service_present(fstype) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *fstype = NULL;
|
||||
bool check = false;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If the user passes us exactly the args -t FSTYPE --check then
|
||||
* we'll just check if there's a service for the FSTYPE fuse server.
|
||||
* This doesn't tell us if the listening socket is actually connected
|
||||
* to anything.
|
||||
*/
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--check")) {
|
||||
if (check) {
|
||||
check = false;
|
||||
break;
|
||||
}
|
||||
check = true;
|
||||
} else if (!strcmp(argv[i], "-t") && i + 1 < argc) {
|
||||
if (fstype) {
|
||||
check = false;
|
||||
break;
|
||||
}
|
||||
fstype = argv[i + 1];
|
||||
i++;
|
||||
} else {
|
||||
check = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (check)
|
||||
return check_service(fstype);
|
||||
|
||||
return mount_service_main(argc, argv);
|
||||
}
|
||||
|
||||
+10
-5
@@ -7,22 +7,27 @@ executable('fusermount3', ['fusermount.c', '../lib/mount_util.c', '../lib/mount_
|
||||
install_dir: get_option('bindir'),
|
||||
c_args: '-DFUSE_CONF="@0@"'.format(fuseconf_path))
|
||||
|
||||
mount_service_sources = []
|
||||
mount_service_cflags = []
|
||||
if private_cfg.get('HAVE_SERVICEMOUNT', false)
|
||||
executable('fuservicemount3', ['mount_service.c', 'fuservicemount.c', '../lib/mount_util.c',
|
||||
'fuser_conf.c'],
|
||||
mount_service_sources += ['mount_service.c', '../lib/mount_util.c', 'fuser_conf.c']
|
||||
mount_service_cflags += ['-DFUSE_CONF="@0@"'.format(fuseconf_path)]
|
||||
fuservicemount_path = join_paths(get_option('prefix'), get_option('sbindir'))
|
||||
mount_service_cflags += ['-DFUSERVICEMOUNT_DIR="@0@"'.format(fuservicemount_path)]
|
||||
executable('fuservicemount3', ['fuservicemount.c'] + mount_service_sources,
|
||||
include_directories: include_dirs,
|
||||
link_with: [ libfuse ],
|
||||
install: true,
|
||||
install_dir: get_option('sbindir'),
|
||||
c_args: ['-DFUSE_USE_VERSION=319', '-DFUSE_CONF="@0@"'.format(fuseconf_path)])
|
||||
c_args: ['-DFUSE_USE_VERSION=319'] + mount_service_cflags)
|
||||
endif
|
||||
|
||||
executable('mount.fuse3', ['mount.fuse.c'],
|
||||
executable('mount.fuse3', ['mount.fuse.c'] + mount_service_sources,
|
||||
include_directories: include_dirs,
|
||||
link_with: [ libfuse ],
|
||||
install: true,
|
||||
install_dir: get_option('sbindir'),
|
||||
c_args: '-DFUSE_USE_VERSION=317')
|
||||
c_args: ['-DFUSE_USE_VERSION=319'] + mount_service_cflags)
|
||||
|
||||
|
||||
udevrulesdir = get_option('udevrulesdir')
|
||||
|
||||
+148
-21
@@ -6,6 +6,9 @@
|
||||
See the file GPL2.txt.
|
||||
*/
|
||||
|
||||
/* For environ */
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "fuse_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -17,6 +20,9 @@
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef HAVE_SERVICEMOUNT
|
||||
#include <spawn.h>
|
||||
#endif
|
||||
|
||||
#ifdef linux
|
||||
#include <sys/prctl.h>
|
||||
@@ -49,6 +55,9 @@
|
||||
#endif
|
||||
|
||||
#include "fuse.h"
|
||||
#ifdef HAVE_SERVICEMOUNT
|
||||
# include "mount_service.h"
|
||||
#endif
|
||||
|
||||
static char *progname;
|
||||
|
||||
@@ -233,6 +242,105 @@ static void drop_and_lock_capabilities(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SERVICEMOUNT
|
||||
#define FUSERVICEMOUNT_PROG "fuservicemount3"
|
||||
|
||||
static int mount_service_child(char **argv)
|
||||
{
|
||||
const char *full_path = FUSERVICEMOUNT_DIR "/" FUSERVICEMOUNT_PROG;
|
||||
pid_t child_pid;
|
||||
int child_status;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* First try the install path, then a system install, just like we do
|
||||
* for fusermount. See man 7 environ for the global environ pointer.
|
||||
*/
|
||||
ret = posix_spawn(&child_pid, full_path, NULL, NULL,
|
||||
(char *const *)argv, environ);
|
||||
if (ret)
|
||||
ret = posix_spawnp(&child_pid, FUSERVICEMOUNT_PROG, NULL, NULL,
|
||||
(char * const *)argv, environ);
|
||||
if (ret) {
|
||||
fprintf(stderr, "%s: could not start %s helper: %s\n",
|
||||
argv[0], FUSERVICEMOUNT_PROG, strerror(ret));
|
||||
return MOUNT_SERVICE_FALLBACK_NEEDED;
|
||||
}
|
||||
|
||||
ret = waitpid(child_pid, &child_status, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "%s: could not wait for %s helper: %s\n",
|
||||
argv[0], FUSERVICEMOUNT_PROG, strerror(errno));
|
||||
return MOUNT_SERVICE_FALLBACK_NEEDED;
|
||||
}
|
||||
|
||||
if (WIFEXITED(child_status))
|
||||
return WEXITSTATUS(child_status);
|
||||
|
||||
/* terminated due to signal or coredump */
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int try_service_main(char *argv0, char *fstype, char *source,
|
||||
const char *mountpoint, char *options)
|
||||
{
|
||||
char **argv;
|
||||
char dash_o[] = "-o";
|
||||
char dash_t[] = "-t";
|
||||
char *mntpt;
|
||||
int argc = 5; /* argv[0], -t type, mountpoint, and trailing NULL */
|
||||
int i = 0;
|
||||
int ret;
|
||||
|
||||
if (!mount_service_present(fstype))
|
||||
return MOUNT_SERVICE_FALLBACK_NEEDED;
|
||||
|
||||
/* This can be an empty string if "mount.fuse3 null# /tmp/a" */
|
||||
if (source && source[0] == 0)
|
||||
source = NULL;
|
||||
|
||||
mntpt = strdup(mountpoint);
|
||||
if (!mntpt) {
|
||||
perror("mountpoint allocation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (source)
|
||||
argc++;
|
||||
if (options)
|
||||
argc += 2;
|
||||
|
||||
argv = calloc(argc, sizeof(char *));
|
||||
if (!argv) {
|
||||
perror("argv allocation");
|
||||
free(mntpt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv[i++] = argv0;
|
||||
if (source)
|
||||
argv[i++] = source;
|
||||
argv[i++] = mntpt;
|
||||
argv[i++] = dash_t;
|
||||
argv[i++] = fstype;
|
||||
if (options) {
|
||||
argv[i++] = dash_o;
|
||||
argv[i++] = options;
|
||||
}
|
||||
argv[i] = 0;
|
||||
|
||||
if (getuid() != 0) {
|
||||
ret = mount_service_child(argv);
|
||||
} else {
|
||||
/* We're root, just do the mount directly. */
|
||||
ret = mount_service_main(argc - 1, argv);
|
||||
}
|
||||
free(argv);
|
||||
free(mntpt);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *type = NULL;
|
||||
@@ -280,9 +388,7 @@ int main(int argc, char *argv[])
|
||||
mountpoint = argv[2];
|
||||
|
||||
for (i = 3; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-v") == 0) {
|
||||
continue;
|
||||
} else if (strcmp(argv[i], "-t") == 0) {
|
||||
if (strcmp(argv[i], "-t") == 0) {
|
||||
i++;
|
||||
|
||||
if (i == argc) {
|
||||
@@ -303,6 +409,30 @@ int main(int argc, char *argv[])
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
if (source) {
|
||||
dup_source = xstrdup(source);
|
||||
type = dup_source;
|
||||
source = strchr(type, '#');
|
||||
if (source)
|
||||
*source++ = '\0';
|
||||
if (!type[0]) {
|
||||
fprintf(stderr, "%s: empty filesystem type\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "%s: empty source\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 3; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-v") == 0) {
|
||||
continue;
|
||||
} else if (strcmp(argv[i], "-o") == 0) {
|
||||
char *opts;
|
||||
const char *opt;
|
||||
@@ -366,24 +496,6 @@ int main(int argc, char *argv[])
|
||||
if (suid)
|
||||
options = add_option("suid", options);
|
||||
|
||||
if (!type) {
|
||||
if (source) {
|
||||
dup_source = xstrdup(source);
|
||||
type = dup_source;
|
||||
source = strchr(type, '#');
|
||||
if (source)
|
||||
*source++ = '\0';
|
||||
if (!type[0]) {
|
||||
fprintf(stderr, "%s: empty filesystem type\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "%s: empty source\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (setuid_name && setuid_name[0]) {
|
||||
#ifdef linux
|
||||
if (drop_privileges) {
|
||||
@@ -429,6 +541,21 @@ int main(int argc, char *argv[])
|
||||
drop_and_lock_capabilities();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SERVICEMOUNT
|
||||
/*
|
||||
* Now that we know the desired filesystem type, see if we can find
|
||||
* a socket service implementing that, if we haven't selected any weird
|
||||
* options that would prevent that.
|
||||
*/
|
||||
if (!pass_fuse_fd && !(setuid_name && setuid_name[0])) {
|
||||
int ret = try_service_main(argv[0], type, source, mountpoint,
|
||||
options);
|
||||
if (ret != MOUNT_SERVICE_FALLBACK_NEEDED)
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
add_arg(&command, type);
|
||||
if (source)
|
||||
add_arg(&command, source);
|
||||
|
||||
@@ -2066,3 +2066,46 @@ out:
|
||||
mount_service_destroy(&mo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool mount_service_present(const char *fstype)
|
||||
{
|
||||
struct sockaddr_un name;
|
||||
struct stat stbuf;
|
||||
char path[PATH_MAX];
|
||||
const char *subtype;
|
||||
ssize_t written;
|
||||
int ret;
|
||||
|
||||
subtype = mount_service_subtype(fstype);
|
||||
if (!subtype)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The full path to the socket must fit within the AF_UNIX socket path
|
||||
* buffer, which is much shorter than PATH_MAX.
|
||||
*/
|
||||
written = snprintf(path, sizeof(path), FUSE_SERVICE_SOCKET_DIR "/%s",
|
||||
subtype);
|
||||
if (written >= sizeof(name.sun_path) || written >= sizeof(path))
|
||||
return false;
|
||||
|
||||
ret = stat(path, &stbuf);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
if (!S_ISSOCK(stbuf.st_mode))
|
||||
return false;
|
||||
|
||||
#ifdef HAVE_FACCESSAT
|
||||
/*
|
||||
* Can we write to the service socket with the real uid? This accounts
|
||||
* for setuid and ACLs on the socket. Note that we connect() to the
|
||||
* socket having dropped setuid privileges.
|
||||
*/
|
||||
ret = faccessat(AT_FDCWD, path, W_OK, 0);
|
||||
#else
|
||||
/* Can we write to the service socket with the real uid? */
|
||||
ret = access(path, W_OK);
|
||||
#endif
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
@@ -37,4 +37,13 @@ int mount_service_main(int argc, char *argv[]);
|
||||
*/
|
||||
const char *mount_service_subtype(const char *fstype);
|
||||
|
||||
/**
|
||||
* Discover if there is a fuse service socket for the given fuse filesystem type.
|
||||
* The type must not contain a path separator.
|
||||
*
|
||||
* @param fstype the type of a fuse filesystem type (e.g. fuse.Y, fuseblk.Y, or Y)
|
||||
* @return true if available, false if not
|
||||
*/
|
||||
bool mount_service_present(const char *fstype);
|
||||
|
||||
#endif /* MOUNT_SERVICE_H_ */
|
||||
|
||||
Reference in New Issue
Block a user