xref: /kernel/linux/linux-6.6/fs/sharefs/super.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 1998-2022 Erez Zadok
462306a36Sopenharmony_ci * Copyright (c) 2009	   Shrikar Archak
562306a36Sopenharmony_ci * Copyright (c) 2003-2022 Stony Brook University
662306a36Sopenharmony_ci * Copyright (c) 2003-2022 The Research Foundation of SUNY
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/backing-dev-defs.h>
962306a36Sopenharmony_ci#include <linux/ratelimit.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/parser.h>
1262306a36Sopenharmony_ci#include "sharefs.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cienum {
1562306a36Sopenharmony_ci	OPT_USER_ID,
1662306a36Sopenharmony_ci	OPT_OVERRIDE,
1762306a36Sopenharmony_ci	OPT_OVERRIDE_SUPPORT_DELETE,
1862306a36Sopenharmony_ci	OPT_ERR,
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic match_table_t sharefs_tokens = {
2262306a36Sopenharmony_ci	{ OPT_USER_ID, "user_id=%s" },
2362306a36Sopenharmony_ci	{ OPT_OVERRIDE, "override" },
2462306a36Sopenharmony_ci	{ OPT_OVERRIDE_SUPPORT_DELETE, "override_support_delete" },
2562306a36Sopenharmony_ci	{ OPT_ERR, NULL }
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciint sharefs_parse_options(struct sharefs_sb_info *sbi, const char *data)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	char *p = NULL;
3162306a36Sopenharmony_ci	char *name = NULL;
3262306a36Sopenharmony_ci	char *options = NULL;
3362306a36Sopenharmony_ci	char *options_src = NULL;
3462306a36Sopenharmony_ci	substring_t args[MAX_OPT_ARGS];
3562306a36Sopenharmony_ci	unsigned int user_id = 0;
3662306a36Sopenharmony_ci	int err = 0;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	options = kstrdup(data, GFP_KERNEL);
3962306a36Sopenharmony_ci	if (data && !options) {
4062306a36Sopenharmony_ci		err = -ENOMEM;
4162306a36Sopenharmony_ci		goto out;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci	options_src = options;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	while ((p = strsep(&options_src, ",")) != NULL) {
4662306a36Sopenharmony_ci		int token;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		if (!*p)
4962306a36Sopenharmony_ci			continue;
5062306a36Sopenharmony_ci		args[0].to = args[0].from = NULL;
5162306a36Sopenharmony_ci		token = match_token(p, sharefs_tokens, args);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		switch (token) {
5462306a36Sopenharmony_ci		case OPT_USER_ID:
5562306a36Sopenharmony_ci			name = match_strdup(&args[0]);
5662306a36Sopenharmony_ci			if (name) {
5762306a36Sopenharmony_ci				err = kstrtouint(name, 10, &user_id);
5862306a36Sopenharmony_ci				kfree(name);
5962306a36Sopenharmony_ci				name = NULL;
6062306a36Sopenharmony_ci				if (err)
6162306a36Sopenharmony_ci					goto out;
6262306a36Sopenharmony_ci				sbi->user_id = user_id;
6362306a36Sopenharmony_ci			}
6462306a36Sopenharmony_ci			break;
6562306a36Sopenharmony_ci		case OPT_OVERRIDE:
6662306a36Sopenharmony_ci			sbi->override = true;
6762306a36Sopenharmony_ci			break;
6862306a36Sopenharmony_ci		case OPT_OVERRIDE_SUPPORT_DELETE:
6962306a36Sopenharmony_ci			sbi->override_support_delete = true;
7062306a36Sopenharmony_ci			break;
7162306a36Sopenharmony_ci		default:
7262306a36Sopenharmony_ci			err = -EINVAL;
7362306a36Sopenharmony_ci			goto out;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ciout:
7762306a36Sopenharmony_ci	kfree(options);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return err;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci * The inode cache is used with alloc_inode for both our inode info and the
8462306a36Sopenharmony_ci * vfs inode.
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistatic struct kmem_cache *sharefs_inode_cachep;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* final actions when unmounting a file system */
8962306a36Sopenharmony_cistatic void sharefs_put_super(struct super_block *sb)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct sharefs_sb_info *spd;
9262306a36Sopenharmony_ci	struct super_block *s;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	spd = SHAREFS_SB(sb);
9562306a36Sopenharmony_ci	if (!spd)
9662306a36Sopenharmony_ci		return;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* decrement lower super references */
9962306a36Sopenharmony_ci	s = sharefs_lower_super(sb);
10062306a36Sopenharmony_ci	sharefs_set_lower_super(sb, NULL);
10162306a36Sopenharmony_ci	atomic_dec(&s->s_active);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	kfree(spd);
10462306a36Sopenharmony_ci	sb->s_fs_info = NULL;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int sharefs_statfs(struct dentry *dentry, struct kstatfs *buf)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int err;
11062306a36Sopenharmony_ci	struct path lower_path;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	sharefs_get_lower_path(dentry, &lower_path);
11362306a36Sopenharmony_ci	err = vfs_statfs(&lower_path, buf);
11462306a36Sopenharmony_ci	sharefs_put_lower_path(dentry, &lower_path);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* set return buf to our f/s to avoid confusing user-level utils */
11762306a36Sopenharmony_ci	buf->f_type = SHAREFS_SUPER_MAGIC;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return err;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/*
12362306a36Sopenharmony_ci * Called by iput() when the inode reference count reached zero
12462306a36Sopenharmony_ci * and the inode is not hashed anywhere.  Used to clear anything
12562306a36Sopenharmony_ci * that needs to be, before the inode is completely destroyed and put
12662306a36Sopenharmony_ci * on the inode free list.
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_cistatic void sharefs_evict_inode(struct inode *inode)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct inode *lower_inode;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	truncate_inode_pages(&inode->i_data, 0);
13362306a36Sopenharmony_ci	clear_inode(inode);
13462306a36Sopenharmony_ci	/*
13562306a36Sopenharmony_ci	 * Decrement a reference to a lower_inode, which was incremented
13662306a36Sopenharmony_ci	 * by our read_inode when it was created initially.
13762306a36Sopenharmony_ci	 */
13862306a36Sopenharmony_ci	lower_inode = sharefs_lower_inode(inode);
13962306a36Sopenharmony_ci	sharefs_set_lower_inode(inode, NULL);
14062306a36Sopenharmony_ci	iput(lower_inode);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_civoid __sharefs_log(const char *level, const bool ratelimited,
14462306a36Sopenharmony_ci		 const char *function, const char *fmt, ...)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct va_format vaf;
14762306a36Sopenharmony_ci	va_list args;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	va_start(args, fmt);
15062306a36Sopenharmony_ci	vaf.fmt = fmt;
15162306a36Sopenharmony_ci	vaf.va = &args;
15262306a36Sopenharmony_ci	if (ratelimited)
15362306a36Sopenharmony_ci		printk_ratelimited("%s sharefs: %s() %pV\n", level,
15462306a36Sopenharmony_ci				   function, &vaf);
15562306a36Sopenharmony_ci	else
15662306a36Sopenharmony_ci		printk("%s sharefs: %s() %pV\n", level, function, &vaf);
15762306a36Sopenharmony_ci	va_end(args);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic struct inode *sharefs_alloc_inode(struct super_block *sb)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct sharefs_inode_info *i;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	i = kmem_cache_alloc(sharefs_inode_cachep, GFP_KERNEL);
16562306a36Sopenharmony_ci	if (!i)
16662306a36Sopenharmony_ci		return NULL;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* memset everything up to the inode to 0 */
16962306a36Sopenharmony_ci	memset(i, 0, offsetof(struct sharefs_inode_info, vfs_inode));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	atomic64_set(&i->vfs_inode.i_version, 1);
17262306a36Sopenharmony_ci	return &i->vfs_inode;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void sharefs_destroy_inode(struct inode *inode)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	kmem_cache_free(sharefs_inode_cachep, SHAREFS_I(inode));
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/* sharefs inode cache constructor */
18162306a36Sopenharmony_cistatic void init_once(void *obj)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct sharefs_inode_info *i = obj;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	inode_init_once(&i->vfs_inode);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciint sharefs_init_inode_cache(void)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int err = 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	sharefs_inode_cachep =
19362306a36Sopenharmony_ci		kmem_cache_create("sharefs_inode_cache",
19462306a36Sopenharmony_ci				  sizeof(struct sharefs_inode_info), 0,
19562306a36Sopenharmony_ci				  SLAB_RECLAIM_ACCOUNT, init_once);
19662306a36Sopenharmony_ci	if (!sharefs_inode_cachep)
19762306a36Sopenharmony_ci		err = -ENOMEM;
19862306a36Sopenharmony_ci	return err;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/* sharefs inode cache destructor */
20262306a36Sopenharmony_civoid sharefs_destroy_inode_cache(void)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	if (sharefs_inode_cachep)
20562306a36Sopenharmony_ci		kmem_cache_destroy(sharefs_inode_cachep);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ciconst struct super_operations sharefs_sops = {
20962306a36Sopenharmony_ci	.put_super	= sharefs_put_super,
21062306a36Sopenharmony_ci	.statfs		= sharefs_statfs,
21162306a36Sopenharmony_ci	.evict_inode	= sharefs_evict_inode,
21262306a36Sopenharmony_ci	.alloc_inode	= sharefs_alloc_inode,
21362306a36Sopenharmony_ci	.destroy_inode	= sharefs_destroy_inode,
21462306a36Sopenharmony_ci};
215