Commit 24f3a63e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'eventfs-v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull eventfs updates from Steven Rostedt:

 - Remove "lookup" parameter of create_dir_dentry() and
   create_file_dentry(). These functions were called by lookup and the
   readdir logic, where readdir needed it to up the ref count of the
   dentry but the lookup did not. A "lookup" parameter was passed in to
   tell it what to do, but this complicated the code. It is better to
   just always up the ref count and require the caller to decrement it,
   even for lookup.

 - Modify the .iterate_shared callback to not use the dcache_readdir()
   logic and just handle what gets displayed by that one function. This
   removes the need for eventfs to hijack the file->private_data from
   the dcache_readdir() "cursor" pointer, and makes the code a bit more
   sane

 - Use the root and instance inodes for default ownership. Instead of
   walking the dentry tree and updating each dentry gid, use the
   getattr(), setattr() and permission() callbacks to set the ownership
   and permissions using the root or instance as the default

 - Some other optimizations with the eventfs iterate_shared logic

 - Hard-code the inodes for eventfs to the same number for files, and
   the same number for directories

 - Have getdent() not create dentries/inodes in iterate_shared() as now
   it has hard-coded inode numbers

 - Use kcalloc() instead of kzalloc() on a list of elements

 - Fix seq_buf warning and make static work properly.

* tag 'eventfs-v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  seq_buf: Make DECLARE_SEQ_BUF() usable
  eventfs: Use kcalloc() instead of kzalloc()
  eventfs: Do not create dentries nor inodes in iterate_shared
  eventfs: Have the inodes all for files and directories all be the same
  eventfs: Shortcut eventfs_iterate() by skipping entries already read
  eventfs: Read ei->entries before ei->children in eventfs_iterate()
  eventfs: Do ctx->pos update for all iterations in eventfs_iterate()
  eventfs: Have eventfs_iterate() stop immediately if ei->is_freed is set
  tracefs/eventfs: Use root and instance inodes as default ownership
  eventfs: Stop using dcache_readdir() for getdents()
  eventfs: Remove "lookup" parameter from create_dir/file_dentry()
