212 lines · 4.8 KB · C
RawDownload
/* * 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 | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),	.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;	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; 	if (!(flags & MS_KERNMOUNT)) {		if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))			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); 	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)		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_getstruct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd){	return __sysfs_get(sd);}EXPORT_SYMBOL_GPL(sysfs_get); #undef sysfs_putvoid sysfs_put(struct sysfs_dirent *sd){	__sysfs_put(sd);}EXPORT_SYMBOL_GPL(sysfs_put);