diff options
Diffstat (limited to 'fs/sysfs/mount.c')
-rw-r--r-- | fs/sysfs/mount.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c new file mode 100644 index 00000000..e34f0d99 --- /dev/null +++ b/fs/sysfs/mount.c @@ -0,0 +1,201 @@ +/* + * 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 "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 | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), + .s_mode = S_IFDIR | S_IRWXU | 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_alloc_root(inode); + if (!root) { + pr_debug("%s: could not get root dentry!\n",__func__); + iput(inode); + return -ENOMEM; + } + root->d_fsdata = &sysfs_root; + sb->s_root = root; + 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 void free_sysfs_super_info(struct sysfs_super_info *info) +{ + int type; + for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) + kobj_ns_drop(type, info->ns[type]); + 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; + enum kobj_ns_type type; + struct super_block *sb; + int error; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + 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, 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) { + sb->s_flags = flags; + 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, +}; + +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); |