diff options
Diffstat (limited to 'fs/configfs')
| -rw-r--r-- | fs/configfs/Kconfig | 1 | ||||
| -rw-r--r-- | fs/configfs/configfs_internal.h | 8 | ||||
| -rw-r--r-- | fs/configfs/dir.c | 87 | ||||
| -rw-r--r-- | fs/configfs/file.c | 2 | ||||
| -rw-r--r-- | fs/configfs/inode.c | 43 | ||||
| -rw-r--r-- | fs/configfs/item.c | 2 | ||||
| -rw-r--r-- | fs/configfs/mount.c | 7 | ||||
| -rw-r--r-- | fs/configfs/symlink.c | 37 |
8 files changed, 92 insertions, 95 deletions
diff --git a/fs/configfs/Kconfig b/fs/configfs/Kconfig index 272b64456999..1fcd761fe7be 100644 --- a/fs/configfs/Kconfig +++ b/fs/configfs/Kconfig @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config CONFIGFS_FS tristate "Userspace-driven configuration filesystem" - select SYSFS help configfs is a RAM-based filesystem that provides the converse of sysfs's functionality. Where sysfs is a filesystem-based diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index c0395363eab9..0b969d0eb8ff 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h @@ -55,6 +55,8 @@ struct configfs_dirent { #define CONFIGFS_USET_IN_MKDIR 0x0200 #define CONFIGFS_USET_CREATING 0x0400 #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR) +#define CONFIGFS_PINNED \ + (CONFIGFS_ROOT | CONFIGFS_DIR | CONFIGFS_ITEM_LINK) extern struct mutex configfs_symlink_mutex; extern spinlock_t configfs_dirent_lock; @@ -73,11 +75,9 @@ extern int configfs_make_dirent(struct configfs_dirent *, struct dentry *, void *, umode_t, int, struct configfs_fragment *); extern int configfs_dirent_is_ready(struct configfs_dirent *); -extern void configfs_hash_and_remove(struct dentry * dir, const char * name); - extern const unsigned char * configfs_get_name(struct configfs_dirent *sd); extern void configfs_drop_dentry(struct configfs_dirent *sd, struct dentry *parent); -extern int configfs_setattr(struct user_namespace *mnt_userns, +extern int configfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *iattr); extern struct dentry *configfs_pin_fs(void); @@ -91,7 +91,7 @@ extern const struct inode_operations configfs_root_inode_operations; extern const struct inode_operations configfs_symlink_inode_operations; extern const struct dentry_operations configfs_dentry_ops; -extern int configfs_symlink(struct user_namespace *mnt_userns, +extern int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const char *symname); extern int configfs_unlink(struct inode *dir, struct dentry *dentry); diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index ec6519e1ca3b..ba95f636a5ab 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -67,7 +67,6 @@ static void configfs_d_iput(struct dentry * dentry, const struct dentry_operations configfs_dentry_ops = { .d_iput = configfs_d_iput, - .d_delete = always_delete_dentry, }; #ifdef CONFIG_LOCKDEP @@ -207,7 +206,17 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *paren return ERR_PTR(-ENOENT); } sd->s_frag = get_fragment(frag); - list_add(&sd->s_sibling, &parent_sd->s_children); + + /* + * configfs_lookup scans only for unpinned items. s_children is + * partitioned so that configfs_lookup can bail out early. + * CONFIGFS_PINNED and CONFIGFS_NOT_PINNED are not symmetrical. readdir + * cursors still need to be inserted at the front of the list. + */ + if (sd->s_type & CONFIGFS_PINNED) + list_add_tail(&sd->s_sibling, &parent_sd->s_children); + else + list_add(&sd->s_sibling, &parent_sd->s_children); spin_unlock(&configfs_dirent_lock); return sd; @@ -220,10 +229,11 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *paren * * called with parent inode's i_mutex held */ -static int configfs_dirent_exists(struct configfs_dirent *parent_sd, - const unsigned char *new) +static int configfs_dirent_exists(struct dentry *dentry) { - struct configfs_dirent * sd; + struct configfs_dirent *parent_sd = dentry->d_parent->d_fsdata; + const unsigned char *new = dentry->d_name.name; + struct configfs_dirent *sd; list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { if (sd->s_element) { @@ -289,10 +299,6 @@ static int configfs_create_dir(struct config_item *item, struct dentry *dentry, BUG_ON(!item); - error = configfs_dirent_exists(p->d_fsdata, dentry->d_name.name); - if (unlikely(error)) - return error; - error = configfs_make_dirent(p->d_fsdata, dentry, item, mode, CONFIGFS_DIR | CONFIGFS_USET_CREATING, frag); @@ -394,8 +400,14 @@ static void remove_dir(struct dentry * d) configfs_remove_dirent(d); - if (d_really_is_positive(d)) - simple_rmdir(d_inode(parent),d); + if (d_really_is_positive(d)) { + if (likely(simple_empty(d))) { + __simple_rmdir(d_inode(parent),d); + dput(d); + } else { + pr_warn("remove_dir (%pd): attributes remain", d); + } + } pr_debug(" o %pd removing done (%d)\n", d, d_count(d)); @@ -451,6 +463,18 @@ static struct dentry * configfs_lookup(struct inode *dir, spin_lock(&configfs_dirent_lock); list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + + /* + * s_children is partitioned, see configfs_new_dirent. The first + * pinned item indicates we can stop scanning. + */ + if (sd->s_type & CONFIGFS_PINNED) + break; + + /* + * Note: CONFIGFS_PINNED and CONFIGFS_NOT_PINNED are asymmetric. + * there may be a readdir cursor in this list + */ if ((sd->s_type & CONFIGFS_NOT_PINNED) && !strcmp(configfs_get_name(sd), dentry->d_name.name)) { struct configfs_attribute *attr = sd->s_element; @@ -580,6 +604,7 @@ static void detach_attrs(struct config_item * item) static int populate_attrs(struct config_item *item) { const struct config_item_type *t = item->ci_type; + const struct configfs_group_operations *ops; struct configfs_attribute *attr; struct configfs_bin_attribute *bin_attr; int error = 0; @@ -587,14 +612,23 @@ static int populate_attrs(struct config_item *item) if (!t) return -EINVAL; + + ops = t->ct_group_ops; + if (t->ct_attrs) { for (i = 0; (attr = t->ct_attrs[i]) != NULL; i++) { + if (ops && ops->is_visible && !ops->is_visible(item, attr, i)) + continue; + if ((error = configfs_create_file(item, attr))) break; } } - if (t->ct_bin_attrs) { + if (!error && t->ct_bin_attrs) { for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) { + if (ops && ops->is_bin_visible && !ops->is_bin_visible(item, bin_attr, i)) + continue; + error = configfs_create_bin_file(item, bin_attr); if (error) break; @@ -941,7 +975,7 @@ static void configfs_dump_one(struct configfs_dirent *sd, int level) { pr_info("%*s\"%s\":\n", level, " ", configfs_get_name(sd)); -#define type_print(_type) if (sd->s_type & _type) pr_info("%*s %s\n", level, " ", #_type); +#define type_print(_type) if (sd->s_type & _type) pr_info("%*s %s\n", level, " ", #_type) type_print(CONFIGFS_ROOT); type_print(CONFIGFS_DIR); type_print(CONFIGFS_ITEM_ATTR); @@ -1251,8 +1285,8 @@ out_root_unlock: } EXPORT_SYMBOL(configfs_depend_item_unlocked); -static int configfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, - struct dentry *dentry, umode_t mode) +static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode) { int ret = 0; int module_got = 0; @@ -1432,7 +1466,7 @@ out_put: put_fragment(frag); out: - return ret; + return ERR_PTR(ret); } static int configfs_rmdir(struct inode *dir, struct dentry *dentry) @@ -1573,10 +1607,7 @@ static int configfs_dir_open(struct inode *inode, struct file *file) err = -ENOENT; if (configfs_dirent_is_ready(parent_sd)) { file->private_data = configfs_new_dirent(parent_sd, NULL, 0, NULL); - if (IS_ERR(file->private_data)) - err = PTR_ERR(file->private_data); - else - err = 0; + err = PTR_ERR_OR_ZERO(file->private_data); } inode_unlock(d_inode(dentry)); @@ -1599,12 +1630,6 @@ static int configfs_dir_close(struct inode *inode, struct file *file) return 0; } -/* Relationship between s_mode and the DT_xxx types */ -static inline unsigned char dt_type(struct configfs_dirent *sd) -{ - return (sd->s_mode >> 12) & 15; -} - static int configfs_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; @@ -1654,7 +1679,8 @@ static int configfs_readdir(struct file *file, struct dir_context *ctx) name = configfs_get_name(next); len = strlen(name); - if (!dir_emit(ctx, name, len, ino, dt_type(next))) + if (!dir_emit(ctx, name, len, ino, + fs_umode_to_dtype(next->s_mode))) return 0; spin_lock(&configfs_dirent_lock); @@ -1880,8 +1906,11 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys) if (dentry) { d_add(dentry, NULL); - err = configfs_attach_group(sd->s_element, &group->cg_item, - dentry, frag); + err = configfs_dirent_exists(dentry); + if (!err) + err = configfs_attach_group(sd->s_element, + &group->cg_item, + dentry, frag); if (err) { BUG_ON(d_inode(dentry)); d_drop(dentry); diff --git a/fs/configfs/file.c b/fs/configfs/file.c index 0ad32150611e..affe4742bbb5 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c @@ -30,7 +30,7 @@ struct configfs_buffer { size_t count; loff_t pos; char * page; - struct configfs_item_operations * ops; + const struct configfs_item_operations *ops; struct mutex mutex; int needs_read_fill; bool read_in_progress; diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index b601610e9907..bcda3372e141 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c @@ -32,7 +32,7 @@ static const struct inode_operations configfs_inode_operations ={ .setattr = configfs_setattr, }; -int configfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, +int configfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *iattr) { struct inode * inode = d_inode(dentry); @@ -60,7 +60,7 @@ int configfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, } /* attributes were changed atleast once in past */ - error = simple_setattr(mnt_userns, dentry, iattr); + error = simple_setattr(idmap, dentry, iattr); if (error) return error; @@ -88,8 +88,7 @@ int configfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, static inline void set_default_inode_attr(struct inode * inode, umode_t mode) { inode->i_mode = mode; - inode->i_atime = inode->i_mtime = - inode->i_ctime = current_time(inode); + simple_inode_init_ts(inode); } static inline void set_inode_attr(struct inode * inode, struct iattr * iattr) @@ -97,9 +96,9 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr) inode->i_mode = iattr->ia_mode; inode->i_uid = iattr->ia_uid; inode->i_gid = iattr->ia_gid; - inode->i_atime = iattr->ia_atime; - inode->i_mtime = iattr->ia_mtime; - inode->i_ctime = iattr->ia_ctime; + inode_set_atime_to_ts(inode, iattr->ia_atime); + inode_set_mtime_to_ts(inode, iattr->ia_mtime); + inode_set_ctime_to_ts(inode, iattr->ia_ctime); } struct inode *configfs_new_inode(umode_t mode, struct configfs_dirent *sd, @@ -172,7 +171,7 @@ struct inode *configfs_create(struct dentry *dentry, umode_t mode) return ERR_PTR(-ENOMEM); p_inode = d_inode(dentry->d_parent); - p_inode->i_mtime = p_inode->i_ctime = current_time(p_inode); + inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode)); configfs_set_inode_lock_class(sd, inode); return inode; } @@ -212,33 +211,9 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent) dget_dlock(dentry); __d_drop(dentry); spin_unlock(&dentry->d_lock); - simple_unlink(d_inode(parent), dentry); + __simple_unlink(d_inode(parent), dentry); + dput(dentry); } else spin_unlock(&dentry->d_lock); } } - -void configfs_hash_and_remove(struct dentry * dir, const char * name) -{ - struct configfs_dirent * sd; - struct configfs_dirent * parent_sd = dir->d_fsdata; - - if (d_really_is_negative(dir)) - /* no inode means this hasn't been made visible yet */ - return; - - inode_lock(d_inode(dir)); - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (!sd->s_element) - continue; - if (!strcmp(configfs_get_name(sd), name)) { - spin_lock(&configfs_dirent_lock); - list_del_init(&sd->s_sibling); - spin_unlock(&configfs_dirent_lock); - configfs_drop_dentry(sd, dir); - configfs_put(sd); - break; - } - } - inode_unlock(d_inode(dir)); -} diff --git a/fs/configfs/item.c b/fs/configfs/item.c index 254170a82aa3..c378b5cbf87d 100644 --- a/fs/configfs/item.c +++ b/fs/configfs/item.c @@ -66,7 +66,7 @@ int config_item_set_name(struct config_item *item, const char *fmt, ...) name = kvasprintf(GFP_KERNEL, fmt, args); va_end(args); if (!name) - return -EFAULT; + return -ENOMEM; } /* Free the old name, if necessary. */ diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index c2d820063ec4..4929f3431189 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -36,7 +36,7 @@ static void configfs_free_inode(struct inode *inode) static const struct super_operations configfs_ops = { .statfs = simple_statfs, - .drop_inode = generic_delete_inode, + .drop_inode = inode_just_drop, .free_inode = configfs_free_inode, }; @@ -92,7 +92,8 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc) configfs_root_group.cg_item.ci_dentry = root; root->d_fsdata = &configfs_root; sb->s_root = root; - sb->s_d_op = &configfs_dentry_ops; /* the rest get that */ + set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */ + sb->s_d_flags |= DCACHE_DONTCACHE; return 0; } @@ -115,7 +116,7 @@ static struct file_system_type configfs_fs_type = { .owner = THIS_MODULE, .name = "configfs", .init_fs_context = configfs_init_fs_context, - .kill_sb = kill_litter_super, + .kill_sb = kill_anon_super, }; MODULE_ALIAS_FS("configfs"); diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 0623c3edcfb9..f3f79c67add5 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -114,34 +114,28 @@ static int create_link(struct config_item *parent_item, } -static int get_target(const char *symname, struct path *path, - struct config_item **target, struct super_block *sb) +static int get_target(const char *symname, struct config_item **target, + struct super_block *sb) { + struct path path __free(path_put) = {}; int ret; - ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path); - if (!ret) { - if (path->dentry->d_sb == sb) { - *target = configfs_get_config_item(path->dentry); - if (!*target) { - ret = -ENOENT; - path_put(path); - } - } else { - ret = -EPERM; - path_put(path); - } - } - - return ret; + ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &path); + if (ret) + return ret; + if (path.dentry->d_sb != sb) + return -EPERM; + *target = configfs_get_config_item(path.dentry); + if (!*target) + return -ENOENT; + return 0; } -int configfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, +int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const char *symname) { int ret; - struct path path; struct configfs_dirent *sd; struct config_item *parent_item; struct config_item *target_item = NULL; @@ -188,7 +182,7 @@ int configfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, * AV, a thoroughly annoyed bastard. */ inode_unlock(dir); - ret = get_target(symname, &path, &target_item, dentry->d_sb); + ret = get_target(symname, &target_item, dentry->d_sb); inode_lock(dir); if (ret) goto out_put; @@ -196,7 +190,7 @@ int configfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, if (dentry->d_inode || d_unhashed(dentry)) ret = -EEXIST; else - ret = inode_permission(&init_user_ns, dir, + ret = inode_permission(&nop_mnt_idmap, dir, MAY_WRITE | MAY_EXEC); if (!ret) ret = type->ct_item_ops->allow_link(parent_item, target_item); @@ -210,7 +204,6 @@ int configfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, } config_item_put(target_item); - path_put(&path); out_put: config_item_put(parent_item); |
