diff options
Diffstat (limited to 'fs/tracefs/event_inode.c')
-rw-r--r-- | fs/tracefs/event_inode.c | 282 |
1 files changed, 139 insertions, 143 deletions
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 894c6ca1e500..5d88c184f0fc 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -49,8 +49,12 @@ static struct eventfs_root_inode *get_root_inode(struct eventfs_inode *ei) /* Just try to make something consistent and unique */ static int eventfs_dir_ino(struct eventfs_inode *ei) { - if (!ei->ino) + if (!ei->ino) { ei->ino = get_next_ino(); + /* Must not have the file inode number */ + if (ei->ino == EVENTFS_FILE_INODE_INO) + ei->ino = get_next_ino(); + } return ei->ino; } @@ -68,11 +72,25 @@ enum { EVENTFS_SAVE_MODE = BIT(16), EVENTFS_SAVE_UID = BIT(17), EVENTFS_SAVE_GID = BIT(18), - EVENTFS_TOPLEVEL = BIT(19), }; #define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1) +static void free_ei_rcu(struct rcu_head *rcu) +{ + struct eventfs_inode *ei = container_of(rcu, struct eventfs_inode, rcu); + struct eventfs_root_inode *rei; + + kfree(ei->entry_attrs); + kfree_const(ei->name); + if (ei->is_events) { + rei = get_root_inode(ei); + kfree(rei); + } else { + kfree(ei); + } +} + /* * eventfs_inode reference count management. * @@ -84,18 +102,17 @@ enum { static void release_ei(struct kref *ref) { struct eventfs_inode *ei = container_of(ref, struct eventfs_inode, kref); - struct eventfs_root_inode *rei; + const struct eventfs_entry *entry; WARN_ON_ONCE(!ei->is_freed); - kfree(ei->entry_attrs); - kfree_const(ei->name); - if (ei->is_events) { - rei = get_root_inode(ei); - kfree_rcu(rei, ei.rcu); - } else { - kfree_rcu(ei, rcu); + for (int i = 0; i < ei->nr_entries; i++) { + entry = &ei->entries[i]; + if (entry->release) + entry->release(entry->name, ei->data); } + + call_rcu(&ei->rcu, free_ei_rcu); } static inline void put_ei(struct eventfs_inode *ei) @@ -112,6 +129,18 @@ static inline void free_ei(struct eventfs_inode *ei) } } +/* + * Called when creation of an ei fails, do not call release() functions. + */ +static inline void cleanup_ei(struct eventfs_inode *ei) +{ + if (ei) { + /* Set nr_entries to 0 to prevent release() function being called */ + ei->nr_entries = 0; + free_ei(ei); + } +} + static inline struct eventfs_inode *get_ei(struct eventfs_inode *ei) { if (ei) @@ -181,21 +210,9 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, * determined by the parent directory. */ if (dentry->d_inode->i_mode & S_IFDIR) { - /* - * The events directory dentry is never freed, unless its - * part of an instance that is deleted. It's attr is the - * default for its child files and directories. - * Do not update it. It's not used for its own mode or ownership. - */ - if (ei->is_events) { - /* But it still needs to know if it was modified */ - if (iattr->ia_valid & ATTR_UID) - ei->attr.mode |= EVENTFS_SAVE_UID; - if (iattr->ia_valid & ATTR_GID) - ei->attr.mode |= EVENTFS_SAVE_GID; - } else { + /* Just use the inode permissions for the events directory */ + if (!ei->is_events) update_attr(&ei->attr, iattr); - } } else { name = dentry->d_name.name; @@ -213,63 +230,9 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, return ret; } -static void update_top_events_attr(struct eventfs_inode *ei, struct super_block *sb) -{ - struct inode *root; - - /* Only update if the "events" was on the top level */ - if (!ei || !(ei->attr.mode & EVENTFS_TOPLEVEL)) - return; - - /* Get the tracefs root inode. */ - root = d_inode(sb->s_root); - ei->attr.uid = root->i_uid; - ei->attr.gid = root->i_gid; -} - -static void set_top_events_ownership(struct inode *inode) -{ - struct tracefs_inode *ti = get_tracefs(inode); - struct eventfs_inode *ei = ti->private; - - /* The top events directory doesn't get automatically updated */ - if (!ei || !ei->is_events || !(ei->attr.mode & EVENTFS_TOPLEVEL)) - return; - - update_top_events_attr(ei, inode->i_sb); - - if (!(ei->attr.mode & EVENTFS_SAVE_UID)) - inode->i_uid = ei->attr.uid; - - if (!(ei->attr.mode & EVENTFS_SAVE_GID)) - inode->i_gid = ei->attr.gid; -} - -static int eventfs_get_attr(struct mnt_idmap *idmap, - const struct path *path, struct kstat *stat, - u32 request_mask, unsigned int flags) -{ - struct dentry *dentry = path->dentry; - struct inode *inode = d_backing_inode(dentry); - - set_top_events_ownership(inode); - - generic_fillattr(idmap, request_mask, inode, stat); - return 0; -} - -static int eventfs_permission(struct mnt_idmap *idmap, - struct inode *inode, int mask) -{ - set_top_events_ownership(inode); - return generic_permission(idmap, inode, mask); -} - -static const struct inode_operations eventfs_root_dir_inode_operations = { +static const struct inode_operations eventfs_dir_inode_operations = { .lookup = eventfs_root_lookup, .setattr = eventfs_set_attr, - .getattr = eventfs_get_attr, - .permission = eventfs_permission, }; static const struct inode_operations eventfs_file_inode_operations = { @@ -282,56 +245,109 @@ static const struct file_operations eventfs_file_operations = { .llseek = generic_file_llseek, }; -/* Return the evenfs_inode of the "events" directory */ -static struct eventfs_inode *eventfs_find_events(struct dentry *dentry) +static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t uid, + bool update_gid, kgid_t gid, int level) { - struct eventfs_inode *ei; + struct eventfs_inode *ei_child; - do { - // The parent is stable because we do not do renames - dentry = dentry->d_parent; - // ... and directories always have d_fsdata - ei = dentry->d_fsdata; + /* Update events/<system>/<event> */ + if (WARN_ON_ONCE(level > 3)) + return; - /* - * If the ei is being freed, the ownership of the children - * doesn't matter. - */ - if (ei->is_freed) { - ei = NULL; - break; - } - // Walk upwards until you find the events inode - } while (!ei->is_events); + if (update_uid) { + ei->attr.mode &= ~EVENTFS_SAVE_UID; + ei->attr.uid = uid; + } + + if (update_gid) { + ei->attr.mode &= ~EVENTFS_SAVE_GID; + ei->attr.gid = gid; + } - update_top_events_attr(ei, dentry->d_sb); + list_for_each_entry(ei_child, &ei->children, list) { + eventfs_set_attrs(ei_child, update_uid, uid, update_gid, gid, level + 1); + } + + if (!ei->entry_attrs) + return; + + for (int i = 0; i < ei->nr_entries; i++) { + if (update_uid) { + ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_UID; + ei->entry_attrs[i].uid = uid; + } + if (update_gid) { + ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_GID; + ei->entry_attrs[i].gid = gid; + } + } - return ei; } -static void update_inode_attr(struct dentry *dentry, struct inode *inode, - struct eventfs_attr *attr, umode_t mode) +/* + * On a remount of tracefs, if UID or GID options are set, then + * the mount point inode permissions should be used. + * Reset the saved permission flags appropriately. + */ +void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid) { - struct eventfs_inode *events_ei = eventfs_find_events(dentry); + struct eventfs_inode *ei = ti->private; - if (!events_ei) + /* Only the events directory does the updates */ + if (!ei || !ei->is_events || ei->is_freed) return; - inode->i_mode = mode; - inode->i_uid = events_ei->attr.uid; - inode->i_gid = events_ei->attr.gid; - - if (!attr) - return; + eventfs_set_attrs(ei, update_uid, ti->vfs_inode.i_uid, + update_gid, ti->vfs_inode.i_gid, 0); +} - if (attr->mode & EVENTFS_SAVE_MODE) +static void update_inode_attr(struct inode *inode, umode_t mode, + struct eventfs_attr *attr, struct eventfs_root_inode *rei) +{ + if (attr && attr->mode & EVENTFS_SAVE_MODE) inode->i_mode = attr->mode & EVENTFS_MODE_MASK; + else + inode->i_mode = mode; - if (attr->mode & EVENTFS_SAVE_UID) + if (attr && attr->mode & EVENTFS_SAVE_UID) inode->i_uid = attr->uid; + else + inode->i_uid = rei->ei.attr.uid; - if (attr->mode & EVENTFS_SAVE_GID) + if (attr && attr->mode & EVENTFS_SAVE_GID) inode->i_gid = attr->gid; + else + inode->i_gid = rei->ei.attr.gid; +} + +static struct inode *eventfs_get_inode(struct dentry *dentry, struct eventfs_attr *attr, + umode_t mode, struct eventfs_inode *ei) +{ + struct eventfs_root_inode *rei; + struct eventfs_inode *pei; + struct tracefs_inode *ti; + struct inode *inode; + + inode = tracefs_get_inode(dentry->d_sb); + if (!inode) + return NULL; + + ti = get_tracefs(inode); + ti->private = ei; + ti->flags |= TRACEFS_EVENT_INODE; + + /* Find the top dentry that holds the "events" directory */ + do { + dentry = dentry->d_parent; + /* Directories always have d_fsdata */ + pei = dentry->d_fsdata; + } while (!pei->is_events); + + rei = get_root_inode(pei); + + update_inode_attr(inode, mode, attr, rei); + + return inode; } /** @@ -354,7 +370,6 @@ static struct dentry *lookup_file(struct eventfs_inode *parent_ei, void *data, const struct file_operations *fop) { - struct tracefs_inode *ti; struct inode *inode; if (!(mode & S_IFMT)) @@ -363,13 +378,11 @@ static struct dentry *lookup_file(struct eventfs_inode *parent_ei, if (WARN_ON_ONCE(!S_ISREG(mode))) return ERR_PTR(-EIO); - inode = tracefs_get_inode(dentry->d_sb); + /* Only directories have ti->private set to an ei, not files */ + inode = eventfs_get_inode(dentry, attr, mode, NULL); if (unlikely(!inode)) return ERR_PTR(-ENOMEM); - /* If the user updated the directory's attributes, use them */ - update_inode_attr(dentry, inode, attr, mode); - inode->i_op = &eventfs_file_inode_operations; inode->i_fop = fop; inode->i_private = data; @@ -377,9 +390,6 @@ static struct dentry *lookup_file(struct eventfs_inode *parent_ei, /* All files will have the same inode number */ inode->i_ino = EVENTFS_FILE_INODE_INO; - ti = get_tracefs(inode); - ti->flags |= TRACEFS_EVENT_INODE; - // Files have their parent's ei as their fsdata dentry->d_fsdata = get_ei(parent_ei); @@ -399,28 +409,19 @@ static struct dentry *lookup_file(struct eventfs_inode *parent_ei, static struct dentry *lookup_dir_entry(struct dentry *dentry, struct eventfs_inode *pei, struct eventfs_inode *ei) { - struct tracefs_inode *ti; struct inode *inode; + umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; - inode = tracefs_get_inode(dentry->d_sb); + inode = eventfs_get_inode(dentry, &ei->attr, mode, ei); if (unlikely(!inode)) return ERR_PTR(-ENOMEM); - /* If the user updated the directory's attributes, use them */ - update_inode_attr(dentry, inode, &ei->attr, - S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); - - inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_op = &eventfs_dir_inode_operations; inode->i_fop = &eventfs_file_operations; /* All directories will have the same inode number */ inode->i_ino = eventfs_dir_ino(ei); - ti = get_tracefs(inode); - ti->flags |= TRACEFS_EVENT_INODE; - /* Only directories have ti->private set to an ei, not files */ - ti->private = ei; - dentry->d_fsdata = get_ei(ei); d_add(dentry, inode); @@ -734,7 +735,7 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode /* Was the parent freed? */ if (list_empty(&ei->list)) { - free_ei(ei); + cleanup_ei(ei); ei = NULL; } return ei; @@ -791,14 +792,9 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry gid = d_inode(dentry->d_parent)->i_gid; /* - * If the events directory is of the top instance, then parent - * is NULL. Set the attr.mode to reflect this and its permissions will - * default to the tracefs root dentry. + * The ei->attr will be used as the default values for the + * files beneath this directory. */ - if (!parent) - ei->attr.mode = EVENTFS_TOPLEVEL; - - /* This is used as the default ownership of the files and directories */ ei->attr.uid = uid; ei->attr.gid = gid; @@ -806,13 +802,13 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry INIT_LIST_HEAD(&ei->list); ti = get_tracefs(inode); - ti->flags |= TRACEFS_EVENT_INODE | TRACEFS_EVENT_TOP_INODE; + ti->flags |= TRACEFS_EVENT_INODE; ti->private = ei; inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; inode->i_uid = uid; inode->i_gid = gid; - inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_op = &eventfs_dir_inode_operations; inode->i_fop = &eventfs_file_operations; dentry->d_fsdata = get_ei(ei); @@ -835,7 +831,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry return ei; fail: - free_ei(ei); + cleanup_ei(ei); tracefs_failed_creating(dentry); return ERR_PTR(-ENOMEM); } |