Merge patch series "fs: last of the pseudofs mount api conversions"

Eric Sandeen <sandeen@redhat.com> says:

pstore used mount_single, which used to transparently do a
remount operation on a fresh mount of an existing superblock.
The new get_tree_single does not do this, but prior discussion
on fsdevel seems to indicate that this isn't expected to be a
problem. We can watch for issues.

devpts is just a forward port from work dhowells did already, and it
seems straightforward. I left error messages as they are rather than
converting to the mount API message channel for now.

devtmpfs was already converted, but left a .mount in place, rather
than using .get_tree. The solution to this is ... unique so some
scrutiny is probably wise.

The last patch removes reconfigure_single, mount_single, and
compare_single because no users remain, but we could also wait until
all conversions are done, and remove all infrastructure at that time
instead, if desired.

* patches from https://lore.kernel.org/r/20250205213931.74614-1-sandeen@redhat.com:
  vfs: remove some unused old mount api code
  devtmpfs: replace ->mount with ->get_tree in public instance
  vfs: Convert devpts to use the new mount API
  pstore: convert to the new mount API

Link: https://lore.kernel.org/r/20250205213931.74614-1-sandeen@redhat.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner
2025-02-06 11:47:55 +01:00
6 changed files with 246 additions and 251 deletions

View File

