Merge tag 'vfs-6.18-rc1.mount' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs mount updates from Christian Brauner:
 "This contains some work around mount api handling:

   - Output the warning message for mnt_too_revealing() triggered during
     fsmount() to the fscontext log. This makes it possible for the
     mount tool to output appropriate warnings on the command line.

     For example, with the newest fsopen()-based mount(8) from
     util-linux, the error messages now look like:

       # mount -t proc proc /tmp
       mount: /tmp: fsmount() failed: VFS: Mount too revealing.
              dmesg(1) may have more information after failed mount system call.

   - Do not consume fscontext log entries when returning -EMSGSIZE

     Userspace generally expects APIs that return -EMSGSIZE to allow for
     them to adjust their buffer size and retry the operation.

     However, the fscontext log would previously clear the message even
     in the -EMSGSIZE case.

     Given that it is very cheap for us to check whether the buffer is
     too small before we remove the message from the ring buffer, let's
     just do that instead.

   - Drop an unused argument from do_remount()"

* tag 'vfs-6.18-rc1.mount' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  vfs: fs/namespace.c: remove ms_flags argument from do_remount
  selftests/filesystems: add basic fscontext log tests
  fscontext: do not consume log entries when returning -EMSGSIZE
  vfs: output mount_too_revealing() errors to fscontext
  docs/vfs: Remove mentions to the old mount API helpers
  fscontext: add custom-prefix log helpers
  fs: Remove mount_bdev
  fs: Remove mount_nodev
This commit is contained in:
Linus Torvalds
2025-09-29 09:32:34 -07:00
9 changed files with 192 additions and 135 deletions

View File

@@ -209,31 +209,8 @@ method fills in is the "s_op" field. This is a pointer to a "struct
super_operations" which describes the next level of the filesystem
implementation.
Usually, a filesystem uses one of the generic mount() implementations
and provides a fill_super() callback instead. The generic variants are:
``mount_bdev``
mount a filesystem residing on a block device
``mount_nodev``
mount a filesystem that is not backed by a device
``mount_single``
mount a filesystem which shares the instance between all mounts
A fill_super() callback implementation has the following arguments:
``struct super_block *sb``
the superblock structure. The callback must initialize this
properly.
``void *data``
arbitrary mount options, usually comes as an ASCII string (see
"Mount Options" section)
``int silent``
whether or not to be silent on error
For more information on mounting (and the new mount API), see
Documentation/filesystems/mount_api.rst.
The Superblock Object
=====================

View File

@@ -18,50 +18,56 @@
#include "internal.h"
#include "mount.h"
static inline const char *fetch_message_locked(struct fc_log *log, size_t len,
bool *need_free)
{
const char *p;
int index;
if (unlikely(log->head == log->tail))
return ERR_PTR(-ENODATA);
index = log->tail & (ARRAY_SIZE(log->buffer) - 1);
p = log->buffer[index];
if (unlikely(strlen(p) > len))
return ERR_PTR(-EMSGSIZE);
log->buffer[index] = NULL;
*need_free = log->need_free & (1 << index);
log->need_free &= ~(1 << index);
log->tail++;
return p;
}
/*
* Allow the user to read back any error, warning or informational messages.
* Only one message is returned for each read(2) call.
*/
static ssize_t fscontext_read(struct file *file,
char __user *_buf, size_t len, loff_t *pos)
{
struct fs_context *fc = file->private_data;
struct fc_log *log = fc->log.log;
unsigned int logsize = ARRAY_SIZE(log->buffer);
ssize_t ret;
char *p;
ssize_t err;
const char *p __free(kfree) = NULL, *message;
bool need_free;
int index, n;
int n;
ret = mutex_lock_interruptible(&fc->uapi_mutex);
if (ret < 0)
return ret;
if (log->head == log->tail) {
mutex_unlock(&fc->uapi_mutex);
return -ENODATA;
}
index = log->tail & (logsize - 1);
p = log->buffer[index];
need_free = log->need_free & (1 << index);
log->buffer[index] = NULL;
log->need_free &= ~(1 << index);
log->tail++;
err = mutex_lock_interruptible(&fc->uapi_mutex);
if (err < 0)
return err;
message = fetch_message_locked(fc->log.log, len, &need_free);
mutex_unlock(&fc->uapi_mutex);
if (IS_ERR(message))
return PTR_ERR(message);
ret = -EMSGSIZE;
n = strlen(p);
if (n > len)
goto err_free;
ret = -EFAULT;
if (copy_to_user(_buf, p, n) != 0)
goto err_free;
ret = n;
err_free:
if (need_free)
kfree(p);
return ret;
p = message;
n = strlen(message);
if (copy_to_user(_buf, message, n))
return -EFAULT;
return n;
}
static int fscontext_release(struct inode *inode, struct file *file)

View File

