Revert "sysfs: drop kobj_ns_type handling"
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Nov 2013 11:47:28 +0000 (20:47 +0900)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Nov 2013 11:47:28 +0000 (20:47 +0900)
This reverts commit cb26a311578e67769e92a39a0a63476533cb7e12.

It mysteriously causes NetworkManager to not find the wireless device
for me.  As far as I can tell, Tejun *meant* for this commit to not make
any semantic changes, but there clearly are some.  So revert it, taking
into account some of the calling convention changes that happened in
this area in subsequent commits.

Cc: Tejun Heo <tj@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/sysfs/dir.c
fs/sysfs/mount.c
fs/sysfs/symlink.c
fs/sysfs/sysfs.h
lib/kobject.c

index 08c66969d52a7af81071cbdf2dfc1a7a81dcad98..5e73d6626e50593ef95eeca259d71d6e8babbb55 100644 (file)
@@ -111,11 +111,6 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd)
        /* add new node and rebalance the tree */
        rb_link_node(&sd->s_rb, parent, node);
        rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
-
-       /* if @sd has ns tag, mark the parent to enable ns filtering */
-       if (sd->s_ns)
-               sd->s_parent->s_flags |= SYSFS_FLAG_HAS_NS;
-
        return 0;
 }
 
@@ -135,13 +130,6 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
                sd->s_parent->s_dir.subdirs--;
 
        rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
-
-       /*
-        * Either all or none of the children have tags.  Clearing HAS_NS
-        * when there's no child left is enough to keep the flag synced.
-        */
-       if (RB_EMPTY_ROOT(&sd->s_parent->s_dir.children))
-               sd->s_parent->s_flags &= ~SYSFS_FLAG_HAS_NS;
 }
 
 /**
@@ -291,6 +279,7 @@ 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;
@@ -311,8 +300,13 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
                goto out_bad;
 
        /* The sysfs dirent has been moved to a different namespace */
-       if (sd->s_ns && sd->s_ns != sysfs_info(dentry->d_sb)->ns)
-               goto out_bad;
+       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;
+       }
 
        mutex_unlock(&sysfs_mutex);
 out_valid:
@@ -432,6 +426,13 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd,
        struct sysfs_inode_attrs *ps_iattr;
        int ret;
 
+       if (!!sysfs_ns_type(parent_sd) != !!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);
+               return -EINVAL;
+       }
+
        sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns);
        sd->s_parent = sysfs_get(parent_sd);
 
@@ -611,6 +612,13 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
        struct rb_node *node = parent_sd->s_dir.children.rb_node;
        unsigned int hash;
 
+       if (!!sysfs_ns_type(parent_sd) != !!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);
+               return NULL;
+       }
+
        hash = sysfs_name_hash(name, ns);
        while (node) {
                struct sysfs_dirent *sd;
@@ -659,6 +667,7 @@ 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)
 {
@@ -672,6 +681,7 @@ 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;
 
@@ -691,7 +701,33 @@ 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, name, NULL, 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;
 }
 
 /**
@@ -701,6 +737,7 @@ int sysfs_create_subdir(struct kobject *kobj, const char *name,
  */
 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;
 
@@ -714,7 +751,9 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
        if (!parent_sd)
                return -ENOENT;
 
-       error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd);
+       type = sysfs_read_ns_type(kobj);
+
+       error = create_dir(kobj, parent_sd, type, kobject_name(kobj), ns, &sd);
        if (!error)
                kobj->sd = sd;
        return error;
@@ -728,12 +767,13 @@ 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;
-       const void *ns = NULL;
+       enum kobj_ns_type type;
+       const void *ns;
 
        mutex_lock(&sysfs_mutex);
 
-       if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS)
-               ns = sysfs_info(dir->i_sb)->ns;
+       type = sysfs_ns_type(parent_sd);
+       ns = sysfs_info(dir->i_sb)->ns[type];
 
        sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns);
 
@@ -1056,15 +1096,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;
-       const void *ns = NULL;
+       enum kobj_ns_type type;
+       const void *ns;
+
+       type = sysfs_ns_type(parent_sd);
+       ns = sysfs_info(dentry->d_sb)->ns[type];
 
        if (!dir_emit_dots(file, ctx))
                return 0;
        mutex_lock(&sysfs_mutex);
