Skip to content
This repository was archived by the owner on Sep 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 92 additions & 11 deletions lib/mount_fsmount.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,87 @@
#include <sys/mount.h>
#include <sys/syscall.h>

#ifdef NEED_NEW_MOUNT_API_SYSCALL_WRAPPERS
/*
* Kernel supports the new mount API (fsopen/fsconfig/fsmount/move_mount)
* but libc doesn't provide the wrapper functions yet (e.g. glibc added
* these only in 2.36) - call the syscalls directly. Numbers are
* identical across x86_64, i386, arm and arm64. fuse_-prefixed so the
* fallback never clashes with a libc extern declaration; where libc has
* them the #else aliases map straight to the libc wrappers.
*/
#ifndef __NR_fsopen
#define __NR_fsopen 430
#endif
#ifndef __NR_fsconfig
#define __NR_fsconfig 431
#endif
#ifndef __NR_fsmount
#define __NR_fsmount 432
#endif
#ifndef __NR_move_mount
#define __NR_move_mount 429
#endif

static inline int fuse_fsopen(const char *fsname, unsigned int flags)
{
return syscall(__NR_fsopen, fsname, flags);
}

static inline int fuse_fsconfig(int fd, unsigned int cmd, const char *key,
const void *value, int aux)
{
return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
}

static inline int fuse_fsmount(int fd, unsigned int flags, unsigned int ms_flags)
{
return syscall(__NR_fsmount, fd, flags, ms_flags);
}

static inline int fuse_move_mount(int from_dfd, const char *from_pathname,
int to_dfd, const char *to_pathname,
unsigned int flags)
{
return syscall(__NR_move_mount, from_dfd, from_pathname,
to_dfd, to_pathname, flags);
}
#else
#define fuse_fsopen fsopen
#define fuse_fsconfig fsconfig
#define fuse_fsmount fsmount
#define fuse_move_mount move_mount
#endif /* NEED_NEW_MOUNT_API_SYSCALL_WRAPPERS */

/*
* New mount API constants come from <linux/mount.h>. Define the ones an
* older or partial uapi header omits so this file still builds - values are
* the kernel uapi definitions. FSCONFIG_* are an enum there, so these guards
* fire only when a name is genuinely absent; <linux/mount.h> is already
* included via mount_i_linux.h, so a fallback macro cannot clash with it.
*/
#ifndef FSOPEN_CLOEXEC
#define FSOPEN_CLOEXEC 0x00000001
#endif
#ifndef FSMOUNT_CLOEXEC
#define FSMOUNT_CLOEXEC 0x00000001
#endif
#ifndef MOVE_MOUNT_F_EMPTY_PATH
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
#endif
#ifndef MOVE_MOUNT_T_EMPTY_PATH
#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040
#endif
#ifndef FSCONFIG_SET_FLAG
#define FSCONFIG_SET_FLAG 0
#endif
#ifndef FSCONFIG_SET_STRING
#define FSCONFIG_SET_STRING 1
#endif
#ifndef FSCONFIG_CMD_CREATE
#define FSCONFIG_CMD_CREATE 6
#endif

