summaryrefslogtreecommitdiffstats
path: root/fs/configfs
AgeCommit message (Expand)Author
2020-06-01Merge tag 'docs-5.8' of git://git.lwn.net/linuxLinus Torvalds
2020-05-05docs: filesystems: convert configfs.txt to ReSTMauro Carvalho Chehab
2020-04-27configfs: fix config_item refcnt leak in configfs_rmdir()Xiyu Yang
2019-12-08utimes: Clamp the timestamps in notify_change()Amir Goldstein
2019-11-06configfs: calculate the depth of parent itemHonggang Li
2019-09-19Merge tag 'configfs-for-5.4' of git://git.infradead.org/users/hch/configfsLinus Torvalds
2019-09-19Merge tag 'y2038-vfs' of git://git.kernel.org/pub/scm/linux/kernel/git/arnd/p...Linus Torvalds
2019-09-11configfs: calculate the symlink target only onceAl Viro
2019-09-11configfs: make configfs_create() return inodeAl Viro
2019-09-11configfs: factor dirent removal into helpersChristoph Hellwig
2019-09-11configfs: fix a deadlock in configfs_symlink()Al Viro
2019-09-04configfs: provide exclusion between IO and removalsAl Viro
2019-09-02configfs: new object reprsenting tree fragmentsAl Viro
2019-09-02configfs_register_group() shouldn't be (and isn't) called in rmdirable partsAl Viro
2019-09-02configfs: stash the data we need into configfs_buffer at open timeAl Viro
2019-08-30timestamp_truncate: Replace users of timespec64_truncDeepa Dinamani
2019-07-19Merge branch 'work.mount0' of git://git.kernel.org/pub/scm/linux/kernel/git/v...Linus Torvalds
2019-07-10Merge tag 'fsnotify_for_v5.3-rc1' of git://git.kernel.org/pub/scm/linux/kerne...Linus Torvalds
2019-07-04vfs: Convert configfs to use the new mount APIDavid Howells
2019-06-20configfs: call fsnotify_rmdir() hookAmir Goldstein
2019-05-31Merge tag 'spdx-5.2-rc3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/g...Linus Torvalds
2019-05-30treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 145Thomas Gleixner
2019-05-28configfs: Fix use-after-free when accessing sd->s_dentrySahitya Tummala
2019-05-21treewide: Add SPDX license identifier - Makefile/KconfigThomas Gleixner
2019-05-16Merge tag 'configfs-for-5.2' of git://git.infradead.org/users/hch/configfsLinus Torvalds
2019-05-08configfs: fix possible use-after-free in configfs_register_groupYueHaibing
2019-04-08fs: mark expected switch fall-throughsGustavo A. R. Silva
2018-07-17configfs: fix registered group removalMike Christie
2018-07-02configfs: replace strncpy with memcpyGuenter Roeck
2018-06-19configfs: use kvasprintf() instead of open-coding itBart Van Assche
2018-06-05vfs: change inode times to use struct timespec64Deepa Dinamani
2017-10-19configfs: make ci_type field, some pointers and function arguments constBhumika Goyal
2017-10-19configfs: Fix bool initialization/comparisonThomas Meyer
2017-06-12configfs: Introduce config_item_get_unless_zero()Bart Van Assche
2017-06-12configfs: Fix race between create_link and configfs_rmdirNicholas Bellinger
2016-12-24Replace <asm/uaccess.h> with <linux/uaccess.h> globallyLinus Torvalds
2016-12-09vfs: remove ".readlink = generic_readlink" assignmentsMiklos Szeredi
2016-10-10Merge remote-tracking branch 'ovl/rename2' into for-linusAl Viro
2016-09-27fs: Replace current_fs_time() with current_time()Deepa Dinamani
2016-09-16configfs: Return -EFBIG from configfs_write_bin_file.Phil Turnbull
2016-07-10configfs: don't set buffer_needs_fill to zero if show() returns errorTal Shorer
2016-06-30configfs: Remove ppos increment in configfs_write_bin_fileMarek Vasut
2016-05-09configfs_readdir(): make safe under shared lockAl Viro
2016-05-02parallel lookups: actual switch to rwsemAl Viro
2016-05-02configfs_detach_prep(): make sure that wait_mutex won't go awayAl Viro
2016-04-04mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macrosKirill A. Shutemov
2016-03-19Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/vir...Linus Torvalds
2016-03-14configfs: move d_rehash() into configfs_create() for regular filesAl Viro
2016-03-06configfs: switch ->default groups to a linked listChristoph Hellwig
2016-02-26configfs: Replace CURRENT_TIME by current_fs_time()Deepa Dinamani
='n385' href='#n385'>385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
// SPDX-License-Identifier: GPL-2.0-or-later
/* Filesystem access-by-fd.
 *
 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 */

