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