/*
* Mount attribute flags for fsmount() - from linux/mount.h
* This file is only compiled conditionally when support for the new
Expand Down Expand Up @@ -107,7 +188,7 @@ int set_fsconfig_ms_flags(int fsfd, unsigned long *ms_flags)
if (!(flags & mount_flags[i].flag))
continue;

ret = fsconfig(fsfd, FSCONFIG_SET_FLAG, mount_flags[i].opt, NULL, 0);
ret = fuse_fsconfig(fsfd, FSCONFIG_SET_FLAG, mount_flags[i].opt, NULL, 0);
if (ret) {
int save_errno = errno;

Expand Down Expand Up @@ -137,7 +218,7 @@ int apply_fsconfig_opt_fd(int fsfd, const char *value)
int res;

/* The fd parameter is a u32 value, not a file descriptor to pass */
res = fsconfig(fsfd, FSCONFIG_SET_STRING, "fd", value, 0);
res = fuse_fsconfig(fsfd, FSCONFIG_SET_STRING, "fd", value, 0);
if (res == -1) {
int save_errno = errno;

Expand All @@ -153,7 +234,7 @@ int apply_fsconfig_opt_string(int fsfd, const char *key, const char *value)
{
int res, save_errno;

res = fsconfig(fsfd, FSCONFIG_SET_STRING, key, value, 0);
res = fuse_fsconfig(fsfd, FSCONFIG_SET_STRING, key, value, 0);
save_errno = errno;
if (res == -1) {
fuse_log(FUSE_LOG_ERR, "fuse: fsconfig SET_STRING %s=%s failed: ",
Expand All @@ -169,7 +250,7 @@ int fuse_fsopen_base_type(int blkdev)
const char *type = blkdev ? "fuseblk" : "fuse";
int fsfd;

fsfd = fsopen(type, FSOPEN_CLOEXEC);
fsfd = fuse_fsopen(type, FSOPEN_CLOEXEC);
if (fsfd == -1)
return -errno;
return fsfd;
Expand All @@ -179,7 +260,7 @@ int fuse_fsconfig_subtype(int fsfd, const char *subtype)
{
int res;

res = fsconfig(fsfd, FSCONFIG_SET_STRING, "subtype", subtype, 0);
res = fuse_fsconfig(fsfd, FSCONFIG_SET_STRING, "subtype", subtype, 0);
if (res == -1) {
int save_errno = errno;

Expand Down Expand Up @@ -213,7 +294,7 @@ static int apply_opt_flag(int fsfd, const char *opt)
{
int res;

res = fsconfig(fsfd, FSCONFIG_SET_FLAG, opt, NULL, 0);
res = fuse_fsconfig(fsfd, FSCONFIG_SET_FLAG, opt, NULL, 0);
if (res == -1) {
int save_errno = errno;

Expand Down Expand Up @@ -450,7 +531,7 @@ int fuse_kern_fsmount(const char *mnt, int dest_mnt_fd, unsigned long flags,
}

/* Configure source */
res = fsconfig(fsfd, FSCONFIG_SET_STRING, "source", source, 0);
res = fuse_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", source, 0);
if (res == -1) {
err = -errno;
log_fsconfig_kmsg(fsfd);
Expand All @@ -475,7 +556,7 @@ int fuse_kern_fsmount(const char *mnt, int dest_mnt_fd, unsigned long flags,
}

/* Create the filesystem instance */
res = fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
res = fuse_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
if (res == -1) {
err = -errno;
log_fsconfig_kmsg(fsfd);
Expand All @@ -493,7 +574,7 @@ int fuse_kern_fsmount(const char *mnt, int dest_mnt_fd, unsigned long flags,
}

/* Create mount object with mount attributes */
mountfd = fsmount(fsfd, FSMOUNT_CLOEXEC, mount_attrs);
mountfd = fuse_fsmount(fsfd, FSMOUNT_CLOEXEC, mount_attrs);
if (mountfd == -1) {
err = -errno;
log_fsconfig_kmsg(fsfd);
Expand All @@ -506,11 +587,11 @@ int fuse_kern_fsmount(const char *mnt, int dest_mnt_fd, unsigned long flags,
fsfd = -1;

if (dest_mnt_fd >= 0)
res = move_mount(mountfd, "", dest_mnt_fd, "",
res = fuse_move_mount(mountfd, "", dest_mnt_fd, "",
MOVE_MOUNT_F_EMPTY_PATH |
MOVE_MOUNT_T_EMPTY_PATH);
else
res = move_mount(mountfd, "", AT_FDCWD, mnt,
res = fuse_move_mount(mountfd, "", AT_FDCWD, mnt,
MOVE_MOUNT_F_EMPTY_PATH);
if (res == -1) {
err = -errno;
Expand Down
71 changes: 56 additions & 15 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,6 @@ special_funcs = {
}
}
''',
'new_mount_api': '''
#define _GNU_SOURCE
#include <sys/mount.h>
#include <linux/mount.h>
#include <unistd.h>
#include <fcntl.h>

int main(void) {
int fsfd = fsopen("fuse", FSOPEN_CLOEXEC);
int res = fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0);
int mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
res = move_mount(mntfd, "", AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);
return 0;
}
''',
'systemd_headers': '''
#include <systemd/sd-daemon.h>

Expand All @@ -161,6 +146,62 @@ foreach name, code : special_funcs
name: name + ' check'))
endforeach

# New Linux mount API (fsopen/fsconfig/fsmount/move_mount): the kernel
# syscalls landed in 5.2, but libc wrapper functions can lag behind (e.g.
# glibc added them only in 2.36). Test kernel header support and libc
# wrapper support separately, so the direct syscall() fallback wrappers
# in mount_i_linux.h are used whenever libc lacks them - regardless of
# which libc or distro that is. The two code snippets and the arg list
# are named so test/meson.build can reuse them verbatim against fixture
# headers, instead of duplicating the checks.
mount_api_headers_check_code = '''
#include <linux/mount.h>
int main(void) {
int x = FSOPEN_CLOEXEC | FSMOUNT_CLOEXEC | MOVE_MOUNT_F_EMPTY_PATH;
int y = FSCONFIG_SET_STRING | FSCONFIG_CMD_CREATE;
int z = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NODEV |
MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NOATIME;
return x + y + z;
}
'''

mount_api_libc_check_code = '''
#define _GNU_SOURCE
#include <sys/mount.h>
#include <linux/mount.h>
#include <unistd.h>
#include <fcntl.h>

int main(void) {
int fsfd = fsopen("fuse", FSOPEN_CLOEXEC);
int res = fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0);
int mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
res = move_mount(mntfd, "", AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);
return 0;
}
'''

# -Werror=implicit-function-declaration: without it, a missing
# declaration only warns on some compilers, and the link can still
# silently succeed against a same-named symbol the libc happens to
# export - which would defeat the point of testing for the wrapper
# functions' declared presence.
mount_api_check_args = args_default + [ '-Werror=implicit-function-declaration' ]

have_new_mount_api_headers = cc.compiles(mount_api_headers_check_code,
args: mount_api_check_args,
name: 'new mount API headers check')

have_new_mount_api_libc_funcs = cc.links(mount_api_libc_check_code,
args: mount_api_check_args,
name: 'new mount API libc wrapper check')

private_cfg.set('HAVE_NEW_MOUNT_API', have_new_mount_api_headers)
if have_new_mount_api_headers and not have_new_mount_api_libc_funcs
private_cfg.set('NEED_NEW_MOUNT_API_SYSCALL_WRAPPERS', true)
message('libc lacks new mount API wrapper functions, using direct syscalls')
endif

# Headers checks
test_headers = [ 'sys/xattr.h', 'linux/limits.h' ]
foreach header : test_headers
Expand Down
Loading