forked from Minki/linux
ba7443bc65
There currently is single kernfs hierarchy in the whole system which is used for sysfs. kernfs needs to support multiple hierarchies to allow other users. This patch introduces struct kernfs_root which serves as the root of each kernfs hierarchy and implements kernfs_create/destroy_root(). * Each kernfs_root is associated with a root sd (sysfs_dentry). The root is freed when the root sd is released and kernfs_destory_root() simply invokes kernfs_remove() on the root sd. sysfs_remove_one() is updated to handle release of the root sd. Note that ps_iattr update in sysfs_remove_one() is trivially updated for readability. * Root sd's are now dynamically allocated using sysfs_new_dirent(). Update sysfs_alloc_ino() so that it gives out ino from 1 so that the root sd still gets ino 1. * While kernfs currently only points to the root sd, it'll soon grow fields which are specific to each hierarchy. As determining a given sd's root will be necessary, sd->s_dir.root is added. This backlink fits better as a separate field in sd; however, sd->s_dir is inside union with space to spare, so use it to save space and provide kernfs_root() accessor to determine the root sd. * As hierarchies may be destroyed now, each mount needs to hold onto the hierarchy it's attached to. Update sysfs_fill_super() and sysfs_kill_sb() so that they get and put the kernfs_root respectively. * sysfs_root is replaced with kernfs_root which is dynamically created by invoking kernfs_create_root() from sysfs_init(). This patch doesn't introduce any visible behavior changes. v2: kernfs_create_root() forgot to set @sd->priv. Fixed. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
187 lines
4.1 KiB
C
187 lines
4.1 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"
|
|
|
|
|
|
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,
|
|
};
|
|
|
|
static struct kernfs_root *sysfs_root;
|
|
struct sysfs_dirent *sysfs_root_sd;
|
|
|
|
static int sysfs_fill_super(struct super_block *sb)
|
|
{
|
|
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_sd);
|
|
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;
|
|
}
|
|
kernfs_get(sysfs_root_sd);
|
|
root->d_fsdata = sysfs_root_sd;
|
|
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, (void *)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);
|
|
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);
|
|
struct sysfs_dirent *root_sd = sb->s_root->d_fsdata;
|
|
|
|
/*
|
|
* 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);
|
|
kernfs_put(root_sd);
|
|
}
|
|
|
|
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;
|
|
|
|
sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
|
|
sizeof(struct sysfs_dirent),
|
|
0, 0, NULL);
|
|
if (!sysfs_dir_cachep)
|
|
return -ENOMEM;
|
|
|
|
err = sysfs_inode_init();
|
|
if (err)
|
|
goto out_err;
|
|
|
|
sysfs_root = kernfs_create_root(NULL);
|
|
if (IS_ERR(sysfs_root)) {
|
|
err = PTR_ERR(sysfs_root);
|
|
goto out_err;
|
|
}
|
|
sysfs_root_sd = sysfs_root->sd;
|
|
|
|
err = register_filesystem(&sysfs_fs_type);
|
|
if (err)
|
|
goto out_destroy_root;
|
|
|
|
return 0;
|
|
|
|
out_destroy_root:
|
|
kernfs_destroy_root(sysfs_root);
|
|
out_err:
|
|
kmem_cache_destroy(sysfs_dir_cachep);
|
|
sysfs_dir_cachep = NULL;
|
|
return err;
|
|
}
|