932 lines · 26.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  inode.c - part of debugfs, a tiny little debug file system * *  Copyright (C) 2004,2019 Greg Kroah-Hartman <greg@kroah.com> *  Copyright (C) 2004 IBM Inc. *  Copyright (C) 2019 Linux Foundation <gregkh@linuxfoundation.org> * *  debugfs is for people to use instead of /proc or /sys. *  See ./Documentation/core-api/kernel-api.rst for more details. */ #define pr_fmt(fmt)	"debugfs: " fmt #include <linux/module.h>#include <linux/fs.h>#include <linux/fs_context.h>#include <linux/fs_parser.h>#include <linux/pagemap.h>#include <linux/init.h>#include <linux/kobject.h>#include <linux/namei.h>#include <linux/debugfs.h>#include <linux/fsnotify.h>#include <linux/string.h>#include <linux/seq_file.h>#include <linux/magic.h>#include <linux/slab.h>#include <linux/security.h> #include "internal.h" #define DEBUGFS_DEFAULT_MODE	0700 static struct vfsmount *debugfs_mount;static int debugfs_mount_count;static bool debugfs_registered;static bool debugfs_enabled __ro_after_init = IS_ENABLED(CONFIG_DEBUG_FS_ALLOW_ALL); /* * Don't allow access attributes to be changed whilst the kernel is locked down * so that we can use the file mode as part of a heuristic to determine whether * to lock down individual files. */static int debugfs_setattr(struct mnt_idmap *idmap,			   struct dentry *dentry, struct iattr *ia){	int ret; 	if (ia->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) {		ret = security_locked_down(LOCKDOWN_DEBUGFS);		if (ret)			return ret;	}	return simple_setattr(&nop_mnt_idmap, dentry, ia);} static const struct inode_operations debugfs_file_inode_operations = {	.setattr	= debugfs_setattr,};static const struct inode_operations debugfs_dir_inode_operations = {	.lookup		= simple_lookup,	.setattr	= debugfs_setattr,};static const struct inode_operations debugfs_symlink_inode_operations = {	.get_link	= simple_get_link,	.setattr	= debugfs_setattr,}; static struct inode *debugfs_get_inode(struct super_block *sb){	struct inode *inode = new_inode(sb);	if (inode) {		inode->i_ino = get_next_ino();		simple_inode_init_ts(inode);	}	return inode;} struct debugfs_fs_info {	kuid_t uid;	kgid_t gid;	umode_t mode;	/* Opt_* bitfield. */	unsigned int opts;}; enum {	Opt_uid,	Opt_gid,	Opt_mode,	Opt_source,}; static const struct fs_parameter_spec debugfs_param_specs[] = {	fsparam_gid	("gid",		Opt_gid),	fsparam_u32oct	("mode",	Opt_mode),	fsparam_uid	("uid",		Opt_uid),	fsparam_string	("source",	Opt_source),	{}}; static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param){	struct debugfs_fs_info *opts = fc->s_fs_info;	struct fs_parse_result result;	int opt; 	opt = fs_parse(fc, debugfs_param_specs, param, &result);	if (opt < 0) {		/*                * We might like to report bad mount options here; but                * traditionally debugfs has ignored all mount options                */		if (opt == -ENOPARAM)			return 0; 		return opt;	} 	switch (opt) {	case Opt_uid:		opts->uid = result.uid;		break;	case Opt_gid:		opts->gid = result.gid;		break;	case Opt_mode:		opts->mode = result.uint_32 & S_IALLUGO;		break;	case Opt_source:		if (fc->source)			return invalfc(fc, "Multiple sources specified");		fc->source = param->string;		param->string = NULL;		break;	/*	 * We might like to report bad mount options here;	 * but traditionally debugfs has ignored all mount options	 */	} 	opts->opts |= BIT(opt); 	return 0;} static void _debugfs_apply_options(struct super_block *sb, bool remount){	struct debugfs_fs_info *fsi = sb->s_fs_info;	struct inode *inode = d_inode(sb->s_root); 	/*	 * On remount, only reset mode/uid/gid if they were provided as mount	 * options.	 */ 	if (!remount || fsi->opts & BIT(Opt_mode)) {		inode->i_mode &= ~S_IALLUGO;		inode->i_mode |= fsi->mode;	} 	if (!remount || fsi->opts & BIT(Opt_uid))		inode->i_uid = fsi->uid; 	if (!remount || fsi->opts & BIT(Opt_gid))		inode->i_gid = fsi->gid;} static void debugfs_apply_options(struct super_block *sb){	_debugfs_apply_options(sb, false);} static void debugfs_apply_options_remount(struct super_block *sb){	_debugfs_apply_options(sb, true);} static int debugfs_reconfigure(struct fs_context *fc){	struct super_block *sb = fc->root->d_sb;	struct debugfs_fs_info *sb_opts = sb->s_fs_info;	struct debugfs_fs_info *new_opts = fc->s_fs_info; 	if (!new_opts)		return 0; 	sync_filesystem(sb); 	/* structure copy of new mount options to sb */	*sb_opts = *new_opts;	debugfs_apply_options_remount(sb); 	return 0;} static int debugfs_show_options(struct seq_file *m, struct dentry *root){	struct debugfs_fs_info *fsi = root->d_sb->s_fs_info; 	if (!uid_eq(fsi->uid, GLOBAL_ROOT_UID))		seq_printf(m, ",uid=%u",			   from_kuid_munged(&init_user_ns, fsi->uid));	if (!gid_eq(fsi->gid, GLOBAL_ROOT_GID))		seq_printf(m, ",gid=%u",			   from_kgid_munged(&init_user_ns, fsi->gid));	if (fsi->mode != DEBUGFS_DEFAULT_MODE)		seq_printf(m, ",mode=%o", fsi->mode); 	return 0;} static struct kmem_cache *debugfs_inode_cachep __ro_after_init; static void init_once(void *foo){	struct debugfs_inode_info *info = foo;	inode_init_once(&info->vfs_inode);} static struct inode *debugfs_alloc_inode(struct super_block *sb){	struct debugfs_inode_info *info;	info = alloc_inode_sb(sb, debugfs_inode_cachep, GFP_KERNEL);	if (!info)		return NULL;	return &info->vfs_inode;} static void debugfs_free_inode(struct inode *inode){	if (S_ISLNK(inode->i_mode))		kfree(inode->i_link);	kmem_cache_free(debugfs_inode_cachep, DEBUGFS_I(inode));} static const struct super_operations debugfs_super_operations = {	.statfs		= simple_statfs,	.show_options	= debugfs_show_options,	.alloc_inode	= debugfs_alloc_inode,	.free_inode	= debugfs_free_inode,}; static void debugfs_release_dentry(struct dentry *dentry){	struct debugfs_fsdata *fsd = dentry->d_fsdata; 	if (fsd) {		WARN_ON(!list_empty(&fsd->cancellations));		mutex_destroy(&fsd->cancellations_mtx);	}	kfree(fsd);} static struct vfsmount *debugfs_automount(struct path *path){	struct inode *inode = path->dentry->d_inode; 	return DEBUGFS_I(inode)->automount(path->dentry, inode->i_private);} static const struct dentry_operations debugfs_dops = {	.d_release = debugfs_release_dentry,	.d_automount = debugfs_automount,}; static int debugfs_fill_super(struct super_block *sb, struct fs_context *fc){	static const struct tree_descr debug_files[] = {{""}};	int err; 	err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);	if (err)		return err; 	sb->s_op = &debugfs_super_operations;	set_default_d_op(sb, &debugfs_dops);	sb->s_d_flags |= DCACHE_DONTCACHE; 	debugfs_apply_options(sb); 	return 0;} static int debugfs_get_tree(struct fs_context *fc){	int err; 	err = get_tree_single(fc, debugfs_fill_super);	if (err)		return err; 	return debugfs_reconfigure(fc);} 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_obj(struct debugfs_fs_info);	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 = {	.owner =	THIS_MODULE,	.name =		"debugfs",	.init_fs_context = debugfs_init_fs_context,	.parameters =	debugfs_param_specs,	.kill_sb =	kill_anon_super,};MODULE_ALIAS_FS("debugfs"); /** * debugfs_lookup() - look up an existing debugfs file * @name: a pointer to a string containing the name of the file to look up. * @parent: a pointer to the parent dentry of the file. * * This function will return a pointer to a dentry if it succeeds.  If the file * doesn't exist or an error occurs, %NULL will be returned.  The returned * dentry must be passed to dput() when it is no longer needed. * * If debugfs is not enabled in the kernel, the value -%ENODEV will be * returned. */struct dentry *debugfs_lookup(const char *name, struct dentry *parent){	struct dentry *dentry; 	if (!debugfs_initialized() || IS_ERR_OR_NULL(name) || IS_ERR(parent))		return NULL; 	if (!parent)		parent = debugfs_mount->mnt_root; 	dentry = lookup_noperm_positive_unlocked(&QSTR(name), parent);	if (IS_ERR(dentry))		return NULL;	return dentry;}EXPORT_SYMBOL_GPL(debugfs_lookup); static struct dentry *debugfs_start_creating(const char *name,					     struct dentry *parent){	struct dentry *dentry;	int error; 	if (!debugfs_enabled)		return ERR_PTR(-EPERM); 	if (!debugfs_initialized()) {		pr_err("Unable to create file '%s', debugfs is not initialized yet\n",		       name);		return ERR_PTR(-ENOENT);	} 	pr_debug("creating file '%s'\n", name); 	if (IS_ERR(parent))		return parent; 	error = simple_pin_fs(&debug_fs_type, &debugfs_mount,			      &debugfs_mount_count);	if (error) {		pr_err("Unable to pin filesystem for file '%s'\n", name);		return ERR_PTR(error);	} 	/* If the parent is not specified, we create it in the root.	 * We need the root dentry to do this, which is in the super	 * block. A pointer to that is in the struct vfsmount that we	 * have around.	 */	if (!parent)		parent = debugfs_mount->mnt_root; 	dentry = simple_start_creating(parent, name);	if (IS_ERR(dentry)) {		if (dentry == ERR_PTR(-EEXIST))			pr_err("'%s' already exists in '%pd'\n", name, parent);		simple_release_fs(&debugfs_mount, &debugfs_mount_count);	}	return dentry;} static struct dentry *debugfs_failed_creating(struct dentry *dentry){	simple_done_creating(dentry);	simple_release_fs(&debugfs_mount, &debugfs_mount_count);	return ERR_PTR(-ENOMEM);} static struct dentry *debugfs_end_creating(struct dentry *dentry){	simple_done_creating(dentry);	return dentry; // borrowed} static struct dentry *__debugfs_create_file(const char *name, umode_t mode,				struct dentry *parent, void *data,				const void *aux,				const struct file_operations *proxy_fops,				const void *real_fops){	struct dentry *dentry;	struct inode *inode; 	if (!(mode & S_IFMT))		mode |= S_IFREG;	BUG_ON(!S_ISREG(mode));	dentry = debugfs_start_creating(name, parent); 	if (IS_ERR(dentry))		return dentry; 	inode = debugfs_get_inode(dentry->d_sb);	if (unlikely(!inode)) {		pr_err("out of free dentries, can not create file '%s'\n",		       name);		return debugfs_failed_creating(dentry);	} 	inode->i_mode = mode;	inode->i_private = data; 	inode->i_op = &debugfs_file_inode_operations;	if (!real_fops)		proxy_fops = &debugfs_noop_file_operations;	inode->i_fop = proxy_fops;	DEBUGFS_I(inode)->raw = real_fops;	DEBUGFS_I(inode)->aux = (void *)aux; 	d_make_persistent(dentry, inode);	fsnotify_create(d_inode(dentry->d_parent), dentry);	return debugfs_end_creating(dentry);} struct dentry *debugfs_create_file_full(const char *name, umode_t mode,					struct dentry *parent, void *data,					const void *aux,					const struct file_operations *fops){	return __debugfs_create_file(name, mode, parent, data, aux,				&debugfs_full_proxy_file_operations,				fops);}EXPORT_SYMBOL_GPL(debugfs_create_file_full); struct dentry *debugfs_create_file_short(const char *name, umode_t mode,					struct dentry *parent, void *data,					const void *aux,					const struct debugfs_short_fops *fops){	return __debugfs_create_file(name, mode, parent, data, aux,				&debugfs_full_short_proxy_file_operations,				fops);}EXPORT_SYMBOL_GPL(debugfs_create_file_short); /** * debugfs_create_file_unsafe - create a file in the debugfs filesystem * @name: a pointer to a string containing the name of the file to create. * @mode: the permission that the file should have. * @parent: a pointer to the parent dentry for this file.  This should be a *          directory dentry if set.  If this parameter is NULL, then the *          file will be created in the root of the debugfs filesystem. * @data: a pointer to something that the caller will want to get to later *        on.  The inode.i_private pointer will point to this value on *        the open() call. * @fops: a pointer to a struct file_operations that should be used for *        this file. * * debugfs_create_file_unsafe() is completely analogous to * debugfs_create_file(), the only difference being that the fops * handed it will not get protected against file removals by the * debugfs core. * * It is your responsibility to protect your struct file_operation * methods against file removals by means of debugfs_file_get() * and debugfs_file_put(). ->open() is still protected by

Lines 1–500 of 932.