162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * inode.c - part of tracefs, a pseudo file system for activating tracing 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on debugfs by: Greg Kroah-Hartman <greg@kroah.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * tracefs is the file system that is used by the tracing infrastructure. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/mount.h> 1562306a36Sopenharmony_ci#include <linux/kobject.h> 1662306a36Sopenharmony_ci#include <linux/namei.h> 1762306a36Sopenharmony_ci#include <linux/tracefs.h> 1862306a36Sopenharmony_ci#include <linux/fsnotify.h> 1962306a36Sopenharmony_ci#include <linux/security.h> 2062306a36Sopenharmony_ci#include <linux/seq_file.h> 2162306a36Sopenharmony_ci#include <linux/parser.h> 2262306a36Sopenharmony_ci#include <linux/magic.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include "internal.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define TRACEFS_DEFAULT_MODE 0700 2762306a36Sopenharmony_cistatic struct kmem_cache *tracefs_inode_cachep __ro_after_init; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct vfsmount *tracefs_mount; 3062306a36Sopenharmony_cistatic int tracefs_mount_count; 3162306a36Sopenharmony_cistatic bool tracefs_registered; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct inode *tracefs_alloc_inode(struct super_block *sb) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct tracefs_inode *ti; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ti = kmem_cache_alloc(tracefs_inode_cachep, GFP_KERNEL); 3862306a36Sopenharmony_ci if (!ti) 3962306a36Sopenharmony_ci return NULL; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return &ti->vfs_inode; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void tracefs_free_inode(struct inode *inode) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci kmem_cache_free(tracefs_inode_cachep, get_tracefs(inode)); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic ssize_t default_read_file(struct file *file, char __user *buf, 5062306a36Sopenharmony_ci size_t count, loff_t *ppos) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic ssize_t default_write_file(struct file *file, const char __user *buf, 5662306a36Sopenharmony_ci size_t count, loff_t *ppos) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return count; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct file_operations tracefs_file_operations = { 6262306a36Sopenharmony_ci .read = default_read_file, 6362306a36Sopenharmony_ci .write = default_write_file, 6462306a36Sopenharmony_ci .open = simple_open, 6562306a36Sopenharmony_ci .llseek = noop_llseek, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct tracefs_dir_ops { 6962306a36Sopenharmony_ci int (*mkdir)(const char *name); 7062306a36Sopenharmony_ci int (*rmdir)(const char *name); 7162306a36Sopenharmony_ci} tracefs_ops __ro_after_init; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic char *get_dname(struct dentry *dentry) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci const char *dname; 7662306a36Sopenharmony_ci char *name; 7762306a36Sopenharmony_ci int len = dentry->d_name.len; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci dname = dentry->d_name.name; 8062306a36Sopenharmony_ci name = kmalloc(len + 1, GFP_KERNEL); 8162306a36Sopenharmony_ci if (!name) 8262306a36Sopenharmony_ci return NULL; 8362306a36Sopenharmony_ci memcpy(name, dname, len); 8462306a36Sopenharmony_ci name[len] = 0; 8562306a36Sopenharmony_ci return name; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int tracefs_syscall_mkdir(struct mnt_idmap *idmap, 8962306a36Sopenharmony_ci struct inode *inode, struct dentry *dentry, 9062306a36Sopenharmony_ci umode_t mode) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct tracefs_inode *ti; 9362306a36Sopenharmony_ci char *name; 9462306a36Sopenharmony_ci int ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci name = get_dname(dentry); 9762306a36Sopenharmony_ci if (!name) 9862306a36Sopenharmony_ci return -ENOMEM; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * This is a new directory that does not take the default of 10262306a36Sopenharmony_ci * the rootfs. It becomes the default permissions for all the 10362306a36Sopenharmony_ci * files and directories underneath it. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci ti = get_tracefs(inode); 10662306a36Sopenharmony_ci ti->flags |= TRACEFS_INSTANCE_INODE; 10762306a36Sopenharmony_ci ti->private = inode; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * The mkdir call can call the generic functions that create 11162306a36Sopenharmony_ci * the files within the tracefs system. It is up to the individual 11262306a36Sopenharmony_ci * mkdir routine to handle races. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci inode_unlock(inode); 11562306a36Sopenharmony_ci ret = tracefs_ops.mkdir(name); 11662306a36Sopenharmony_ci inode_lock(inode); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci kfree(name); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci char *name; 12662306a36Sopenharmony_ci int ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci name = get_dname(dentry); 12962306a36Sopenharmony_ci if (!name) 13062306a36Sopenharmony_ci return -ENOMEM; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * The rmdir call can call the generic functions that create 13462306a36Sopenharmony_ci * the files within the tracefs system. It is up to the individual 13562306a36Sopenharmony_ci * rmdir routine to handle races. 13662306a36Sopenharmony_ci * This time we need to unlock not only the parent (inode) but 13762306a36Sopenharmony_ci * also the directory that is being deleted. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci inode_unlock(inode); 14062306a36Sopenharmony_ci inode_unlock(d_inode(dentry)); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ret = tracefs_ops.rmdir(name); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci inode_lock_nested(inode, I_MUTEX_PARENT); 14562306a36Sopenharmony_ci inode_lock(d_inode(dentry)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci kfree(name); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void set_tracefs_inode_owner(struct inode *inode) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct tracefs_inode *ti = get_tracefs(inode); 15562306a36Sopenharmony_ci struct inode *root_inode = ti->private; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * If this inode has never been referenced, then update 15962306a36Sopenharmony_ci * the permissions to the superblock. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci if (!(ti->flags & TRACEFS_UID_PERM_SET)) 16262306a36Sopenharmony_ci inode->i_uid = root_inode->i_uid; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!(ti->flags & TRACEFS_GID_PERM_SET)) 16562306a36Sopenharmony_ci inode->i_gid = root_inode->i_gid; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int tracefs_permission(struct mnt_idmap *idmap, 16962306a36Sopenharmony_ci struct inode *inode, int mask) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci set_tracefs_inode_owner(inode); 17262306a36Sopenharmony_ci return generic_permission(idmap, inode, mask); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int tracefs_getattr(struct mnt_idmap *idmap, 17662306a36Sopenharmony_ci const struct path *path, struct kstat *stat, 17762306a36Sopenharmony_ci u32 request_mask, unsigned int flags) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct inode *inode = d_backing_inode(path->dentry); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci set_tracefs_inode_owner(inode); 18262306a36Sopenharmony_ci generic_fillattr(idmap, request_mask, inode, stat); 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int tracefs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, 18762306a36Sopenharmony_ci struct iattr *attr) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci unsigned int ia_valid = attr->ia_valid; 19062306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 19162306a36Sopenharmony_ci struct tracefs_inode *ti = get_tracefs(inode); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (ia_valid & ATTR_UID) 19462306a36Sopenharmony_ci ti->flags |= TRACEFS_UID_PERM_SET; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (ia_valid & ATTR_GID) 19762306a36Sopenharmony_ci ti->flags |= TRACEFS_GID_PERM_SET; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return simple_setattr(idmap, dentry, attr); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const struct inode_operations tracefs_instance_dir_inode_operations = { 20362306a36Sopenharmony_ci .lookup = simple_lookup, 20462306a36Sopenharmony_ci .mkdir = tracefs_syscall_mkdir, 20562306a36Sopenharmony_ci .rmdir = tracefs_syscall_rmdir, 20662306a36Sopenharmony_ci .permission = tracefs_permission, 20762306a36Sopenharmony_ci .getattr = tracefs_getattr, 20862306a36Sopenharmony_ci .setattr = tracefs_setattr, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const struct inode_operations tracefs_dir_inode_operations = { 21262306a36Sopenharmony_ci .lookup = simple_lookup, 21362306a36Sopenharmony_ci .permission = tracefs_permission, 21462306a36Sopenharmony_ci .getattr = tracefs_getattr, 21562306a36Sopenharmony_ci .setattr = tracefs_setattr, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic const struct inode_operations tracefs_file_inode_operations = { 21962306a36Sopenharmony_ci .permission = tracefs_permission, 22062306a36Sopenharmony_ci .getattr = tracefs_getattr, 22162306a36Sopenharmony_ci .setattr = tracefs_setattr, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistruct inode *tracefs_get_inode(struct super_block *sb) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct inode *inode = new_inode(sb); 22762306a36Sopenharmony_ci if (inode) { 22862306a36Sopenharmony_ci inode->i_ino = get_next_ino(); 22962306a36Sopenharmony_ci inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci return inode; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistruct tracefs_mount_opts { 23562306a36Sopenharmony_ci kuid_t uid; 23662306a36Sopenharmony_ci kgid_t gid; 23762306a36Sopenharmony_ci umode_t mode; 23862306a36Sopenharmony_ci /* Opt_* bitfield. */ 23962306a36Sopenharmony_ci unsigned int opts; 24062306a36Sopenharmony_ci}; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cienum { 24362306a36Sopenharmony_ci Opt_uid, 24462306a36Sopenharmony_ci Opt_gid, 24562306a36Sopenharmony_ci Opt_mode, 24662306a36Sopenharmony_ci Opt_err 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic const match_table_t tokens = { 25062306a36Sopenharmony_ci {Opt_uid, "uid=%u"}, 25162306a36Sopenharmony_ci {Opt_gid, "gid=%u"}, 25262306a36Sopenharmony_ci {Opt_mode, "mode=%o"}, 25362306a36Sopenharmony_ci {Opt_err, NULL} 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistruct tracefs_fs_info { 25762306a36Sopenharmony_ci struct tracefs_mount_opts mount_opts; 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 26362306a36Sopenharmony_ci int option; 26462306a36Sopenharmony_ci int token; 26562306a36Sopenharmony_ci kuid_t uid; 26662306a36Sopenharmony_ci kgid_t gid; 26762306a36Sopenharmony_ci char *p; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci opts->opts = 0; 27062306a36Sopenharmony_ci opts->mode = TRACEFS_DEFAULT_MODE; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci while ((p = strsep(&data, ",")) != NULL) { 27362306a36Sopenharmony_ci if (!*p) 27462306a36Sopenharmony_ci continue; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci token = match_token(p, tokens, args); 27762306a36Sopenharmony_ci switch (token) { 27862306a36Sopenharmony_ci case Opt_uid: 27962306a36Sopenharmony_ci if (match_int(&args[0], &option)) 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci uid = make_kuid(current_user_ns(), option); 28262306a36Sopenharmony_ci if (!uid_valid(uid)) 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci opts->uid = uid; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case Opt_gid: 28762306a36Sopenharmony_ci if (match_int(&args[0], &option)) 28862306a36Sopenharmony_ci return -EINVAL; 28962306a36Sopenharmony_ci gid = make_kgid(current_user_ns(), option); 29062306a36Sopenharmony_ci if (!gid_valid(gid)) 29162306a36Sopenharmony_ci return -EINVAL; 29262306a36Sopenharmony_ci opts->gid = gid; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci case Opt_mode: 29562306a36Sopenharmony_ci if (match_octal(&args[0], &option)) 29662306a36Sopenharmony_ci return -EINVAL; 29762306a36Sopenharmony_ci opts->mode = option & S_IALLUGO; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * We might like to report bad mount options here; 30162306a36Sopenharmony_ci * but traditionally tracefs has ignored all mount options 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci opts->opts |= BIT(token); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int tracefs_apply_options(struct super_block *sb, bool remount) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct tracefs_fs_info *fsi = sb->s_fs_info; 31462306a36Sopenharmony_ci struct inode *inode = d_inode(sb->s_root); 31562306a36Sopenharmony_ci struct tracefs_mount_opts *opts = &fsi->mount_opts; 31662306a36Sopenharmony_ci umode_t tmp_mode; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * On remount, only reset mode/uid/gid if they were provided as mount 32062306a36Sopenharmony_ci * options. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!remount || opts->opts & BIT(Opt_mode)) { 32462306a36Sopenharmony_ci tmp_mode = READ_ONCE(inode->i_mode) & ~S_IALLUGO; 32562306a36Sopenharmony_ci tmp_mode |= opts->mode; 32662306a36Sopenharmony_ci WRITE_ONCE(inode->i_mode, tmp_mode); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (!remount || opts->opts & BIT(Opt_uid)) 33062306a36Sopenharmony_ci inode->i_uid = opts->uid; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!remount || opts->opts & BIT(Opt_gid)) 33362306a36Sopenharmony_ci inode->i_gid = opts->gid; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int tracefs_remount(struct super_block *sb, int *flags, char *data) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci int err; 34162306a36Sopenharmony_ci struct tracefs_fs_info *fsi = sb->s_fs_info; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci sync_filesystem(sb); 34462306a36Sopenharmony_ci err = tracefs_parse_options(data, &fsi->mount_opts); 34562306a36Sopenharmony_ci if (err) 34662306a36Sopenharmony_ci goto fail; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci tracefs_apply_options(sb, true); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cifail: 35162306a36Sopenharmony_ci return err; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int tracefs_show_options(struct seq_file *m, struct dentry *root) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct tracefs_fs_info *fsi = root->d_sb->s_fs_info; 35762306a36Sopenharmony_ci struct tracefs_mount_opts *opts = &fsi->mount_opts; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!uid_eq(opts->uid, GLOBAL_ROOT_UID)) 36062306a36Sopenharmony_ci seq_printf(m, ",uid=%u", 36162306a36Sopenharmony_ci from_kuid_munged(&init_user_ns, opts->uid)); 36262306a36Sopenharmony_ci if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) 36362306a36Sopenharmony_ci seq_printf(m, ",gid=%u", 36462306a36Sopenharmony_ci from_kgid_munged(&init_user_ns, opts->gid)); 36562306a36Sopenharmony_ci if (opts->mode != TRACEFS_DEFAULT_MODE) 36662306a36Sopenharmony_ci seq_printf(m, ",mode=%o", opts->mode); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic const struct super_operations tracefs_super_operations = { 37262306a36Sopenharmony_ci .alloc_inode = tracefs_alloc_inode, 37362306a36Sopenharmony_ci .free_inode = tracefs_free_inode, 37462306a36Sopenharmony_ci .drop_inode = generic_delete_inode, 37562306a36Sopenharmony_ci .statfs = simple_statfs, 37662306a36Sopenharmony_ci .remount_fs = tracefs_remount, 37762306a36Sopenharmony_ci .show_options = tracefs_show_options, 37862306a36Sopenharmony_ci}; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* 38162306a36Sopenharmony_ci * It would be cleaner if eventfs had its own dentry ops. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * Note that d_revalidate is called potentially under RCU, 38462306a36Sopenharmony_ci * so it can't take the eventfs mutex etc. It's fine - if 38562306a36Sopenharmony_ci * we open a file just as it's marked dead, things will 38662306a36Sopenharmony_ci * still work just fine, and just see the old stale case. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_cistatic void tracefs_d_release(struct dentry *dentry) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci if (dentry->d_fsdata) 39162306a36Sopenharmony_ci eventfs_d_release(dentry); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int tracefs_d_revalidate(struct dentry *dentry, unsigned int flags) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct eventfs_inode *ei = dentry->d_fsdata; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return !(ei && ei->is_freed); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic const struct dentry_operations tracefs_dentry_operations = { 40262306a36Sopenharmony_ci .d_revalidate = tracefs_d_revalidate, 40362306a36Sopenharmony_ci .d_release = tracefs_d_release, 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int trace_fill_super(struct super_block *sb, void *data, int silent) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci static const struct tree_descr trace_files[] = {{""}}; 40962306a36Sopenharmony_ci struct tracefs_fs_info *fsi; 41062306a36Sopenharmony_ci int err; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); 41362306a36Sopenharmony_ci sb->s_fs_info = fsi; 41462306a36Sopenharmony_ci if (!fsi) { 41562306a36Sopenharmony_ci err = -ENOMEM; 41662306a36Sopenharmony_ci goto fail; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci err = tracefs_parse_options(data, &fsi->mount_opts); 42062306a36Sopenharmony_ci if (err) 42162306a36Sopenharmony_ci goto fail; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); 42462306a36Sopenharmony_ci if (err) 42562306a36Sopenharmony_ci goto fail; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci sb->s_op = &tracefs_super_operations; 42862306a36Sopenharmony_ci sb->s_d_op = &tracefs_dentry_operations; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci tracefs_apply_options(sb, false); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cifail: 43562306a36Sopenharmony_ci kfree(fsi); 43662306a36Sopenharmony_ci sb->s_fs_info = NULL; 43762306a36Sopenharmony_ci return err; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic struct dentry *trace_mount(struct file_system_type *fs_type, 44162306a36Sopenharmony_ci int flags, const char *dev_name, 44262306a36Sopenharmony_ci void *data) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci return mount_single(fs_type, flags, data, trace_fill_super); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic struct file_system_type trace_fs_type = { 44862306a36Sopenharmony_ci .owner = THIS_MODULE, 44962306a36Sopenharmony_ci .name = "tracefs", 45062306a36Sopenharmony_ci .mount = trace_mount, 45162306a36Sopenharmony_ci .kill_sb = kill_litter_super, 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ciMODULE_ALIAS_FS("tracefs"); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistruct dentry *tracefs_start_creating(const char *name, struct dentry *parent) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct dentry *dentry; 45862306a36Sopenharmony_ci int error; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci pr_debug("tracefs: creating file '%s'\n",name); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci error = simple_pin_fs(&trace_fs_type, &tracefs_mount, 46362306a36Sopenharmony_ci &tracefs_mount_count); 46462306a36Sopenharmony_ci if (error) 46562306a36Sopenharmony_ci return ERR_PTR(error); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* If the parent is not specified, we create it in the root. 46862306a36Sopenharmony_ci * We need the root dentry to do this, which is in the super 46962306a36Sopenharmony_ci * block. A pointer to that is in the struct vfsmount that we 47062306a36Sopenharmony_ci * have around. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci if (!parent) 47362306a36Sopenharmony_ci parent = tracefs_mount->mnt_root; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci inode_lock(d_inode(parent)); 47662306a36Sopenharmony_ci if (unlikely(IS_DEADDIR(d_inode(parent)))) 47762306a36Sopenharmony_ci dentry = ERR_PTR(-ENOENT); 47862306a36Sopenharmony_ci else 47962306a36Sopenharmony_ci dentry = lookup_one_len(name, parent, strlen(name)); 48062306a36Sopenharmony_ci if (!IS_ERR(dentry) && d_inode(dentry)) { 48162306a36Sopenharmony_ci dput(dentry); 48262306a36Sopenharmony_ci dentry = ERR_PTR(-EEXIST); 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (IS_ERR(dentry)) { 48662306a36Sopenharmony_ci inode_unlock(d_inode(parent)); 48762306a36Sopenharmony_ci simple_release_fs(&tracefs_mount, &tracefs_mount_count); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return dentry; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistruct dentry *tracefs_failed_creating(struct dentry *dentry) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci inode_unlock(d_inode(dentry->d_parent)); 49662306a36Sopenharmony_ci dput(dentry); 49762306a36Sopenharmony_ci simple_release_fs(&tracefs_mount, &tracefs_mount_count); 49862306a36Sopenharmony_ci return NULL; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistruct dentry *tracefs_end_creating(struct dentry *dentry) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci inode_unlock(d_inode(dentry->d_parent)); 50462306a36Sopenharmony_ci return dentry; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/* Find the inode that this will use for default */ 50862306a36Sopenharmony_cistatic struct inode *instance_inode(struct dentry *parent, struct inode *inode) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct tracefs_inode *ti; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* If parent is NULL then use root inode */ 51362306a36Sopenharmony_ci if (!parent) 51462306a36Sopenharmony_ci return d_inode(inode->i_sb->s_root); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Find the inode that is flagged as an instance or the root inode */ 51762306a36Sopenharmony_ci while (!IS_ROOT(parent)) { 51862306a36Sopenharmony_ci ti = get_tracefs(d_inode(parent)); 51962306a36Sopenharmony_ci if (ti->flags & TRACEFS_INSTANCE_INODE) 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci parent = parent->d_parent; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return d_inode(parent); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * tracefs_create_file - create a file in the tracefs filesystem 52962306a36Sopenharmony_ci * @name: a pointer to a string containing the name of the file to create. 53062306a36Sopenharmony_ci * @mode: the permission that the file should have. 53162306a36Sopenharmony_ci * @parent: a pointer to the parent dentry for this file. This should be a 53262306a36Sopenharmony_ci * directory dentry if set. If this parameter is NULL, then the 53362306a36Sopenharmony_ci * file will be created in the root of the tracefs filesystem. 53462306a36Sopenharmony_ci * @data: a pointer to something that the caller will want to get to later 53562306a36Sopenharmony_ci * on. The inode.i_private pointer will point to this value on 53662306a36Sopenharmony_ci * the open() call. 53762306a36Sopenharmony_ci * @fops: a pointer to a struct file_operations that should be used for 53862306a36Sopenharmony_ci * this file. 53962306a36Sopenharmony_ci * 54062306a36Sopenharmony_ci * This is the basic "create a file" function for tracefs. It allows for a 54162306a36Sopenharmony_ci * wide range of flexibility in creating a file, or a directory (if you want 54262306a36Sopenharmony_ci * to create a directory, the tracefs_create_dir() function is 54362306a36Sopenharmony_ci * recommended to be used instead.) 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * This function will return a pointer to a dentry if it succeeds. This 54662306a36Sopenharmony_ci * pointer must be passed to the tracefs_remove() function when the file is 54762306a36Sopenharmony_ci * to be removed (no automatic cleanup happens if your module is unloaded, 54862306a36Sopenharmony_ci * you are responsible here.) If an error occurs, %NULL will be returned. 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * If tracefs is not enabled in the kernel, the value -%ENODEV will be 55162306a36Sopenharmony_ci * returned. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_cistruct dentry *tracefs_create_file(const char *name, umode_t mode, 55462306a36Sopenharmony_ci struct dentry *parent, void *data, 55562306a36Sopenharmony_ci const struct file_operations *fops) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct tracefs_inode *ti; 55862306a36Sopenharmony_ci struct dentry *dentry; 55962306a36Sopenharmony_ci struct inode *inode; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (security_locked_down(LOCKDOWN_TRACEFS)) 56262306a36Sopenharmony_ci return NULL; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (!(mode & S_IFMT)) 56562306a36Sopenharmony_ci mode |= S_IFREG; 56662306a36Sopenharmony_ci BUG_ON(!S_ISREG(mode)); 56762306a36Sopenharmony_ci dentry = tracefs_start_creating(name, parent); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (IS_ERR(dentry)) 57062306a36Sopenharmony_ci return NULL; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci inode = tracefs_get_inode(dentry->d_sb); 57362306a36Sopenharmony_ci if (unlikely(!inode)) 57462306a36Sopenharmony_ci return tracefs_failed_creating(dentry); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ti = get_tracefs(inode); 57762306a36Sopenharmony_ci ti->private = instance_inode(parent, inode); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci inode->i_mode = mode; 58062306a36Sopenharmony_ci inode->i_op = &tracefs_file_inode_operations; 58162306a36Sopenharmony_ci inode->i_fop = fops ? fops : &tracefs_file_operations; 58262306a36Sopenharmony_ci inode->i_private = data; 58362306a36Sopenharmony_ci inode->i_uid = d_inode(dentry->d_parent)->i_uid; 58462306a36Sopenharmony_ci inode->i_gid = d_inode(dentry->d_parent)->i_gid; 58562306a36Sopenharmony_ci d_instantiate(dentry, inode); 58662306a36Sopenharmony_ci fsnotify_create(d_inode(dentry->d_parent), dentry); 58762306a36Sopenharmony_ci return tracefs_end_creating(dentry); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic struct dentry *__create_dir(const char *name, struct dentry *parent, 59162306a36Sopenharmony_ci const struct inode_operations *ops) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct tracefs_inode *ti; 59462306a36Sopenharmony_ci struct dentry *dentry = tracefs_start_creating(name, parent); 59562306a36Sopenharmony_ci struct inode *inode; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (IS_ERR(dentry)) 59862306a36Sopenharmony_ci return NULL; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci inode = tracefs_get_inode(dentry->d_sb); 60162306a36Sopenharmony_ci if (unlikely(!inode)) 60262306a36Sopenharmony_ci return tracefs_failed_creating(dentry); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* Do not set bits for OTH */ 60562306a36Sopenharmony_ci inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP; 60662306a36Sopenharmony_ci inode->i_op = ops; 60762306a36Sopenharmony_ci inode->i_fop = &simple_dir_operations; 60862306a36Sopenharmony_ci inode->i_uid = d_inode(dentry->d_parent)->i_uid; 60962306a36Sopenharmony_ci inode->i_gid = d_inode(dentry->d_parent)->i_gid; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ti = get_tracefs(inode); 61262306a36Sopenharmony_ci ti->private = instance_inode(parent, inode); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* directory inodes start off with i_nlink == 2 (for "." entry) */ 61562306a36Sopenharmony_ci inc_nlink(inode); 61662306a36Sopenharmony_ci d_instantiate(dentry, inode); 61762306a36Sopenharmony_ci inc_nlink(d_inode(dentry->d_parent)); 61862306a36Sopenharmony_ci fsnotify_mkdir(d_inode(dentry->d_parent), dentry); 61962306a36Sopenharmony_ci return tracefs_end_creating(dentry); 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci/** 62362306a36Sopenharmony_ci * tracefs_create_dir - create a directory in the tracefs filesystem 62462306a36Sopenharmony_ci * @name: a pointer to a string containing the name of the directory to 62562306a36Sopenharmony_ci * create. 62662306a36Sopenharmony_ci * @parent: a pointer to the parent dentry for this file. This should be a 62762306a36Sopenharmony_ci * directory dentry if set. If this parameter is NULL, then the 62862306a36Sopenharmony_ci * directory will be created in the root of the tracefs filesystem. 62962306a36Sopenharmony_ci * 63062306a36Sopenharmony_ci * This function creates a directory in tracefs with the given name. 63162306a36Sopenharmony_ci * 63262306a36Sopenharmony_ci * This function will return a pointer to a dentry if it succeeds. This 63362306a36Sopenharmony_ci * pointer must be passed to the tracefs_remove() function when the file is 63462306a36Sopenharmony_ci * to be removed. If an error occurs, %NULL will be returned. 63562306a36Sopenharmony_ci * 63662306a36Sopenharmony_ci * If tracing is not enabled in the kernel, the value -%ENODEV will be 63762306a36Sopenharmony_ci * returned. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_cistruct dentry *tracefs_create_dir(const char *name, struct dentry *parent) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci if (security_locked_down(LOCKDOWN_TRACEFS)) 64262306a36Sopenharmony_ci return NULL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return __create_dir(name, parent, &tracefs_dir_inode_operations); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/** 64862306a36Sopenharmony_ci * tracefs_create_instance_dir - create the tracing instances directory 64962306a36Sopenharmony_ci * @name: The name of the instances directory to create 65062306a36Sopenharmony_ci * @parent: The parent directory that the instances directory will exist 65162306a36Sopenharmony_ci * @mkdir: The function to call when a mkdir is performed. 65262306a36Sopenharmony_ci * @rmdir: The function to call when a rmdir is performed. 65362306a36Sopenharmony_ci * 65462306a36Sopenharmony_ci * Only one instances directory is allowed. 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * The instances directory is special as it allows for mkdir and rmdir 65762306a36Sopenharmony_ci * to be done by userspace. When a mkdir or rmdir is performed, the inode 65862306a36Sopenharmony_ci * locks are released and the methods passed in (@mkdir and @rmdir) are 65962306a36Sopenharmony_ci * called without locks and with the name of the directory being created 66062306a36Sopenharmony_ci * within the instances directory. 66162306a36Sopenharmony_ci * 66262306a36Sopenharmony_ci * Returns the dentry of the instances directory. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci__init struct dentry *tracefs_create_instance_dir(const char *name, 66562306a36Sopenharmony_ci struct dentry *parent, 66662306a36Sopenharmony_ci int (*mkdir)(const char *name), 66762306a36Sopenharmony_ci int (*rmdir)(const char *name)) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct dentry *dentry; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Only allow one instance of the instances directory. */ 67262306a36Sopenharmony_ci if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir)) 67362306a36Sopenharmony_ci return NULL; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci dentry = __create_dir(name, parent, &tracefs_instance_dir_inode_operations); 67662306a36Sopenharmony_ci if (!dentry) 67762306a36Sopenharmony_ci return NULL; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci tracefs_ops.mkdir = mkdir; 68062306a36Sopenharmony_ci tracefs_ops.rmdir = rmdir; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return dentry; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic void remove_one(struct dentry *victim) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci simple_release_fs(&tracefs_mount, &tracefs_mount_count); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci/** 69162306a36Sopenharmony_ci * tracefs_remove - recursively removes a directory 69262306a36Sopenharmony_ci * @dentry: a pointer to a the dentry of the directory to be removed. 69362306a36Sopenharmony_ci * 69462306a36Sopenharmony_ci * This function recursively removes a directory tree in tracefs that 69562306a36Sopenharmony_ci * was previously created with a call to another tracefs function 69662306a36Sopenharmony_ci * (like tracefs_create_file() or variants thereof.) 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_civoid tracefs_remove(struct dentry *dentry) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dentry)) 70162306a36Sopenharmony_ci return; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci simple_pin_fs(&trace_fs_type, &tracefs_mount, &tracefs_mount_count); 70462306a36Sopenharmony_ci simple_recursive_removal(dentry, remove_one); 70562306a36Sopenharmony_ci simple_release_fs(&tracefs_mount, &tracefs_mount_count); 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci/** 70962306a36Sopenharmony_ci * tracefs_initialized - Tells whether tracefs has been registered 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_cibool tracefs_initialized(void) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci return tracefs_registered; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void init_once(void *foo) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct tracefs_inode *ti = (struct tracefs_inode *) foo; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* inode_init_once() calls memset() on the vfs_inode portion */ 72162306a36Sopenharmony_ci inode_init_once(&ti->vfs_inode); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Zero out the rest */ 72462306a36Sopenharmony_ci memset_after(ti, 0, vfs_inode); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int __init tracefs_init(void) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci int retval; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci tracefs_inode_cachep = kmem_cache_create("tracefs_inode_cache", 73262306a36Sopenharmony_ci sizeof(struct tracefs_inode), 73362306a36Sopenharmony_ci 0, (SLAB_RECLAIM_ACCOUNT| 73462306a36Sopenharmony_ci SLAB_MEM_SPREAD| 73562306a36Sopenharmony_ci SLAB_ACCOUNT), 73662306a36Sopenharmony_ci init_once); 73762306a36Sopenharmony_ci if (!tracefs_inode_cachep) 73862306a36Sopenharmony_ci return -ENOMEM; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci retval = sysfs_create_mount_point(kernel_kobj, "tracing"); 74162306a36Sopenharmony_ci if (retval) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci retval = register_filesystem(&trace_fs_type); 74562306a36Sopenharmony_ci if (!retval) 74662306a36Sopenharmony_ci tracefs_registered = true; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return retval; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_cicore_initcall(tracefs_init); 751