vfs: Convert debugfs to use the new mount API

Convert the debugfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
[sandeen: forward port to modern kernel, fix remounting]
Link: https://lore.kernel.org/r/49d1f108-46e3-443f-85a3-6dd730c5d076@redhat.com
cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
cc: Rafael J. Wysocki <rafael@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
David Howells 2024-03-05 17:08:39 -06:00 committed by Christian Brauner
parent 8f27829974
commit a20971c187
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -14,7 +14,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mount.h> #include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kobject.h> #include <linux/kobject.h>
@ -23,7 +24,6 @@
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/parser.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/security.h> #include <linux/security.h>
@ -77,7 +77,7 @@ static struct inode *debugfs_get_inode(struct super_block *sb)
return inode; return inode;
} }
struct debugfs_mount_opts { struct debugfs_fs_info {
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
umode_t mode; umode_t mode;
@ -89,68 +89,51 @@ enum {
Opt_uid, Opt_uid,
Opt_gid, Opt_gid,
Opt_mode, Opt_mode,
Opt_err
}; };
static const match_table_t tokens = { static const struct fs_parameter_spec debugfs_param_specs[] = {
{Opt_uid, "uid=%u"}, fsparam_u32 ("gid", Opt_gid),
{Opt_gid, "gid=%u"}, fsparam_u32oct ("mode", Opt_mode),
{Opt_mode, "mode=%o"}, fsparam_u32 ("uid", Opt_uid),
{Opt_err, NULL} {}
}; };
struct debugfs_fs_info { static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
struct debugfs_mount_opts mount_opts;
};
static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts)
{ {
substring_t args[MAX_OPT_ARGS]; struct debugfs_fs_info *opts = fc->s_fs_info;
int option; struct fs_parse_result result;
int token;
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
char *p; int opt;
opts->opts = 0; opt = fs_parse(fc, debugfs_param_specs, param, &result);
opts->mode = DEBUGFS_DEFAULT_MODE; if (opt < 0)
return opt;
while ((p = strsep(&data, ",")) != NULL) { switch (opt) {
if (!*p) case Opt_uid:
continue; uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid))
token = match_token(p, tokens, args); return invalf(fc, "Unknown uid");
switch (token) { opts->uid = uid;
case Opt_uid: break;
if (match_int(&args[0], &option)) case Opt_gid:
return -EINVAL; gid = make_kgid(current_user_ns(), result.uint_32);
uid = make_kuid(current_user_ns(), option); if (!gid_valid(gid))
if (!uid_valid(uid)) return invalf(fc, "Unknown gid");
return -EINVAL; opts->gid = gid;
opts->uid = uid; break;
break; case Opt_mode:
case Opt_gid: opts->mode = result.uint_32 & S_IALLUGO;
if (match_int(&args[0], &option)) break;
return -EINVAL; /*
gid = make_kgid(current_user_ns(), option); * We might like to report bad mount options here;
if (!gid_valid(gid)) * but traditionally debugfs has ignored all mount options
return -EINVAL; */
opts->gid = gid;
break;
case Opt_mode:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->mode = option & S_IALLUGO;
break;
/*
* We might like to report bad mount options here;
* but traditionally debugfs has ignored all mount options
*/
}
opts->opts |= BIT(token);
} }
opts->opts |= BIT(opt);
return 0; return 0;
} }
@ -158,23 +141,22 @@ static void _debugfs_apply_options(struct super_block *sb, bool remount)
{ {
struct debugfs_fs_info *fsi = sb->s_fs_info; struct debugfs_fs_info *fsi = sb->s_fs_info;
struct inode *inode = d_inode(sb->s_root); struct inode *inode = d_inode(sb->s_root);
struct debugfs_mount_opts *opts = &fsi->mount_opts;
/* /*
* On remount, only reset mode/uid/gid if they were provided as mount * On remount, only reset mode/uid/gid if they were provided as mount
* options. * options.
*/ */
if (!remount || opts->opts & BIT(Opt_mode)) { if (!remount || fsi->opts & BIT(Opt_mode)) {
inode->i_mode &= ~S_IALLUGO; inode->i_mode &= ~S_IALLUGO;
inode->i_mode |= opts->mode; inode->i_mode |= fsi->mode;
} }
if (!remount || opts->opts & BIT(Opt_uid)) if (!remount || fsi->opts & BIT(Opt_uid))
inode->i_uid = opts->uid; inode->i_uid = fsi->uid;
if (!remount || opts->opts & BIT(Opt_gid)) if (!remount || fsi->opts & BIT(Opt_gid))
inode->i_gid = opts->gid; inode->i_gid = fsi->gid;
} }
static void debugfs_apply_options(struct super_block *sb) static void debugfs_apply_options(struct super_block *sb)
@ -187,35 +169,33 @@ static void debugfs_apply_options_remount(struct super_block *sb)
_debugfs_apply_options(sb, true); _debugfs_apply_options(sb, true);
} }
static int debugfs_remount(struct super_block *sb, int *flags, char *data) static int debugfs_reconfigure(struct fs_context *fc)
{ {
int err; struct super_block *sb = fc->root->d_sb;
struct debugfs_fs_info *fsi = sb->s_fs_info; struct debugfs_fs_info *sb_opts = sb->s_fs_info;
struct debugfs_fs_info *new_opts = fc->s_fs_info;
sync_filesystem(sb); sync_filesystem(sb);
err = debugfs_parse_options(data, &fsi->mount_opts);
if (err)
goto fail;
/* structure copy of new mount options to sb */
*sb_opts = *new_opts;
debugfs_apply_options_remount(sb); debugfs_apply_options_remount(sb);
fail: return 0;
return err;
} }
static int debugfs_show_options(struct seq_file *m, struct dentry *root) static int debugfs_show_options(struct seq_file *m, struct dentry *root)
{ {
struct debugfs_fs_info *fsi = root->d_sb->s_fs_info; struct debugfs_fs_info *fsi = root->d_sb->s_fs_info;
struct debugfs_mount_opts *opts = &fsi->mount_opts;
if (!uid_eq(opts->uid, GLOBAL_ROOT_UID)) if (!uid_eq(fsi->uid, GLOBAL_ROOT_UID))
seq_printf(m, ",uid=%u", seq_printf(m, ",uid=%u",
from_kuid_munged(&init_user_ns, opts->uid)); from_kuid_munged(&init_user_ns, fsi->uid));
if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) if (!gid_eq(fsi->gid, GLOBAL_ROOT_GID))
seq_printf(m, ",gid=%u", seq_printf(m, ",gid=%u",
from_kgid_munged(&init_user_ns, opts->gid)); from_kgid_munged(&init_user_ns, fsi->gid));
if (opts->mode != DEBUGFS_DEFAULT_MODE) if (fsi->mode != DEBUGFS_DEFAULT_MODE)
seq_printf(m, ",mode=%o", opts->mode); seq_printf(m, ",mode=%o", fsi->mode);
return 0; return 0;
} }
@ -229,7 +209,6 @@ static void debugfs_free_inode(struct inode *inode)
static const struct super_operations debugfs_super_operations = { static const struct super_operations debugfs_super_operations = {
.statfs = simple_statfs, .statfs = simple_statfs,
.remount_fs = debugfs_remount,
.show_options = debugfs_show_options, .show_options = debugfs_show_options,
.free_inode = debugfs_free_inode, .free_inode = debugfs_free_inode,
}; };
@ -263,26 +242,14 @@ static const struct dentry_operations debugfs_dops = {
.d_automount = debugfs_automount, .d_automount = debugfs_automount,
}; };
static int debug_fill_super(struct super_block *sb, void *data, int silent) static int debugfs_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
static const struct tree_descr debug_files[] = {{""}}; static const struct tree_descr debug_files[] = {{""}};
struct debugfs_fs_info *fsi;
int err; int err;
fsi = kzalloc(sizeof(struct debugfs_fs_info), GFP_KERNEL); err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
sb->s_fs_info = fsi;
if (!fsi) {
err = -ENOMEM;
goto fail;
}
err = debugfs_parse_options(data, &fsi->mount_opts);
if (err) if (err)
goto fail; return err;
err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
if (err)
goto fail;
sb->s_op = &debugfs_super_operations; sb->s_op = &debugfs_super_operations;
sb->s_d_op = &debugfs_dops; sb->s_d_op = &debugfs_dops;
@ -290,27 +257,48 @@ static int debug_fill_super(struct super_block *sb, void *data, int silent)
debugfs_apply_options(sb); debugfs_apply_options(sb);
return 0; return 0;
fail:
kfree(fsi);
sb->s_fs_info = NULL;
return err;
} }
static struct dentry *debug_mount(struct file_system_type *fs_type, static int debugfs_get_tree(struct fs_context *fc)
int flags, const char *dev_name,
void *data)
{ {
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) if (!(debugfs_allow & DEBUGFS_ALLOW_API))
return ERR_PTR(-EPERM); return -EPERM;
return mount_single(fs_type, flags, data, debug_fill_super); return get_tree_single(fc, debugfs_fill_super);
}
static void debugfs_free_fc(struct fs_context *fc)
{
kfree(fc->s_fs_info);
}
static const struct fs_context_operations debugfs_context_ops = {
.free = debugfs_free_fc,
.parse_param = debugfs_parse_param,
.get_tree = debugfs_get_tree,
.reconfigure = debugfs_reconfigure,
};
static int debugfs_init_fs_context(struct fs_context *fc)
{
struct debugfs_fs_info *fsi;
fsi = kzalloc(sizeof(struct debugfs_fs_info), GFP_KERNEL);
if (!fsi)
return -ENOMEM;
fsi->mode = DEBUGFS_DEFAULT_MODE;
fc->s_fs_info = fsi;
fc->ops = &debugfs_context_ops;
return 0;
} }
static struct file_system_type debug_fs_type = { static struct file_system_type debug_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "debugfs", .name = "debugfs",
.mount = debug_mount, .init_fs_context = debugfs_init_fs_context,
.parameters = debugfs_param_specs,
.kill_sb = kill_litter_super, .kill_sb = kill_litter_super,
}; };
MODULE_ALIAS_FS("debugfs"); MODULE_ALIAS_FS("debugfs");