#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
#include <linux/security.h>
#include <linux/anon_inodes.h>
#include <linux/namei.h>
#include <linux/file.h>
#include <uapi/linux/mount.h>
#include "internal.h"
#include "mount.h"

/*
 * Allow the user to read back any error, warning or informational messages.
 */
static ssize_t fscontext_read(struct file *file,
			      char __user *_buf, size_t len, loff_t *pos)
{
	struct fs_context *fc = file->private_data;
	struct fc_log *log = fc->log.log;
	unsigned int logsize = ARRAY_SIZE(log->buffer);
	ssize_t ret;
	char *p;
	bool need_free;
	int index, n;

	ret = mutex_lock_interruptible(&fc->uapi_mutex);
	if (ret < 0)
		return ret;

	if (log->head == log->tail) {
		mutex_unlock(&fc->uapi_mutex);
		return -ENODATA;
	}

	index = log->tail & (logsize - 1);
	p = log->buffer[index];
	need_free = log->need_free & (1 << index);
	log->buffer[index] = NULL;
	log->need_free &= ~(1 << index);
	log->tail++;
	mutex_unlock(&fc->uapi_mutex);

	ret = -EMSGSIZE;
	n = strlen(p);
	if (n > len)
		goto err_free;
	ret = -EFAULT;
	if (copy_to_user(_buf, p, n) != 0)
		goto err_free;
	ret = n;

err_free:
	if (need_free)
		kfree(p);
	return ret;
}

static int fscontext_release(struct inode *inode, struct file *file)
{
	struct fs_context *fc = file->private_data;

	if (fc) {
		file->private_data = NULL;
		put_fs_context(fc);
	}
	return 0;
}

const struct file_operations fscontext_fops = {
	.read		= fscontext_read,
	.release	= fscontext_release,
	.llseek		= no_llseek,
};

/*
 * Attach a filesystem context to a file and an fd.
 */
static int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags)
{
	int fd;

	fd = anon_inode_getfd("[fscontext]", &fscontext_fops, fc,
			      O_RDWR | o_flags);
	if (fd < 0)
		put_fs_context(fc);
	return fd;
}

static int fscontext_alloc_log(struct fs_context *fc)
{
	fc->log.log = kzalloc(sizeof(*fc->log.log), GFP_KERNEL);
	if (!fc->log.log)
		return -ENOMEM;
	refcount_set(&fc->log.log->usage, 1);
	fc->log.log->owner = fc->fs_type->owner;
	return 0;
}

/*
 * Open a filesystem by name so that it can be configured for mounting.
 *
 * We are allowed to specify a container in which the filesystem will be
 * opened, thereby indicating which namespaces will be used (notably, which
 * network namespace will be used for network filesystems).
 */
SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
{
	struct file_system_type *fs_type;
	struct fs_context *fc;
	const char *fs_name;
	int ret;

	if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
		return -EPERM;

	if (flags & ~FSOPEN_CLOEXEC)
		return -EINVAL;

	fs_name = strndup_user(_fs_name, PAGE_SIZE);
	if (IS_ERR(fs_name))
		return PTR_ERR(fs_name);

	fs_type = get_fs_type(fs_name);
	kfree(fs_name);
	if (!fs_type)
		return -ENODEV;

	fc = fs_context_for_mount(fs_type, 0);
	put_filesystem(fs_type);
	if (IS_ERR(fc))
		return PTR_ERR(fc);

	fc->phase = FS_CONTEXT_CREATE_PARAMS;

	ret = fscontext_alloc_log(fc);
	if (ret < 0)
		goto err_fc;

	return fscontext_create_fd(fc, flags & FSOPEN_CLOEXEC ? O_CLOEXEC : 0);

err_fc:
	put_fs_context(fc);
	return ret;
}

/*
 * Pick a superblock into a context for reconfiguration.
 */
SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags)
{
	struct fs_context *fc;
	struct path target;
	unsigned int lookup_flags;
	int ret;

	if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
		return -EPERM;

	if ((flags & ~(FSPICK_CLOEXEC |
		       FSPICK_SYMLINK_NOFOLLOW |
		       FSPICK_NO_AUTOMOUNT |
		       FSPICK_EMPTY_PATH)) != 0)
		return -EINVAL;

	lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
	if (flags & FSPICK_SYMLINK_NOFOLLOW)
		lookup_flags &= ~LOOKUP_FOLLOW;
	if (flags & FSPICK_NO_AUTOMOUNT)
		lookup_flags &= ~LOOKUP_AUTOMOUNT;
	if (flags & FSPICK_EMPTY_PATH)
		lookup_flags |= LOOKUP_EMPTY;
	ret = user_path_at(dfd, path, lookup_flags, &target);
	if (ret < 0)
		goto err;

	ret = -EINVAL;
	if (target.mnt->mnt_root != target.dentry)
		goto err_path;

	fc = fs_context_for_reconfigure(target.dentry, 0, 0);
	if (IS_ERR(fc)) {
		ret = PTR_ERR(fc);
		goto err_path;
	}

	fc->phase = FS_CONTEXT_RECONF_PARAMS;

	ret = fscontext_alloc_log(fc);
	if (ret < 0)
		goto err_fc;

	path_put(&target);
	return fscontext_create_fd(fc, flags & FSPICK_CLOEXEC ? O_CLOEXEC : 0);

err_fc:
	put_fs_context(fc);
err_path:
	path_put(&target);
err:
	return ret;
}

/*
 * Check the state and apply the configuration.  Note that this function is
 * allowed to 'steal' the value by setting param->xxx to NULL before returning.
 */
static int vfs_fsconfig_locked(struct fs_context *fc, int cmd,
			       struct fs_parameter *param)
{
	struct super_block *sb;
	int ret;

	ret = finish_clean_context(fc);
	if (ret)
		return ret;
	switch (cmd) {
	case FSCONFIG_CMD_CREATE:
		if (fc->phase != FS_CONTEXT_CREATE_PARAMS)
			return -EBUSY;
		if (!mount_capable(fc))
			return -EPERM;
		fc->phase = FS_CONTEXT_CREATING;
		ret = vfs_get_tree(fc);
		if (ret)
			break;
		sb = fc->root->d_sb;
		ret = security_sb_kern_mount(sb);
		if (unlikely(ret)) {
			fc_drop_locked(fc);
			break;
		}
		up_write(&sb->s_umount);
		fc->phase = FS_CONTEXT_AWAITING_MOUNT;
		return 0;
	case FSCONFIG_CMD_RECONFIGURE:
		if (fc->phase != FS_CONTEXT_RECONF_PARAMS)
			return -EBUSY;
		fc->phase = FS_CONTEXT_RECONFIGURING;
		sb = fc->root->d_sb;
		if (!ns_capable(sb<