Greg Kroah-Hartmanfs: remove vfs_ioctl exporte5bca061 year agoHistory
702 lines · 16.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 <linux/mount.h>#include <linux/fscrypt.h>#include <linux/fileattr.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. */static int 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;} 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. */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; #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) 	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 int ioctl_file_clone(struct file *dst_file, unsigned long srcfd,			    u64 off, u64 olen, u64 destoff){	CLASS(fd, src_file)(srcfd);	loff_t cloned;	int ret; 	if (fd_empty(src_file))		return -EBADF;	cloned = vfs_clone_file_range(fd_file(src_file), off, dst_file, destoff,				      olen, 0);	if (cloned < 0)		ret = cloned;	else if (olen && cloned != olen)		ret = -EINVAL;	else		ret = 0;	return ret;} static int 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);} /* * 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 */static int compat_ioctl_preallocate(struct file *file, int mode,				    struct space_resv_32 __user *argp){	struct inode *inode = file_inode(file);	struct space_resv_32 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 += file->f_pos;		break;	case SEEK_END:		sr.l_start += i_size_read(inode);		break;	default:		return -EINVAL;	} 	return vfs_fallocate(file, mode | FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len);}#endif static int file_ioctl(struct file *filp, unsigned int cmd, int __user *p){	switch (cmd) {	case FIBMAP:		return ioctl_fibmap(filp, p);	case FS_IOC_RESVSP:	case FS_IOC_RESVSP64:		return ioctl_preallocate(filp, 0, p);	case FS_IOC_UNRESVSP:	case FS_IOC_UNRESVSP64:		return ioctl_preallocate(filp, FALLOC_FL_PUNCH_HOLE, p);	case FS_IOC_ZERO_RANGE:		return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p);	} 	return -ENOIOCTLCMD;} static int ioctl_fionbio(struct file *filp, int __user *argp){	unsigned int flag;	int on, error; 	error = get_user(on, argp);	if (error)		return error;	flag = O_NONBLOCK;#ifdef __sparc__	/* SunOS compatibility item. */	if (O_NONBLOCK != O_NDELAY)		flag |= O_NDELAY;#endif	spin_lock(&filp->f_lock);	if (on)		filp->f_flags |= flag;	else		filp->f_flags &= ~flag;	spin_unlock(&filp->f_lock);	return error;} static int ioctl_fioasync(unsigned int fd, struct file *filp,			  int __user *argp){	unsigned int flag;	int on, error; 	error = get_user(on, argp);	if (error)		return error;	flag = on ? FASYNC : 0; 	/* Did FASYNC state change ? */	if ((flag ^ filp->f_flags) & FASYNC) {		if (filp->f_op->fasync)			/* fasync() adjusts filp->f_flags */			error = filp->f_op->fasync(fd, filp, on);		else			error = -ENOTTY;	}	return error < 0 ? error : 0;} static int ioctl_fsfreeze(struct file *filp){	struct super_block *sb = file_inode(filp)->i_sb; 	if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))		return -EPERM; 	/* If filesystem doesn't support freeze feature, return. */	if (sb->s_op->freeze_fs == NULL && sb->s_op->freeze_super == NULL)		return -EOPNOTSUPP; 	/* Freeze */	if (sb->s_op->freeze_super)		return sb->s_op->freeze_super(sb, FREEZE_HOLDER_USERSPACE, NULL);	return freeze_super(sb, FREEZE_HOLDER_USERSPACE, NULL);} static int ioctl_fsthaw(struct file *filp){	struct super_block *sb = file_inode(filp)->i_sb; 	if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))		return -EPERM; 	/* Thaw */	if (sb->s_op->thaw_super)		return sb->s_op->thaw_super(sb, FREEZE_HOLDER_USERSPACE, NULL);	return thaw_super(sb, FREEZE_HOLDER_USERSPACE, NULL);} static int ioctl_file_dedupe_range(struct file *file,				   struct file_dedupe_range __user *argp){	struct file_dedupe_range *same = NULL;	int ret;	unsigned long size;	u16 count; 	if (get_user(count, &argp->dest_count)) {		ret = -EFAULT;		goto out;	} 	size = struct_size(same, info, count);	if (size > PAGE_SIZE) {		ret = -ENOMEM;		goto out;	} 	same = memdup_user(argp, size);	if (IS_ERR(same)) {		ret = PTR_ERR(same);		same = NULL;		goto out;	} 	same->dest_count = count;	ret = vfs_dedupe_file_range(file, same);	if (ret)		goto out; 	ret = copy_to_user(argp, same, size);	if (ret)		ret = -EFAULT; out:	kfree(same);	return ret;} static int ioctl_getfsuuid(struct file *file, void __user *argp){	struct super_block *sb = file_inode(file)->i_sb;	struct fsuuid2 u = { .len = sb->s_uuid_len, }; 	if (!sb->s_uuid_len)		return -ENOTTY; 	memcpy(&u.uuid[0], &sb->s_uuid, sb->s_uuid_len); 	return copy_to_user(argp, &u, sizeof(u)) ? -EFAULT : 0;} static int ioctl_get_fs_sysfs_path(struct file *file, void __user *argp){	struct super_block *sb = file_inode(file)->i_sb; 	if (!strlen(sb->s_sysfs_name))		return -ENOTTY; 	struct fs_sysfs_path u = {}; 	u.len = scnprintf(u.name, sizeof(u.name), "%s/%s", sb->s_type->name, sb->s_sysfs_name); 	return copy_to_user(argp, &u, sizeof(u)) ? -EFAULT : 0;} /* * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. * It's just a simple helper for sys_ioctl and compat_sys_ioctl. * * When you add any new common ioctls to the switches above and below, * please ensure they have compatible arguments in compat mode. * * The LSM mailing list should also be notified of any command additions or * changes, as specific LSMs may be affected. */static int do_vfs_ioctl(struct file *filp, unsigned int fd,			unsigned int cmd, unsigned long arg){	void __user *argp = (void __user *)arg;	struct inode *inode = file_inode(filp); 	switch (cmd) {	case FIOCLEX:		set_close_on_exec(fd, 1);

Lines 1–500 of 702.