162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fs/sysfs/symlink.c - operations for initializing and mounting sysfs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2001-3 Patrick Mochel 662306a36Sopenharmony_ci * Copyright (c) 2007 SUSE Linux Products GmbH 762306a36Sopenharmony_ci * Copyright (c) 2007 Tejun Heo <teheo@suse.de> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Please see Documentation/filesystems/sysfs.rst for more information. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/magic.h> 1462306a36Sopenharmony_ci#include <linux/mount.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/user_namespace.h> 1862306a36Sopenharmony_ci#include <linux/fs_context.h> 1962306a36Sopenharmony_ci#include <net/net_namespace.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "sysfs.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic struct kernfs_root *sysfs_root; 2462306a36Sopenharmony_cistruct kernfs_node *sysfs_root_kn; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int sysfs_get_tree(struct fs_context *fc) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct kernfs_fs_context *kfc = fc->fs_private; 2962306a36Sopenharmony_ci int ret; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci ret = kernfs_get_tree(fc); 3262306a36Sopenharmony_ci if (ret) 3362306a36Sopenharmony_ci return ret; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (kfc->new_sb_created) 3662306a36Sopenharmony_ci fc->root->d_sb->s_iflags |= SB_I_USERNS_VISIBLE; 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void sysfs_fs_context_free(struct fs_context *fc) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct kernfs_fs_context *kfc = fc->fs_private; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (kfc->ns_tag) 4562306a36Sopenharmony_ci kobj_ns_drop(KOBJ_NS_TYPE_NET, kfc->ns_tag); 4662306a36Sopenharmony_ci kernfs_free_fs_context(fc); 4762306a36Sopenharmony_ci kfree(kfc); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct fs_context_operations sysfs_fs_context_ops = { 5162306a36Sopenharmony_ci .free = sysfs_fs_context_free, 5262306a36Sopenharmony_ci .get_tree = sysfs_get_tree, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int sysfs_init_fs_context(struct fs_context *fc) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct kernfs_fs_context *kfc; 5862306a36Sopenharmony_ci struct net *netns; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (!(fc->sb_flags & SB_KERNMOUNT)) { 6162306a36Sopenharmony_ci if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET)) 6262306a36Sopenharmony_ci return -EPERM; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci kfc = kzalloc(sizeof(struct kernfs_fs_context), GFP_KERNEL); 6662306a36Sopenharmony_ci if (!kfc) 6762306a36Sopenharmony_ci return -ENOMEM; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci kfc->ns_tag = netns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); 7062306a36Sopenharmony_ci kfc->root = sysfs_root; 7162306a36Sopenharmony_ci kfc->magic = SYSFS_MAGIC; 7262306a36Sopenharmony_ci fc->fs_private = kfc; 7362306a36Sopenharmony_ci fc->ops = &sysfs_fs_context_ops; 7462306a36Sopenharmony_ci if (netns) { 7562306a36Sopenharmony_ci put_user_ns(fc->user_ns); 7662306a36Sopenharmony_ci fc->user_ns = get_user_ns(netns->user_ns); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci fc->global = true; 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void sysfs_kill_sb(struct super_block *sb) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci void *ns = (void *)kernfs_super_ns(sb); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci kernfs_kill_sb(sb); 8762306a36Sopenharmony_ci kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic struct file_system_type sysfs_fs_type = { 9162306a36Sopenharmony_ci .name = "sysfs", 9262306a36Sopenharmony_ci .init_fs_context = sysfs_init_fs_context, 9362306a36Sopenharmony_ci .kill_sb = sysfs_kill_sb, 9462306a36Sopenharmony_ci .fs_flags = FS_USERNS_MOUNT, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciint __init sysfs_init(void) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int err; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK, 10262306a36Sopenharmony_ci NULL); 10362306a36Sopenharmony_ci if (IS_ERR(sysfs_root)) 10462306a36Sopenharmony_ci return PTR_ERR(sysfs_root); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci sysfs_root_kn = kernfs_root_to_node(sysfs_root); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci err = register_filesystem(&sysfs_fs_type); 10962306a36Sopenharmony_ci if (err) { 11062306a36Sopenharmony_ci kernfs_destroy_root(sysfs_root); 11162306a36Sopenharmony_ci return err; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 116