diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 188 | 
1 files changed, 103 insertions, 85 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 0ba98e08a029..41524f9aeac3 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -27,6 +27,7 @@  #include <linux/uaccess.h>  #include <linux/iversion.h>  #include <linux/fileattr.h> +#include <linux/fsverity.h>  #include "ctree.h"  #include "disk-io.h"  #include "export.h" @@ -103,9 +104,11 @@ static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode,   * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS   * ioctl.   */ -static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags) +static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode)  {  	unsigned int iflags = 0; +	u32 flags = binode->flags; +	u32 ro_flags = binode->ro_flags;  	if (flags & BTRFS_INODE_SYNC)  		iflags |= FS_SYNC_FL; @@ -121,6 +124,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags)  		iflags |= FS_DIRSYNC_FL;  	if (flags & BTRFS_INODE_NODATACOW)  		iflags |= FS_NOCOW_FL; +	if (ro_flags & BTRFS_INODE_RO_VERITY) +		iflags |= FS_VERITY_FL;  	if (flags & BTRFS_INODE_NOCOMPRESS)  		iflags |= FS_NOCOMP_FL; @@ -148,10 +153,12 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)  		new_fl |= S_NOATIME;  	if (binode->flags & BTRFS_INODE_DIRSYNC)  		new_fl |= S_DIRSYNC; +	if (binode->ro_flags & BTRFS_INODE_RO_VERITY) +		new_fl |= S_VERITY;  	set_mask_bits(&inode->i_flags, -		      S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC, -		      new_fl); +		      S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC | +		      S_VERITY, new_fl);  }  /* @@ -200,7 +207,7 @@ int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)  {  	struct btrfs_inode *binode = BTRFS_I(d_inode(dentry)); -	fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode->flags)); +	fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode));  	return 0;  } @@ -224,7 +231,7 @@ int btrfs_fileattr_set(struct user_namespace *mnt_userns,  		return -EOPNOTSUPP;  	fsflags = btrfs_mask_fsflags_for_type(inode, fa->flags); -	old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags); +	old_fsflags = btrfs_inode_flags_to_fsflags(binode);  	ret = check_fsflags(old_fsflags, fsflags);  	if (ret)  		return ret; @@ -492,8 +499,8 @@ int __pure btrfs_is_empty_uuid(u8 *uuid)  	return 1;  } -static noinline int create_subvol(struct inode *dir, -				  struct dentry *dentry, +static noinline int create_subvol(struct user_namespace *mnt_userns, +				  struct inode *dir, struct dentry *dentry,  				  const char *name, int namelen,  				  struct btrfs_qgroup_inherit *inherit)  { @@ -638,7 +645,7 @@ static noinline int create_subvol(struct inode *dir,  		goto fail;  	} -	ret = btrfs_create_subvol_root(trans, new_root, root); +	ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns);  	btrfs_put_root(new_root);  	if (ret) {  		/* We potentially lose an unused inode item here */ @@ -830,7 +837,8 @@ free_pending:   *     nfs_async_unlink().   */ -static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) +static int btrfs_may_delete(struct user_namespace *mnt_userns, +			    struct inode *dir, struct dentry *victim, int isdir)  {  	int error; @@ -840,12 +848,12 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)  	BUG_ON(d_inode(victim->d_parent) != dir);  	audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); -	error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); +	error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);  	if (error)  		return error;  	if (IS_APPEND(dir))  		return -EPERM; -	if (check_sticky(&init_user_ns, dir, d_inode(victim)) || +	if (check_sticky(mnt_userns, dir, d_inode(victim)) ||  	    IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) ||  	    IS_SWAPFILE(d_inode(victim)))  		return -EPERM; @@ -864,13 +872,16 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)  }  /* copy of may_create in fs/namei.c() */ -static inline int btrfs_may_create(struct inode *dir, struct dentry *child) +static inline int btrfs_may_create(struct user_namespace *mnt_userns, +				   struct inode *dir, struct dentry *child)  {  	if (d_really_is_positive(child))  		return -EEXIST;  	if (IS_DEADDIR(dir))  		return -ENOENT; -	return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); +	if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns)) +		return -EOVERFLOW; +	return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);  }  /* @@ -879,6 +890,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)   * inside this filesystem so it's quite a bit simpler.   */  static noinline int btrfs_mksubvol(const struct path *parent, +				   struct user_namespace *mnt_userns,  				   const char *name, int namelen,  				   struct btrfs_root *snap_src,  				   bool readonly, @@ -893,12 +905,12 @@ static noinline int btrfs_mksubvol(const struct path *parent,  	if (error == -EINTR)  		return error; -	dentry = lookup_one_len(name, parent->dentry, namelen); +	dentry = lookup_one(mnt_userns, name, parent->dentry, namelen);  	error = PTR_ERR(dentry);  	if (IS_ERR(dentry))  		goto out_unlock; -	error = btrfs_may_create(dir, dentry); +	error = btrfs_may_create(mnt_userns, dir, dentry);  	if (error)  		goto out_dput; @@ -920,7 +932,7 @@ static noinline int btrfs_mksubvol(const struct path *parent,  	if (snap_src)  		error = create_snapshot(snap_src, dir, dentry, readonly, inherit);  	else -		error = create_subvol(dir, dentry, name, namelen, inherit); +		error = create_subvol(mnt_userns, dir, dentry, name, namelen, inherit);  	if (!error)  		fsnotify_mkdir(dir, dentry); @@ -934,6 +946,7 @@ out_unlock:  }  static noinline int btrfs_mksnapshot(const struct path *parent, +				   struct user_namespace *mnt_userns,  				   const char *name, int namelen,  				   struct btrfs_root *root,  				   bool readonly, @@ -963,7 +976,7 @@ static noinline int btrfs_mksnapshot(const struct path *parent,  	btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); -	ret = btrfs_mksubvol(parent, name, namelen, +	ret = btrfs_mksubvol(parent, mnt_userns, name, namelen,  			     root, readonly, inherit);  out:  	if (snapshot_force_cow) @@ -1792,6 +1805,7 @@ out_drop:  }  static noinline int __btrfs_ioctl_snap_create(struct file *file, +				struct user_namespace *mnt_userns,  				const char *name, unsigned long fd, int subvol,  				bool readonly,  				struct btrfs_qgroup_inherit *inherit) @@ -1819,8 +1833,8 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file,  	}  	if (subvol) { -		ret = btrfs_mksubvol(&file->f_path, name, namelen, -				     NULL, readonly, inherit); +		ret = btrfs_mksubvol(&file->f_path, mnt_userns, name, +				     namelen, NULL, readonly, inherit);  	} else {  		struct fd src = fdget(fd);  		struct inode *src_inode; @@ -1834,16 +1848,17 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file,  			btrfs_info(BTRFS_I(file_inode(file))->root->fs_info,  				   "Snapshot src from another FS");  			ret = -EXDEV; -		} else if (!inode_owner_or_capable(&init_user_ns, src_inode)) { +		} else if (!inode_owner_or_capable(mnt_userns, src_inode)) {  			/*  			 * Subvolume creation is not restricted, but snapshots  			 * are limited to own subvolumes only  			 */  			ret = -EPERM;  		} else { -			ret = btrfs_mksnapshot(&file->f_path, name, namelen, -					     BTRFS_I(src_inode)->root, -					     readonly, inherit); +			ret = btrfs_mksnapshot(&file->f_path, mnt_userns, +					       name, namelen, +					       BTRFS_I(src_inode)->root, +					       readonly, inherit);  		}  		fdput(src);  	} @@ -1867,8 +1882,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,  		return PTR_ERR(vol_args);  	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; -	ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, -					subvol, false, NULL); +	ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), +					vol_args->name, vol_args->fd, subvol, +					false, NULL);  	kfree(vol_args);  	return ret; @@ -1926,8 +1942,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,  		}  	} -	ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, -					subvol, readonly, inherit); +	ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), +					vol_args->name, vol_args->fd, subvol, +					readonly, inherit);  	if (ret)  		goto free_inherit;  free_inherit: @@ -1971,7 +1988,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,  	u64 flags;  	int ret = 0; -	if (!inode_owner_or_capable(&init_user_ns, inode)) +	if (!inode_owner_or_capable(file_mnt_user_ns(file), inode))  		return -EPERM;  	ret = mnt_want_write_file(file); @@ -2382,23 +2399,16 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,  	key.offset = (u64)-1;  	while (1) { -		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); +		ret = btrfs_search_backwards(root, &key, path);  		if (ret < 0)  			goto out;  		else if (ret > 0) { -			ret = btrfs_previous_item(root, path, dirid, -						  BTRFS_INODE_REF_KEY); -			if (ret < 0) -				goto out; -			else if (ret > 0) { -				ret = -ENOENT; -				goto out; -			} +			ret = -ENOENT; +			goto out;  		}  		l = path->nodes[0];  		slot = path->slots[0]; -		btrfs_item_key_to_cpu(l, &key, slot);  		iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref);  		len = btrfs_inode_ref_name_len(l, iref); @@ -2429,7 +2439,8 @@ out:  	return ret;  } -static int btrfs_search_path_in_tree_user(struct inode *inode, +static int btrfs_search_path_in_tree_user(struct user_namespace *mnt_userns, +				struct inode *inode,  				struct btrfs_ioctl_ino_lookup_user_args *args)  {  	struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; @@ -2473,23 +2484,16 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,  		key.type = BTRFS_INODE_REF_KEY;  		key.offset = (u64)-1;  		while (1) { -			ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); -			if (ret < 0) { +			ret = btrfs_search_backwards(root, &key, path); +			if (ret < 0) +				goto out_put; +			else if (ret > 0) { +				ret = -ENOENT;  				goto out_put; -			} else if (ret > 0) { -				ret = btrfs_previous_item(root, path, dirid, -							  BTRFS_INODE_REF_KEY); -				if (ret < 0) { -					goto out_put; -				} else if (ret > 0) { -					ret = -ENOENT; -					goto out_put; -				}  			}  			leaf = path->nodes[0];  			slot = path->slots[0]; -			btrfs_item_key_to_cpu(leaf, &key, slot);  			iref = btrfs_item_ptr(leaf, slot, struct btrfs_inode_ref);  			len = btrfs_inode_ref_name_len(leaf, iref); @@ -2527,7 +2531,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,  				ret = PTR_ERR(temp_inode);  				goto out_put;  			} -			ret = inode_permission(&init_user_ns, temp_inode, +			ret = inode_permission(mnt_userns, temp_inode,  					       MAY_READ | MAY_EXEC);  			iput(temp_inode);  			if (ret) { @@ -2669,7 +2673,7 @@ static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp)  		return -EACCES;  	} -	ret = btrfs_search_path_in_tree_user(inode, args); +	ret = btrfs_search_path_in_tree_user(file_mnt_user_ns(file), inode, args);  	if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))  		ret = -EFAULT; @@ -2905,6 +2909,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  	struct btrfs_root *dest = NULL;  	struct btrfs_ioctl_vol_args *vol_args = NULL;  	struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL; +	struct user_namespace *mnt_userns = file_mnt_user_ns(file);  	char *subvol_name, *subvol_name_ptr = NULL;  	int subvol_namelen;  	int err = 0; @@ -2932,6 +2937,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  			if (err)  				goto out;  		} else { +			struct inode *old_dir; +  			if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) {  				err = -EINVAL;  				goto out; @@ -2968,6 +2975,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  				err = PTR_ERR(parent);  				goto out_drop_write;  			} +			old_dir = dir;  			dir = d_inode(parent);  			/* @@ -2978,6 +2986,20 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  			 */  			destroy_parent = true; +			/* +			 * On idmapped mounts, deletion via subvolid is +			 * restricted to subvolumes that are immediate +			 * ancestors of the inode referenced by the file +			 * descriptor in the ioctl. Otherwise the idmapping +			 * could potentially be abused to delete subvolumes +			 * anywhere in the filesystem the user wouldn't be able +			 * to delete without an idmapped mount. +			 */ +			if (old_dir != dir && mnt_userns != &init_user_ns) { +				err = -EOPNOTSUPP; +				goto free_parent; +			} +  			subvol_name_ptr = btrfs_get_subvol_name_from_objectid(  						fs_info, vol_args2->subvolid);  			if (IS_ERR(subvol_name_ptr)) { @@ -3016,7 +3038,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  	err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);  	if (err == -EINTR)  		goto free_subvol_name; -	dentry = lookup_one_len(subvol_name, parent, subvol_namelen); +	dentry = lookup_one(mnt_userns, subvol_name, parent, subvol_namelen);  	if (IS_ERR(dentry)) {  		err = PTR_ERR(dentry);  		goto out_unlock_dir; @@ -3058,14 +3080,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  		if (root == dest)  			goto out_dput; -		err = inode_permission(&init_user_ns, inode, -				       MAY_WRITE | MAY_EXEC); +		err = inode_permission(mnt_userns, inode, MAY_WRITE | MAY_EXEC);  		if (err)  			goto out_dput;  	}  	/* check if subvolume may be deleted by a user */ -	err = btrfs_may_delete(dir, dentry, 1); +	err = btrfs_may_delete(mnt_userns, dir, dentry, 1);  	if (err)  		goto out_dput; @@ -3103,7 +3124,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  {  	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root; -	struct btrfs_ioctl_defrag_range_args *range; +	struct btrfs_ioctl_defrag_range_args range = {0};  	int ret;  	ret = mnt_want_write_file(file); @@ -3115,6 +3136,12 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  		goto out;  	} +	/* Subpage defrag will be supported in later commits */ +	if (root->fs_info->sectorsize < PAGE_SIZE) { +		ret = -ENOTTY; +		goto out; +	} +  	switch (inode->i_mode & S_IFMT) {  	case S_IFDIR:  		if (!capable(CAP_SYS_ADMIN)) { @@ -3135,33 +3162,24 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  			goto out;  		} -		range = kzalloc(sizeof(*range), GFP_KERNEL); -		if (!range) { -			ret = -ENOMEM; -			goto out; -		} -  		if (argp) { -			if (copy_from_user(range, argp, -					   sizeof(*range))) { +			if (copy_from_user(&range, argp, sizeof(range))) {  				ret = -EFAULT; -				kfree(range);  				goto out;  			}  			/* compression requires us to start the IO */ -			if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) { -				range->flags |= BTRFS_DEFRAG_RANGE_START_IO; -				range->extent_thresh = (u32)-1; +			if ((range.flags & BTRFS_DEFRAG_RANGE_COMPRESS)) { +				range.flags |= BTRFS_DEFRAG_RANGE_START_IO; +				range.extent_thresh = (u32)-1;  			}  		} else {  			/* the rest are all set to zero by kzalloc */ -			range->len = (u64)-1; +			range.len = (u64)-1;  		}  		ret = btrfs_defrag_file(file_inode(file), file, -					range, BTRFS_OLDEST_GENERATION, 0); +					&range, BTRFS_OLDEST_GENERATION, 0);  		if (ret > 0)  			ret = 0; -		kfree(range);  		break;  	default:  		ret = -EINVAL; @@ -4404,25 +4422,20 @@ drop_write:  static long btrfs_ioctl_quota_rescan_status(struct btrfs_fs_info *fs_info,  						void __user *arg)  { -	struct btrfs_ioctl_quota_rescan_args *qsa; +	struct btrfs_ioctl_quota_rescan_args qsa = {0};  	int ret = 0;  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	qsa = kzalloc(sizeof(*qsa), GFP_KERNEL); -	if (!qsa) -		return -ENOMEM; -  	if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) { -		qsa->flags = 1; -		qsa->progress = fs_info->qgroup_rescan_progress.objectid; +		qsa.flags = 1; +		qsa.progress = fs_info->qgroup_rescan_progress.objectid;  	} -	if (copy_to_user(arg, qsa, sizeof(*qsa))) +	if (copy_to_user(arg, &qsa, sizeof(qsa)))  		ret = -EFAULT; -	kfree(qsa);  	return ret;  } @@ -4436,6 +4449,7 @@ static long btrfs_ioctl_quota_rescan_wait(struct btrfs_fs_info *fs_info,  }  static long _btrfs_ioctl_set_received_subvol(struct file *file, +					    struct user_namespace *mnt_userns,  					    struct btrfs_ioctl_received_subvol_args *sa)  {  	struct inode *inode = file_inode(file); @@ -4447,7 +4461,7 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file,  	int ret = 0;  	int received_uuid_changed; -	if (!inode_owner_or_capable(&init_user_ns, inode)) +	if (!inode_owner_or_capable(mnt_userns, inode))  		return -EPERM;  	ret = mnt_want_write_file(file); @@ -4552,7 +4566,7 @@ static long btrfs_ioctl_set_received_subvol_32(struct file *file,  	args64->rtime.nsec = args32->rtime.nsec;  	args64->flags = args32->flags; -	ret = _btrfs_ioctl_set_received_subvol(file, args64); +	ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), args64);  	if (ret)  		goto out; @@ -4586,7 +4600,7 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,  	if (IS_ERR(sa))  		return PTR_ERR(sa); -	ret = _btrfs_ioctl_set_received_subvol(file, sa); +	ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), sa);  	if (ret)  		goto out; @@ -5013,6 +5027,10 @@ long btrfs_ioctl(struct file *file, unsigned int  		return btrfs_ioctl_get_subvol_rootref(file, argp);  	case BTRFS_IOC_INO_LOOKUP_USER:  		return btrfs_ioctl_ino_lookup_user(file, argp); +	case FS_IOC_ENABLE_VERITY: +		return fsverity_ioctl_enable(file, (const void __user *)argp); +	case FS_IOC_MEASURE_VERITY: +		return fsverity_ioctl_measure(file, argp);  	}  	return -ENOTTY;  | 
