Merge pull request #26704 from poettering/mnt-nosymlinks
Set MS_NOSYMFOLLOW for ESP + XBOOTLDR and many mount option clean-ups
This commit is contained in:
@@ -591,10 +591,22 @@ static inline int missing_fsopen(const char *fsname, unsigned flags) {
|
||||
|
||||
#if !HAVE_FSCONFIG
|
||||
|
||||
#ifndef FSCONFIG_SET_FLAG
|
||||
#define FSCONFIG_SET_FLAG 0 /* Set parameter, supplying no value */
|
||||
#endif
|
||||
|
||||
#ifndef FSCONFIG_SET_STRING
|
||||
#define FSCONFIG_SET_STRING 1 /* Set parameter, supplying a string value */
|
||||
#endif
|
||||
|
||||
#ifndef FSCONFIG_SET_FD
|
||||
#define FSCONFIG_SET_FD 5 /* Set parameter, supplying an object by fd */
|
||||
#endif
|
||||
|
||||
#ifndef FSCONFIG_CMD_CREATE
|
||||
#define FSCONFIG_CMD_CREATE 6 /* Invoke superblock creation */
|
||||
#endif
|
||||
|
||||
static inline int missing_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux) {
|
||||
# if defined __NR_fsconfig && __NR_fsconfig >= 0
|
||||
return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
|
||||
@@ -609,6 +621,26 @@ static inline int missing_fsconfig(int fd, unsigned cmd, const char *key, const
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_FSMOUNT
|
||||
|
||||
#ifndef FSMOUNT_CLOEXEC
|
||||
#define FSMOUNT_CLOEXEC 0x00000001
|
||||
#endif
|
||||
|
||||
static inline int missing_fsmount(int fd, unsigned flags, unsigned ms_flags) {
|
||||
# if defined __NR_fsmount && __NR_fsmount >= 0
|
||||
return syscall(__NR_fsmount, fd, flags, ms_flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define fsmount missing_fsmount
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_GETDENTS64
|
||||
|
||||
static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
|
||||
|
||||
+157
-3
@@ -3,6 +3,9 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mount.h>
|
||||
#if WANT_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase-symlinks.h"
|
||||
@@ -10,6 +13,8 @@
|
||||
#include "fileio.h"
|
||||
#include "filesystems.h"
|
||||
#include "fs-util.h"
|
||||
#include "missing_fs.h"
|
||||
#include "missing_mount.h"
|
||||
#include "missing_stat.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "mkdir.h"
|
||||
@@ -456,6 +461,15 @@ bool fstype_is_ro(const char *fstype) {
|
||||
}
|
||||
|
||||
bool fstype_can_discard(const char *fstype) {
|
||||
int r;
|
||||
|
||||
assert(fstype);
|
||||
|
||||
/* On new kernels we can just ask the kernel */
|
||||
r = mount_option_supported(fstype, "discard", NULL);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
return STR_IN_SET(fstype,
|
||||
"btrfs",
|
||||
"f2fs",
|
||||
@@ -464,10 +478,42 @@ bool fstype_can_discard(const char *fstype) {
|
||||
"xfs");
|
||||
}
|
||||
|
||||
bool fstype_can_uid_gid(const char *fstype) {
|
||||
bool fstype_can_norecovery(const char *fstype) {
|
||||
int r;
|
||||
|
||||
/* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
|
||||
* current and future. */
|
||||
assert(fstype);
|
||||
|
||||
/* On new kernels we can just ask the kernel */
|
||||
r = mount_option_supported(fstype, "norecovery", NULL);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
return STR_IN_SET(fstype,
|
||||
"ext3",
|
||||
"ext4",
|
||||
"xfs",
|
||||
"btrfs");
|
||||
}
|
||||
|
||||
bool fstype_can_umask(const char *fstype) {
|
||||
int r;
|
||||
|
||||
assert(fstype);
|
||||
|
||||
/* On new kernels we can just ask the kernel */
|
||||
r = mount_option_supported(fstype, "umask", "0077");
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
return streq(fstype, "vfat");
|
||||
}
|
||||
|
||||
bool fstype_can_uid_gid(const char *fstype) {
|
||||
/* All file systems that have a uid=/gid= mount option that fixates the owners of all files and
|
||||
* directories, current and future. Note that this does *not* ask the kernel via
|
||||
* mount_option_supported() here because the uid=/gid= setting of various file systems mean different
|
||||
* things: some apply it only to the root dir inode, others to all inodes in the file system. Thus we
|
||||
* maintain the curated list below. 😢 */
|
||||
|
||||
return STR_IN_SET(fstype,
|
||||
"adfs",
|
||||
@@ -602,3 +648,111 @@ int mount_propagation_flag_from_string(const char *name, unsigned long *ret) {
|
||||
bool mount_propagation_flag_is_valid(unsigned long flag) {
|
||||
return IN_SET(flag, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE);
|
||||
}
|
||||
|
||||
unsigned long ms_nosymfollow_supported(void) {
|
||||
_cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
|
||||
static int cache = -1;
|
||||
|
||||
/* Returns MS_NOSYMFOLLOW if it is supported, zero otherwise. */
|
||||
|
||||
if (cache >= 0)
|
||||
return cache ? MS_NOSYMFOLLOW : 0;
|
||||
|
||||
/* Checks if MS_NOSYMFOLLOW is supported (which was added in 5.10). We use the new mount API's
|
||||
* mount_setattr() call for that, which was added in 5.12, which is close enough. */
|
||||
|
||||
fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
|
||||
if (fsfd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to open superblock context for tmpfs: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to create tmpfs superblock: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
|
||||
if (mntfd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to turn superblock fd into mount fd: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mount_setattr(mntfd, "", AT_EMPTY_PATH|AT_RECURSIVE,
|
||||
&(struct mount_attr) {
|
||||
.attr_set = MOUNT_ATTR_NOSYMFOLLOW,
|
||||
}, sizeof(struct mount_attr)) < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to set MOUNT_ATTR_NOSYMFOLLOW mount attribute: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache = true;
|
||||
return MS_NOSYMFOLLOW;
|
||||
|
||||
not_supported:
|
||||
cache = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mount_option_supported(const char *fstype, const char *key, const char *value) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
/* Checks if the specified file system supports a mount option. Returns > 0 if it suppors it, == 0 if
|
||||
* it does not. Return -EAGAIN if we can't determine it. And any other error otherwise. */
|
||||
|
||||
assert(fstype);
|
||||
assert(key);
|
||||
|
||||
fd = fsopen(fstype, FSOPEN_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return -EAGAIN; /* new mount API not available → don't know */
|
||||
|
||||
return log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype);
|
||||
}
|
||||
|
||||
/* Various file systems have not been converted to the new mount API yet. For such file systems
|
||||
* fsconfig() with FSCONFIG_SET_STRING/FSCONFIG_SET_FLAG never fail. Which sucks, because we want to
|
||||
* use it for testing support, after all. Let's hence do a check if the file system got converted yet
|
||||
* first. */
|
||||
if (fsconfig(fd, FSCONFIG_SET_FD, "adefinitelynotexistingmountoption", NULL, fd) < 0) {
|
||||
/* If FSCONFIG_SET_FD is not supported for the fs, then the file system was not converted to
|
||||
* the new mount API yet. If it returns EINVAL the mount option doesn't exist, but the fstype
|
||||
* is converted. */
|
||||
if (errno == EOPNOTSUPP)
|
||||
return -EAGAIN; /* FSCONFIG_SET_FD not supported on the fs, hence not converted to new mount API → don't know */
|
||||
if (errno != EINVAL)
|
||||
return log_debug_errno(errno, "Failed to check if file system has been converted to new mount API: %m");
|
||||
|
||||
/* So FSCONFIG_SET_FD worked, but the option didn't exist (we got EINVAL), this means the fs
|
||||
* is converted. Let's now ask the actual question we wonder about. */
|
||||
} else
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "FSCONFIG_SET_FD worked unexpectedly for '%s', whoa!", fstype);
|
||||
|
||||
if (value)
|
||||
r = fsconfig(fd, FSCONFIG_SET_STRING, key, value, 0);
|
||||
else
|
||||
r = fsconfig(fd, FSCONFIG_SET_FLAG, key, NULL, 0);
|
||||
if (r < 0) {
|
||||
if (errno == EINVAL)
|
||||
return false; /* EINVAL means option not supported. */
|
||||
|
||||
return log_debug_errno(errno, "Failed to set '%s%s%s' on '%s' superblock context: %m",
|
||||
key, value ? "=" : "", strempty(value), fstype);
|
||||
}
|
||||
|
||||
return true; /* works! */
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ bool fstype_is_blockdev_backed(const char *fstype);
|
||||
bool fstype_is_ro(const char *fsype);
|
||||
bool fstype_can_discard(const char *fstype);
|
||||
bool fstype_can_uid_gid(const char *fstype);
|
||||
bool fstype_can_norecovery(const char *fstype);
|
||||
bool fstype_can_umask(const char *fstype);
|
||||
|
||||
int dev_is_devtmpfs(void);
|
||||
|
||||
@@ -58,3 +60,7 @@ int mount_nofollow(const char *source, const char *target, const char *filesyste
|
||||
const char *mount_propagation_flag_to_string(unsigned long flags);
|
||||
int mount_propagation_flag_from_string(const char *name, unsigned long *ret);
|
||||
bool mount_propagation_flag_is_valid(unsigned long flag);
|
||||
|
||||
unsigned long ms_nosymfollow_supported(void);
|
||||
|
||||
int mount_option_supported(const char *fstype, const char *key, const char *value);
|
||||
|
||||
Reference in New Issue
Block a user