@@ -63,22 +63,6 @@ __setup("devtmpfs.mount=", mount_param);
static struct vfsmount *mnt;
static struct dentry *public_dev_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
{
struct super_block *s = mnt->mnt_sb;
int err;
atomic_inc(&s->s_active);
down_write(&s->s_umount);
err = reconfigure_single(s, flags, data);
if (err < 0) {
deactivate_locked_super(s);
return ERR_PTR(err);
}
return dget(s->s_root);
}
static struct file_system_type internal_fs_type = {
.name = "devtmpfs",
#ifdef CONFIG_TMPFS
@@ -89,9 +73,40 @@ static struct file_system_type internal_fs_type = {
.kill_sb = kill_litter_super,
};
/* Simply take a ref on the existing mount */
static int devtmpfs_get_tree(struct fs_context *fc)
{
struct super_block *sb = mnt->mnt_sb;
atomic_inc(&sb->s_active);
down_write(&sb->s_umount);
fc->root = dget(sb->s_root);
return 0;
}
/* Ops are filled in during init depending on underlying shmem or ramfs type */
struct fs_context_operations devtmpfs_context_ops = {};
/* Call the underlying initialization and set to our ops */
static int devtmpfs_init_fs_context(struct fs_context *fc)
{
int ret;
#ifdef CONFIG_TMPFS
ret = shmem_init_fs_context(fc);
#else
ret = ramfs_init_fs_context(fc);
#endif
if (ret < 0)
return ret;
fc->ops = &devtmpfs_context_ops;
return 0;
}
static struct file_system_type dev_fs_type = {
.name = "devtmpfs",
.mount = public_dev_mount,
.init_fs_context = devtmpfs_init_fs_context,
};
static int devtmpfs_submit_req(struct req *req, const char *tmp)
@@ -442,6 +457,31 @@ static int __ref devtmpfsd(void *p)
return 0;
}
/*
* Get the underlying (shmem/ramfs) context ops to build ours
*/
static int devtmpfs_configure_context(void)
{
struct fs_context *fc;
fc = fs_context_for_reconfigure(mnt->mnt_root, mnt->mnt_sb->s_flags,
MS_RMT_MASK);
if (IS_ERR(fc))
return PTR_ERR(fc);
/* Set up devtmpfs_context_ops based on underlying type */
devtmpfs_context_ops.free = fc->ops->free;
devtmpfs_context_ops.dup = fc->ops->dup;
devtmpfs_context_ops.parse_param = fc->ops->parse_param;
devtmpfs_context_ops.parse_monolithic = fc->ops->parse_monolithic;
devtmpfs_context_ops.get_tree = &devtmpfs_get_tree;
devtmpfs_context_ops.reconfigure = fc->ops->reconfigure;
put_fs_context(fc);
return 0;
}
/*
* Create devtmpfs instance, driver-core devices will add their device
* nodes here.
@@ -456,6 +496,13 @@ int __init devtmpfs_init(void)
pr_err("unable to create devtmpfs %ld\n", PTR_ERR(mnt));
return PTR_ERR(mnt);
}
err = devtmpfs_configure_context();
if (err) {
pr_err("unable to configure devtmpfs type %d\n", err);
return err;
}
err = register_filesystem(&dev_fs_type);
if (err) {
pr_err("unable to register devtmpfs type %d\n", err);

View File

@@ -12,6 +12,8 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include <linux/slab.h>
@@ -21,7 +23,6 @@
#include <linux/magic.h>
#include <linux/idr.h>
#include <linux/devpts_fs.h>
#include <linux/parser.h>
#include <linux/fsnotify.h>
#include <linux/seq_file.h>
@@ -87,14 +88,14 @@ enum {
Opt_err
};
static const match_table_t tokens = {
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_mode, "mode=%o"},
{Opt_ptmxmode, "ptmxmode=%o"},
{Opt_newinstance, "newinstance"},
{Opt_max, "max=%d"},
{Opt_err, NULL}
static const struct fs_parameter_spec devpts_param_specs[] = {
fsparam_u32 ("gid", Opt_gid),
fsparam_s32 ("max", Opt_max),
fsparam_u32oct ("mode", Opt_mode),
fsparam_flag ("newinstance", Opt_newinstance),
fsparam_u32oct ("ptmxmode", Opt_ptmxmode),
fsparam_u32 ("uid", Opt_uid),
{}
};
struct pts_fs_info {
@@ -214,93 +215,48 @@ void devpts_release(struct pts_fs_info *fsi)
deactivate_super(fsi->sb);
}
#define PARSE_MOUNT 0
#define PARSE_REMOUNT 1
/*
* parse_mount_options():
* Set @opts to mount options specified in @data. If an option is not
* specified in @data, set it to its default value.
*
* Note: @data may be NULL (in which case all options are set to default).
* devpts_parse_param - Parse mount parameters
*/
static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
static int devpts_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
char *p;
kuid_t uid;
kgid_t gid;
struct pts_fs_info *fsi = fc->s_fs_info;
struct pts_mount_opts *opts = &fsi->mount_opts;
struct fs_parse_result result;
int opt;
opts->setuid = 0;
opts->setgid = 0;
opts->uid = GLOBAL_ROOT_UID;
opts->gid = GLOBAL_ROOT_GID;
opts->mode = DEVPTS_DEFAULT_MODE;
opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
opts->max = NR_UNIX98_PTY_MAX;
opt = fs_parse(fc, devpts_param_specs, param, &result);
if (opt < 0)
return opt;
/* Only allow instances mounted from the initial mount
* namespace to tap the reserve pool of ptys.
*/
if (op == PARSE_MOUNT)
opts->reserve =
(current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns);
while ((p = strsep(&data, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
int option;
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case Opt_uid:
if (match_int(&args[0], &option))
return -EINVAL;
uid = make_kuid(current_user_ns(), option);
if (!uid_valid(uid))
return -EINVAL;
opts->uid = uid;
opts->setuid = 1;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return -EINVAL;
gid = make_kgid(current_user_ns(), option);
if (!gid_valid(gid))
return -EINVAL;
opts->gid = gid;
opts->setgid = 1;
break;
case Opt_mode:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->mode = option & S_IALLUGO;
break;
case Opt_ptmxmode:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->ptmxmode = option & S_IALLUGO;
break;
case Opt_newinstance:
break;
case Opt_max:
if (match_int(&args[0], &option) ||
option < 0 || option > NR_UNIX98_PTY_MAX)
return -EINVAL;
opts->max = option;
break;
default:
pr_err("called with bogus options\n");
return -EINVAL;
}
switch (opt) {
case Opt_uid:
opts->uid = result.uid;
opts->setuid = 1;
break;
case Opt_gid:
opts->gid = result.gid;
opts->setgid = 1;
break;
case Opt_mode:
opts->mode = result.uint_32 & S_IALLUGO;
break;
case Opt_ptmxmode:
opts->ptmxmode = result.uint_32 & S_IALLUGO;
break;
case Opt_newinstance:
break;
case Opt_max:
if (result.uint_32 > NR_UNIX98_PTY_MAX)
return invalf(fc, "max out of range");
opts->max = result.uint_32;
break;
}
return 0;
}
static int mknod_ptmx(struct super_block *sb)
static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
{
int mode;
int rc = -ENOMEM;
@@ -362,13 +318,23 @@ static void update_ptmx_mode(struct pts_fs_info *fsi)
}
}
static int devpts_remount(struct super_block *sb, int *flags, char *data)
static int devpts_reconfigure(struct fs_context *fc)
{
int err;
struct pts_fs_info *fsi = DEVPTS_SB(sb);
struct pts_mount_opts *opts = &fsi->mount_opts;
struct pts_fs_info *fsi = DEVPTS_SB(fc->root->d_sb);
struct pts_fs_info *new = fc->s_fs_info;
err = parse_mount_options(data, PARSE_REMOUNT, opts);
/* Apply the revised options. We don't want to change ->reserve.
* Ideally, we'd update each option conditionally on it having been
* explicitly changed, but the default is to reset everything so that
* would break UAPI...
*/
fsi->mount_opts.setuid = new->mount_opts.setuid;
fsi->mount_opts.setgid = new->mount_opts.setgid;
fsi->mount_opts.uid = new->mount_opts.uid;
fsi->mount_opts.gid = new->mount_opts.gid;
fsi->mount_opts.mode = new->mount_opts.mode;
fsi->mount_opts.ptmxmode = new->mount_opts.ptmxmode;
fsi->mount_opts.max = new->mount_opts.max;
/*
* parse_mount_options() restores options to default values
@@ -378,7 +344,7 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
*/
update_ptmx_mode(fsi);
return err;
return 0;
}
static int devpts_show_options(struct seq_file *seq, struct dentry *root)
@@ -402,31 +368,13 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root)
static const struct super_operations devpts_sops = {
.statfs = simple_statfs,
.remount_fs = devpts_remount,
.show_options = devpts_show_options,
};
static void *new_pts_fs_info(struct super_block *sb)
{
struct pts_fs_info *fsi;
fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
if (!fsi)
return NULL;
ida_init(&fsi->allocated_ptys);
fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
fsi->sb = sb;
return fsi;
}
static int
devpts_fill_super(struct super_block *s, void *data, int silent)
static int devpts_fill_super(struct super_block *s, struct fs_context *fc)
{
struct pts_fs_info *fsi = DEVPTS_SB(s);
struct inode *inode;
int error;
s->s_iflags &= ~SB_I_NODEV;
s->s_blocksize = 1024;
@@ -435,20 +383,11 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
s->s_op = &devpts_sops;
s->s_d_op = &simple_dentry_operations;
s->s_time_gran = 1;
fsi->sb = s;
error = -ENOMEM;
s->s_fs_info = new_pts_fs_info(s);
if (!s->s_fs_info)
goto fail;
error = parse_mount_options(data, PARSE_MOUNT, &DEVPTS_SB(s)->mount_opts);
if (error)
goto fail;
error = -ENOMEM;
inode = new_inode(s);
if (!inode)
goto fail;
return -ENOMEM;
inode->i_ino = 1;
simple_inode_init_ts(inode);
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
@@ -459,31 +398,60 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
s->s_root = d_make_root(inode);
if (!s->s_root) {
pr_err("get root dentry failed\n");
goto fail;
return -ENOMEM;
}
error = mknod_ptmx(s);
if (error)
goto fail_dput;
return 0;
fail_dput:
dput(s->s_root);
s->s_root = NULL;
fail:
return error;
return mknod_ptmx(s, fc);
}
/*
* devpts_mount()
* devpts_get_tree()
*
* Mount a new (private) instance of devpts. PTYs created in this
* instance are independent of the PTYs in other devpts instances.
*/
static struct dentry *devpts_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
static int devpts_get_tree(struct fs_context *fc)
{
return mount_nodev(fs_type, flags, data, devpts_fill_super);
return get_tree_nodev(fc, devpts_fill_super);
}
static void devpts_free_fc(struct fs_context *fc)
{
kfree(fc->s_fs_info);
}
static const struct fs_context_operations devpts_context_ops = {
.free = devpts_free_fc,
.parse_param = devpts_parse_param,
.get_tree = devpts_get_tree,
.reconfigure = devpts_reconfigure,
};
/*
* Set up the filesystem mount context.
*/
static int devpts_init_fs_context(struct fs_context *fc)
{
struct pts_fs_info *fsi;
fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
if (!fsi)
return -ENOMEM;
ida_init(&fsi->allocated_ptys);
fsi->mount_opts.uid = GLOBAL_ROOT_UID;
fsi->mount_opts.gid = GLOBAL_ROOT_GID;
fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
fsi->mount_opts.max = NR_UNIX98_PTY_MAX;
if (fc->purpose == FS_CONTEXT_FOR_MOUNT &&
current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns)
fsi->mount_opts.reserve = true;
fc->s_fs_info = fsi;
fc->ops = &devpts_context_ops;
return 0;
}
static void devpts_kill_sb(struct super_block *sb)
@@ -498,7 +466,8 @@ static void devpts_kill_sb(struct super_block *sb)
static struct file_system_type devpts_fs_type = {
.name = "devpts",
.mount = devpts_mount,
.init_fs_context = devpts_init_fs_context,
.parameters = devpts_param_specs,
.kill_sb = devpts_kill_sb,
.fs_flags = FS_USERNS_MOUNT,
};

