diff options
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r-- | fs/sysfs/dir.c | 138 |
1 files changed, 64 insertions, 74 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 5e73d6626e50..5ba896630d04 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)) { |