162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/sharefs/main.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 1998-2022 Erez Zadok
662306a36Sopenharmony_ci * Copyright (c) 2009	   Shrikar Archak
762306a36Sopenharmony_ci * Copyright (c) 2003-2022 Stony Brook University
862306a36Sopenharmony_ci * Copyright (c) 2003-2022 The Research Foundation of SUNY
962306a36Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include "sharefs.h"
1462306a36Sopenharmony_ci#include "authentication.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct sharefs_mount_priv {
1862306a36Sopenharmony_ci	const char *dev_name;
1962306a36Sopenharmony_ci	const char *raw_data;
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * There is no need to lock the sharefs_super_info's rwsem as there is no
2462306a36Sopenharmony_ci * way anyone can have a reference to the superblock at this point in time.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cistatic int sharefs_fill_super(struct super_block *sb, void *data, int silent)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	struct sharefs_mount_priv *priv = (struct sharefs_mount_priv *)data;
3062306a36Sopenharmony_ci	const char *dev_name = priv->dev_name;
3162306a36Sopenharmony_ci	const char *raw_data = priv->raw_data;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	int err = 0;
3462306a36Sopenharmony_ci	struct super_block *lower_sb;
3562306a36Sopenharmony_ci	struct path lower_path;
3662306a36Sopenharmony_ci	struct inode *inode;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* parse lower path */
3962306a36Sopenharmony_ci	err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
4062306a36Sopenharmony_ci			&lower_path);
4162306a36Sopenharmony_ci	if (err) {
4262306a36Sopenharmony_ci		printk(KERN_ERR	"sharefs: error accessing "
4362306a36Sopenharmony_ci		       "lower directory '%s'\n", dev_name);
4462306a36Sopenharmony_ci		goto out;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* allocate superblock private data */
4862306a36Sopenharmony_ci	sb->s_fs_info = kzalloc(sizeof(struct sharefs_sb_info), GFP_KERNEL);
4962306a36Sopenharmony_ci	if (!SHAREFS_SB(sb)) {
5062306a36Sopenharmony_ci		printk(KERN_CRIT "sharefs: fill_super: out of memory\n");
5162306a36Sopenharmony_ci		err = -ENOMEM;
5262306a36Sopenharmony_ci		goto out_pput;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* set the lower superblock field of upper superblock */
5662306a36Sopenharmony_ci	lower_sb = lower_path.dentry->d_sb;
5762306a36Sopenharmony_ci	atomic_inc(&lower_sb->s_active);
5862306a36Sopenharmony_ci	sharefs_set_lower_super(sb, lower_sb);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* inherit maxbytes from lower file system */
6162306a36Sopenharmony_ci	sb->s_maxbytes = lower_sb->s_maxbytes;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/*
6462306a36Sopenharmony_ci	 * Our c/m/atime granularity is 1 ns because we may stack on file
6562306a36Sopenharmony_ci	 * systems whose granularity is as good.
6662306a36Sopenharmony_ci	 */
6762306a36Sopenharmony_ci	sb->s_time_gran = 1;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	sb->s_op = &sharefs_sops;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* get a new inode and allocate our root dentry */
7262306a36Sopenharmony_ci	inode = sharefs_iget(sb, d_inode(lower_path.dentry));
7362306a36Sopenharmony_ci	if (IS_ERR(inode)) {
7462306a36Sopenharmony_ci		err = PTR_ERR(inode);
7562306a36Sopenharmony_ci		goto out_pput;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	sharefs_root_inode_perm_init(inode);
7862306a36Sopenharmony_ci	sb->s_root = d_make_root(inode);
7962306a36Sopenharmony_ci	if (!sb->s_root) {
8062306a36Sopenharmony_ci		err = -ENOMEM;
8162306a36Sopenharmony_ci		goto out_pput;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci	d_set_d_op(sb->s_root, &sharefs_dops);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	err = sharefs_parse_options(sb->s_fs_info, raw_data);
8662306a36Sopenharmony_ci	if (err)
8762306a36Sopenharmony_ci		goto out_pput;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* link the upper and lower dentries */
9062306a36Sopenharmony_ci	sb->s_root->d_fsdata = NULL;
9162306a36Sopenharmony_ci	err = new_dentry_private_data(sb->s_root);
9262306a36Sopenharmony_ci	if (err)
9362306a36Sopenharmony_ci		goto out_pput;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* if get here: cannot have error */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* set the lower dentries for s_root */
9862306a36Sopenharmony_ci	sharefs_set_lower_path(sb->s_root, &lower_path);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/*
10162306a36Sopenharmony_ci	 * No need to call interpose because we already have a positive
10262306a36Sopenharmony_ci	 * dentry, which was instantiated by d_make_root.  Just need to
10362306a36Sopenharmony_ci	 * d_rehash it.
10462306a36Sopenharmony_ci	 */
10562306a36Sopenharmony_ci	d_rehash(sb->s_root);
10662306a36Sopenharmony_ci	if (!silent)
10762306a36Sopenharmony_ci		printk(KERN_INFO
10862306a36Sopenharmony_ci		       "sharefs: mounted on top of %s type %s\n",
10962306a36Sopenharmony_ci		       dev_name, lower_sb->s_type->name);
11062306a36Sopenharmony_ci	goto out; /* all is well */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/*
11362306a36Sopenharmony_ci	 * path_put is the only resource we need to free if an error occurred
11462306a36Sopenharmony_ci	 * because returning an error from this function will cause
11562306a36Sopenharmony_ci	 * generic_shutdown_super to be called, which will call
11662306a36Sopenharmony_ci	 * sharefs_put_super, and that function will release any other
11762306a36Sopenharmony_ci	 * resources we took.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ciout_pput:
12062306a36Sopenharmony_ci	path_put(&lower_path);
12162306a36Sopenharmony_ciout:
12262306a36Sopenharmony_ci	return err;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct dentry *sharefs_mount(struct file_system_type *fs_type, int flags,
12662306a36Sopenharmony_ci			    const char *dev_name, void *raw_data)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct sharefs_mount_priv priv = {
12962306a36Sopenharmony_ci		.dev_name = dev_name,
13062306a36Sopenharmony_ci		.raw_data = raw_data,
13162306a36Sopenharmony_ci	};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* sharefs needs a valid dev_name to get the lower_sb's metadata */
13462306a36Sopenharmony_ci	if (!dev_name || !*dev_name)
13562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return mount_nodev(fs_type, flags, &priv,
13862306a36Sopenharmony_ci			   sharefs_fill_super);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct file_system_type sharefs_fs_type = {
14262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
14362306a36Sopenharmony_ci	.name		= SHAREFS_NAME,
14462306a36Sopenharmony_ci	.mount		= sharefs_mount,
14562306a36Sopenharmony_ci	.kill_sb	= generic_shutdown_super,
14662306a36Sopenharmony_ci	.fs_flags	= 0,
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int __init init_sharefs_fs(void)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int err;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	pr_info("Registering sharefs");
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	err = sharefs_init_inode_cache();
15662306a36Sopenharmony_ci	if (err)
15762306a36Sopenharmony_ci		goto out_err;
15862306a36Sopenharmony_ci	err = sharefs_init_dentry_cache();
15962306a36Sopenharmony_ci	if (err)
16062306a36Sopenharmony_ci		goto out_err;
16162306a36Sopenharmony_ci	err = register_filesystem(&sharefs_fs_type);
16262306a36Sopenharmony_ci	if (err) {
16362306a36Sopenharmony_ci		sharefs_err("share register failed!");
16462306a36Sopenharmony_ci		goto out_err;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	err = sharefs_init_configfs();
16862306a36Sopenharmony_ci	if (err)
16962306a36Sopenharmony_ci		goto out_err;
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ciout_err:
17262306a36Sopenharmony_ci	sharefs_exit_configfs();
17362306a36Sopenharmony_ci	sharefs_destroy_inode_cache();
17462306a36Sopenharmony_ci	sharefs_destroy_dentry_cache();
17562306a36Sopenharmony_ci	sharefs_err("sharefs init failed!");
17662306a36Sopenharmony_ci	return err;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void __exit exit_sharefs_fs(void)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	sharefs_destroy_inode_cache();
18262306a36Sopenharmony_ci	sharefs_destroy_dentry_cache();
18362306a36Sopenharmony_ci	unregister_filesystem(&sharefs_fs_type);
18462306a36Sopenharmony_ci	sharefs_exit_configfs();
18562306a36Sopenharmony_ci	pr_info("Completed sharefs module unload\n");
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cimodule_init(init_sharefs_fs);
18962306a36Sopenharmony_cimodule_exit(exit_sharefs_fs);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ciMODULE_LICENSE("GPL V2");
19262306a36Sopenharmony_ciMODULE_DESCRIPTION("Share File System");
19362306a36Sopenharmony_ciMODULE_ALIAS_FS("sharefs");