diff options
Diffstat (limited to 'fs/ioctl.c')
| -rw-r--r-- | fs/ioctl.c | 405 |
1 files changed, 64 insertions, 341 deletions
diff --git a/fs/ioctl.c b/fs/ioctl.c index 80ac36aea913..1c152c2b1b67 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -41,7 +41,7 @@ * * Returns 0 on success, -errno on error. */ -long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static int vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int error = -ENOTTY; @@ -54,7 +54,6 @@ long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) out: return error; } -EXPORT_SYMBOL(vfs_ioctl); static int ioctl_fibmap(struct file *filp, int __user *p) { @@ -109,9 +108,6 @@ static int ioctl_fibmap(struct file *filp, int __user *p) * 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) { @@ -127,6 +123,10 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical, 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) @@ -227,16 +227,16 @@ static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap) return error; } -static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd, - u64 off, u64 olen, u64 destoff) +static int ioctl_file_clone(struct file *dst_file, unsigned long srcfd, + u64 off, u64 olen, u64 destoff) { - struct fd src_file = fdget(srcfd); + CLASS(fd, src_file)(srcfd); loff_t cloned; int ret; - if (!src_file.file) + if (fd_empty(src_file)) return -EBADF; - cloned = vfs_clone_file_range(src_file.file, off, dst_file, destoff, + cloned = vfs_clone_file_range(fd_file(src_file), off, dst_file, destoff, olen, 0); if (cloned < 0) ret = cloned; @@ -244,12 +244,11 @@ static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd, ret = -EINVAL; else ret = 0; - fdput(src_file); return ret; } -static long ioctl_file_clone_range(struct file *file, - struct file_clone_range __user *argp) +static int ioctl_file_clone_range(struct file *file, + struct file_clone_range __user *argp) { struct file_clone_range args; @@ -396,8 +395,8 @@ static int ioctl_fsfreeze(struct file *filp) /* Freeze */ if (sb->s_op->freeze_super) - return sb->s_op->freeze_super(sb); - return freeze_super(sb); + 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) @@ -409,8 +408,8 @@ static int ioctl_fsthaw(struct file *filp) /* Thaw */ if (sb->s_op->thaw_super) - return sb->s_op->thaw_super(sb); - return thaw_super(sb); + 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, @@ -426,7 +425,7 @@ static int ioctl_file_dedupe_range(struct file *file, goto out; } - size = offsetof(struct file_dedupe_range, info[count]); + size = struct_size(same, info, count); if (size > PAGE_SIZE) { ret = -ENOMEM; goto out; @@ -453,313 +452,31 @@ out: return ret; } -/** - * fileattr_fill_xflags - initialize fileattr with xflags - * @fa: fileattr pointer - * @xflags: FS_XFLAG_* flags - * - * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All - * other fields are zeroed. - */ -void fileattr_fill_xflags(struct fileattr *fa, u32 xflags) -{ - memset(fa, 0, sizeof(*fa)); - fa->fsx_valid = true; - fa->fsx_xflags = xflags; - if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE) - fa->flags |= FS_IMMUTABLE_FL; - if (fa->fsx_xflags & FS_XFLAG_APPEND) - fa->flags |= FS_APPEND_FL; - if (fa->fsx_xflags & FS_XFLAG_SYNC) - fa->flags |= FS_SYNC_FL; - if (fa->fsx_xflags & FS_XFLAG_NOATIME) - fa->flags |= FS_NOATIME_FL; - if (fa->fsx_xflags & FS_XFLAG_NODUMP) - fa->flags |= FS_NODUMP_FL; - if (fa->fsx_xflags & FS_XFLAG_DAX) - fa->flags |= FS_DAX_FL; - if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT) - fa->flags |= FS_PROJINHERIT_FL; -} -EXPORT_SYMBOL(fileattr_fill_xflags); - -/** - * fileattr_fill_flags - initialize fileattr with flags - * @fa: fileattr pointer - * @flags: FS_*_FL flags - * - * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags). - * All other fields are zeroed. - */ -void fileattr_fill_flags(struct fileattr *fa, u32 flags) -{ - memset(fa, 0, sizeof(*fa)); - fa->flags_valid = true; - fa->flags = flags; - if (fa->flags & FS_SYNC_FL) - fa->fsx_xflags |= FS_XFLAG_SYNC; - if (fa->flags & FS_IMMUTABLE_FL) - fa->fsx_xflags |= FS_XFLAG_IMMUTABLE; - if (fa->flags & FS_APPEND_FL) - fa->fsx_xflags |= FS_XFLAG_APPEND; - if (fa->flags & FS_NODUMP_FL) - fa->fsx_xflags |= FS_XFLAG_NODUMP; - if (fa->flags & FS_NOATIME_FL) - fa->fsx_xflags |= FS_XFLAG_NOATIME; - if (fa->flags & FS_DAX_FL) - fa->fsx_xflags |= FS_XFLAG_DAX; - if (fa->flags & FS_PROJINHERIT_FL) - fa->fsx_xflags |= FS_XFLAG_PROJINHERIT; -} -EXPORT_SYMBOL(fileattr_fill_flags); - -/** - * vfs_fileattr_get - retrieve miscellaneous file attributes - * @dentry: the object to retrieve from - * @fa: fileattr pointer - * - * Call i_op->fileattr_get() callback, if exists. - * - * Return: 0 on success, or a negative error on failure. - */ -int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) -{ - struct inode *inode = d_inode(dentry); - - if (!inode->i_op->fileattr_get) - return -ENOIOCTLCMD; - - return inode->i_op->fileattr_get(dentry, fa); -} -EXPORT_SYMBOL(vfs_fileattr_get); - -/** - * copy_fsxattr_to_user - copy fsxattr to userspace. - * @fa: fileattr pointer - * @ufa: fsxattr user pointer - * - * Return: 0 on success, or -EFAULT on failure. - */ -int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa) -{ - struct fsxattr xfa; - - memset(&xfa, 0, sizeof(xfa)); - xfa.fsx_xflags = fa->fsx_xflags; - xfa.fsx_extsize = fa->fsx_extsize; - xfa.fsx_nextents = fa->fsx_nextents; - xfa.fsx_projid = fa->fsx_projid; - xfa.fsx_cowextsize = fa->fsx_cowextsize; - - if (copy_to_user(ufa, &xfa, sizeof(xfa))) - return -EFAULT; - - return 0; -} -EXPORT_SYMBOL(copy_fsxattr_to_user); - -static int copy_fsxattr_from_user(struct fileattr *fa, - struct fsxattr __user *ufa) -{ - struct fsxattr xfa; - - if (copy_from_user(&xfa, ufa, sizeof(xfa))) - return -EFAULT; - - fileattr_fill_xflags(fa, xfa.fsx_xflags); - fa->fsx_extsize = xfa.fsx_extsize; - fa->fsx_nextents = xfa.fsx_nextents; - fa->fsx_projid = xfa.fsx_projid; - fa->fsx_cowextsize = xfa.fsx_cowextsize; - - return 0; -} - -/* - * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject - * any invalid configurations. - * - * Note: must be called with inode lock held. - */ -static int fileattr_set_prepare(struct inode *inode, - const struct fileattr *old_ma, - struct fileattr *fa) +static int ioctl_getfsuuid(struct file *file, void __user *argp) { - int err; + struct super_block *sb = file_inode(file)->i_sb; + struct fsuuid2 u = { .len = sb->s_uuid_len, }; - /* - * The IMMUTABLE and APPEND_ONLY flags can only be changed by - * the relevant capability. - */ - if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) && - !capable(CAP_LINUX_IMMUTABLE)) - return -EPERM; - - err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags); - if (err) - return err; - - /* - * Project Quota ID state is only allowed to change from within the init - * namespace. Enforce that restriction only if we are trying to change - * the quota ID state. Everything else is allowed in user namespaces. - */ - if (current_user_ns() != &init_user_ns) { - if (old_ma->fsx_projid != fa->fsx_projid) - return -EINVAL; - if ((old_ma->fsx_xflags ^ fa->fsx_xflags) & - FS_XFLAG_PROJINHERIT) - return -EINVAL; - } else { - /* - * Caller is allowed to change the project ID. If it is being - * changed, make sure that the new value is valid. - */ - if (old_ma->fsx_projid != fa->fsx_projid && - !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid))) - return -EINVAL; - } - - /* Check extent size hints. */ - if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode)) - return -EINVAL; - - if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) && - !S_ISDIR(inode->i_mode)) - return -EINVAL; - - if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) && - !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) - return -EINVAL; - - /* - * It is only valid to set the DAX flag on regular files and - * directories on filesystems. - */ - if ((fa->fsx_xflags & FS_XFLAG_DAX) && - !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) - return -EINVAL; - - /* Extent size hints of zero turn off the flags. */ - if (fa->fsx_extsize == 0) - fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT); - if (fa->fsx_cowextsize == 0) - fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE; - - return 0; -} - -/** - * vfs_fileattr_set - change miscellaneous file attributes - * @mnt_userns: user namespace of the mount - * @dentry: the object to change - * @fa: fileattr pointer - * - * After verifying permissions, call i_op->fileattr_set() callback, if - * exists. - * - * Verifying attributes involves retrieving current attributes with - * i_op->fileattr_get(), this also allows initializing attributes that have - * not been set by the caller to current values. Inode lock is held - * thoughout to prevent racing with another instance. - * - * Return: 0 on success, or a negative error on failure. - */ -int vfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, - struct fileattr *fa) -{ - struct inode *inode = d_inode(dentry); - struct fileattr old_ma = {}; - int err; - - if (!inode->i_op->fileattr_set) - return -ENOIOCTLCMD; - - if (!inode_owner_or_capable(mnt_userns, inode)) - return -EPERM; + if (!sb->s_uuid_len) + return -ENOTTY; - inode_lock(inode); - err = vfs_fileattr_get(dentry, &old_ma); - if (!err) { - /* initialize missing bits from old_ma */ - if (fa->flags_valid) { - fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON; - fa->fsx_extsize = old_ma.fsx_extsize; - fa->fsx_nextents = old_ma.fsx_nextents; - fa->fsx_projid = old_ma.fsx_projid; - fa->fsx_cowextsize = old_ma.fsx_cowextsize; - } else { - fa->flags |= old_ma.flags & ~FS_COMMON_FL; - } - err = fileattr_set_prepare(inode, &old_ma, fa); - if (!err) - err = inode->i_op->fileattr_set(mnt_userns, dentry, fa); - } - inode_unlock(inode); + memcpy(&u.uuid[0], &sb->s_uuid, sb->s_uuid_len); - return err; + return copy_to_user(argp, &u, sizeof(u)) ? -EFAULT : 0; } -EXPORT_SYMBOL(vfs_fileattr_set); -static int ioctl_getflags(struct file *file, unsigned int __user *argp) +static int ioctl_get_fs_sysfs_path(struct file *file, void __user *argp) { - struct fileattr fa = { .flags_valid = true }; /* hint only */ - int err; + struct super_block *sb = file_inode(file)->i_sb; - err = vfs_fileattr_get(file->f_path.dentry, &fa); - if (!err) - err = put_user(fa.flags, argp); - return err; -} - -static int ioctl_setflags(struct file *file, unsigned int __user *argp) -{ - struct user_namespace *mnt_userns = file_mnt_user_ns(file); - struct dentry *dentry = file->f_path.dentry; - struct fileattr fa; - unsigned int flags; - int err; - - err = get_user(flags, argp); - if (!err) { - err = mnt_want_write_file(file); - if (!err) { - fileattr_fill_flags(&fa, flags); - err = vfs_fileattr_set(mnt_userns, dentry, &fa); - mnt_drop_write_file(file); - } - } - return err; -} + if (!strlen(sb->s_sysfs_name)) + return -ENOTTY; -static int ioctl_fsgetxattr(struct file *file, void __user *argp) -{ - struct fileattr fa = { .fsx_valid = true }; /* hint only */ - int err; + struct fs_sysfs_path u = {}; - err = vfs_fileattr_get(file->f_path.dentry, &fa); - if (!err) - err = copy_fsxattr_to_user(&fa, argp); + u.len = scnprintf(u.name, sizeof(u.name), "%s/%s", sb->s_type->name, sb->s_sysfs_name); - return err; -} - -static int ioctl_fssetxattr(struct file *file, void __user *argp) -{ - struct user_namespace *mnt_userns = file_mnt_user_ns(file); - struct dentry *dentry = file->f_path.dentry; - struct fileattr fa; - int err; - - err = copy_fsxattr_from_user(&fa, argp); - if (!err) { - err = mnt_want_write_file(file); - if (!err) { - err = vfs_fileattr_set(mnt_userns, dentry, &fa); - mnt_drop_write_file(file); - } - } - return err; + return copy_to_user(argp, &u, sizeof(u)) ? -EFAULT : 0; } /* @@ -768,6 +485,9 @@ static int ioctl_fssetxattr(struct file *file, void __user *argp) * * 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) @@ -791,7 +511,8 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, return ioctl_fioasync(fd, filp, argp); case FIOQSIZE: - if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) || + if (S_ISDIR(inode->i_mode) || + (S_ISREG(inode->i_mode) && !IS_ANON_FILE(inode)) || S_ISLNK(inode->i_mode)) { loff_t res = inode_get_bytes(inode); return copy_to_user(argp, &res, sizeof(res)) ? @@ -826,7 +547,7 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, return ioctl_file_dedupe_range(filp, argp); case FIONREAD: - if (!S_ISREG(inode->i_mode)) + if (!S_ISREG(inode->i_mode) || IS_ANON_FILE(inode)) return vfs_ioctl(filp, cmd, arg); return put_user(i_size_read(inode) - filp->f_pos, @@ -844,8 +565,14 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, case FS_IOC_FSSETXATTR: return ioctl_fssetxattr(filp, argp); + case FS_IOC_GETFSUUID: + return ioctl_getfsuuid(filp, argp); + + case FS_IOC_GETFSSYSFSPATH: + return ioctl_get_fs_sysfs_path(filp, argp); + default: - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode) && !IS_ANON_FILE(inode)) return file_ioctl(filp, cmd, argp); break; } @@ -855,28 +582,29 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) { - struct fd f = fdget(fd); + CLASS(fd, f)(fd); int error; - if (!f.file) + if (fd_empty(f)) return -EBADF; - error = security_file_ioctl(f.file, cmd, arg); + error = security_file_ioctl(fd_file(f), cmd, arg); if (error) - goto out; + return error; - error = do_vfs_ioctl(f.file, fd, cmd, arg); + error = do_vfs_ioctl(fd_file(f), fd, cmd, arg); if (error == -ENOIOCTLCMD) - error = vfs_ioctl(f.file, cmd, arg); + error = vfs_ioctl(fd_file(f), cmd, arg); -out: - fdput(f); return error; } #ifdef CONFIG_COMPAT /** * compat_ptr_ioctl - generic implementation of .compat_ioctl file operation + * @file: The file to operate on. + * @cmd: The ioctl command number. + * @arg: The argument to the ioctl. * * This is not normally called as a function, but instead set in struct * file_operations as @@ -910,36 +638,35 @@ EXPORT_SYMBOL(compat_ptr_ioctl); COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, compat_ulong_t, arg) { - struct fd f = fdget(fd); + CLASS(fd, f)(fd); int error; - if (!f.file) + if (fd_empty(f)) return -EBADF; - /* RED-PEN how should LSM module know it's handling 32bit? */ - error = security_file_ioctl(f.file, cmd, arg); + error = security_file_ioctl_compat(fd_file(f), cmd, arg); if (error) - goto out; + return error; switch (cmd) { /* FICLONE takes an int argument, so don't use compat_ptr() */ case FICLONE: - error = ioctl_file_clone(f.file, arg, 0, 0, 0); + error = ioctl_file_clone(fd_file(f), arg, 0, 0, 0); break; #if defined(CONFIG_X86_64) /* these get messy on amd64 due to alignment differences */ case FS_IOC_RESVSP_32: case FS_IOC_RESVSP64_32: - error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg)); + error = compat_ioctl_preallocate(fd_file(f), 0, compat_ptr(arg)); break; case FS_IOC_UNRESVSP_32: case FS_IOC_UNRESVSP64_32: - error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE, + error = compat_ioctl_preallocate(fd_file(f), FALLOC_FL_PUNCH_HOLE, compat_ptr(arg)); break; case FS_IOC_ZERO_RANGE_32: - error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE, + error = compat_ioctl_preallocate(fd_file(f), FALLOC_FL_ZERO_RANGE, compat_ptr(arg)); break; #endif @@ -959,21 +686,17 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, * argument. */ default: - error = do_vfs_ioctl(f.file, fd, cmd, + error = do_vfs_ioctl(fd_file(f), fd, cmd, (unsigned long)compat_ptr(arg)); if (error != -ENOIOCTLCMD) break; - if (f.file->f_op->compat_ioctl) - error = f.file->f_op->compat_ioctl(f.file, cmd, arg); + if (fd_file(f)->f_op->compat_ioctl) + error = fd_file(f)->f_op->compat_ioctl(fd_file(f), cmd, arg); if (error == -ENOIOCTLCMD) error = -ENOTTY; break; } - - out: - fdput(f); - return error; } #endif |
