diff options
-rw-r--r-- | fs/Makefile | 2 | ||||
-rw-r--r-- | fs/kernfs/Makefile | 5 | ||||
-rw-r--r-- | fs/kernfs/dir.c | 9 | ||||
-rw-r--r-- | fs/kernfs/file.c | 9 | ||||
-rw-r--r-- | fs/kernfs/inode.c | 9 | ||||
-rw-r--r-- | fs/kernfs/mount.c | 9 | ||||
-rw-r--r-- | fs/kernfs/symlink.c | 9 | ||||
-rw-r--r-- | fs/sysfs/dir.c | 138 | ||||
-rw-r--r-- | fs/sysfs/file.c | 19 | ||||
-rw-r--r-- | fs/sysfs/group.c | 15 | ||||
-rw-r--r-- | fs/sysfs/inode.c | 21 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 24 | ||||
-rw-r--r-- | fs/sysfs/symlink.c | 98 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 32 | ||||
-rw-r--r-- | include/linux/kernfs.h | 60 | ||||
-rw-r--r-- | include/linux/sysfs.h | 9 | ||||
-rw-r--r-- | lib/kobject.c | 27 |
17 files changed, 301 insertions, 194 deletions
diff --git a/fs/Makefile b/fs/Makefile index 4fe6df3..39a824f 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_FHANDLE) += fhandle.o obj-y += quota/ obj-$(CONFIG_PROC_FS) += proc/ -obj-$(CONFIG_SYSFS) += sysfs/ +obj-$(CONFIG_SYSFS) += sysfs/ kernfs/ obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-y += devpts/ diff --git a/fs/kernfs/Makefile b/fs/kernfs/Makefile new file mode 100644 index 0000000..674337c --- /dev/null +++ b/fs/kernfs/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the kernfs pseudo filesystem +# + +obj-y := mount.o inode.o dir.o file.o symlink.o diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c new file mode 100644 index 0000000..1061602 --- /dev/null +++ b/fs/kernfs/dir.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/dir.c - kernfs directory implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org> + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c new file mode 100644 index 0000000..90b1e88 --- /dev/null +++ b/fs/kernfs/file.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/file.c - kernfs file implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org> + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c new file mode 100644 index 0000000..86bfeea --- /dev/null +++ b/fs/kernfs/inode.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/inode.c - kernfs inode implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org> + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c new file mode 100644 index 0000000..872e262 --- /dev/null +++ b/fs/kernfs/mount.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/mount.c - kernfs mount implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org> + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c new file mode 100644 index 0000000..2578715 --- /dev/null +++ b/fs/kernfs/symlink.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/symlink.c - kernfs symlink implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org> + * + * This file is released under the GPLv2. + */ diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 5e73d66..5ba8966 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -279,7 +279,6 @@ static int sysfs_dentry_delete(const struct dentry *dentry) static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) { struct sysfs_dirent *sd; - int type; if (flags & LOOKUP_RCU) return -ECHILD; @@ -300,13 +299,9 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) goto out_bad; /* The sysfs dirent has been moved to a different namespace */ - type = KOBJ_NS_TYPE_NONE; - if (sd->s_parent) { - type = sysfs_ns_type(sd->s_parent); - if (type != KOBJ_NS_TYPE_NONE && - sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns) - goto out_bad; - } + if (sd->s_parent && (sd->s_parent->s_flags & SYSFS_FLAG_NS) && + sysfs_info(dentry->d_sb)->ns != sd->s_ns) + goto out_bad; mutex_unlock(&sysfs_mutex); out_valid: @@ -423,16 +418,20 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd) { + bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; struct sysfs_inode_attrs *ps_iattr; int ret; - if (!!sysfs_ns_type(parent_sd) != !!sd->s_ns) { + if (has_ns != (bool)sd->s_ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - sysfs_ns_type(parent_sd) ? "required" : "invalid", - parent_sd->s_name, sd->s_name); + has_ns ? "required" : "invalid", + parent_sd->s_name, sd->s_name); return -EINVAL; } + if (sysfs_type(parent_sd) != SYSFS_DIR) + return -EINVAL; + sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); sd->s_parent = sysfs_get(parent_sd); @@ -610,12 +609,13 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, const void *ns) { struct rb_node *node = parent_sd->s_dir.children.rb_node; + bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; unsigned int hash; - if (!!sysfs_ns_type(parent_sd) != !!ns) { + if (has_ns != (bool)ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - sysfs_ns_type(parent_sd) ? "required" : "invalid", - parent_sd->s_name, name); + has_ns ? "required" : "invalid", + parent_sd->s_name, name); return NULL; } @@ -667,7 +667,6 @@ struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns); static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, - enum kobj_ns_type type, const char *name, const void *ns, struct sysfs_dirent **p_sd) { @@ -681,7 +680,6 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, if (!sd) return -ENOMEM; - sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); sd->s_ns = ns; sd->s_dir.kobj = kobj; @@ -701,33 +699,7 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd) { - return create_dir(kobj, kobj->sd, - KOBJ_NS_TYPE_NONE, name, NULL, p_sd); -} - -/** - * sysfs_read_ns_type: return associated ns_type - * @kobj: the kobject being queried - * - * Each kobject can be tagged with exactly one namespace type - * (i.e. network or user). Return the ns_type associated with - * this object if any - */ -static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj) -{ - const struct kobj_ns_type_operations *ops; - enum kobj_ns_type type; - - ops = kobj_child_ns_ops(kobj); - if (!ops) - return KOBJ_NS_TYPE_NONE; - - type = ops->type; - BUG_ON(type <= KOBJ_NS_TYPE_NONE); - BUG_ON(type >= KOBJ_NS_TYPES); - BUG_ON(!kobj_ns_type_registered(type)); - - return type; + return create_dir(kobj, kobj->sd, name, NULL, p_sd); } /** @@ -737,7 +709,6 @@ static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj) */ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { - enum kobj_ns_type type; struct sysfs_dirent *parent_sd, *sd; int error = 0; @@ -751,9 +722,7 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) if (!parent_sd) return -ENOENT; - type = sysfs_read_ns_type(kobj); - - error = create_dir(kobj, parent_sd, type, kobject_name(kobj), ns, &sd); + error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd); if (!error) kobj->sd = sd; return error; @@ -767,13 +736,12 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, struct sysfs_dirent *parent_sd = parent->d_fsdata; struct sysfs_dirent *sd; struct inode *inode; - enum kobj_ns_type type; - const void *ns; + const void *ns = NULL; mutex_lock(&sysfs_mutex); - type = sysfs_ns_type(parent_sd); - ns = sysfs_info(dir->i_sb)->ns[type]; + if (parent_sd->s_flags & SYSFS_FLAG_NS) + ns = sysfs_info(dir->i_sb)->ns; sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns); @@ -861,8 +829,8 @@ static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, return pos->s_parent; } -static void __sysfs_remove(struct sysfs_addrm_cxt *acxt, - struct sysfs_dirent *sd) +static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd) { struct sysfs_dirent *pos, *next; @@ -881,22 +849,22 @@ static void __sysfs_remove(struct sysfs_addrm_cxt *acxt, } /** - * sysfs_remove - remove a sysfs_dirent recursively + * kernfs_remove - remove a sysfs_dirent recursively * @sd: the sysfs_dirent to remove * * Remove @sd along with all its subdirectories and files. */ -void sysfs_remove(struct sysfs_dirent *sd) +void kernfs_remove(struct sysfs_dirent *sd) { struct sysfs_addrm_cxt acxt; sysfs_addrm_start(&acxt); - __sysfs_remove(&acxt, sd); + __kernfs_remove(&acxt, sd); sysfs_addrm_finish(&acxt); } /** - * sysfs_hash_and_remove - find a sysfs_dirent by name and remove it + * kernfs_remove_by_name_ns - find a sysfs_dirent by name and remove it * @dir_sd: parent of the target * @name: name of the sysfs_dirent to remove * @ns: namespace tag of the sysfs_dirent to remove @@ -904,8 +872,8 @@ void sysfs_remove(struct sysfs_dirent *sd) * Look for the sysfs_dirent with @name and @ns under @dir_sd and remove * it. Returns 0 on success, -ENOENT if such entry doesn't exist. */ -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name, - const void *ns) +int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, + const void *ns) { struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; @@ -920,7 +888,7 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name, sd = sysfs_find_dirent(dir_sd, name, ns); if (sd) - __sysfs_remove(&acxt, sd); + __kernfs_remove(&acxt, sd); sysfs_addrm_finish(&acxt); @@ -960,24 +928,31 @@ void sysfs_remove_dir(struct kobject *kobj) if (sd) { WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); - sysfs_remove(sd); + kernfs_remove(sd); } } -int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, - const char *new_name, const void *new_ns) +/** + * kernfs_rename_ns - move and rename a kernfs_node + * @sd: target node + * @new_parent: new parent to put @sd under + * @new_name: new name + * @new_ns: new namespace tag + */ +int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, + const char *new_name, const void *new_ns) { int error; mutex_lock(&sysfs_mutex); error = 0; - if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) && + if ((sd->s_parent == new_parent) && (sd->s_ns == new_ns) && (strcmp(sd->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; - if (sysfs_find_dirent(new_parent_sd, new_name, new_ns)) + if (sysfs_find_dirent(new_parent, new_name, new_ns)) goto out; /* rename sysfs_dirent */ @@ -995,11 +970,11 @@ int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, * Move to the appropriate place in the appropriate directories rbtree. */ sysfs_unlink_sibling(sd); - sysfs_get(new_parent_sd); + sysfs_get(new_parent); sysfs_put(sd->s_parent); sd->s_ns = new_ns; sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); - sd->s_parent = new_parent_sd; + sd->s_parent = new_parent; sysfs_link_sibling(sd); error = 0; @@ -1013,7 +988,7 @@ int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, { struct sysfs_dirent *parent_sd = kobj->sd->s_parent; - return sysfs_rename(kobj->sd, parent_sd, new_name, new_ns); + return kernfs_rename_ns(kobj->sd, parent_sd, new_name, new_ns); } int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, @@ -1026,7 +1001,22 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, new_parent_sd = new_parent_kobj && new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; - return sysfs_rename(sd, new_parent_sd, sd->s_name, new_ns); + return kernfs_rename_ns(sd, new_parent_sd, sd->s_name, new_ns); +} + +/** + * sysfs_enable_ns - enable namespace under a directory + * @sd: directory of interest, should be empty + * + * This is to be called right after @sd is created to enable namespace + * under it. All children of @sd must have non-NULL namespace tags and + * only the ones which match the super_block's tag will be visible. + */ +void sysfs_enable_ns(struct sysfs_dirent *sd) +{ + WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); + WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); + sd->s_flags |= SYSFS_FLAG_NS; } /* Relationship between s_mode and the DT_xxx types */ @@ -1096,15 +1086,15 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) struct dentry *dentry = file->f_path.dentry; struct sysfs_dirent *parent_sd = dentry->d_fsdata; struct sysfs_dirent *pos = file->private_data; - enum kobj_ns_type type; - const void *ns; - - type = sysfs_ns_type(parent_sd); - ns = sysfs_info(dentry->d_sb)->ns[type]; + const void *ns = NULL; if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&sysfs_mutex); + + if (parent_sd->s_flags & SYSFS_FLAG_NS) + ns = sysfs_info(dentry->d_sb)->ns; + for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); pos; pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index b94f936..0f3f0a2 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -940,19 +940,16 @@ int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, struct iattr newattrs; int rc; - mutex_lock(&sysfs_mutex); - - rc = -ENOENT; - sd = sysfs_find_dirent(kobj->sd, attr->name, NULL); + sd = sysfs_get_dirent(kobj->sd, attr->name); if (!sd) - goto out; + return -ENOENT; newattrs.ia_mode = (mode & S_IALLUGO) | (sd->s_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE; - rc = sysfs_sd_setattr(sd, &newattrs); - out: - mutex_unlock(&sysfs_mutex); + rc = kernfs_setattr(sd, &newattrs); + + sysfs_put(sd); return rc; } EXPORT_SYMBOL_GPL(sysfs_chmod_file); @@ -970,7 +967,7 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, { struct sysfs_dirent *dir_sd = kobj->sd; - sysfs_hash_and_remove(dir_sd, attr->name, ns); + kernfs_remove_by_name_ns(dir_sd, attr->name, ns); } EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); @@ -998,7 +995,7 @@ void sysfs_remove_file_from_group(struct kobject *kobj, else dir_sd = sysfs_get(kobj->sd); if (dir_sd) { - sysfs_hash_and_remove(dir_sd, attr->name, NULL); + kernfs_remove_by_name(dir_sd, attr->name); sysfs_put(dir_sd); } } @@ -1026,7 +1023,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_bin_file); void sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr) { - sysfs_hash_and_remove(kobj->sd, attr->attr.name, NULL); + kernfs_remove_by_name(kobj->sd, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 1898a10..4bd9973 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -26,7 +26,7 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, if (grp->attrs) for (attr = grp->attrs; *attr; attr++) - sysfs_hash_and_remove(dir_sd, (*attr)->name, NULL); + kernfs_remove_by_name(dir_sd, (*attr)->name); if (grp->bin_attrs) for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) sysfs_remove_bin_file(kobj, *bin_attr); @@ -49,8 +49,7 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, * re-adding (if required) the file. */ if (update) - sysfs_hash_and_remove(dir_sd, (*attr)->name, - NULL); + kernfs_remove_by_name(dir_sd, (*attr)->name); if (grp->is_visible) { mode = grp->is_visible(kobj, *attr, i); if (!mode) @@ -111,7 +110,7 @@ static int internal_create_group(struct kobject *kobj, int update, error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) - sysfs_remove(sd); + kernfs_remove(sd); } sysfs_put(sd); return error; @@ -219,7 +218,7 @@ void sysfs_remove_group(struct kobject *kobj, remove_files(sd, kobj, grp); if (grp->name) - sysfs_remove(sd); + kernfs_remove(sd); sysfs_put(sd); } @@ -270,7 +269,7 @@ int sysfs_merge_group(struct kobject *kobj, error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); if (error) { while (--i >= 0) - sysfs_hash_and_remove(dir_sd, (*--attr)->name, NULL); + kernfs_remove_by_name(dir_sd, (*--attr)->name); } sysfs_put(dir_sd); @@ -292,7 +291,7 @@ void sysfs_unmerge_group(struct kobject *kobj, dir_sd = sysfs_get_dirent(kobj->sd, grp->name); if (dir_sd) { for (attr = grp->attrs; *attr; ++attr) - sysfs_hash_and_remove(dir_sd, (*attr)->name, NULL); + kernfs_remove_by_name(dir_sd, (*attr)->name); sysfs_put(dir_sd); } } @@ -335,7 +334,7 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, dir_sd = sysfs_get_dirent(kobj->sd, group_name); if (dir_sd) { - sysfs_hash_and_remove(dir_sd, link_name, NULL); + kernfs_remove_by_name(dir_sd, link_name); sysfs_put(dir_sd); } } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 1750f79..5f7e2af 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -67,7 +67,7 @@ static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) return attrs; } -int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr) +static int __kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) { struct sysfs_inode_attrs *sd_attrs; struct iattr *iattrs; @@ -102,6 +102,23 @@ int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr) return 0; } +/** + * kernfs_setattr - set iattr on a node + * @sd: target node + * @iattr: iattr to set + * + * Returns 0 on success, -errno on failure. + */ +int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) +{ + int ret; + + mutex_lock(&sysfs_mutex); + ret = __kernfs_setattr(sd, iattr); + mutex_unlock(&sysfs_mutex); + return ret; +} + int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) { struct inode *inode = dentry->d_inode; @@ -116,7 +133,7 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) if (error) goto out; - error = sysfs_sd_setattr(sd, iattr); + error = __kernfs_setattr(sd, iattr); if (error) goto out; diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 834ec2c..8c24bce 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -36,7 +36,7 @@ static const struct super_operations sysfs_ops = { struct sysfs_dirent sysfs_root = { .s_name = "", .s_count = ATOMIC_INIT(1), - .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), + .s_flags = SYSFS_DIR, .s_mode = S_IFDIR | S_IRUGO | S_IXUGO, .s_ino = 1, }; @@ -77,14 +77,8 @@ static int sysfs_test_super(struct super_block *sb, void *data) { struct sysfs_super_info *sb_info = sysfs_info(sb); struct sysfs_super_info *info = data; - enum kobj_ns_type type; - int found = 1; - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { - if (sb_info->ns[type] != info->ns[type]) - found = 0; - } - return found; + return sb_info->ns == info->ns; } static int sysfs_set_super(struct super_block *sb, void *data) @@ -98,9 +92,7 @@ static int sysfs_set_super(struct super_block *sb, void *data) static void free_sysfs_super_info(struct sysfs_super_info *info) { - int type; - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) - kobj_ns_drop(type, info->ns[type]); + kobj_ns_drop(KOBJ_NS_TYPE_NET, info->ns); kfree(info); } @@ -108,7 +100,6 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { struct sysfs_super_info *info; - enum kobj_ns_type type; struct super_block *sb; int error; @@ -116,18 +107,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) return ERR_PTR(-EPERM); - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { - if (!kobj_ns_current_may_mount(type)) - return ERR_PTR(-EPERM); - } + if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET)) + return ERR_PTR(-EPERM); } info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return ERR_PTR(-ENOMEM); - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) - info->ns[type] = kobj_ns_grab_current(type); + info->ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); if (IS_ERR(sb) || sb->s_fs_info != info) diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 3ae3f1b..0922c53 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -21,15 +21,48 @@ #include "sysfs.h" +/** + * kernfs_create_link - create a symlink + * @parent: directory to create the symlink in + * @name: name of the symlink + * @target: target node for the symlink to point to + * + * Returns the created node on success, ERR_PTR() value on error. + */ +struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, + const char *name, + struct sysfs_dirent *target) +{ + struct sysfs_dirent *sd; + struct sysfs_addrm_cxt acxt; + int error; + + sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + if (!sd) + return ERR_PTR(-ENOMEM); + + if (parent->s_flags & SYSFS_FLAG_NS) + sd->s_ns = target->s_ns; + sd->s_symlink.target_sd = target; + sysfs_get(target); /* ref owned by symlink */ + + sysfs_addrm_start(&acxt); + error = __sysfs_add_one(&acxt, sd, parent); + sysfs_addrm_finish(&acxt); + + if (!error) + return sd; + + sysfs_put(sd); + return ERR_PTR(error); +} + + static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, struct kobject *target, const char *name, int warn) { - struct sysfs_dirent *target_sd = NULL; - struct sysfs_dirent *sd = NULL; - struct sysfs_addrm_cxt acxt; - enum kobj_ns_type ns_type; - int error; + struct sysfs_dirent *sd, *target_sd = NULL; BUG_ON(!name || !parent_sd); @@ -43,49 +76,18 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, target_sd = sysfs_get(target->sd); spin_unlock(&sysfs_symlink_target_lock); - error = -ENOENT; if (!target_sd) - goto out_put; + return -ENOENT; - error = -ENOMEM; - sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); - if (!sd) - goto out_put; - - ns_type = sysfs_ns_type(parent_sd); - if (ns_type) - sd->s_ns = target_sd->s_ns; - sd->s_symlink.target_sd = target_sd; - target_sd = NULL; /* reference is now owned by the symlink */ - - sysfs_addrm_start(&acxt); - /* Symlinks must be between directories with the same ns_type */ - if (!ns_type || - (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) { - if (warn) - error = sysfs_add_one(&acxt, sd, parent_sd); - else - error = __sysfs_add_one(&acxt, sd, parent_sd); - } else { - error = -EINVAL; - WARN(1, KERN_WARNING - "sysfs: symlink across ns_types %s/%s -> %s/%s\n", - parent_sd->s_name, - sd->s_name, - sd->s_symlink.target_sd->s_parent->s_name, - sd->s_symlink.target_sd->s_name); - } - sysfs_addrm_finish(&acxt); - - if (error) - goto out_put; + sd = kernfs_create_link(parent_sd, name, target_sd); + sysfs_put(target_sd); - return 0; + if (!IS_ERR(sd)) + return 0; - out_put: - sysfs_put(target_sd); - sysfs_put(sd); - return error; + if (warn && PTR_ERR(sd) == -EEXIST) + sysfs_warn_dup(parent_sd, name); + return PTR_ERR(sd); } /** @@ -164,10 +166,10 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, * sysfs_remove_dir() for details. */ spin_lock(&sysfs_symlink_target_lock); - if (targ->sd && sysfs_ns_type(kobj->sd)) + if (targ->sd && (kobj->sd->s_flags & SYSFS_FLAG_NS)) ns = targ->sd->s_ns; spin_unlock(&sysfs_symlink_target_lock); - sysfs_hash_and_remove(kobj->sd, name, ns); + kernfs_remove_by_name_ns(kobj->sd, name, ns); } /** @@ -184,7 +186,7 @@ void sysfs_remove_link(struct kobject *kobj, const char *name) else parent_sd = kobj->sd; - sysfs_hash_and_remove(parent_sd, name, NULL); + kernfs_remove_by_name(parent_sd, name); } EXPORT_SYMBOL_GPL(sysfs_remove_link); @@ -224,7 +226,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, if (sd->s_symlink.target_sd->s_dir.kobj != targ) goto out; - result = sysfs_rename(sd, parent_sd, new, new_ns); + result = kernfs_rename_ns(sd, parent_sd, new, new_ns); out: sysfs_put(sd); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 0af09fb..2abccfd 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -90,11 +90,8 @@ struct sysfs_dirent { #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) #define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR) -/* identify any namespace tag on sysfs_dirents */ -#define SYSFS_NS_TYPE_MASK 0xf00 -#define SYSFS_NS_TYPE_SHIFT 8 - -#define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK) +#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK +#define SYSFS_FLAG_NS 0x01000 #define SYSFS_FLAG_REMOVED 0x02000 static inline unsigned int sysfs_type(struct sysfs_dirent *sd) @@ -102,15 +99,6 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd) return sd->s_flags & SYSFS_TYPE_MASK; } -/* - * Return any namespace tags on this dirent. - * enum kobj_ns_type is defined in linux/kobject.h - */ -static inline enum kobj_ns_type sysfs_ns_type(struct sysfs_dirent *sd) -{ - return (sd->s_flags & SYSFS_NS_TYPE_MASK) >> SYSFS_NS_TYPE_SHIFT; -} - #ifdef CONFIG_DEBUG_LOCK_ALLOC #define sysfs_dirent_init_lockdep(sd) \ @@ -155,12 +143,13 @@ struct sysfs_addrm_cxt { */ /* - * Each sb is associated with a set of namespace tags (i.e. - * the network namespace of the task which mounted this sysfs - * instance). + * Each sb is associated with one namespace tag, currently the network + * namespace of the task which mounted this sysfs instance. If multiple + * tags become necessary, make the following an array and compare + * sysfs_dirent tag against every entry. */ struct sysfs_super_info { - void *ns[KOBJ_NS_TYPES]; + void *ns; }; #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) extern struct sysfs_dirent sysfs_root; @@ -184,9 +173,6 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd); int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd); -void sysfs_remove(struct sysfs_dirent *sd); -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name, - const void *ns); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, @@ -199,9 +185,6 @@ void release_sysfs_dirent(struct sysfs_dirent *sd); int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd); -int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, - const char *new_name, const void *new_ns); - static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd) { if (sd) { @@ -224,7 +207,6 @@ static inline void __sysfs_put(struct sysfs_dirent *sd) */ struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd); void sysfs_evict_inode(struct inode *inode); -int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr); int sysfs_permission(struct inode *inode, int mask); int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h new file mode 100644 index 0000000..8cb6738 --- /dev/null +++ b/include/linux/kernfs.h @@ -0,0 +1,60 @@ +/* + * kernfs.h - pseudo filesystem decoupled from vfs locking + * + * This file is released under the GPLv2. + */ + +#ifndef __LINUX_KERNFS_H +#define __LINUX_KERNFS_H + +#include <linux/kernel.h> +#include <linux/err.h> + +struct file; +struct iattr; + +struct sysfs_dirent; + +#ifdef CONFIG_SYSFS + +struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, + const char *name, + struct sysfs_dirent *target); +void kernfs_remove(struct sysfs_dirent *sd); +int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, + const void *ns); +int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, + const char *new_name, const void *new_ns); +int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr); + +#else /* CONFIG_SYSFS */ + +static inline struct sysfs_dirent * +kernfs_create_link(struct sysfs_dirent *parent, const char *name, + struct sysfs_dirent *target) +{ return ERR_PTR(-ENOSYS); } + +static inline void kernfs_remove(struct sysfs_dirent *sd) { } + +static inline int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, + const char *name, const void *ns) +{ return -ENOSYS; } + +static inline int kernfs_rename_ns(struct sysfs_dirent *sd, + struct sysfs_dirent *new_parent, + const char *new_name, const void *new_ns) +{ return -ENOSYS; } + +static inline int kernfs_setattr(struct sysfs_dirent *sd, + const struct iattr *iattr) +{ return -ENOSYS; } + +#endif /* CONFIG_SYSFS */ + +static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, + const char *name) +{ + return kernfs_remove_by_name_ns(parent, name, NULL); +} + +#endif /* __LINUX_KERNFS_H */ diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 6695040..e17381a 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -12,6 +12,7 @@ #ifndef _SYSFS_H_ #define _SYSFS_H_ +#include <linux/kernfs.h> #include <linux/compiler.h> #include <linux/errno.h> #include <linux/list.h> @@ -175,8 +176,6 @@ struct sysfs_ops { ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); }; -struct sysfs_dirent; - #ifdef CONFIG_SYSFS int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), @@ -220,6 +219,8 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *target, void sysfs_delete_link(struct kobject *dir, struct kobject *targ, const char *name); +void sysfs_enable_ns(struct sysfs_dirent *sd); + int __must_check sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); int __must_check sysfs_create_groups(struct kobject *kobj, @@ -353,6 +354,10 @@ static inline void sysfs_delete_link(struct kobject *k, struct kobject *t, { } +static inline void sysfs_enable_ns(struct sysfs_dirent *sd) +{ +} + static inline int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { diff --git a/lib/kobject.c b/lib/kobject.c index 5b4b888..16e9335 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -65,13 +65,17 @@ static int populate_dir(struct kobject *kobj) static int create_dir(struct kobject *kobj) { + const struct kobj_ns_type_operations *ops; int error; error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); - if (!error) { - error = populate_dir(kobj); - if (error) - sysfs_remove_dir(kobj); + if (error) + return error; + + error = populate_dir(kobj); + if (error) { + sysfs_remove_dir(kobj); + return error; } /* @@ -80,7 +84,20 @@ static int create_dir(struct kobject *kobj) */ sysfs_get(kobj->sd); - return error; + /* + * If @kobj has ns_ops, its children need to be filtered based on + * their namespace tags. Enable namespace support on @kobj->sd. + */ + ops = kobj_child_ns_ops(kobj); + if (ops) { + BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE); + BUG_ON(ops->type >= KOBJ_NS_TYPES); + BUG_ON(!kobj_ns_type_registered(ops->type)); + + sysfs_enable_ns(kobj->sd); + } + + return 0; } static int get_kobj_path_length(struct kobject *kobj) |