162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/ioctl.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/syscalls.h>
962306a36Sopenharmony_ci#include <linux/mm.h>
1062306a36Sopenharmony_ci#include <linux/capability.h>
1162306a36Sopenharmony_ci#include <linux/compat.h>
1262306a36Sopenharmony_ci#include <linux/file.h>
1362306a36Sopenharmony_ci#include <linux/fs.h>
1462306a36Sopenharmony_ci#include <linux/security.h>
1562306a36Sopenharmony_ci#include <linux/export.h>
1662306a36Sopenharmony_ci#include <linux/uaccess.h>
1762306a36Sopenharmony_ci#include <linux/writeback.h>
1862306a36Sopenharmony_ci#include <linux/buffer_head.h>
1962306a36Sopenharmony_ci#include <linux/falloc.h>
2062306a36Sopenharmony_ci#include <linux/sched/signal.h>
2162306a36Sopenharmony_ci#include <linux/fiemap.h>
2262306a36Sopenharmony_ci#include <linux/mount.h>
2362306a36Sopenharmony_ci#include <linux/fscrypt.h>
2462306a36Sopenharmony_ci#include <linux/fileattr.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "internal.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <asm/ioctls.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* So that the fiemap access checks can't overflow on 32 bit machines. */
3162306a36Sopenharmony_ci#define FIEMAP_MAX_EXTENTS	(UINT_MAX / sizeof(struct fiemap_extent))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/**
3462306a36Sopenharmony_ci * vfs_ioctl - call filesystem specific ioctl methods
3562306a36Sopenharmony_ci * @filp:	open file to invoke ioctl method on
3662306a36Sopenharmony_ci * @cmd:	ioctl command to execute
3762306a36Sopenharmony_ci * @arg:	command-specific argument for ioctl
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise
4062306a36Sopenharmony_ci * returns -ENOTTY.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Returns 0 on success, -errno on error.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cilong vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	int error = -ENOTTY;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (!filp->f_op->unlocked_ioctl)
4962306a36Sopenharmony_ci		goto out;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
5262306a36Sopenharmony_ci	if (error == -ENOIOCTLCMD)
5362306a36Sopenharmony_ci		error = -ENOTTY;
5462306a36Sopenharmony_ci out:
5562306a36Sopenharmony_ci	return error;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_ioctl);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int ioctl_fibmap(struct file *filp, int __user *p)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
6262306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
6362306a36Sopenharmony_ci	int error, ur_block;
6462306a36Sopenharmony_ci	sector_t block;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (!capable(CAP_SYS_RAWIO))
6762306a36Sopenharmony_ci		return -EPERM;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	error = get_user(ur_block, p);
7062306a36Sopenharmony_ci	if (error)
7162306a36Sopenharmony_ci		return error;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (ur_block < 0)
7462306a36Sopenharmony_ci		return -EINVAL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	block = ur_block;
7762306a36Sopenharmony_ci	error = bmap(inode, &block);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (block > INT_MAX) {
8062306a36Sopenharmony_ci		error = -ERANGE;
8162306a36Sopenharmony_ci		pr_warn_ratelimited("[%s/%d] FS: %s File: %pD4 would truncate fibmap result\n",
8262306a36Sopenharmony_ci				    current->comm, task_pid_nr(current),
8362306a36Sopenharmony_ci				    sb->s_id, filp);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (error)
8762306a36Sopenharmony_ci		ur_block = 0;
8862306a36Sopenharmony_ci	else
8962306a36Sopenharmony_ci		ur_block = block;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (put_user(ur_block, p))
9262306a36Sopenharmony_ci		error = -EFAULT;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return error;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/**
9862306a36Sopenharmony_ci * fiemap_fill_next_extent - Fiemap helper function
9962306a36Sopenharmony_ci * @fieinfo:	Fiemap context passed into ->fiemap
10062306a36Sopenharmony_ci * @logical:	Extent logical start offset, in bytes
10162306a36Sopenharmony_ci * @phys:	Extent physical start offset, in bytes
10262306a36Sopenharmony_ci * @len:	Extent length, in bytes
10362306a36Sopenharmony_ci * @flags:	FIEMAP_EXTENT flags that describe this extent
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * Called from file system ->fiemap callback. Will populate extent
10662306a36Sopenharmony_ci * info as passed in via arguments and copy to user memory. On
10762306a36Sopenharmony_ci * success, extent count on fieinfo is incremented.
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * Returns 0 on success, -errno on error, 1 if this was the last
11062306a36Sopenharmony_ci * extent that will fit in user array.
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_ciint fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
11362306a36Sopenharmony_ci			    u64 phys, u64 len, u32 flags)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct fiemap_extent extent;
11662306a36Sopenharmony_ci	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* only count the extents */
11962306a36Sopenharmony_ci	if (fieinfo->fi_extents_max == 0) {
12062306a36Sopenharmony_ci		fieinfo->fi_extents_mapped++;
12162306a36Sopenharmony_ci		return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
12562306a36Sopenharmony_ci		return 1;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define SET_UNKNOWN_FLAGS	(FIEMAP_EXTENT_DELALLOC)
12862306a36Sopenharmony_ci#define SET_NO_UNMOUNTED_IO_FLAGS	(FIEMAP_EXTENT_DATA_ENCRYPTED)
12962306a36Sopenharmony_ci#define SET_NOT_ALIGNED_FLAGS	(FIEMAP_EXTENT_DATA_TAIL|FIEMAP_EXTENT_DATA_INLINE)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (flags & SET_UNKNOWN_FLAGS)
13262306a36Sopenharmony_ci		flags |= FIEMAP_EXTENT_UNKNOWN;
13362306a36Sopenharmony_ci	if (flags & SET_NO_UNMOUNTED_IO_FLAGS)
13462306a36Sopenharmony_ci		flags |= FIEMAP_EXTENT_ENCODED;
13562306a36Sopenharmony_ci	if (flags & SET_NOT_ALIGNED_FLAGS)
13662306a36Sopenharmony_ci		flags |= FIEMAP_EXTENT_NOT_ALIGNED;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	memset(&extent, 0, sizeof(extent));
13962306a36Sopenharmony_ci	extent.fe_logical = logical;
14062306a36Sopenharmony_ci	extent.fe_physical = phys;
14162306a36Sopenharmony_ci	extent.fe_length = len;
14262306a36Sopenharmony_ci	extent.fe_flags = flags;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	dest += fieinfo->fi_extents_mapped;
14562306a36Sopenharmony_ci	if (copy_to_user(dest, &extent, sizeof(extent)))
14662306a36Sopenharmony_ci		return -EFAULT;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	fieinfo->fi_extents_mapped++;
14962306a36Sopenharmony_ci	if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
15062306a36Sopenharmony_ci		return 1;
15162306a36Sopenharmony_ci	return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL(fiemap_fill_next_extent);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * fiemap_prep - check validity of requested flags for fiemap
15762306a36Sopenharmony_ci * @inode:	Inode to operate on
15862306a36Sopenharmony_ci * @fieinfo:	Fiemap context passed into ->fiemap
15962306a36Sopenharmony_ci * @start:	Start of the mapped range
16062306a36Sopenharmony_ci * @len:	Length of the mapped range, can be truncated by this function.
16162306a36Sopenharmony_ci * @supported_flags:	Set of fiemap flags that the file system understands
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * This function must be called from each ->fiemap instance to validate the
16462306a36Sopenharmony_ci * fiemap request against the file system parameters.
16562306a36Sopenharmony_ci *
16662306a36Sopenharmony_ci * Returns 0 on success, or a negative error on failure.
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_ciint fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
16962306a36Sopenharmony_ci		u64 start, u64 *len, u32 supported_flags)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	u64 maxbytes = inode->i_sb->s_maxbytes;
17262306a36Sopenharmony_ci	u32 incompat_flags;
17362306a36Sopenharmony_ci	int ret = 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (*len == 0)
17662306a36Sopenharmony_ci		return -EINVAL;
17762306a36Sopenharmony_ci	if (start >= maxbytes)
17862306a36Sopenharmony_ci		return -EFBIG;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/*
18162306a36Sopenharmony_ci	 * Shrink request scope to what the fs can actually handle.
18262306a36Sopenharmony_ci	 */
18362306a36Sopenharmony_ci	if (*len > maxbytes || (maxbytes - *len) < start)
18462306a36Sopenharmony_ci		*len = maxbytes - start;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	supported_flags |= FIEMAP_FLAG_SYNC;
18762306a36Sopenharmony_ci	supported_flags &= FIEMAP_FLAGS_COMPAT;
18862306a36Sopenharmony_ci	incompat_flags = fieinfo->fi_flags & ~supported_flags;
18962306a36Sopenharmony_ci	if (incompat_flags) {
19062306a36Sopenharmony_ci		fieinfo->fi_flags = incompat_flags;
19162306a36Sopenharmony_ci		return -EBADR;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC)
19562306a36Sopenharmony_ci		ret = filemap_write_and_wait(inode->i_mapping);
19662306a36Sopenharmony_ci	return ret;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ciEXPORT_SYMBOL(fiemap_prep);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct fiemap fiemap;
20362306a36Sopenharmony_ci	struct fiemap_extent_info fieinfo = { 0, };
20462306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
20562306a36Sopenharmony_ci	int error;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (!inode->i_op->fiemap)
20862306a36Sopenharmony_ci		return -EOPNOTSUPP;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))
21162306a36Sopenharmony_ci		return -EFAULT;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
21462306a36Sopenharmony_ci		return -EINVAL;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	fieinfo.fi_flags = fiemap.fm_flags;
21762306a36Sopenharmony_ci	fieinfo.fi_extents_max = fiemap.fm_extent_count;
21862306a36Sopenharmony_ci	fieinfo.fi_extents_start = ufiemap->fm_extents;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start,
22162306a36Sopenharmony_ci			fiemap.fm_length);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	fiemap.fm_flags = fieinfo.fi_flags;
22462306a36Sopenharmony_ci	fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
22562306a36Sopenharmony_ci	if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
22662306a36Sopenharmony_ci		error = -EFAULT;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return error;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
23262306a36Sopenharmony_ci			     u64 off, u64 olen, u64 destoff)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct fd src_file = fdget(srcfd);
23562306a36Sopenharmony_ci	loff_t cloned;
23662306a36Sopenharmony_ci	int ret;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (!src_file.file)
23962306a36Sopenharmony_ci		return -EBADF;
24062306a36Sopenharmony_ci	cloned = vfs_clone_file_range(src_file.file, off, dst_file, destoff,
24162306a36Sopenharmony_ci				      olen, 0);
24262306a36Sopenharmony_ci	if (cloned < 0)
24362306a36Sopenharmony_ci		ret = cloned;
24462306a36Sopenharmony_ci	else if (olen && cloned != olen)
24562306a36Sopenharmony_ci		ret = -EINVAL;
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		ret = 0;
24862306a36Sopenharmony_ci	fdput(src_file);
24962306a36Sopenharmony_ci	return ret;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic long ioctl_file_clone_range(struct file *file,
25362306a36Sopenharmony_ci				   struct file_clone_range __user *argp)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct file_clone_range args;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (copy_from_user(&args, argp, sizeof(args)))
25862306a36Sopenharmony_ci		return -EFAULT;
25962306a36Sopenharmony_ci	return ioctl_file_clone(file, args.src_fd, args.src_offset,
26062306a36Sopenharmony_ci				args.src_length, args.dest_offset);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/*
26462306a36Sopenharmony_ci * This provides compatibility with legacy XFS pre-allocation ioctls
26562306a36Sopenharmony_ci * which predate the fallocate syscall.
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * Only the l_start, l_len and l_whence fields of the 'struct space_resv'
26862306a36Sopenharmony_ci * are used here, rest are ignored.
26962306a36Sopenharmony_ci */
27062306a36Sopenharmony_cistatic int ioctl_preallocate(struct file *filp, int mode, void __user *argp)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
27362306a36Sopenharmony_ci	struct space_resv sr;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (copy_from_user(&sr, argp, sizeof(sr)))
27662306a36Sopenharmony_ci		return -EFAULT;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	switch (sr.l_whence) {
27962306a36Sopenharmony_ci	case SEEK_SET:
28062306a36Sopenharmony_ci		break;
28162306a36Sopenharmony_ci	case SEEK_CUR:
28262306a36Sopenharmony_ci		sr.l_start += filp->f_pos;
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case SEEK_END:
28562306a36Sopenharmony_ci		sr.l_start += i_size_read(inode);
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	default:
28862306a36Sopenharmony_ci		return -EINVAL;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return vfs_fallocate(filp, mode | FALLOC_FL_KEEP_SIZE, sr.l_start,
29262306a36Sopenharmony_ci			sr.l_len);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/* on ia32 l_start is on a 32-bit boundary */
29662306a36Sopenharmony_ci#if defined CONFIG_COMPAT && defined(CONFIG_X86_64)
29762306a36Sopenharmony_ci/* just account for different alignment */
29862306a36Sopenharmony_cistatic int compat_ioctl_preallocate(struct file *file, int mode,
29962306a36Sopenharmony_ci				    struct space_resv_32 __user *argp)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
30262306a36Sopenharmony_ci	struct space_resv_32 sr;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (copy_from_user(&sr, argp, sizeof(sr)))
30562306a36Sopenharmony_ci		return -EFAULT;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	switch (sr.l_whence) {
30862306a36Sopenharmony_ci	case SEEK_SET:
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	case SEEK_CUR:
31162306a36Sopenharmony_ci		sr.l_start += file->f_pos;
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	case SEEK_END:
31462306a36Sopenharmony_ci		sr.l_start += i_size_read(inode);
31562306a36Sopenharmony_ci		break;
31662306a36Sopenharmony_ci	default:
31762306a36Sopenharmony_ci		return -EINVAL;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return vfs_fallocate(file, mode | FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci#endif
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic int file_ioctl(struct file *filp, unsigned int cmd, int __user *p)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	switch (cmd) {
32762306a36Sopenharmony_ci	case FIBMAP:
32862306a36Sopenharmony_ci		return ioctl_fibmap(filp, p);
32962306a36Sopenharmony_ci	case FS_IOC_RESVSP:
33062306a36Sopenharmony_ci	case FS_IOC_RESVSP64:
33162306a36Sopenharmony_ci		return ioctl_preallocate(filp, 0, p);
33262306a36Sopenharmony_ci	case FS_IOC_UNRESVSP:
33362306a36Sopenharmony_ci	case FS_IOC_UNRESVSP64:
33462306a36Sopenharmony_ci		return ioctl_preallocate(filp, FALLOC_FL_PUNCH_HOLE, p);
33562306a36Sopenharmony_ci	case FS_IOC_ZERO_RANGE:
33662306a36Sopenharmony_ci		return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p);
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return -ENOIOCTLCMD;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int ioctl_fionbio(struct file *filp, int __user *argp)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	unsigned int flag;
34562306a36Sopenharmony_ci	int on, error;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	error = get_user(on, argp);
34862306a36Sopenharmony_ci	if (error)
34962306a36Sopenharmony_ci		return error;
35062306a36Sopenharmony_ci	flag = O_NONBLOCK;
35162306a36Sopenharmony_ci#ifdef __sparc__
35262306a36Sopenharmony_ci	/* SunOS compatibility item. */
35362306a36Sopenharmony_ci	if (O_NONBLOCK != O_NDELAY)
35462306a36Sopenharmony_ci		flag |= O_NDELAY;
35562306a36Sopenharmony_ci#endif
35662306a36Sopenharmony_ci	spin_lock(&filp->f_lock);
35762306a36Sopenharmony_ci	if (on)
35862306a36Sopenharmony_ci		filp->f_flags |= flag;
35962306a36Sopenharmony_ci	else
36062306a36Sopenharmony_ci		filp->f_flags &= ~flag;
36162306a36Sopenharmony_ci	spin_unlock(&filp->f_lock);
36262306a36Sopenharmony_ci	return error;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int ioctl_fioasync(unsigned int fd, struct file *filp,
36662306a36Sopenharmony_ci			  int __user *argp)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	unsigned int flag;
36962306a36Sopenharmony_ci	int on, error;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	error = get_user(on, argp);
37262306a36Sopenharmony_ci	if (error)
37362306a36Sopenharmony_ci		return error;
37462306a36Sopenharmony_ci	flag = on ? FASYNC : 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Did FASYNC state change ? */
37762306a36Sopenharmony_ci	if ((flag ^ filp->f_flags) & FASYNC) {
37862306a36Sopenharmony_ci		if (filp->f_op->fasync)
37962306a36Sopenharmony_ci			/* fasync() adjusts filp->f_flags */
38062306a36Sopenharmony_ci			error = filp->f_op->fasync(fd, filp, on);
38162306a36Sopenharmony_ci		else
38262306a36Sopenharmony_ci			error = -ENOTTY;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci	return error < 0 ? error : 0;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int ioctl_fsfreeze(struct file *filp)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct super_block *sb = file_inode(filp)->i_sb;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
39262306a36Sopenharmony_ci		return -EPERM;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* If filesystem doesn't support freeze feature, return. */
39562306a36Sopenharmony_ci	if (sb->s_op->freeze_fs == NULL && sb->s_op->freeze_super == NULL)
39662306a36Sopenharmony_ci		return -EOPNOTSUPP;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Freeze */
39962306a36Sopenharmony_ci	if (sb->s_op->freeze_super)
40062306a36Sopenharmony_ci		return sb->s_op->freeze_super(sb, FREEZE_HOLDER_USERSPACE);
40162306a36Sopenharmony_ci	return freeze_super(sb, FREEZE_HOLDER_USERSPACE);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int ioctl_fsthaw(struct file *filp)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct super_block *sb = file_inode(filp)->i_sb;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
40962306a36Sopenharmony_ci		return -EPERM;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* Thaw */
41262306a36Sopenharmony_ci	if (sb->s_op->thaw_super)
41362306a36Sopenharmony_ci		return sb->s_op->thaw_super(sb, FREEZE_HOLDER_USERSPACE);
41462306a36Sopenharmony_ci	return thaw_super(sb, FREEZE_HOLDER_USERSPACE);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int ioctl_file_dedupe_range(struct file *file,
41862306a36Sopenharmony_ci				   struct file_dedupe_range __user *argp)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct file_dedupe_range *same = NULL;
42162306a36Sopenharmony_ci	int ret;
42262306a36Sopenharmony_ci	unsigned long size;
42362306a36Sopenharmony_ci	u16 count;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (get_user(count, &argp->dest_count)) {
42662306a36Sopenharmony_ci		ret = -EFAULT;
42762306a36Sopenharmony_ci		goto out;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	size = offsetof(struct file_dedupe_range, info[count]);
43162306a36Sopenharmony_ci	if (size > PAGE_SIZE) {
43262306a36Sopenharmony_ci		ret = -ENOMEM;
43362306a36Sopenharmony_ci		goto out;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	same = memdup_user(argp, size);
43762306a36Sopenharmony_ci	if (IS_ERR(same)) {
43862306a36Sopenharmony_ci		ret = PTR_ERR(same);
43962306a36Sopenharmony_ci		same = NULL;
44062306a36Sopenharmony_ci		goto out;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	same->dest_count = count;
44462306a36Sopenharmony_ci	ret = vfs_dedupe_file_range(file, same);
44562306a36Sopenharmony_ci	if (ret)
44662306a36Sopenharmony_ci		goto out;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	ret = copy_to_user(argp, same, size);
44962306a36Sopenharmony_ci	if (ret)
45062306a36Sopenharmony_ci		ret = -EFAULT;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ciout:
45362306a36Sopenharmony_ci	kfree(same);
45462306a36Sopenharmony_ci	return ret;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/**
45862306a36Sopenharmony_ci * fileattr_fill_xflags - initialize fileattr with xflags
45962306a36Sopenharmony_ci * @fa:		fileattr pointer
46062306a36Sopenharmony_ci * @xflags:	FS_XFLAG_* flags
46162306a36Sopenharmony_ci *
46262306a36Sopenharmony_ci * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags).  All
46362306a36Sopenharmony_ci * other fields are zeroed.
46462306a36Sopenharmony_ci */
46562306a36Sopenharmony_civoid fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	memset(fa, 0, sizeof(*fa));
46862306a36Sopenharmony_ci	fa->fsx_valid = true;
46962306a36Sopenharmony_ci	fa->fsx_xflags = xflags;
47062306a36Sopenharmony_ci	if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
47162306a36Sopenharmony_ci		fa->flags |= FS_IMMUTABLE_FL;
47262306a36Sopenharmony_ci	if (fa->fsx_xflags & FS_XFLAG_APPEND)
47362306a36Sopenharmony_ci		fa->flags |= FS_APPEND_FL;
47462306a36Sopenharmony_ci	if (fa->fsx_xflags & FS_XFLAG_SYNC)
47562306a36Sopenharmony_ci		fa->flags |= FS_SYNC_FL;
47662306a36Sopenharmony_ci	if (fa->fsx_xflags & FS_XFLAG_NOATIME)
47762306a36Sopenharmony_ci		fa->flags |= FS_NOATIME_FL;
47862306a36Sopenharmony_ci	if (fa->fsx_xflags & FS_XFLAG_NODUMP)
47962306a36Sopenharmony_ci		fa->flags |= FS_NODUMP_FL;
48062306a36Sopenharmony_ci	if (fa->fsx_xflags & FS_XFLAG_DAX)
48162306a36Sopenharmony_ci		fa->flags |= FS_DAX_FL;
48262306a36Sopenharmony_ci	if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
48362306a36Sopenharmony_ci		fa->flags |= FS_PROJINHERIT_FL;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ciEXPORT_SYMBOL(fileattr_fill_xflags);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/**
48862306a36Sopenharmony_ci * fileattr_fill_flags - initialize fileattr with flags
48962306a36Sopenharmony_ci * @fa:		fileattr pointer
49062306a36Sopenharmony_ci * @flags:	FS_*_FL flags
49162306a36Sopenharmony_ci *
49262306a36Sopenharmony_ci * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
49362306a36Sopenharmony_ci * All other fields are zeroed.
49462306a36Sopenharmony_ci */
49562306a36Sopenharmony_civoid fileattr_fill_flags(struct fileattr *fa, u32 flags)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	memset(fa, 0, sizeof(*fa));
49862306a36Sopenharmony_ci	fa->flags_valid = true;
49962306a36Sopenharmony_ci	fa->flags = flags;
50062306a36Sopenharmony_ci	if (fa->flags & FS_SYNC_FL)
50162306a36Sopenharmony_ci		fa->fsx_xflags |= FS_XFLAG_SYNC;
50262306a36Sopenharmony_ci	if (fa->flags & FS_IMMUTABLE_FL)
50362306a36Sopenharmony_ci		fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
50462306a36Sopenharmony_ci	if (fa->flags & FS_APPEND_FL)
50562306a36Sopenharmony_ci		fa->fsx_xflags |= FS_XFLAG_APPEND;
50662306a36Sopenharmony_ci	if (fa->flags & FS_NODUMP_FL)
50762306a36Sopenharmony_ci		fa->fsx_xflags |= FS_XFLAG_NODUMP;
50862306a36Sopenharmony_ci	if (fa->flags & FS_NOATIME_FL)
50962306a36Sopenharmony_ci		fa->fsx_xflags |= FS_XFLAG_NOATIME;
51062306a36Sopenharmony_ci	if (fa->flags & FS_DAX_FL)
51162306a36Sopenharmony_ci		fa->fsx_xflags |= FS_XFLAG_DAX;
51262306a36Sopenharmony_ci	if (fa->flags & FS_PROJINHERIT_FL)
51362306a36Sopenharmony_ci		fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ciEXPORT_SYMBOL(fileattr_fill_flags);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci/**
51862306a36Sopenharmony_ci * vfs_fileattr_get - retrieve miscellaneous file attributes
51962306a36Sopenharmony_ci * @dentry:	the object to retrieve from
52062306a36Sopenharmony_ci * @fa:		fileattr pointer
52162306a36Sopenharmony_ci *
52262306a36Sopenharmony_ci * Call i_op->fileattr_get() callback, if exists.
52362306a36Sopenharmony_ci *
52462306a36Sopenharmony_ci * Return: 0 on success, or a negative error on failure.
52562306a36Sopenharmony_ci */
52662306a36Sopenharmony_ciint vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (!inode->i_op->fileattr_get)
53162306a36Sopenharmony_ci		return -ENOIOCTLCMD;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	return inode->i_op->fileattr_get(dentry, fa);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_fileattr_get);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci/**
53862306a36Sopenharmony_ci * copy_fsxattr_to_user - copy fsxattr to userspace.
53962306a36Sopenharmony_ci * @fa:		fileattr pointer
54062306a36Sopenharmony_ci * @ufa:	fsxattr user pointer
54162306a36Sopenharmony_ci *
54262306a36Sopenharmony_ci * Return: 0 on success, or -EFAULT on failure.
54362306a36Sopenharmony_ci */
54462306a36Sopenharmony_ciint copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct fsxattr xfa;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	memset(&xfa, 0, sizeof(xfa));
54962306a36Sopenharmony_ci	xfa.fsx_xflags = fa->fsx_xflags;
55062306a36Sopenharmony_ci	xfa.fsx_extsize = fa->fsx_extsize;
55162306a36Sopenharmony_ci	xfa.fsx_nextents = fa->fsx_nextents;
55262306a36Sopenharmony_ci	xfa.fsx_projid = fa->fsx_projid;
55362306a36Sopenharmony_ci	xfa.fsx_cowextsize = fa->fsx_cowextsize;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (copy_to_user(ufa, &xfa, sizeof(xfa)))
55662306a36Sopenharmony_ci		return -EFAULT;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return 0;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ciEXPORT_SYMBOL(copy_fsxattr_to_user);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int copy_fsxattr_from_user(struct fileattr *fa,
56362306a36Sopenharmony_ci				  struct fsxattr __user *ufa)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct fsxattr xfa;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (copy_from_user(&xfa, ufa, sizeof(xfa)))
56862306a36Sopenharmony_ci		return -EFAULT;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	fileattr_fill_xflags(fa, xfa.fsx_xflags);
57162306a36Sopenharmony_ci	fa->fsx_extsize = xfa.fsx_extsize;
57262306a36Sopenharmony_ci	fa->fsx_nextents = xfa.fsx_nextents;
57362306a36Sopenharmony_ci	fa->fsx_projid = xfa.fsx_projid;
57462306a36Sopenharmony_ci	fa->fsx_cowextsize = xfa.fsx_cowextsize;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return 0;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/*
58062306a36Sopenharmony_ci * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
58162306a36Sopenharmony_ci * any invalid configurations.
58262306a36Sopenharmony_ci *
58362306a36Sopenharmony_ci * Note: must be called with inode lock held.
58462306a36Sopenharmony_ci */
58562306a36Sopenharmony_cistatic int fileattr_set_prepare(struct inode *inode,
58662306a36Sopenharmony_ci			      const struct fileattr *old_ma,
58762306a36Sopenharmony_ci			      struct fileattr *fa)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	int err;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/*
59262306a36Sopenharmony_ci	 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
59362306a36Sopenharmony_ci	 * the relevant capability.
59462306a36Sopenharmony_ci	 */
59562306a36Sopenharmony_ci	if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
59662306a36Sopenharmony_ci	    !capable(CAP_LINUX_IMMUTABLE))
59762306a36Sopenharmony_ci		return -EPERM;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
60062306a36Sopenharmony_ci	if (err)
60162306a36Sopenharmony_ci		return err;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/*
60462306a36Sopenharmony_ci	 * Project Quota ID state is only allowed to change from within the init
60562306a36Sopenharmony_ci	 * namespace. Enforce that restriction only if we are trying to change
60662306a36Sopenharmony_ci	 * the quota ID state. Everything else is allowed in user namespaces.
60762306a36Sopenharmony_ci	 */
60862306a36Sopenharmony_ci	if (current_user_ns() != &init_user_ns) {
60962306a36Sopenharmony_ci		if (old_ma->fsx_projid != fa->fsx_projid)
61062306a36Sopenharmony_ci			return -EINVAL;
61162306a36Sopenharmony_ci		if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
61262306a36Sopenharmony_ci				FS_XFLAG_PROJINHERIT)
61362306a36Sopenharmony_ci			return -EINVAL;
61462306a36Sopenharmony_ci	} else {
61562306a36Sopenharmony_ci		/*
61662306a36Sopenharmony_ci		 * Caller is allowed to change the project ID. If it is being
61762306a36Sopenharmony_ci		 * changed, make sure that the new value is valid.
61862306a36Sopenharmony_ci		 */
61962306a36Sopenharmony_ci		if (old_ma->fsx_projid != fa->fsx_projid &&
62062306a36Sopenharmony_ci		    !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
62162306a36Sopenharmony_ci			return -EINVAL;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* Check extent size hints. */
62562306a36Sopenharmony_ci	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
62662306a36Sopenharmony_ci		return -EINVAL;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
62962306a36Sopenharmony_ci			!S_ISDIR(inode->i_mode))
63062306a36Sopenharmony_ci		return -EINVAL;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
63362306a36Sopenharmony_ci	    !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
63462306a36Sopenharmony_ci		return -EINVAL;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/*
63762306a36Sopenharmony_ci	 * It is only valid to set the DAX flag on regular files and
63862306a36Sopenharmony_ci	 * directories on filesystems.
63962306a36Sopenharmony_ci	 */
64062306a36Sopenharmony_ci	if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
64162306a36Sopenharmony_ci	    !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
64262306a36Sopenharmony_ci		return -EINVAL;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	/* Extent size hints of zero turn off the flags. */
64562306a36Sopenharmony_ci	if (fa->fsx_extsize == 0)
64662306a36Sopenharmony_ci		fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
64762306a36Sopenharmony_ci	if (fa->fsx_cowextsize == 0)
64862306a36Sopenharmony_ci		fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	return 0;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci/**
65462306a36Sopenharmony_ci * vfs_fileattr_set - change miscellaneous file attributes
65562306a36Sopenharmony_ci * @idmap:	idmap of the mount
65662306a36Sopenharmony_ci * @dentry:	the object to change
65762306a36Sopenharmony_ci * @fa:		fileattr pointer
65862306a36Sopenharmony_ci *
65962306a36Sopenharmony_ci * After verifying permissions, call i_op->fileattr_set() callback, if
66062306a36Sopenharmony_ci * exists.
66162306a36Sopenharmony_ci *
66262306a36Sopenharmony_ci * Verifying attributes involves retrieving current attributes with
66362306a36Sopenharmony_ci * i_op->fileattr_get(), this also allows initializing attributes that have
66462306a36Sopenharmony_ci * not been set by the caller to current values.  Inode lock is held
66562306a36Sopenharmony_ci * thoughout to prevent racing with another instance.
66662306a36Sopenharmony_ci *
66762306a36Sopenharmony_ci * Return: 0 on success, or a negative error on failure.
66862306a36Sopenharmony_ci */
66962306a36Sopenharmony_ciint vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
67062306a36Sopenharmony_ci		     struct fileattr *fa)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
67362306a36Sopenharmony_ci	struct fileattr old_ma = {};
67462306a36Sopenharmony_ci	int err;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (!inode->i_op->fileattr_set)
67762306a36Sopenharmony_ci		return -ENOIOCTLCMD;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (!inode_owner_or_capable(idmap, inode))
68062306a36Sopenharmony_ci		return -EPERM;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	inode_lock(inode);
68362306a36Sopenharmony_ci	err = vfs_fileattr_get(dentry, &old_ma);
68462306a36Sopenharmony_ci	if (!err) {
68562306a36Sopenharmony_ci		/* initialize missing bits from old_ma */
68662306a36Sopenharmony_ci		if (fa->flags_valid) {
68762306a36Sopenharmony_ci			fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
68862306a36Sopenharmony_ci			fa->fsx_extsize = old_ma.fsx_extsize;
68962306a36Sopenharmony_ci			fa->fsx_nextents = old_ma.fsx_nextents;
69062306a36Sopenharmony_ci			fa->fsx_projid = old_ma.fsx_projid;
69162306a36Sopenharmony_ci			fa->fsx_cowextsize = old_ma.fsx_cowextsize;
69262306a36Sopenharmony_ci		} else {
69362306a36Sopenharmony_ci			fa->flags |= old_ma.flags & ~FS_COMMON_FL;
69462306a36Sopenharmony_ci		}
69562306a36Sopenharmony_ci		err = fileattr_set_prepare(inode, &old_ma, fa);
69662306a36Sopenharmony_ci		if (!err)
69762306a36Sopenharmony_ci			err = inode->i_op->fileattr_set(idmap, dentry, fa);
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci	inode_unlock(inode);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return err;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_fileattr_set);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int ioctl_getflags(struct file *file, unsigned int __user *argp)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	struct fileattr fa = { .flags_valid = true }; /* hint only */
70862306a36Sopenharmony_ci	int err;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	err = vfs_fileattr_get(file->f_path.dentry, &fa);
71162306a36Sopenharmony_ci	if (!err)
71262306a36Sopenharmony_ci		err = put_user(fa.flags, argp);
71362306a36Sopenharmony_ci	return err;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic int ioctl_setflags(struct file *file, unsigned int __user *argp)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct mnt_idmap *idmap = file_mnt_idmap(file);
71962306a36Sopenharmony_ci	struct dentry *dentry = file->f_path.dentry;
72062306a36Sopenharmony_ci	struct fileattr fa;
72162306a36Sopenharmony_ci	unsigned int flags;
72262306a36Sopenharmony_ci	int err;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	err = get_user(flags, argp);
72562306a36Sopenharmony_ci	if (!err) {
72662306a36Sopenharmony_ci		err = mnt_want_write_file(file);
72762306a36Sopenharmony_ci		if (!err) {
72862306a36Sopenharmony_ci			fileattr_fill_flags(&fa, flags);
72962306a36Sopenharmony_ci			err = vfs_fileattr_set(idmap, dentry, &fa);
73062306a36Sopenharmony_ci			mnt_drop_write_file(file);
73162306a36Sopenharmony_ci		}
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci	return err;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int ioctl_fsgetxattr(struct file *file, void __user *argp)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct fileattr fa = { .fsx_valid = true }; /* hint only */
73962306a36Sopenharmony_ci	int err;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	err = vfs_fileattr_get(file->f_path.dentry, &fa);
74262306a36Sopenharmony_ci	if (!err)
74362306a36Sopenharmony_ci		err = copy_fsxattr_to_user(&fa, argp);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	return err;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int ioctl_fssetxattr(struct file *file, void __user *argp)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct mnt_idmap *idmap = file_mnt_idmap(file);
75162306a36Sopenharmony_ci	struct dentry *dentry = file->f_path.dentry;
75262306a36Sopenharmony_ci	struct fileattr fa;
75362306a36Sopenharmony_ci	int err;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	err = copy_fsxattr_from_user(&fa, argp);
75662306a36Sopenharmony_ci	if (!err) {
75762306a36Sopenharmony_ci		err = mnt_want_write_file(file);
75862306a36Sopenharmony_ci		if (!err) {
75962306a36Sopenharmony_ci			err = vfs_fileattr_set(idmap, dentry, &fa);
76062306a36Sopenharmony_ci			mnt_drop_write_file(file);
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci	return err;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci/*
76762306a36Sopenharmony_ci * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d.
76862306a36Sopenharmony_ci * It's just a simple helper for sys_ioctl and compat_sys_ioctl.
76962306a36Sopenharmony_ci *
77062306a36Sopenharmony_ci * When you add any new common ioctls to the switches above and below,
77162306a36Sopenharmony_ci * please ensure they have compatible arguments in compat mode.
77262306a36Sopenharmony_ci */
77362306a36Sopenharmony_cistatic int do_vfs_ioctl(struct file *filp, unsigned int fd,
77462306a36Sopenharmony_ci			unsigned int cmd, unsigned long arg)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
77762306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	switch (cmd) {
78062306a36Sopenharmony_ci	case FIOCLEX:
78162306a36Sopenharmony_ci		set_close_on_exec(fd, 1);
78262306a36Sopenharmony_ci		return 0;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	case FIONCLEX:
78562306a36Sopenharmony_ci		set_close_on_exec(fd, 0);
78662306a36Sopenharmony_ci		return 0;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	case FIONBIO:
78962306a36Sopenharmony_ci		return ioctl_fionbio(filp, argp);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	case FIOASYNC:
79262306a36Sopenharmony_ci		return ioctl_fioasync(fd, filp, argp);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	case FIOQSIZE:
79562306a36Sopenharmony_ci		if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) ||
79662306a36Sopenharmony_ci		    S_ISLNK(inode->i_mode)) {
79762306a36Sopenharmony_ci			loff_t res = inode_get_bytes(inode);
79862306a36Sopenharmony_ci			return copy_to_user(argp, &res, sizeof(res)) ?
79962306a36Sopenharmony_ci					    -EFAULT : 0;
80062306a36Sopenharmony_ci		}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci		return -ENOTTY;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	case FIFREEZE:
80562306a36Sopenharmony_ci		return ioctl_fsfreeze(filp);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	case FITHAW:
80862306a36Sopenharmony_ci		return ioctl_fsthaw(filp);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	case FS_IOC_FIEMAP:
81162306a36Sopenharmony_ci		return ioctl_fiemap(filp, argp);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	case FIGETBSZ:
81462306a36Sopenharmony_ci		/* anon_bdev filesystems may not have a block size */
81562306a36Sopenharmony_ci		if (!inode->i_sb->s_blocksize)
81662306a36Sopenharmony_ci			return -EINVAL;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		return put_user(inode->i_sb->s_blocksize, (int __user *)argp);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	case FICLONE:
82162306a36Sopenharmony_ci		return ioctl_file_clone(filp, arg, 0, 0, 0);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	case FICLONERANGE:
82462306a36Sopenharmony_ci		return ioctl_file_clone_range(filp, argp);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	case FIDEDUPERANGE:
82762306a36Sopenharmony_ci		return ioctl_file_dedupe_range(filp, argp);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	case FIONREAD:
83062306a36Sopenharmony_ci		if (!S_ISREG(inode->i_mode))
83162306a36Sopenharmony_ci			return vfs_ioctl(filp, cmd, arg);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		return put_user(i_size_read(inode) - filp->f_pos,
83462306a36Sopenharmony_ci				(int __user *)argp);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	case FS_IOC_GETFLAGS:
83762306a36Sopenharmony_ci		return ioctl_getflags(filp, argp);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	case FS_IOC_SETFLAGS:
84062306a36Sopenharmony_ci		return ioctl_setflags(filp, argp);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	case FS_IOC_FSGETXATTR:
84362306a36Sopenharmony_ci		return ioctl_fsgetxattr(filp, argp);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	case FS_IOC_FSSETXATTR:
84662306a36Sopenharmony_ci		return ioctl_fssetxattr(filp, argp);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	default:
84962306a36Sopenharmony_ci		if (S_ISREG(inode->i_mode))
85062306a36Sopenharmony_ci			return file_ioctl(filp, cmd, argp);
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	return -ENOIOCTLCMD;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ciSYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct fd f = fdget(fd);
86062306a36Sopenharmony_ci	int error;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	if (!f.file)
86362306a36Sopenharmony_ci		return -EBADF;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	error = security_file_ioctl(f.file, cmd, arg);
86662306a36Sopenharmony_ci	if (error)
86762306a36Sopenharmony_ci		goto out;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	error = do_vfs_ioctl(f.file, fd, cmd, arg);
87062306a36Sopenharmony_ci	if (error == -ENOIOCTLCMD)
87162306a36Sopenharmony_ci		error = vfs_ioctl(f.file, cmd, arg);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ciout:
87462306a36Sopenharmony_ci	fdput(f);
87562306a36Sopenharmony_ci	return error;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
87962306a36Sopenharmony_ci/**
88062306a36Sopenharmony_ci * compat_ptr_ioctl - generic implementation of .compat_ioctl file operation
88162306a36Sopenharmony_ci * @file: The file to operate on.
88262306a36Sopenharmony_ci * @cmd: The ioctl command number.
88362306a36Sopenharmony_ci * @arg: The argument to the ioctl.
88462306a36Sopenharmony_ci *
88562306a36Sopenharmony_ci * This is not normally called as a function, but instead set in struct
88662306a36Sopenharmony_ci * file_operations as
88762306a36Sopenharmony_ci *
88862306a36Sopenharmony_ci *     .compat_ioctl = compat_ptr_ioctl,
88962306a36Sopenharmony_ci *
89062306a36Sopenharmony_ci * On most architectures, the compat_ptr_ioctl() just passes all arguments
89162306a36Sopenharmony_ci * to the corresponding ->ioctl handler. The exception is arch/s390, where
89262306a36Sopenharmony_ci * compat_ptr() clears the top bit of a 32-bit pointer value, so user space
89362306a36Sopenharmony_ci * pointers to the second 2GB alias the first 2GB, as is the case for
89462306a36Sopenharmony_ci * native 32-bit s390 user space.
89562306a36Sopenharmony_ci *
89662306a36Sopenharmony_ci * The compat_ptr_ioctl() function must therefore be used only with ioctl
89762306a36Sopenharmony_ci * functions that either ignore the argument or pass a pointer to a
89862306a36Sopenharmony_ci * compatible data type.
89962306a36Sopenharmony_ci *
90062306a36Sopenharmony_ci * If any ioctl command handled by fops->unlocked_ioctl passes a plain
90162306a36Sopenharmony_ci * integer instead of a pointer, or any of the passed data types
90262306a36Sopenharmony_ci * is incompatible between 32-bit and 64-bit architectures, a proper
90362306a36Sopenharmony_ci * handler is required instead of compat_ptr_ioctl.
90462306a36Sopenharmony_ci */
90562306a36Sopenharmony_cilong compat_ptr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	if (!file->f_op->unlocked_ioctl)
90862306a36Sopenharmony_ci		return -ENOIOCTLCMD;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ciEXPORT_SYMBOL(compat_ptr_ioctl);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
91562306a36Sopenharmony_ci		       compat_ulong_t, arg)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	struct fd f = fdget(fd);
91862306a36Sopenharmony_ci	int error;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (!f.file)
92162306a36Sopenharmony_ci		return -EBADF;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	error = security_file_ioctl_compat(f.file, cmd, arg);
92462306a36Sopenharmony_ci	if (error)
92562306a36Sopenharmony_ci		goto out;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	switch (cmd) {
92862306a36Sopenharmony_ci	/* FICLONE takes an int argument, so don't use compat_ptr() */
92962306a36Sopenharmony_ci	case FICLONE:
93062306a36Sopenharmony_ci		error = ioctl_file_clone(f.file, arg, 0, 0, 0);
93162306a36Sopenharmony_ci		break;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci#if defined(CONFIG_X86_64)
93462306a36Sopenharmony_ci	/* these get messy on amd64 due to alignment differences */
93562306a36Sopenharmony_ci	case FS_IOC_RESVSP_32:
93662306a36Sopenharmony_ci	case FS_IOC_RESVSP64_32:
93762306a36Sopenharmony_ci		error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg));
93862306a36Sopenharmony_ci		break;
93962306a36Sopenharmony_ci	case FS_IOC_UNRESVSP_32:
94062306a36Sopenharmony_ci	case FS_IOC_UNRESVSP64_32:
94162306a36Sopenharmony_ci		error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE,
94262306a36Sopenharmony_ci				compat_ptr(arg));
94362306a36Sopenharmony_ci		break;
94462306a36Sopenharmony_ci	case FS_IOC_ZERO_RANGE_32:
94562306a36Sopenharmony_ci		error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE,
94662306a36Sopenharmony_ci				compat_ptr(arg));
94762306a36Sopenharmony_ci		break;
94862306a36Sopenharmony_ci#endif
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/*
95162306a36Sopenharmony_ci	 * These access 32-bit values anyway so no further handling is
95262306a36Sopenharmony_ci	 * necessary.
95362306a36Sopenharmony_ci	 */
95462306a36Sopenharmony_ci	case FS_IOC32_GETFLAGS:
95562306a36Sopenharmony_ci	case FS_IOC32_SETFLAGS:
95662306a36Sopenharmony_ci		cmd = (cmd == FS_IOC32_GETFLAGS) ?
95762306a36Sopenharmony_ci			FS_IOC_GETFLAGS : FS_IOC_SETFLAGS;
95862306a36Sopenharmony_ci		fallthrough;
95962306a36Sopenharmony_ci	/*
96062306a36Sopenharmony_ci	 * everything else in do_vfs_ioctl() takes either a compatible
96162306a36Sopenharmony_ci	 * pointer argument or no argument -- call it with a modified
96262306a36Sopenharmony_ci	 * argument.
96362306a36Sopenharmony_ci	 */
96462306a36Sopenharmony_ci	default:
96562306a36Sopenharmony_ci		error = do_vfs_ioctl(f.file, fd, cmd,
96662306a36Sopenharmony_ci				     (unsigned long)compat_ptr(arg));
96762306a36Sopenharmony_ci		if (error != -ENOIOCTLCMD)
96862306a36Sopenharmony_ci			break;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		if (f.file->f_op->compat_ioctl)
97162306a36Sopenharmony_ci			error = f.file->f_op->compat_ioctl(f.file, cmd, arg);
97262306a36Sopenharmony_ci		if (error == -ENOIOCTLCMD)
97362306a36Sopenharmony_ci			error = -ENOTTY;
97462306a36Sopenharmony_ci		break;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci out:
97862306a36Sopenharmony_ci	fdput(f);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	return error;
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci#endif
983