diff options
Diffstat (limited to 'fs/tracefs')
-rw-r--r-- | fs/tracefs/event_inode.c | 172 | ||||
-rw-r--r-- | fs/tracefs/inode.c | 217 |
2 files changed, 152 insertions, 237 deletions
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 129d0f54ba..5d88c184f0 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -37,7 +37,6 @@ static DEFINE_MUTEX(eventfs_mutex); struct eventfs_root_inode { struct eventfs_inode ei; - struct inode *parent_inode; struct dentry *events_dir; }; @@ -211,7 +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) { - update_attr(&ei->attr, iattr); + /* Just use the inode permissions for the events directory */ + if (!ei->is_events) + update_attr(&ei->attr, iattr); } else { name = dentry->d_name.name; @@ -229,70 +230,9 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, return ret; } -static void update_events_attr(struct eventfs_inode *ei, struct super_block *sb) -{ - struct eventfs_root_inode *rei; - struct inode *parent; - - rei = get_root_inode(ei); - - /* Use the parent inode permissions unless root set its permissions */ - parent = rei->parent_inode; - - if (rei->ei.attr.mode & EVENTFS_SAVE_UID) - ei->attr.uid = rei->ei.attr.uid; - else - ei->attr.uid = parent->i_uid; - - if (rei->ei.attr.mode & EVENTFS_SAVE_GID) - ei->attr.gid = rei->ei.attr.gid; - else - ei->attr.gid = parent->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) - return; - - update_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_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 = { @@ -361,55 +301,53 @@ void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid) update_gid, ti->vfs_inode.i_gid, 0); } -/* Return the evenfs_inode of the "events" directory */ -static struct eventfs_inode *eventfs_find_events(struct dentry *dentry) +static void update_inode_attr(struct inode *inode, umode_t mode, + struct eventfs_attr *attr, struct eventfs_root_inode *rei) { - struct eventfs_inode *ei; - - 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; - - /* - * If the ei is being freed, the ownership of the children - * doesn't matter. - */ - if (ei->is_freed) - return NULL; - - // Walk upwards until you find the events inode - } while (!ei->is_events); + if (attr && attr->mode & EVENTFS_SAVE_MODE) + inode->i_mode = attr->mode & EVENTFS_MODE_MASK; + else + inode->i_mode = mode; - update_events_attr(ei, dentry->d_sb); + if (attr && attr->mode & EVENTFS_SAVE_UID) + inode->i_uid = attr->uid; + else + inode->i_uid = rei->ei.attr.uid; - return ei; + if (attr && attr->mode & EVENTFS_SAVE_GID) + inode->i_gid = attr->gid; + else + inode->i_gid = rei->ei.attr.gid; } -static void update_inode_attr(struct dentry *dentry, struct inode *inode, - struct eventfs_attr *attr, umode_t mode) +static struct inode *eventfs_get_inode(struct dentry *dentry, struct eventfs_attr *attr, + umode_t mode, struct eventfs_inode *ei) { - struct eventfs_inode *events_ei = eventfs_find_events(dentry); + struct eventfs_root_inode *rei; + struct eventfs_inode *pei; + struct tracefs_inode *ti; + struct inode *inode; - if (!events_ei) - return; + inode = tracefs_get_inode(dentry->d_sb); + if (!inode) + return NULL; - inode->i_mode = mode; - inode->i_uid = events_ei->attr.uid; - inode->i_gid = events_ei->attr.gid; + ti = get_tracefs(inode); + ti->private = ei; + ti->flags |= TRACEFS_EVENT_INODE; - if (!attr) - return; + /* 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); - if (attr->mode & EVENTFS_SAVE_MODE) - inode->i_mode = attr->mode & EVENTFS_MODE_MASK; + rei = get_root_inode(pei); - if (attr->mode & EVENTFS_SAVE_UID) - inode->i_uid = attr->uid; + update_inode_attr(inode, mode, attr, rei); - if (attr->mode & EVENTFS_SAVE_GID) - inode->i_gid = attr->gid; + return inode; } /** @@ -432,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)) @@ -441,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; @@ -455,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); @@ -477,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_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); @@ -859,7 +782,6 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry // Note: we have a ref to the dentry from tracefs_start_creating() rei = get_root_inode(ei); rei->events_dir = dentry; - rei->parent_inode = d_inode(dentry->d_sb->s_root); ei->entries = entries; ei->nr_entries = size; @@ -869,14 +791,12 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry uid = d_inode(dentry->d_parent)->i_uid; gid = d_inode(dentry->d_parent)->i_gid; - ei->attr.uid = uid; - ei->attr.gid = gid; - /* - * When the "events" directory is created, it takes on the - * permissions of its parent. But can be reset on remount. + * The ei->attr will be used as the default values for the + * files beneath this directory. */ - ei->attr.mode |= EVENTFS_SAVE_UID | EVENTFS_SAVE_GID; + ei->attr.uid = uid; + ei->attr.gid = gid; INIT_LIST_HEAD(&ei->children); INIT_LIST_HEAD(&ei->list); diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index d8c60c2b76..7c29f4afc2 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -11,14 +11,14 @@ #include <linux/module.h> #include <linux/fs.h> -#include <linux/mount.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> #include <linux/kobject.h> #include <linux/namei.h> #include <linux/tracefs.h> #include <linux/fsnotify.h> #include <linux/security.h> #include <linux/seq_file.h> -#include <linux/parser.h> #include <linux/magic.h> #include <linux/slab.h> #include "internal.h" @@ -281,7 +281,7 @@ struct inode *tracefs_get_inode(struct super_block *sb) return inode; } -struct tracefs_mount_opts { +struct tracefs_fs_info { kuid_t uid; kgid_t gid; umode_t mode; @@ -293,68 +293,51 @@ enum { Opt_uid, Opt_gid, Opt_mode, - Opt_err }; -static const match_table_t tokens = { - {Opt_uid, "uid=%u"}, - {Opt_gid, "gid=%u"}, - {Opt_mode, "mode=%o"}, - {Opt_err, NULL} +static const struct fs_parameter_spec tracefs_param_specs[] = { + fsparam_u32 ("gid", Opt_gid), + fsparam_u32oct ("mode", Opt_mode), + fsparam_u32 ("uid", Opt_uid), + {} }; -struct tracefs_fs_info { - struct tracefs_mount_opts mount_opts; -}; - -static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) +static int tracefs_parse_param(struct fs_context *fc, struct fs_parameter *param) { - substring_t args[MAX_OPT_ARGS]; - int option; - int token; + struct tracefs_fs_info *opts = fc->s_fs_info; + struct fs_parse_result result; kuid_t uid; kgid_t gid; - char *p; - - opts->opts = 0; - opts->mode = TRACEFS_DEFAULT_MODE; - - while ((p = strsep(&data, ",")) != NULL) { - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_uid: - if (match_int(&args[0], &option)) - return -EINVAL; - uid = make_kuid(current_user_ns(), option); - if (!uid_valid(uid)) - return -EINVAL; - opts->uid = uid; - break; - case Opt_gid: - if (match_int(&args[0], &option)) - return -EINVAL; - gid = make_kgid(current_user_ns(), option); - if (!gid_valid(gid)) - return -EINVAL; - opts->gid = gid; - break; - case Opt_mode: - if (match_octal(&args[0], &option)) - return -EINVAL; - opts->mode = option & S_IALLUGO; - break; - /* - * We might like to report bad mount options here; - * but traditionally tracefs has ignored all mount options - */ - } - - opts->opts |= BIT(token); + int opt; + + opt = fs_parse(fc, tracefs_param_specs, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_uid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + return invalf(fc, "Unknown uid"); + opts->uid = uid; + break; + case Opt_gid: + gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(gid)) + return invalf(fc, "Unknown gid"); + opts->gid = gid; + break; + case Opt_mode: + opts->mode = result.uint_32 & S_IALLUGO; + break; + /* + * We might like to report bad mount options here; + * but traditionally tracefs has ignored all mount options + */ } + opts->opts |= BIT(opt); + return 0; } @@ -362,7 +345,6 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) { struct tracefs_fs_info *fsi = sb->s_fs_info; struct inode *inode = d_inode(sb->s_root); - struct tracefs_mount_opts *opts = &fsi->mount_opts; struct tracefs_inode *ti; bool update_uid, update_gid; umode_t tmp_mode; @@ -372,31 +354,40 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) * options. */ - if (!remount || opts->opts & BIT(Opt_mode)) { + if (!remount || fsi->opts & BIT(Opt_mode)) { tmp_mode = READ_ONCE(inode->i_mode) & ~S_IALLUGO; - tmp_mode |= opts->mode; + tmp_mode |= fsi->mode; WRITE_ONCE(inode->i_mode, tmp_mode); } - if (!remount || opts->opts & BIT(Opt_uid)) - inode->i_uid = opts->uid; + if (!remount || fsi->opts & BIT(Opt_uid)) + inode->i_uid = fsi->uid; - if (!remount || opts->opts & BIT(Opt_gid)) - inode->i_gid = opts->gid; + if (!remount || fsi->opts & BIT(Opt_gid)) + inode->i_gid = fsi->gid; - if (remount && (opts->opts & BIT(Opt_uid) || opts->opts & BIT(Opt_gid))) { + if (remount && (fsi->opts & BIT(Opt_uid) || fsi->opts & BIT(Opt_gid))) { - update_uid = opts->opts & BIT(Opt_uid); - update_gid = opts->opts & BIT(Opt_gid); + update_uid = fsi->opts & BIT(Opt_uid); + update_gid = fsi->opts & BIT(Opt_gid); rcu_read_lock(); list_for_each_entry_rcu(ti, &tracefs_inodes, list) { - if (update_uid) + if (update_uid) { ti->flags &= ~TRACEFS_UID_PERM_SET; + ti->vfs_inode.i_uid = fsi->uid; + } - if (update_gid) + if (update_gid) { ti->flags &= ~TRACEFS_GID_PERM_SET; - + ti->vfs_inode.i_gid = fsi->gid; + } + + /* + * Note, the above ti->vfs_inode updates are + * used in eventfs_remount() so they must come + * before calling it. + */ if (ti->flags & TRACEFS_EVENT_INODE) eventfs_remount(ti, update_uid, update_gid); } @@ -406,35 +397,31 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) return 0; } -static int tracefs_remount(struct super_block *sb, int *flags, char *data) +static int tracefs_reconfigure(struct fs_context *fc) { - int err; - struct tracefs_fs_info *fsi = sb->s_fs_info; + struct super_block *sb = fc->root->d_sb; + struct tracefs_fs_info *sb_opts = sb->s_fs_info; + struct tracefs_fs_info *new_opts = fc->s_fs_info; sync_filesystem(sb); - err = tracefs_parse_options(data, &fsi->mount_opts); - if (err) - goto fail; - - tracefs_apply_options(sb, true); + /* structure copy of new mount options to sb */ + *sb_opts = *new_opts; -fail: - return err; + return tracefs_apply_options(sb, true); } static int tracefs_show_options(struct seq_file *m, struct dentry *root) { struct tracefs_fs_info *fsi = root->d_sb->s_fs_info; - struct tracefs_mount_opts *opts = &fsi->mount_opts; - if (!uid_eq(opts->uid, GLOBAL_ROOT_UID)) + if (!uid_eq(fsi->uid, GLOBAL_ROOT_UID)) seq_printf(m, ",uid=%u", - from_kuid_munged(&init_user_ns, opts->uid)); - if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) + from_kuid_munged(&init_user_ns, fsi->uid)); + if (!gid_eq(fsi->gid, GLOBAL_ROOT_GID)) seq_printf(m, ",gid=%u", - from_kgid_munged(&init_user_ns, opts->gid)); - if (opts->mode != TRACEFS_DEFAULT_MODE) - seq_printf(m, ",mode=%o", opts->mode); + from_kgid_munged(&init_user_ns, fsi->gid)); + if (fsi->mode != TRACEFS_DEFAULT_MODE) + seq_printf(m, ",mode=%o", fsi->mode); return 0; } @@ -460,7 +447,6 @@ static const struct super_operations tracefs_super_operations = { .free_inode = tracefs_free_inode, .drop_inode = tracefs_drop_inode, .statfs = simple_statfs, - .remount_fs = tracefs_remount, .show_options = tracefs_show_options, }; @@ -490,26 +476,14 @@ static const struct dentry_operations tracefs_dentry_operations = { .d_release = tracefs_d_release, }; -static int trace_fill_super(struct super_block *sb, void *data, int silent) +static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc) { static const struct tree_descr trace_files[] = {{""}}; - struct tracefs_fs_info *fsi; int err; - fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); - sb->s_fs_info = fsi; - if (!fsi) { - err = -ENOMEM; - goto fail; - } - - err = tracefs_parse_options(data, &fsi->mount_opts); + err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); if (err) - goto fail; - - err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); - if (err) - goto fail; + return err; sb->s_op = &tracefs_super_operations; sb->s_d_op = &tracefs_dentry_operations; @@ -517,24 +491,45 @@ static int trace_fill_super(struct super_block *sb, void *data, int silent) tracefs_apply_options(sb, false); return 0; +} -fail: - kfree(fsi); - sb->s_fs_info = NULL; - return err; +static int tracefs_get_tree(struct fs_context *fc) +{ + return get_tree_single(fc, tracefs_fill_super); +} + +static void tracefs_free_fc(struct fs_context *fc) +{ + kfree(fc->s_fs_info); } -static struct dentry *trace_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static const struct fs_context_operations tracefs_context_ops = { + .free = tracefs_free_fc, + .parse_param = tracefs_parse_param, + .get_tree = tracefs_get_tree, + .reconfigure = tracefs_reconfigure, +}; + +static int tracefs_init_fs_context(struct fs_context *fc) { - return mount_single(fs_type, flags, data, trace_fill_super); + struct tracefs_fs_info *fsi; + + fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); + if (!fsi) + return -ENOMEM; + + fsi->mode = TRACEFS_DEFAULT_MODE; + + fc->s_fs_info = fsi; + fc->ops = &tracefs_context_ops; + return 0; } static struct file_system_type trace_fs_type = { .owner = THIS_MODULE, .name = "tracefs", - .mount = trace_mount, + .init_fs_context = tracefs_init_fs_context, + .parameters = tracefs_param_specs, .kill_sb = kill_litter_super, }; MODULE_ALIAS_FS("tracefs"); |