162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mount.c - operations for initializing and mounting configfs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on sysfs: 662306a36Sopenharmony_ci * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * configfs Copyright (C) 2005 Oracle. All rights reserved. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mount.h> 1462306a36Sopenharmony_ci#include <linux/fs_context.h> 1562306a36Sopenharmony_ci#include <linux/pagemap.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/configfs.h> 2062306a36Sopenharmony_ci#include "configfs_internal.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Random magic number */ 2362306a36Sopenharmony_ci#define CONFIGFS_MAGIC 0x62656570 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic struct vfsmount *configfs_mount = NULL; 2662306a36Sopenharmony_cistruct kmem_cache *configfs_dir_cachep; 2762306a36Sopenharmony_cistatic int configfs_mnt_count = 0; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void configfs_free_inode(struct inode *inode) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 3362306a36Sopenharmony_ci kfree(inode->i_link); 3462306a36Sopenharmony_ci free_inode_nonrcu(inode); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct super_operations configfs_ops = { 3862306a36Sopenharmony_ci .statfs = simple_statfs, 3962306a36Sopenharmony_ci .drop_inode = generic_delete_inode, 4062306a36Sopenharmony_ci .free_inode = configfs_free_inode, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct config_group configfs_root_group = { 4462306a36Sopenharmony_ci .cg_item = { 4562306a36Sopenharmony_ci .ci_namebuf = "root", 4662306a36Sopenharmony_ci .ci_name = configfs_root_group.cg_item.ci_namebuf, 4762306a36Sopenharmony_ci }, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciint configfs_is_root(struct config_item *item) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return item == &configfs_root_group.cg_item; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct configfs_dirent configfs_root = { 5662306a36Sopenharmony_ci .s_sibling = LIST_HEAD_INIT(configfs_root.s_sibling), 5762306a36Sopenharmony_ci .s_children = LIST_HEAD_INIT(configfs_root.s_children), 5862306a36Sopenharmony_ci .s_element = &configfs_root_group.cg_item, 5962306a36Sopenharmony_ci .s_type = CONFIGFS_ROOT, 6062306a36Sopenharmony_ci .s_iattr = NULL, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int configfs_fill_super(struct super_block *sb, struct fs_context *fc) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct inode *inode; 6662306a36Sopenharmony_ci struct dentry *root; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci sb->s_blocksize = PAGE_SIZE; 6962306a36Sopenharmony_ci sb->s_blocksize_bits = PAGE_SHIFT; 7062306a36Sopenharmony_ci sb->s_magic = CONFIGFS_MAGIC; 7162306a36Sopenharmony_ci sb->s_op = &configfs_ops; 7262306a36Sopenharmony_ci sb->s_time_gran = 1; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, 7562306a36Sopenharmony_ci &configfs_root, sb); 7662306a36Sopenharmony_ci if (inode) { 7762306a36Sopenharmony_ci inode->i_op = &configfs_root_inode_operations; 7862306a36Sopenharmony_ci inode->i_fop = &configfs_dir_operations; 7962306a36Sopenharmony_ci /* directory inodes start off with i_nlink == 2 (for "." entry) */ 8062306a36Sopenharmony_ci inc_nlink(inode); 8162306a36Sopenharmony_ci } else { 8262306a36Sopenharmony_ci pr_debug("could not get root inode\n"); 8362306a36Sopenharmony_ci return -ENOMEM; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci root = d_make_root(inode); 8762306a36Sopenharmony_ci if (!root) { 8862306a36Sopenharmony_ci pr_debug("%s: could not get root dentry!\n",__func__); 8962306a36Sopenharmony_ci return -ENOMEM; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci config_group_init(&configfs_root_group); 9262306a36Sopenharmony_ci configfs_root_group.cg_item.ci_dentry = root; 9362306a36Sopenharmony_ci root->d_fsdata = &configfs_root; 9462306a36Sopenharmony_ci sb->s_root = root; 9562306a36Sopenharmony_ci sb->s_d_op = &configfs_dentry_ops; /* the rest get that */ 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int configfs_get_tree(struct fs_context *fc) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return get_tree_single(fc, configfs_fill_super); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic const struct fs_context_operations configfs_context_ops = { 10562306a36Sopenharmony_ci .get_tree = configfs_get_tree, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int configfs_init_fs_context(struct fs_context *fc) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci fc->ops = &configfs_context_ops; 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic struct file_system_type configfs_fs_type = { 11562306a36Sopenharmony_ci .owner = THIS_MODULE, 11662306a36Sopenharmony_ci .name = "configfs", 11762306a36Sopenharmony_ci .init_fs_context = configfs_init_fs_context, 11862306a36Sopenharmony_ci .kill_sb = kill_litter_super, 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ciMODULE_ALIAS_FS("configfs"); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct dentry *configfs_pin_fs(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int err = simple_pin_fs(&configfs_fs_type, &configfs_mount, 12562306a36Sopenharmony_ci &configfs_mnt_count); 12662306a36Sopenharmony_ci return err ? ERR_PTR(err) : configfs_mount->mnt_root; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_civoid configfs_release_fs(void) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci simple_release_fs(&configfs_mount, &configfs_mnt_count); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int __init configfs_init(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int err = -ENOMEM; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci configfs_dir_cachep = kmem_cache_create("configfs_dir_cache", 14062306a36Sopenharmony_ci sizeof(struct configfs_dirent), 14162306a36Sopenharmony_ci 0, 0, NULL); 14262306a36Sopenharmony_ci if (!configfs_dir_cachep) 14362306a36Sopenharmony_ci goto out; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = sysfs_create_mount_point(kernel_kobj, "config"); 14662306a36Sopenharmony_ci if (err) 14762306a36Sopenharmony_ci goto out2; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci err = register_filesystem(&configfs_fs_type); 15062306a36Sopenharmony_ci if (err) 15162306a36Sopenharmony_ci goto out3; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ciout3: 15562306a36Sopenharmony_ci pr_err("Unable to register filesystem!\n"); 15662306a36Sopenharmony_ci sysfs_remove_mount_point(kernel_kobj, "config"); 15762306a36Sopenharmony_ciout2: 15862306a36Sopenharmony_ci kmem_cache_destroy(configfs_dir_cachep); 15962306a36Sopenharmony_ci configfs_dir_cachep = NULL; 16062306a36Sopenharmony_ciout: 16162306a36Sopenharmony_ci return err; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void __exit configfs_exit(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci unregister_filesystem(&configfs_fs_type); 16762306a36Sopenharmony_ci sysfs_remove_mount_point(kernel_kobj, "config"); 16862306a36Sopenharmony_ci kmem_cache_destroy(configfs_dir_cachep); 16962306a36Sopenharmony_ci configfs_dir_cachep = NULL; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciMODULE_AUTHOR("Oracle"); 17362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 17462306a36Sopenharmony_ciMODULE_VERSION("0.0.2"); 17562306a36Sopenharmony_ciMODULE_DESCRIPTION("Simple RAM filesystem for user driven kernel subsystem configuration."); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cicore_initcall(configfs_init); 17862306a36Sopenharmony_cimodule_exit(configfs_exit); 179