18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/ioctl.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
98c2ecf20Sopenharmony_ci#include <linux/mm.h>
108c2ecf20Sopenharmony_ci#include <linux/capability.h>
118c2ecf20Sopenharmony_ci#include <linux/compat.h>
128c2ecf20Sopenharmony_ci#include <linux/file.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/security.h>
158c2ecf20Sopenharmony_ci#include <linux/export.h>
168c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
178c2ecf20Sopenharmony_ci#include <linux/writeback.h>
188c2ecf20Sopenharmony_ci#include <linux/buffer_head.h>
198c2ecf20Sopenharmony_ci#include <linux/falloc.h>
208c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
218c2ecf20Sopenharmony_ci#include <linux/fiemap.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "internal.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/ioctls.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* So that the fiemap access checks can't overflow on 32 bit machines. */
288c2ecf20Sopenharmony_ci#define FIEMAP_MAX_EXTENTS	(UINT_MAX / sizeof(struct fiemap_extent))
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/**
318c2ecf20Sopenharmony_ci * vfs_ioctl - call filesystem specific ioctl methods
328c2ecf20Sopenharmony_ci * @filp:	open file to invoke ioctl method on
338c2ecf20Sopenharmony_ci * @cmd:	ioctl command to execute
348c2ecf20Sopenharmony_ci * @arg:	command-specific argument for ioctl
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise
378c2ecf20Sopenharmony_ci * returns -ENOTTY.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * Returns 0 on success, -errno on error.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cilong vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	int error = -ENOTTY;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (!filp->f_op->unlocked_ioctl)
468c2ecf20Sopenharmony_ci		goto out;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
498c2ecf20Sopenharmony_ci	if (error == -ENOIOCTLCMD)
508c2ecf20Sopenharmony_ci		error = -ENOTTY;
518c2ecf20Sopenharmony_ci out:
528c2ecf20Sopenharmony_ci	return error;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vfs_ioctl);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int ioctl_fibmap(struct file *filp, int __user *p)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
598c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
608c2ecf20Sopenharmony_ci	int error, ur_block;
618c2ecf20Sopenharmony_ci	sector_t block;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_RAWIO))
648c2ecf20Sopenharmony_ci		return -EPERM;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	error = get_user(ur_block, p);
678c2ecf20Sopenharmony_ci	if (error)
688c2ecf20Sopenharmony_ci		return error;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (ur_block < 0)
718c2ecf20Sopenharmony_ci		return -EINVAL;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	block = ur_block;
748c2ecf20Sopenharmony_ci	error = bmap(inode, &block);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (block > INT_MAX) {
778c2ecf20Sopenharmony_ci		error = -ERANGE;
788c2ecf20Sopenharmony_ci		pr_warn_ratelimited("[%s/%d] FS: %s File: %pD4 would truncate fibmap result\n",
798c2ecf20Sopenharmony_ci				    current->comm, task_pid_nr(current),
808c2ecf20Sopenharmony_ci				    sb->s_id, filp);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (error)
848c2ecf20Sopenharmony_ci		ur_block = 0;
858c2ecf20Sopenharmony_ci	else
868c2ecf20Sopenharmony_ci		ur_block = block;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (put_user(ur_block, p))
898c2ecf20Sopenharmony_ci		error = -EFAULT;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return error;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/**
958c2ecf20Sopenharmony_ci * fiemap_fill_next_extent - Fiemap helper function
968c2ecf20Sopenharmony_ci * @fieinfo:	Fiemap context passed into ->fiemap
978c2ecf20Sopenharmony_ci * @logical:	Extent logical start offset, in bytes
988c2ecf20Sopenharmony_ci * @phys:	Extent physical start offset, in bytes
998c2ecf20Sopenharmony_ci * @len:	Extent length, in bytes
1008c2ecf20Sopenharmony_ci * @flags:	FIEMAP_EXTENT flags that describe this extent
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * Called from file system ->fiemap callback. Will populate extent
1038c2ecf20Sopenharmony_ci * info as passed in via arguments and copy to user memory. On
1048c2ecf20Sopenharmony_ci * success, extent count on fieinfo is incremented.
1058c2ecf20Sopenharmony_ci *
1068c2ecf20Sopenharmony_ci * Returns 0 on success, -errno on error, 1 if this was the last
1078c2ecf20Sopenharmony_ci * extent that will fit in user array.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_ci#define SET_UNKNOWN_FLAGS	(FIEMAP_EXTENT_DELALLOC)
1108c2ecf20Sopenharmony_ci#define SET_NO_UNMOUNTED_IO_FLAGS	(FIEMAP_EXTENT_DATA_ENCRYPTED)
1118c2ecf20Sopenharmony_ci#define SET_NOT_ALIGNED_FLAGS	(FIEMAP_EXTENT_DATA_TAIL|FIEMAP_EXTENT_DATA_INLINE)
1128c2ecf20Sopenharmony_ciint fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
1138c2ecf20Sopenharmony_ci			    u64 phys, u64 len, u32 flags)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct fiemap_extent extent;
1168c2ecf20Sopenharmony_ci	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* only count the extents */
1198c2ecf20Sopenharmony_ci	if (fieinfo->fi_extents_max == 0) {
1208c2ecf20Sopenharmony_ci		fieinfo->fi_extents_mapped++;
1218c2ecf20Sopenharmony_ci		return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
1258c2ecf20Sopenharmony_ci		return 1;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (flags & SET_UNKNOWN_FLAGS)
1288c2ecf20Sopenharmony_ci		flags |= FIEMAP_EXTENT_UNKNOWN;
1298c2ecf20Sopenharmony_ci	if (flags & SET_NO_UNMOUNTED_IO_FLAGS)
1308c2ecf20Sopenharmony_ci		flags |= FIEMAP_EXTENT_ENCODED;
1318c2ecf20Sopenharmony_ci	if (flags & SET_NOT_ALIGNED_FLAGS)
1328c2ecf20Sopenharmony_ci		flags |= FIEMAP_EXTENT_NOT_ALIGNED;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	memset(&extent, 0, sizeof(extent));
1358c2ecf20Sopenharmony_ci	extent.fe_logical = logical;
1368c2ecf20Sopenharmony_ci	extent.fe_physical = phys;
1378c2ecf20Sopenharmony_ci	extent.fe_length = len;
1388c2ecf20Sopenharmony_ci	extent.fe_flags = flags;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	dest += fieinfo->fi_extents_mapped;
1418c2ecf20Sopenharmony_ci	if (copy_to_user(dest, &extent, sizeof(extent)))
1428c2ecf20Sopenharmony_ci		return -EFAULT;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	fieinfo->fi_extents_mapped++;
1458c2ecf20Sopenharmony_ci	if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
1468c2ecf20Sopenharmony_ci		return 1;
1478c2ecf20Sopenharmony_ci	return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fiemap_fill_next_extent);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * fiemap_prep - check validity of requested flags for fiemap
1538c2ecf20Sopenharmony_ci * @inode:	Inode to operate on
1548c2ecf20Sopenharmony_ci * @fieinfo:	Fiemap context passed into ->fiemap
1558c2ecf20Sopenharmony_ci * @start:	Start of the mapped range
1568c2ecf20Sopenharmony_ci * @len:	Length of the mapped range, can be truncated by this function.
1578c2ecf20Sopenharmony_ci * @supported_flags:	Set of fiemap flags that the file system understands
1588c2ecf20Sopenharmony_ci *
1598c2ecf20Sopenharmony_ci * This function must be called from each ->fiemap instance to validate the
1608c2ecf20Sopenharmony_ci * fiemap request against the file system parameters.
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * Returns 0 on success, or a negative error on failure.
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_ciint fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
1658c2ecf20Sopenharmony_ci		u64 start, u64 *len, u32 supported_flags)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	u64 maxbytes = inode->i_sb->s_maxbytes;
1688c2ecf20Sopenharmony_ci	u32 incompat_flags;
1698c2ecf20Sopenharmony_ci	int ret = 0;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (*len == 0)
1728c2ecf20Sopenharmony_ci		return -EINVAL;
1738c2ecf20Sopenharmony_ci	if (start >= maxbytes)
1748c2ecf20Sopenharmony_ci		return -EFBIG;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/*
1778c2ecf20Sopenharmony_ci	 * Shrink request scope to what the fs can actually handle.
1788c2ecf20Sopenharmony_ci	 */
1798c2ecf20Sopenharmony_ci	if (*len > maxbytes || (maxbytes - *len) < start)
1808c2ecf20Sopenharmony_ci		*len = maxbytes - start;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	supported_flags |= FIEMAP_FLAG_SYNC;
1838c2ecf20Sopenharmony_ci	supported_flags &= FIEMAP_FLAGS_COMPAT;
1848c2ecf20Sopenharmony_ci	incompat_flags = fieinfo->fi_flags & ~supported_flags;
1858c2ecf20Sopenharmony_ci	if (incompat_flags) {
1868c2ecf20Sopenharmony_ci		fieinfo->fi_flags = incompat_flags;
1878c2ecf20Sopenharmony_ci		return -EBADR;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC)
1918c2ecf20Sopenharmony_ci		ret = filemap_write_and_wait(inode->i_mapping);
1928c2ecf20Sopenharmony_ci	return ret;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fiemap_prep);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct fiemap fiemap;
1998c2ecf20Sopenharmony_ci	struct fiemap_extent_info fieinfo = { 0, };
2008c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
2018c2ecf20Sopenharmony_ci	int error;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (!inode->i_op->fiemap)
2048c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))
2078c2ecf20Sopenharmony_ci		return -EFAULT;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
2108c2ecf20Sopenharmony_ci		return -EINVAL;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	fieinfo.fi_flags = fiemap.fm_flags;
2138c2ecf20Sopenharmony_ci	fieinfo.fi_extents_max = fiemap.fm_extent_count;
2148c2ecf20Sopenharmony_ci	fieinfo.fi_extents_start = ufiemap->fm_extents;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start,
2178c2ecf20Sopenharmony_ci			fiemap.fm_length);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	fiemap.fm_flags = fieinfo.fi_flags;
2208c2ecf20Sopenharmony_ci	fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
2218c2ecf20Sopenharmony_ci	if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
2228c2ecf20Sopenharmony_ci		error = -EFAULT;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return error;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
2288c2ecf20Sopenharmony_ci			     u64 off, u64 olen, u64 destoff)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct fd src_file = fdget(srcfd);
2318c2ecf20Sopenharmony_ci	loff_t cloned;
2328c2ecf20Sopenharmony_ci	int ret;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (!src_file.file)
2358c2ecf20Sopenharmony_ci		return -EBADF;
2368c2ecf20Sopenharmony_ci	ret = -EXDEV;
2378c2ecf20Sopenharmony_ci	if (src_file.file->f_path.mnt != dst_file->f_path.mnt)
2388c2ecf20Sopenharmony_ci		goto fdput;
2398c2ecf20Sopenharmony_ci	cloned = vfs_clone_file_range(src_file.file, off, dst_file, destoff,
2408c2ecf20Sopenharmony_ci				      olen, 0);
2418c2ecf20Sopenharmony_ci	if (cloned < 0)
2428c2ecf20Sopenharmony_ci		ret = cloned;
2438c2ecf20Sopenharmony_ci	else if (olen && cloned != olen)
2448c2ecf20Sopenharmony_ci		ret = -EINVAL;
2458c2ecf20Sopenharmony_ci	else
2468c2ecf20Sopenharmony_ci		ret = 0;
2478c2ecf20Sopenharmony_cifdput:
2488c2ecf20Sopenharmony_ci	fdput(src_file);
2498c2ecf20Sopenharmony_ci	return ret;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic long ioctl_file_clone_range(struct file *file,
2538c2ecf20Sopenharmony_ci				   struct file_clone_range __user *argp)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct file_clone_range args;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (copy_from_user(&args, argp, sizeof(args)))
2588c2ecf20Sopenharmony_ci		return -EFAULT;
2598c2ecf20Sopenharmony_ci	return ioctl_file_clone(file, args.src_fd, args.src_offset,
2608c2ecf20Sopenharmony_ci				args.src_length, args.dest_offset);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci#ifdef CONFIG_BLOCK
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	return (offset >> inode->i_blkbits);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic inline loff_t blk_to_logical(struct inode *inode, sector_t blk)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	return (blk << inode->i_blkbits);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/**
2768c2ecf20Sopenharmony_ci * __generic_block_fiemap - FIEMAP for block based inodes (no locking)
2778c2ecf20Sopenharmony_ci * @inode: the inode to map
2788c2ecf20Sopenharmony_ci * @fieinfo: the fiemap info struct that will be passed back to userspace
2798c2ecf20Sopenharmony_ci * @start: where to start mapping in the inode
2808c2ecf20Sopenharmony_ci * @len: how much space to map
2818c2ecf20Sopenharmony_ci * @get_block: the fs's get_block function
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * This does FIEMAP for block based inodes.  Basically it will just loop
2848c2ecf20Sopenharmony_ci * through get_block until we hit the number of extents we want to map, or we
2858c2ecf20Sopenharmony_ci * go past the end of the file and hit a hole.
2868c2ecf20Sopenharmony_ci *
2878c2ecf20Sopenharmony_ci * If it is possible to have data blocks beyond a hole past @inode->i_size, then
2888c2ecf20Sopenharmony_ci * please do not use this function, it will stop at the first unmapped block
2898c2ecf20Sopenharmony_ci * beyond i_size.
2908c2ecf20Sopenharmony_ci *
2918c2ecf20Sopenharmony_ci * If you use this function directly, you need to do your own locking. Use
2928c2ecf20Sopenharmony_ci * generic_block_fiemap if you want the locking done for you.
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_cistatic int __generic_block_fiemap(struct inode *inode,
2958c2ecf20Sopenharmony_ci			   struct fiemap_extent_info *fieinfo, loff_t start,
2968c2ecf20Sopenharmony_ci			   loff_t len, get_block_t *get_block)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct buffer_head map_bh;
2998c2ecf20Sopenharmony_ci	sector_t start_blk, last_blk;
3008c2ecf20Sopenharmony_ci	loff_t isize = i_size_read(inode);
3018c2ecf20Sopenharmony_ci	u64 logical = 0, phys = 0, size = 0;
3028c2ecf20Sopenharmony_ci	u32 flags = FIEMAP_EXTENT_MERGED;
3038c2ecf20Sopenharmony_ci	bool past_eof = false, whole_file = false;
3048c2ecf20Sopenharmony_ci	int ret = 0;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC);
3078c2ecf20Sopenharmony_ci	if (ret)
3088c2ecf20Sopenharmony_ci		return ret;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/*
3118c2ecf20Sopenharmony_ci	 * Either the i_mutex or other appropriate locking needs to be held
3128c2ecf20Sopenharmony_ci	 * since we expect isize to not change at all through the duration of
3138c2ecf20Sopenharmony_ci	 * this call.
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	if (len >= isize) {
3168c2ecf20Sopenharmony_ci		whole_file = true;
3178c2ecf20Sopenharmony_ci		len = isize;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/*
3218c2ecf20Sopenharmony_ci	 * Some filesystems can't deal with being asked to map less than
3228c2ecf20Sopenharmony_ci	 * blocksize, so make sure our len is at least block length.
3238c2ecf20Sopenharmony_ci	 */
3248c2ecf20Sopenharmony_ci	if (logical_to_blk(inode, len) == 0)
3258c2ecf20Sopenharmony_ci		len = blk_to_logical(inode, 1);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	start_blk = logical_to_blk(inode, start);
3288c2ecf20Sopenharmony_ci	last_blk = logical_to_blk(inode, start + len - 1);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	do {
3318c2ecf20Sopenharmony_ci		/*
3328c2ecf20Sopenharmony_ci		 * we set b_size to the total size we want so it will map as
3338c2ecf20Sopenharmony_ci		 * many contiguous blocks as possible at once
3348c2ecf20Sopenharmony_ci		 */
3358c2ecf20Sopenharmony_ci		memset(&map_bh, 0, sizeof(struct buffer_head));
3368c2ecf20Sopenharmony_ci		map_bh.b_size = len;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		ret = get_block(inode, start_blk, &map_bh, 0);
3398c2ecf20Sopenharmony_ci		if (ret)
3408c2ecf20Sopenharmony_ci			break;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		/* HOLE */
3438c2ecf20Sopenharmony_ci		if (!buffer_mapped(&map_bh)) {
3448c2ecf20Sopenharmony_ci			start_blk++;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci			/*
3478c2ecf20Sopenharmony_ci			 * We want to handle the case where there is an
3488c2ecf20Sopenharmony_ci			 * allocated block at the front of the file, and then
3498c2ecf20Sopenharmony_ci			 * nothing but holes up to the end of the file properly,
3508c2ecf20Sopenharmony_ci			 * to make sure that extent at the front gets properly
3518c2ecf20Sopenharmony_ci			 * marked with FIEMAP_EXTENT_LAST
3528c2ecf20Sopenharmony_ci			 */
3538c2ecf20Sopenharmony_ci			if (!past_eof &&
3548c2ecf20Sopenharmony_ci			    blk_to_logical(inode, start_blk) >= isize)
3558c2ecf20Sopenharmony_ci				past_eof = 1;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci			/*
3588c2ecf20Sopenharmony_ci			 * First hole after going past the EOF, this is our
3598c2ecf20Sopenharmony_ci			 * last extent
3608c2ecf20Sopenharmony_ci			 */
3618c2ecf20Sopenharmony_ci			if (past_eof && size) {
3628c2ecf20Sopenharmony_ci				flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST;
3638c2ecf20Sopenharmony_ci				ret = fiemap_fill_next_extent(fieinfo, logical,
3648c2ecf20Sopenharmony_ci							      phys, size,
3658c2ecf20Sopenharmony_ci							      flags);
3668c2ecf20Sopenharmony_ci			} else if (size) {
3678c2ecf20Sopenharmony_ci				ret = fiemap_fill_next_extent(fieinfo, logical,
3688c2ecf20Sopenharmony_ci							      phys, size, flags);
3698c2ecf20Sopenharmony_ci				size = 0;
3708c2ecf20Sopenharmony_ci			}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci			/* if we have holes up to/past EOF then we're done */
3738c2ecf20Sopenharmony_ci			if (start_blk > last_blk || past_eof || ret)
3748c2ecf20Sopenharmony_ci				break;
3758c2ecf20Sopenharmony_ci		} else {
3768c2ecf20Sopenharmony_ci			/*
3778c2ecf20Sopenharmony_ci			 * We have gone over the length of what we wanted to
3788c2ecf20Sopenharmony_ci			 * map, and it wasn't the entire file, so add the extent
3798c2ecf20Sopenharmony_ci			 * we got last time and exit.
3808c2ecf20Sopenharmony_ci			 *
3818c2ecf20Sopenharmony_ci			 * This is for the case where say we want to map all the
3828c2ecf20Sopenharmony_ci			 * way up to the second to the last block in a file, but
3838c2ecf20Sopenharmony_ci			 * the last block is a hole, making the second to last
3848c2ecf20Sopenharmony_ci			 * block FIEMAP_EXTENT_LAST.  In this case we want to
3858c2ecf20Sopenharmony_ci			 * see if there is a hole after the second to last block
3868c2ecf20Sopenharmony_ci			 * so we can mark it properly.  If we found data after
3878c2ecf20Sopenharmony_ci			 * we exceeded the length we were requesting, then we
3888c2ecf20Sopenharmony_ci			 * are good to go, just add the extent to the fieinfo
3898c2ecf20Sopenharmony_ci			 * and break
3908c2ecf20Sopenharmony_ci			 */
3918c2ecf20Sopenharmony_ci			if (start_blk > last_blk && !whole_file) {
3928c2ecf20Sopenharmony_ci				ret = fiemap_fill_next_extent(fieinfo, logical,
3938c2ecf20Sopenharmony_ci							      phys, size,
3948c2ecf20Sopenharmony_ci							      flags);
3958c2ecf20Sopenharmony_ci				break;
3968c2ecf20Sopenharmony_ci			}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci			/*
3998c2ecf20Sopenharmony_ci			 * if size != 0 then we know we already have an extent
4008c2ecf20Sopenharmony_ci			 * to add, so add it.
4018c2ecf20Sopenharmony_ci			 */
4028c2ecf20Sopenharmony_ci			if (size) {
4038c2ecf20Sopenharmony_ci				ret = fiemap_fill_next_extent(fieinfo, logical,
4048c2ecf20Sopenharmony_ci							      phys, size,
4058c2ecf20Sopenharmony_ci							      flags);
4068c2ecf20Sopenharmony_ci				if (ret)
4078c2ecf20Sopenharmony_ci					break;
4088c2ecf20Sopenharmony_ci			}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci			logical = blk_to_logical(inode, start_blk);
4118c2ecf20Sopenharmony_ci			phys = blk_to_logical(inode, map_bh.b_blocknr);
4128c2ecf20Sopenharmony_ci			size = map_bh.b_size;
4138c2ecf20Sopenharmony_ci			flags = FIEMAP_EXTENT_MERGED;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci			start_blk += logical_to_blk(inode, size);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci			/*
4188c2ecf20Sopenharmony_ci			 * If we are past the EOF, then we need to make sure as
4198c2ecf20Sopenharmony_ci			 * soon as we find a hole that the last extent we found
4208c2ecf20Sopenharmony_ci			 * is marked with FIEMAP_EXTENT_LAST
4218c2ecf20Sopenharmony_ci			 */
4228c2ecf20Sopenharmony_ci			if (!past_eof && logical + size >= isize)
4238c2ecf20Sopenharmony_ci				past_eof = true;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci		cond_resched();
4268c2ecf20Sopenharmony_ci		if (fatal_signal_pending(current)) {
4278c2ecf20Sopenharmony_ci			ret = -EINTR;
4288c2ecf20Sopenharmony_ci			break;
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	} while (1);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* If ret is 1 then we just hit the end of the extent array */
4348c2ecf20Sopenharmony_ci	if (ret == 1)
4358c2ecf20Sopenharmony_ci		ret = 0;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return ret;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci/**
4418c2ecf20Sopenharmony_ci * generic_block_fiemap - FIEMAP for block based inodes
4428c2ecf20Sopenharmony_ci * @inode: The inode to map
4438c2ecf20Sopenharmony_ci * @fieinfo: The mapping information
4448c2ecf20Sopenharmony_ci * @start: The initial block to map
4458c2ecf20Sopenharmony_ci * @len: The length of the extect to attempt to map
4468c2ecf20Sopenharmony_ci * @get_block: The block mapping function for the fs
4478c2ecf20Sopenharmony_ci *
4488c2ecf20Sopenharmony_ci * Calls __generic_block_fiemap to map the inode, after taking
4498c2ecf20Sopenharmony_ci * the inode's mutex lock.
4508c2ecf20Sopenharmony_ci */
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ciint generic_block_fiemap(struct inode *inode,
4538c2ecf20Sopenharmony_ci			 struct fiemap_extent_info *fieinfo, u64 start,
4548c2ecf20Sopenharmony_ci			 u64 len, get_block_t *get_block)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	int ret;
4578c2ecf20Sopenharmony_ci	inode_lock(inode);
4588c2ecf20Sopenharmony_ci	ret = __generic_block_fiemap(inode, fieinfo, start, len, get_block);
4598c2ecf20Sopenharmony_ci	inode_unlock(inode);
4608c2ecf20Sopenharmony_ci	return ret;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(generic_block_fiemap);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci#endif  /*  CONFIG_BLOCK  */
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci/*
4678c2ecf20Sopenharmony_ci * This provides compatibility with legacy XFS pre-allocation ioctls
4688c2ecf20Sopenharmony_ci * which predate the fallocate syscall.
4698c2ecf20Sopenharmony_ci *
4708c2ecf20Sopenharmony_ci * Only the l_start, l_len and l_whence fields of the 'struct space_resv'
4718c2ecf20Sopenharmony_ci * are used here, rest are ignored.
4728c2ecf20Sopenharmony_ci */
4738c2ecf20Sopenharmony_cistatic int ioctl_preallocate(struct file *filp, int mode, void __user *argp)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
4768c2ecf20Sopenharmony_ci	struct space_resv sr;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (copy_from_user(&sr, argp, sizeof(sr)))
4798c2ecf20Sopenharmony_ci		return -EFAULT;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	switch (sr.l_whence) {
4828c2ecf20Sopenharmony_ci	case SEEK_SET:
4838c2ecf20Sopenharmony_ci		break;
4848c2ecf20Sopenharmony_ci	case SEEK_CUR:
4858c2ecf20Sopenharmony_ci		sr.l_start += filp->f_pos;
4868c2ecf20Sopenharmony_ci		break;
4878c2ecf20Sopenharmony_ci	case SEEK_END:
4888c2ecf20Sopenharmony_ci		sr.l_start += i_size_read(inode);
4898c2ecf20Sopenharmony_ci		break;
4908c2ecf20Sopenharmony_ci	default:
4918c2ecf20Sopenharmony_ci		return -EINVAL;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	return vfs_fallocate(filp, mode | FALLOC_FL_KEEP_SIZE, sr.l_start,
4958c2ecf20Sopenharmony_ci			sr.l_len);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci/* on ia32 l_start is on a 32-bit boundary */
4998c2ecf20Sopenharmony_ci#if defined CONFIG_COMPAT && defined(CONFIG_X86_64)
5008c2ecf20Sopenharmony_ci/* just account for different alignment */
5018c2ecf20Sopenharmony_cistatic int compat_ioctl_preallocate(struct file *file, int mode,
5028c2ecf20Sopenharmony_ci				    struct space_resv_32 __user *argp)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
5058c2ecf20Sopenharmony_ci	struct space_resv_32 sr;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (copy_from_user(&sr, argp, sizeof(sr)))
5088c2ecf20Sopenharmony_ci		return -EFAULT;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	switch (sr.l_whence) {
5118c2ecf20Sopenharmony_ci	case SEEK_SET:
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci	case SEEK_CUR:
5148c2ecf20Sopenharmony_ci		sr.l_start += file->f_pos;
5158c2ecf20Sopenharmony_ci		break;
5168c2ecf20Sopenharmony_ci	case SEEK_END:
5178c2ecf20Sopenharmony_ci		sr.l_start += i_size_read(inode);
5188c2ecf20Sopenharmony_ci		break;
5198c2ecf20Sopenharmony_ci	default:
5208c2ecf20Sopenharmony_ci		return -EINVAL;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	return vfs_fallocate(file, mode | FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len);
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci#endif
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic int file_ioctl(struct file *filp, unsigned int cmd, int __user *p)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	switch (cmd) {
5308c2ecf20Sopenharmony_ci	case FIBMAP:
5318c2ecf20Sopenharmony_ci		return ioctl_fibmap(filp, p);
5328c2ecf20Sopenharmony_ci	case FS_IOC_RESVSP:
5338c2ecf20Sopenharmony_ci	case FS_IOC_RESVSP64:
5348c2ecf20Sopenharmony_ci		return ioctl_preallocate(filp, 0, p);
5358c2ecf20Sopenharmony_ci	case FS_IOC_UNRESVSP:
5368c2ecf20Sopenharmony_ci	case FS_IOC_UNRESVSP64:
5378c2ecf20Sopenharmony_ci		return ioctl_preallocate(filp, FALLOC_FL_PUNCH_HOLE, p);
5388c2ecf20Sopenharmony_ci	case FS_IOC_ZERO_RANGE:
5398c2ecf20Sopenharmony_ci		return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p);
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic int ioctl_fionbio(struct file *filp, int __user *argp)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	unsigned int flag;
5488c2ecf20Sopenharmony_ci	int on, error;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	error = get_user(on, argp);
5518c2ecf20Sopenharmony_ci	if (error)
5528c2ecf20Sopenharmony_ci		return error;
5538c2ecf20Sopenharmony_ci	flag = O_NONBLOCK;
5548c2ecf20Sopenharmony_ci#ifdef __sparc__
5558c2ecf20Sopenharmony_ci	/* SunOS compatibility item. */
5568c2ecf20Sopenharmony_ci	if (O_NONBLOCK != O_NDELAY)
5578c2ecf20Sopenharmony_ci		flag |= O_NDELAY;
5588c2ecf20Sopenharmony_ci#endif
5598c2ecf20Sopenharmony_ci	spin_lock(&filp->f_lock);
5608c2ecf20Sopenharmony_ci	if (on)
5618c2ecf20Sopenharmony_ci		filp->f_flags |= flag;
5628c2ecf20Sopenharmony_ci	else
5638c2ecf20Sopenharmony_ci		filp->f_flags &= ~flag;
5648c2ecf20Sopenharmony_ci	spin_unlock(&filp->f_lock);
5658c2ecf20Sopenharmony_ci	return error;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic int ioctl_fioasync(unsigned int fd, struct file *filp,
5698c2ecf20Sopenharmony_ci			  int __user *argp)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	unsigned int flag;
5728c2ecf20Sopenharmony_ci	int on, error;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	error = get_user(on, argp);
5758c2ecf20Sopenharmony_ci	if (error)
5768c2ecf20Sopenharmony_ci		return error;
5778c2ecf20Sopenharmony_ci	flag = on ? FASYNC : 0;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/* Did FASYNC state change ? */
5808c2ecf20Sopenharmony_ci	if ((flag ^ filp->f_flags) & FASYNC) {
5818c2ecf20Sopenharmony_ci		if (filp->f_op->fasync)
5828c2ecf20Sopenharmony_ci			/* fasync() adjusts filp->f_flags */
5838c2ecf20Sopenharmony_ci			error = filp->f_op->fasync(fd, filp, on);
5848c2ecf20Sopenharmony_ci		else
5858c2ecf20Sopenharmony_ci			error = -ENOTTY;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	return error < 0 ? error : 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic int ioctl_fsfreeze(struct file *filp)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	struct super_block *sb = file_inode(filp)->i_sb;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
5958c2ecf20Sopenharmony_ci		return -EPERM;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* If filesystem doesn't support freeze feature, return. */
5988c2ecf20Sopenharmony_ci	if (sb->s_op->freeze_fs == NULL && sb->s_op->freeze_super == NULL)
5998c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Freeze */
6028c2ecf20Sopenharmony_ci	if (sb->s_op->freeze_super)
6038c2ecf20Sopenharmony_ci		return sb->s_op->freeze_super(sb);
6048c2ecf20Sopenharmony_ci	return freeze_super(sb);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int ioctl_fsthaw(struct file *filp)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct super_block *sb = file_inode(filp)->i_sb;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
6128c2ecf20Sopenharmony_ci		return -EPERM;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* Thaw */
6158c2ecf20Sopenharmony_ci	if (sb->s_op->thaw_super)
6168c2ecf20Sopenharmony_ci		return sb->s_op->thaw_super(sb);
6178c2ecf20Sopenharmony_ci	return thaw_super(sb);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int ioctl_file_dedupe_range(struct file *file,
6218c2ecf20Sopenharmony_ci				   struct file_dedupe_range __user *argp)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct file_dedupe_range *same = NULL;
6248c2ecf20Sopenharmony_ci	int ret;
6258c2ecf20Sopenharmony_ci	unsigned long size;
6268c2ecf20Sopenharmony_ci	u16 count;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (get_user(count, &argp->dest_count)) {
6298c2ecf20Sopenharmony_ci		ret = -EFAULT;
6308c2ecf20Sopenharmony_ci		goto out;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	size = offsetof(struct file_dedupe_range __user, info[count]);
6348c2ecf20Sopenharmony_ci	if (size > PAGE_SIZE) {
6358c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6368c2ecf20Sopenharmony_ci		goto out;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	same = memdup_user(argp, size);
6408c2ecf20Sopenharmony_ci	if (IS_ERR(same)) {
6418c2ecf20Sopenharmony_ci		ret = PTR_ERR(same);
6428c2ecf20Sopenharmony_ci		same = NULL;
6438c2ecf20Sopenharmony_ci		goto out;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	same->dest_count = count;
6478c2ecf20Sopenharmony_ci	ret = vfs_dedupe_file_range(file, same);
6488c2ecf20Sopenharmony_ci	if (ret)
6498c2ecf20Sopenharmony_ci		goto out;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	ret = copy_to_user(argp, same, size);
6528c2ecf20Sopenharmony_ci	if (ret)
6538c2ecf20Sopenharmony_ci		ret = -EFAULT;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ciout:
6568c2ecf20Sopenharmony_ci	kfree(same);
6578c2ecf20Sopenharmony_ci	return ret;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci/*
6618c2ecf20Sopenharmony_ci * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d.
6628c2ecf20Sopenharmony_ci * It's just a simple helper for sys_ioctl and compat_sys_ioctl.
6638c2ecf20Sopenharmony_ci *
6648c2ecf20Sopenharmony_ci * When you add any new common ioctls to the switches above and below,
6658c2ecf20Sopenharmony_ci * please ensure they have compatible arguments in compat mode.
6668c2ecf20Sopenharmony_ci */
6678c2ecf20Sopenharmony_cistatic int do_vfs_ioctl(struct file *filp, unsigned int fd,
6688c2ecf20Sopenharmony_ci			unsigned int cmd, unsigned long arg)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
6718c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	switch (cmd) {
6748c2ecf20Sopenharmony_ci	case FIOCLEX:
6758c2ecf20Sopenharmony_ci		set_close_on_exec(fd, 1);
6768c2ecf20Sopenharmony_ci		return 0;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	case FIONCLEX:
6798c2ecf20Sopenharmony_ci		set_close_on_exec(fd, 0);
6808c2ecf20Sopenharmony_ci		return 0;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	case FIONBIO:
6838c2ecf20Sopenharmony_ci		return ioctl_fionbio(filp, argp);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	case FIOASYNC:
6868c2ecf20Sopenharmony_ci		return ioctl_fioasync(fd, filp, argp);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	case FIOQSIZE:
6898c2ecf20Sopenharmony_ci		if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) ||
6908c2ecf20Sopenharmony_ci		    S_ISLNK(inode->i_mode)) {
6918c2ecf20Sopenharmony_ci			loff_t res = inode_get_bytes(inode);
6928c2ecf20Sopenharmony_ci			return copy_to_user(argp, &res, sizeof(res)) ?
6938c2ecf20Sopenharmony_ci					    -EFAULT : 0;
6948c2ecf20Sopenharmony_ci		}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci		return -ENOTTY;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	case FIFREEZE:
6998c2ecf20Sopenharmony_ci		return ioctl_fsfreeze(filp);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	case FITHAW:
7028c2ecf20Sopenharmony_ci		return ioctl_fsthaw(filp);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	case FS_IOC_FIEMAP:
7058c2ecf20Sopenharmony_ci		return ioctl_fiemap(filp, argp);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	case FIGETBSZ:
7088c2ecf20Sopenharmony_ci		/* anon_bdev filesystems may not have a block size */
7098c2ecf20Sopenharmony_ci		if (!inode->i_sb->s_blocksize)
7108c2ecf20Sopenharmony_ci			return -EINVAL;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		return put_user(inode->i_sb->s_blocksize, (int __user *)argp);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	case FICLONE:
7158c2ecf20Sopenharmony_ci		return ioctl_file_clone(filp, arg, 0, 0, 0);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	case FICLONERANGE:
7188c2ecf20Sopenharmony_ci		return ioctl_file_clone_range(filp, argp);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	case FIDEDUPERANGE:
7218c2ecf20Sopenharmony_ci		return ioctl_file_dedupe_range(filp, argp);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	case FIONREAD:
7248c2ecf20Sopenharmony_ci		if (!S_ISREG(inode->i_mode))
7258c2ecf20Sopenharmony_ci			return vfs_ioctl(filp, cmd, arg);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		return put_user(i_size_read(inode) - filp->f_pos,
7288c2ecf20Sopenharmony_ci				(int __user *)argp);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	default:
7318c2ecf20Sopenharmony_ci		if (S_ISREG(inode->i_mode))
7328c2ecf20Sopenharmony_ci			return file_ioctl(filp, cmd, argp);
7338c2ecf20Sopenharmony_ci		break;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct fd f = fdget(fd);
7428c2ecf20Sopenharmony_ci	int error;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (!f.file)
7458c2ecf20Sopenharmony_ci		return -EBADF;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	error = security_file_ioctl(f.file, cmd, arg);
7488c2ecf20Sopenharmony_ci	if (error)
7498c2ecf20Sopenharmony_ci		goto out;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	error = do_vfs_ioctl(f.file, fd, cmd, arg);
7528c2ecf20Sopenharmony_ci	if (error == -ENOIOCTLCMD)
7538c2ecf20Sopenharmony_ci		error = vfs_ioctl(f.file, cmd, arg);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ciout:
7568c2ecf20Sopenharmony_ci	fdput(f);
7578c2ecf20Sopenharmony_ci	return error;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
7618c2ecf20Sopenharmony_ci/**
7628c2ecf20Sopenharmony_ci * compat_ptr_ioctl - generic implementation of .compat_ioctl file operation
7638c2ecf20Sopenharmony_ci *
7648c2ecf20Sopenharmony_ci * This is not normally called as a function, but instead set in struct
7658c2ecf20Sopenharmony_ci * file_operations as
7668c2ecf20Sopenharmony_ci *
7678c2ecf20Sopenharmony_ci *     .compat_ioctl = compat_ptr_ioctl,
7688c2ecf20Sopenharmony_ci *
7698c2ecf20Sopenharmony_ci * On most architectures, the compat_ptr_ioctl() just passes all arguments
7708c2ecf20Sopenharmony_ci * to the corresponding ->ioctl handler. The exception is arch/s390, where
7718c2ecf20Sopenharmony_ci * compat_ptr() clears the top bit of a 32-bit pointer value, so user space
7728c2ecf20Sopenharmony_ci * pointers to the second 2GB alias the first 2GB, as is the case for
7738c2ecf20Sopenharmony_ci * native 32-bit s390 user space.
7748c2ecf20Sopenharmony_ci *
7758c2ecf20Sopenharmony_ci * The compat_ptr_ioctl() function must therefore be used only with ioctl
7768c2ecf20Sopenharmony_ci * functions that either ignore the argument or pass a pointer to a
7778c2ecf20Sopenharmony_ci * compatible data type.
7788c2ecf20Sopenharmony_ci *
7798c2ecf20Sopenharmony_ci * If any ioctl command handled by fops->unlocked_ioctl passes a plain
7808c2ecf20Sopenharmony_ci * integer instead of a pointer, or any of the passed data types
7818c2ecf20Sopenharmony_ci * is incompatible between 32-bit and 64-bit architectures, a proper
7828c2ecf20Sopenharmony_ci * handler is required instead of compat_ptr_ioctl.
7838c2ecf20Sopenharmony_ci */
7848c2ecf20Sopenharmony_cilong compat_ptr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	if (!file->f_op->unlocked_ioctl)
7878c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(compat_ptr_ioctl);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
7948c2ecf20Sopenharmony_ci		       compat_ulong_t, arg)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	struct fd f = fdget(fd);
7978c2ecf20Sopenharmony_ci	int error;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (!f.file)
8008c2ecf20Sopenharmony_ci		return -EBADF;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	error = security_file_ioctl_compat(f.file, cmd, arg);
8038c2ecf20Sopenharmony_ci	if (error)
8048c2ecf20Sopenharmony_ci		goto out;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	switch (cmd) {
8078c2ecf20Sopenharmony_ci	/* FICLONE takes an int argument, so don't use compat_ptr() */
8088c2ecf20Sopenharmony_ci	case FICLONE:
8098c2ecf20Sopenharmony_ci		error = ioctl_file_clone(f.file, arg, 0, 0, 0);
8108c2ecf20Sopenharmony_ci		break;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci#if defined(CONFIG_X86_64)
8138c2ecf20Sopenharmony_ci	/* these get messy on amd64 due to alignment differences */
8148c2ecf20Sopenharmony_ci	case FS_IOC_RESVSP_32:
8158c2ecf20Sopenharmony_ci	case FS_IOC_RESVSP64_32:
8168c2ecf20Sopenharmony_ci		error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg));
8178c2ecf20Sopenharmony_ci		break;
8188c2ecf20Sopenharmony_ci	case FS_IOC_UNRESVSP_32:
8198c2ecf20Sopenharmony_ci	case FS_IOC_UNRESVSP64_32:
8208c2ecf20Sopenharmony_ci		error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE,
8218c2ecf20Sopenharmony_ci				compat_ptr(arg));
8228c2ecf20Sopenharmony_ci		break;
8238c2ecf20Sopenharmony_ci	case FS_IOC_ZERO_RANGE_32:
8248c2ecf20Sopenharmony_ci		error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE,
8258c2ecf20Sopenharmony_ci				compat_ptr(arg));
8268c2ecf20Sopenharmony_ci		break;
8278c2ecf20Sopenharmony_ci#endif
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/*
8308c2ecf20Sopenharmony_ci	 * everything else in do_vfs_ioctl() takes either a compatible
8318c2ecf20Sopenharmony_ci	 * pointer argument or no argument -- call it with a modified
8328c2ecf20Sopenharmony_ci	 * argument.
8338c2ecf20Sopenharmony_ci	 */
8348c2ecf20Sopenharmony_ci	default:
8358c2ecf20Sopenharmony_ci		error = do_vfs_ioctl(f.file, fd, cmd,
8368c2ecf20Sopenharmony_ci				     (unsigned long)compat_ptr(arg));
8378c2ecf20Sopenharmony_ci		if (error != -ENOIOCTLCMD)
8388c2ecf20Sopenharmony_ci			break;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		if (f.file->f_op->compat_ioctl)
8418c2ecf20Sopenharmony_ci			error = f.file->f_op->compat_ioctl(f.file, cmd, arg);
8428c2ecf20Sopenharmony_ci		if (error == -ENOIOCTLCMD)
8438c2ecf20Sopenharmony_ci			error = -ENOTTY;
8448c2ecf20Sopenharmony_ci		break;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci out:
8488c2ecf20Sopenharmony_ci	fdput(f);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	return error;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci#endif
853