18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Filesystem access-by-fd.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/fs_context.h>
98c2ecf20Sopenharmony_ci#include <linux/fs_parser.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
128c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
138c2ecf20Sopenharmony_ci#include <linux/security.h>
148c2ecf20Sopenharmony_ci#include <linux/anon_inodes.h>
158c2ecf20Sopenharmony_ci#include <linux/namei.h>
168c2ecf20Sopenharmony_ci#include <linux/file.h>
178c2ecf20Sopenharmony_ci#include <uapi/linux/mount.h>
188c2ecf20Sopenharmony_ci#include "internal.h"
198c2ecf20Sopenharmony_ci#include "mount.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * Allow the user to read back any error, warning or informational messages.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic ssize_t fscontext_read(struct file *file,
258c2ecf20Sopenharmony_ci			      char __user *_buf, size_t len, loff_t *pos)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct fs_context *fc = file->private_data;
288c2ecf20Sopenharmony_ci	struct fc_log *log = fc->log.log;
298c2ecf20Sopenharmony_ci	unsigned int logsize = ARRAY_SIZE(log->buffer);
308c2ecf20Sopenharmony_ci	ssize_t ret;
318c2ecf20Sopenharmony_ci	char *p;
328c2ecf20Sopenharmony_ci	bool need_free;
338c2ecf20Sopenharmony_ci	int index, n;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	ret = mutex_lock_interruptible(&fc->uapi_mutex);
368c2ecf20Sopenharmony_ci	if (ret < 0)
378c2ecf20Sopenharmony_ci		return ret;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (log->head == log->tail) {
408c2ecf20Sopenharmony_ci		mutex_unlock(&fc->uapi_mutex);
418c2ecf20Sopenharmony_ci		return -ENODATA;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	index = log->tail & (logsize - 1);
458c2ecf20Sopenharmony_ci	p = log->buffer[index];
468c2ecf20Sopenharmony_ci	need_free = log->need_free & (1 << index);
478c2ecf20Sopenharmony_ci	log->buffer[index] = NULL;
488c2ecf20Sopenharmony_ci	log->need_free &= ~(1 << index);
498c2ecf20Sopenharmony_ci	log->tail++;
508c2ecf20Sopenharmony_ci	mutex_unlock(&fc->uapi_mutex);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	ret = -EMSGSIZE;
538c2ecf20Sopenharmony_ci	n = strlen(p);
548c2ecf20Sopenharmony_ci	if (n > len)
558c2ecf20Sopenharmony_ci		goto err_free;
568c2ecf20Sopenharmony_ci	ret = -EFAULT;
578c2ecf20Sopenharmony_ci	if (copy_to_user(_buf, p, n) != 0)
588c2ecf20Sopenharmony_ci		goto err_free;
598c2ecf20Sopenharmony_ci	ret = n;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cierr_free:
628c2ecf20Sopenharmony_ci	if (need_free)
638c2ecf20Sopenharmony_ci		kfree(p);
648c2ecf20Sopenharmony_ci	return ret;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int fscontext_release(struct inode *inode, struct file *file)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct fs_context *fc = file->private_data;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (fc) {
728c2ecf20Sopenharmony_ci		file->private_data = NULL;
738c2ecf20Sopenharmony_ci		put_fs_context(fc);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciconst struct file_operations fscontext_fops = {
798c2ecf20Sopenharmony_ci	.read		= fscontext_read,
808c2ecf20Sopenharmony_ci	.release	= fscontext_release,
818c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci * Attach a filesystem context to a file and an fd.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int fd;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	fd = anon_inode_getfd("[fscontext]", &fscontext_fops, fc,
928c2ecf20Sopenharmony_ci			      O_RDWR | o_flags);
938c2ecf20Sopenharmony_ci	if (fd < 0)
948c2ecf20Sopenharmony_ci		put_fs_context(fc);
958c2ecf20Sopenharmony_ci	return fd;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int fscontext_alloc_log(struct fs_context *fc)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	fc->log.log = kzalloc(sizeof(*fc->log.log), GFP_KERNEL);
1018c2ecf20Sopenharmony_ci	if (!fc->log.log)
1028c2ecf20Sopenharmony_ci		return -ENOMEM;
1038c2ecf20Sopenharmony_ci	refcount_set(&fc->log.log->usage, 1);
1048c2ecf20Sopenharmony_ci	fc->log.log->owner = fc->fs_type->owner;
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ci * Open a filesystem by name so that it can be configured for mounting.
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * We are allowed to specify a container in which the filesystem will be
1128c2ecf20Sopenharmony_ci * opened, thereby indicating which namespaces will be used (notably, which
1138c2ecf20Sopenharmony_ci * network namespace will be used for network filesystems).
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_ciSYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct file_system_type *fs_type;
1188c2ecf20Sopenharmony_ci	struct fs_context *fc;
1198c2ecf20Sopenharmony_ci	const char *fs_name;
1208c2ecf20Sopenharmony_ci	int ret;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
1238c2ecf20Sopenharmony_ci		return -EPERM;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (flags & ~FSOPEN_CLOEXEC)
1268c2ecf20Sopenharmony_ci		return -EINVAL;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	fs_name = strndup_user(_fs_name, PAGE_SIZE);
1298c2ecf20Sopenharmony_ci	if (IS_ERR(fs_name))
1308c2ecf20Sopenharmony_ci		return PTR_ERR(fs_name);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	fs_type = get_fs_type(fs_name);
1338c2ecf20Sopenharmony_ci	kfree(fs_name);
1348c2ecf20Sopenharmony_ci	if (!fs_type)
1358c2ecf20Sopenharmony_ci		return -ENODEV;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	fc = fs_context_for_mount(fs_type, 0);
1388c2ecf20Sopenharmony_ci	put_filesystem(fs_type);
1398c2ecf20Sopenharmony_ci	if (IS_ERR(fc))
1408c2ecf20Sopenharmony_ci		return PTR_ERR(fc);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	fc->phase = FS_CONTEXT_CREATE_PARAMS;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	ret = fscontext_alloc_log(fc);
1458c2ecf20Sopenharmony_ci	if (ret < 0)
1468c2ecf20Sopenharmony_ci		goto err_fc;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return fscontext_create_fd(fc, flags & FSOPEN_CLOEXEC ? O_CLOEXEC : 0);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cierr_fc:
1518c2ecf20Sopenharmony_ci	put_fs_context(fc);
1528c2ecf20Sopenharmony_ci	return ret;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci * Pick a superblock into a context for reconfiguration.
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct fs_context *fc;
1618c2ecf20Sopenharmony_ci	struct path target;
1628c2ecf20Sopenharmony_ci	unsigned int lookup_flags;
1638c2ecf20Sopenharmony_ci	int ret;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
1668c2ecf20Sopenharmony_ci		return -EPERM;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if ((flags & ~(FSPICK_CLOEXEC |
1698c2ecf20Sopenharmony_ci		       FSPICK_SYMLINK_NOFOLLOW |
1708c2ecf20Sopenharmony_ci		       FSPICK_NO_AUTOMOUNT |
1718c2ecf20Sopenharmony_ci		       FSPICK_EMPTY_PATH)) != 0)
1728c2ecf20Sopenharmony_ci		return -EINVAL;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
1758c2ecf20Sopenharmony_ci	if (flags & FSPICK_SYMLINK_NOFOLLOW)
1768c2ecf20Sopenharmony_ci		lookup_flags &= ~LOOKUP_FOLLOW;
1778c2ecf20Sopenharmony_ci	if (flags & FSPICK_NO_AUTOMOUNT)
1788c2ecf20Sopenharmony_ci		lookup_flags &= ~LOOKUP_AUTOMOUNT;
1798c2ecf20Sopenharmony_ci	if (flags & FSPICK_EMPTY_PATH)
1808c2ecf20Sopenharmony_ci		lookup_flags |= LOOKUP_EMPTY;
1818c2ecf20Sopenharmony_ci	ret = user_path_at(dfd, path, lookup_flags, &target);
1828c2ecf20Sopenharmony_ci	if (ret < 0)
1838c2ecf20Sopenharmony_ci		goto err;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ret = -EINVAL;
1868c2ecf20Sopenharmony_ci	if (target.mnt->mnt_root != target.dentry)
1878c2ecf20Sopenharmony_ci		goto err_path;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	fc = fs_context_for_reconfigure(target.dentry, 0, 0);
1908c2ecf20Sopenharmony_ci	if (IS_ERR(fc)) {
1918c2ecf20Sopenharmony_ci		ret = PTR_ERR(fc);
1928c2ecf20Sopenharmony_ci		goto err_path;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	fc->phase = FS_CONTEXT_RECONF_PARAMS;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = fscontext_alloc_log(fc);
1988c2ecf20Sopenharmony_ci	if (ret < 0)
1998c2ecf20Sopenharmony_ci		goto err_fc;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	path_put(&target);
2028c2ecf20Sopenharmony_ci	return fscontext_create_fd(fc, flags & FSPICK_CLOEXEC ? O_CLOEXEC : 0);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cierr_fc:
2058c2ecf20Sopenharmony_ci	put_fs_context(fc);
2068c2ecf20Sopenharmony_cierr_path:
2078c2ecf20Sopenharmony_ci	path_put(&target);
2088c2ecf20Sopenharmony_cierr:
2098c2ecf20Sopenharmony_ci	return ret;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/*
2138c2ecf20Sopenharmony_ci * Check the state and apply the configuration.  Note that this function is
2148c2ecf20Sopenharmony_ci * allowed to 'steal' the value by setting param->xxx to NULL before returning.
2158c2ecf20Sopenharmony_ci */
2168c2ecf20Sopenharmony_cistatic int vfs_fsconfig_locked(struct fs_context *fc, int cmd,
2178c2ecf20Sopenharmony_ci			       struct fs_parameter *param)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct super_block *sb;
2208c2ecf20Sopenharmony_ci	int ret;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	ret = finish_clean_context(fc);
2238c2ecf20Sopenharmony_ci	if (ret)
2248c2ecf20Sopenharmony_ci		return ret;
2258c2ecf20Sopenharmony_ci	switch (cmd) {
2268c2ecf20Sopenharmony_ci	case FSCONFIG_CMD_CREATE:
2278c2ecf20Sopenharmony_ci		if (fc->phase != FS_CONTEXT_CREATE_PARAMS)
2288c2ecf20Sopenharmony_ci			return -EBUSY;
2298c2ecf20Sopenharmony_ci		if (!mount_capable(fc))
2308c2ecf20Sopenharmony_ci			return -EPERM;
2318c2ecf20Sopenharmony_ci		fc->phase = FS_CONTEXT_CREATING;
2328c2ecf20Sopenharmony_ci		ret = vfs_get_tree(fc);
2338c2ecf20Sopenharmony_ci		if (ret)
2348c2ecf20Sopenharmony_ci			break;
2358c2ecf20Sopenharmony_ci		sb = fc->root->d_sb;
2368c2ecf20Sopenharmony_ci		ret = security_sb_kern_mount(sb);
2378c2ecf20Sopenharmony_ci		if (unlikely(ret)) {
2388c2ecf20Sopenharmony_ci			fc_drop_locked(fc);
2398c2ecf20Sopenharmony_ci			break;
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci		up_write(&sb->s_umount);
2428c2ecf20Sopenharmony_ci		fc->phase = FS_CONTEXT_AWAITING_MOUNT;
2438c2ecf20Sopenharmony_ci		return 0;
2448c2ecf20Sopenharmony_ci	case FSCONFIG_CMD_RECONFIGURE:
2458c2ecf20Sopenharmony_ci		if (fc->phase != FS_CONTEXT_RECONF_PARAMS)
2468c2ecf20Sopenharmony_ci			return -EBUSY;
2478c2ecf20Sopenharmony_ci		fc->phase = FS_CONTEXT_RECONFIGURING;
2488c2ecf20Sopenharmony_ci		sb = fc->root->d_sb;
2498c2ecf20Sopenharmony_ci		if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) {
2508c2ecf20Sopenharmony_ci			ret = -EPERM;
2518c2ecf20Sopenharmony_ci			break;
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci		down_write(&sb->s_umount);
2548c2ecf20Sopenharmony_ci		ret = reconfigure_super(fc);
2558c2ecf20Sopenharmony_ci		up_write(&sb->s_umount);
2568c2ecf20Sopenharmony_ci		if (ret)
2578c2ecf20Sopenharmony_ci			break;
2588c2ecf20Sopenharmony_ci		vfs_clean_context(fc);
2598c2ecf20Sopenharmony_ci		return 0;
2608c2ecf20Sopenharmony_ci	default:
2618c2ecf20Sopenharmony_ci		if (fc->phase != FS_CONTEXT_CREATE_PARAMS &&
2628c2ecf20Sopenharmony_ci		    fc->phase != FS_CONTEXT_RECONF_PARAMS)
2638c2ecf20Sopenharmony_ci			return -EBUSY;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		return vfs_parse_fs_param(fc, param);
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	fc->phase = FS_CONTEXT_FAILED;
2688c2ecf20Sopenharmony_ci	return ret;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/**
2728c2ecf20Sopenharmony_ci * sys_fsconfig - Set parameters and trigger actions on a context
2738c2ecf20Sopenharmony_ci * @fd: The filesystem context to act upon
2748c2ecf20Sopenharmony_ci * @cmd: The action to take
2758c2ecf20Sopenharmony_ci * @_key: Where appropriate, the parameter key to set
2768c2ecf20Sopenharmony_ci * @_value: Where appropriate, the parameter value to set
2778c2ecf20Sopenharmony_ci * @aux: Additional information for the value
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * This system call is used to set parameters on a context, including
2808c2ecf20Sopenharmony_ci * superblock settings, data source and security labelling.
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci * Actions include triggering the creation of a superblock and the
2838c2ecf20Sopenharmony_ci * reconfiguration of the superblock attached to the specified context.
2848c2ecf20Sopenharmony_ci *
2858c2ecf20Sopenharmony_ci * When setting a parameter, @cmd indicates the type of value being proposed
2868c2ecf20Sopenharmony_ci * and @_key indicates the parameter to be altered.
2878c2ecf20Sopenharmony_ci *
2888c2ecf20Sopenharmony_ci * @_value and @aux are used to specify the value, should a value be required:
2898c2ecf20Sopenharmony_ci *
2908c2ecf20Sopenharmony_ci * (*) fsconfig_set_flag: No value is specified.  The parameter must be boolean
2918c2ecf20Sopenharmony_ci *     in nature.  The key may be prefixed with "no" to invert the
2928c2ecf20Sopenharmony_ci *     setting. @_value must be NULL and @aux must be 0.
2938c2ecf20Sopenharmony_ci *
2948c2ecf20Sopenharmony_ci * (*) fsconfig_set_string: A string value is specified.  The parameter can be
2958c2ecf20Sopenharmony_ci *     expecting boolean, integer, string or take a path.  A conversion to an
2968c2ecf20Sopenharmony_ci *     appropriate type will be attempted (which may include looking up as a
2978c2ecf20Sopenharmony_ci *     path).  @_value points to a NUL-terminated string and @aux must be 0.
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * (*) fsconfig_set_binary: A binary blob is specified.  @_value points to the
3008c2ecf20Sopenharmony_ci *     blob and @aux indicates its size.  The parameter must be expecting a
3018c2ecf20Sopenharmony_ci *     blob.
3028c2ecf20Sopenharmony_ci *
3038c2ecf20Sopenharmony_ci * (*) fsconfig_set_path: A non-empty path is specified.  The parameter must be
3048c2ecf20Sopenharmony_ci *     expecting a path object.  @_value points to a NUL-terminated string that
3058c2ecf20Sopenharmony_ci *     is the path and @aux is a file descriptor at which to start a relative
3068c2ecf20Sopenharmony_ci *     lookup or AT_FDCWD.
3078c2ecf20Sopenharmony_ci *
3088c2ecf20Sopenharmony_ci * (*) fsconfig_set_path_empty: As fsconfig_set_path, but with AT_EMPTY_PATH
3098c2ecf20Sopenharmony_ci *     implied.
3108c2ecf20Sopenharmony_ci *
3118c2ecf20Sopenharmony_ci * (*) fsconfig_set_fd: An open file descriptor is specified.  @_value must be
3128c2ecf20Sopenharmony_ci *     NULL and @aux indicates the file descriptor.
3138c2ecf20Sopenharmony_ci */
3148c2ecf20Sopenharmony_ciSYSCALL_DEFINE5(fsconfig,
3158c2ecf20Sopenharmony_ci		int, fd,
3168c2ecf20Sopenharmony_ci		unsigned int, cmd,
3178c2ecf20Sopenharmony_ci		const char __user *, _key,
3188c2ecf20Sopenharmony_ci		const void __user *, _value,
3198c2ecf20Sopenharmony_ci		int, aux)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct fs_context *fc;
3228c2ecf20Sopenharmony_ci	struct fd f;
3238c2ecf20Sopenharmony_ci	int ret;
3248c2ecf20Sopenharmony_ci	int lookup_flags = 0;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	struct fs_parameter param = {
3278c2ecf20Sopenharmony_ci		.type	= fs_value_is_undefined,
3288c2ecf20Sopenharmony_ci	};
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (fd < 0)
3318c2ecf20Sopenharmony_ci		return -EINVAL;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	switch (cmd) {
3348c2ecf20Sopenharmony_ci	case FSCONFIG_SET_FLAG:
3358c2ecf20Sopenharmony_ci		if (!_key || _value || aux)
3368c2ecf20Sopenharmony_ci			return -EINVAL;
3378c2ecf20Sopenharmony_ci		break;
3388c2ecf20Sopenharmony_ci	case FSCONFIG_SET_STRING:
3398c2ecf20Sopenharmony_ci		if (!_key || !_value || aux)
3408c2ecf20Sopenharmony_ci			return -EINVAL;
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	case FSCONFIG_SET_BINARY:
3438c2ecf20Sopenharmony_ci		if (!_key || !_value || aux <= 0 || aux > 1024 * 1024)
3448c2ecf20Sopenharmony_ci			return -EINVAL;
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	case FSCONFIG_SET_PATH:
3478c2ecf20Sopenharmony_ci	case FSCONFIG_SET_PATH_EMPTY:
3488c2ecf20Sopenharmony_ci		if (!_key || !_value || (aux != AT_FDCWD && aux < 0))
3498c2ecf20Sopenharmony_ci			return -EINVAL;
3508c2ecf20Sopenharmony_ci		break;
3518c2ecf20Sopenharmony_ci	case FSCONFIG_SET_FD:
3528c2ecf20Sopenharmony_ci		if (!_key || _value || aux < 0)
3538c2ecf20Sopenharmony_ci			return -EINVAL;
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	case FSCONFIG_CMD_CREATE:
3568c2ecf20Sopenharmony_ci	case FSCONFIG_CMD_RECONFIGURE:
3578c2ecf20Sopenharmony_ci		if (_key || _value || aux)
3588c2ecf20Sopenharmony_ci			return -EINVAL;
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci	default:
3618c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	f = fdget(fd);
3658c2ecf20Sopenharmony_ci	if (!f.file)
3668c2ecf20Sopenharmony_ci		return -EBADF;
3678c2ecf20Sopenharmony_ci	ret = -EINVAL;
3688c2ecf20Sopenharmony_ci	if (f.file->f_op != &fscontext_fops)
3698c2ecf20Sopenharmony_ci		goto out_f;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	fc = f.file->private_data;
3728c2ecf20Sopenharmony_ci	if (fc->ops == &legacy_fs_context_ops) {
3738c2ecf20Sopenharmony_ci		switch (cmd) {
3748c2ecf20Sopenharmony_ci		case FSCONFIG_SET_BINARY:
3758c2ecf20Sopenharmony_ci		case FSCONFIG_SET_PATH:
3768c2ecf20Sopenharmony_ci		case FSCONFIG_SET_PATH_EMPTY:
3778c2ecf20Sopenharmony_ci		case FSCONFIG_SET_FD:
3788c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
3798c2ecf20Sopenharmony_ci			goto out_f;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (_key) {
3848c2ecf20Sopenharmony_ci		param.key = strndup_user(_key, 256);
3858c2ecf20Sopenharmony_ci		if (IS_ERR(param.key)) {
3868c2ecf20Sopenharmony_ci			ret = PTR_ERR(param.key);
3878c2ecf20Sopenharmony_ci			goto out_f;
3888c2ecf20Sopenharmony_ci		}
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	switch (cmd) {
3928c2ecf20Sopenharmony_ci	case FSCONFIG_SET_FLAG:
3938c2ecf20Sopenharmony_ci		param.type = fs_value_is_flag;
3948c2ecf20Sopenharmony_ci		break;
3958c2ecf20Sopenharmony_ci	case FSCONFIG_SET_STRING:
3968c2ecf20Sopenharmony_ci		param.type = fs_value_is_string;
3978c2ecf20Sopenharmony_ci		param.string = strndup_user(_value, 256);
3988c2ecf20Sopenharmony_ci		if (IS_ERR(param.string)) {
3998c2ecf20Sopenharmony_ci			ret = PTR_ERR(param.string);
4008c2ecf20Sopenharmony_ci			goto out_key;
4018c2ecf20Sopenharmony_ci		}
4028c2ecf20Sopenharmony_ci		param.size = strlen(param.string);
4038c2ecf20Sopenharmony_ci		break;
4048c2ecf20Sopenharmony_ci	case FSCONFIG_SET_BINARY:
4058c2ecf20Sopenharmony_ci		param.type = fs_value_is_blob;
4068c2ecf20Sopenharmony_ci		param.size = aux;
4078c2ecf20Sopenharmony_ci		param.blob = memdup_user_nul(_value, aux);
4088c2ecf20Sopenharmony_ci		if (IS_ERR(param.blob)) {
4098c2ecf20Sopenharmony_ci			ret = PTR_ERR(param.blob);
4108c2ecf20Sopenharmony_ci			goto out_key;
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci		break;
4138c2ecf20Sopenharmony_ci	case FSCONFIG_SET_PATH_EMPTY:
4148c2ecf20Sopenharmony_ci		lookup_flags = LOOKUP_EMPTY;
4158c2ecf20Sopenharmony_ci		fallthrough;
4168c2ecf20Sopenharmony_ci	case FSCONFIG_SET_PATH:
4178c2ecf20Sopenharmony_ci		param.type = fs_value_is_filename;
4188c2ecf20Sopenharmony_ci		param.name = getname_flags(_value, lookup_flags, NULL);
4198c2ecf20Sopenharmony_ci		if (IS_ERR(param.name)) {
4208c2ecf20Sopenharmony_ci			ret = PTR_ERR(param.name);
4218c2ecf20Sopenharmony_ci			goto out_key;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci		param.dirfd = aux;
4248c2ecf20Sopenharmony_ci		param.size = strlen(param.name->name);
4258c2ecf20Sopenharmony_ci		break;
4268c2ecf20Sopenharmony_ci	case FSCONFIG_SET_FD:
4278c2ecf20Sopenharmony_ci		param.type = fs_value_is_file;
4288c2ecf20Sopenharmony_ci		ret = -EBADF;
4298c2ecf20Sopenharmony_ci		param.file = fget(aux);
4308c2ecf20Sopenharmony_ci		if (!param.file)
4318c2ecf20Sopenharmony_ci			goto out_key;
4328c2ecf20Sopenharmony_ci		break;
4338c2ecf20Sopenharmony_ci	default:
4348c2ecf20Sopenharmony_ci		break;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	ret = mutex_lock_interruptible(&fc->uapi_mutex);
4388c2ecf20Sopenharmony_ci	if (ret == 0) {
4398c2ecf20Sopenharmony_ci		ret = vfs_fsconfig_locked(fc, cmd, &param);
4408c2ecf20Sopenharmony_ci		mutex_unlock(&fc->uapi_mutex);
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* Clean up the our record of any value that we obtained from
4448c2ecf20Sopenharmony_ci	 * userspace.  Note that the value may have been stolen by the LSM or
4458c2ecf20Sopenharmony_ci	 * filesystem, in which case the value pointer will have been cleared.
4468c2ecf20Sopenharmony_ci	 */
4478c2ecf20Sopenharmony_ci	switch (cmd) {
4488c2ecf20Sopenharmony_ci	case FSCONFIG_SET_STRING:
4498c2ecf20Sopenharmony_ci	case FSCONFIG_SET_BINARY:
4508c2ecf20Sopenharmony_ci		kfree(param.string);
4518c2ecf20Sopenharmony_ci		break;
4528c2ecf20Sopenharmony_ci	case FSCONFIG_SET_PATH:
4538c2ecf20Sopenharmony_ci	case FSCONFIG_SET_PATH_EMPTY:
4548c2ecf20Sopenharmony_ci		if (param.name)
4558c2ecf20Sopenharmony_ci			putname(param.name);
4568c2ecf20Sopenharmony_ci		break;
4578c2ecf20Sopenharmony_ci	case FSCONFIG_SET_FD:
4588c2ecf20Sopenharmony_ci		if (param.file)
4598c2ecf20Sopenharmony_ci			fput(param.file);
4608c2ecf20Sopenharmony_ci		break;
4618c2ecf20Sopenharmony_ci	default:
4628c2ecf20Sopenharmony_ci		break;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ciout_key:
4658c2ecf20Sopenharmony_ci	kfree(param.key);
4668c2ecf20Sopenharmony_ciout_f:
4678c2ecf20Sopenharmony_ci	fdput(f);
4688c2ecf20Sopenharmony_ci	return ret;
4698c2ecf20Sopenharmony_ci}
470