-
-       if (parent_sd->s_flags & SYSFS_FLAG_HAS_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)) {
index 8c24bce2f4ae514770302f53ca85f6bb00da7260..834ec2cdb7a37e070b7e5ed04ec684ba46cb2093 100644 (file)
@@ -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,
+       .s_flags        = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),
        .s_mode         = S_IFDIR | S_IRUGO | S_IXUGO,
        .s_ino          = 1,
 };
@@ -77,8 +77,14 @@ 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;
 
-       return sb_info->ns == info->ns;
+       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
+               if (sb_info->ns[type] != info->ns[type])
+                       found = 0;
+       }
+       return found;
 }
 
 static int sysfs_set_super(struct super_block *sb, void *data)
@@ -92,7 +98,9 @@ static int sysfs_set_super(struct super_block *sb, void *data)
 
 static void free_sysfs_super_info(struct sysfs_super_info *info)
 {
-       kobj_ns_drop(KOBJ_NS_TYPE_NET, info->ns);
+       int type;
+       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
+               kobj_ns_drop(type, info->ns[type]);
        kfree(info);
 }
 
@@ -100,6 +108,7 @@ 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;
 
@@ -107,15 +116,18 @@ 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);
 
-               if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET))
-                       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);
+               }
        }
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return ERR_PTR(-ENOMEM);
 
-       info->ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET);
+       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
+               info->ns[type] = kobj_ns_grab_current(type);
 
        sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info);
        if (IS_ERR(sb) || sb->s_fs_info != info)
index 1a23681b817901a46dad3e4432c54de147248d37..3ae3f1bf1a0957075bec9d36d8834550000c3b14 100644 (file)
@@ -28,6 +28,7 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd,
        struct sysfs_dirent *target_sd = NULL;
        struct sysfs_dirent *sd = NULL;
        struct sysfs_addrm_cxt acxt;
+       enum kobj_ns_type ns_type;
        int error;
 
        BUG_ON(!name || !parent_sd);
@@ -51,15 +52,29 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd,
        if (!sd)
                goto out_put;
 
-       sd->s_ns = target_sd->s_ns;
+       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);
-       if (warn)
-               error = sysfs_add_one(&acxt, sd, parent_sd);
-       else
-               error = __sysfs_add_one(&acxt, sd, parent_sd);
+       /* 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)
@@ -149,7 +164,7 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ,
         * sysfs_remove_dir() for details.
         */
        spin_lock(&sysfs_symlink_target_lock);
-       if (targ->sd)
+       if (targ->sd && sysfs_ns_type(kobj->sd))
                ns = targ->sd->s_ns;
        spin_unlock(&sysfs_symlink_target_lock);
        sysfs_hash_and_remove(kobj->sd, name, ns);
index e3aea92ebfa3668580ad2bab1ad80925be0f17a8..0af09fbfb3f6abc112581638bf401b4b562430e8 100644 (file)
@@ -90,8 +90,11 @@ struct sysfs_dirent {
 #define SYSFS_COPY_NAME                        (SYSFS_DIR | SYSFS_KOBJ_LINK)
 #define SYSFS_ACTIVE_REF               (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR)
 
-#define SYSFS_FLAG_MASK                        ~SYSFS_TYPE_MASK
-#define SYSFS_FLAG_HAS_NS              0x01000
+/* 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_REMOVED             0x02000
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
@@ -99,6 +102,15 @@ 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)                          \
@@ -143,13 +155,12 @@ struct sysfs_addrm_cxt {
  */
 
 /*
- * 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.
+ * Each sb is associated with a set of namespace tags (i.e.
+ * the network namespace of the task which mounted this sysfs
+ * instance).
  */
 struct sysfs_super_info {
-       void *ns;
+       void *ns[KOBJ_NS_TYPES];
 };
 #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info))
 extern struct sysfs_dirent sysfs_root;
index 7a1c203083eb59e6e17db1af3e942a1351e756f7..5b4b8886435ec77c7f51ff0221b3bc01558e5798 100644 (file)
 const void *kobject_namespace(struct kobject *kobj)
 {
        const struct kobj_ns_type_operations *ns_ops = kobj_ns_ops(kobj);
-       const void *ns;
 
        if (!ns_ops || ns_ops->type == KOBJ_NS_TYPE_NONE)
                return NULL;
 
-       ns = kobj->ktype->namespace(kobj);
-       WARN_ON(!ns);   /* @kobj in a namespace is required to have !NULL tag */
-       return ns;
+       return kobj->ktype->namespace(kobj);
 }
 
 /*