@@ -3298,7 +3298,7 @@ static int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags)
* If you've mounted a non-root directory somewhere and want to do remount
* on it - tough luck.
*/
static int do_remount(struct path *path, int ms_flags, int sb_flags,
static int do_remount(struct path *path, int sb_flags,
int mnt_flags, void *data)
{
int err;
@@ -3736,8 +3736,10 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
int error;
error = security_sb_kern_mount(sb);
if (!error && mount_too_revealing(sb, &mnt_flags))
if (!error && mount_too_revealing(sb, &mnt_flags)) {
errorfcp(fc, "VFS", "Mount too revealing");
error = -EPERM;
}
if (unlikely(error)) {
fc_drop_locked(fc);
@@ -4121,7 +4123,7 @@ int path_mount(const char *dev_name, struct path *path,
if ((flags & (MS_REMOUNT | MS_BIND)) == (MS_REMOUNT | MS_BIND))
return do_reconfigure_mnt(path, mnt_flags);
if (flags & MS_REMOUNT)
return do_remount(path, flags, sb_flags, mnt_flags, data_page);
return do_remount(path, sb_flags, mnt_flags, data_page);
if (flags & MS_BIND)
return do_loopback(path, dev_name, flags & MS_REC);
if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
@@ -4453,7 +4455,7 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
ret = -EPERM;
if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) {
pr_warn("VFS: Mount too revealing\n");
errorfcp(fc, "VFS", "Mount too revealing");
goto err_unlock;
}

View File

@@ -1716,49 +1716,6 @@ int get_tree_bdev(struct fs_context *fc,
}
EXPORT_SYMBOL(get_tree_bdev);
static int test_bdev_super(struct super_block *s, void *data)
{
return !(s->s_iflags & SB_I_RETIRED) && s->s_dev == *(dev_t *)data;
}
struct dentry *mount_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int))
{
struct super_block *s;
int error;
dev_t dev;
error = lookup_bdev(dev_name, &dev);
if (error)
return ERR_PTR(error);
flags |= SB_NOSEC;
s = sget(fs_type, test_bdev_super, set_bdev_super, flags, &dev);
if (IS_ERR(s))
return ERR_CAST(s);
if (s->s_root) {
if ((flags ^ s->s_flags) & SB_RDONLY) {
deactivate_locked_super(s);
return ERR_PTR(-EBUSY);
}
} else {
error = setup_bdev_super(s, flags, NULL);
if (!error)
error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
}
s->s_flags |= SB_ACTIVE;
}
return dget(s->s_root);
}
EXPORT_SYMBOL(mount_bdev);
void kill_block_super(struct super_block *sb)
{
struct block_device *bdev = sb->s_bdev;
@@ -1773,26 +1730,6 @@ void kill_block_super(struct super_block *sb)
EXPORT_SYMBOL(kill_block_super);
#endif
struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int))
{
int error;
struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
if (IS_ERR(s))
return ERR_CAST(s);
error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
}
s->s_flags |= SB_ACTIVE;
return dget(s->s_root);
}
EXPORT_SYMBOL(mount_nodev);
/**
* vfs_get_tree - Get the mountable root
* @fc: The superblock configuration context.

View File

@@ -2713,12 +2713,6 @@ static inline bool is_mgtime(const struct inode *inode)
return inode->i_opflags & IOP_MGTIME;
}
extern struct dentry *mount_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int));
extern struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int));
extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path);
void retire_super(struct super_block *sb);
void generic_shutdown_super(struct super_block *sb);

View File

@@ -186,10 +186,12 @@ struct fc_log {
extern __attribute__((format(printf, 4, 5)))
void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt, ...);
#define __logfc(fc, l, fmt, ...) logfc((fc)->log.log, NULL, \
l, fmt, ## __VA_ARGS__)
#define __plog(p, l, fmt, ...) logfc((p)->log, (p)->prefix, \
l, fmt, ## __VA_ARGS__)
#define __logfc(fc, l, fmt, ...) \
logfc((fc)->log.log, NULL, (l), (fmt), ## __VA_ARGS__)
#define __plogp(p, prefix, l, fmt, ...) \
logfc((p)->log, (prefix), (l), (fmt), ## __VA_ARGS__)
#define __plog(p, l, fmt, ...) __plogp(p, (p)->prefix, l, fmt, ## __VA_ARGS__)
/**
* infof - Store supplementary informational message
* @fc: The context in which to log the informational message
@@ -201,6 +203,8 @@ void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt,
#define infof(fc, fmt, ...) __logfc(fc, 'i', fmt, ## __VA_ARGS__)
#define info_plog(p, fmt, ...) __plog(p, 'i', fmt, ## __VA_ARGS__)
#define infofc(fc, fmt, ...) __plog((&(fc)->log), 'i', fmt, ## __VA_ARGS__)
#define infofcp(fc, prefix, fmt, ...) \
__plogp((&(fc)->log), prefix, 'i', fmt, ## __VA_ARGS__)
/**
* warnf - Store supplementary warning message
@@ -213,6 +217,8 @@ void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt,
#define warnf(fc, fmt, ...) __logfc(fc, 'w', fmt, ## __VA_ARGS__)
#define warn_plog(p, fmt, ...) __plog(p, 'w', fmt, ## __VA_ARGS__)
#define warnfc(fc, fmt, ...) __plog((&(fc)->log), 'w', fmt, ## __VA_ARGS__)
#define warnfcp(fc, prefix, fmt, ...) \
__plogp((&(fc)->log), prefix, 'w', fmt, ## __VA_ARGS__)
/**
* errorf - Store supplementary error message
@@ -225,6 +231,8 @@ void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt,
#define errorf(fc, fmt, ...) __logfc(fc, 'e', fmt, ## __VA_ARGS__)
#define error_plog(p, fmt, ...) __plog(p, 'e', fmt, ## __VA_ARGS__)
#define errorfc(fc, fmt, ...) __plog((&(fc)->log), 'e', fmt, ## __VA_ARGS__)
#define errorfcp(fc, prefix, fmt, ...) \
__plogp((&(fc)->log), prefix, 'e', fmt, ## __VA_ARGS__)
/**
* invalf - Store supplementary invalid argument error message
@@ -237,5 +245,7 @@ void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt,
#define invalf(fc, fmt, ...) (errorf(fc, fmt, ## __VA_ARGS__), -EINVAL)
#define inval_plog(p, fmt, ...) (error_plog(p, fmt, ## __VA_ARGS__), -EINVAL)
#define invalfc(fc, fmt, ...) (errorfc(fc, fmt, ## __VA_ARGS__), -EINVAL)
#define invalfcp(fc, prefix, fmt, ...) \
(errorfcp(fc, prefix, fmt, ## __VA_ARGS__), -EINVAL)
#endif /* _LINUX_FS_CONTEXT_H */

