162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/filesystems.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  table of configured filesystems
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/syscalls.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/proc_fs.h>
1362306a36Sopenharmony_ci#include <linux/seq_file.h>
1462306a36Sopenharmony_ci#include <linux/kmod.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/uaccess.h>
1962306a36Sopenharmony_ci#include <linux/fs_parser.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * Handling of filesystem drivers list.
2362306a36Sopenharmony_ci * Rules:
2462306a36Sopenharmony_ci *	Inclusion to/removals from/scanning of list are protected by spinlock.
2562306a36Sopenharmony_ci *	During the unload module must call unregister_filesystem().
2662306a36Sopenharmony_ci *	We can access the fields of list element if:
2762306a36Sopenharmony_ci *		1) spinlock is held or
2862306a36Sopenharmony_ci *		2) we hold the reference to the module.
2962306a36Sopenharmony_ci *	The latter can be guaranteed by call of try_module_get(); if it
3062306a36Sopenharmony_ci *	returned 0 we must skip the element, otherwise we got the reference.
3162306a36Sopenharmony_ci *	Once the reference is obtained we can drop the spinlock.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic struct file_system_type *file_systems;
3562306a36Sopenharmony_cistatic DEFINE_RWLOCK(file_systems_lock);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* WARNING: This can be used only if we _already_ own a reference */
3862306a36Sopenharmony_cistruct file_system_type *get_filesystem(struct file_system_type *fs)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	__module_get(fs->owner);
4162306a36Sopenharmony_ci	return fs;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_civoid put_filesystem(struct file_system_type *fs)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	module_put(fs->owner);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic struct file_system_type **find_filesystem(const char *name, unsigned len)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct file_system_type **p;
5262306a36Sopenharmony_ci	for (p = &file_systems; *p; p = &(*p)->next)
5362306a36Sopenharmony_ci		if (strncmp((*p)->name, name, len) == 0 &&
5462306a36Sopenharmony_ci		    !(*p)->name[len])
5562306a36Sopenharmony_ci			break;
5662306a36Sopenharmony_ci	return p;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/**
6062306a36Sopenharmony_ci *	register_filesystem - register a new filesystem
6162306a36Sopenharmony_ci *	@fs: the file system structure
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci *	Adds the file system passed to the list of file systems the kernel
6462306a36Sopenharmony_ci *	is aware of for mount and other syscalls. Returns 0 on success,
6562306a36Sopenharmony_ci *	or a negative errno code on an error.
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci *	The &struct file_system_type that is passed is linked into the kernel
6862306a36Sopenharmony_ci *	structures and must not be freed until the file system has been
6962306a36Sopenharmony_ci *	unregistered.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciint register_filesystem(struct file_system_type * fs)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int res = 0;
7562306a36Sopenharmony_ci	struct file_system_type ** p;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (fs->parameters &&
7862306a36Sopenharmony_ci	    !fs_validate_description(fs->name, fs->parameters))
7962306a36Sopenharmony_ci		return -EINVAL;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	BUG_ON(strchr(fs->name, '.'));
8262306a36Sopenharmony_ci	if (fs->next)
8362306a36Sopenharmony_ci		return -EBUSY;
8462306a36Sopenharmony_ci	write_lock(&file_systems_lock);
8562306a36Sopenharmony_ci	p = find_filesystem(fs->name, strlen(fs->name));
8662306a36Sopenharmony_ci	if (*p)
8762306a36Sopenharmony_ci		res = -EBUSY;
8862306a36Sopenharmony_ci	else
8962306a36Sopenharmony_ci		*p = fs;
9062306a36Sopenharmony_ci	write_unlock(&file_systems_lock);
9162306a36Sopenharmony_ci	return res;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciEXPORT_SYMBOL(register_filesystem);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci *	unregister_filesystem - unregister a file system
9862306a36Sopenharmony_ci *	@fs: filesystem to unregister
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci *	Remove a file system that was previously successfully registered
10162306a36Sopenharmony_ci *	with the kernel. An error is returned if the file system is not found.
10262306a36Sopenharmony_ci *	Zero is returned on a success.
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci *	Once this function has returned the &struct file_system_type structure
10562306a36Sopenharmony_ci *	may be freed or reused.
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciint unregister_filesystem(struct file_system_type * fs)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct file_system_type ** tmp;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	write_lock(&file_systems_lock);
11362306a36Sopenharmony_ci	tmp = &file_systems;
11462306a36Sopenharmony_ci	while (*tmp) {
11562306a36Sopenharmony_ci		if (fs == *tmp) {
11662306a36Sopenharmony_ci			*tmp = fs->next;
11762306a36Sopenharmony_ci			fs->next = NULL;
11862306a36Sopenharmony_ci			write_unlock(&file_systems_lock);
11962306a36Sopenharmony_ci			synchronize_rcu();
12062306a36Sopenharmony_ci			return 0;
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci		tmp = &(*tmp)->next;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	write_unlock(&file_systems_lock);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return -EINVAL;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_filesystem);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#ifdef CONFIG_SYSFS_SYSCALL
13262306a36Sopenharmony_cistatic int fs_index(const char __user * __name)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct file_system_type * tmp;
13562306a36Sopenharmony_ci	struct filename *name;
13662306a36Sopenharmony_ci	int err, index;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	name = getname(__name);
13962306a36Sopenharmony_ci	err = PTR_ERR(name);
14062306a36Sopenharmony_ci	if (IS_ERR(name))
14162306a36Sopenharmony_ci		return err;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	err = -EINVAL;
14462306a36Sopenharmony_ci	read_lock(&file_systems_lock);
14562306a36Sopenharmony_ci	for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {
14662306a36Sopenharmony_ci		if (strcmp(tmp->name, name->name) == 0) {
14762306a36Sopenharmony_ci			err = index;
14862306a36Sopenharmony_ci			break;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	read_unlock(&file_systems_lock);
15262306a36Sopenharmony_ci	putname(name);
15362306a36Sopenharmony_ci	return err;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int fs_name(unsigned int index, char __user * buf)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct file_system_type * tmp;
15962306a36Sopenharmony_ci	int len, res;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	read_lock(&file_systems_lock);
16262306a36Sopenharmony_ci	for (tmp = file_systems; tmp; tmp = tmp->next, index--)
16362306a36Sopenharmony_ci		if (index <= 0 && try_module_get(tmp->owner))
16462306a36Sopenharmony_ci			break;
16562306a36Sopenharmony_ci	read_unlock(&file_systems_lock);
16662306a36Sopenharmony_ci	if (!tmp)
16762306a36Sopenharmony_ci		return -EINVAL;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* OK, we got the reference, so we can safely block */
17062306a36Sopenharmony_ci	len = strlen(tmp->name) + 1;
17162306a36Sopenharmony_ci	res = copy_to_user(buf, tmp->name, len) ? -EFAULT : 0;
17262306a36Sopenharmony_ci	put_filesystem(tmp);
17362306a36Sopenharmony_ci	return res;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int fs_maxindex(void)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct file_system_type * tmp;
17962306a36Sopenharmony_ci	int index;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	read_lock(&file_systems_lock);
18262306a36Sopenharmony_ci	for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next, index++)
18362306a36Sopenharmony_ci		;
18462306a36Sopenharmony_ci	read_unlock(&file_systems_lock);
18562306a36Sopenharmony_ci	return index;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Whee.. Weird sysv syscall.
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_ciSYSCALL_DEFINE3(sysfs, int, option, unsigned long, arg1, unsigned long, arg2)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	int retval = -EINVAL;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	switch (option) {
19662306a36Sopenharmony_ci		case 1:
19762306a36Sopenharmony_ci			retval = fs_index((const char __user *) arg1);
19862306a36Sopenharmony_ci			break;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		case 2:
20162306a36Sopenharmony_ci			retval = fs_name(arg1, (char __user *) arg2);
20262306a36Sopenharmony_ci			break;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		case 3:
20562306a36Sopenharmony_ci			retval = fs_maxindex();
20662306a36Sopenharmony_ci			break;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	return retval;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci#endif
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ciint __init list_bdev_fs_names(char *buf, size_t size)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct file_system_type *p;
21562306a36Sopenharmony_ci	size_t len;
21662306a36Sopenharmony_ci	int count = 0;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	read_lock(&file_systems_lock);
21962306a36Sopenharmony_ci	for (p = file_systems; p; p = p->next) {
22062306a36Sopenharmony_ci		if (!(p->fs_flags & FS_REQUIRES_DEV))
22162306a36Sopenharmony_ci			continue;
22262306a36Sopenharmony_ci		len = strlen(p->name) + 1;
22362306a36Sopenharmony_ci		if (len > size) {
22462306a36Sopenharmony_ci			pr_warn("%s: truncating file system list\n", __func__);
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci		memcpy(buf, p->name, len);
22862306a36Sopenharmony_ci		buf += len;
22962306a36Sopenharmony_ci		size -= len;
23062306a36Sopenharmony_ci		count++;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci	read_unlock(&file_systems_lock);
23362306a36Sopenharmony_ci	return count;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
23762306a36Sopenharmony_cistatic int filesystems_proc_show(struct seq_file *m, void *v)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct file_system_type * tmp;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	read_lock(&file_systems_lock);
24262306a36Sopenharmony_ci	tmp = file_systems;
24362306a36Sopenharmony_ci	while (tmp) {
24462306a36Sopenharmony_ci		seq_printf(m, "%s\t%s\n",
24562306a36Sopenharmony_ci			(tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
24662306a36Sopenharmony_ci			tmp->name);
24762306a36Sopenharmony_ci		tmp = tmp->next;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	read_unlock(&file_systems_lock);
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic int __init proc_filesystems_init(void)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	proc_create_single("filesystems", 0, NULL, filesystems_proc_show);
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_cimodule_init(proc_filesystems_init);
25962306a36Sopenharmony_ci#endif
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic struct file_system_type *__get_fs_type(const char *name, int len)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct file_system_type *fs;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	read_lock(&file_systems_lock);
26662306a36Sopenharmony_ci	fs = *(find_filesystem(name, len));
26762306a36Sopenharmony_ci	if (fs && !try_module_get(fs->owner))
26862306a36Sopenharmony_ci		fs = NULL;
26962306a36Sopenharmony_ci	read_unlock(&file_systems_lock);
27062306a36Sopenharmony_ci	return fs;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistruct file_system_type *get_fs_type(const char *name)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct file_system_type *fs;
27662306a36Sopenharmony_ci	const char *dot = strchr(name, '.');
27762306a36Sopenharmony_ci	int len = dot ? dot - name : strlen(name);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	fs = __get_fs_type(name, len);
28062306a36Sopenharmony_ci	if (!fs && (request_module("fs-%.*s", len, name) == 0)) {
28162306a36Sopenharmony_ci		fs = __get_fs_type(name, len);
28262306a36Sopenharmony_ci		if (!fs)
28362306a36Sopenharmony_ci			pr_warn_once("request_module fs-%.*s succeeded, but still no fs?\n",
28462306a36Sopenharmony_ci				     len, name);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (dot && fs && !(fs->fs_flags & FS_HAS_SUBTYPE)) {
28862306a36Sopenharmony_ci		put_filesystem(fs);
28962306a36Sopenharmony_ci		fs = NULL;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci	return fs;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ciEXPORT_SYMBOL(get_fs_type);
295