xref: /openbmc/linux/fs/tracefs/inode.c (revision 51aab5ffceb43e05119eb059048fd75765d2bc21)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24282d606SSteven Rostedt (Red Hat) /*
34282d606SSteven Rostedt (Red Hat)  *  inode.c - part of tracefs, a pseudo file system for activating tracing
44282d606SSteven Rostedt (Red Hat)  *
54282d606SSteven Rostedt (Red Hat)  * Based on debugfs by: Greg Kroah-Hartman <greg@kroah.com>
64282d606SSteven Rostedt (Red Hat)  *
74282d606SSteven Rostedt (Red Hat)  *  Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com>
84282d606SSteven Rostedt (Red Hat)  *
94282d606SSteven Rostedt (Red Hat)  * tracefs is the file system that is used by the tracing infrastructure.
104282d606SSteven Rostedt (Red Hat)  */
114282d606SSteven Rostedt (Red Hat) 
124282d606SSteven Rostedt (Red Hat) #include <linux/module.h>
134282d606SSteven Rostedt (Red Hat) #include <linux/fs.h>
144282d606SSteven Rostedt (Red Hat) #include <linux/mount.h>
15cc31004aSSteven Rostedt (Red Hat) #include <linux/kobject.h>
164282d606SSteven Rostedt (Red Hat) #include <linux/namei.h>
174282d606SSteven Rostedt (Red Hat) #include <linux/tracefs.h>
184282d606SSteven Rostedt (Red Hat) #include <linux/fsnotify.h>
19bf8e6021SSteven Rostedt (VMware) #include <linux/security.h>
204282d606SSteven Rostedt (Red Hat) #include <linux/seq_file.h>
214282d606SSteven Rostedt (Red Hat) #include <linux/parser.h>
224282d606SSteven Rostedt (Red Hat) #include <linux/magic.h>
234282d606SSteven Rostedt (Red Hat) #include <linux/slab.h>
24ba37ff75SAjay Kaher #include "internal.h"
254282d606SSteven Rostedt (Red Hat) 
264282d606SSteven Rostedt (Red Hat) #define TRACEFS_DEFAULT_MODE	0700
27ba37ff75SAjay Kaher static struct kmem_cache *tracefs_inode_cachep __ro_after_init;
284282d606SSteven Rostedt (Red Hat) 
294282d606SSteven Rostedt (Red Hat) static struct vfsmount *tracefs_mount;
304282d606SSteven Rostedt (Red Hat) static int tracefs_mount_count;
314282d606SSteven Rostedt (Red Hat) static bool tracefs_registered;
324282d606SSteven Rostedt (Red Hat) 
33ba37ff75SAjay Kaher static struct inode *tracefs_alloc_inode(struct super_block *sb)
34ba37ff75SAjay Kaher {
35ba37ff75SAjay Kaher 	struct tracefs_inode *ti;
36ba37ff75SAjay Kaher 
37ba37ff75SAjay Kaher 	ti = kmem_cache_alloc(tracefs_inode_cachep, GFP_KERNEL);
38ba37ff75SAjay Kaher 	if (!ti)
39ba37ff75SAjay Kaher 		return NULL;
40ba37ff75SAjay Kaher 
41ba37ff75SAjay Kaher 	ti->flags = 0;
42ba37ff75SAjay Kaher 
43ba37ff75SAjay Kaher 	return &ti->vfs_inode;
44ba37ff75SAjay Kaher }
45ba37ff75SAjay Kaher 
46ba37ff75SAjay Kaher static void tracefs_free_inode(struct inode *inode)
47ba37ff75SAjay Kaher {
48ba37ff75SAjay Kaher 	kmem_cache_free(tracefs_inode_cachep, get_tracefs(inode));
49ba37ff75SAjay Kaher }
50ba37ff75SAjay Kaher 
514282d606SSteven Rostedt (Red Hat) static ssize_t default_read_file(struct file *file, char __user *buf,
524282d606SSteven Rostedt (Red Hat) 				 size_t count, loff_t *ppos)
534282d606SSteven Rostedt (Red Hat) {
544282d606SSteven Rostedt (Red Hat) 	return 0;
554282d606SSteven Rostedt (Red Hat) }
564282d606SSteven Rostedt (Red Hat) 
574282d606SSteven Rostedt (Red Hat) static ssize_t default_write_file(struct file *file, const char __user *buf,
584282d606SSteven Rostedt (Red Hat) 				   size_t count, loff_t *ppos)
594282d606SSteven Rostedt (Red Hat) {
604282d606SSteven Rostedt (Red Hat) 	return count;
614282d606SSteven Rostedt (Red Hat) }
624282d606SSteven Rostedt (Red Hat) 
634282d606SSteven Rostedt (Red Hat) static const struct file_operations tracefs_file_operations = {
644282d606SSteven Rostedt (Red Hat) 	.read =		default_read_file,
654282d606SSteven Rostedt (Red Hat) 	.write =	default_write_file,
664282d606SSteven Rostedt (Red Hat) 	.open =		simple_open,
674282d606SSteven Rostedt (Red Hat) 	.llseek =	noop_llseek,
684282d606SSteven Rostedt (Red Hat) };
694282d606SSteven Rostedt (Red Hat) 
70eae47358SSteven Rostedt (Red Hat) static struct tracefs_dir_ops {
71eae47358SSteven Rostedt (Red Hat) 	int (*mkdir)(const char *name);
72eae47358SSteven Rostedt (Red Hat) 	int (*rmdir)(const char *name);
735248ee85SZubin Mithra } tracefs_ops __ro_after_init;
74eae47358SSteven Rostedt (Red Hat) 
75eae47358SSteven Rostedt (Red Hat) static char *get_dname(struct dentry *dentry)
76eae47358SSteven Rostedt (Red Hat) {
77eae47358SSteven Rostedt (Red Hat) 	const char *dname;
78eae47358SSteven Rostedt (Red Hat) 	char *name;
79eae47358SSteven Rostedt (Red Hat) 	int len = dentry->d_name.len;
80eae47358SSteven Rostedt (Red Hat) 
81eae47358SSteven Rostedt (Red Hat) 	dname = dentry->d_name.name;
82eae47358SSteven Rostedt (Red Hat) 	name = kmalloc(len + 1, GFP_KERNEL);
83eae47358SSteven Rostedt (Red Hat) 	if (!name)
84eae47358SSteven Rostedt (Red Hat) 		return NULL;
85eae47358SSteven Rostedt (Red Hat) 	memcpy(name, dname, len);
86eae47358SSteven Rostedt (Red Hat) 	name[len] = 0;
87eae47358SSteven Rostedt (Red Hat) 	return name;
88eae47358SSteven Rostedt (Red Hat) }
89eae47358SSteven Rostedt (Red Hat) 
90c54bd91eSChristian Brauner static int tracefs_syscall_mkdir(struct mnt_idmap *idmap,
91549c7297SChristian Brauner 				 struct inode *inode, struct dentry *dentry,
92549c7297SChristian Brauner 				 umode_t mode)
93eae47358SSteven Rostedt (Red Hat) {
94eae47358SSteven Rostedt (Red Hat) 	char *name;
95eae47358SSteven Rostedt (Red Hat) 	int ret;
96eae47358SSteven Rostedt (Red Hat) 
97eae47358SSteven Rostedt (Red Hat) 	name = get_dname(dentry);
98eae47358SSteven Rostedt (Red Hat) 	if (!name)
99eae47358SSteven Rostedt (Red Hat) 		return -ENOMEM;
100eae47358SSteven Rostedt (Red Hat) 
101eae47358SSteven Rostedt (Red Hat) 	/*
102eae47358SSteven Rostedt (Red Hat) 	 * The mkdir call can call the generic functions that create
103eae47358SSteven Rostedt (Red Hat) 	 * the files within the tracefs system. It is up to the individual
104eae47358SSteven Rostedt (Red Hat) 	 * mkdir routine to handle races.
105eae47358SSteven Rostedt (Red Hat) 	 */
1065955102cSAl Viro 	inode_unlock(inode);
107eae47358SSteven Rostedt (Red Hat) 	ret = tracefs_ops.mkdir(name);
1085955102cSAl Viro 	inode_lock(inode);
109eae47358SSteven Rostedt (Red Hat) 
110eae47358SSteven Rostedt (Red Hat) 	kfree(name);
111eae47358SSteven Rostedt (Red Hat) 
112eae47358SSteven Rostedt (Red Hat) 	return ret;
113eae47358SSteven Rostedt (Red Hat) }
114eae47358SSteven Rostedt (Red Hat) 
115eae47358SSteven Rostedt (Red Hat) static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry)
116eae47358SSteven Rostedt (Red Hat) {
117eae47358SSteven Rostedt (Red Hat) 	char *name;
118eae47358SSteven Rostedt (Red Hat) 	int ret;
119eae47358SSteven Rostedt (Red Hat) 
120eae47358SSteven Rostedt (Red Hat) 	name = get_dname(dentry);
121eae47358SSteven Rostedt (Red Hat) 	if (!name)
122eae47358SSteven Rostedt (Red Hat) 		return -ENOMEM;
123eae47358SSteven Rostedt (Red Hat) 
124eae47358SSteven Rostedt (Red Hat) 	/*
125eae47358SSteven Rostedt (Red Hat) 	 * The rmdir call can call the generic functions that create
126eae47358SSteven Rostedt (Red Hat) 	 * the files within the tracefs system. It is up to the individual
127eae47358SSteven Rostedt (Red Hat) 	 * rmdir routine to handle races.
128eae47358SSteven Rostedt (Red Hat) 	 * This time we need to unlock not only the parent (inode) but
129eae47358SSteven Rostedt (Red Hat) 	 * also the directory that is being deleted.
130eae47358SSteven Rostedt (Red Hat) 	 */
1315955102cSAl Viro 	inode_unlock(inode);
132ee34c52cSSteven Rostedt (VMware) 	inode_unlock(d_inode(dentry));
133eae47358SSteven Rostedt (Red Hat) 
134eae47358SSteven Rostedt (Red Hat) 	ret = tracefs_ops.rmdir(name);
135eae47358SSteven Rostedt (Red Hat) 
1365955102cSAl Viro 	inode_lock_nested(inode, I_MUTEX_PARENT);
137ee34c52cSSteven Rostedt (VMware) 	inode_lock(d_inode(dentry));
138eae47358SSteven Rostedt (Red Hat) 
139eae47358SSteven Rostedt (Red Hat) 	kfree(name);
140eae47358SSteven Rostedt (Red Hat) 
141eae47358SSteven Rostedt (Red Hat) 	return ret;
142eae47358SSteven Rostedt (Red Hat) }
143eae47358SSteven Rostedt (Red Hat) 
144eae47358SSteven Rostedt (Red Hat) static const struct inode_operations tracefs_dir_inode_operations = {
145eae47358SSteven Rostedt (Red Hat) 	.lookup		= simple_lookup,
146eae47358SSteven Rostedt (Red Hat) 	.mkdir		= tracefs_syscall_mkdir,
147eae47358SSteven Rostedt (Red Hat) 	.rmdir		= tracefs_syscall_rmdir,
148eae47358SSteven Rostedt (Red Hat) };
149eae47358SSteven Rostedt (Red Hat) 
1502c6b6b10SAjay Kaher struct inode *tracefs_get_inode(struct super_block *sb)
1514282d606SSteven Rostedt (Red Hat) {
1524282d606SSteven Rostedt (Red Hat) 	struct inode *inode = new_inode(sb);
1534282d606SSteven Rostedt (Red Hat) 	if (inode) {
1544282d606SSteven Rostedt (Red Hat) 		inode->i_ino = get_next_ino();
155bb9c40e6SJeff Layton 		inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);
1564282d606SSteven Rostedt (Red Hat) 	}
1574282d606SSteven Rostedt (Red Hat) 	return inode;
1584282d606SSteven Rostedt (Red Hat) }
1594282d606SSteven Rostedt (Red Hat) 
1604282d606SSteven Rostedt (Red Hat) struct tracefs_mount_opts {
1614282d606SSteven Rostedt (Red Hat) 	kuid_t uid;
1624282d606SSteven Rostedt (Red Hat) 	kgid_t gid;
1634282d606SSteven Rostedt (Red Hat) 	umode_t mode;
16447311db8SBrian Norris 	/* Opt_* bitfield. */
16547311db8SBrian Norris 	unsigned int opts;
1664282d606SSteven Rostedt (Red Hat) };
1674282d606SSteven Rostedt (Red Hat) 
1684282d606SSteven Rostedt (Red Hat) enum {
1694282d606SSteven Rostedt (Red Hat) 	Opt_uid,
1704282d606SSteven Rostedt (Red Hat) 	Opt_gid,
1714282d606SSteven Rostedt (Red Hat) 	Opt_mode,
1724282d606SSteven Rostedt (Red Hat) 	Opt_err
1734282d606SSteven Rostedt (Red Hat) };
1744282d606SSteven Rostedt (Red Hat) 
1754282d606SSteven Rostedt (Red Hat) static const match_table_t tokens = {
1764282d606SSteven Rostedt (Red Hat) 	{Opt_uid, "uid=%u"},
1774282d606SSteven Rostedt (Red Hat) 	{Opt_gid, "gid=%u"},
1784282d606SSteven Rostedt (Red Hat) 	{Opt_mode, "mode=%o"},
1794282d606SSteven Rostedt (Red Hat) 	{Opt_err, NULL}
1804282d606SSteven Rostedt (Red Hat) };
1814282d606SSteven Rostedt (Red Hat) 
1824282d606SSteven Rostedt (Red Hat) struct tracefs_fs_info {
1834282d606SSteven Rostedt (Red Hat) 	struct tracefs_mount_opts mount_opts;
1844282d606SSteven Rostedt (Red Hat) };
1854282d606SSteven Rostedt (Red Hat) 
18648b27b6bSSteven Rostedt (VMware) static void change_gid(struct dentry *dentry, kgid_t gid)
18748b27b6bSSteven Rostedt (VMware) {
18848b27b6bSSteven Rostedt (VMware) 	if (!dentry->d_inode)
18948b27b6bSSteven Rostedt (VMware) 		return;
19048b27b6bSSteven Rostedt (VMware) 	dentry->d_inode->i_gid = gid;
19148b27b6bSSteven Rostedt (VMware) }
19248b27b6bSSteven Rostedt (VMware) 
19348b27b6bSSteven Rostedt (VMware) /*
19448b27b6bSSteven Rostedt (VMware)  * Taken from d_walk, but without he need for handling renames.
19548b27b6bSSteven Rostedt (VMware)  * Nothing can be renamed while walking the list, as tracefs
19648b27b6bSSteven Rostedt (VMware)  * does not support renames. This is only called when mounting
19748b27b6bSSteven Rostedt (VMware)  * or remounting the file system, to set all the files to
19848b27b6bSSteven Rostedt (VMware)  * the given gid.
19948b27b6bSSteven Rostedt (VMware)  */
20048b27b6bSSteven Rostedt (VMware) static void set_gid(struct dentry *parent, kgid_t gid)
20148b27b6bSSteven Rostedt (VMware) {
20248b27b6bSSteven Rostedt (VMware) 	struct dentry *this_parent;
20348b27b6bSSteven Rostedt (VMware) 	struct list_head *next;
20448b27b6bSSteven Rostedt (VMware) 
20548b27b6bSSteven Rostedt (VMware) 	this_parent = parent;
20648b27b6bSSteven Rostedt (VMware) 	spin_lock(&this_parent->d_lock);
20748b27b6bSSteven Rostedt (VMware) 
20848b27b6bSSteven Rostedt (VMware) 	change_gid(this_parent, gid);
20948b27b6bSSteven Rostedt (VMware) repeat:
21048b27b6bSSteven Rostedt (VMware) 	next = this_parent->d_subdirs.next;
21148b27b6bSSteven Rostedt (VMware) resume:
21248b27b6bSSteven Rostedt (VMware) 	while (next != &this_parent->d_subdirs) {
21348b27b6bSSteven Rostedt (VMware) 		struct list_head *tmp = next;
21448b27b6bSSteven Rostedt (VMware) 		struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
21548b27b6bSSteven Rostedt (VMware) 		next = tmp->next;
21648b27b6bSSteven Rostedt (VMware) 
21748b27b6bSSteven Rostedt (VMware) 		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
21848b27b6bSSteven Rostedt (VMware) 
21948b27b6bSSteven Rostedt (VMware) 		change_gid(dentry, gid);
22048b27b6bSSteven Rostedt (VMware) 
22148b27b6bSSteven Rostedt (VMware) 		if (!list_empty(&dentry->d_subdirs)) {
22248b27b6bSSteven Rostedt (VMware) 			spin_unlock(&this_parent->d_lock);
22348b27b6bSSteven Rostedt (VMware) 			spin_release(&dentry->d_lock.dep_map, _RET_IP_);
22448b27b6bSSteven Rostedt (VMware) 			this_parent = dentry;
22548b27b6bSSteven Rostedt (VMware) 			spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
22648b27b6bSSteven Rostedt (VMware) 			goto repeat;
22748b27b6bSSteven Rostedt (VMware) 		}
22848b27b6bSSteven Rostedt (VMware) 		spin_unlock(&dentry->d_lock);
22948b27b6bSSteven Rostedt (VMware) 	}
23048b27b6bSSteven Rostedt (VMware) 	/*
23148b27b6bSSteven Rostedt (VMware) 	 * All done at this level ... ascend and resume the search.
23248b27b6bSSteven Rostedt (VMware) 	 */
23348b27b6bSSteven Rostedt (VMware) 	rcu_read_lock();
23448b27b6bSSteven Rostedt (VMware) ascend:
23548b27b6bSSteven Rostedt (VMware) 	if (this_parent != parent) {
23648b27b6bSSteven Rostedt (VMware) 		struct dentry *child = this_parent;
23748b27b6bSSteven Rostedt (VMware) 		this_parent = child->d_parent;
23848b27b6bSSteven Rostedt (VMware) 
23948b27b6bSSteven Rostedt (VMware) 		spin_unlock(&child->d_lock);
24048b27b6bSSteven Rostedt (VMware) 		spin_lock(&this_parent->d_lock);
24148b27b6bSSteven Rostedt (VMware) 
24248b27b6bSSteven Rostedt (VMware) 		/* go into the first sibling still alive */
24348b27b6bSSteven Rostedt (VMware) 		do {
24448b27b6bSSteven Rostedt (VMware) 			next = child->d_child.next;
24548b27b6bSSteven Rostedt (VMware) 			if (next == &this_parent->d_subdirs)
24648b27b6bSSteven Rostedt (VMware) 				goto ascend;
24748b27b6bSSteven Rostedt (VMware) 			child = list_entry(next, struct dentry, d_child);
24848b27b6bSSteven Rostedt (VMware) 		} while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
24948b27b6bSSteven Rostedt (VMware) 		rcu_read_unlock();
25048b27b6bSSteven Rostedt (VMware) 		goto resume;
25148b27b6bSSteven Rostedt (VMware) 	}
25248b27b6bSSteven Rostedt (VMware) 	rcu_read_unlock();
25348b27b6bSSteven Rostedt (VMware) 	spin_unlock(&this_parent->d_lock);
25448b27b6bSSteven Rostedt (VMware) 	return;
25548b27b6bSSteven Rostedt (VMware) }
25648b27b6bSSteven Rostedt (VMware) 
2574282d606SSteven Rostedt (Red Hat) static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)
2584282d606SSteven Rostedt (Red Hat) {
2594282d606SSteven Rostedt (Red Hat) 	substring_t args[MAX_OPT_ARGS];
2604282d606SSteven Rostedt (Red Hat) 	int option;
2614282d606SSteven Rostedt (Red Hat) 	int token;
2624282d606SSteven Rostedt (Red Hat) 	kuid_t uid;
2634282d606SSteven Rostedt (Red Hat) 	kgid_t gid;
2644282d606SSteven Rostedt (Red Hat) 	char *p;
2654282d606SSteven Rostedt (Red Hat) 
26647311db8SBrian Norris 	opts->opts = 0;
2674282d606SSteven Rostedt (Red Hat) 	opts->mode = TRACEFS_DEFAULT_MODE;
2684282d606SSteven Rostedt (Red Hat) 
2694282d606SSteven Rostedt (Red Hat) 	while ((p = strsep(&data, ",")) != NULL) {
2704282d606SSteven Rostedt (Red Hat) 		if (!*p)
2714282d606SSteven Rostedt (Red Hat) 			continue;
2724282d606SSteven Rostedt (Red Hat) 
2734282d606SSteven Rostedt (Red Hat) 		token = match_token(p, tokens, args);
2744282d606SSteven Rostedt (Red Hat) 		switch (token) {
2754282d606SSteven Rostedt (Red Hat) 		case Opt_uid:
2764282d606SSteven Rostedt (Red Hat) 			if (match_int(&args[0], &option))
2774282d606SSteven Rostedt (Red Hat) 				return -EINVAL;
2784282d606SSteven Rostedt (Red Hat) 			uid = make_kuid(current_user_ns(), option);
2794282d606SSteven Rostedt (Red Hat) 			if (!uid_valid(uid))
2804282d606SSteven Rostedt (Red Hat) 				return -EINVAL;
2814282d606SSteven Rostedt (Red Hat) 			opts->uid = uid;
2824282d606SSteven Rostedt (Red Hat) 			break;
2834282d606SSteven Rostedt (Red Hat) 		case Opt_gid:
2844282d606SSteven Rostedt (Red Hat) 			if (match_int(&args[0], &option))
2854282d606SSteven Rostedt (Red Hat) 				return -EINVAL;
2864282d606SSteven Rostedt (Red Hat) 			gid = make_kgid(current_user_ns(), option);
2874282d606SSteven Rostedt (Red Hat) 			if (!gid_valid(gid))
2884282d606SSteven Rostedt (Red Hat) 				return -EINVAL;
2894282d606SSteven Rostedt (Red Hat) 			opts->gid = gid;
2904282d606SSteven Rostedt (Red Hat) 			break;
2914282d606SSteven Rostedt (Red Hat) 		case Opt_mode:
2924282d606SSteven Rostedt (Red Hat) 			if (match_octal(&args[0], &option))
2934282d606SSteven Rostedt (Red Hat) 				return -EINVAL;
2944282d606SSteven Rostedt (Red Hat) 			opts->mode = option & S_IALLUGO;
2954282d606SSteven Rostedt (Red Hat) 			break;
2964282d606SSteven Rostedt (Red Hat) 		/*
2974282d606SSteven Rostedt (Red Hat) 		 * We might like to report bad mount options here;
2984282d606SSteven Rostedt (Red Hat) 		 * but traditionally tracefs has ignored all mount options
2994282d606SSteven Rostedt (Red Hat) 		 */
3004282d606SSteven Rostedt (Red Hat) 		}
30147311db8SBrian Norris 
30247311db8SBrian Norris 		opts->opts |= BIT(token);
3034282d606SSteven Rostedt (Red Hat) 	}
3044282d606SSteven Rostedt (Red Hat) 
3054282d606SSteven Rostedt (Red Hat) 	return 0;
3064282d606SSteven Rostedt (Red Hat) }
3074282d606SSteven Rostedt (Red Hat) 
30847311db8SBrian Norris static int tracefs_apply_options(struct super_block *sb, bool remount)
3094282d606SSteven Rostedt (Red Hat) {
3104282d606SSteven Rostedt (Red Hat) 	struct tracefs_fs_info *fsi = sb->s_fs_info;
311ee34c52cSSteven Rostedt (VMware) 	struct inode *inode = d_inode(sb->s_root);
3124282d606SSteven Rostedt (Red Hat) 	struct tracefs_mount_opts *opts = &fsi->mount_opts;
31308662977SSishuai Gong 	umode_t tmp_mode;
3144282d606SSteven Rostedt (Red Hat) 
31547311db8SBrian Norris 	/*
31647311db8SBrian Norris 	 * On remount, only reset mode/uid/gid if they were provided as mount
31747311db8SBrian Norris 	 * options.
31847311db8SBrian Norris 	 */
31947311db8SBrian Norris 
32047311db8SBrian Norris 	if (!remount || opts->opts & BIT(Opt_mode)) {
32108662977SSishuai Gong 		tmp_mode = READ_ONCE(inode->i_mode) & ~S_IALLUGO;
32208662977SSishuai Gong 		tmp_mode |= opts->mode;
32308662977SSishuai Gong 		WRITE_ONCE(inode->i_mode, tmp_mode);
32447311db8SBrian Norris 	}
3254282d606SSteven Rostedt (Red Hat) 
32647311db8SBrian Norris 	if (!remount || opts->opts & BIT(Opt_uid))
3274282d606SSteven Rostedt (Red Hat) 		inode->i_uid = opts->uid;
328851e99ebSSteven Rostedt (Google) 
32947311db8SBrian Norris 	if (!remount || opts->opts & BIT(Opt_gid)) {
330851e99ebSSteven Rostedt (Google) 		/* Set all the group ids to the mount option */
331851e99ebSSteven Rostedt (Google) 		set_gid(sb->s_root, opts->gid);
33247311db8SBrian Norris 	}
3334282d606SSteven Rostedt (Red Hat) 
3344282d606SSteven Rostedt (Red Hat) 	return 0;
3354282d606SSteven Rostedt (Red Hat) }
3364282d606SSteven Rostedt (Red Hat) 
3374282d606SSteven Rostedt (Red Hat) static int tracefs_remount(struct super_block *sb, int *flags, char *data)
3384282d606SSteven Rostedt (Red Hat) {
3394282d606SSteven Rostedt (Red Hat) 	int err;
3404282d606SSteven Rostedt (Red Hat) 	struct tracefs_fs_info *fsi = sb->s_fs_info;
3414282d606SSteven Rostedt (Red Hat) 
3424282d606SSteven Rostedt (Red Hat) 	sync_filesystem(sb);
3434282d606SSteven Rostedt (Red Hat) 	err = tracefs_parse_options(data, &fsi->mount_opts);
3444282d606SSteven Rostedt (Red Hat) 	if (err)
3454282d606SSteven Rostedt (Red Hat) 		goto fail;
3464282d606SSteven Rostedt (Red Hat) 
34747311db8SBrian Norris 	tracefs_apply_options(sb, true);
3484282d606SSteven Rostedt (Red Hat) 
3494282d606SSteven Rostedt (Red Hat) fail:
3504282d606SSteven Rostedt (Red Hat) 	return err;
3514282d606SSteven Rostedt (Red Hat) }
3524282d606SSteven Rostedt (Red Hat) 
3534282d606SSteven Rostedt (Red Hat) static int tracefs_show_options(struct seq_file *m, struct dentry *root)
3544282d606SSteven Rostedt (Red Hat) {
3554282d606SSteven Rostedt (Red Hat) 	struct tracefs_fs_info *fsi = root->d_sb->s_fs_info;
3564282d606SSteven Rostedt (Red Hat) 	struct tracefs_mount_opts *opts = &fsi->mount_opts;
3574282d606SSteven Rostedt (Red Hat) 
3584282d606SSteven Rostedt (Red Hat) 	if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
3594282d606SSteven Rostedt (Red Hat) 		seq_printf(m, ",uid=%u",
3604282d606SSteven Rostedt (Red Hat) 			   from_kuid_munged(&init_user_ns, opts->uid));
3614282d606SSteven Rostedt (Red Hat) 	if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
3624282d606SSteven Rostedt (Red Hat) 		seq_printf(m, ",gid=%u",
3634282d606SSteven Rostedt (Red Hat) 			   from_kgid_munged(&init_user_ns, opts->gid));
3644282d606SSteven Rostedt (Red Hat) 	if (opts->mode != TRACEFS_DEFAULT_MODE)
3654282d606SSteven Rostedt (Red Hat) 		seq_printf(m, ",mode=%o", opts->mode);
3664282d606SSteven Rostedt (Red Hat) 
3674282d606SSteven Rostedt (Red Hat) 	return 0;
3684282d606SSteven Rostedt (Red Hat) }
3694282d606SSteven Rostedt (Red Hat) 
3704282d606SSteven Rostedt (Red Hat) static const struct super_operations tracefs_super_operations = {
371ba37ff75SAjay Kaher 	.alloc_inode    = tracefs_alloc_inode,
372ba37ff75SAjay Kaher 	.free_inode     = tracefs_free_inode,
373ba37ff75SAjay Kaher 	.drop_inode     = generic_delete_inode,
3744282d606SSteven Rostedt (Red Hat) 	.statfs		= simple_statfs,
3754282d606SSteven Rostedt (Red Hat) 	.remount_fs	= tracefs_remount,
3764282d606SSteven Rostedt (Red Hat) 	.show_options	= tracefs_show_options,
3774282d606SSteven Rostedt (Red Hat) };
3784282d606SSteven Rostedt (Red Hat) 
37927152bceSAjay Kaher static void tracefs_dentry_iput(struct dentry *dentry, struct inode *inode)
38027152bceSAjay Kaher {
38127152bceSAjay Kaher 	struct tracefs_inode *ti;
38227152bceSAjay Kaher 
38327152bceSAjay Kaher 	if (!dentry || !inode)
38427152bceSAjay Kaher 		return;
38527152bceSAjay Kaher 
38627152bceSAjay Kaher 	ti = get_tracefs(inode);
38727152bceSAjay Kaher 	if (ti && ti->flags & TRACEFS_EVENT_INODE)
38827152bceSAjay Kaher 		eventfs_set_ef_status_free(dentry);
38927152bceSAjay Kaher 	iput(inode);
39027152bceSAjay Kaher }
39127152bceSAjay Kaher 
39227152bceSAjay Kaher static const struct dentry_operations tracefs_dentry_operations = {
39327152bceSAjay Kaher 	.d_iput = tracefs_dentry_iput,
39427152bceSAjay Kaher };
39527152bceSAjay Kaher 
3964282d606SSteven Rostedt (Red Hat) static int trace_fill_super(struct super_block *sb, void *data, int silent)
3974282d606SSteven Rostedt (Red Hat) {
398cda37124SEric Biggers 	static const struct tree_descr trace_files[] = {{""}};
3994282d606SSteven Rostedt (Red Hat) 	struct tracefs_fs_info *fsi;
4004282d606SSteven Rostedt (Red Hat) 	int err;
4014282d606SSteven Rostedt (Red Hat) 
4024282d606SSteven Rostedt (Red Hat) 	fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL);
4034282d606SSteven Rostedt (Red Hat) 	sb->s_fs_info = fsi;
4044282d606SSteven Rostedt (Red Hat) 	if (!fsi) {
4054282d606SSteven Rostedt (Red Hat) 		err = -ENOMEM;
4064282d606SSteven Rostedt (Red Hat) 		goto fail;
4074282d606SSteven Rostedt (Red Hat) 	}
4084282d606SSteven Rostedt (Red Hat) 
4094282d606SSteven Rostedt (Red Hat) 	err = tracefs_parse_options(data, &fsi->mount_opts);
4104282d606SSteven Rostedt (Red Hat) 	if (err)
4114282d606SSteven Rostedt (Red Hat) 		goto fail;
4124282d606SSteven Rostedt (Red Hat) 
4134282d606SSteven Rostedt (Red Hat) 	err  =  simple_fill_super(sb, TRACEFS_MAGIC, trace_files);
4144282d606SSteven Rostedt (Red Hat) 	if (err)
4154282d606SSteven Rostedt (Red Hat) 		goto fail;
4164282d606SSteven Rostedt (Red Hat) 
4174282d606SSteven Rostedt (Red Hat) 	sb->s_op = &tracefs_super_operations;
41827152bceSAjay Kaher 	sb->s_d_op = &tracefs_dentry_operations;
4194282d606SSteven Rostedt (Red Hat) 
42047311db8SBrian Norris 	tracefs_apply_options(sb, false);
4214282d606SSteven Rostedt (Red Hat) 
4224282d606SSteven Rostedt (Red Hat) 	return 0;
4234282d606SSteven Rostedt (Red Hat) 
4244282d606SSteven Rostedt (Red Hat) fail:
4254282d606SSteven Rostedt (Red Hat) 	kfree(fsi);
4264282d606SSteven Rostedt (Red Hat) 	sb->s_fs_info = NULL;
4274282d606SSteven Rostedt (Red Hat) 	return err;
4284282d606SSteven Rostedt (Red Hat) }
4294282d606SSteven Rostedt (Red Hat) 
4304282d606SSteven Rostedt (Red Hat) static struct dentry *trace_mount(struct file_system_type *fs_type,
4314282d606SSteven Rostedt (Red Hat) 			int flags, const char *dev_name,
4324282d606SSteven Rostedt (Red Hat) 			void *data)
4334282d606SSteven Rostedt (Red Hat) {
4344282d606SSteven Rostedt (Red Hat) 	return mount_single(fs_type, flags, data, trace_fill_super);
4354282d606SSteven Rostedt (Red Hat) }
4364282d606SSteven Rostedt (Red Hat) 
4374282d606SSteven Rostedt (Red Hat) static struct file_system_type trace_fs_type = {
4384282d606SSteven Rostedt (Red Hat) 	.owner =	THIS_MODULE,
4394282d606SSteven Rostedt (Red Hat) 	.name =		"tracefs",
4404282d606SSteven Rostedt (Red Hat) 	.mount =	trace_mount,
4414282d606SSteven Rostedt (Red Hat) 	.kill_sb =	kill_litter_super,
4424282d606SSteven Rostedt (Red Hat) };
4434282d606SSteven Rostedt (Red Hat) MODULE_ALIAS_FS("tracefs");
4444282d606SSteven Rostedt (Red Hat) 
4452c6b6b10SAjay Kaher struct dentry *tracefs_start_creating(const char *name, struct dentry *parent)
4464282d606SSteven Rostedt (Red Hat) {
4474282d606SSteven Rostedt (Red Hat) 	struct dentry *dentry;
4484282d606SSteven Rostedt (Red Hat) 	int error;
4494282d606SSteven Rostedt (Red Hat) 
4504282d606SSteven Rostedt (Red Hat) 	pr_debug("tracefs: creating file '%s'\n",name);
4514282d606SSteven Rostedt (Red Hat) 
4524282d606SSteven Rostedt (Red Hat) 	error = simple_pin_fs(&trace_fs_type, &tracefs_mount,
4534282d606SSteven Rostedt (Red Hat) 			      &tracefs_mount_count);
4544282d606SSteven Rostedt (Red Hat) 	if (error)
4554282d606SSteven Rostedt (Red Hat) 		return ERR_PTR(error);
4564282d606SSteven Rostedt (Red Hat) 
4574282d606SSteven Rostedt (Red Hat) 	/* If the parent is not specified, we create it in the root.
4584282d606SSteven Rostedt (Red Hat) 	 * We need the root dentry to do this, which is in the super
4594282d606SSteven Rostedt (Red Hat) 	 * block. A pointer to that is in the struct vfsmount that we
4604282d606SSteven Rostedt (Red Hat) 	 * have around.
4614282d606SSteven Rostedt (Red Hat) 	 */
4624282d606SSteven Rostedt (Red Hat) 	if (!parent)
4634282d606SSteven Rostedt (Red Hat) 		parent = tracefs_mount->mnt_root;
4644282d606SSteven Rostedt (Red Hat) 
465ee34c52cSSteven Rostedt (VMware) 	inode_lock(d_inode(parent));
466ee34c52cSSteven Rostedt (VMware) 	if (unlikely(IS_DEADDIR(d_inode(parent))))
467a3d1e7ebSAl Viro 		dentry = ERR_PTR(-ENOENT);
468a3d1e7ebSAl Viro 	else
4694282d606SSteven Rostedt (Red Hat) 		dentry = lookup_one_len(name, parent, strlen(name));
470ee34c52cSSteven Rostedt (VMware) 	if (!IS_ERR(dentry) && d_inode(dentry)) {
4714282d606SSteven Rostedt (Red Hat) 		dput(dentry);
4724282d606SSteven Rostedt (Red Hat) 		dentry = ERR_PTR(-EEXIST);
4734282d606SSteven Rostedt (Red Hat) 	}
474d227c3aeSDaniel Borkmann 
475d227c3aeSDaniel Borkmann 	if (IS_ERR(dentry)) {
476ee34c52cSSteven Rostedt (VMware) 		inode_unlock(d_inode(parent));
477d227c3aeSDaniel Borkmann 		simple_release_fs(&tracefs_mount, &tracefs_mount_count);
478d227c3aeSDaniel Borkmann 	}
479d227c3aeSDaniel Borkmann 
4804282d606SSteven Rostedt (Red Hat) 	return dentry;
4814282d606SSteven Rostedt (Red Hat) }
4824282d606SSteven Rostedt (Red Hat) 
4832c6b6b10SAjay Kaher struct dentry *tracefs_failed_creating(struct dentry *dentry)
4844282d606SSteven Rostedt (Red Hat) {
485ee34c52cSSteven Rostedt (VMware) 	inode_unlock(d_inode(dentry->d_parent));
4864282d606SSteven Rostedt (Red Hat) 	dput(dentry);
4874282d606SSteven Rostedt (Red Hat) 	simple_release_fs(&tracefs_mount, &tracefs_mount_count);
4884282d606SSteven Rostedt (Red Hat) 	return NULL;
4894282d606SSteven Rostedt (Red Hat) }
4904282d606SSteven Rostedt (Red Hat) 
4912c6b6b10SAjay Kaher struct dentry *tracefs_end_creating(struct dentry *dentry)
4924282d606SSteven Rostedt (Red Hat) {
493ee34c52cSSteven Rostedt (VMware) 	inode_unlock(d_inode(dentry->d_parent));
4944282d606SSteven Rostedt (Red Hat) 	return dentry;
4954282d606SSteven Rostedt (Red Hat) }
4964282d606SSteven Rostedt (Red Hat) 
4974282d606SSteven Rostedt (Red Hat) /**
498a3760079SAjay Kaher  * eventfs_start_creating - start the process of creating a dentry
499a3760079SAjay Kaher  * @name: Name of the file created for the dentry
500a3760079SAjay Kaher  * @parent: The parent dentry where this dentry will be created
501a3760079SAjay Kaher  *
502a3760079SAjay Kaher  * This is a simple helper function for the dynamically created eventfs
503a3760079SAjay Kaher  * files. When the directory of the eventfs files are accessed, their
504a3760079SAjay Kaher  * dentries are created on the fly. This function is used to start that
505a3760079SAjay Kaher  * process.
506a3760079SAjay Kaher  */
507a3760079SAjay Kaher struct dentry *eventfs_start_creating(const char *name, struct dentry *parent)
508a3760079SAjay Kaher {
509a3760079SAjay Kaher 	struct dentry *dentry;
510a3760079SAjay Kaher 	int error;
511a3760079SAjay Kaher 
512a3760079SAjay Kaher 	error = simple_pin_fs(&trace_fs_type, &tracefs_mount,
513a3760079SAjay Kaher 			      &tracefs_mount_count);
514a3760079SAjay Kaher 	if (error)
515a3760079SAjay Kaher 		return ERR_PTR(error);
516a3760079SAjay Kaher 
517a3760079SAjay Kaher 	/*
518a3760079SAjay Kaher 	 * If the parent is not specified, we create it in the root.
519a3760079SAjay Kaher 	 * We need the root dentry to do this, which is in the super
520a3760079SAjay Kaher 	 * block. A pointer to that is in the struct vfsmount that we
521a3760079SAjay Kaher 	 * have around.
522a3760079SAjay Kaher 	 */
523a3760079SAjay Kaher 	if (!parent)
524a3760079SAjay Kaher 		parent = tracefs_mount->mnt_root;
525a3760079SAjay Kaher 
526a3760079SAjay Kaher 	if (unlikely(IS_DEADDIR(parent->d_inode)))
527a3760079SAjay Kaher 		dentry = ERR_PTR(-ENOENT);
528a3760079SAjay Kaher 	else
529a3760079SAjay Kaher 		dentry = lookup_one_len(name, parent, strlen(name));
530a3760079SAjay Kaher 
531a3760079SAjay Kaher 	if (!IS_ERR(dentry) && dentry->d_inode) {
532a3760079SAjay Kaher 		dput(dentry);
533a3760079SAjay Kaher 		dentry = ERR_PTR(-EEXIST);
534a3760079SAjay Kaher 	}
535a3760079SAjay Kaher 
536a3760079SAjay Kaher 	if (IS_ERR(dentry))
537a3760079SAjay Kaher 		simple_release_fs(&tracefs_mount, &tracefs_mount_count);
538a3760079SAjay Kaher 
539a3760079SAjay Kaher 	return dentry;
540a3760079SAjay Kaher }
541a3760079SAjay Kaher 
542a3760079SAjay Kaher /**
543a3760079SAjay Kaher  * eventfs_failed_creating - clean up a failed eventfs dentry creation
544a3760079SAjay Kaher  * @dentry: The dentry to clean up
545a3760079SAjay Kaher  *
546a3760079SAjay Kaher  * If after calling eventfs_start_creating(), a failure is detected, the
547a3760079SAjay Kaher  * resources created by eventfs_start_creating() needs to be cleaned up. In
548a3760079SAjay Kaher  * that case, this function should be called to perform that clean up.
549a3760079SAjay Kaher  */
550a3760079SAjay Kaher struct dentry *eventfs_failed_creating(struct dentry *dentry)
551a3760079SAjay Kaher {
552a3760079SAjay Kaher 	dput(dentry);
553a3760079SAjay Kaher 	simple_release_fs(&tracefs_mount, &tracefs_mount_count);
554a3760079SAjay Kaher 	return NULL;
555a3760079SAjay Kaher }
556a3760079SAjay Kaher 
557a3760079SAjay Kaher /**
558a3760079SAjay Kaher  * eventfs_end_creating - Finish the process of creating a eventfs dentry
559a3760079SAjay Kaher  * @dentry: The dentry that has successfully been created.
560a3760079SAjay Kaher  *
561a3760079SAjay Kaher  * This function is currently just a place holder to match
562a3760079SAjay Kaher  * eventfs_start_creating(). In case any synchronization needs to be added,
563a3760079SAjay Kaher  * this function will be used to implement that without having to modify
564a3760079SAjay Kaher  * the callers of eventfs_start_creating().
565a3760079SAjay Kaher  */
566a3760079SAjay Kaher struct dentry *eventfs_end_creating(struct dentry *dentry)
567a3760079SAjay Kaher {
568a3760079SAjay Kaher 	return dentry;
569a3760079SAjay Kaher }
570a3760079SAjay Kaher 
571a3760079SAjay Kaher /**
5724282d606SSteven Rostedt (Red Hat)  * tracefs_create_file - create a file in the tracefs filesystem
5734282d606SSteven Rostedt (Red Hat)  * @name: a pointer to a string containing the name of the file to create.
5744282d606SSteven Rostedt (Red Hat)  * @mode: the permission that the file should have.
5754282d606SSteven Rostedt (Red Hat)  * @parent: a pointer to the parent dentry for this file.  This should be a
5764282d606SSteven Rostedt (Red Hat)  *          directory dentry if set.  If this parameter is NULL, then the
5774282d606SSteven Rostedt (Red Hat)  *          file will be created in the root of the tracefs filesystem.
5784282d606SSteven Rostedt (Red Hat)  * @data: a pointer to something that the caller will want to get to later
5794282d606SSteven Rostedt (Red Hat)  *        on.  The inode.i_private pointer will point to this value on
5804282d606SSteven Rostedt (Red Hat)  *        the open() call.
5814282d606SSteven Rostedt (Red Hat)  * @fops: a pointer to a struct file_operations that should be used for
5824282d606SSteven Rostedt (Red Hat)  *        this file.
5834282d606SSteven Rostedt (Red Hat)  *
5844282d606SSteven Rostedt (Red Hat)  * This is the basic "create a file" function for tracefs.  It allows for a
5854282d606SSteven Rostedt (Red Hat)  * wide range of flexibility in creating a file, or a directory (if you want
5864282d606SSteven Rostedt (Red Hat)  * to create a directory, the tracefs_create_dir() function is
5874282d606SSteven Rostedt (Red Hat)  * recommended to be used instead.)
5884282d606SSteven Rostedt (Red Hat)  *
5894282d606SSteven Rostedt (Red Hat)  * This function will return a pointer to a dentry if it succeeds.  This
5904282d606SSteven Rostedt (Red Hat)  * pointer must be passed to the tracefs_remove() function when the file is
5914282d606SSteven Rostedt (Red Hat)  * to be removed (no automatic cleanup happens if your module is unloaded,
5924282d606SSteven Rostedt (Red Hat)  * you are responsible here.)  If an error occurs, %NULL will be returned.
5934282d606SSteven Rostedt (Red Hat)  *
5944282d606SSteven Rostedt (Red Hat)  * If tracefs is not enabled in the kernel, the value -%ENODEV will be
5954282d606SSteven Rostedt (Red Hat)  * returned.
5964282d606SSteven Rostedt (Red Hat)  */
5974282d606SSteven Rostedt (Red Hat) struct dentry *tracefs_create_file(const char *name, umode_t mode,
5984282d606SSteven Rostedt (Red Hat) 				   struct dentry *parent, void *data,
5994282d606SSteven Rostedt (Red Hat) 				   const struct file_operations *fops)
6004282d606SSteven Rostedt (Red Hat) {
6014282d606SSteven Rostedt (Red Hat) 	struct dentry *dentry;
6024282d606SSteven Rostedt (Red Hat) 	struct inode *inode;
6034282d606SSteven Rostedt (Red Hat) 
604bf8e6021SSteven Rostedt (VMware) 	if (security_locked_down(LOCKDOWN_TRACEFS))
605bf8e6021SSteven Rostedt (VMware) 		return NULL;
606bf8e6021SSteven Rostedt (VMware) 
6074282d606SSteven Rostedt (Red Hat) 	if (!(mode & S_IFMT))
6084282d606SSteven Rostedt (Red Hat) 		mode |= S_IFREG;
6094282d606SSteven Rostedt (Red Hat) 	BUG_ON(!S_ISREG(mode));
6102c6b6b10SAjay Kaher 	dentry = tracefs_start_creating(name, parent);
6114282d606SSteven Rostedt (Red Hat) 
6124282d606SSteven Rostedt (Red Hat) 	if (IS_ERR(dentry))
6134282d606SSteven Rostedt (Red Hat) 		return NULL;
6144282d606SSteven Rostedt (Red Hat) 
6154282d606SSteven Rostedt (Red Hat) 	inode = tracefs_get_inode(dentry->d_sb);
6164282d606SSteven Rostedt (Red Hat) 	if (unlikely(!inode))
6172c6b6b10SAjay Kaher 		return tracefs_failed_creating(dentry);
6184282d606SSteven Rostedt (Red Hat) 
6194282d606SSteven Rostedt (Red Hat) 	inode->i_mode = mode;
6203ed270b1SSteven Rostedt (VMware) 	inode->i_fop = fops ? fops : &tracefs_file_operations;
6214282d606SSteven Rostedt (Red Hat) 	inode->i_private = data;
622ee7f3666SSteven Rostedt (VMware) 	inode->i_uid = d_inode(dentry->d_parent)->i_uid;
623ee7f3666SSteven Rostedt (VMware) 	inode->i_gid = d_inode(dentry->d_parent)->i_gid;
6244282d606SSteven Rostedt (Red Hat) 	d_instantiate(dentry, inode);
625ee34c52cSSteven Rostedt (VMware) 	fsnotify_create(d_inode(dentry->d_parent), dentry);
6262c6b6b10SAjay Kaher 	return tracefs_end_creating(dentry);
6274282d606SSteven Rostedt (Red Hat) }
6284282d606SSteven Rostedt (Red Hat) 
629eae47358SSteven Rostedt (Red Hat) static struct dentry *__create_dir(const char *name, struct dentry *parent,
630eae47358SSteven Rostedt (Red Hat) 				   const struct inode_operations *ops)
631eae47358SSteven Rostedt (Red Hat) {
6322c6b6b10SAjay Kaher 	struct dentry *dentry = tracefs_start_creating(name, parent);
633eae47358SSteven Rostedt (Red Hat) 	struct inode *inode;
634eae47358SSteven Rostedt (Red Hat) 
635eae47358SSteven Rostedt (Red Hat) 	if (IS_ERR(dentry))
636eae47358SSteven Rostedt (Red Hat) 		return NULL;
637eae47358SSteven Rostedt (Red Hat) 
638eae47358SSteven Rostedt (Red Hat) 	inode = tracefs_get_inode(dentry->d_sb);
639eae47358SSteven Rostedt (Red Hat) 	if (unlikely(!inode))
6402c6b6b10SAjay Kaher 		return tracefs_failed_creating(dentry);
641eae47358SSteven Rostedt (Red Hat) 
64249d67e44SSteven Rostedt (VMware) 	/* Do not set bits for OTH */
64349d67e44SSteven Rostedt (VMware) 	inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP;
644eae47358SSteven Rostedt (Red Hat) 	inode->i_op = ops;
645eae47358SSteven Rostedt (Red Hat) 	inode->i_fop = &simple_dir_operations;
646ee7f3666SSteven Rostedt (VMware) 	inode->i_uid = d_inode(dentry->d_parent)->i_uid;
647ee7f3666SSteven Rostedt (VMware) 	inode->i_gid = d_inode(dentry->d_parent)->i_gid;
648eae47358SSteven Rostedt (Red Hat) 
649eae47358SSteven Rostedt (Red Hat) 	/* directory inodes start off with i_nlink == 2 (for "." entry) */
650eae47358SSteven Rostedt (Red Hat) 	inc_nlink(inode);
651eae47358SSteven Rostedt (Red Hat) 	d_instantiate(dentry, inode);
652ee34c52cSSteven Rostedt (VMware) 	inc_nlink(d_inode(dentry->d_parent));
653ee34c52cSSteven Rostedt (VMware) 	fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
6542c6b6b10SAjay Kaher 	return tracefs_end_creating(dentry);
655eae47358SSteven Rostedt (Red Hat) }
656eae47358SSteven Rostedt (Red Hat) 
6574282d606SSteven Rostedt (Red Hat) /**
6584282d606SSteven Rostedt (Red Hat)  * tracefs_create_dir - create a directory in the tracefs filesystem
6594282d606SSteven Rostedt (Red Hat)  * @name: a pointer to a string containing the name of the directory to
6604282d606SSteven Rostedt (Red Hat)  *        create.
6614282d606SSteven Rostedt (Red Hat)  * @parent: a pointer to the parent dentry for this file.  This should be a
6624282d606SSteven Rostedt (Red Hat)  *          directory dentry if set.  If this parameter is NULL, then the
6634282d606SSteven Rostedt (Red Hat)  *          directory will be created in the root of the tracefs filesystem.
6644282d606SSteven Rostedt (Red Hat)  *
6654282d606SSteven Rostedt (Red Hat)  * This function creates a directory in tracefs with the given name.
6664282d606SSteven Rostedt (Red Hat)  *
6674282d606SSteven Rostedt (Red Hat)  * This function will return a pointer to a dentry if it succeeds.  This
6684282d606SSteven Rostedt (Red Hat)  * pointer must be passed to the tracefs_remove() function when the file is
6694282d606SSteven Rostedt (Red Hat)  * to be removed. If an error occurs, %NULL will be returned.
6704282d606SSteven Rostedt (Red Hat)  *
6714282d606SSteven Rostedt (Red Hat)  * If tracing is not enabled in the kernel, the value -%ENODEV will be
6724282d606SSteven Rostedt (Red Hat)  * returned.
6734282d606SSteven Rostedt (Red Hat)  */
6744282d606SSteven Rostedt (Red Hat) struct dentry *tracefs_create_dir(const char *name, struct dentry *parent)
6754282d606SSteven Rostedt (Red Hat) {
676*51aab5ffSSteven Rostedt (Google) 	if (security_locked_down(LOCKDOWN_TRACEFS))
677*51aab5ffSSteven Rostedt (Google) 		return NULL;
678*51aab5ffSSteven Rostedt (Google) 
679eae47358SSteven Rostedt (Red Hat) 	return __create_dir(name, parent, &simple_dir_inode_operations);
680eae47358SSteven Rostedt (Red Hat) }
6814282d606SSteven Rostedt (Red Hat) 
682eae47358SSteven Rostedt (Red Hat) /**
683eae47358SSteven Rostedt (Red Hat)  * tracefs_create_instance_dir - create the tracing instances directory
684eae47358SSteven Rostedt (Red Hat)  * @name: The name of the instances directory to create
685eae47358SSteven Rostedt (Red Hat)  * @parent: The parent directory that the instances directory will exist
686eae47358SSteven Rostedt (Red Hat)  * @mkdir: The function to call when a mkdir is performed.
687eae47358SSteven Rostedt (Red Hat)  * @rmdir: The function to call when a rmdir is performed.
688eae47358SSteven Rostedt (Red Hat)  *
689eae47358SSteven Rostedt (Red Hat)  * Only one instances directory is allowed.
690eae47358SSteven Rostedt (Red Hat)  *
69193a8c044SXiang wangx  * The instances directory is special as it allows for mkdir and rmdir
692eae47358SSteven Rostedt (Red Hat)  * to be done by userspace. When a mkdir or rmdir is performed, the inode
693f2cc020dSIngo Molnar  * locks are released and the methods passed in (@mkdir and @rmdir) are
694eae47358SSteven Rostedt (Red Hat)  * called without locks and with the name of the directory being created
695eae47358SSteven Rostedt (Red Hat)  * within the instances directory.
696eae47358SSteven Rostedt (Red Hat)  *
697eae47358SSteven Rostedt (Red Hat)  * Returns the dentry of the instances directory.
698eae47358SSteven Rostedt (Red Hat)  */
6995248ee85SZubin Mithra __init struct dentry *tracefs_create_instance_dir(const char *name,
7005248ee85SZubin Mithra 					  struct dentry *parent,
701eae47358SSteven Rostedt (Red Hat) 					  int (*mkdir)(const char *name),
702eae47358SSteven Rostedt (Red Hat) 					  int (*rmdir)(const char *name))
703eae47358SSteven Rostedt (Red Hat) {
704eae47358SSteven Rostedt (Red Hat) 	struct dentry *dentry;
705eae47358SSteven Rostedt (Red Hat) 
706eae47358SSteven Rostedt (Red Hat) 	/* Only allow one instance of the instances directory. */
707eae47358SSteven Rostedt (Red Hat) 	if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir))
7084282d606SSteven Rostedt (Red Hat) 		return NULL;
7094282d606SSteven Rostedt (Red Hat) 
710eae47358SSteven Rostedt (Red Hat) 	dentry = __create_dir(name, parent, &tracefs_dir_inode_operations);
711eae47358SSteven Rostedt (Red Hat) 	if (!dentry)
712eae47358SSteven Rostedt (Red Hat) 		return NULL;
7134282d606SSteven Rostedt (Red Hat) 
714eae47358SSteven Rostedt (Red Hat) 	tracefs_ops.mkdir = mkdir;
715eae47358SSteven Rostedt (Red Hat) 	tracefs_ops.rmdir = rmdir;
7164282d606SSteven Rostedt (Red Hat) 
717eae47358SSteven Rostedt (Red Hat) 	return dentry;
7184282d606SSteven Rostedt (Red Hat) }
7194282d606SSteven Rostedt (Red Hat) 
720a3d1e7ebSAl Viro static void remove_one(struct dentry *victim)
7214282d606SSteven Rostedt (Red Hat) {
7224282d606SSteven Rostedt (Red Hat) 	simple_release_fs(&tracefs_mount, &tracefs_mount_count);
7234282d606SSteven Rostedt (Red Hat) }
7244282d606SSteven Rostedt (Red Hat) 
7254282d606SSteven Rostedt (Red Hat) /**
726a3d1e7ebSAl Viro  * tracefs_remove - recursively removes a directory
7274282d606SSteven Rostedt (Red Hat)  * @dentry: a pointer to a the dentry of the directory to be removed.
7284282d606SSteven Rostedt (Red Hat)  *
7294282d606SSteven Rostedt (Red Hat)  * This function recursively removes a directory tree in tracefs that
7304282d606SSteven Rostedt (Red Hat)  * was previously created with a call to another tracefs function
7314282d606SSteven Rostedt (Red Hat)  * (like tracefs_create_file() or variants thereof.)
7324282d606SSteven Rostedt (Red Hat)  */
733a3d1e7ebSAl Viro void tracefs_remove(struct dentry *dentry)
7344282d606SSteven Rostedt (Red Hat) {
7354282d606SSteven Rostedt (Red Hat) 	if (IS_ERR_OR_NULL(dentry))
7364282d606SSteven Rostedt (Red Hat) 		return;
7374282d606SSteven Rostedt (Red Hat) 
738a3d1e7ebSAl Viro 	simple_pin_fs(&trace_fs_type, &tracefs_mount, &tracefs_mount_count);
739a3d1e7ebSAl Viro 	simple_recursive_removal(dentry, remove_one);
7404282d606SSteven Rostedt (Red Hat) 	simple_release_fs(&tracefs_mount, &tracefs_mount_count);
7414282d606SSteven Rostedt (Red Hat) }
7424282d606SSteven Rostedt (Red Hat) 
7434282d606SSteven Rostedt (Red Hat) /**
7444282d606SSteven Rostedt (Red Hat)  * tracefs_initialized - Tells whether tracefs has been registered
7454282d606SSteven Rostedt (Red Hat)  */
7464282d606SSteven Rostedt (Red Hat) bool tracefs_initialized(void)
7474282d606SSteven Rostedt (Red Hat) {
7484282d606SSteven Rostedt (Red Hat) 	return tracefs_registered;
7494282d606SSteven Rostedt (Red Hat) }
7504282d606SSteven Rostedt (Red Hat) 
751ba37ff75SAjay Kaher static void init_once(void *foo)
752ba37ff75SAjay Kaher {
753ba37ff75SAjay Kaher 	struct tracefs_inode *ti = (struct tracefs_inode *) foo;
754ba37ff75SAjay Kaher 
755ba37ff75SAjay Kaher 	inode_init_once(&ti->vfs_inode);
756ba37ff75SAjay Kaher }
757ba37ff75SAjay Kaher 
7584282d606SSteven Rostedt (Red Hat) static int __init tracefs_init(void)
7594282d606SSteven Rostedt (Red Hat) {
7604282d606SSteven Rostedt (Red Hat) 	int retval;
7614282d606SSteven Rostedt (Red Hat) 
762ba37ff75SAjay Kaher 	tracefs_inode_cachep = kmem_cache_create("tracefs_inode_cache",
763ba37ff75SAjay Kaher 						 sizeof(struct tracefs_inode),
764ba37ff75SAjay Kaher 						 0, (SLAB_RECLAIM_ACCOUNT|
765ba37ff75SAjay Kaher 						     SLAB_MEM_SPREAD|
766ba37ff75SAjay Kaher 						     SLAB_ACCOUNT),
767ba37ff75SAjay Kaher 						 init_once);
768ba37ff75SAjay Kaher 	if (!tracefs_inode_cachep)
769ba37ff75SAjay Kaher 		return -ENOMEM;
770ba37ff75SAjay Kaher 
771f9bb4882SEric W. Biederman 	retval = sysfs_create_mount_point(kernel_kobj, "tracing");
772f9bb4882SEric W. Biederman 	if (retval)
773cc31004aSSteven Rostedt (Red Hat) 		return -EINVAL;
774cc31004aSSteven Rostedt (Red Hat) 
7754282d606SSteven Rostedt (Red Hat) 	retval = register_filesystem(&trace_fs_type);
7764282d606SSteven Rostedt (Red Hat) 	if (!retval)
7774282d606SSteven Rostedt (Red Hat) 		tracefs_registered = true;
7784282d606SSteven Rostedt (Red Hat) 
7794282d606SSteven Rostedt (Red Hat) 	return retval;
7804282d606SSteven Rostedt (Red Hat) }
7814282d606SSteven Rostedt (Red Hat) core_initcall(tracefs_init);
782