parents a2ded784 7a8e9cdf
...@@ -32,6 +32,10 @@ ...@@ -32,6 +32,10 @@
*/ */
static DEFINE_MUTEX(eventfs_mutex); static DEFINE_MUTEX(eventfs_mutex);
/* Choose something "unique" ;-) */
#define EVENTFS_FILE_INODE_INO 0x12c4e37
#define EVENTFS_DIR_INODE_INO 0x134b2f5
/* /*
* The eventfs_inode (ei) itself is protected by SRCU. It is released from * The eventfs_inode (ei) itself is protected by SRCU. It is released from
* its parent's list and will have is_freed set (under eventfs_mutex). * its parent's list and will have is_freed set (under eventfs_mutex).
...@@ -45,6 +49,7 @@ enum { ...@@ -45,6 +49,7 @@ enum {
EVENTFS_SAVE_MODE = BIT(16), EVENTFS_SAVE_MODE = BIT(16),
EVENTFS_SAVE_UID = BIT(17), EVENTFS_SAVE_UID = BIT(17),
EVENTFS_SAVE_GID = BIT(18), EVENTFS_SAVE_GID = BIT(18),
EVENTFS_TOPLEVEL = BIT(19),
}; };
#define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1) #define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1)
...@@ -52,9 +57,7 @@ enum { ...@@ -52,9 +57,7 @@ enum {
static struct dentry *eventfs_root_lookup(struct inode *dir, static struct dentry *eventfs_root_lookup(struct inode *dir,
struct dentry *dentry, struct dentry *dentry,
unsigned int flags); unsigned int flags);
static int dcache_dir_open_wrapper(struct inode *inode, struct file *file); static int eventfs_iterate(struct file *file, struct dir_context *ctx);
static int dcache_readdir_wrapper(struct file *file, struct dir_context *ctx);
static int eventfs_release(struct inode *inode, struct file *file);
static void update_attr(struct eventfs_attr *attr, struct iattr *iattr) static void update_attr(struct eventfs_attr *attr, struct iattr *iattr)
{ {
...@@ -94,7 +97,7 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -94,7 +97,7 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,
/* Preallocate the children mode array if necessary */ /* Preallocate the children mode array if necessary */
if (!(dentry->d_inode->i_mode & S_IFDIR)) { if (!(dentry->d_inode->i_mode & S_IFDIR)) {
if (!ei->entry_attrs) { if (!ei->entry_attrs) {
ei->entry_attrs = kzalloc(sizeof(*ei->entry_attrs) * ei->nr_entries, ei->entry_attrs = kcalloc(ei->nr_entries, sizeof(*ei->entry_attrs),
GFP_NOFS); GFP_NOFS);
if (!ei->entry_attrs) { if (!ei->entry_attrs) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -117,10 +120,17 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -117,10 +120,17 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,
* The events directory dentry is never freed, unless its * The events directory dentry is never freed, unless its
* part of an instance that is deleted. It's attr is the * part of an instance that is deleted. It's attr is the
* default for its child files and directories. * default for its child files and directories.
* Do not update it. It's not used for its own mode or ownership * Do not update it. It's not used for its own mode or ownership.
*/ */
if (!ei->is_events) 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 {
update_attr(&ei->attr, iattr); update_attr(&ei->attr, iattr);
}
} else { } else {
name = dentry->d_name.name; name = dentry->d_name.name;
...@@ -138,9 +148,66 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -138,9 +148,66 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,
return ret; return ret;
} }
static void update_top_events_attr(struct eventfs_inode *ei, struct dentry *dentry)
{
struct inode *inode;
/* Only update if the "events" was on the top level */
if (!ei || !(ei->attr.mode & EVENTFS_TOPLEVEL))
return;
/* Get the tracefs root inode. */
inode = d_inode(dentry->d_sb->s_root);
ei->attr.uid = inode->i_uid;
ei->attr.gid = inode->i_gid;
}
static void set_top_events_ownership(struct inode *inode)
{
struct tracefs_inode *ti = get_tracefs(inode);
struct eventfs_inode *ei = ti->private;
struct dentry *dentry;
/* The top events directory doesn't get automatically updated */
if (!ei || !ei->is_events || !(ei->attr.mode & EVENTFS_TOPLEVEL))
return;
dentry = ei->dentry;
update_top_events_attr(ei, dentry);
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_root_dir_inode_operations = {
.lookup = eventfs_root_lookup, .lookup = eventfs_root_lookup,
.setattr = eventfs_set_attr, .setattr = eventfs_set_attr,
.getattr = eventfs_get_attr,
.permission = eventfs_permission,
}; };
static const struct inode_operations eventfs_file_inode_operations = { static const struct inode_operations eventfs_file_inode_operations = {
...@@ -148,11 +215,9 @@ static const struct inode_operations eventfs_file_inode_operations = { ...@@ -148,11 +215,9 @@ static const struct inode_operations eventfs_file_inode_operations = {
}; };
static const struct file_operations eventfs_file_operations = { static const struct file_operations eventfs_file_operations = {
.open = dcache_dir_open_wrapper,
.read = generic_read_dir, .read = generic_read_dir,
.iterate_shared = dcache_readdir_wrapper, .iterate_shared = eventfs_iterate,
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.release = eventfs_release,
}; };
/* Return the evenfs_inode of the "events" directory */ /* Return the evenfs_inode of the "events" directory */
...@@ -178,6 +243,8 @@ static struct eventfs_inode *eventfs_find_events(struct dentry *dentry) ...@@ -178,6 +243,8 @@ static struct eventfs_inode *eventfs_find_events(struct dentry *dentry)
} while (!ei->is_events); } while (!ei->is_events);
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
update_top_events_attr(ei, dentry);
return ei; return ei;
} }
...@@ -289,6 +356,9 @@ static struct dentry *create_file(const char *name, umode_t mode, ...@@ -289,6 +356,9 @@ static struct dentry *create_file(const char *name, umode_t mode,
inode->i_fop = fop; inode->i_fop = fop;
inode->i_private = data; inode->i_private = data;
/* All files will have the same inode number */
inode->i_ino = EVENTFS_FILE_INODE_INO;
ti = get_tracefs(inode); ti = get_tracefs(inode);
ti->flags |= TRACEFS_EVENT_INODE; ti->flags |= TRACEFS_EVENT_INODE;
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -325,6 +395,9 @@ static struct dentry *create_dir(struct eventfs_inode *ei, struct dentry *parent ...@@ -325,6 +395,9 @@ static struct dentry *create_dir(struct eventfs_inode *ei, struct dentry *parent
inode->i_op = &eventfs_root_dir_inode_operations; inode->i_op = &eventfs_root_dir_inode_operations;
inode->i_fop = &eventfs_file_operations; inode->i_fop = &eventfs_file_operations;
/* All directories will have the same inode number */
inode->i_ino = EVENTFS_DIR_INODE_INO;
ti = get_tracefs(inode); ti = get_tracefs(inode);
ti->flags |= TRACEFS_EVENT_INODE; ti->flags |= TRACEFS_EVENT_INODE;
...@@ -390,16 +463,14 @@ void eventfs_set_ei_status_free(struct tracefs_inode *ti, struct dentry *dentry) ...@@ -390,16 +463,14 @@ void eventfs_set_ei_status_free(struct tracefs_inode *ti, struct dentry *dentry)
* @mode: The mode of the file. * @mode: The mode of the file.
* @data: The data to use to set the inode of the file with on open() * @data: The data to use to set the inode of the file with on open()
* @fops: The fops of the file to be created. * @fops: The fops of the file to be created.
* @lookup: If called by the lookup routine, in which case, dput() the created dentry.
* *
* Create a dentry for a file of an eventfs_inode @ei and place it into the * Create a dentry for a file of an eventfs_inode @ei and place it into the
* address located at @e_dentry. If the @e_dentry already has a dentry, then * address located at @e_dentry.
* just do a dget() on it and return. Otherwise create the dentry and attach it.
*/ */
static struct dentry * static struct dentry *
create_file_dentry(struct eventfs_inode *ei, int idx, create_file_dentry(struct eventfs_inode *ei, int idx,
struct dentry *parent, const char *name, umode_t mode, void *data, struct dentry *parent, const char *name, umode_t mode, void *data,
const struct file_operations *fops, bool lookup) const struct file_operations *fops)
{ {
struct eventfs_attr *attr = NULL; struct eventfs_attr *attr = NULL;
struct dentry **e_dentry = &ei->d_children[idx]; struct dentry **e_dentry = &ei->d_children[idx];
...@@ -414,8 +485,6 @@ create_file_dentry(struct eventfs_inode *ei, int idx, ...@@ -414,8 +485,6 @@ create_file_dentry(struct eventfs_inode *ei, int idx,
} }
/* If the e_dentry already has a dentry, use it */ /* If the e_dentry already has a dentry, use it */
if (*e_dentry) { if (*e_dentry) {
/* lookup does not need to up the ref count */
if (!lookup)
dget(*e_dentry); dget(*e_dentry);
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
return *e_dentry; return *e_dentry;
...@@ -441,13 +510,12 @@ create_file_dentry(struct eventfs_inode *ei, int idx, ...@@ -441,13 +510,12 @@ create_file_dentry(struct eventfs_inode *ei, int idx,
* way to being freed, don't return it. If e_dentry is NULL * way to being freed, don't return it. If e_dentry is NULL
* it means it was already freed. * it means it was already freed.
*/ */
if (ei->is_freed) if (ei->is_freed) {
dentry = NULL; dentry = NULL;
else } else {
dentry = *e_dentry; dentry = *e_dentry;
/* The lookup does not need to up the dentry refcount */
if (dentry && !lookup)
dget(dentry); dget(dentry);
}
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
return dentry; return dentry;
} }
...@@ -465,9 +533,6 @@ create_file_dentry(struct eventfs_inode *ei, int idx, ...@@ -465,9 +533,6 @@ create_file_dentry(struct eventfs_inode *ei, int idx,
} }
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
if (lookup)
dput(dentry);
return dentry; return dentry;
} }
...@@ -500,13 +565,12 @@ static void eventfs_post_create_dir(struct eventfs_inode *ei) ...@@ -500,13 +565,12 @@ static void eventfs_post_create_dir(struct eventfs_inode *ei)
* @pei: The eventfs_inode parent of ei. * @pei: The eventfs_inode parent of ei.
* @ei: The eventfs_inode to create the directory for * @ei: The eventfs_inode to create the directory for
* @parent: The dentry of the parent of this directory * @parent: The dentry of the parent of this directory
* @lookup: True if this is called by the lookup code
* *
* This creates and attaches a directory dentry to the eventfs_inode @ei. * This creates and attaches a directory dentry to the eventfs_inode @ei.
*/ */
static struct dentry * static struct dentry *
create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei,
struct dentry *parent, bool lookup) struct dentry *parent)
{ {
struct dentry *dentry = NULL; struct dentry *dentry = NULL;
...@@ -518,10 +582,8 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, ...@@ -518,10 +582,8 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei,
return NULL; return NULL;
} }
if (ei->dentry) { if (ei->dentry) {
/* If the dentry already has a dentry, use it */ /* If the eventfs_inode already has a dentry, use it */
dentry = ei->dentry; dentry = ei->dentry;
/* lookup does not need to up the ref count */
if (!lookup)
dget(dentry); dget(dentry);
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
return dentry; return dentry;
...@@ -542,7 +604,7 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, ...@@ -542,7 +604,7 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei,
* way to being freed. * way to being freed.
*/ */
dentry = ei->dentry; dentry = ei->dentry;
if (dentry && !lookup) if (dentry)
dget(dentry); dget(dentry);
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
return dentry; return dentry;
...@@ -562,9 +624,6 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, ...@@ -562,9 +624,6 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei,
} }
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
if (lookup)
dput(dentry);
return dentry; return dentry;
} }
...@@ -589,8 +648,8 @@ static struct dentry *eventfs_root_lookup(struct inode *dir, ...@@ -589,8 +648,8 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
struct eventfs_inode *ei; struct eventfs_inode *ei;
struct dentry *ei_dentry = NULL; struct dentry *ei_dentry = NULL;
struct dentry *ret = NULL; struct dentry *ret = NULL;
struct dentry *d;
const char *name = dentry->d_name.name; const char *name = dentry->d_name.name;
bool created = false;
umode_t mode; umode_t mode;
void *data; void *data;
int idx; int idx;
...@@ -626,13 +685,10 @@ static struct dentry *eventfs_root_lookup(struct inode *dir, ...@@ -626,13 +685,10 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
ret = simple_lookup(dir, dentry, flags); ret = simple_lookup(dir, dentry, flags);
if (IS_ERR(ret)) if (IS_ERR(ret))
goto out; goto out;
create_dir_dentry(ei, ei_child, ei_dentry, true); d = create_dir_dentry(ei, ei_child, ei_dentry);
created = true; dput(d);
break;
}
if (created)
goto out; goto out;
}
for (i = 0; i < ei->nr_entries; i++) { for (i = 0; i < ei->nr_entries; i++) {
entry = &ei->entries[i]; entry = &ei->entries[i];
...@@ -650,8 +706,8 @@ static struct dentry *eventfs_root_lookup(struct inode *dir, ...@@ -650,8 +706,8 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
ret = simple_lookup(dir, dentry, flags); ret = simple_lookup(dir, dentry, flags);
if (IS_ERR(ret)) if (IS_ERR(ret))
goto out; goto out;
create_file_dentry(ei, i, ei_dentry, name, mode, cdata, d = create_file_dentry(ei, i, ei_dentry, name, mode, cdata, fops);
fops, true); dput(d);
break; break;
} }
} }
...@@ -660,172 +716,107 @@ static struct dentry *eventfs_root_lookup(struct inode *dir, ...@@ -660,172 +716,107 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
return ret; return ret;
} }
struct dentry_list { /*
void *cursor; * Walk the children of a eventfs_inode to fill in getdents().
struct dentry **dentries;
};
/**
* eventfs_release - called to release eventfs file/dir
* @inode: inode to be released
* @file: file to be released (not used)
*/
static int eventfs_release(struct inode *inode, struct file *file)
{
struct tracefs_inode *ti;
struct dentry_list *dlist = file->private_data;
void *cursor;
int i;
ti = get_tracefs(inode);
if (!(ti->flags & TRACEFS_EVENT_INODE))
return -EINVAL;
if (WARN_ON_ONCE(!dlist))
return -EINVAL;
for (i = 0; dlist->dentries && dlist->dentries[i]; i++) {
dput(dlist->dentries[i]);
}
cursor = dlist->cursor;
kfree(dlist->dentries);
kfree(dlist);
file->private_data = cursor;
return dcache_dir_close(inode, file);
}
static int add_dentries(struct dentry ***dentries, struct dentry *d, int cnt)
{
struct dentry **tmp;
tmp = krealloc(*dentries, sizeof(d) * (cnt + 2), GFP_NOFS);
if (!tmp)
return -1;
tmp[cnt] = d;
tmp[cnt + 1] = NULL;
*dentries = tmp;
return 0;
}
/**
* dcache_dir_open_wrapper - eventfs open wrapper
* @inode: not used
* @file: dir to be opened (to create it's children)
*
* Used to dynamic create file/dir with-in @file, all the
* file/dir will be created. If already created then references
* will be increased
*/ */
static int dcache_dir_open_wrapper(struct inode *inode, struct file *file) static int eventfs_iterate(struct file *file, struct dir_context *ctx)
{ {
const struct file_operations *fops; const struct file_operations *fops;
struct inode *f_inode = file_inode(file);
const struct eventfs_entry *entry; const struct eventfs_entry *entry;
struct eventfs_inode *ei_child; struct eventfs_inode *ei_child;
struct tracefs_inode *ti; struct tracefs_inode *ti;
struct eventfs_inode *ei; struct eventfs_inode *ei;
struct dentry_list *dlist; const char *name;
struct dentry **dentries = NULL;
struct dentry *parent = file_dentry(file);
struct dentry *d;
struct inode *f_inode = file_inode(file);
const char *name = parent->d_name.name;
umode_t mode; umode_t mode;
void *data;
int cnt = 0;
int idx; int idx;
int ret; int ret = -EINVAL;
int i; int ino;
int r; int i, r, c;
if (!dir_emit_dots(file, ctx))
return 0;
ti = get_tracefs(f_inode); ti = get_tracefs(f_inode);
if (!(ti->flags & TRACEFS_EVENT_INODE)) if (!(ti->flags & TRACEFS_EVENT_INODE))
return -EINVAL; return -EINVAL;
if (WARN_ON_ONCE(file->private_data)) c = ctx->pos - 2;
return -EINVAL;
idx = srcu_read_lock(&eventfs_srcu); idx = srcu_read_lock(&eventfs_srcu);
mutex_lock(&eventfs_mutex); mutex_lock(&eventfs_mutex);
ei = READ_ONCE(ti->private); ei = READ_ONCE(ti->private);
if (ei && ei->is_freed)
ei = NULL;
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
if (!ei) { if (!ei)
srcu_read_unlock(&eventfs_srcu, idx); goto out;
return -EINVAL;
}
data = ei->data;
dlist = kmalloc(sizeof(*dlist), GFP_KERNEL); /*
if (!dlist) { * Need to create the dentries and inodes to have a consistent
srcu_read_unlock(&eventfs_srcu, idx); * inode number.
return -ENOMEM; */
} ret = 0;
inode_lock(parent->d_inode); /* Start at 'c' to jump over already read entries */
list_for_each_entry_srcu(ei_child, &ei->children, list, for (i = c; i < ei->nr_entries; i++, ctx->pos++) {
srcu_read_lock_held(&eventfs_srcu)) { void *cdata = ei->data;
d = create_dir_dentry(ei, ei_child, parent, false);
if (d) {
ret = add_dentries(&dentries, d, cnt);
if (ret < 0)
break;
cnt++;
}
}
for (i = 0; i < ei->nr_entries; i++) {
void *cdata = data;
entry = &ei->entries[i]; entry = &ei->entries[i];
name = entry->name; name = entry->name;
mutex_lock(&eventfs_mutex); mutex_lock(&eventfs_mutex);
/* If ei->is_freed, then the event itself may be too */ /* If ei->is_freed then just bail here, nothing more to do */
if (!ei->is_freed) if (ei->is_freed) {
mutex_unlock(&eventfs_mutex);
goto out;
}
r = entry->callback(name, &mode, &cdata, &fops); r = entry->callback(name, &mode, &cdata, &fops);
else
r = -1;
mutex_unlock(&eventfs_mutex); mutex_unlock(&eventfs_mutex);
if (r <= 0) if (r <= 0)
continue; continue;
d = create_file_dentry(ei, i, parent, name, mode, cdata, fops, false);
if (d) { ino = EVENTFS_FILE_INODE_INO;
ret = add_dentries(&dentries, d, cnt);
if (ret < 0) if (!dir_emit(ctx, name, strlen(name), ino, DT_REG))
break; goto out;
cnt++;
} }
/* Subtract the skipped entries above */
c -= min((unsigned int)c, (unsigned int)ei->nr_entries);
list_for_each_entry_srcu(ei_child, &ei->children, list,
srcu_read_lock_held(&eventfs_srcu)) {
if (c > 0) {
c--;
continue;
} }
inode_unlock(parent->d_inode);
srcu_read_unlock(&eventfs_srcu, idx);
ret = dcache_dir_open(inode, file);
/* ctx->pos++;
* dcache_dir_open() sets file->private_data to a dentry cursor.
* Need to save that but also save all the dentries that were
* opened by this function.
*/
dlist->cursor = file->private_data;
dlist->dentries = dentries;
file->private_data = dlist;
return ret;
}
/* if (ei_child->is_freed)
* This just sets the file->private_data back to the cursor and back. continue;
*/
static int dcache_readdir_wrapper(struct file *file, struct dir_context *ctx) name = ei_child->name;
{
struct dentry_list *dlist = file->private_data; ino = EVENTFS_DIR_INODE_INO;
int ret;
if (!dir_emit(ctx, name, strlen(name), ino, DT_DIR))
goto out_dec;
}
ret = 1;
out:
srcu_read_unlock(&eventfs_srcu, idx);
file->private_data = dlist->cursor;
ret = dcache_readdir(file, ctx);
dlist->cursor = file->private_data;
file->private_data = dlist;
return ret; return ret;
out_dec:
/* Incremented ctx->pos without adding something, reset it */
ctx->pos--;
goto out;
} }
/** /**
...@@ -883,7 +874,7 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode ...@@ -883,7 +874,7 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode
} }
if (size) { if (size) {
ei->d_children = kzalloc(sizeof(*ei->d_children) * size, GFP_KERNEL); ei->d_children = kcalloc(size, sizeof(*ei->d_children), GFP_KERNEL);
if (!ei->d_children) { if (!ei->d_children) {
kfree_const(ei->name); kfree_const(ei->name);
kfree(ei); kfree(ei);
...@@ -950,7 +941,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry ...@@ -950,7 +941,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
goto fail; goto fail;
if (size) { if (size) {
ei->d_children = kzalloc(sizeof(*ei->d_children) * size, GFP_KERNEL); ei->d_children = kcalloc(size, sizeof(*ei->d_children), GFP_KERNEL);
if (!ei->d_children) if (!ei->d_children)
goto fail; goto fail;
} }
...@@ -968,6 +959,14 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry ...@@ -968,6 +959,14 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
uid = d_inode(dentry->d_parent)->i_uid; uid = d_inode(dentry->d_parent)->i_uid;
gid = d_inode(dentry->d_parent)->i_gid; 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.
*/
if (!parent)
ei->attr.mode = EVENTFS_TOPLEVEL;
/* This is used as the default ownership of the files and directories */ /* This is used as the default ownership of the files and directories */
ei->attr.uid = uid; ei->attr.uid = uid;
ei->attr.gid = gid; ei->attr.gid = gid;
......
...@@ -91,6 +91,7 @@ static int tracefs_syscall_mkdir(struct mnt_idmap *idmap, ...@@ -91,6 +91,7 @@ static int tracefs_syscall_mkdir(struct mnt_idmap *idmap,
struct inode *inode, struct dentry *dentry, struct inode *inode, struct dentry *dentry,
umode_t mode) umode_t mode)
{ {
struct tracefs_inode *ti;
char *name; char *name;
int ret; int ret;
...@@ -98,6 +99,15 @@ static int tracefs_syscall_mkdir(struct mnt_idmap *idmap, ...@@ -98,6 +99,15 @@ static int tracefs_syscall_mkdir(struct mnt_idmap *idmap,
if (!name) if (!name)
return -ENOMEM; return -ENOMEM;
/*
* This is a new directory that does not take the default of
* the rootfs. It becomes the default permissions for all the
* files and directories underneath it.
*/
ti = get_tracefs(inode);
ti->flags |= TRACEFS_INSTANCE_INODE;
ti->private = inode;
/* /*
* The mkdir call can call the generic functions that create * The mkdir call can call the generic functions that create
* the files within the tracefs system. It is up to the individual * the files within the tracefs system. It is up to the individual
...@@ -141,10 +151,76 @@ static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry) ...@@ -141,10 +151,76 @@ static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry)
return ret; return ret;
} }
static const struct inode_operations tracefs_dir_inode_operations = { static void set_tracefs_inode_owner(struct inode *inode)
{
struct tracefs_inode *ti = get_tracefs(inode);
struct inode *root_inode = ti->private;
/*
* If this inode has never been referenced, then update
* the permissions to the superblock.
*/
if (!(ti->flags & TRACEFS_UID_PERM_SET))
inode->i_uid = root_inode->i_uid;
if (!(ti->flags & TRACEFS_GID_PERM_SET))
inode->i_gid = root_inode->i_gid;
}
static int tracefs_permission(struct mnt_idmap *idmap,
struct inode *inode, int mask)
{
set_tracefs_inode_owner(inode);
return generic_permission(idmap, inode, mask);
}
static int tracefs_getattr(struct mnt_idmap *idmap,
const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int flags)
{
struct inode *inode = d_backing_inode(path->dentry);
set_tracefs_inode_owner(inode);
generic_fillattr(idmap, request_mask, inode, stat);
return 0;
}
static int tracefs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
struct inode *inode = d_inode(dentry);
struct tracefs_inode *ti = get_tracefs(inode);
if (ia_valid & ATTR_UID)
ti->flags |= TRACEFS_UID_PERM_SET;
if (ia_valid & ATTR_GID)
ti->flags |= TRACEFS_GID_PERM_SET;
return simple_setattr(idmap, dentry, attr);
}
static const struct inode_operations tracefs_instance_dir_inode_operations = {
.lookup = simple_lookup, .lookup = simple_lookup,
.mkdir = tracefs_syscall_mkdir, .mkdir = tracefs_syscall_mkdir,
.rmdir = tracefs_syscall_rmdir, .rmdir = tracefs_syscall_rmdir,
.permission = tracefs_permission,
.getattr = tracefs_getattr,
.setattr = tracefs_setattr,
};
static const struct inode_operations tracefs_dir_inode_operations = {
.lookup = simple_lookup,
.permission = tracefs_permission,
.getattr = tracefs_getattr,
.setattr = tracefs_setattr,
};
static const struct inode_operations tracefs_file_inode_operations = {
.permission = tracefs_permission,
.getattr = tracefs_getattr,
.setattr = tracefs_setattr,
}; };
struct inode *tracefs_get_inode(struct super_block *sb) struct inode *tracefs_get_inode(struct super_block *sb)
...@@ -183,82 +259,6 @@ struct tracefs_fs_info { ...@@ -183,82 +259,6 @@ struct tracefs_fs_info {
struct tracefs_mount_opts mount_opts; struct tracefs_mount_opts mount_opts;
}; };
static void change_gid(struct dentry *dentry, kgid_t gid)
{
if (!dentry->d_inode)
return;
dentry->d_inode->i_gid = gid;
}
/*
* Taken from d_walk, but without he need for handling renames.
* Nothing can be renamed while walking the list, as tracefs
* does not support renames. This is only called when mounting
* or remounting the file system, to set all the files to
* the given gid.
*/
static void set_gid(struct dentry *parent, kgid_t gid)
{
struct dentry *this_parent, *dentry;
this_parent = parent;
spin_lock(&this_parent->d_lock);
change_gid(this_parent, gid);
repeat:
dentry = d_first_child(this_parent);
resume:
hlist_for_each_entry_from(dentry, d_sib) {
struct tracefs_inode *ti;
/* Note, getdents() can add a cursor dentry with no inode */
if (!dentry->d_inode)
continue;
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
change_gid(dentry, gid);
/* If this is the events directory, update that too */
ti = get_tracefs(dentry->d_inode);
if (ti && (ti->flags & TRACEFS_EVENT_INODE))
eventfs_update_gid(dentry, gid);
if (!hlist_empty(&dentry->d_children)) {
spin_unlock(&this_parent->d_lock);
spin_release(&dentry->d_lock.dep_map, _RET_IP_);
this_parent = dentry;
spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
goto repeat;
}
spin_unlock(&dentry->d_lock);
}
/*
* All done at this level ... ascend and resume the search.
*/
rcu_read_lock();
ascend:
if (this_parent != parent) {
dentry = this_parent;
this_parent = dentry->d_parent;
spin_unlock(&dentry->d_lock);
spin_lock(&this_parent->d_lock);
/* go into the first sibling still alive */
hlist_for_each_entry_continue(dentry, d_sib) {
if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
rcu_read_unlock();
goto resume;
}
}
goto ascend;
}
rcu_read_unlock();
spin_unlock(&this_parent->d_lock);
return;
}
static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)
{ {
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
...@@ -331,10 +331,8 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) ...@@ -331,10 +331,8 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
if (!remount || opts->opts & BIT(Opt_uid)) if (!remount || opts->opts & BIT(Opt_uid))
inode->i_uid = opts->uid; inode->i_uid = opts->uid;
if (!remount || opts->opts & BIT(Opt_gid)) { if (!remount || opts->opts & BIT(Opt_gid))
/* Set all the group ids to the mount option */ inode->i_gid = opts->gid;
set_gid(sb->s_root, opts->gid);
}
return 0; return 0;
} }
...@@ -568,6 +566,26 @@ struct dentry *eventfs_end_creating(struct dentry *dentry) ...@@ -568,6 +566,26 @@ struct dentry *eventfs_end_creating(struct dentry *dentry)
return dentry; return dentry;
} }
/* Find the inode that this will use for default */
static struct inode *instance_inode(struct dentry *parent, struct inode *inode)
{
struct tracefs_inode *ti;
/* If parent is NULL then use root inode */
if (!parent)
return d_inode(inode->i_sb->s_root);
/* Find the inode that is flagged as an instance or the root inode */
while (!IS_ROOT(parent)) {
ti = get_tracefs(d_inode(parent));
if (ti->flags & TRACEFS_INSTANCE_INODE)
break;
parent = parent->d_parent;
}
return d_inode(parent);
}
/** /**
* tracefs_create_file - create a file in the tracefs filesystem * tracefs_create_file - create a file in the tracefs filesystem
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
...@@ -598,6 +616,7 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, ...@@ -598,6 +616,7 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data, struct dentry *parent, void *data,
const struct file_operations *fops) const struct file_operations *fops)
{ {
struct tracefs_inode *ti;
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
...@@ -616,7 +635,11 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, ...@@ -616,7 +635,11 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
if (unlikely(!inode)) if (unlikely(!inode))
return tracefs_failed_creating(dentry); return tracefs_failed_creating(dentry);
ti = get_tracefs(inode);
ti->private = instance_inode(parent, inode);
inode->i_mode = mode; inode->i_mode = mode;
inode->i_op = &tracefs_file_inode_operations;
inode->i_fop = fops ? fops : &tracefs_file_operations; inode->i_fop = fops ? fops : &tracefs_file_operations;
inode->i_private = data; inode->i_private = data;
inode->i_uid = d_inode(dentry->d_parent)->i_uid; inode->i_uid = d_inode(dentry->d_parent)->i_uid;
...@@ -629,6 +652,7 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, ...@@ -629,6 +652,7 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
static struct dentry *__create_dir(const char *name, struct dentry *parent, static struct dentry *__create_dir(const char *name, struct dentry *parent,
const struct inode_operations *ops) const struct inode_operations *ops)
{ {
struct tracefs_inode *ti;
struct dentry *dentry = tracefs_start_creating(name, parent); struct dentry *dentry = tracefs_start_creating(name, parent);
struct inode *inode; struct inode *inode;
...@@ -646,6 +670,9 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, ...@@ -646,6 +670,9 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent,
inode->i_uid = d_inode(dentry->d_parent)->i_uid; inode->i_uid = d_inode(dentry->d_parent)->i_uid;
inode->i_gid = d_inode(dentry->d_parent)->i_gid; inode->i_gid = d_inode(dentry->d_parent)->i_gid;
ti = get_tracefs(inode);
ti->private = instance_inode(parent, inode);
/* directory inodes start off with i_nlink == 2 (for "." entry) */ /* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode); inc_nlink(inode);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -676,7 +703,7 @@ struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) ...@@ -676,7 +703,7 @@ struct dentry *tracefs_create_dir(const char *name, struct dentry *parent)
if (security_locked_down(LOCKDOWN_TRACEFS)) if (security_locked_down(LOCKDOWN_TRACEFS))
return NULL; return NULL;
return __create_dir(name, parent, &simple_dir_inode_operations); return __create_dir(name, parent, &tracefs_dir_inode_operations);
} }
/** /**
...@@ -707,7 +734,7 @@ __init struct dentry *tracefs_create_instance_dir(const char *name, ...@@ -707,7 +734,7 @@ __init struct dentry *tracefs_create_instance_dir(const char *name,
if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir)) if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir))
return NULL; return NULL;
dentry = __create_dir(name, parent, &tracefs_dir_inode_operations); dentry = __create_dir(name, parent, &tracefs_instance_dir_inode_operations);
if (!dentry) if (!dentry)
return NULL; return NULL;
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
enum { enum {
TRACEFS_EVENT_INODE = BIT(1), TRACEFS_EVENT_INODE = BIT(1),
TRACEFS_EVENT_TOP_INODE = BIT(2), TRACEFS_EVENT_TOP_INODE = BIT(2),
TRACEFS_GID_PERM_SET = BIT(3),
TRACEFS_UID_PERM_SET = BIT(4),
TRACEFS_INSTANCE_INODE = BIT(5),
}; };
struct tracefs_inode { struct tracefs_inode {
......
...@@ -22,9 +22,8 @@ struct seq_buf { ...@@ -22,9 +22,8 @@ struct seq_buf {
}; };
#define DECLARE_SEQ_BUF(NAME, SIZE) \ #define DECLARE_SEQ_BUF(NAME, SIZE) \
char __ ## NAME ## _buffer[SIZE] = ""; \
struct seq_buf NAME = { \ struct seq_buf NAME = { \
.buffer = &__ ## NAME ## _buffer, \ .buffer = (char[SIZE]) { 0 }, \
.size = SIZE, \ .size = SIZE, \
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment