1c1504e51SAjay Kaher // SPDX-License-Identifier: GPL-2.0-only
2c1504e51SAjay Kaher /*
3c1504e51SAjay Kaher * event_inode.c - part of tracefs, a pseudo file system for activating tracing
4c1504e51SAjay Kaher *
535ee34c0SSteven Rostedt (Google) * Copyright (C) 2020-23 VMware Inc, author: Steven Rostedt <rostedt@goodmis.org>
6c1504e51SAjay Kaher * Copyright (C) 2020-23 VMware Inc, author: Ajay Kaher <akaher@vmware.com>
735ee34c0SSteven Rostedt (Google) * Copyright (C) 2023 Google, author: Steven Rostedt <rostedt@goodmis.org>
8c1504e51SAjay Kaher *
9c1504e51SAjay Kaher * eventfs is used to dynamically create inodes and dentries based on the
10c1504e51SAjay Kaher * meta data provided by the tracing system.
11c1504e51SAjay Kaher *
12c1504e51SAjay Kaher * eventfs stores the meta-data of files/dirs and holds off on creating
13c1504e51SAjay Kaher * inodes/dentries of the files. When accessed, the eventfs will create the
14c1504e51SAjay Kaher * inodes/dentries in a just-in-time (JIT) manner. The eventfs will clean up
15c1504e51SAjay Kaher * and delete the inodes/dentries when they are no longer referenced.
16c1504e51SAjay Kaher */
17c1504e51SAjay Kaher #include <linux/fsnotify.h>
18c1504e51SAjay Kaher #include <linux/fs.h>
19c1504e51SAjay Kaher #include <linux/namei.h>
20c1504e51SAjay Kaher #include <linux/workqueue.h>
21c1504e51SAjay Kaher #include <linux/security.h>
22c1504e51SAjay Kaher #include <linux/tracefs.h>
23c1504e51SAjay Kaher #include <linux/kref.h>
24c1504e51SAjay Kaher #include <linux/delay.h>
25c1504e51SAjay Kaher #include "internal.h"
26c1504e51SAjay Kaher
27d2a632aeSSteven Rostedt (Google) /*
28d2a632aeSSteven Rostedt (Google) * eventfs_mutex protects the eventfs_inode (ei) dentry. Any access
29d2a632aeSSteven Rostedt (Google) * to the ei->dentry must be done under this mutex and after checking
30b1391e36SSteven Rostedt (Google) * if ei->is_freed is not set. When ei->is_freed is set, the dentry
31b1391e36SSteven Rostedt (Google) * is on its way to being freed after the last dput() is made on it.
32d2a632aeSSteven Rostedt (Google) */
33c1504e51SAjay Kaher static DEFINE_MUTEX(eventfs_mutex);
34d2a632aeSSteven Rostedt (Google)
354e8731d2SSteven Rostedt (Google) /* Choose something "unique" ;-) */
364e8731d2SSteven Rostedt (Google) #define EVENTFS_FILE_INODE_INO 0x12c4e37
379a187657SSteven Rostedt (Google)
38e5c80b23SSteven Rostedt (Google) struct eventfs_root_inode {
39e5c80b23SSteven Rostedt (Google) struct eventfs_inode ei;
4051a2049aSSteven Rostedt (Google) struct inode *parent_inode;
41e5c80b23SSteven Rostedt (Google) struct dentry *events_dir;
42e5c80b23SSteven Rostedt (Google) };
43e5c80b23SSteven Rostedt (Google)
get_root_inode(struct eventfs_inode * ei)44e5c80b23SSteven Rostedt (Google) static struct eventfs_root_inode *get_root_inode(struct eventfs_inode *ei)
45e5c80b23SSteven Rostedt (Google) {
46e5c80b23SSteven Rostedt (Google) WARN_ON_ONCE(!ei->is_events);
47e5c80b23SSteven Rostedt (Google) return container_of(ei, struct eventfs_root_inode, ei);
48e5c80b23SSteven Rostedt (Google) }
49e5c80b23SSteven Rostedt (Google)
509a187657SSteven Rostedt (Google) /* Just try to make something consistent and unique */
eventfs_dir_ino(struct eventfs_inode * ei)519a187657SSteven Rostedt (Google) static int eventfs_dir_ino(struct eventfs_inode *ei)
529a187657SSteven Rostedt (Google) {
534e84ead3SSteven Rostedt (Google) if (!ei->ino) {
549a187657SSteven Rostedt (Google) ei->ino = get_next_ino();
554e84ead3SSteven Rostedt (Google) /* Must not have the file inode number */
564e84ead3SSteven Rostedt (Google) if (ei->ino == EVENTFS_FILE_INODE_INO)
574e84ead3SSteven Rostedt (Google) ei->ino = get_next_ino();
584e84ead3SSteven Rostedt (Google) }
599a187657SSteven Rostedt (Google)
609a187657SSteven Rostedt (Google) return ei->ino;
619a187657SSteven Rostedt (Google) }
624e8731d2SSteven Rostedt (Google)
63d2a632aeSSteven Rostedt (Google) /*
64d2a632aeSSteven Rostedt (Google) * The eventfs_inode (ei) itself is protected by SRCU. It is released from
65d2a632aeSSteven Rostedt (Google) * its parent's list and will have is_freed set (under eventfs_mutex).
66b1391e36SSteven Rostedt (Google) * After the SRCU grace period is over and the last dput() is called
67b1391e36SSteven Rostedt (Google) * the ei is freed.
68d2a632aeSSteven Rostedt (Google) */
6963940449SAjay Kaher DEFINE_STATIC_SRCU(eventfs_srcu);
7063940449SAjay Kaher
7132f4c167SSteven Rostedt (Google) /* Mode is unsigned short, use the upper bits for flags */
7232f4c167SSteven Rostedt (Google) enum {
7332f4c167SSteven Rostedt (Google) EVENTFS_SAVE_MODE = BIT(16),
7432f4c167SSteven Rostedt (Google) EVENTFS_SAVE_UID = BIT(17),
7532f4c167SSteven Rostedt (Google) EVENTFS_SAVE_GID = BIT(18),
7632f4c167SSteven Rostedt (Google) };
7732f4c167SSteven Rostedt (Google)
7832f4c167SSteven Rostedt (Google) #define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1)
7932f4c167SSteven Rostedt (Google)
free_ei_rcu(struct rcu_head * rcu)80281eaee3SSteven Rostedt (Google) static void free_ei_rcu(struct rcu_head *rcu)
81281eaee3SSteven Rostedt (Google) {
82281eaee3SSteven Rostedt (Google) struct eventfs_inode *ei = container_of(rcu, struct eventfs_inode, rcu);
83281eaee3SSteven Rostedt (Google) struct eventfs_root_inode *rei;
84281eaee3SSteven Rostedt (Google)
85281eaee3SSteven Rostedt (Google) kfree(ei->entry_attrs);
86281eaee3SSteven Rostedt (Google) kfree_const(ei->name);
87281eaee3SSteven Rostedt (Google) if (ei->is_events) {
88281eaee3SSteven Rostedt (Google) rei = get_root_inode(ei);
89281eaee3SSteven Rostedt (Google) kfree(rei);
90281eaee3SSteven Rostedt (Google) } else {
91281eaee3SSteven Rostedt (Google) kfree(ei);
92281eaee3SSteven Rostedt (Google) }
93281eaee3SSteven Rostedt (Google) }
94281eaee3SSteven Rostedt (Google)
955dfb0410SLinus Torvalds /*
965dfb0410SLinus Torvalds * eventfs_inode reference count management.
975dfb0410SLinus Torvalds *
985dfb0410SLinus Torvalds * NOTE! We count only references from dentries, in the
995dfb0410SLinus Torvalds * form 'dentry->d_fsdata'. There are also references from
1005dfb0410SLinus Torvalds * directory inodes ('ti->private'), but the dentry reference
1015dfb0410SLinus Torvalds * count is always a superset of the inode reference count.
1025dfb0410SLinus Torvalds */
release_ei(struct kref * ref)1035dfb0410SLinus Torvalds static void release_ei(struct kref *ref)
1045dfb0410SLinus Torvalds {
1055dfb0410SLinus Torvalds struct eventfs_inode *ei = container_of(ref, struct eventfs_inode, kref);
10614aa4f3eSSteven Rostedt (Google) const struct eventfs_entry *entry;
1075c3ea7dfSSteven Rostedt (Google)
1085c3ea7dfSSteven Rostedt (Google) WARN_ON_ONCE(!ei->is_freed);
1095c3ea7dfSSteven Rostedt (Google)
11014aa4f3eSSteven Rostedt (Google) for (int i = 0; i < ei->nr_entries; i++) {
11114aa4f3eSSteven Rostedt (Google) entry = &ei->entries[i];
11214aa4f3eSSteven Rostedt (Google) if (entry->release)
11314aa4f3eSSteven Rostedt (Google) entry->release(entry->name, ei->data);
11414aa4f3eSSteven Rostedt (Google) }
11514aa4f3eSSteven Rostedt (Google)
1162ef3d120SMathias Krause call_srcu(&eventfs_srcu, &ei->rcu, free_ei_rcu);
117e5c80b23SSteven Rostedt (Google) }
1185dfb0410SLinus Torvalds
put_ei(struct eventfs_inode * ei)1195dfb0410SLinus Torvalds static inline void put_ei(struct eventfs_inode *ei)
1205dfb0410SLinus Torvalds {
1215dfb0410SLinus Torvalds if (ei)
1225dfb0410SLinus Torvalds kref_put(&ei->kref, release_ei);
1235dfb0410SLinus Torvalds }
1245dfb0410SLinus Torvalds
free_ei(struct eventfs_inode * ei)1255c3ea7dfSSteven Rostedt (Google) static inline void free_ei(struct eventfs_inode *ei)
1265c3ea7dfSSteven Rostedt (Google) {
1275c3ea7dfSSteven Rostedt (Google) if (ei) {
1285c3ea7dfSSteven Rostedt (Google) ei->is_freed = 1;
1295c3ea7dfSSteven Rostedt (Google) put_ei(ei);
1305c3ea7dfSSteven Rostedt (Google) }
1315c3ea7dfSSteven Rostedt (Google) }
1325c3ea7dfSSteven Rostedt (Google)
13314aa4f3eSSteven Rostedt (Google) /*
13414aa4f3eSSteven Rostedt (Google) * Called when creation of an ei fails, do not call release() functions.
13514aa4f3eSSteven Rostedt (Google) */
cleanup_ei(struct eventfs_inode * ei)13614aa4f3eSSteven Rostedt (Google) static inline void cleanup_ei(struct eventfs_inode *ei)
13714aa4f3eSSteven Rostedt (Google) {
13814aa4f3eSSteven Rostedt (Google) if (ei) {
13914aa4f3eSSteven Rostedt (Google) /* Set nr_entries to 0 to prevent release() function being called */
14014aa4f3eSSteven Rostedt (Google) ei->nr_entries = 0;
14114aa4f3eSSteven Rostedt (Google) free_ei(ei);
14214aa4f3eSSteven Rostedt (Google) }
14314aa4f3eSSteven Rostedt (Google) }
14414aa4f3eSSteven Rostedt (Google)
get_ei(struct eventfs_inode * ei)1455dfb0410SLinus Torvalds static inline struct eventfs_inode *get_ei(struct eventfs_inode *ei)
1465dfb0410SLinus Torvalds {
1475dfb0410SLinus Torvalds if (ei)
1485dfb0410SLinus Torvalds kref_get(&ei->kref);
1495dfb0410SLinus Torvalds return ei;
1505dfb0410SLinus Torvalds }
1515dfb0410SLinus Torvalds
15263940449SAjay Kaher static struct dentry *eventfs_root_lookup(struct inode *dir,
15363940449SAjay Kaher struct dentry *dentry,
15463940449SAjay Kaher unsigned int flags);
1551bfdd54aSSteven Rostedt (Google) static int eventfs_iterate(struct file *file, struct dir_context *ctx);
156c1504e51SAjay Kaher
update_attr(struct eventfs_attr * attr,struct iattr * iattr)15732f4c167SSteven Rostedt (Google) static void update_attr(struct eventfs_attr *attr, struct iattr *iattr)
15832f4c167SSteven Rostedt (Google) {
15932f4c167SSteven Rostedt (Google) unsigned int ia_valid = iattr->ia_valid;
16032f4c167SSteven Rostedt (Google)
16132f4c167SSteven Rostedt (Google) if (ia_valid & ATTR_MODE) {
16232f4c167SSteven Rostedt (Google) attr->mode = (attr->mode & ~EVENTFS_MODE_MASK) |
16332f4c167SSteven Rostedt (Google) (iattr->ia_mode & EVENTFS_MODE_MASK) |
16432f4c167SSteven Rostedt (Google) EVENTFS_SAVE_MODE;
16532f4c167SSteven Rostedt (Google) }
16632f4c167SSteven Rostedt (Google) if (ia_valid & ATTR_UID) {
16732f4c167SSteven Rostedt (Google) attr->mode |= EVENTFS_SAVE_UID;
16832f4c167SSteven Rostedt (Google) attr->uid = iattr->ia_uid;
16932f4c167SSteven Rostedt (Google) }
17032f4c167SSteven Rostedt (Google) if (ia_valid & ATTR_GID) {
17132f4c167SSteven Rostedt (Google) attr->mode |= EVENTFS_SAVE_GID;
17232f4c167SSteven Rostedt (Google) attr->gid = iattr->ia_gid;
17332f4c167SSteven Rostedt (Google) }
17432f4c167SSteven Rostedt (Google) }
17532f4c167SSteven Rostedt (Google)
eventfs_set_attr(struct mnt_idmap * idmap,struct dentry * dentry,struct iattr * iattr)17632f4c167SSteven Rostedt (Google) static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,
17732f4c167SSteven Rostedt (Google) struct iattr *iattr)
17832f4c167SSteven Rostedt (Google) {
17932f4c167SSteven Rostedt (Google) const struct eventfs_entry *entry;
18032f4c167SSteven Rostedt (Google) struct eventfs_inode *ei;
18132f4c167SSteven Rostedt (Google) const char *name;
18232f4c167SSteven Rostedt (Google) int ret;
18332f4c167SSteven Rostedt (Google)
18432f4c167SSteven Rostedt (Google) mutex_lock(&eventfs_mutex);
18532f4c167SSteven Rostedt (Google) ei = dentry->d_fsdata;
186c58673caSSteven Rostedt (Google) if (ei->is_freed) {
18732f4c167SSteven Rostedt (Google) /* Do not allow changes if the event is about to be removed. */
18832f4c167SSteven Rostedt (Google) mutex_unlock(&eventfs_mutex);
18932f4c167SSteven Rostedt (Google) return -ENODEV;
19032f4c167SSteven Rostedt (Google) }
19132f4c167SSteven Rostedt (Google)
19232f4c167SSteven Rostedt (Google) /* Preallocate the children mode array if necessary */
19332f4c167SSteven Rostedt (Google) if (!(dentry->d_inode->i_mode & S_IFDIR)) {
19432f4c167SSteven Rostedt (Google) if (!ei->entry_attrs) {
1951f20155aSErick Archer ei->entry_attrs = kcalloc(ei->nr_entries, sizeof(*ei->entry_attrs),
1966586a12dSSteven Rostedt (Google) GFP_NOFS);
19732f4c167SSteven Rostedt (Google) if (!ei->entry_attrs) {
19832f4c167SSteven Rostedt (Google) ret = -ENOMEM;
19932f4c167SSteven Rostedt (Google) goto out;
20032f4c167SSteven Rostedt (Google) }
20132f4c167SSteven Rostedt (Google) }
20232f4c167SSteven Rostedt (Google) }
20332f4c167SSteven Rostedt (Google)
20432f4c167SSteven Rostedt (Google) ret = simple_setattr(idmap, dentry, iattr);
20532f4c167SSteven Rostedt (Google) if (ret < 0)
20632f4c167SSteven Rostedt (Google) goto out;
20732f4c167SSteven Rostedt (Google)
20832f4c167SSteven Rostedt (Google) /*
20932f4c167SSteven Rostedt (Google) * If this is a dir, then update the ei cache, only the file
21032f4c167SSteven Rostedt (Google) * mode is saved in the ei->m_children, and the ownership is
21132f4c167SSteven Rostedt (Google) * determined by the parent directory.
21232f4c167SSteven Rostedt (Google) */
21332f4c167SSteven Rostedt (Google) if (dentry->d_inode->i_mode & S_IFDIR) {
21432f4c167SSteven Rostedt (Google) update_attr(&ei->attr, iattr);
21532f4c167SSteven Rostedt (Google)
21632f4c167SSteven Rostedt (Google) } else {
21732f4c167SSteven Rostedt (Google) name = dentry->d_name.name;
21832f4c167SSteven Rostedt (Google)
21932f4c167SSteven Rostedt (Google) for (int i = 0; i < ei->nr_entries; i++) {
22032f4c167SSteven Rostedt (Google) entry = &ei->entries[i];
22132f4c167SSteven Rostedt (Google) if (strcmp(name, entry->name) == 0) {
22232f4c167SSteven Rostedt (Google) update_attr(&ei->entry_attrs[i], iattr);
22332f4c167SSteven Rostedt (Google) break;
22432f4c167SSteven Rostedt (Google) }
22532f4c167SSteven Rostedt (Google) }
22632f4c167SSteven Rostedt (Google) }
22732f4c167SSteven Rostedt (Google) out:
22832f4c167SSteven Rostedt (Google) mutex_unlock(&eventfs_mutex);
22932f4c167SSteven Rostedt (Google) return ret;
23032f4c167SSteven Rostedt (Google) }
23132f4c167SSteven Rostedt (Google)
update_events_attr(struct eventfs_inode * ei,struct super_block * sb)232e26405d5SSteven Rostedt (Google) static void update_events_attr(struct eventfs_inode *ei, struct super_block *sb)
233628adb84SSteven Rostedt (Google) {
23451a2049aSSteven Rostedt (Google) struct eventfs_root_inode *rei;
23551a2049aSSteven Rostedt (Google) struct inode *parent;
236628adb84SSteven Rostedt (Google)
23751a2049aSSteven Rostedt (Google) rei = get_root_inode(ei);
23851a2049aSSteven Rostedt (Google)
23951a2049aSSteven Rostedt (Google) /* Use the parent inode permissions unless root set its permissions */
24051a2049aSSteven Rostedt (Google) parent = rei->parent_inode;
24151a2049aSSteven Rostedt (Google)
24251a2049aSSteven Rostedt (Google) if (rei->ei.attr.mode & EVENTFS_SAVE_UID)
24351a2049aSSteven Rostedt (Google) ei->attr.uid = rei->ei.attr.uid;
24451a2049aSSteven Rostedt (Google) else
24551a2049aSSteven Rostedt (Google) ei->attr.uid = parent->i_uid;
24651a2049aSSteven Rostedt (Google)
24751a2049aSSteven Rostedt (Google) if (rei->ei.attr.mode & EVENTFS_SAVE_GID)
24851a2049aSSteven Rostedt (Google) ei->attr.gid = rei->ei.attr.gid;
24951a2049aSSteven Rostedt (Google) else
25051a2049aSSteven Rostedt (Google) ei->attr.gid = parent->i_gid;
251628adb84SSteven Rostedt (Google) }
252628adb84SSteven Rostedt (Google)
set_top_events_ownership(struct inode * inode)253628adb84SSteven Rostedt (Google) static void set_top_events_ownership(struct inode *inode)
254628adb84SSteven Rostedt (Google) {
255628adb84SSteven Rostedt (Google) struct tracefs_inode *ti = get_tracefs(inode);
256628adb84SSteven Rostedt (Google) struct eventfs_inode *ei = ti->private;
257628adb84SSteven Rostedt (Google)
258628adb84SSteven Rostedt (Google) /* The top events directory doesn't get automatically updated */
259e26405d5SSteven Rostedt (Google) if (!ei || !ei->is_events)
260628adb84SSteven Rostedt (Google) return;
261628adb84SSteven Rostedt (Google)
262e26405d5SSteven Rostedt (Google) update_events_attr(ei, inode->i_sb);
263628adb84SSteven Rostedt (Google)
264628adb84SSteven Rostedt (Google) if (!(ei->attr.mode & EVENTFS_SAVE_UID))
265628adb84SSteven Rostedt (Google) inode->i_uid = ei->attr.uid;
266628adb84SSteven Rostedt (Google)
267628adb84SSteven Rostedt (Google) if (!(ei->attr.mode & EVENTFS_SAVE_GID))
268628adb84SSteven Rostedt (Google) inode->i_gid = ei->attr.gid;
269628adb84SSteven Rostedt (Google) }
270628adb84SSteven Rostedt (Google)
eventfs_get_attr(struct mnt_idmap * idmap,const struct path * path,struct kstat * stat,u32 request_mask,unsigned int flags)271628adb84SSteven Rostedt (Google) static int eventfs_get_attr(struct mnt_idmap *idmap,
272628adb84SSteven Rostedt (Google) const struct path *path, struct kstat *stat,
273628adb84SSteven Rostedt (Google) u32 request_mask, unsigned int flags)
274628adb84SSteven Rostedt (Google) {
275628adb84SSteven Rostedt (Google) struct dentry *dentry = path->dentry;
276628adb84SSteven Rostedt (Google) struct inode *inode = d_backing_inode(dentry);
277628adb84SSteven Rostedt (Google)
278628adb84SSteven Rostedt (Google) set_top_events_ownership(inode);
279628adb84SSteven Rostedt (Google)
280628adb84SSteven Rostedt (Google) generic_fillattr(idmap, request_mask, inode, stat);
281628adb84SSteven Rostedt (Google) return 0;
282628adb84SSteven Rostedt (Google) }
283628adb84SSteven Rostedt (Google)
eventfs_permission(struct mnt_idmap * idmap,struct inode * inode,int mask)284628adb84SSteven Rostedt (Google) static int eventfs_permission(struct mnt_idmap *idmap,
285628adb84SSteven Rostedt (Google) struct inode *inode, int mask)
286628adb84SSteven Rostedt (Google) {
287628adb84SSteven Rostedt (Google) set_top_events_ownership(inode);
288628adb84SSteven Rostedt (Google) return generic_permission(idmap, inode, mask);
289628adb84SSteven Rostedt (Google) }
290628adb84SSteven Rostedt (Google)
291e26405d5SSteven Rostedt (Google) static const struct inode_operations eventfs_dir_inode_operations = {
29263940449SAjay Kaher .lookup = eventfs_root_lookup,
29332f4c167SSteven Rostedt (Google) .setattr = eventfs_set_attr,
294628adb84SSteven Rostedt (Google) .getattr = eventfs_get_attr,
295628adb84SSteven Rostedt (Google) .permission = eventfs_permission,
29632f4c167SSteven Rostedt (Google) };
29732f4c167SSteven Rostedt (Google)
29832f4c167SSteven Rostedt (Google) static const struct inode_operations eventfs_file_inode_operations = {
29932f4c167SSteven Rostedt (Google) .setattr = eventfs_set_attr,
300c1504e51SAjay Kaher };
301c1504e51SAjay Kaher
302c1504e51SAjay Kaher static const struct file_operations eventfs_file_operations = {
30363940449SAjay Kaher .read = generic_read_dir,
3041bfdd54aSSteven Rostedt (Google) .iterate_shared = eventfs_iterate,
30563940449SAjay Kaher .llseek = generic_file_llseek,
306c1504e51SAjay Kaher };
307c1504e51SAjay Kaher
eventfs_set_attrs(struct eventfs_inode * ei,bool update_uid,kuid_t uid,bool update_gid,kgid_t gid,int level)3087ec535edSSteven Rostedt (Google) static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t uid,
3097ec535edSSteven Rostedt (Google) bool update_gid, kgid_t gid, int level)
3107ec535edSSteven Rostedt (Google) {
3117ec535edSSteven Rostedt (Google) struct eventfs_inode *ei_child;
3127ec535edSSteven Rostedt (Google)
3137ec535edSSteven Rostedt (Google) /* Update events/<system>/<event> */
3147ec535edSSteven Rostedt (Google) if (WARN_ON_ONCE(level > 3))
3157ec535edSSteven Rostedt (Google) return;
3167ec535edSSteven Rostedt (Google)
3177ec535edSSteven Rostedt (Google) if (update_uid) {
3187ec535edSSteven Rostedt (Google) ei->attr.mode &= ~EVENTFS_SAVE_UID;
3197ec535edSSteven Rostedt (Google) ei->attr.uid = uid;
3207ec535edSSteven Rostedt (Google) }
3217ec535edSSteven Rostedt (Google)
3227ec535edSSteven Rostedt (Google) if (update_gid) {
3237ec535edSSteven Rostedt (Google) ei->attr.mode &= ~EVENTFS_SAVE_GID;
3247ec535edSSteven Rostedt (Google) ei->attr.gid = gid;
3257ec535edSSteven Rostedt (Google) }
3267ec535edSSteven Rostedt (Google)
3277ec535edSSteven Rostedt (Google) list_for_each_entry(ei_child, &ei->children, list) {
3287ec535edSSteven Rostedt (Google) eventfs_set_attrs(ei_child, update_uid, uid, update_gid, gid, level + 1);
3297ec535edSSteven Rostedt (Google) }
3307ec535edSSteven Rostedt (Google)
3317ec535edSSteven Rostedt (Google) if (!ei->entry_attrs)
3327ec535edSSteven Rostedt (Google) return;
3337ec535edSSteven Rostedt (Google)
3347ec535edSSteven Rostedt (Google) for (int i = 0; i < ei->nr_entries; i++) {
3357ec535edSSteven Rostedt (Google) if (update_uid) {
3367ec535edSSteven Rostedt (Google) ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_UID;
3377ec535edSSteven Rostedt (Google) ei->entry_attrs[i].uid = uid;
3387ec535edSSteven Rostedt (Google) }
3397ec535edSSteven Rostedt (Google) if (update_gid) {
3407ec535edSSteven Rostedt (Google) ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_GID;
3417ec535edSSteven Rostedt (Google) ei->entry_attrs[i].gid = gid;
3427ec535edSSteven Rostedt (Google) }
3437ec535edSSteven Rostedt (Google) }
3447ec535edSSteven Rostedt (Google)
3457ec535edSSteven Rostedt (Google) }
3467ec535edSSteven Rostedt (Google)
3475f91fc82SSteven Rostedt (Google) /*
3485f91fc82SSteven Rostedt (Google) * On a remount of tracefs, if UID or GID options are set, then
3495f91fc82SSteven Rostedt (Google) * the mount point inode permissions should be used.
3505f91fc82SSteven Rostedt (Google) * Reset the saved permission flags appropriately.
3515f91fc82SSteven Rostedt (Google) */
eventfs_remount(struct tracefs_inode * ti,bool update_uid,bool update_gid)3525f91fc82SSteven Rostedt (Google) void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid)
3535f91fc82SSteven Rostedt (Google) {
3545f91fc82SSteven Rostedt (Google) struct eventfs_inode *ei = ti->private;
3555f91fc82SSteven Rostedt (Google)
3567ec535edSSteven Rostedt (Google) /* Only the events directory does the updates */
3577ec535edSSteven Rostedt (Google) if (!ei || !ei->is_events || ei->is_freed)
3585f91fc82SSteven Rostedt (Google) return;
3595f91fc82SSteven Rostedt (Google)
3607ec535edSSteven Rostedt (Google) eventfs_set_attrs(ei, update_uid, ti->vfs_inode.i_uid,
3617ec535edSSteven Rostedt (Google) update_gid, ti->vfs_inode.i_gid, 0);
3625f91fc82SSteven Rostedt (Google) }
3635f91fc82SSteven Rostedt (Google)
3641b4dfdb3SSteven Rostedt (Google) /* Return the evenfs_inode of the "events" directory */
eventfs_find_events(struct dentry * dentry)3651b4dfdb3SSteven Rostedt (Google) static struct eventfs_inode *eventfs_find_events(struct dentry *dentry)
3661b4dfdb3SSteven Rostedt (Google) {
3671b4dfdb3SSteven Rostedt (Google) struct eventfs_inode *ei;
3681b4dfdb3SSteven Rostedt (Google)
3691b4dfdb3SSteven Rostedt (Google) do {
3704928d0e3SLinus Torvalds // The parent is stable because we do not do renames
3714928d0e3SLinus Torvalds dentry = dentry->d_parent;
3724928d0e3SLinus Torvalds // ... and directories always have d_fsdata
3734928d0e3SLinus Torvalds ei = dentry->d_fsdata;
3741b4dfdb3SSteven Rostedt (Google)
3751b4dfdb3SSteven Rostedt (Google) /*
3761b4dfdb3SSteven Rostedt (Google) * If the ei is being freed, the ownership of the children
3771b4dfdb3SSteven Rostedt (Google) * doesn't matter.
3781b4dfdb3SSteven Rostedt (Google) */
3795ade5fbdSHao Ge if (ei->is_freed)
3805ade5fbdSHao Ge return NULL;
3815ade5fbdSHao Ge
3824928d0e3SLinus Torvalds // Walk upwards until you find the events inode
3831b4dfdb3SSteven Rostedt (Google) } while (!ei->is_events);
3841b4dfdb3SSteven Rostedt (Google)
385e26405d5SSteven Rostedt (Google) update_events_attr(ei, dentry->d_sb);
386628adb84SSteven Rostedt (Google)
3871b4dfdb3SSteven Rostedt (Google) return ei;
3881b4dfdb3SSteven Rostedt (Google) }
3891b4dfdb3SSteven Rostedt (Google)
update_inode_attr(struct dentry * dentry,struct inode * inode,struct eventfs_attr * attr,umode_t mode)3904d12a83eSSteven Rostedt (Google) static void update_inode_attr(struct dentry *dentry, struct inode *inode,
3914d12a83eSSteven Rostedt (Google) struct eventfs_attr *attr, umode_t mode)
39232f4c167SSteven Rostedt (Google) {
3931b4dfdb3SSteven Rostedt (Google) struct eventfs_inode *events_ei = eventfs_find_events(dentry);
3941b4dfdb3SSteven Rostedt (Google)
3951b4dfdb3SSteven Rostedt (Google) if (!events_ei)
39632f4c167SSteven Rostedt (Google) return;
3971b4dfdb3SSteven Rostedt (Google)
3981b4dfdb3SSteven Rostedt (Google) inode->i_mode = mode;
3991b4dfdb3SSteven Rostedt (Google) inode->i_uid = events_ei->attr.uid;
4001b4dfdb3SSteven Rostedt (Google) inode->i_gid = events_ei->attr.gid;
4011b4dfdb3SSteven Rostedt (Google)
4021b4dfdb3SSteven Rostedt (Google) if (!attr)
4031b4dfdb3SSteven Rostedt (Google) return;
40432f4c167SSteven Rostedt (Google)
40532f4c167SSteven Rostedt (Google) if (attr->mode & EVENTFS_SAVE_MODE)
40632f4c167SSteven Rostedt (Google) inode->i_mode = attr->mode & EVENTFS_MODE_MASK;
40732f4c167SSteven Rostedt (Google)
40832f4c167SSteven Rostedt (Google) if (attr->mode & EVENTFS_SAVE_UID)
40932f4c167SSteven Rostedt (Google) inode->i_uid = attr->uid;
41032f4c167SSteven Rostedt (Google)
41132f4c167SSteven Rostedt (Google) if (attr->mode & EVENTFS_SAVE_GID)
41232f4c167SSteven Rostedt (Google) inode->i_gid = attr->gid;
4131b4dfdb3SSteven Rostedt (Google) }
4141b4dfdb3SSteven Rostedt (Google)
415c1504e51SAjay Kaher /**
41621faa3deSLinus Torvalds * lookup_file - look up a file in the tracefs filesystem
41721faa3deSLinus Torvalds * @dentry: the dentry to look up
418dc9ee9a8SSteven Rostedt (Google) * @mode: the permission that the file should have.
41932f4c167SSteven Rostedt (Google) * @attr: saved attributes changed by user
42063940449SAjay Kaher * @data: something that the caller will want to get to later on.
42163940449SAjay Kaher * @fop: struct file_operations that should be used for this file.
42263940449SAjay Kaher *
42335ee34c0SSteven Rostedt (Google) * This function creates a dentry that represents a file in the eventsfs_inode
42435ee34c0SSteven Rostedt (Google) * directory. The inode.i_private pointer will point to @data in the open()
42535ee34c0SSteven Rostedt (Google) * call.
42663940449SAjay Kaher */
lookup_file(struct eventfs_inode * parent_ei,struct dentry * dentry,umode_t mode,struct eventfs_attr * attr,void * data,const struct file_operations * fop)4275dfb0410SLinus Torvalds static struct dentry *lookup_file(struct eventfs_inode *parent_ei,
4285dfb0410SLinus Torvalds struct dentry *dentry,
42921faa3deSLinus Torvalds umode_t mode,
43032f4c167SSteven Rostedt (Google) struct eventfs_attr *attr,
43121faa3deSLinus Torvalds void *data,
43263940449SAjay Kaher const struct file_operations *fop)
43363940449SAjay Kaher {
434a3760079SAjay Kaher struct tracefs_inode *ti;
435a3760079SAjay Kaher struct inode *inode;
436a3760079SAjay Kaher
437dc9ee9a8SSteven Rostedt (Google) if (!(mode & S_IFMT))
438dc9ee9a8SSteven Rostedt (Google) mode |= S_IFREG;
439a3760079SAjay Kaher
440dc9ee9a8SSteven Rostedt (Google) if (WARN_ON_ONCE(!S_ISREG(mode)))
4415dfb0410SLinus Torvalds return ERR_PTR(-EIO);
442a3760079SAjay Kaher
443a3760079SAjay Kaher inode = tracefs_get_inode(dentry->d_sb);
444a3760079SAjay Kaher if (unlikely(!inode))
44521faa3deSLinus Torvalds return ERR_PTR(-ENOMEM);
446a3760079SAjay Kaher
44732f4c167SSteven Rostedt (Google) /* If the user updated the directory's attributes, use them */
4484d12a83eSSteven Rostedt (Google) update_inode_attr(dentry, inode, attr, mode);
44932f4c167SSteven Rostedt (Google)
45032f4c167SSteven Rostedt (Google) inode->i_op = &eventfs_file_inode_operations;
451a3760079SAjay Kaher inode->i_fop = fop;
452a3760079SAjay Kaher inode->i_private = data;
453a3760079SAjay Kaher
4544e8731d2SSteven Rostedt (Google) /* All files will have the same inode number */
4554e8731d2SSteven Rostedt (Google) inode->i_ino = EVENTFS_FILE_INODE_INO;
4564e8731d2SSteven Rostedt (Google)
457a3760079SAjay Kaher ti = get_tracefs(inode);
458a3760079SAjay Kaher ti->flags |= TRACEFS_EVENT_INODE;
45921faa3deSLinus Torvalds
4605dfb0410SLinus Torvalds // Files have their parent's ei as their fsdata
4615dfb0410SLinus Torvalds dentry->d_fsdata = get_ei(parent_ei);
4625dfb0410SLinus Torvalds
46321faa3deSLinus Torvalds d_add(dentry, inode);
4645dfb0410SLinus Torvalds return NULL;
46563940449SAjay Kaher };
46663940449SAjay Kaher
46763940449SAjay Kaher /**
46821faa3deSLinus Torvalds * lookup_dir_entry - look up a dir in the tracefs filesystem
46921faa3deSLinus Torvalds * @dentry: the directory to look up
47032f4c167SSteven Rostedt (Google) * @ei: the eventfs_inode that represents the directory to create
47163940449SAjay Kaher *
47221faa3deSLinus Torvalds * This function will look up a dentry for a directory represented by
47335ee34c0SSteven Rostedt (Google) * a eventfs_inode.
47463940449SAjay Kaher */
lookup_dir_entry(struct dentry * dentry,struct eventfs_inode * pei,struct eventfs_inode * ei)47521faa3deSLinus Torvalds static struct dentry *lookup_dir_entry(struct dentry *dentry,
47621faa3deSLinus Torvalds struct eventfs_inode *pei, struct eventfs_inode *ei)
47763940449SAjay Kaher {
478a3760079SAjay Kaher struct tracefs_inode *ti;
479a3760079SAjay Kaher struct inode *inode;
480a3760079SAjay Kaher
481a3760079SAjay Kaher inode = tracefs_get_inode(dentry->d_sb);
482a3760079SAjay Kaher if (unlikely(!inode))
48321faa3deSLinus Torvalds return ERR_PTR(-ENOMEM);
484a3760079SAjay Kaher
48532f4c167SSteven Rostedt (Google) /* If the user updated the directory's attributes, use them */
4864d12a83eSSteven Rostedt (Google) update_inode_attr(dentry, inode, &ei->attr,
4874d12a83eSSteven Rostedt (Google) S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO);
48832f4c167SSteven Rostedt (Google)
489e26405d5SSteven Rostedt (Google) inode->i_op = &eventfs_dir_inode_operations;
490a3760079SAjay Kaher inode->i_fop = &eventfs_file_operations;
491a3760079SAjay Kaher
4924e8731d2SSteven Rostedt (Google) /* All directories will have the same inode number */
4939a187657SSteven Rostedt (Google) inode->i_ino = eventfs_dir_ino(ei);
4944e8731d2SSteven Rostedt (Google)
495a3760079SAjay Kaher ti = get_tracefs(inode);
496a3760079SAjay Kaher ti->flags |= TRACEFS_EVENT_INODE;
497d1bcde94SLinus Torvalds /* Only directories have ti->private set to an ei, not files */
498d1bcde94SLinus Torvalds ti->private = ei;
499a3760079SAjay Kaher
5005dfb0410SLinus Torvalds dentry->d_fsdata = get_ei(ei);
50121faa3deSLinus Torvalds
50221faa3deSLinus Torvalds d_add(dentry, inode);
5035dfb0410SLinus Torvalds return NULL;
50463940449SAjay Kaher }
50563940449SAjay Kaher
init_ei(struct eventfs_inode * ei,const char * name)506e5c80b23SSteven Rostedt (Google) static inline struct eventfs_inode *init_ei(struct eventfs_inode *ei, const char *name)
507e5c80b23SSteven Rostedt (Google) {
508e5c80b23SSteven Rostedt (Google) ei->name = kstrdup_const(name, GFP_KERNEL);
509e5c80b23SSteven Rostedt (Google) if (!ei->name)
510e5c80b23SSteven Rostedt (Google) return NULL;
511e5c80b23SSteven Rostedt (Google) kref_init(&ei->kref);
512e5c80b23SSteven Rostedt (Google) return ei;
513e5c80b23SSteven Rostedt (Google) }
514e5c80b23SSteven Rostedt (Google)
alloc_ei(const char * name)5155dfb0410SLinus Torvalds static inline struct eventfs_inode *alloc_ei(const char *name)
5166e2a3352SSteven Rostedt (Google) {
5175dfb0410SLinus Torvalds struct eventfs_inode *ei = kzalloc(sizeof(*ei), GFP_KERNEL);
518e5c80b23SSteven Rostedt (Google) struct eventfs_inode *result;
5195dfb0410SLinus Torvalds
5205dfb0410SLinus Torvalds if (!ei)
5215dfb0410SLinus Torvalds return NULL;
5225dfb0410SLinus Torvalds
523e5c80b23SSteven Rostedt (Google) result = init_ei(ei, name);
524e5c80b23SSteven Rostedt (Google) if (!result)
5256e2a3352SSteven Rostedt (Google) kfree(ei);
526e5c80b23SSteven Rostedt (Google)
527e5c80b23SSteven Rostedt (Google) return result;
5285dfb0410SLinus Torvalds }
529e5c80b23SSteven Rostedt (Google)
alloc_root_ei(const char * name)530e5c80b23SSteven Rostedt (Google) static inline struct eventfs_inode *alloc_root_ei(const char *name)
531e5c80b23SSteven Rostedt (Google) {
532e5c80b23SSteven Rostedt (Google) struct eventfs_root_inode *rei = kzalloc(sizeof(*rei), GFP_KERNEL);
533e5c80b23SSteven Rostedt (Google) struct eventfs_inode *ei;
534e5c80b23SSteven Rostedt (Google)
535e5c80b23SSteven Rostedt (Google) if (!rei)
536e5c80b23SSteven Rostedt (Google) return NULL;
537e5c80b23SSteven Rostedt (Google)
538e5c80b23SSteven Rostedt (Google) rei->ei.is_events = 1;
539e5c80b23SSteven Rostedt (Google) ei = init_ei(&rei->ei, name);
540e5c80b23SSteven Rostedt (Google) if (!ei)
541e5c80b23SSteven Rostedt (Google) kfree(rei);
542e5c80b23SSteven Rostedt (Google)
5435dfb0410SLinus Torvalds return ei;
5446e2a3352SSteven Rostedt (Google) }
5456e2a3352SSteven Rostedt (Google)
54663940449SAjay Kaher /**
547c4619205SLinus Torvalds * eventfs_d_release - dentry is going away
54835ee34c0SSteven Rostedt (Google) * @dentry: dentry which has the reference to remove.
54963940449SAjay Kaher *
55035ee34c0SSteven Rostedt (Google) * Remove the association between a dentry from an eventfs_inode.
55163940449SAjay Kaher */
eventfs_d_release(struct dentry * dentry)552c4619205SLinus Torvalds void eventfs_d_release(struct dentry *dentry)
55363940449SAjay Kaher {
5545dfb0410SLinus Torvalds put_ei(dentry->d_fsdata);
55563940449SAjay Kaher }
55663940449SAjay Kaher
55763940449SAjay Kaher /**
55821faa3deSLinus Torvalds * lookup_file_dentry - create a dentry for a file of an eventfs_inode
55935ee34c0SSteven Rostedt (Google) * @ei: the eventfs_inode that the file will be created under
5605dfb0410SLinus Torvalds * @idx: the index into the entry_attrs[] of the @ei
56135ee34c0SSteven Rostedt (Google) * @parent: The parent dentry of the created file.
56235ee34c0SSteven Rostedt (Google) * @name: The name of the file to create
56335ee34c0SSteven Rostedt (Google) * @mode: The mode of the file.
56435ee34c0SSteven Rostedt (Google) * @data: The data to use to set the inode of the file with on open()
56535ee34c0SSteven Rostedt (Google) * @fops: The fops of the file to be created.
56663940449SAjay Kaher *
56735ee34c0SSteven Rostedt (Google) * Create a dentry for a file of an eventfs_inode @ei and place it into the
568ee699b54SSteven Rostedt (Google) * address located at @e_dentry.
56963940449SAjay Kaher */
57063940449SAjay Kaher static struct dentry *
lookup_file_dentry(struct dentry * dentry,struct eventfs_inode * ei,int idx,umode_t mode,void * data,const struct file_operations * fops)57121faa3deSLinus Torvalds lookup_file_dentry(struct dentry *dentry,
57221faa3deSLinus Torvalds struct eventfs_inode *ei, int idx,
57321faa3deSLinus Torvalds umode_t mode, void *data,
574ee699b54SSteven Rostedt (Google) const struct file_operations *fops)
57563940449SAjay Kaher {
57632f4c167SSteven Rostedt (Google) struct eventfs_attr *attr = NULL;
57763940449SAjay Kaher
57832f4c167SSteven Rostedt (Google) if (ei->entry_attrs)
57932f4c167SSteven Rostedt (Google) attr = &ei->entry_attrs[idx];
58032f4c167SSteven Rostedt (Google)
5815dfb0410SLinus Torvalds return lookup_file(ei, dentry, mode, attr, data, fops);
58263940449SAjay Kaher }
58363940449SAjay Kaher
58463940449SAjay Kaher /**
58563940449SAjay Kaher * eventfs_root_lookup - lookup routine to create file/dir
58663940449SAjay Kaher * @dir: in which a lookup is being done
58763940449SAjay Kaher * @dentry: file/dir dentry
58835ee34c0SSteven Rostedt (Google) * @flags: Just passed to simple_lookup()
58963940449SAjay Kaher *
59035ee34c0SSteven Rostedt (Google) * Used to create dynamic file/dir with-in @dir, search with-in @ei
59135ee34c0SSteven Rostedt (Google) * list, if @dentry found go ahead and create the file/dir
59263940449SAjay Kaher */
59335ee34c0SSteven Rostedt (Google)
eventfs_root_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)59463940449SAjay Kaher static struct dentry *eventfs_root_lookup(struct inode *dir,
59563940449SAjay Kaher struct dentry *dentry,
59663940449SAjay Kaher unsigned int flags)
59763940449SAjay Kaher {
59835ee34c0SSteven Rostedt (Google) struct eventfs_inode *ei_child;
59963940449SAjay Kaher struct tracefs_inode *ti;
60063940449SAjay Kaher struct eventfs_inode *ei;
60135ee34c0SSteven Rostedt (Google) const char *name = dentry->d_name.name;
6025dfb0410SLinus Torvalds struct dentry *result = NULL;
60363940449SAjay Kaher
60463940449SAjay Kaher ti = get_tracefs(dir);
60563940449SAjay Kaher if (!(ti->flags & TRACEFS_EVENT_INODE))
60621faa3deSLinus Torvalds return ERR_PTR(-EIO);
60763940449SAjay Kaher
60835ee34c0SSteven Rostedt (Google) mutex_lock(&eventfs_mutex);
60935ee34c0SSteven Rostedt (Google)
61021faa3deSLinus Torvalds ei = ti->private;
61121faa3deSLinus Torvalds if (!ei || ei->is_freed)
61235ee34c0SSteven Rostedt (Google) goto out;
61335ee34c0SSteven Rostedt (Google)
61421faa3deSLinus Torvalds list_for_each_entry(ei_child, &ei->children, list) {
61535ee34c0SSteven Rostedt (Google) if (strcmp(ei_child->name, name) != 0)
61663940449SAjay Kaher continue;
61721faa3deSLinus Torvalds if (ei_child->is_freed)
61853cd8c57SBeau Belgrave goto out;
6195dfb0410SLinus Torvalds result = lookup_dir_entry(dentry, ei, ei_child);
62035ee34c0SSteven Rostedt (Google) goto out;
621ee699b54SSteven Rostedt (Google) }
62235ee34c0SSteven Rostedt (Google)
62321faa3deSLinus Torvalds for (int i = 0; i < ei->nr_entries; i++) {
62421faa3deSLinus Torvalds void *data;
62521faa3deSLinus Torvalds umode_t mode;
62621faa3deSLinus Torvalds const struct file_operations *fops;
62721faa3deSLinus Torvalds const struct eventfs_entry *entry = &ei->entries[i];
62821faa3deSLinus Torvalds
62921faa3deSLinus Torvalds if (strcmp(name, entry->name) != 0)
63035ee34c0SSteven Rostedt (Google) continue;
63121faa3deSLinus Torvalds
63221faa3deSLinus Torvalds data = ei->data;
63321faa3deSLinus Torvalds if (entry->callback(name, &mode, &data, &fops) <= 0)
63453cd8c57SBeau Belgrave goto out;
63521faa3deSLinus Torvalds
6365dfb0410SLinus Torvalds result = lookup_file_dentry(dentry, ei, i, mode, data, fops);
63721faa3deSLinus Torvalds goto out;
63835ee34c0SSteven Rostedt (Google) }
63935ee34c0SSteven Rostedt (Google) out:
64021faa3deSLinus Torvalds mutex_unlock(&eventfs_mutex);
6415dfb0410SLinus Torvalds return result;
64263940449SAjay Kaher }
64363940449SAjay Kaher
6441bfdd54aSSteven Rostedt (Google) /*
6451bfdd54aSSteven Rostedt (Google) * Walk the children of a eventfs_inode to fill in getdents().
64663940449SAjay Kaher */
eventfs_iterate(struct file * file,struct dir_context * ctx)6471bfdd54aSSteven Rostedt (Google) static int eventfs_iterate(struct file *file, struct dir_context *ctx)
64863940449SAjay Kaher {
64935ee34c0SSteven Rostedt (Google) const struct file_operations *fops;
6501bfdd54aSSteven Rostedt (Google) struct inode *f_inode = file_inode(file);
65135ee34c0SSteven Rostedt (Google) const struct eventfs_entry *entry;
65235ee34c0SSteven Rostedt (Google) struct eventfs_inode *ei_child;
65363940449SAjay Kaher struct tracefs_inode *ti;
65463940449SAjay Kaher struct eventfs_inode *ei;
6551bfdd54aSSteven Rostedt (Google) const char *name;
65635ee34c0SSteven Rostedt (Google) umode_t mode;
65763940449SAjay Kaher int idx;
6581bfdd54aSSteven Rostedt (Google) int ret = -EINVAL;
6591bfdd54aSSteven Rostedt (Google) int ino;
6601bfdd54aSSteven Rostedt (Google) int i, r, c;
6611bfdd54aSSteven Rostedt (Google)
6621bfdd54aSSteven Rostedt (Google) if (!dir_emit_dots(file, ctx))
6631bfdd54aSSteven Rostedt (Google) return 0;
66463940449SAjay Kaher
66563940449SAjay Kaher ti = get_tracefs(f_inode);
66663940449SAjay Kaher if (!(ti->flags & TRACEFS_EVENT_INODE))
66763940449SAjay Kaher return -EINVAL;
66863940449SAjay Kaher
6691bfdd54aSSteven Rostedt (Google) c = ctx->pos - 2;
670ef36b4f9SSteven Rostedt (Google)
67163940449SAjay Kaher idx = srcu_read_lock(&eventfs_srcu);
672ef36b4f9SSteven Rostedt (Google)
67335ee34c0SSteven Rostedt (Google) mutex_lock(&eventfs_mutex);
67435ee34c0SSteven Rostedt (Google) ei = READ_ONCE(ti->private);
675c55d11eaSSteven Rostedt (Google) if (ei && ei->is_freed)
676c55d11eaSSteven Rostedt (Google) ei = NULL;
67735ee34c0SSteven Rostedt (Google) mutex_unlock(&eventfs_mutex);
67835ee34c0SSteven Rostedt (Google)
679c55d11eaSSteven Rostedt (Google) if (!ei)
6801bfdd54aSSteven Rostedt (Google) goto out;
68135ee34c0SSteven Rostedt (Google)
6821bfdd54aSSteven Rostedt (Google) /*
6831bfdd54aSSteven Rostedt (Google) * Need to create the dentries and inodes to have a consistent
6841bfdd54aSSteven Rostedt (Google) * inode number.
6851bfdd54aSSteven Rostedt (Google) */
686e638899fSSteven Rostedt (Google) ret = 0;
687e638899fSSteven Rostedt (Google)
688e638899fSSteven Rostedt (Google) /* Start at 'c' to jump over already read entries */
689e638899fSSteven Rostedt (Google) for (i = c; i < ei->nr_entries; i++, ctx->pos++) {
6901bfdd54aSSteven Rostedt (Google) void *cdata = ei->data;
6911bfdd54aSSteven Rostedt (Google)
69235ee34c0SSteven Rostedt (Google) entry = &ei->entries[i];
69335ee34c0SSteven Rostedt (Google) name = entry->name;
6941bfdd54aSSteven Rostedt (Google)
6951a6edfc7SSteven Rostedt (Google) mutex_lock(&eventfs_mutex);
69698102764SSteven Rostedt (Google) /* If ei->is_freed then just bail here, nothing more to do */
69798102764SSteven Rostedt (Google) if (ei->is_freed) {
69898102764SSteven Rostedt (Google) mutex_unlock(&eventfs_mutex);
699e638899fSSteven Rostedt (Google) goto out;
70098102764SSteven Rostedt (Google) }
70135ee34c0SSteven Rostedt (Google) r = entry->callback(name, &mode, &cdata, &fops);
7021a6edfc7SSteven Rostedt (Google) mutex_unlock(&eventfs_mutex);
70335ee34c0SSteven Rostedt (Google) if (r <= 0)
70435ee34c0SSteven Rostedt (Google) continue;
7051bfdd54aSSteven Rostedt (Google)
706c55d11eaSSteven Rostedt (Google) ino = EVENTFS_FILE_INODE_INO;
7071bfdd54aSSteven Rostedt (Google)
7081bfdd54aSSteven Rostedt (Google) if (!dir_emit(ctx, name, strlen(name), ino, DT_REG))
709e638899fSSteven Rostedt (Google) goto out;
710ef36b4f9SSteven Rostedt (Google) }
711f3f41f44SSteven Rostedt (Google)
712e638899fSSteven Rostedt (Google) /* Subtract the skipped entries above */
713e638899fSSteven Rostedt (Google) c -= min((unsigned int)c, (unsigned int)ei->nr_entries);
714e638899fSSteven Rostedt (Google)
715f3f41f44SSteven Rostedt (Google) list_for_each_entry_srcu(ei_child, &ei->children, list,
716f3f41f44SSteven Rostedt (Google) srcu_read_lock_held(&eventfs_srcu)) {
717f3f41f44SSteven Rostedt (Google)
718f3f41f44SSteven Rostedt (Google) if (c > 0) {
719f3f41f44SSteven Rostedt (Google) c--;
720f3f41f44SSteven Rostedt (Google) continue;
721f3f41f44SSteven Rostedt (Google) }
722f3f41f44SSteven Rostedt (Google)
723f3f41f44SSteven Rostedt (Google) ctx->pos++;
724f3f41f44SSteven Rostedt (Google)
725f3f41f44SSteven Rostedt (Google) if (ei_child->is_freed)
726f3f41f44SSteven Rostedt (Google) continue;
727f3f41f44SSteven Rostedt (Google)
728f3f41f44SSteven Rostedt (Google) name = ei_child->name;
729f3f41f44SSteven Rostedt (Google)
7309a187657SSteven Rostedt (Google) ino = eventfs_dir_ino(ei_child);
731f3f41f44SSteven Rostedt (Google)
732f3f41f44SSteven Rostedt (Google) if (!dir_emit(ctx, name, strlen(name), ino, DT_DIR))
733f3f41f44SSteven Rostedt (Google) goto out_dec;
734f3f41f44SSteven Rostedt (Google) }
7351bfdd54aSSteven Rostedt (Google) ret = 1;
7361bfdd54aSSteven Rostedt (Google) out:
73763940449SAjay Kaher srcu_read_unlock(&eventfs_srcu, idx);
738ef36b4f9SSteven Rostedt (Google)
739ef36b4f9SSteven Rostedt (Google) return ret;
74082820a2dSSteven Rostedt (Google)
74182820a2dSSteven Rostedt (Google) out_dec:
74282820a2dSSteven Rostedt (Google) /* Incremented ctx->pos without adding something, reset it */
74382820a2dSSteven Rostedt (Google) ctx->pos--;
74482820a2dSSteven Rostedt (Google) goto out;
74563940449SAjay Kaher }
74663940449SAjay Kaher
74763940449SAjay Kaher /**
74835ee34c0SSteven Rostedt (Google) * eventfs_create_dir - Create the eventfs_inode for this directory
74935ee34c0SSteven Rostedt (Google) * @name: The name of the directory to create.
75035ee34c0SSteven Rostedt (Google) * @parent: The eventfs_inode of the parent directory.
75135ee34c0SSteven Rostedt (Google) * @entries: A list of entries that represent the files under this directory
75235ee34c0SSteven Rostedt (Google) * @size: The number of @entries
75335ee34c0SSteven Rostedt (Google) * @data: The default data to pass to the files (an entry may override it).
754c1504e51SAjay Kaher *
75535ee34c0SSteven Rostedt (Google) * This function creates the descriptor to represent a directory in the
75635ee34c0SSteven Rostedt (Google) * eventfs. This descriptor is an eventfs_inode, and it is returned to be
75735ee34c0SSteven Rostedt (Google) * used to create other children underneath.
75835ee34c0SSteven Rostedt (Google) *
75935ee34c0SSteven Rostedt (Google) * The @entries is an array of eventfs_entry structures which has:
76035ee34c0SSteven Rostedt (Google) * const char *name
76135ee34c0SSteven Rostedt (Google) * eventfs_callback callback;
76235ee34c0SSteven Rostedt (Google) *
76335ee34c0SSteven Rostedt (Google) * The name is the name of the file, and the callback is a pointer to a function
76435ee34c0SSteven Rostedt (Google) * that will be called when the file is reference (either by lookup or by
76535ee34c0SSteven Rostedt (Google) * reading a directory). The callback is of the prototype:
76635ee34c0SSteven Rostedt (Google) *
76735ee34c0SSteven Rostedt (Google) * int callback(const char *name, umode_t *mode, void **data,
76835ee34c0SSteven Rostedt (Google) * const struct file_operations **fops);
76935ee34c0SSteven Rostedt (Google) *
77035ee34c0SSteven Rostedt (Google) * When a file needs to be created, this callback will be called with
77135ee34c0SSteven Rostedt (Google) * name = the name of the file being created (so that the same callback
77235ee34c0SSteven Rostedt (Google) * may be used for multiple files).
77335ee34c0SSteven Rostedt (Google) * mode = a place to set the file's mode
77435ee34c0SSteven Rostedt (Google) * data = A pointer to @data, and the callback may replace it, which will
77535ee34c0SSteven Rostedt (Google) * cause the file created to pass the new data to the open() call.
77635ee34c0SSteven Rostedt (Google) * fops = the fops to use for the created file.
7771a6edfc7SSteven Rostedt (Google) *
7781a6edfc7SSteven Rostedt (Google) * NB. @callback is called while holding internal locks of the eventfs
7791a6edfc7SSteven Rostedt (Google) * system. The callback must not call any code that might also call into
7801a6edfc7SSteven Rostedt (Google) * the tracefs or eventfs system or it will risk creating a deadlock.
781c1504e51SAjay Kaher */
eventfs_create_dir(const char * name,struct eventfs_inode * parent,const struct eventfs_entry * entries,int size,void * data)78235ee34c0SSteven Rostedt (Google) struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode *parent,
78335ee34c0SSteven Rostedt (Google) const struct eventfs_entry *entries,
78435ee34c0SSteven Rostedt (Google) int size, void *data)
785c1504e51SAjay Kaher {
78635ee34c0SSteven Rostedt (Google) struct eventfs_inode *ei;
787c1504e51SAjay Kaher
78835ee34c0SSteven Rostedt (Google) if (!parent)
78935ee34c0SSteven Rostedt (Google) return ERR_PTR(-EINVAL);
79035ee34c0SSteven Rostedt (Google)
7915dfb0410SLinus Torvalds ei = alloc_ei(name);
79235ee34c0SSteven Rostedt (Google) if (!ei)
793c1504e51SAjay Kaher return ERR_PTR(-ENOMEM);
794c1504e51SAjay Kaher
79535ee34c0SSteven Rostedt (Google) ei->entries = entries;
79635ee34c0SSteven Rostedt (Google) ei->nr_entries = size;
79735ee34c0SSteven Rostedt (Google) ei->data = data;
79835ee34c0SSteven Rostedt (Google) INIT_LIST_HEAD(&ei->children);
799d2a632aeSSteven Rostedt (Google) INIT_LIST_HEAD(&ei->list);
80035ee34c0SSteven Rostedt (Google)
80135ee34c0SSteven Rostedt (Google) mutex_lock(&eventfs_mutex);
802ca2d3b2cSLinus Torvalds if (!parent->is_freed)
80335ee34c0SSteven Rostedt (Google) list_add_tail(&ei->list, &parent->children);
80435ee34c0SSteven Rostedt (Google) mutex_unlock(&eventfs_mutex);
80535ee34c0SSteven Rostedt (Google)
806d2a632aeSSteven Rostedt (Google) /* Was the parent freed? */
807d2a632aeSSteven Rostedt (Google) if (list_empty(&ei->list)) {
80814aa4f3eSSteven Rostedt (Google) cleanup_ei(ei);
809b59603a1SMathias Krause ei = ERR_PTR(-EBUSY);
810d2a632aeSSteven Rostedt (Google) }
81135ee34c0SSteven Rostedt (Google) return ei;
812c1504e51SAjay Kaher }
813c1504e51SAjay Kaher
814c1504e51SAjay Kaher /**
81535ee34c0SSteven Rostedt (Google) * eventfs_create_events_dir - create the top level events directory
81635ee34c0SSteven Rostedt (Google) * @name: The name of the top level directory to create.
81735ee34c0SSteven Rostedt (Google) * @parent: Parent dentry for this file in the tracefs directory.
81835ee34c0SSteven Rostedt (Google) * @entries: A list of entries that represent the files under this directory
81935ee34c0SSteven Rostedt (Google) * @size: The number of @entries
82035ee34c0SSteven Rostedt (Google) * @data: The default data to pass to the files (an entry may override it).
821c1504e51SAjay Kaher *
822c1504e51SAjay Kaher * This function creates the top of the trace event directory.
8231a6edfc7SSteven Rostedt (Google) *
8241a6edfc7SSteven Rostedt (Google) * See eventfs_create_dir() for use of @entries.
825c1504e51SAjay Kaher */
eventfs_create_events_dir(const char * name,struct dentry * parent,const struct eventfs_entry * entries,int size,void * data)82635ee34c0SSteven Rostedt (Google) struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry *parent,
82735ee34c0SSteven Rostedt (Google) const struct eventfs_entry *entries,
82835ee34c0SSteven Rostedt (Google) int size, void *data)
829c1504e51SAjay Kaher {
830c1504e51SAjay Kaher struct dentry *dentry = tracefs_start_creating(name, parent);
831e5c80b23SSteven Rostedt (Google) struct eventfs_root_inode *rei;
832c1504e51SAjay Kaher struct eventfs_inode *ei;
833c1504e51SAjay Kaher struct tracefs_inode *ti;
834c1504e51SAjay Kaher struct inode *inode;
8351b4dfdb3SSteven Rostedt (Google) kuid_t uid;
8361b4dfdb3SSteven Rostedt (Google) kgid_t gid;
837c1504e51SAjay Kaher
838e2470945SSteven Rostedt (Google) if (security_locked_down(LOCKDOWN_TRACEFS))
839e2470945SSteven Rostedt (Google) return NULL;
840e2470945SSteven Rostedt (Google)
841c1504e51SAjay Kaher if (IS_ERR(dentry))
84217e4e4d2SNathan Chancellor return ERR_CAST(dentry);
843c1504e51SAjay Kaher
844e5c80b23SSteven Rostedt (Google) ei = alloc_root_ei(name);
845c1504e51SAjay Kaher if (!ei)
8465dfb0410SLinus Torvalds goto fail;
84735ee34c0SSteven Rostedt (Google)
848c1504e51SAjay Kaher inode = tracefs_get_inode(dentry->d_sb);
84935ee34c0SSteven Rostedt (Google) if (unlikely(!inode))
85035ee34c0SSteven Rostedt (Google) goto fail;
85135ee34c0SSteven Rostedt (Google)
8525dfb0410SLinus Torvalds // Note: we have a ref to the dentry from tracefs_start_creating()
853e5c80b23SSteven Rostedt (Google) rei = get_root_inode(ei);
854e5c80b23SSteven Rostedt (Google) rei->events_dir = dentry;
85551a2049aSSteven Rostedt (Google) rei->parent_inode = d_inode(dentry->d_sb->s_root);
856e5c80b23SSteven Rostedt (Google)
85735ee34c0SSteven Rostedt (Google) ei->entries = entries;
85835ee34c0SSteven Rostedt (Google) ei->nr_entries = size;
85935ee34c0SSteven Rostedt (Google) ei->data = data;
86035ee34c0SSteven Rostedt (Google)
8611b4dfdb3SSteven Rostedt (Google) /* Save the ownership of this directory */
8621b4dfdb3SSteven Rostedt (Google) uid = d_inode(dentry->d_parent)->i_uid;
8631b4dfdb3SSteven Rostedt (Google) gid = d_inode(dentry->d_parent)->i_gid;
8641b4dfdb3SSteven Rostedt (Google)
8651b4dfdb3SSteven Rostedt (Google) ei->attr.uid = uid;
8661b4dfdb3SSteven Rostedt (Google) ei->attr.gid = gid;
8671b4dfdb3SSteven Rostedt (Google)
86851a2049aSSteven Rostedt (Google) /*
86951a2049aSSteven Rostedt (Google) * When the "events" directory is created, it takes on the
87051a2049aSSteven Rostedt (Google) * permissions of its parent. But can be reset on remount.
87151a2049aSSteven Rostedt (Google) */
87251a2049aSSteven Rostedt (Google) ei->attr.mode |= EVENTFS_SAVE_UID | EVENTFS_SAVE_GID;
87351a2049aSSteven Rostedt (Google)
87435ee34c0SSteven Rostedt (Google) INIT_LIST_HEAD(&ei->children);
87535ee34c0SSteven Rostedt (Google) INIT_LIST_HEAD(&ei->list);
876c1504e51SAjay Kaher
877c1504e51SAjay Kaher ti = get_tracefs(inode);
878e26405d5SSteven Rostedt (Google) ti->flags |= TRACEFS_EVENT_INODE;
879c1504e51SAjay Kaher ti->private = ei;
880c1504e51SAjay Kaher
881c1504e51SAjay Kaher inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
8821b4dfdb3SSteven Rostedt (Google) inode->i_uid = uid;
8831b4dfdb3SSteven Rostedt (Google) inode->i_gid = gid;
884e26405d5SSteven Rostedt (Google) inode->i_op = &eventfs_dir_inode_operations;
885c1504e51SAjay Kaher inode->i_fop = &eventfs_file_operations;
886c1504e51SAjay Kaher
8875dfb0410SLinus Torvalds dentry->d_fsdata = get_ei(ei);
888fb9b8eeaSSteven Rostedt (Google)
889a49e9c72SSteven Rostedt (Google) /*
890a49e9c72SSteven Rostedt (Google) * Keep all eventfs directories with i_nlink == 1.
891a49e9c72SSteven Rostedt (Google) * Due to the dynamic nature of the dentry creations and not
892a49e9c72SSteven Rostedt (Google) * wanting to add a pointer to the parent eventfs_inode in the
893a49e9c72SSteven Rostedt (Google) * eventfs_inode structure, keeping the i_nlink in sync with the
894a49e9c72SSteven Rostedt (Google) * number of directories would cause too much complexity for
895a49e9c72SSteven Rostedt (Google) * something not worth much. Keeping directory links at 1
896a49e9c72SSteven Rostedt (Google) * tells userspace not to trust the link number.
897a49e9c72SSteven Rostedt (Google) */
898c1504e51SAjay Kaher d_instantiate(dentry, inode);
899a49e9c72SSteven Rostedt (Google) /* The dentry of the "events" parent does keep track though */
900c1504e51SAjay Kaher inc_nlink(dentry->d_parent->d_inode);
901c1504e51SAjay Kaher fsnotify_mkdir(dentry->d_parent->d_inode, dentry);
90235ee34c0SSteven Rostedt (Google) tracefs_end_creating(dentry);
90335ee34c0SSteven Rostedt (Google)
90435ee34c0SSteven Rostedt (Google) return ei;
90535ee34c0SSteven Rostedt (Google)
90635ee34c0SSteven Rostedt (Google) fail:
90714aa4f3eSSteven Rostedt (Google) cleanup_ei(ei);
90835ee34c0SSteven Rostedt (Google) tracefs_failed_creating(dentry);
90935ee34c0SSteven Rostedt (Google) return ERR_PTR(-ENOMEM);
910c1504e51SAjay Kaher }
911c1504e51SAjay Kaher
9125bdcd5f5SAjay Kaher /**
9135bdcd5f5SAjay Kaher * eventfs_remove_rec - remove eventfs dir or file from list
91435ee34c0SSteven Rostedt (Google) * @ei: eventfs_inode to be removed.
915773cd7dfSSteven Rostedt (Google) * @level: prevent recursion from going more than 3 levels deep.
9165bdcd5f5SAjay Kaher *
917773cd7dfSSteven Rostedt (Google) * This function recursively removes eventfs_inodes which
918773cd7dfSSteven Rostedt (Google) * contains info of files and/or directories.
9195bdcd5f5SAjay Kaher */
eventfs_remove_rec(struct eventfs_inode * ei,int level)92074a9e56bSSteven Rostedt (Google) static void eventfs_remove_rec(struct eventfs_inode *ei, int level)
9215bdcd5f5SAjay Kaher {
92235ee34c0SSteven Rostedt (Google) struct eventfs_inode *ei_child;
9235bdcd5f5SAjay Kaher
9245bdcd5f5SAjay Kaher /*
9255bdcd5f5SAjay Kaher * Check recursion depth. It should never be greater than 3:
9265bdcd5f5SAjay Kaher * 0 - events/
9275bdcd5f5SAjay Kaher * 1 - events/group/
9285bdcd5f5SAjay Kaher * 2 - events/group/event/
9295bdcd5f5SAjay Kaher * 3 - events/group/event/file
9305bdcd5f5SAjay Kaher */
9315bdcd5f5SAjay Kaher if (WARN_ON_ONCE(level > 3))
9325bdcd5f5SAjay Kaher return;
9335bdcd5f5SAjay Kaher
9345bdcd5f5SAjay Kaher /* search for nested folders or files */
9355dfb0410SLinus Torvalds list_for_each_entry(ei_child, &ei->children, list)
93674a9e56bSSteven Rostedt (Google) eventfs_remove_rec(ei_child, level + 1);
93774a9e56bSSteven Rostedt (Google)
938*05e08297SSteven Rostedt list_del_rcu(&ei->list);
9395c3ea7dfSSteven Rostedt (Google) free_ei(ei);
9405bdcd5f5SAjay Kaher }
9415bdcd5f5SAjay Kaher
94235ee34c0SSteven Rostedt (Google) /**
943843879a6SJiapeng Chong * eventfs_remove_dir - remove eventfs dir or file from list
94435ee34c0SSteven Rostedt (Google) * @ei: eventfs_inode to be removed.
94535ee34c0SSteven Rostedt (Google) *
94635ee34c0SSteven Rostedt (Google) * This function acquire the eventfs_mutex lock and call eventfs_remove_rec()
94735ee34c0SSteven Rostedt (Google) */
eventfs_remove_dir(struct eventfs_inode * ei)94835ee34c0SSteven Rostedt (Google) void eventfs_remove_dir(struct eventfs_inode *ei)
94935ee34c0SSteven Rostedt (Google) {
95035ee34c0SSteven Rostedt (Google) if (!ei)
95135ee34c0SSteven Rostedt (Google) return;
95235ee34c0SSteven Rostedt (Google)
95335ee34c0SSteven Rostedt (Google) mutex_lock(&eventfs_mutex);
95474a9e56bSSteven Rostedt (Google) eventfs_remove_rec(ei, 0);
955c58673caSSteven Rostedt (Google) mutex_unlock(&eventfs_mutex);
9565bdcd5f5SAjay Kaher }
9575bdcd5f5SAjay Kaher
9585bdcd5f5SAjay Kaher /**
959bceba0d3SSteven Rostedt (Google) * eventfs_remove_events_dir - remove the top level eventfs directory
960bceba0d3SSteven Rostedt (Google) * @ei: the event_inode returned by eventfs_create_events_dir().
9615bdcd5f5SAjay Kaher *
962bceba0d3SSteven Rostedt (Google) * This function removes the events main directory
9635bdcd5f5SAjay Kaher */
eventfs_remove_events_dir(struct eventfs_inode * ei)964bceba0d3SSteven Rostedt (Google) void eventfs_remove_events_dir(struct eventfs_inode *ei)
9655bdcd5f5SAjay Kaher {
966e5c80b23SSteven Rostedt (Google) struct eventfs_root_inode *rei;
96774a9e56bSSteven Rostedt (Google) struct dentry *dentry;
9685bdcd5f5SAjay Kaher
969e5c80b23SSteven Rostedt (Google) rei = get_root_inode(ei);
970e5c80b23SSteven Rostedt (Google) dentry = rei->events_dir;
9715dfb0410SLinus Torvalds if (!dentry)
9725dfb0410SLinus Torvalds return;
9735dfb0410SLinus Torvalds
974e5c80b23SSteven Rostedt (Google) rei->events_dir = NULL;
975bceba0d3SSteven Rostedt (Google) eventfs_remove_dir(ei);
9765bdcd5f5SAjay Kaher
97774a9e56bSSteven Rostedt (Google) /*
97874a9e56bSSteven Rostedt (Google) * Matches the dget() done by tracefs_start_creating()
97974a9e56bSSteven Rostedt (Google) * in eventfs_create_events_dir() when it the dentry was
98074a9e56bSSteven Rostedt (Google) * created. In other words, it's a normal dentry that
98174a9e56bSSteven Rostedt (Google) * sticks around while the other ei->dentry are created
98274a9e56bSSteven Rostedt (Google) * and destroyed dynamically.
98374a9e56bSSteven Rostedt (Google) */
9845dfb0410SLinus Torvalds d_invalidate(dentry);
985601daf7eSSteven Rostedt (Google) dput(dentry);
9865bdcd5f5SAjay Kaher }
987