c84a3b2779
The way namespace tags are implemented in sysfs is more complicated than necessary. As each tag is a pointer value and required to be non-NULL under a namespace enabled parent, there's no need to record separately what type each tag is. If multiple namespace types are needed, which currently aren't, we can simply compare the tag to a set of allowed tags in the superblock assuming that the tags, being pointers, won't have the same value across multiple types. This patch rips out kobj_ns_type handling from sysfs. sysfs now has an enable switch to turn on namespace under a node. If enabled, all children are required to have non-NULL namespace tags and filtered against the super_block's tag. kobject namespace determination is now performed in lib/kobject.c::create_dir() making sysfs_read_ns_type() unnecessary. The sanity checks are also moved. create_dir() is restructured to ease such addition. This removes most kobject namespace knowledge from sysfs proper which will enable proper separation and layering of sysfs. This is the second try. The first one wascb26a31157
("sysfs: drop kobj_ns_type handling") which tried to automatically enable namespace if there are children with non-NULL namespace tags; however, it was broken for symlinks as they should inherit the target's tag iff namespace is enabled in the parent. This led to namespace filtering enabled incorrectly for wireless net class devices through phy80211 symlinks and thus network configuration failure.a1212d278c
("Revert "sysfs: drop kobj_ns_type handling"") reverted the commit. This shouldn't introduce any behavior changes, for real. v2: Dummy implementation of sysfs_enable_ns() for !CONFIG_SYSFS was missing and caused build failure. Reported by kbuild test robot. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: Linus Torvalds <torvalds@linux-foundation.org> Cc: Eric W. Biederman <ebiederm@xmission.com> Cc: Kay Sievers <kay@vrfy.org> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: kbuild test robot <fengguang.wu@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
201 lines
4.4 KiB
C
201 lines
4.4 KiB
C
/*
|
|
* fs/sysfs/symlink.c - operations for initializing and mounting sysfs
|
|
*
|
|
* Copyright (c) 2001-3 Patrick Mochel
|
|
* Copyright (c) 2007 SUSE Linux Products GmbH
|
|
* Copyright (c) 2007 Tejun Heo <teheo@suse.de>
|
|
*
|
|
* This file is released under the GPLv2.
|
|
*
|
|
* Please see Documentation/filesystems/sysfs.txt for more information.
|
|
*/
|
|
|
|
#define DEBUG
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/magic.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/user_namespace.h>
|
|
|
|
#include "sysfs.h"
|
|
|
|
|
|
static struct vfsmount *sysfs_mnt;
|
|
struct kmem_cache *sysfs_dir_cachep;
|
|
|
|
static const struct super_operations sysfs_ops = {
|
|
.statfs = simple_statfs,
|
|
.drop_inode = generic_delete_inode,
|
|
.evict_inode = sysfs_evict_inode,
|
|
};
|
|
|
|
struct sysfs_dirent sysfs_root = {
|
|
.s_name = "",
|
|
.s_count = ATOMIC_INIT(1),
|
|
.s_flags = SYSFS_DIR,
|
|
.s_mode = S_IFDIR | S_IRUGO | S_IXUGO,
|
|
.s_ino = 1,
|
|
};
|
|
|
|
static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
|
|
{
|
|
struct inode *inode;
|
|
struct dentry *root;
|
|
|
|
sb->s_blocksize = PAGE_CACHE_SIZE;
|
|
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
|
sb->s_magic = SYSFS_MAGIC;
|
|
sb->s_op = &sysfs_ops;
|
|
sb->s_time_gran = 1;
|
|
|
|
/* get root inode, initialize and unlock it */
|
|
mutex_lock(&sysfs_mutex);
|
|
inode = sysfs_get_inode(sb, &sysfs_root);
|
|
mutex_unlock(&sysfs_mutex);
|
|
if (!inode) {
|
|
pr_debug("sysfs: could not get root inode\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* instantiate and link root dentry */
|
|
root = d_make_root(inode);
|
|
if (!root) {
|
|
pr_debug("%s: could not get root dentry!\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
root->d_fsdata = &sysfs_root;
|
|
sb->s_root = root;
|
|
sb->s_d_op = &sysfs_dentry_ops;
|
|
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;
|
|
|
|
return sb_info->ns == info->ns;
|
|
}
|
|
|
|
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 void free_sysfs_super_info(struct sysfs_super_info *info)
|
|
{
|
|
kobj_ns_drop(KOBJ_NS_TYPE_NET, info->ns);
|
|
kfree(info);
|
|
}
|
|
|
|
static struct dentry *sysfs_mount(struct file_system_type *fs_type,
|
|
int flags, const char *dev_name, void *data)
|
|
{
|
|
struct sysfs_super_info *info;
|
|
struct super_block *sb;
|
|
int error;
|
|
|
|
if (!(flags & MS_KERNMOUNT)) {
|
|
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);
|
|
}
|
|
|
|
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
if (!info)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
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)
|
|
free_sysfs_super_info(info);
|
|
if (IS_ERR(sb))
|
|
return ERR_CAST(sb);
|
|
if (!sb->s_root) {
|
|
error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
|
|
if (error) {
|
|
deactivate_locked_super(sb);
|
|
return ERR_PTR(error);
|
|
}
|
|
sb->s_flags |= MS_ACTIVE;
|
|
}
|
|
|
|
return dget(sb->s_root);
|
|
}
|
|
|
|
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);
|
|
free_sysfs_super_info(info);
|
|
}
|
|
|
|
static struct file_system_type sysfs_fs_type = {
|
|
.name = "sysfs",
|
|
.mount = sysfs_mount,
|
|
.kill_sb = sysfs_kill_sb,
|
|
.fs_flags = FS_USERNS_MOUNT,
|
|
};
|
|
|
|
int __init sysfs_init(void)
|
|
{
|
|
int err = -ENOMEM;
|
|
|
|
sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
|
|
sizeof(struct sysfs_dirent),
|
|
0, 0, NULL);
|
|
if (!sysfs_dir_cachep)
|
|
goto out;
|
|
|
|
err = sysfs_inode_init();
|
|
if (err)
|
|
goto out_err;
|
|
|
|
err = register_filesystem(&sysfs_fs_type);
|
|
if (!err) {
|
|
sysfs_mnt = kern_mount(&sysfs_fs_type);
|
|
if (IS_ERR(sysfs_mnt)) {
|
|
printk(KERN_ERR "sysfs: could not mount!\n");
|
|
err = PTR_ERR(sysfs_mnt);
|
|
sysfs_mnt = NULL;
|
|
unregister_filesystem(&sysfs_fs_type);
|
|
goto out_err;
|
|
}
|
|
} else
|
|
goto out_err;
|
|
out:
|
|
return err;
|
|
out_err:
|
|
kmem_cache_destroy(sysfs_dir_cachep);
|
|
sysfs_dir_cachep = NULL;
|
|
goto out;
|
|
}
|
|
|
|
#undef sysfs_get
|
|
struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd)
|
|
{
|
|
return __sysfs_get(sd);
|
|
}
|
|
EXPORT_SYMBOL_GPL(sysfs_get);
|
|
|
|
#undef sysfs_put
|
|
void sysfs_put(struct sysfs_dirent *sd)
|
|
{
|
|
__sysfs_put(sd);
|
|
}
|
|
EXPORT_SYMBOL_GPL(sysfs_put);
|