View File

@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
dnotify_test
devpts_pts
fclog
file_stressor
anon_inode_test
kernfs_test

View File

@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test
TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog
TEST_GEN_PROGS_EXTENDED := dnotify_test
include ../lib.mk

View File

@@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Author: Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2025 SUSE LLC.
*/
#include <assert.h>
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>
#include "../kselftest_harness.h"
#define ASSERT_ERRNO(expected, _t, seen) \
__EXPECT(expected, #expected, \
({__typeof__(seen) _tmp_seen = (seen); \
_tmp_seen >= 0 ? _tmp_seen : -errno; }), #seen, _t, 1)
#define ASSERT_ERRNO_EQ(expected, seen) \
ASSERT_ERRNO(expected, ==, seen)
#define ASSERT_SUCCESS(seen) \
ASSERT_ERRNO(0, <=, seen)
FIXTURE(ns)
{
int host_mntns;
};
FIXTURE_SETUP(ns)
{
/* Stash the old mntns. */
self->host_mntns = open("/proc/self/ns/mnt", O_RDONLY|O_CLOEXEC);
ASSERT_SUCCESS(self->host_mntns);
/* Create a new mount namespace and make it private. */
ASSERT_SUCCESS(unshare(CLONE_NEWNS));
ASSERT_SUCCESS(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL));
}
FIXTURE_TEARDOWN(ns)
{
ASSERT_SUCCESS(setns(self->host_mntns, CLONE_NEWNS));
ASSERT_SUCCESS(close(self->host_mntns));
}
TEST_F(ns, fscontext_log_enodata)
{
int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
ASSERT_SUCCESS(fsfd);
/* A brand new fscontext has no log entries. */
char buf[128] = {};
for (int i = 0; i < 16; i++)
ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
ASSERT_SUCCESS(close(fsfd));
}
TEST_F(ns, fscontext_log_errorfc)
{
int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
ASSERT_SUCCESS(fsfd);
ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
char buf[128] = {};
ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
/* The message has been consumed. */
ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
ASSERT_SUCCESS(close(fsfd));
}
TEST_F(ns, fscontext_log_errorfc_after_fsmount)
{
int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
ASSERT_SUCCESS(fsfd);
ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0));
int mfd = fsmount(fsfd, FSMOUNT_CLOEXEC, MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NOSUID);
ASSERT_SUCCESS(mfd);
ASSERT_SUCCESS(move_mount(mfd, "", AT_FDCWD, "/tmp", MOVE_MOUNT_F_EMPTY_PATH));
/*
* The fscontext log should still contain data even after
* FSCONFIG_CMD_CREATE and fsmount().
*/
char buf[128] = {};
ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
/* The message has been consumed. */
ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
ASSERT_SUCCESS(close(fsfd));
}
TEST_F(ns, fscontext_log_emsgsize)
{
int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
ASSERT_SUCCESS(fsfd);
ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
char buf[128] = {};
/*
* Attempting to read a message with too small a buffer should not
* result in the message getting consumed.
*/
ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 0));
ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 1));
for (int i = 0; i < 16; i++)
ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 16));
ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
/* The message has been consumed. */
ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
ASSERT_SUCCESS(close(fsfd));
}
TEST_HARNESS_MAIN