View File

@@ -14,10 +14,10 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/mount.h>
#include <linux/seq_file.h>
#include <linux/ramfs.h>
#include <linux/parser.h>
#include <linux/fs_parser.h>
#include <linux/fs_context.h>
#include <linux/sched.h>
#include <linux/magic.h>
#include <linux/pstore.h>
@@ -226,37 +226,38 @@ static struct inode *pstore_get_inode(struct super_block *sb)
}
enum {
Opt_kmsg_bytes, Opt_err
Opt_kmsg_bytes
};
static const match_table_t tokens = {
{Opt_kmsg_bytes, "kmsg_bytes=%u"},
{Opt_err, NULL}
static const struct fs_parameter_spec pstore_param_spec[] = {
fsparam_u32 ("kmsg_bytes", Opt_kmsg_bytes),
{}
};
static void parse_options(char *options)
struct pstore_context {
unsigned int kmsg_bytes;
};
static int pstore_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
struct pstore_context *ctx = fc->fs_private;
struct fs_parse_result result;
int opt;
if (!options)
return;
opt = fs_parse(fc, pstore_param_spec, param, &result);
/* pstore has historically ignored invalid kmsg_bytes param */
if (opt < 0)
return 0;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case Opt_kmsg_bytes:
if (!match_int(&args[0], &option))
pstore_set_kmsg_bytes(option);
break;
}
switch (opt) {
case Opt_kmsg_bytes:
ctx->kmsg_bytes = result.uint_32;
break;
default:
return -EINVAL;
}
return 0;
}
/*
@@ -269,10 +270,12 @@ static int pstore_show_options(struct seq_file *m, struct dentry *root)
return 0;
}
static int pstore_remount(struct super_block *sb, int *flags, char *data)
static int pstore_reconfigure(struct fs_context *fc)
{
sync_filesystem(sb);
parse_options(data);
struct pstore_context *ctx = fc->fs_private;
sync_filesystem(fc->root->d_sb);
pstore_set_kmsg_bytes(ctx->kmsg_bytes);
return 0;
}
@@ -281,7 +284,6 @@ static const struct super_operations pstore_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
.evict_inode = pstore_evict_inode,
.remount_fs = pstore_remount,
.show_options = pstore_show_options,
};
@@ -406,8 +408,9 @@ void pstore_get_records(int quiet)
inode_unlock(d_inode(root));
}
static int pstore_fill_super(struct super_block *sb, void *data, int silent)
static int pstore_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct pstore_context *ctx = fc->fs_private;
struct inode *inode;
sb->s_maxbytes = MAX_LFS_FILESIZE;
@@ -417,7 +420,7 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
sb->s_op = &pstore_ops;
sb->s_time_gran = 1;
parse_options(data);
pstore_set_kmsg_bytes(ctx->kmsg_bytes);
inode = pstore_get_inode(sb);
if (inode) {
@@ -438,12 +441,26 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
return 0;
}
static struct dentry *pstore_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
static int pstore_get_tree(struct fs_context *fc)
{
return mount_single(fs_type, flags, data, pstore_fill_super);
if (fc->root)
return pstore_reconfigure(fc);
return get_tree_single(fc, pstore_fill_super);
}
static void pstore_free_fc(struct fs_context *fc)
{
kfree(fc->fs_private);
}
static const struct fs_context_operations pstore_context_ops = {
.parse_param = pstore_parse_param,
.get_tree = pstore_get_tree,
.reconfigure = pstore_reconfigure,
.free = pstore_free_fc,
};
static void pstore_kill_sb(struct super_block *sb)
{
guard(mutex)(&pstore_sb_lock);
@@ -456,11 +473,33 @@ static void pstore_kill_sb(struct super_block *sb)
INIT_LIST_HEAD(&records_list);
}
static int pstore_init_fs_context(struct fs_context *fc)
{
struct pstore_context *ctx;
ctx = kzalloc(sizeof(struct pstore_context), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
/*
* Global kmsg_bytes is initialized to default, and updated
* every time we (re)mount the single-sb filesystem with the
* option specified.
*/
ctx->kmsg_bytes = kmsg_bytes;
fc->fs_private = ctx;
fc->ops = &pstore_context_ops;
return 0;
}
static struct file_system_type pstore_fs_type = {
.owner = THIS_MODULE,
.name = "pstore",
.mount = pstore_mount,
.kill_sb = pstore_kill_sb,
.init_fs_context = pstore_init_fs_context,
.parameters = pstore_param_spec,
};
int __init pstore_init_fs(void)

