| // 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 | |
| * debugfs though. | |
| * | |
| * Any struct file_operations defined by means of | |
| * DEFINE_DEBUGFS_ATTRIBUTE() is protected against file removals and | |
| * thus, may be used here. | |
| */ | |
| struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, | |
| struct dentry *parent, void *data, | |
| const struct file_operations *fops) | |
| { | |
| return __debugfs_create_file(name, mode, parent, data, NULL, | |
| &debugfs_open_proxy_file_operations, | |
| fops); | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe); | |
| /** | |
| * debugfs_create_file_size - 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. | |
| * @file_size: initial file size | |
| * | |
| * This is the basic "create a file" function for debugfs. It allows for a | |
| * wide range of flexibility in creating a file, or a directory (if you want | |
| * to create a directory, the debugfs_create_dir() function is | |
| * recommended to be used instead.) | |
| */ | |
| void debugfs_create_file_size(const char *name, umode_t mode, | |
| struct dentry *parent, void *data, | |
| const struct file_operations *fops, | |
| loff_t file_size) | |
| { | |
| struct dentry *de = debugfs_create_file(name, mode, parent, data, fops); | |
| if (!IS_ERR(de)) | |
| d_inode(de)->i_size = file_size; | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_create_file_size); | |
| /** | |
| * debugfs_create_dir - create a directory in the debugfs filesystem | |
| * @name: a pointer to a string containing the name of the directory to | |
| * create. | |
| * @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 | |
| * directory will be created in the root of the debugfs filesystem. | |
| * | |
| * This function creates a directory in debugfs with the given name. | |
| * | |
| * This function will return a pointer to a dentry if it succeeds. This | |
| * pointer must be passed to the debugfs_remove() function when the file is | |
| * to be removed (no automatic cleanup happens if your module is unloaded, | |
| * you are responsible here.) If an error occurs, ERR_PTR(-ERROR) will be | |
| * returned. | |
| * | |
| * If debugfs is not enabled in the kernel, the value -%ENODEV will be | |
| * returned. | |
| * | |
| * NOTE: it's expected that most callers should _ignore_ the errors returned | |
| * by this function. Other debugfs functions handle the fact that the "dentry" | |
| * passed to them could be an error and they don't crash in that case. | |
| * Drivers should generally work fine even if debugfs fails to init anyway. | |
| */ | |
| struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) | |
| { | |
| struct dentry *dentry = debugfs_start_creating(name, parent); | |
| struct inode *inode; | |
| 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 directory '%s'\n", | |
| name); | |
| return debugfs_failed_creating(dentry); | |
| } | |
| inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; | |
| inode->i_op = &debugfs_dir_inode_operations; | |
| inode->i_fop = &simple_dir_operations; | |
| /* directory inodes start off with i_nlink == 2 (for "." entry) */ | |
| inc_nlink(inode); | |
| d_make_persistent(dentry, inode); | |
| inc_nlink(d_inode(dentry->d_parent)); | |
| fsnotify_mkdir(d_inode(dentry->d_parent), dentry); | |
| return debugfs_end_creating(dentry); | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_create_dir); | |
| /** | |
| * debugfs_create_automount - create automount point in the debugfs filesystem | |
| * @name: a pointer to a string containing the name of the file to create. | |
| * @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. | |
| * @f: function to be called when pathname resolution steps on that one. | |
| * @data: opaque argument to pass to f(). | |
| * | |
| * @f should return what ->d_automount() would. | |
| */ | |
| struct dentry *debugfs_create_automount(const char *name, | |
| struct dentry *parent, | |
| debugfs_automount_t f, | |
| void *data) | |
| { | |
| struct dentry *dentry = debugfs_start_creating(name, parent); | |
| struct inode *inode; | |
| 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 automount '%s'\n", | |
| name); | |
| return debugfs_failed_creating(dentry); | |
| } | |
| make_empty_dir_inode(inode); | |
| inode->i_flags |= S_AUTOMOUNT; | |
| inode->i_private = data; | |
| DEBUGFS_I(inode)->automount = f; | |
| /* directory inodes start off with i_nlink == 2 (for "." entry) */ | |
| inc_nlink(inode); | |
| d_make_persistent(dentry, inode); | |
| inc_nlink(d_inode(dentry->d_parent)); | |
| fsnotify_mkdir(d_inode(dentry->d_parent), dentry); | |
| return debugfs_end_creating(dentry); | |
| } | |
| EXPORT_SYMBOL(debugfs_create_automount); | |
| /** | |
| * debugfs_create_symlink- create a symbolic link in the debugfs filesystem | |
| * @name: a pointer to a string containing the name of the symbolic link to | |
| * create. | |
| * @parent: a pointer to the parent dentry for this symbolic link. This | |
| * should be a directory dentry if set. If this parameter is NULL, | |
| * then the symbolic link will be created in the root of the debugfs | |
| * filesystem. | |
| * @target: a pointer to a string containing the path to the target of the | |
| * symbolic link. | |
| * | |
| * This function creates a symbolic link with the given name in debugfs that | |
| * links to the given target path. | |
| * | |
| * This function will return a pointer to a dentry if it succeeds. This | |
| * pointer must be passed to the debugfs_remove() function when the symbolic | |
| * link is to be removed (no automatic cleanup happens if your module is | |
| * unloaded, you are responsible here.) If an error occurs, ERR_PTR(-ERROR) | |
| * will be returned. | |
| * | |
| * If debugfs is not enabled in the kernel, the value -%ENODEV will be | |
| * returned. | |
| */ | |
| struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, | |
| const char *target) | |
| { | |
| struct dentry *dentry; | |
| struct inode *inode; | |
| char *link = kstrdup(target, GFP_KERNEL); | |
| if (!link) | |
| return ERR_PTR(-ENOMEM); | |
| dentry = debugfs_start_creating(name, parent); | |
| if (IS_ERR(dentry)) { | |
| kfree(link); | |
| return dentry; | |
| } | |
| inode = debugfs_get_inode(dentry->d_sb); | |
| if (unlikely(!inode)) { | |
| pr_err("out of free dentries, can not create symlink '%s'\n", | |
| name); | |
| kfree(link); | |
| return debugfs_failed_creating(dentry); | |
| } | |
| inode->i_mode = S_IFLNK | S_IRWXUGO; | |
| inode->i_op = &debugfs_symlink_inode_operations; | |
| inode->i_link = link; | |
| d_make_persistent(dentry, inode); | |
| return debugfs_end_creating(dentry); | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_create_symlink); | |
| static void __debugfs_file_removed(struct dentry *dentry) | |
| { | |
| struct debugfs_fsdata *fsd; | |
| /* | |
| * Paired with the closing smp_mb() implied by a successful | |
| * cmpxchg() in debugfs_file_get(): either | |
| * debugfs_file_get() must see a dead dentry or we must see a | |
| * debugfs_fsdata instance at ->d_fsdata here (or both). | |
| */ | |
| smp_mb(); | |
| fsd = READ_ONCE(dentry->d_fsdata); | |
| if (!fsd) | |
| return; | |
| /* if this was the last reference, we're done */ | |
| if (refcount_dec_and_test(&fsd->active_users)) | |
| return; | |
| /* | |
| * If there's still a reference, the code that obtained it can | |
| * be in different states: | |
| * - The common case of not using cancellations, or already | |
| * after debugfs_leave_cancellation(), where we just need | |
| * to wait for debugfs_file_put() which signals the completion; | |
| * - inside a cancellation section, i.e. between | |
| * debugfs_enter_cancellation() and debugfs_leave_cancellation(), | |
| * in which case we need to trigger the ->cancel() function, | |
| * and then wait for debugfs_file_put() just like in the | |
| * previous case; | |
| * - before debugfs_enter_cancellation() (but obviously after | |
| * debugfs_file_get()), in which case we may not see the | |
| * cancellation in the list on the first round of the loop, | |
| * but debugfs_enter_cancellation() signals the completion | |
| * after adding it, so this code gets woken up to call the | |
| * ->cancel() function. | |
| */ | |
| while (refcount_read(&fsd->active_users)) { | |
| struct debugfs_cancellation *c; | |
| /* | |
| * Lock the cancellations. Note that the cancellations | |
| * structs are meant to be on the stack, so we need to | |
| * ensure we either use them here or don't touch them, | |
| * and debugfs_leave_cancellation() will wait for this | |
| * to be finished processing before exiting one. It may | |
| * of course win and remove the cancellation, but then | |
| * chances are we never even got into this bit, we only | |
| * do if the refcount isn't zero already. | |
| */ | |
| mutex_lock(&fsd->cancellations_mtx); | |
| while ((c = list_first_entry_or_null(&fsd->cancellations, | |
| typeof(*c), list))) { | |
| list_del_init(&c->list); | |
| c->cancel(dentry, c->cancel_data); | |
| } | |
| mutex_unlock(&fsd->cancellations_mtx); | |
| wait_for_completion(&fsd->active_users_drained); | |
| } | |
| } | |
| static void remove_one(struct dentry *victim) | |
| { | |
| if (d_is_reg(victim)) | |
| __debugfs_file_removed(victim); | |
| simple_release_fs(&debugfs_mount, &debugfs_mount_count); | |
| } | |
| /** | |
| * debugfs_remove - recursively removes a directory | |
| * @dentry: a pointer to a the dentry of the directory to be removed. If this | |
| * parameter is NULL or an error value, nothing will be done. | |
| * | |
| * This function recursively removes a directory tree in debugfs that | |
| * was previously created with a call to another debugfs function | |
| * (like debugfs_create_file() or variants thereof.) | |
| * | |
| * This function is required to be called in order for the file to be | |
| * removed, no automatic cleanup of files will happen when a module is | |
| * removed, you are responsible here. | |
| */ | |
| void debugfs_remove(struct dentry *dentry) | |
| { | |
| if (IS_ERR_OR_NULL(dentry)) | |
| return; | |
| simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count); | |
| simple_recursive_removal(dentry, remove_one); | |
| simple_release_fs(&debugfs_mount, &debugfs_mount_count); | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_remove); | |
| /** | |
| * debugfs_lookup_and_remove - lookup a directory or file and recursively remove it | |
| * @name: a pointer to a string containing the name of the item to look up. | |
| * @parent: a pointer to the parent dentry of the item. | |
| * | |
| * This is the equlivant of doing something like | |
| * debugfs_remove(debugfs_lookup(..)) but with the proper reference counting | |
| * handled for the directory being looked up. | |
| */ | |
| void debugfs_lookup_and_remove(const char *name, struct dentry *parent) | |
| { | |
| struct dentry *dentry; | |
| dentry = debugfs_lookup(name, parent); | |
| if (!dentry) | |
| return; | |
| debugfs_remove(dentry); | |
| dput(dentry); | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_lookup_and_remove); | |
| /** | |
| * debugfs_change_name - rename a file/directory in the debugfs filesystem | |
| * @dentry: dentry of an object to be renamed. | |
| * @fmt: format for new name | |
| * | |
| * This function renames a file/directory in debugfs. The target must not | |
| * exist for rename to succeed. | |
| * | |
| * This function will return 0 on success and -E... on failure. | |
| * | |
| * If debugfs is not enabled in the kernel, the value -%ENODEV will be | |
| * returned. | |
| */ | |
| int __printf(2, 3) debugfs_change_name(struct dentry *dentry, const char *fmt, ...) | |
| { | |
| int error = 0; | |
| const char *new_name; | |
| struct name_snapshot old_name; | |
| struct dentry *target; | |
| struct renamedata rd = {}; | |
| struct inode *dir; | |
| va_list ap; | |
| if (IS_ERR_OR_NULL(dentry)) | |
| return 0; | |
| va_start(ap, fmt); | |
| new_name = kvasprintf_const(GFP_KERNEL, fmt, ap); | |
| va_end(ap); | |
| if (!new_name) | |
| return -ENOMEM; | |
| rd.old_parent = dget_parent(dentry); | |
| rd.new_parent = rd.old_parent; | |
| rd.flags = RENAME_NOREPLACE; | |
| target = lookup_noperm_unlocked(&QSTR(new_name), rd.new_parent); | |
| if (IS_ERR(target)) { | |
| error = PTR_ERR(target); | |
| goto out_free; | |
| } | |
| error = start_renaming_two_dentries(&rd, dentry, target); | |
| if (error) { | |
| if (error == -EEXIST && target == dentry) | |
| /* it isn't an error to rename a thing to itself */ | |
| error = 0; | |
| goto out; | |
| } | |
| dir = d_inode(rd.old_parent); | |
| take_dentry_name_snapshot(&old_name, dentry); | |
| simple_rename_timestamp(dir, dentry, dir, rd.new_dentry); | |
| d_move(dentry, rd.new_dentry); | |
| fsnotify_move(dir, dir, &old_name.name, d_is_dir(dentry), NULL, dentry); | |
| release_dentry_name_snapshot(&old_name); | |
| end_renaming(&rd); | |
| out: | |
| dput(rd.old_parent); | |
| dput(target); | |
| out_free: | |
| kfree_const(new_name); | |
| return error; | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_change_name); | |
| /** | |
| * debugfs_initialized - Tells whether debugfs has been registered | |
| */ | |
| bool debugfs_initialized(void) | |
| { | |
| return debugfs_registered; | |
| } | |
| EXPORT_SYMBOL_GPL(debugfs_initialized); | |
| static int __init debugfs_kernel(char *str) | |
| { | |
| if (str) { | |
| if (!strcmp(str, "on")) | |
| debugfs_enabled = true; | |
| else if (!strcmp(str, "off")) | |
| debugfs_enabled = false; | |
| else if (!strcmp(str, "no-mount")) { | |
| pr_notice("debugfs=no-mount is a deprecated alias " | |
| "for debugfs=off\n"); | |
| debugfs_enabled = false; | |
| } | |
| } | |
| return 0; | |
| } | |
| early_param("debugfs", debugfs_kernel); | |
| static int __init debugfs_init(void) | |
| { | |
| int retval; | |
| if (!debugfs_enabled) | |
| return -EPERM; | |
| retval = sysfs_create_mount_point(kernel_kobj, "debug"); | |
| if (retval) | |
| return retval; | |
| debugfs_inode_cachep = kmem_cache_create("debugfs_inode_cache", | |
| sizeof(struct debugfs_inode_info), 0, | |
| SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT, | |
| init_once); | |
| if (debugfs_inode_cachep == NULL) { | |
| sysfs_remove_mount_point(kernel_kobj, "debug"); | |
| return -ENOMEM; | |
| } | |
| retval = register_filesystem(&debug_fs_type); | |
| if (retval) { // Really not going to happen | |
| sysfs_remove_mount_point(kernel_kobj, "debug"); | |
| kmem_cache_destroy(debugfs_inode_cachep); | |
| return retval; | |
| } | |
| debugfs_registered = true; | |
| return 0; | |
| } | |
| core_initcall(debugfs_init); |