Unverified Commit e8fe0d4b authored by Christian Brauner's avatar Christian Brauner
Browse files

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: default avatarChristian Brauner <brauner@kernel.org>
parents 2014c95a bdfa77e7
Loading
Loading
Loading
Loading
+64 −17
Original line number Diff line number Diff line
@@ -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);
+110 −141
Original line number Diff line number Diff line
@@ -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;

	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;

	/* 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;
	struct pts_fs_info *fsi = fc->s_fs_info;
	struct pts_mount_opts *opts = &fsi->mount_opts;
	struct fs_parse_result result;
	int opt;

		if (!*p)
			continue;
	opt = fs_parse(fc, devpts_param_specs, param, &result);
	if (opt < 0)
		return opt;

		token = match_token(p, tokens, args);
		switch (token) {
	switch (opt) {
	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->uid = result.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->gid = result.gid;
		opts->setgid = 1;
		break;
	case Opt_mode:
			if (match_octal(&args[0], &option))
				return -EINVAL;
			opts->mode = option & S_IALLUGO;
		opts->mode = result.uint_32 & S_IALLUGO;
		break;
	case Opt_ptmxmode:
			if (match_octal(&args[0], &option))
				return -EINVAL;
			opts->ptmxmode = option & S_IALLUGO;
		opts->ptmxmode = result.uint_32 & 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;
		if (result.uint_32 > NR_UNIX98_PTY_MAX)
			return invalf(fc, "max out of range");
		opts->max = result.uint_32;
		break;
		default:
			pr_err("called with bogus options\n");
			return -EINVAL;
		}
	}

	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 get_tree_nodev(fc, devpts_fill_super);
}

static void devpts_free_fc(struct fs_context *fc)
{
	return mount_nodev(fs_type, flags, data, devpts_fill_super);
	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,
};
+74 −35
Original line number Diff line number Diff line
@@ -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)
{
	char		*p;
	substring_t	args[MAX_OPT_ARGS];
	int		option;

	if (!options)
		return;
struct pstore_context {
	unsigned int kmsg_bytes;
};

	while ((p = strsep(&options, ",")) != NULL) {
		int token;
static int pstore_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
	struct pstore_context *ctx = fc->fs_private;
	struct fs_parse_result result;
	int opt;

		if (!*p)
			continue;
	opt = fs_parse(fc, pstore_param_spec, param, &result);
	/* pstore has historically ignored invalid kmsg_bytes param */
	if (opt < 0)
		return 0;

		token = match_token(p, tokens, args);
		switch (token) {
	switch (opt) {
	case Opt_kmsg_bytes:
			if (!match_int(&args[0], &option))
				pstore_set_kmsg_bytes(option);
		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)
{
	if (fc->root)
		return pstore_reconfigure(fc);

	return get_tree_single(fc, pstore_fill_super);
}

static void pstore_free_fc(struct fs_context *fc)
{
	return mount_single(fs_type, flags, data, pstore_fill_super);
	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)
+0 −55
Original line number Diff line number Diff line
@@ -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.
+0 −3
Original line number Diff line number Diff line
@@ -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));
Loading