Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[sfrench/cifs-2.6.git] / fs / sysfs / mount.c
index 776137828dcaf6587bed86fb7cf16c7a3c89ffe0..281c0c9bc39f84f85d9ab6f78adc2d93e56a4a40 100644 (file)
@@ -35,7 +35,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_IRWXU | S_IRUGO | S_IXUGO,
        .s_ino          = 1,
 };
@@ -72,18 +72,107 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
        return 0;
 }
 
+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;
+}
+
+static int sysfs_set_super(struct super_block *sb, void *data)
+{
+       int error;
+       error = set_anon_super(sb, data);
+       if (!error)
+               sb->s_fs_info = data;
+       return error;
+}
+
 static int sysfs_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data, struct vfsmount *mnt)
 {
-       return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);
+       struct sysfs_super_info *info;
+       enum kobj_ns_type type;
+       struct super_block *sb;
+       int error;
+
+       error = -ENOMEM;
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               goto out;
+
+       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
+               info->ns[type] = kobj_ns_current(type);
+
+       sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
+       if (IS_ERR(sb) || sb->s_fs_info != info)
+               kfree(info);
+       if (IS_ERR(sb)) {
+               error = PTR_ERR(sb);
+               goto out;
+       }
+       if (!sb->s_root) {
+               sb->s_flags = flags;
+               error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
+               if (error) {
+                       deactivate_locked_super(sb);
+                       goto out;
+               }
+               sb->s_flags |= MS_ACTIVE;
+       }
+
+       simple_set_mnt(mnt, sb);
+       error = 0;
+out:
+       return error;
+}
+
+static void sysfs_kill_sb(struct super_block *sb)
+{
+       struct sysfs_super_info *info = sysfs_info(sb);
+
+       /* Remove the superblock from fs_supers/s_instances
+        * so we can't find it, before freeing sysfs_super_info.
+        */
+       kill_anon_super(sb);
+       kfree(info);
 }
 
 static struct file_system_type sysfs_fs_type = {
        .name           = "sysfs",
        .get_sb         = sysfs_get_sb,
-       .kill_sb        = kill_anon_super,
+       .kill_sb        = sysfs_kill_sb,
 };
 
+void sysfs_exit_ns(enum kobj_ns_type type, const void *ns)
+{
+       struct super_block *sb;
+
+       mutex_lock(&sysfs_mutex);
+       spin_lock(&sb_lock);
+       list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) {
+               struct sysfs_super_info *info = sysfs_info(sb);
+               /*
+                * If we see a superblock on the fs_supers/s_instances
+                * list the unmount has not completed and sb->s_fs_info
+                * points to a valid struct sysfs_super_info.
+                */
+               /* Ignore superblocks with the wrong ns */
+               if (info->ns[type] != ns)
+                       continue;
+               info->ns[type] = NULL;
+       }
+       spin_unlock(&sb_lock);
+       mutex_unlock(&sysfs_mutex);
+}
+
 int __init sysfs_init(void)
 {
        int err = -ENOMEM;