View File

@@ -1737,61 +1737,6 @@ struct dentry *mount_nodev(struct file_system_type *fs_type,
}
EXPORT_SYMBOL(mount_nodev);
int reconfigure_single(struct super_block *s,
int flags, void *data)
{
struct fs_context *fc;
int ret;
/* The caller really need to be passing fc down into mount_single(),
* then a chunk of this can be removed. [Bollocks -- AV]
* Better yet, reconfiguration shouldn't happen, but rather the second
* mount should be rejected if the parameters are not compatible.
*/
fc = fs_context_for_reconfigure(s->s_root, flags, MS_RMT_MASK);
if (IS_ERR(fc))
return PTR_ERR(fc);
ret = parse_monolithic_mount_data(fc, data);
if (ret < 0)
goto out;
ret = reconfigure_super(fc);
out:
put_fs_context(fc);
return ret;
}
static int compare_single(struct super_block *s, void *p)
{
return 1;
}
struct dentry *mount_single(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int))
{
struct super_block *s;
int error;
s = sget(fs_type, compare_single, set_anon_super, flags, NULL);
if (IS_ERR(s))
return ERR_CAST(s);
if (!s->s_root) {
error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
if (!error)
s->s_flags |= SB_ACTIVE;
} else {
error = reconfigure_single(s, flags, data);
}
if (unlikely(error)) {
deactivate_locked_super(s);
return ERR_PTR(error);
}
return dget(s->s_root);
}
EXPORT_SYMBOL(mount_single);
/**
* vfs_get_tree - Get the mountable root
* @fc: The superblock configuration context.

View File

@@ -2641,9 +2641,6 @@ static inline bool is_mgtime(const struct inode *inode)
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_single(struct file_system_type *fs_type,
int flags, 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));

View File

@@ -144,8 +144,6 @@ extern void put_fs_context(struct fs_context *fc);
extern int vfs_parse_fs_param_source(struct fs_context *fc,
struct fs_parameter *param);
extern void fc_drop_locked(struct fs_context *fc);
int reconfigure_single(struct super_block *s,
int flags, void *data);
extern int get_tree_nodev(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,