Christoph Hellwigfs: remove ksys_ioctl863b67e6 years agoHistory
853 lines · 20.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  linux/fs/ioctl.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/syscalls.h>#include <linux/mm.h>#include <linux/capability.h>#include <linux/compat.h>#include <linux/file.h>#include <linux/fs.h>#include <linux/security.h>#include <linux/export.h>#include <linux/uaccess.h>#include <linux/writeback.h>#include <linux/buffer_head.h>#include <linux/falloc.h>#include <linux/sched/signal.h>#include <linux/fiemap.h> #include "internal.h" #include <asm/ioctls.h> /* So that the fiemap access checks can't overflow on 32 bit machines. */#define FIEMAP_MAX_EXTENTS	(UINT_MAX / sizeof(struct fiemap_extent)) /** * vfs_ioctl - call filesystem specific ioctl methods * @filp:	open file to invoke ioctl method on * @cmd:	ioctl command to execute * @arg:	command-specific argument for ioctl * * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise * returns -ENOTTY. * * Returns 0 on success, -errno on error. */long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){	int error = -ENOTTY; 	if (!filp->f_op->unlocked_ioctl)		goto out; 	error = filp->f_op->unlocked_ioctl(filp, cmd, arg);	if (error == -ENOIOCTLCMD)		error = -ENOTTY; out:	return error;}EXPORT_SYMBOL(vfs_ioctl); static int ioctl_fibmap(struct file *filp, int __user *p){	struct inode *inode = file_inode(filp);	struct super_block *sb = inode->i_sb;	int error, ur_block;	sector_t block; 	if (!capable(CAP_SYS_RAWIO))		return -EPERM; 	error = get_user(ur_block, p);	if (error)		return error; 	if (ur_block < 0)		return -EINVAL; 	block = ur_block;	error = bmap(inode, &block); 	if (block > INT_MAX) {		error = -ERANGE;		pr_warn_ratelimited("[%s/%d] FS: %s File: %pD4 would truncate fibmap result\n",				    current->comm, task_pid_nr(current),				    sb->s_id, filp);	} 	if (error)		ur_block = 0;	else		ur_block = block; 	if (put_user(ur_block, p))		error = -EFAULT; 	return error;} /** * fiemap_fill_next_extent - Fiemap helper function * @fieinfo:	Fiemap context passed into ->fiemap * @logical:	Extent logical start offset, in bytes * @phys:	Extent physical start offset, in bytes * @len:	Extent length, in bytes * @flags:	FIEMAP_EXTENT flags that describe this extent * * Called from file system ->fiemap callback. Will populate extent * info as passed in via arguments and copy to user memory. On * success, extent count on fieinfo is incremented. * * Returns 0 on success, -errno on error, 1 if this was the last * extent that will fit in user array. */#define SET_UNKNOWN_FLAGS	(FIEMAP_EXTENT_DELALLOC)#define SET_NO_UNMOUNTED_IO_FLAGS	(FIEMAP_EXTENT_DATA_ENCRYPTED)#define SET_NOT_ALIGNED_FLAGS	(FIEMAP_EXTENT_DATA_TAIL|FIEMAP_EXTENT_DATA_INLINE)int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,			    u64 phys, u64 len, u32 flags){	struct fiemap_extent extent;	struct fiemap_extent __user *dest = fieinfo->fi_extents_start; 	/* only count the extents */	if (fieinfo->fi_extents_max == 0) {		fieinfo->fi_extents_mapped++;		return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;	} 	if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)		return 1; 	if (flags & SET_UNKNOWN_FLAGS)		flags |= FIEMAP_EXTENT_UNKNOWN;	if (flags & SET_NO_UNMOUNTED_IO_FLAGS)		flags |= FIEMAP_EXTENT_ENCODED;	if (flags & SET_NOT_ALIGNED_FLAGS)		flags |= FIEMAP_EXTENT_NOT_ALIGNED; 	memset(&extent, 0, sizeof(extent));	extent.fe_logical = logical;	extent.fe_physical = phys;	extent.fe_length = len;	extent.fe_flags = flags; 	dest += fieinfo->fi_extents_mapped;	if (copy_to_user(dest, &extent, sizeof(extent)))		return -EFAULT; 	fieinfo->fi_extents_mapped++;	if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)		return 1;	return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;}EXPORT_SYMBOL(fiemap_fill_next_extent); /** * fiemap_prep - check validity of requested flags for fiemap * @inode:	Inode to operate on * @fieinfo:	Fiemap context passed into ->fiemap * @start:	Start of the mapped range * @len:	Length of the mapped range, can be truncated by this function. * @supported_flags:	Set of fiemap flags that the file system understands * * This function must be called from each ->fiemap instance to validate the * fiemap request against the file system parameters. * * Returns 0 on success, or a negative error on failure. */int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,		u64 start, u64 *len, u32 supported_flags){	u64 maxbytes = inode->i_sb->s_maxbytes;	u32 incompat_flags;	int ret = 0; 	if (*len == 0)		return -EINVAL;	if (start > maxbytes)		return -EFBIG; 	/*	 * Shrink request scope to what the fs can actually handle.	 */	if (*len > maxbytes || (maxbytes - *len) < start)		*len = maxbytes - start; 	supported_flags |= FIEMAP_FLAG_SYNC;	supported_flags &= FIEMAP_FLAGS_COMPAT;	incompat_flags = fieinfo->fi_flags & ~supported_flags;	if (incompat_flags) {		fieinfo->fi_flags = incompat_flags;		return -EBADR;	} 	if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC)		ret = filemap_write_and_wait(inode->i_mapping);	return ret;}EXPORT_SYMBOL(fiemap_prep); static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap){	struct fiemap fiemap;	struct fiemap_extent_info fieinfo = { 0, };	struct inode *inode = file_inode(filp);	int error; 	if (!inode->i_op->fiemap)		return -EOPNOTSUPP; 	if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))		return -EFAULT; 	if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)		return -EINVAL; 	fieinfo.fi_flags = fiemap.fm_flags;	fieinfo.fi_extents_max = fiemap.fm_extent_count;	fieinfo.fi_extents_start = ufiemap->fm_extents; 	error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start,			fiemap.fm_length); 	fiemap.fm_flags = fieinfo.fi_flags;	fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;	if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))		error = -EFAULT; 	return error;} static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,			     u64 off, u64 olen, u64 destoff){	struct fd src_file = fdget(srcfd);	loff_t cloned;	int ret; 	if (!src_file.file)		return -EBADF;	ret = -EXDEV;	if (src_file.file->f_path.mnt != dst_file->f_path.mnt)		goto fdput;	cloned = vfs_clone_file_range(src_file.file, off, dst_file, destoff,				      olen, 0);	if (cloned < 0)		ret = cloned;	else if (olen && cloned != olen)		ret = -EINVAL;	else		ret = 0;fdput:	fdput(src_file);	return ret;} static long ioctl_file_clone_range(struct file *file,				   struct file_clone_range __user *argp){	struct file_clone_range args; 	if (copy_from_user(&args, argp, sizeof(args)))		return -EFAULT;	return ioctl_file_clone(file, args.src_fd, args.src_offset,				args.src_length, args.dest_offset);} #ifdef CONFIG_BLOCK static inline sector_t logical_to_blk(struct inode *inode, loff_t offset){	return (offset >> inode->i_blkbits);} static inline loff_t blk_to_logical(struct inode *inode, sector_t blk){	return (blk << inode->i_blkbits);} /** * __generic_block_fiemap - FIEMAP for block based inodes (no locking) * @inode: the inode to map * @fieinfo: the fiemap info struct that will be passed back to userspace * @start: where to start mapping in the inode * @len: how much space to map * @get_block: the fs's get_block function * * This does FIEMAP for block based inodes.  Basically it will just loop * through get_block until we hit the number of extents we want to map, or we * go past the end of the file and hit a hole. * * If it is possible to have data blocks beyond a hole past @inode->i_size, then * please do not use this function, it will stop at the first unmapped block * beyond i_size. * * If you use this function directly, you need to do your own locking. Use * generic_block_fiemap if you want the locking done for you. */static int __generic_block_fiemap(struct inode *inode,			   struct fiemap_extent_info *fieinfo, loff_t start,			   loff_t len, get_block_t *get_block){	struct buffer_head map_bh;	sector_t start_blk, last_blk;	loff_t isize = i_size_read(inode);	u64 logical = 0, phys = 0, size = 0;	u32 flags = FIEMAP_EXTENT_MERGED;	bool past_eof = false, whole_file = false;	int ret = 0; 	ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC);	if (ret)		return ret; 	/*	 * Either the i_mutex or other appropriate locking needs to be held	 * since we expect isize to not change at all through the duration of	 * this call.	 */	if (len >= isize) {		whole_file = true;		len = isize;	} 	/*	 * Some filesystems can't deal with being asked to map less than	 * blocksize, so make sure our len is at least block length.	 */	if (logical_to_blk(inode, len) == 0)		len = blk_to_logical(inode, 1); 	start_blk = logical_to_blk(inode, start);	last_blk = logical_to_blk(inode, start + len - 1); 	do {		/*		 * we set b_size to the total size we want so it will map as		 * many contiguous blocks as possible at once		 */		memset(&map_bh, 0, sizeof(struct buffer_head));		map_bh.b_size = len; 		ret = get_block(inode, start_blk, &map_bh, 0);		if (ret)			break; 		/* HOLE */		if (!buffer_mapped(&map_bh)) {			start_blk++; 			/*			 * We want to handle the case where there is an			 * allocated block at the front of the file, and then			 * nothing but holes up to the end of the file properly,			 * to make sure that extent at the front gets properly			 * marked with FIEMAP_EXTENT_LAST			 */			if (!past_eof &&			    blk_to_logical(inode, start_blk) >= isize)				past_eof = 1; 			/*			 * First hole after going past the EOF, this is our			 * last extent			 */			if (past_eof && size) {				flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST;				ret = fiemap_fill_next_extent(fieinfo, logical,							      phys, size,							      flags);			} else if (size) {				ret = fiemap_fill_next_extent(fieinfo, logical,							      phys, size, flags);				size = 0;			} 			/* if we have holes up to/past EOF then we're done */			if (start_blk > last_blk || past_eof || ret)				break;		} else {			/*			 * We have gone over the length of what we wanted to			 * map, and it wasn't the entire file, so add the extent			 * we got last time and exit.			 *			 * This is for the case where say we want to map all the			 * way up to the second to the last block in a file, but			 * the last block is a hole, making the second to last			 * block FIEMAP_EXTENT_LAST.  In this case we want to			 * see if there is a hole after the second to last block			 * so we can mark it properly.  If we found data after			 * we exceeded the length we were requesting, then we			 * are good to go, just add the extent to the fieinfo			 * and break			 */			if (start_blk > last_blk && !whole_file) {				ret = fiemap_fill_next_extent(fieinfo, logical,							      phys, size,							      flags);				break;			} 			/*			 * if size != 0 then we know we already have an extent			 * to add, so add it.			 */			if (size) {				ret = fiemap_fill_next_extent(fieinfo, logical,							      phys, size,							      flags);				if (ret)					break;			} 			logical = blk_to_logical(inode, start_blk);			phys = blk_to_logical(inode, map_bh.b_blocknr);			size = map_bh.b_size;			flags = FIEMAP_EXTENT_MERGED; 			start_blk += logical_to_blk(inode, size); 			/*			 * If we are past the EOF, then we need to make sure as			 * soon as we find a hole that the last extent we found			 * is marked with FIEMAP_EXTENT_LAST			 */			if (!past_eof && logical + size >= isize)				past_eof = true;		}		cond_resched();		if (fatal_signal_pending(current)) {			ret = -EINTR;			break;		} 	} while (1); 	/* If ret is 1 then we just hit the end of the extent array */	if (ret == 1)		ret = 0; 	return ret;} /** * generic_block_fiemap - FIEMAP for block based inodes * @inode: The inode to map * @fieinfo: The mapping information * @start: The initial block to map * @len: The length of the extect to attempt to map * @get_block: The block mapping function for the fs * * Calls __generic_block_fiemap to map the inode, after taking * the inode's mutex lock. */ int generic_block_fiemap(struct inode *inode,			 struct fiemap_extent_info *fieinfo, u64 start,			 u64 len, get_block_t *get_block){	int ret;	inode_lock(inode);	ret = __generic_block_fiemap(inode, fieinfo, start, len, get_block);	inode_unlock(inode);	return ret;}EXPORT_SYMBOL(generic_block_fiemap); #endif  /*  CONFIG_BLOCK  */ /* * This provides compatibility with legacy XFS pre-allocation ioctls * which predate the fallocate syscall. * * Only the l_start, l_len and l_whence fields of the 'struct space_resv' * are used here, rest are ignored. */static int ioctl_preallocate(struct file *filp, int mode, void __user *argp){	struct inode *inode = file_inode(filp);	struct space_resv sr; 	if (copy_from_user(&sr, argp, sizeof(sr)))		return -EFAULT; 	switch (sr.l_whence) {	case SEEK_SET:		break;	case SEEK_CUR:		sr.l_start += filp->f_pos;		break;	case SEEK_END:		sr.l_start += i_size_read(inode);		break;	default:		return -EINVAL;	} 	return vfs_fallocate(filp, mode | FALLOC_FL_KEEP_SIZE, sr.l_start,			sr.l_len);} /* on ia32 l_start is on a 32-bit boundary */#if defined CONFIG_COMPAT && defined(CONFIG_X86_64)/* just account for different alignment */

Lines 1–500 of 853.