162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * event_inode.c - part of tracefs, a pseudo file system for activating tracing 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020-23 VMware Inc, author: Steven Rostedt <rostedt@goodmis.org> 662306a36Sopenharmony_ci * Copyright (C) 2020-23 VMware Inc, author: Ajay Kaher <akaher@vmware.com> 762306a36Sopenharmony_ci * Copyright (C) 2023 Google, author: Steven Rostedt <rostedt@goodmis.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * eventfs is used to dynamically create inodes and dentries based on the 1062306a36Sopenharmony_ci * meta data provided by the tracing system. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * eventfs stores the meta-data of files/dirs and holds off on creating 1362306a36Sopenharmony_ci * inodes/dentries of the files. When accessed, the eventfs will create the 1462306a36Sopenharmony_ci * inodes/dentries in a just-in-time (JIT) manner. The eventfs will clean up 1562306a36Sopenharmony_ci * and delete the inodes/dentries when they are no longer referenced. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci#include <linux/fsnotify.h> 1862306a36Sopenharmony_ci#include <linux/fs.h> 1962306a36Sopenharmony_ci#include <linux/namei.h> 2062306a36Sopenharmony_ci#include <linux/workqueue.h> 2162306a36Sopenharmony_ci#include <linux/security.h> 2262306a36Sopenharmony_ci#include <linux/tracefs.h> 2362306a36Sopenharmony_ci#include <linux/kref.h> 2462306a36Sopenharmony_ci#include <linux/delay.h> 2562306a36Sopenharmony_ci#include "internal.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * eventfs_mutex protects the eventfs_inode (ei) dentry. Any access 2962306a36Sopenharmony_ci * to the ei->dentry must be done under this mutex and after checking 3062306a36Sopenharmony_ci * if ei->is_freed is not set. When ei->is_freed is set, the dentry 3162306a36Sopenharmony_ci * is on its way to being freed after the last dput() is made on it. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic DEFINE_MUTEX(eventfs_mutex); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Choose something "unique" ;-) */ 3662306a36Sopenharmony_ci#define EVENTFS_FILE_INODE_INO 0x12c4e37 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Just try to make something consistent and unique */ 3962306a36Sopenharmony_cistatic int eventfs_dir_ino(struct eventfs_inode *ei) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci if (!ei->ino) 4262306a36Sopenharmony_ci ei->ino = get_next_ino(); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return ei->ino; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * The eventfs_inode (ei) itself is protected by SRCU. It is released from 4962306a36Sopenharmony_ci * its parent's list and will have is_freed set (under eventfs_mutex). 5062306a36Sopenharmony_ci * After the SRCU grace period is over and the last dput() is called 5162306a36Sopenharmony_ci * the ei is freed. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ciDEFINE_STATIC_SRCU(eventfs_srcu); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Mode is unsigned short, use the upper bits for flags */ 5662306a36Sopenharmony_cienum { 5762306a36Sopenharmony_ci EVENTFS_SAVE_MODE = BIT(16), 5862306a36Sopenharmony_ci EVENTFS_SAVE_UID = BIT(17), 5962306a36Sopenharmony_ci EVENTFS_SAVE_GID = BIT(18), 6062306a36Sopenharmony_ci EVENTFS_TOPLEVEL = BIT(19), 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * eventfs_inode reference count management. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * NOTE! We count only references from dentries, in the 6962306a36Sopenharmony_ci * form 'dentry->d_fsdata'. There are also references from 7062306a36Sopenharmony_ci * directory inodes ('ti->private'), but the dentry reference 7162306a36Sopenharmony_ci * count is always a superset of the inode reference count. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic void release_ei(struct kref *ref) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct eventfs_inode *ei = container_of(ref, struct eventfs_inode, kref); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci WARN_ON_ONCE(!ei->is_freed); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci kfree(ei->entry_attrs); 8062306a36Sopenharmony_ci kfree_const(ei->name); 8162306a36Sopenharmony_ci kfree_rcu(ei, rcu); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic inline void put_ei(struct eventfs_inode *ei) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci if (ei) 8762306a36Sopenharmony_ci kref_put(&ei->kref, release_ei); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic inline void free_ei(struct eventfs_inode *ei) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci if (ei) { 9362306a36Sopenharmony_ci ei->is_freed = 1; 9462306a36Sopenharmony_ci put_ei(ei); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic inline struct eventfs_inode *get_ei(struct eventfs_inode *ei) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (ei) 10162306a36Sopenharmony_ci kref_get(&ei->kref); 10262306a36Sopenharmony_ci return ei; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic struct dentry *eventfs_root_lookup(struct inode *dir, 10662306a36Sopenharmony_ci struct dentry *dentry, 10762306a36Sopenharmony_ci unsigned int flags); 10862306a36Sopenharmony_cistatic int eventfs_iterate(struct file *file, struct dir_context *ctx); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void update_attr(struct eventfs_attr *attr, struct iattr *iattr) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci unsigned int ia_valid = iattr->ia_valid; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (ia_valid & ATTR_MODE) { 11562306a36Sopenharmony_ci attr->mode = (attr->mode & ~EVENTFS_MODE_MASK) | 11662306a36Sopenharmony_ci (iattr->ia_mode & EVENTFS_MODE_MASK) | 11762306a36Sopenharmony_ci EVENTFS_SAVE_MODE; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci if (ia_valid & ATTR_UID) { 12062306a36Sopenharmony_ci attr->mode |= EVENTFS_SAVE_UID; 12162306a36Sopenharmony_ci attr->uid = iattr->ia_uid; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci if (ia_valid & ATTR_GID) { 12462306a36Sopenharmony_ci attr->mode |= EVENTFS_SAVE_GID; 12562306a36Sopenharmony_ci attr->gid = iattr->ia_gid; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, 13062306a36Sopenharmony_ci struct iattr *iattr) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci const struct eventfs_entry *entry; 13362306a36Sopenharmony_ci struct eventfs_inode *ei; 13462306a36Sopenharmony_ci const char *name; 13562306a36Sopenharmony_ci int ret; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci mutex_lock(&eventfs_mutex); 13862306a36Sopenharmony_ci ei = dentry->d_fsdata; 13962306a36Sopenharmony_ci if (ei->is_freed) { 14062306a36Sopenharmony_ci /* Do not allow changes if the event is about to be removed. */ 14162306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 14262306a36Sopenharmony_ci return -ENODEV; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Preallocate the children mode array if necessary */ 14662306a36Sopenharmony_ci if (!(dentry->d_inode->i_mode & S_IFDIR)) { 14762306a36Sopenharmony_ci if (!ei->entry_attrs) { 14862306a36Sopenharmony_ci ei->entry_attrs = kcalloc(ei->nr_entries, sizeof(*ei->entry_attrs), 14962306a36Sopenharmony_ci GFP_NOFS); 15062306a36Sopenharmony_ci if (!ei->entry_attrs) { 15162306a36Sopenharmony_ci ret = -ENOMEM; 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ret = simple_setattr(idmap, dentry, iattr); 15862306a36Sopenharmony_ci if (ret < 0) 15962306a36Sopenharmony_ci goto out; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * If this is a dir, then update the ei cache, only the file 16362306a36Sopenharmony_ci * mode is saved in the ei->m_children, and the ownership is 16462306a36Sopenharmony_ci * determined by the parent directory. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci if (dentry->d_inode->i_mode & S_IFDIR) { 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * The events directory dentry is never freed, unless its 16962306a36Sopenharmony_ci * part of an instance that is deleted. It's attr is the 17062306a36Sopenharmony_ci * default for its child files and directories. 17162306a36Sopenharmony_ci * Do not update it. It's not used for its own mode or ownership. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci if (ei->is_events) { 17462306a36Sopenharmony_ci /* But it still needs to know if it was modified */ 17562306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_UID) 17662306a36Sopenharmony_ci ei->attr.mode |= EVENTFS_SAVE_UID; 17762306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_GID) 17862306a36Sopenharmony_ci ei->attr.mode |= EVENTFS_SAVE_GID; 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci update_attr(&ei->attr, iattr); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci } else { 18462306a36Sopenharmony_ci name = dentry->d_name.name; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (int i = 0; i < ei->nr_entries; i++) { 18762306a36Sopenharmony_ci entry = &ei->entries[i]; 18862306a36Sopenharmony_ci if (strcmp(name, entry->name) == 0) { 18962306a36Sopenharmony_ci update_attr(&ei->entry_attrs[i], iattr); 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci out: 19562306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void update_top_events_attr(struct eventfs_inode *ei, struct super_block *sb) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct inode *root; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Only update if the "events" was on the top level */ 20462306a36Sopenharmony_ci if (!ei || !(ei->attr.mode & EVENTFS_TOPLEVEL)) 20562306a36Sopenharmony_ci return; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Get the tracefs root inode. */ 20862306a36Sopenharmony_ci root = d_inode(sb->s_root); 20962306a36Sopenharmony_ci ei->attr.uid = root->i_uid; 21062306a36Sopenharmony_ci ei->attr.gid = root->i_gid; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void set_top_events_ownership(struct inode *inode) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct tracefs_inode *ti = get_tracefs(inode); 21662306a36Sopenharmony_ci struct eventfs_inode *ei = ti->private; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* The top events directory doesn't get automatically updated */ 21962306a36Sopenharmony_ci if (!ei || !ei->is_events || !(ei->attr.mode & EVENTFS_TOPLEVEL)) 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci update_top_events_attr(ei, inode->i_sb); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (!(ei->attr.mode & EVENTFS_SAVE_UID)) 22562306a36Sopenharmony_ci inode->i_uid = ei->attr.uid; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!(ei->attr.mode & EVENTFS_SAVE_GID)) 22862306a36Sopenharmony_ci inode->i_gid = ei->attr.gid; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int eventfs_get_attr(struct mnt_idmap *idmap, 23262306a36Sopenharmony_ci const struct path *path, struct kstat *stat, 23362306a36Sopenharmony_ci u32 request_mask, unsigned int flags) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct dentry *dentry = path->dentry; 23662306a36Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci set_top_events_ownership(inode); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci generic_fillattr(idmap, request_mask, inode, stat); 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int eventfs_permission(struct mnt_idmap *idmap, 24562306a36Sopenharmony_ci struct inode *inode, int mask) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci set_top_events_ownership(inode); 24862306a36Sopenharmony_ci return generic_permission(idmap, inode, mask); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic const struct inode_operations eventfs_root_dir_inode_operations = { 25262306a36Sopenharmony_ci .lookup = eventfs_root_lookup, 25362306a36Sopenharmony_ci .setattr = eventfs_set_attr, 25462306a36Sopenharmony_ci .getattr = eventfs_get_attr, 25562306a36Sopenharmony_ci .permission = eventfs_permission, 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic const struct inode_operations eventfs_file_inode_operations = { 25962306a36Sopenharmony_ci .setattr = eventfs_set_attr, 26062306a36Sopenharmony_ci}; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic const struct file_operations eventfs_file_operations = { 26362306a36Sopenharmony_ci .read = generic_read_dir, 26462306a36Sopenharmony_ci .iterate_shared = eventfs_iterate, 26562306a36Sopenharmony_ci .llseek = generic_file_llseek, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* Return the evenfs_inode of the "events" directory */ 26962306a36Sopenharmony_cistatic struct eventfs_inode *eventfs_find_events(struct dentry *dentry) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct eventfs_inode *ei; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci do { 27462306a36Sopenharmony_ci // The parent is stable because we do not do renames 27562306a36Sopenharmony_ci dentry = dentry->d_parent; 27662306a36Sopenharmony_ci // ... and directories always have d_fsdata 27762306a36Sopenharmony_ci ei = dentry->d_fsdata; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * If the ei is being freed, the ownership of the children 28162306a36Sopenharmony_ci * doesn't matter. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci if (ei->is_freed) { 28462306a36Sopenharmony_ci ei = NULL; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci // Walk upwards until you find the events inode 28862306a36Sopenharmony_ci } while (!ei->is_events); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci update_top_events_attr(ei, dentry->d_sb); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return ei; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void update_inode_attr(struct dentry *dentry, struct inode *inode, 29662306a36Sopenharmony_ci struct eventfs_attr *attr, umode_t mode) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct eventfs_inode *events_ei = eventfs_find_events(dentry); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (!events_ei) 30162306a36Sopenharmony_ci return; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci inode->i_mode = mode; 30462306a36Sopenharmony_ci inode->i_uid = events_ei->attr.uid; 30562306a36Sopenharmony_ci inode->i_gid = events_ei->attr.gid; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (!attr) 30862306a36Sopenharmony_ci return; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (attr->mode & EVENTFS_SAVE_MODE) 31162306a36Sopenharmony_ci inode->i_mode = attr->mode & EVENTFS_MODE_MASK; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (attr->mode & EVENTFS_SAVE_UID) 31462306a36Sopenharmony_ci inode->i_uid = attr->uid; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (attr->mode & EVENTFS_SAVE_GID) 31762306a36Sopenharmony_ci inode->i_gid = attr->gid; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/** 32162306a36Sopenharmony_ci * lookup_file - look up a file in the tracefs filesystem 32262306a36Sopenharmony_ci * @dentry: the dentry to look up 32362306a36Sopenharmony_ci * @mode: the permission that the file should have. 32462306a36Sopenharmony_ci * @attr: saved attributes changed by user 32562306a36Sopenharmony_ci * @data: something that the caller will want to get to later on. 32662306a36Sopenharmony_ci * @fop: struct file_operations that should be used for this file. 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * This function creates a dentry that represents a file in the eventsfs_inode 32962306a36Sopenharmony_ci * directory. The inode.i_private pointer will point to @data in the open() 33062306a36Sopenharmony_ci * call. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_cistatic struct dentry *lookup_file(struct eventfs_inode *parent_ei, 33362306a36Sopenharmony_ci struct dentry *dentry, 33462306a36Sopenharmony_ci umode_t mode, 33562306a36Sopenharmony_ci struct eventfs_attr *attr, 33662306a36Sopenharmony_ci void *data, 33762306a36Sopenharmony_ci const struct file_operations *fop) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct tracefs_inode *ti; 34062306a36Sopenharmony_ci struct inode *inode; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!(mode & S_IFMT)) 34362306a36Sopenharmony_ci mode |= S_IFREG; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (WARN_ON_ONCE(!S_ISREG(mode))) 34662306a36Sopenharmony_ci return ERR_PTR(-EIO); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci inode = tracefs_get_inode(dentry->d_sb); 34962306a36Sopenharmony_ci if (unlikely(!inode)) 35062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* If the user updated the directory's attributes, use them */ 35362306a36Sopenharmony_ci update_inode_attr(dentry, inode, attr, mode); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci inode->i_op = &eventfs_file_inode_operations; 35662306a36Sopenharmony_ci inode->i_fop = fop; 35762306a36Sopenharmony_ci inode->i_private = data; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* All files will have the same inode number */ 36062306a36Sopenharmony_ci inode->i_ino = EVENTFS_FILE_INODE_INO; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ti = get_tracefs(inode); 36362306a36Sopenharmony_ci ti->flags |= TRACEFS_EVENT_INODE; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci // Files have their parent's ei as their fsdata 36662306a36Sopenharmony_ci dentry->d_fsdata = get_ei(parent_ei); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci d_add(dentry, inode); 36962306a36Sopenharmony_ci return NULL; 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/** 37362306a36Sopenharmony_ci * lookup_dir_entry - look up a dir in the tracefs filesystem 37462306a36Sopenharmony_ci * @dentry: the directory to look up 37562306a36Sopenharmony_ci * @ei: the eventfs_inode that represents the directory to create 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * This function will look up a dentry for a directory represented by 37862306a36Sopenharmony_ci * a eventfs_inode. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic struct dentry *lookup_dir_entry(struct dentry *dentry, 38162306a36Sopenharmony_ci struct eventfs_inode *pei, struct eventfs_inode *ei) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct tracefs_inode *ti; 38462306a36Sopenharmony_ci struct inode *inode; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci inode = tracefs_get_inode(dentry->d_sb); 38762306a36Sopenharmony_ci if (unlikely(!inode)) 38862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* If the user updated the directory's attributes, use them */ 39162306a36Sopenharmony_ci update_inode_attr(dentry, inode, &ei->attr, 39262306a36Sopenharmony_ci S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci inode->i_op = &eventfs_root_dir_inode_operations; 39562306a36Sopenharmony_ci inode->i_fop = &eventfs_file_operations; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* All directories will have the same inode number */ 39862306a36Sopenharmony_ci inode->i_ino = eventfs_dir_ino(ei); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ti = get_tracefs(inode); 40162306a36Sopenharmony_ci ti->flags |= TRACEFS_EVENT_INODE; 40262306a36Sopenharmony_ci /* Only directories have ti->private set to an ei, not files */ 40362306a36Sopenharmony_ci ti->private = ei; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci dentry->d_fsdata = get_ei(ei); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci d_add(dentry, inode); 40862306a36Sopenharmony_ci return NULL; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic inline struct eventfs_inode *alloc_ei(const char *name) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct eventfs_inode *ei = kzalloc(sizeof(*ei), GFP_KERNEL); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!ei) 41662306a36Sopenharmony_ci return NULL; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ei->name = kstrdup_const(name, GFP_KERNEL); 41962306a36Sopenharmony_ci if (!ei->name) { 42062306a36Sopenharmony_ci kfree(ei); 42162306a36Sopenharmony_ci return NULL; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci kref_init(&ei->kref); 42462306a36Sopenharmony_ci return ei; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/** 42862306a36Sopenharmony_ci * eventfs_d_release - dentry is going away 42962306a36Sopenharmony_ci * @dentry: dentry which has the reference to remove. 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * Remove the association between a dentry from an eventfs_inode. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_civoid eventfs_d_release(struct dentry *dentry) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci put_ei(dentry->d_fsdata); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/** 43962306a36Sopenharmony_ci * lookup_file_dentry - create a dentry for a file of an eventfs_inode 44062306a36Sopenharmony_ci * @ei: the eventfs_inode that the file will be created under 44162306a36Sopenharmony_ci * @idx: the index into the entry_attrs[] of the @ei 44262306a36Sopenharmony_ci * @parent: The parent dentry of the created file. 44362306a36Sopenharmony_ci * @name: The name of the file to create 44462306a36Sopenharmony_ci * @mode: The mode of the file. 44562306a36Sopenharmony_ci * @data: The data to use to set the inode of the file with on open() 44662306a36Sopenharmony_ci * @fops: The fops of the file to be created. 44762306a36Sopenharmony_ci * 44862306a36Sopenharmony_ci * Create a dentry for a file of an eventfs_inode @ei and place it into the 44962306a36Sopenharmony_ci * address located at @e_dentry. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_cistatic struct dentry * 45262306a36Sopenharmony_cilookup_file_dentry(struct dentry *dentry, 45362306a36Sopenharmony_ci struct eventfs_inode *ei, int idx, 45462306a36Sopenharmony_ci umode_t mode, void *data, 45562306a36Sopenharmony_ci const struct file_operations *fops) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct eventfs_attr *attr = NULL; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (ei->entry_attrs) 46062306a36Sopenharmony_ci attr = &ei->entry_attrs[idx]; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return lookup_file(ei, dentry, mode, attr, data, fops); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/** 46662306a36Sopenharmony_ci * eventfs_root_lookup - lookup routine to create file/dir 46762306a36Sopenharmony_ci * @dir: in which a lookup is being done 46862306a36Sopenharmony_ci * @dentry: file/dir dentry 46962306a36Sopenharmony_ci * @flags: Just passed to simple_lookup() 47062306a36Sopenharmony_ci * 47162306a36Sopenharmony_ci * Used to create dynamic file/dir with-in @dir, search with-in @ei 47262306a36Sopenharmony_ci * list, if @dentry found go ahead and create the file/dir 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic struct dentry *eventfs_root_lookup(struct inode *dir, 47662306a36Sopenharmony_ci struct dentry *dentry, 47762306a36Sopenharmony_ci unsigned int flags) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct eventfs_inode *ei_child; 48062306a36Sopenharmony_ci struct tracefs_inode *ti; 48162306a36Sopenharmony_ci struct eventfs_inode *ei; 48262306a36Sopenharmony_ci const char *name = dentry->d_name.name; 48362306a36Sopenharmony_ci struct dentry *result = NULL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ti = get_tracefs(dir); 48662306a36Sopenharmony_ci if (!(ti->flags & TRACEFS_EVENT_INODE)) 48762306a36Sopenharmony_ci return ERR_PTR(-EIO); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci mutex_lock(&eventfs_mutex); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ei = ti->private; 49262306a36Sopenharmony_ci if (!ei || ei->is_freed) 49362306a36Sopenharmony_ci goto out; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci list_for_each_entry(ei_child, &ei->children, list) { 49662306a36Sopenharmony_ci if (strcmp(ei_child->name, name) != 0) 49762306a36Sopenharmony_ci continue; 49862306a36Sopenharmony_ci if (ei_child->is_freed) 49962306a36Sopenharmony_ci goto out; 50062306a36Sopenharmony_ci result = lookup_dir_entry(dentry, ei, ei_child); 50162306a36Sopenharmony_ci goto out; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci for (int i = 0; i < ei->nr_entries; i++) { 50562306a36Sopenharmony_ci void *data; 50662306a36Sopenharmony_ci umode_t mode; 50762306a36Sopenharmony_ci const struct file_operations *fops; 50862306a36Sopenharmony_ci const struct eventfs_entry *entry = &ei->entries[i]; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (strcmp(name, entry->name) != 0) 51162306a36Sopenharmony_ci continue; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci data = ei->data; 51462306a36Sopenharmony_ci if (entry->callback(name, &mode, &data, &fops) <= 0) 51562306a36Sopenharmony_ci goto out; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci result = lookup_file_dentry(dentry, ei, i, mode, data, fops); 51862306a36Sopenharmony_ci goto out; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci out: 52162306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 52262306a36Sopenharmony_ci return result; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* 52662306a36Sopenharmony_ci * Walk the children of a eventfs_inode to fill in getdents(). 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_cistatic int eventfs_iterate(struct file *file, struct dir_context *ctx) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci const struct file_operations *fops; 53162306a36Sopenharmony_ci struct inode *f_inode = file_inode(file); 53262306a36Sopenharmony_ci const struct eventfs_entry *entry; 53362306a36Sopenharmony_ci struct eventfs_inode *ei_child; 53462306a36Sopenharmony_ci struct tracefs_inode *ti; 53562306a36Sopenharmony_ci struct eventfs_inode *ei; 53662306a36Sopenharmony_ci const char *name; 53762306a36Sopenharmony_ci umode_t mode; 53862306a36Sopenharmony_ci int idx; 53962306a36Sopenharmony_ci int ret = -EINVAL; 54062306a36Sopenharmony_ci int ino; 54162306a36Sopenharmony_ci int i, r, c; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci ti = get_tracefs(f_inode); 54762306a36Sopenharmony_ci if (!(ti->flags & TRACEFS_EVENT_INODE)) 54862306a36Sopenharmony_ci return -EINVAL; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci c = ctx->pos - 2; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci idx = srcu_read_lock(&eventfs_srcu); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci mutex_lock(&eventfs_mutex); 55562306a36Sopenharmony_ci ei = READ_ONCE(ti->private); 55662306a36Sopenharmony_ci if (ei && ei->is_freed) 55762306a36Sopenharmony_ci ei = NULL; 55862306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (!ei) 56162306a36Sopenharmony_ci goto out; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * Need to create the dentries and inodes to have a consistent 56562306a36Sopenharmony_ci * inode number. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci ret = 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Start at 'c' to jump over already read entries */ 57062306a36Sopenharmony_ci for (i = c; i < ei->nr_entries; i++, ctx->pos++) { 57162306a36Sopenharmony_ci void *cdata = ei->data; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci entry = &ei->entries[i]; 57462306a36Sopenharmony_ci name = entry->name; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci mutex_lock(&eventfs_mutex); 57762306a36Sopenharmony_ci /* If ei->is_freed then just bail here, nothing more to do */ 57862306a36Sopenharmony_ci if (ei->is_freed) { 57962306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 58062306a36Sopenharmony_ci goto out; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci r = entry->callback(name, &mode, &cdata, &fops); 58362306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 58462306a36Sopenharmony_ci if (r <= 0) 58562306a36Sopenharmony_ci continue; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci ino = EVENTFS_FILE_INODE_INO; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!dir_emit(ctx, name, strlen(name), ino, DT_REG)) 59062306a36Sopenharmony_ci goto out; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Subtract the skipped entries above */ 59462306a36Sopenharmony_ci c -= min((unsigned int)c, (unsigned int)ei->nr_entries); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci list_for_each_entry_srcu(ei_child, &ei->children, list, 59762306a36Sopenharmony_ci srcu_read_lock_held(&eventfs_srcu)) { 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (c > 0) { 60062306a36Sopenharmony_ci c--; 60162306a36Sopenharmony_ci continue; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ctx->pos++; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (ei_child->is_freed) 60762306a36Sopenharmony_ci continue; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci name = ei_child->name; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ino = eventfs_dir_ino(ei_child); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!dir_emit(ctx, name, strlen(name), ino, DT_DIR)) 61462306a36Sopenharmony_ci goto out_dec; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci ret = 1; 61762306a36Sopenharmony_ci out: 61862306a36Sopenharmony_ci srcu_read_unlock(&eventfs_srcu, idx); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return ret; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci out_dec: 62362306a36Sopenharmony_ci /* Incremented ctx->pos without adding something, reset it */ 62462306a36Sopenharmony_ci ctx->pos--; 62562306a36Sopenharmony_ci goto out; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci/** 62962306a36Sopenharmony_ci * eventfs_create_dir - Create the eventfs_inode for this directory 63062306a36Sopenharmony_ci * @name: The name of the directory to create. 63162306a36Sopenharmony_ci * @parent: The eventfs_inode of the parent directory. 63262306a36Sopenharmony_ci * @entries: A list of entries that represent the files under this directory 63362306a36Sopenharmony_ci * @size: The number of @entries 63462306a36Sopenharmony_ci * @data: The default data to pass to the files (an entry may override it). 63562306a36Sopenharmony_ci * 63662306a36Sopenharmony_ci * This function creates the descriptor to represent a directory in the 63762306a36Sopenharmony_ci * eventfs. This descriptor is an eventfs_inode, and it is returned to be 63862306a36Sopenharmony_ci * used to create other children underneath. 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * The @entries is an array of eventfs_entry structures which has: 64162306a36Sopenharmony_ci * const char *name 64262306a36Sopenharmony_ci * eventfs_callback callback; 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * The name is the name of the file, and the callback is a pointer to a function 64562306a36Sopenharmony_ci * that will be called when the file is reference (either by lookup or by 64662306a36Sopenharmony_ci * reading a directory). The callback is of the prototype: 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * int callback(const char *name, umode_t *mode, void **data, 64962306a36Sopenharmony_ci * const struct file_operations **fops); 65062306a36Sopenharmony_ci * 65162306a36Sopenharmony_ci * When a file needs to be created, this callback will be called with 65262306a36Sopenharmony_ci * name = the name of the file being created (so that the same callback 65362306a36Sopenharmony_ci * may be used for multiple files). 65462306a36Sopenharmony_ci * mode = a place to set the file's mode 65562306a36Sopenharmony_ci * data = A pointer to @data, and the callback may replace it, which will 65662306a36Sopenharmony_ci * cause the file created to pass the new data to the open() call. 65762306a36Sopenharmony_ci * fops = the fops to use for the created file. 65862306a36Sopenharmony_ci * 65962306a36Sopenharmony_ci * NB. @callback is called while holding internal locks of the eventfs 66062306a36Sopenharmony_ci * system. The callback must not call any code that might also call into 66162306a36Sopenharmony_ci * the tracefs or eventfs system or it will risk creating a deadlock. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_cistruct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode *parent, 66462306a36Sopenharmony_ci const struct eventfs_entry *entries, 66562306a36Sopenharmony_ci int size, void *data) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct eventfs_inode *ei; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!parent) 67062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ei = alloc_ei(name); 67362306a36Sopenharmony_ci if (!ei) 67462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ei->entries = entries; 67762306a36Sopenharmony_ci ei->nr_entries = size; 67862306a36Sopenharmony_ci ei->data = data; 67962306a36Sopenharmony_ci INIT_LIST_HEAD(&ei->children); 68062306a36Sopenharmony_ci INIT_LIST_HEAD(&ei->list); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci mutex_lock(&eventfs_mutex); 68362306a36Sopenharmony_ci if (!parent->is_freed) 68462306a36Sopenharmony_ci list_add_tail(&ei->list, &parent->children); 68562306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Was the parent freed? */ 68862306a36Sopenharmony_ci if (list_empty(&ei->list)) { 68962306a36Sopenharmony_ci free_ei(ei); 69062306a36Sopenharmony_ci ei = NULL; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci return ei; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci/** 69662306a36Sopenharmony_ci * eventfs_create_events_dir - create the top level events directory 69762306a36Sopenharmony_ci * @name: The name of the top level directory to create. 69862306a36Sopenharmony_ci * @parent: Parent dentry for this file in the tracefs directory. 69962306a36Sopenharmony_ci * @entries: A list of entries that represent the files under this directory 70062306a36Sopenharmony_ci * @size: The number of @entries 70162306a36Sopenharmony_ci * @data: The default data to pass to the files (an entry may override it). 70262306a36Sopenharmony_ci * 70362306a36Sopenharmony_ci * This function creates the top of the trace event directory. 70462306a36Sopenharmony_ci * 70562306a36Sopenharmony_ci * See eventfs_create_dir() for use of @entries. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_cistruct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry *parent, 70862306a36Sopenharmony_ci const struct eventfs_entry *entries, 70962306a36Sopenharmony_ci int size, void *data) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct dentry *dentry = tracefs_start_creating(name, parent); 71262306a36Sopenharmony_ci struct eventfs_inode *ei; 71362306a36Sopenharmony_ci struct tracefs_inode *ti; 71462306a36Sopenharmony_ci struct inode *inode; 71562306a36Sopenharmony_ci kuid_t uid; 71662306a36Sopenharmony_ci kgid_t gid; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (security_locked_down(LOCKDOWN_TRACEFS)) 71962306a36Sopenharmony_ci return NULL; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (IS_ERR(dentry)) 72262306a36Sopenharmony_ci return ERR_CAST(dentry); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ei = alloc_ei(name); 72562306a36Sopenharmony_ci if (!ei) 72662306a36Sopenharmony_ci goto fail; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci inode = tracefs_get_inode(dentry->d_sb); 72962306a36Sopenharmony_ci if (unlikely(!inode)) 73062306a36Sopenharmony_ci goto fail; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci // Note: we have a ref to the dentry from tracefs_start_creating() 73362306a36Sopenharmony_ci ei->events_dir = dentry; 73462306a36Sopenharmony_ci ei->entries = entries; 73562306a36Sopenharmony_ci ei->nr_entries = size; 73662306a36Sopenharmony_ci ei->is_events = 1; 73762306a36Sopenharmony_ci ei->data = data; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Save the ownership of this directory */ 74062306a36Sopenharmony_ci uid = d_inode(dentry->d_parent)->i_uid; 74162306a36Sopenharmony_ci gid = d_inode(dentry->d_parent)->i_gid; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* 74462306a36Sopenharmony_ci * If the events directory is of the top instance, then parent 74562306a36Sopenharmony_ci * is NULL. Set the attr.mode to reflect this and its permissions will 74662306a36Sopenharmony_ci * default to the tracefs root dentry. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ci if (!parent) 74962306a36Sopenharmony_ci ei->attr.mode = EVENTFS_TOPLEVEL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* This is used as the default ownership of the files and directories */ 75262306a36Sopenharmony_ci ei->attr.uid = uid; 75362306a36Sopenharmony_ci ei->attr.gid = gid; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci INIT_LIST_HEAD(&ei->children); 75662306a36Sopenharmony_ci INIT_LIST_HEAD(&ei->list); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci ti = get_tracefs(inode); 75962306a36Sopenharmony_ci ti->flags |= TRACEFS_EVENT_INODE | TRACEFS_EVENT_TOP_INODE; 76062306a36Sopenharmony_ci ti->private = ei; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; 76362306a36Sopenharmony_ci inode->i_uid = uid; 76462306a36Sopenharmony_ci inode->i_gid = gid; 76562306a36Sopenharmony_ci inode->i_op = &eventfs_root_dir_inode_operations; 76662306a36Sopenharmony_ci inode->i_fop = &eventfs_file_operations; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci dentry->d_fsdata = get_ei(ei); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* 77162306a36Sopenharmony_ci * Keep all eventfs directories with i_nlink == 1. 77262306a36Sopenharmony_ci * Due to the dynamic nature of the dentry creations and not 77362306a36Sopenharmony_ci * wanting to add a pointer to the parent eventfs_inode in the 77462306a36Sopenharmony_ci * eventfs_inode structure, keeping the i_nlink in sync with the 77562306a36Sopenharmony_ci * number of directories would cause too much complexity for 77662306a36Sopenharmony_ci * something not worth much. Keeping directory links at 1 77762306a36Sopenharmony_ci * tells userspace not to trust the link number. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci d_instantiate(dentry, inode); 78062306a36Sopenharmony_ci /* The dentry of the "events" parent does keep track though */ 78162306a36Sopenharmony_ci inc_nlink(dentry->d_parent->d_inode); 78262306a36Sopenharmony_ci fsnotify_mkdir(dentry->d_parent->d_inode, dentry); 78362306a36Sopenharmony_ci tracefs_end_creating(dentry); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return ei; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci fail: 78862306a36Sopenharmony_ci free_ei(ei); 78962306a36Sopenharmony_ci tracefs_failed_creating(dentry); 79062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci/** 79462306a36Sopenharmony_ci * eventfs_remove_rec - remove eventfs dir or file from list 79562306a36Sopenharmony_ci * @ei: eventfs_inode to be removed. 79662306a36Sopenharmony_ci * @level: prevent recursion from going more than 3 levels deep. 79762306a36Sopenharmony_ci * 79862306a36Sopenharmony_ci * This function recursively removes eventfs_inodes which 79962306a36Sopenharmony_ci * contains info of files and/or directories. 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_cistatic void eventfs_remove_rec(struct eventfs_inode *ei, int level) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct eventfs_inode *ei_child; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* 80662306a36Sopenharmony_ci * Check recursion depth. It should never be greater than 3: 80762306a36Sopenharmony_ci * 0 - events/ 80862306a36Sopenharmony_ci * 1 - events/group/ 80962306a36Sopenharmony_ci * 2 - events/group/event/ 81062306a36Sopenharmony_ci * 3 - events/group/event/file 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci if (WARN_ON_ONCE(level > 3)) 81362306a36Sopenharmony_ci return; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* search for nested folders or files */ 81662306a36Sopenharmony_ci list_for_each_entry(ei_child, &ei->children, list) 81762306a36Sopenharmony_ci eventfs_remove_rec(ei_child, level + 1); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci list_del(&ei->list); 82062306a36Sopenharmony_ci free_ei(ei); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/** 82462306a36Sopenharmony_ci * eventfs_remove_dir - remove eventfs dir or file from list 82562306a36Sopenharmony_ci * @ei: eventfs_inode to be removed. 82662306a36Sopenharmony_ci * 82762306a36Sopenharmony_ci * This function acquire the eventfs_mutex lock and call eventfs_remove_rec() 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_civoid eventfs_remove_dir(struct eventfs_inode *ei) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci if (!ei) 83262306a36Sopenharmony_ci return; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci mutex_lock(&eventfs_mutex); 83562306a36Sopenharmony_ci eventfs_remove_rec(ei, 0); 83662306a36Sopenharmony_ci mutex_unlock(&eventfs_mutex); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci/** 84062306a36Sopenharmony_ci * eventfs_remove_events_dir - remove the top level eventfs directory 84162306a36Sopenharmony_ci * @ei: the event_inode returned by eventfs_create_events_dir(). 84262306a36Sopenharmony_ci * 84362306a36Sopenharmony_ci * This function removes the events main directory 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_civoid eventfs_remove_events_dir(struct eventfs_inode *ei) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct dentry *dentry; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci dentry = ei->events_dir; 85062306a36Sopenharmony_ci if (!dentry) 85162306a36Sopenharmony_ci return; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci ei->events_dir = NULL; 85462306a36Sopenharmony_ci eventfs_remove_dir(ei); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* 85762306a36Sopenharmony_ci * Matches the dget() done by tracefs_start_creating() 85862306a36Sopenharmony_ci * in eventfs_create_events_dir() when it the dentry was 85962306a36Sopenharmony_ci * created. In other words, it's a normal dentry that 86062306a36Sopenharmony_ci * sticks around while the other ei->dentry are created 86162306a36Sopenharmony_ci * and destroyed dynamically. 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci d_invalidate(dentry); 86462306a36Sopenharmony_ci dput(dentry); 86562306a36Sopenharmony_ci} 866