18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Oracle.  All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/bio.h>
88c2ecf20Sopenharmony_ci#include <linux/file.h>
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/fsnotify.h>
118c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
128c2ecf20Sopenharmony_ci#include <linux/highmem.h>
138c2ecf20Sopenharmony_ci#include <linux/time.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/backing-dev.h>
168c2ecf20Sopenharmony_ci#include <linux/mount.h>
178c2ecf20Sopenharmony_ci#include <linux/namei.h>
188c2ecf20Sopenharmony_ci#include <linux/writeback.h>
198c2ecf20Sopenharmony_ci#include <linux/compat.h>
208c2ecf20Sopenharmony_ci#include <linux/security.h>
218c2ecf20Sopenharmony_ci#include <linux/xattr.h>
228c2ecf20Sopenharmony_ci#include <linux/mm.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
258c2ecf20Sopenharmony_ci#include <linux/uuid.h>
268c2ecf20Sopenharmony_ci#include <linux/btrfs.h>
278c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
288c2ecf20Sopenharmony_ci#include <linux/iversion.h>
298c2ecf20Sopenharmony_ci#include "ctree.h"
308c2ecf20Sopenharmony_ci#include "disk-io.h"
318c2ecf20Sopenharmony_ci#include "export.h"
328c2ecf20Sopenharmony_ci#include "transaction.h"
338c2ecf20Sopenharmony_ci#include "btrfs_inode.h"
348c2ecf20Sopenharmony_ci#include "print-tree.h"
358c2ecf20Sopenharmony_ci#include "volumes.h"
368c2ecf20Sopenharmony_ci#include "locking.h"
378c2ecf20Sopenharmony_ci#include "inode-map.h"
388c2ecf20Sopenharmony_ci#include "backref.h"
398c2ecf20Sopenharmony_ci#include "rcu-string.h"
408c2ecf20Sopenharmony_ci#include "send.h"
418c2ecf20Sopenharmony_ci#include "dev-replace.h"
428c2ecf20Sopenharmony_ci#include "props.h"
438c2ecf20Sopenharmony_ci#include "sysfs.h"
448c2ecf20Sopenharmony_ci#include "qgroup.h"
458c2ecf20Sopenharmony_ci#include "tree-log.h"
468c2ecf20Sopenharmony_ci#include "compression.h"
478c2ecf20Sopenharmony_ci#include "space-info.h"
488c2ecf20Sopenharmony_ci#include "delalloc-space.h"
498c2ecf20Sopenharmony_ci#include "block-group.h"
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
528c2ecf20Sopenharmony_ci/* If we have a 32-bit userspace and 64-bit kernel, then the UAPI
538c2ecf20Sopenharmony_ci * structures are incorrect, as the timespec structure from userspace
548c2ecf20Sopenharmony_ci * is 4 bytes too small. We define these alternatives here to teach
558c2ecf20Sopenharmony_ci * the kernel about the 32-bit struct packing.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistruct btrfs_ioctl_timespec_32 {
588c2ecf20Sopenharmony_ci	__u64 sec;
598c2ecf20Sopenharmony_ci	__u32 nsec;
608c2ecf20Sopenharmony_ci} __attribute__ ((__packed__));
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistruct btrfs_ioctl_received_subvol_args_32 {
638c2ecf20Sopenharmony_ci	char	uuid[BTRFS_UUID_SIZE];	/* in */
648c2ecf20Sopenharmony_ci	__u64	stransid;		/* in */
658c2ecf20Sopenharmony_ci	__u64	rtransid;		/* out */
668c2ecf20Sopenharmony_ci	struct btrfs_ioctl_timespec_32 stime; /* in */
678c2ecf20Sopenharmony_ci	struct btrfs_ioctl_timespec_32 rtime; /* out */
688c2ecf20Sopenharmony_ci	__u64	flags;			/* in */
698c2ecf20Sopenharmony_ci	__u64	reserved[16];		/* in */
708c2ecf20Sopenharmony_ci} __attribute__ ((__packed__));
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \
738c2ecf20Sopenharmony_ci				struct btrfs_ioctl_received_subvol_args_32)
748c2ecf20Sopenharmony_ci#endif
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
778c2ecf20Sopenharmony_cistruct btrfs_ioctl_send_args_32 {
788c2ecf20Sopenharmony_ci	__s64 send_fd;			/* in */
798c2ecf20Sopenharmony_ci	__u64 clone_sources_count;	/* in */
808c2ecf20Sopenharmony_ci	compat_uptr_t clone_sources;	/* in */
818c2ecf20Sopenharmony_ci	__u64 parent_root;		/* in */
828c2ecf20Sopenharmony_ci	__u64 flags;			/* in */
838c2ecf20Sopenharmony_ci	__u64 reserved[4];		/* in */
848c2ecf20Sopenharmony_ci} __attribute__ ((__packed__));
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define BTRFS_IOC_SEND_32 _IOW(BTRFS_IOCTL_MAGIC, 38, \
878c2ecf20Sopenharmony_ci			       struct btrfs_ioctl_send_args_32)
888c2ecf20Sopenharmony_ci#endif
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* Mask out flags that are inappropriate for the given type of inode. */
918c2ecf20Sopenharmony_cistatic unsigned int btrfs_mask_fsflags_for_type(struct inode *inode,
928c2ecf20Sopenharmony_ci		unsigned int flags)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
958c2ecf20Sopenharmony_ci		return flags;
968c2ecf20Sopenharmony_ci	else if (S_ISREG(inode->i_mode))
978c2ecf20Sopenharmony_ci		return flags & ~FS_DIRSYNC_FL;
988c2ecf20Sopenharmony_ci	else
998c2ecf20Sopenharmony_ci		return flags & (FS_NODUMP_FL | FS_NOATIME_FL);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS
1048c2ecf20Sopenharmony_ci * ioctl.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistatic unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	unsigned int iflags = 0;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_SYNC)
1118c2ecf20Sopenharmony_ci		iflags |= FS_SYNC_FL;
1128c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_IMMUTABLE)
1138c2ecf20Sopenharmony_ci		iflags |= FS_IMMUTABLE_FL;
1148c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_APPEND)
1158c2ecf20Sopenharmony_ci		iflags |= FS_APPEND_FL;
1168c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_NODUMP)
1178c2ecf20Sopenharmony_ci		iflags |= FS_NODUMP_FL;
1188c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_NOATIME)
1198c2ecf20Sopenharmony_ci		iflags |= FS_NOATIME_FL;
1208c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_DIRSYNC)
1218c2ecf20Sopenharmony_ci		iflags |= FS_DIRSYNC_FL;
1228c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_NODATACOW)
1238c2ecf20Sopenharmony_ci		iflags |= FS_NOCOW_FL;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_NOCOMPRESS)
1268c2ecf20Sopenharmony_ci		iflags |= FS_NOCOMP_FL;
1278c2ecf20Sopenharmony_ci	else if (flags & BTRFS_INODE_COMPRESS)
1288c2ecf20Sopenharmony_ci		iflags |= FS_COMPR_FL;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return iflags;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/*
1348c2ecf20Sopenharmony_ci * Update inode->i_flags based on the btrfs internal flags.
1358c2ecf20Sopenharmony_ci */
1368c2ecf20Sopenharmony_civoid btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct btrfs_inode *binode = BTRFS_I(inode);
1398c2ecf20Sopenharmony_ci	unsigned int new_fl = 0;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (binode->flags & BTRFS_INODE_SYNC)
1428c2ecf20Sopenharmony_ci		new_fl |= S_SYNC;
1438c2ecf20Sopenharmony_ci	if (binode->flags & BTRFS_INODE_IMMUTABLE)
1448c2ecf20Sopenharmony_ci		new_fl |= S_IMMUTABLE;
1458c2ecf20Sopenharmony_ci	if (binode->flags & BTRFS_INODE_APPEND)
1468c2ecf20Sopenharmony_ci		new_fl |= S_APPEND;
1478c2ecf20Sopenharmony_ci	if (binode->flags & BTRFS_INODE_NOATIME)
1488c2ecf20Sopenharmony_ci		new_fl |= S_NOATIME;
1498c2ecf20Sopenharmony_ci	if (binode->flags & BTRFS_INODE_DIRSYNC)
1508c2ecf20Sopenharmony_ci		new_fl |= S_DIRSYNC;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	set_mask_bits(&inode->i_flags,
1538c2ecf20Sopenharmony_ci		      S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC,
1548c2ecf20Sopenharmony_ci		      new_fl);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int btrfs_ioctl_getflags(struct file *file, void __user *arg)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct btrfs_inode *binode = BTRFS_I(file_inode(file));
1608c2ecf20Sopenharmony_ci	unsigned int flags = btrfs_inode_flags_to_fsflags(binode->flags);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &flags, sizeof(flags)))
1638c2ecf20Sopenharmony_ci		return -EFAULT;
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/*
1688c2ecf20Sopenharmony_ci * Check if @flags are a supported and valid set of FS_*_FL flags and that
1698c2ecf20Sopenharmony_ci * the old and new flags are not conflicting
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cistatic int check_fsflags(unsigned int old_flags, unsigned int flags)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
1748c2ecf20Sopenharmony_ci		      FS_NOATIME_FL | FS_NODUMP_FL | \
1758c2ecf20Sopenharmony_ci		      FS_SYNC_FL | FS_DIRSYNC_FL | \
1768c2ecf20Sopenharmony_ci		      FS_NOCOMP_FL | FS_COMPR_FL |
1778c2ecf20Sopenharmony_ci		      FS_NOCOW_FL))
1788c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* COMPR and NOCOMP on new/old are valid */
1818c2ecf20Sopenharmony_ci	if ((flags & FS_NOCOMP_FL) && (flags & FS_COMPR_FL))
1828c2ecf20Sopenharmony_ci		return -EINVAL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if ((flags & FS_COMPR_FL) && (flags & FS_NOCOW_FL))
1858c2ecf20Sopenharmony_ci		return -EINVAL;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* NOCOW and compression options are mutually exclusive */
1888c2ecf20Sopenharmony_ci	if ((old_flags & FS_NOCOW_FL) && (flags & (FS_COMPR_FL | FS_NOCOMP_FL)))
1898c2ecf20Sopenharmony_ci		return -EINVAL;
1908c2ecf20Sopenharmony_ci	if ((flags & FS_NOCOW_FL) && (old_flags & (FS_COMPR_FL | FS_NOCOMP_FL)))
1918c2ecf20Sopenharmony_ci		return -EINVAL;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int btrfs_ioctl_setflags(struct file *file, void __user *arg)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
1998c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
2008c2ecf20Sopenharmony_ci	struct btrfs_inode *binode = BTRFS_I(inode);
2018c2ecf20Sopenharmony_ci	struct btrfs_root *root = binode->root;
2028c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
2038c2ecf20Sopenharmony_ci	unsigned int fsflags, old_fsflags;
2048c2ecf20Sopenharmony_ci	int ret;
2058c2ecf20Sopenharmony_ci	const char *comp = NULL;
2068c2ecf20Sopenharmony_ci	u32 binode_flags;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (!inode_owner_or_capable(inode))
2098c2ecf20Sopenharmony_ci		return -EPERM;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (btrfs_root_readonly(root))
2128c2ecf20Sopenharmony_ci		return -EROFS;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (copy_from_user(&fsflags, arg, sizeof(fsflags)))
2158c2ecf20Sopenharmony_ci		return -EFAULT;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
2188c2ecf20Sopenharmony_ci	if (ret)
2198c2ecf20Sopenharmony_ci		return ret;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	inode_lock(inode);
2228c2ecf20Sopenharmony_ci	fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
2238c2ecf20Sopenharmony_ci	old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags);
2268c2ecf20Sopenharmony_ci	if (ret)
2278c2ecf20Sopenharmony_ci		goto out_unlock;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	ret = check_fsflags(old_fsflags, fsflags);
2308c2ecf20Sopenharmony_ci	if (ret)
2318c2ecf20Sopenharmony_ci		goto out_unlock;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	binode_flags = binode->flags;
2348c2ecf20Sopenharmony_ci	if (fsflags & FS_SYNC_FL)
2358c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_SYNC;
2368c2ecf20Sopenharmony_ci	else
2378c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_SYNC;
2388c2ecf20Sopenharmony_ci	if (fsflags & FS_IMMUTABLE_FL)
2398c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_IMMUTABLE;
2408c2ecf20Sopenharmony_ci	else
2418c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_IMMUTABLE;
2428c2ecf20Sopenharmony_ci	if (fsflags & FS_APPEND_FL)
2438c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_APPEND;
2448c2ecf20Sopenharmony_ci	else
2458c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_APPEND;
2468c2ecf20Sopenharmony_ci	if (fsflags & FS_NODUMP_FL)
2478c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_NODUMP;
2488c2ecf20Sopenharmony_ci	else
2498c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_NODUMP;
2508c2ecf20Sopenharmony_ci	if (fsflags & FS_NOATIME_FL)
2518c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_NOATIME;
2528c2ecf20Sopenharmony_ci	else
2538c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_NOATIME;
2548c2ecf20Sopenharmony_ci	if (fsflags & FS_DIRSYNC_FL)
2558c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_DIRSYNC;
2568c2ecf20Sopenharmony_ci	else
2578c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_DIRSYNC;
2588c2ecf20Sopenharmony_ci	if (fsflags & FS_NOCOW_FL) {
2598c2ecf20Sopenharmony_ci		if (S_ISREG(inode->i_mode)) {
2608c2ecf20Sopenharmony_ci			/*
2618c2ecf20Sopenharmony_ci			 * It's safe to turn csums off here, no extents exist.
2628c2ecf20Sopenharmony_ci			 * Otherwise we want the flag to reflect the real COW
2638c2ecf20Sopenharmony_ci			 * status of the file and will not set it.
2648c2ecf20Sopenharmony_ci			 */
2658c2ecf20Sopenharmony_ci			if (inode->i_size == 0)
2668c2ecf20Sopenharmony_ci				binode_flags |= BTRFS_INODE_NODATACOW |
2678c2ecf20Sopenharmony_ci						BTRFS_INODE_NODATASUM;
2688c2ecf20Sopenharmony_ci		} else {
2698c2ecf20Sopenharmony_ci			binode_flags |= BTRFS_INODE_NODATACOW;
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci	} else {
2728c2ecf20Sopenharmony_ci		/*
2738c2ecf20Sopenharmony_ci		 * Revert back under same assumptions as above
2748c2ecf20Sopenharmony_ci		 */
2758c2ecf20Sopenharmony_ci		if (S_ISREG(inode->i_mode)) {
2768c2ecf20Sopenharmony_ci			if (inode->i_size == 0)
2778c2ecf20Sopenharmony_ci				binode_flags &= ~(BTRFS_INODE_NODATACOW |
2788c2ecf20Sopenharmony_ci						  BTRFS_INODE_NODATASUM);
2798c2ecf20Sopenharmony_ci		} else {
2808c2ecf20Sopenharmony_ci			binode_flags &= ~BTRFS_INODE_NODATACOW;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/*
2858c2ecf20Sopenharmony_ci	 * The COMPRESS flag can only be changed by users, while the NOCOMPRESS
2868c2ecf20Sopenharmony_ci	 * flag may be changed automatically if compression code won't make
2878c2ecf20Sopenharmony_ci	 * things smaller.
2888c2ecf20Sopenharmony_ci	 */
2898c2ecf20Sopenharmony_ci	if (fsflags & FS_NOCOMP_FL) {
2908c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_COMPRESS;
2918c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_NOCOMPRESS;
2928c2ecf20Sopenharmony_ci	} else if (fsflags & FS_COMPR_FL) {
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		if (IS_SWAPFILE(inode)) {
2958c2ecf20Sopenharmony_ci			ret = -ETXTBSY;
2968c2ecf20Sopenharmony_ci			goto out_unlock;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		binode_flags |= BTRFS_INODE_COMPRESS;
3008c2ecf20Sopenharmony_ci		binode_flags &= ~BTRFS_INODE_NOCOMPRESS;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		comp = btrfs_compress_type2str(fs_info->compress_type);
3038c2ecf20Sopenharmony_ci		if (!comp || comp[0] == 0)
3048c2ecf20Sopenharmony_ci			comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB);
3058c2ecf20Sopenharmony_ci	} else {
3068c2ecf20Sopenharmony_ci		binode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/*
3108c2ecf20Sopenharmony_ci	 * 1 for inode item
3118c2ecf20Sopenharmony_ci	 * 2 for properties
3128c2ecf20Sopenharmony_ci	 */
3138c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 3);
3148c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
3158c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
3168c2ecf20Sopenharmony_ci		goto out_unlock;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (comp) {
3208c2ecf20Sopenharmony_ci		ret = btrfs_set_prop(trans, inode, "btrfs.compression", comp,
3218c2ecf20Sopenharmony_ci				     strlen(comp), 0);
3228c2ecf20Sopenharmony_ci		if (ret) {
3238c2ecf20Sopenharmony_ci			btrfs_abort_transaction(trans, ret);
3248c2ecf20Sopenharmony_ci			goto out_end_trans;
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci	} else {
3278c2ecf20Sopenharmony_ci		ret = btrfs_set_prop(trans, inode, "btrfs.compression", NULL,
3288c2ecf20Sopenharmony_ci				     0, 0);
3298c2ecf20Sopenharmony_ci		if (ret && ret != -ENODATA) {
3308c2ecf20Sopenharmony_ci			btrfs_abort_transaction(trans, ret);
3318c2ecf20Sopenharmony_ci			goto out_end_trans;
3328c2ecf20Sopenharmony_ci		}
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	binode->flags = binode_flags;
3368c2ecf20Sopenharmony_ci	btrfs_sync_inode_flags_to_i_flags(inode);
3378c2ecf20Sopenharmony_ci	inode_inc_iversion(inode);
3388c2ecf20Sopenharmony_ci	inode->i_ctime = current_time(inode);
3398c2ecf20Sopenharmony_ci	ret = btrfs_update_inode(trans, root, inode);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci out_end_trans:
3428c2ecf20Sopenharmony_ci	btrfs_end_transaction(trans);
3438c2ecf20Sopenharmony_ci out_unlock:
3448c2ecf20Sopenharmony_ci	inode_unlock(inode);
3458c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
3468c2ecf20Sopenharmony_ci	return ret;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/*
3508c2ecf20Sopenharmony_ci * Translate btrfs internal inode flags to xflags as expected by the
3518c2ecf20Sopenharmony_ci * FS_IOC_FSGETXATT ioctl. Filter only the supported ones, unknown flags are
3528c2ecf20Sopenharmony_ci * silently dropped.
3538c2ecf20Sopenharmony_ci */
3548c2ecf20Sopenharmony_cistatic unsigned int btrfs_inode_flags_to_xflags(unsigned int flags)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	unsigned int xflags = 0;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_APPEND)
3598c2ecf20Sopenharmony_ci		xflags |= FS_XFLAG_APPEND;
3608c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_IMMUTABLE)
3618c2ecf20Sopenharmony_ci		xflags |= FS_XFLAG_IMMUTABLE;
3628c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_NOATIME)
3638c2ecf20Sopenharmony_ci		xflags |= FS_XFLAG_NOATIME;
3648c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_NODUMP)
3658c2ecf20Sopenharmony_ci		xflags |= FS_XFLAG_NODUMP;
3668c2ecf20Sopenharmony_ci	if (flags & BTRFS_INODE_SYNC)
3678c2ecf20Sopenharmony_ci		xflags |= FS_XFLAG_SYNC;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return xflags;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci/* Check if @flags are a supported and valid set of FS_XFLAGS_* flags */
3738c2ecf20Sopenharmony_cistatic int check_xflags(unsigned int flags)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	if (flags & ~(FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE | FS_XFLAG_NOATIME |
3768c2ecf20Sopenharmony_ci		      FS_XFLAG_NODUMP | FS_XFLAG_SYNC))
3778c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cibool btrfs_exclop_start(struct btrfs_fs_info *fs_info,
3828c2ecf20Sopenharmony_ci			enum btrfs_exclusive_operation type)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	return !cmpxchg(&fs_info->exclusive_operation, BTRFS_EXCLOP_NONE, type);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_civoid btrfs_exclop_finish(struct btrfs_fs_info *fs_info)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	WRITE_ONCE(fs_info->exclusive_operation, BTRFS_EXCLOP_NONE);
3908c2ecf20Sopenharmony_ci	sysfs_notify(&fs_info->fs_devices->fsid_kobj, NULL, "exclusive_operation");
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci/*
3948c2ecf20Sopenharmony_ci * Set the xflags from the internal inode flags. The remaining items of fsxattr
3958c2ecf20Sopenharmony_ci * are zeroed.
3968c2ecf20Sopenharmony_ci */
3978c2ecf20Sopenharmony_cistatic int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct btrfs_inode *binode = BTRFS_I(file_inode(file));
4008c2ecf20Sopenharmony_ci	struct fsxattr fa;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	simple_fill_fsxattr(&fa, btrfs_inode_flags_to_xflags(binode->flags));
4038c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &fa, sizeof(fa)))
4048c2ecf20Sopenharmony_ci		return -EFAULT;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
4128c2ecf20Sopenharmony_ci	struct btrfs_inode *binode = BTRFS_I(inode);
4138c2ecf20Sopenharmony_ci	struct btrfs_root *root = binode->root;
4148c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
4158c2ecf20Sopenharmony_ci	struct fsxattr fa, old_fa;
4168c2ecf20Sopenharmony_ci	unsigned old_flags;
4178c2ecf20Sopenharmony_ci	unsigned old_i_flags;
4188c2ecf20Sopenharmony_ci	int ret = 0;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (!inode_owner_or_capable(inode))
4218c2ecf20Sopenharmony_ci		return -EPERM;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (btrfs_root_readonly(root))
4248c2ecf20Sopenharmony_ci		return -EROFS;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (copy_from_user(&fa, arg, sizeof(fa)))
4278c2ecf20Sopenharmony_ci		return -EFAULT;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ret = check_xflags(fa.fsx_xflags);
4308c2ecf20Sopenharmony_ci	if (ret)
4318c2ecf20Sopenharmony_ci		return ret;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (fa.fsx_extsize != 0 || fa.fsx_projid != 0 || fa.fsx_cowextsize != 0)
4348c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
4378c2ecf20Sopenharmony_ci	if (ret)
4388c2ecf20Sopenharmony_ci		return ret;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	inode_lock(inode);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	old_flags = binode->flags;
4438c2ecf20Sopenharmony_ci	old_i_flags = inode->i_flags;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	simple_fill_fsxattr(&old_fa,
4468c2ecf20Sopenharmony_ci			    btrfs_inode_flags_to_xflags(binode->flags));
4478c2ecf20Sopenharmony_ci	ret = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
4488c2ecf20Sopenharmony_ci	if (ret)
4498c2ecf20Sopenharmony_ci		goto out_unlock;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (fa.fsx_xflags & FS_XFLAG_SYNC)
4528c2ecf20Sopenharmony_ci		binode->flags |= BTRFS_INODE_SYNC;
4538c2ecf20Sopenharmony_ci	else
4548c2ecf20Sopenharmony_ci		binode->flags &= ~BTRFS_INODE_SYNC;
4558c2ecf20Sopenharmony_ci	if (fa.fsx_xflags & FS_XFLAG_IMMUTABLE)
4568c2ecf20Sopenharmony_ci		binode->flags |= BTRFS_INODE_IMMUTABLE;
4578c2ecf20Sopenharmony_ci	else
4588c2ecf20Sopenharmony_ci		binode->flags &= ~BTRFS_INODE_IMMUTABLE;
4598c2ecf20Sopenharmony_ci	if (fa.fsx_xflags & FS_XFLAG_APPEND)
4608c2ecf20Sopenharmony_ci		binode->flags |= BTRFS_INODE_APPEND;
4618c2ecf20Sopenharmony_ci	else
4628c2ecf20Sopenharmony_ci		binode->flags &= ~BTRFS_INODE_APPEND;
4638c2ecf20Sopenharmony_ci	if (fa.fsx_xflags & FS_XFLAG_NODUMP)
4648c2ecf20Sopenharmony_ci		binode->flags |= BTRFS_INODE_NODUMP;
4658c2ecf20Sopenharmony_ci	else
4668c2ecf20Sopenharmony_ci		binode->flags &= ~BTRFS_INODE_NODUMP;
4678c2ecf20Sopenharmony_ci	if (fa.fsx_xflags & FS_XFLAG_NOATIME)
4688c2ecf20Sopenharmony_ci		binode->flags |= BTRFS_INODE_NOATIME;
4698c2ecf20Sopenharmony_ci	else
4708c2ecf20Sopenharmony_ci		binode->flags &= ~BTRFS_INODE_NOATIME;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/* 1 item for the inode */
4738c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 1);
4748c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
4758c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
4768c2ecf20Sopenharmony_ci		goto out_unlock;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	btrfs_sync_inode_flags_to_i_flags(inode);
4808c2ecf20Sopenharmony_ci	inode_inc_iversion(inode);
4818c2ecf20Sopenharmony_ci	inode->i_ctime = current_time(inode);
4828c2ecf20Sopenharmony_ci	ret = btrfs_update_inode(trans, root, inode);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	btrfs_end_transaction(trans);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ciout_unlock:
4878c2ecf20Sopenharmony_ci	if (ret) {
4888c2ecf20Sopenharmony_ci		binode->flags = old_flags;
4898c2ecf20Sopenharmony_ci		inode->i_flags = old_i_flags;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	inode_unlock(inode);
4938c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return ret;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int btrfs_ioctl_getversion(struct file *file, int __user *arg)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	return put_user(inode->i_generation, arg);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_fitrim(struct btrfs_fs_info *fs_info,
5068c2ecf20Sopenharmony_ci					void __user *arg)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	struct btrfs_device *device;
5098c2ecf20Sopenharmony_ci	struct request_queue *q;
5108c2ecf20Sopenharmony_ci	struct fstrim_range range;
5118c2ecf20Sopenharmony_ci	u64 minlen = ULLONG_MAX;
5128c2ecf20Sopenharmony_ci	u64 num_devices = 0;
5138c2ecf20Sopenharmony_ci	int ret;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
5168c2ecf20Sopenharmony_ci		return -EPERM;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/*
5198c2ecf20Sopenharmony_ci	 * If the fs is mounted with nologreplay, which requires it to be
5208c2ecf20Sopenharmony_ci	 * mounted in RO mode as well, we can not allow discard on free space
5218c2ecf20Sopenharmony_ci	 * inside block groups, because log trees refer to extents that are not
5228c2ecf20Sopenharmony_ci	 * pinned in a block group's free space cache (pinning the extents is
5238c2ecf20Sopenharmony_ci	 * precisely the first phase of replaying a log tree).
5248c2ecf20Sopenharmony_ci	 */
5258c2ecf20Sopenharmony_ci	if (btrfs_test_opt(fs_info, NOLOGREPLAY))
5268c2ecf20Sopenharmony_ci		return -EROFS;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	rcu_read_lock();
5298c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(device, &fs_info->fs_devices->devices,
5308c2ecf20Sopenharmony_ci				dev_list) {
5318c2ecf20Sopenharmony_ci		if (!device->bdev)
5328c2ecf20Sopenharmony_ci			continue;
5338c2ecf20Sopenharmony_ci		q = bdev_get_queue(device->bdev);
5348c2ecf20Sopenharmony_ci		if (blk_queue_discard(q)) {
5358c2ecf20Sopenharmony_ci			num_devices++;
5368c2ecf20Sopenharmony_ci			minlen = min_t(u64, q->limits.discard_granularity,
5378c2ecf20Sopenharmony_ci				     minlen);
5388c2ecf20Sopenharmony_ci		}
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci	rcu_read_unlock();
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (!num_devices)
5438c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5448c2ecf20Sopenharmony_ci	if (copy_from_user(&range, arg, sizeof(range)))
5458c2ecf20Sopenharmony_ci		return -EFAULT;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/*
5488c2ecf20Sopenharmony_ci	 * NOTE: Don't truncate the range using super->total_bytes.  Bytenr of
5498c2ecf20Sopenharmony_ci	 * block group is in the logical address space, which can be any
5508c2ecf20Sopenharmony_ci	 * sectorsize aligned bytenr in  the range [0, U64_MAX].
5518c2ecf20Sopenharmony_ci	 */
5528c2ecf20Sopenharmony_ci	if (range.len < fs_info->sb->s_blocksize)
5538c2ecf20Sopenharmony_ci		return -EINVAL;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	range.minlen = max(range.minlen, minlen);
5568c2ecf20Sopenharmony_ci	ret = btrfs_trim_fs(fs_info, &range);
5578c2ecf20Sopenharmony_ci	if (ret < 0)
5588c2ecf20Sopenharmony_ci		return ret;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &range, sizeof(range)))
5618c2ecf20Sopenharmony_ci		return -EFAULT;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	return 0;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ciint __pure btrfs_is_empty_uuid(u8 *uuid)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	int i;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	for (i = 0; i < BTRFS_UUID_SIZE; i++) {
5718c2ecf20Sopenharmony_ci		if (uuid[i])
5728c2ecf20Sopenharmony_ci			return 0;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci	return 1;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic noinline int create_subvol(struct inode *dir,
5788c2ecf20Sopenharmony_ci				  struct dentry *dentry,
5798c2ecf20Sopenharmony_ci				  const char *name, int namelen,
5808c2ecf20Sopenharmony_ci				  struct btrfs_qgroup_inherit *inherit)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
5838c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
5848c2ecf20Sopenharmony_ci	struct btrfs_key key;
5858c2ecf20Sopenharmony_ci	struct btrfs_root_item *root_item;
5868c2ecf20Sopenharmony_ci	struct btrfs_inode_item *inode_item;
5878c2ecf20Sopenharmony_ci	struct extent_buffer *leaf;
5888c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(dir)->root;
5898c2ecf20Sopenharmony_ci	struct btrfs_root *new_root;
5908c2ecf20Sopenharmony_ci	struct btrfs_block_rsv block_rsv;
5918c2ecf20Sopenharmony_ci	struct timespec64 cur_time = current_time(dir);
5928c2ecf20Sopenharmony_ci	struct inode *inode;
5938c2ecf20Sopenharmony_ci	int ret;
5948c2ecf20Sopenharmony_ci	int err;
5958c2ecf20Sopenharmony_ci	dev_t anon_dev = 0;
5968c2ecf20Sopenharmony_ci	u64 objectid;
5978c2ecf20Sopenharmony_ci	u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
5988c2ecf20Sopenharmony_ci	u64 index = 0;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	root_item = kzalloc(sizeof(*root_item), GFP_KERNEL);
6018c2ecf20Sopenharmony_ci	if (!root_item)
6028c2ecf20Sopenharmony_ci		return -ENOMEM;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	ret = btrfs_find_free_objectid(fs_info->tree_root, &objectid);
6058c2ecf20Sopenharmony_ci	if (ret)
6068c2ecf20Sopenharmony_ci		goto fail_free;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	ret = get_anon_bdev(&anon_dev);
6098c2ecf20Sopenharmony_ci	if (ret < 0)
6108c2ecf20Sopenharmony_ci		goto fail_free;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/*
6138c2ecf20Sopenharmony_ci	 * Don't create subvolume whose level is not zero. Or qgroup will be
6148c2ecf20Sopenharmony_ci	 * screwed up since it assumes subvolume qgroup's level to be 0.
6158c2ecf20Sopenharmony_ci	 */
6168c2ecf20Sopenharmony_ci	if (btrfs_qgroup_level(objectid)) {
6178c2ecf20Sopenharmony_ci		ret = -ENOSPC;
6188c2ecf20Sopenharmony_ci		goto fail_free;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
6228c2ecf20Sopenharmony_ci	/*
6238c2ecf20Sopenharmony_ci	 * The same as the snapshot creation, please see the comment
6248c2ecf20Sopenharmony_ci	 * of create_snapshot().
6258c2ecf20Sopenharmony_ci	 */
6268c2ecf20Sopenharmony_ci	ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 8, false);
6278c2ecf20Sopenharmony_ci	if (ret)
6288c2ecf20Sopenharmony_ci		goto fail_free;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 0);
6318c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
6328c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
6338c2ecf20Sopenharmony_ci		btrfs_subvolume_release_metadata(root, &block_rsv);
6348c2ecf20Sopenharmony_ci		goto fail_free;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci	trans->block_rsv = &block_rsv;
6378c2ecf20Sopenharmony_ci	trans->bytes_reserved = block_rsv.size;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	ret = btrfs_qgroup_inherit(trans, 0, objectid, inherit);
6408c2ecf20Sopenharmony_ci	if (ret)
6418c2ecf20Sopenharmony_ci		goto fail;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0,
6448c2ecf20Sopenharmony_ci				      BTRFS_NESTING_NORMAL);
6458c2ecf20Sopenharmony_ci	if (IS_ERR(leaf)) {
6468c2ecf20Sopenharmony_ci		ret = PTR_ERR(leaf);
6478c2ecf20Sopenharmony_ci		goto fail;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	btrfs_mark_buffer_dirty(leaf);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	inode_item = &root_item->inode;
6538c2ecf20Sopenharmony_ci	btrfs_set_stack_inode_generation(inode_item, 1);
6548c2ecf20Sopenharmony_ci	btrfs_set_stack_inode_size(inode_item, 3);
6558c2ecf20Sopenharmony_ci	btrfs_set_stack_inode_nlink(inode_item, 1);
6568c2ecf20Sopenharmony_ci	btrfs_set_stack_inode_nbytes(inode_item,
6578c2ecf20Sopenharmony_ci				     fs_info->nodesize);
6588c2ecf20Sopenharmony_ci	btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	btrfs_set_root_flags(root_item, 0);
6618c2ecf20Sopenharmony_ci	btrfs_set_root_limit(root_item, 0);
6628c2ecf20Sopenharmony_ci	btrfs_set_stack_inode_flags(inode_item, BTRFS_INODE_ROOT_ITEM_INIT);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	btrfs_set_root_bytenr(root_item, leaf->start);
6658c2ecf20Sopenharmony_ci	btrfs_set_root_generation(root_item, trans->transid);
6668c2ecf20Sopenharmony_ci	btrfs_set_root_level(root_item, 0);
6678c2ecf20Sopenharmony_ci	btrfs_set_root_refs(root_item, 1);
6688c2ecf20Sopenharmony_ci	btrfs_set_root_used(root_item, leaf->len);
6698c2ecf20Sopenharmony_ci	btrfs_set_root_last_snapshot(root_item, 0);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	btrfs_set_root_generation_v2(root_item,
6728c2ecf20Sopenharmony_ci			btrfs_root_generation(root_item));
6738c2ecf20Sopenharmony_ci	generate_random_guid(root_item->uuid);
6748c2ecf20Sopenharmony_ci	btrfs_set_stack_timespec_sec(&root_item->otime, cur_time.tv_sec);
6758c2ecf20Sopenharmony_ci	btrfs_set_stack_timespec_nsec(&root_item->otime, cur_time.tv_nsec);
6768c2ecf20Sopenharmony_ci	root_item->ctime = root_item->otime;
6778c2ecf20Sopenharmony_ci	btrfs_set_root_ctransid(root_item, trans->transid);
6788c2ecf20Sopenharmony_ci	btrfs_set_root_otransid(root_item, trans->transid);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	btrfs_tree_unlock(leaf);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	btrfs_set_root_dirid(root_item, new_dirid);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	key.objectid = objectid;
6858c2ecf20Sopenharmony_ci	key.offset = 0;
6868c2ecf20Sopenharmony_ci	key.type = BTRFS_ROOT_ITEM_KEY;
6878c2ecf20Sopenharmony_ci	ret = btrfs_insert_root(trans, fs_info->tree_root, &key,
6888c2ecf20Sopenharmony_ci				root_item);
6898c2ecf20Sopenharmony_ci	if (ret) {
6908c2ecf20Sopenharmony_ci		/*
6918c2ecf20Sopenharmony_ci		 * Since we don't abort the transaction in this case, free the
6928c2ecf20Sopenharmony_ci		 * tree block so that we don't leak space and leave the
6938c2ecf20Sopenharmony_ci		 * filesystem in an inconsistent state (an extent item in the
6948c2ecf20Sopenharmony_ci		 * extent tree without backreferences). Also no need to have
6958c2ecf20Sopenharmony_ci		 * the tree block locked since it is not in any tree at this
6968c2ecf20Sopenharmony_ci		 * point, so no other task can find it and use it.
6978c2ecf20Sopenharmony_ci		 */
6988c2ecf20Sopenharmony_ci		btrfs_free_tree_block(trans, root, leaf, 0, 1);
6998c2ecf20Sopenharmony_ci		free_extent_buffer(leaf);
7008c2ecf20Sopenharmony_ci		goto fail;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	free_extent_buffer(leaf);
7048c2ecf20Sopenharmony_ci	leaf = NULL;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	key.offset = (u64)-1;
7078c2ecf20Sopenharmony_ci	new_root = btrfs_get_new_fs_root(fs_info, objectid, anon_dev);
7088c2ecf20Sopenharmony_ci	if (IS_ERR(new_root)) {
7098c2ecf20Sopenharmony_ci		free_anon_bdev(anon_dev);
7108c2ecf20Sopenharmony_ci		ret = PTR_ERR(new_root);
7118c2ecf20Sopenharmony_ci		btrfs_abort_transaction(trans, ret);
7128c2ecf20Sopenharmony_ci		goto fail;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci	/* Freeing will be done in btrfs_put_root() of new_root */
7158c2ecf20Sopenharmony_ci	anon_dev = 0;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	btrfs_record_root_in_trans(trans, new_root);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	ret = btrfs_create_subvol_root(trans, new_root, root, new_dirid);
7208c2ecf20Sopenharmony_ci	btrfs_put_root(new_root);
7218c2ecf20Sopenharmony_ci	if (ret) {
7228c2ecf20Sopenharmony_ci		/* We potentially lose an unused inode item here */
7238c2ecf20Sopenharmony_ci		btrfs_abort_transaction(trans, ret);
7248c2ecf20Sopenharmony_ci		goto fail;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	mutex_lock(&new_root->objectid_mutex);
7288c2ecf20Sopenharmony_ci	new_root->highest_objectid = new_dirid;
7298c2ecf20Sopenharmony_ci	mutex_unlock(&new_root->objectid_mutex);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/*
7328c2ecf20Sopenharmony_ci	 * insert the directory item
7338c2ecf20Sopenharmony_ci	 */
7348c2ecf20Sopenharmony_ci	ret = btrfs_set_inode_index(BTRFS_I(dir), &index);
7358c2ecf20Sopenharmony_ci	if (ret) {
7368c2ecf20Sopenharmony_ci		btrfs_abort_transaction(trans, ret);
7378c2ecf20Sopenharmony_ci		goto fail;
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	ret = btrfs_insert_dir_item(trans, name, namelen, BTRFS_I(dir), &key,
7418c2ecf20Sopenharmony_ci				    BTRFS_FT_DIR, index);
7428c2ecf20Sopenharmony_ci	if (ret) {
7438c2ecf20Sopenharmony_ci		btrfs_abort_transaction(trans, ret);
7448c2ecf20Sopenharmony_ci		goto fail;
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2);
7488c2ecf20Sopenharmony_ci	ret = btrfs_update_inode(trans, root, dir);
7498c2ecf20Sopenharmony_ci	if (ret) {
7508c2ecf20Sopenharmony_ci		btrfs_abort_transaction(trans, ret);
7518c2ecf20Sopenharmony_ci		goto fail;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	ret = btrfs_add_root_ref(trans, objectid, root->root_key.objectid,
7558c2ecf20Sopenharmony_ci				 btrfs_ino(BTRFS_I(dir)), index, name, namelen);
7568c2ecf20Sopenharmony_ci	if (ret) {
7578c2ecf20Sopenharmony_ci		btrfs_abort_transaction(trans, ret);
7588c2ecf20Sopenharmony_ci		goto fail;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	ret = btrfs_uuid_tree_add(trans, root_item->uuid,
7628c2ecf20Sopenharmony_ci				  BTRFS_UUID_KEY_SUBVOL, objectid);
7638c2ecf20Sopenharmony_ci	if (ret)
7648c2ecf20Sopenharmony_ci		btrfs_abort_transaction(trans, ret);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cifail:
7678c2ecf20Sopenharmony_ci	kfree(root_item);
7688c2ecf20Sopenharmony_ci	trans->block_rsv = NULL;
7698c2ecf20Sopenharmony_ci	trans->bytes_reserved = 0;
7708c2ecf20Sopenharmony_ci	btrfs_subvolume_release_metadata(root, &block_rsv);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	err = btrfs_commit_transaction(trans);
7738c2ecf20Sopenharmony_ci	if (err && !ret)
7748c2ecf20Sopenharmony_ci		ret = err;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (!ret) {
7778c2ecf20Sopenharmony_ci		inode = btrfs_lookup_dentry(dir, dentry);
7788c2ecf20Sopenharmony_ci		if (IS_ERR(inode))
7798c2ecf20Sopenharmony_ci			return PTR_ERR(inode);
7808c2ecf20Sopenharmony_ci		d_instantiate(dentry, inode);
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci	return ret;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cifail_free:
7858c2ecf20Sopenharmony_ci	if (anon_dev)
7868c2ecf20Sopenharmony_ci		free_anon_bdev(anon_dev);
7878c2ecf20Sopenharmony_ci	kfree(root_item);
7888c2ecf20Sopenharmony_ci	return ret;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic int create_snapshot(struct btrfs_root *root, struct inode *dir,
7928c2ecf20Sopenharmony_ci			   struct dentry *dentry, bool readonly,
7938c2ecf20Sopenharmony_ci			   struct btrfs_qgroup_inherit *inherit)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
7968c2ecf20Sopenharmony_ci	struct inode *inode;
7978c2ecf20Sopenharmony_ci	struct btrfs_pending_snapshot *pending_snapshot;
7988c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
7998c2ecf20Sopenharmony_ci	int ret;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	if (btrfs_root_refs(&root->root_item) == 0)
8028c2ecf20Sopenharmony_ci		return -ENOENT;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if (!test_bit(BTRFS_ROOT_SHAREABLE, &root->state))
8058c2ecf20Sopenharmony_ci		return -EINVAL;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (atomic_read(&root->nr_swapfiles)) {
8088c2ecf20Sopenharmony_ci		btrfs_warn(fs_info,
8098c2ecf20Sopenharmony_ci			   "cannot snapshot subvolume with active swapfile");
8108c2ecf20Sopenharmony_ci		return -ETXTBSY;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_KERNEL);
8148c2ecf20Sopenharmony_ci	if (!pending_snapshot)
8158c2ecf20Sopenharmony_ci		return -ENOMEM;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	ret = get_anon_bdev(&pending_snapshot->anon_dev);
8188c2ecf20Sopenharmony_ci	if (ret < 0)
8198c2ecf20Sopenharmony_ci		goto free_pending;
8208c2ecf20Sopenharmony_ci	pending_snapshot->root_item = kzalloc(sizeof(struct btrfs_root_item),
8218c2ecf20Sopenharmony_ci			GFP_KERNEL);
8228c2ecf20Sopenharmony_ci	pending_snapshot->path = btrfs_alloc_path();
8238c2ecf20Sopenharmony_ci	if (!pending_snapshot->root_item || !pending_snapshot->path) {
8248c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8258c2ecf20Sopenharmony_ci		goto free_pending;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	btrfs_init_block_rsv(&pending_snapshot->block_rsv,
8298c2ecf20Sopenharmony_ci			     BTRFS_BLOCK_RSV_TEMP);
8308c2ecf20Sopenharmony_ci	/*
8318c2ecf20Sopenharmony_ci	 * 1 - parent dir inode
8328c2ecf20Sopenharmony_ci	 * 2 - dir entries
8338c2ecf20Sopenharmony_ci	 * 1 - root item
8348c2ecf20Sopenharmony_ci	 * 2 - root ref/backref
8358c2ecf20Sopenharmony_ci	 * 1 - root of snapshot
8368c2ecf20Sopenharmony_ci	 * 1 - UUID item
8378c2ecf20Sopenharmony_ci	 */
8388c2ecf20Sopenharmony_ci	ret = btrfs_subvolume_reserve_metadata(BTRFS_I(dir)->root,
8398c2ecf20Sopenharmony_ci					&pending_snapshot->block_rsv, 8,
8408c2ecf20Sopenharmony_ci					false);
8418c2ecf20Sopenharmony_ci	if (ret)
8428c2ecf20Sopenharmony_ci		goto free_pending;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	pending_snapshot->dentry = dentry;
8458c2ecf20Sopenharmony_ci	pending_snapshot->root = root;
8468c2ecf20Sopenharmony_ci	pending_snapshot->readonly = readonly;
8478c2ecf20Sopenharmony_ci	pending_snapshot->dir = dir;
8488c2ecf20Sopenharmony_ci	pending_snapshot->inherit = inherit;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 0);
8518c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
8528c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
8538c2ecf20Sopenharmony_ci		goto fail;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	spin_lock(&fs_info->trans_lock);
8578c2ecf20Sopenharmony_ci	list_add(&pending_snapshot->list,
8588c2ecf20Sopenharmony_ci		 &trans->transaction->pending_snapshots);
8598c2ecf20Sopenharmony_ci	spin_unlock(&fs_info->trans_lock);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	ret = btrfs_commit_transaction(trans);
8628c2ecf20Sopenharmony_ci	if (ret)
8638c2ecf20Sopenharmony_ci		goto fail;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	ret = pending_snapshot->error;
8668c2ecf20Sopenharmony_ci	if (ret)
8678c2ecf20Sopenharmony_ci		goto fail;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	ret = btrfs_orphan_cleanup(pending_snapshot->snap);
8708c2ecf20Sopenharmony_ci	if (ret)
8718c2ecf20Sopenharmony_ci		goto fail;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	inode = btrfs_lookup_dentry(d_inode(dentry->d_parent), dentry);
8748c2ecf20Sopenharmony_ci	if (IS_ERR(inode)) {
8758c2ecf20Sopenharmony_ci		ret = PTR_ERR(inode);
8768c2ecf20Sopenharmony_ci		goto fail;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	d_instantiate(dentry, inode);
8808c2ecf20Sopenharmony_ci	ret = 0;
8818c2ecf20Sopenharmony_ci	pending_snapshot->anon_dev = 0;
8828c2ecf20Sopenharmony_cifail:
8838c2ecf20Sopenharmony_ci	/* Prevent double freeing of anon_dev */
8848c2ecf20Sopenharmony_ci	if (ret && pending_snapshot->snap)
8858c2ecf20Sopenharmony_ci		pending_snapshot->snap->anon_dev = 0;
8868c2ecf20Sopenharmony_ci	btrfs_put_root(pending_snapshot->snap);
8878c2ecf20Sopenharmony_ci	btrfs_subvolume_release_metadata(root, &pending_snapshot->block_rsv);
8888c2ecf20Sopenharmony_cifree_pending:
8898c2ecf20Sopenharmony_ci	if (pending_snapshot->anon_dev)
8908c2ecf20Sopenharmony_ci		free_anon_bdev(pending_snapshot->anon_dev);
8918c2ecf20Sopenharmony_ci	kfree(pending_snapshot->root_item);
8928c2ecf20Sopenharmony_ci	btrfs_free_path(pending_snapshot->path);
8938c2ecf20Sopenharmony_ci	kfree(pending_snapshot);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return ret;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/*  copy of may_delete in fs/namei.c()
8998c2ecf20Sopenharmony_ci *	Check whether we can remove a link victim from directory dir, check
9008c2ecf20Sopenharmony_ci *  whether the type of victim is right.
9018c2ecf20Sopenharmony_ci *  1. We can't do it if dir is read-only (done in permission())
9028c2ecf20Sopenharmony_ci *  2. We should have write and exec permissions on dir
9038c2ecf20Sopenharmony_ci *  3. We can't remove anything from append-only dir
9048c2ecf20Sopenharmony_ci *  4. We can't do anything with immutable dir (done in permission())
9058c2ecf20Sopenharmony_ci *  5. If the sticky bit on dir is set we should either
9068c2ecf20Sopenharmony_ci *	a. be owner of dir, or
9078c2ecf20Sopenharmony_ci *	b. be owner of victim, or
9088c2ecf20Sopenharmony_ci *	c. have CAP_FOWNER capability
9098c2ecf20Sopenharmony_ci *  6. If the victim is append-only or immutable we can't do anything with
9108c2ecf20Sopenharmony_ci *     links pointing to it.
9118c2ecf20Sopenharmony_ci *  7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
9128c2ecf20Sopenharmony_ci *  8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
9138c2ecf20Sopenharmony_ci *  9. We can't remove a root or mountpoint.
9148c2ecf20Sopenharmony_ci * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
9158c2ecf20Sopenharmony_ci *     nfs_async_unlink().
9168c2ecf20Sopenharmony_ci */
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	int error;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (d_really_is_negative(victim))
9238c2ecf20Sopenharmony_ci		return -ENOENT;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	BUG_ON(d_inode(victim->d_parent) != dir);
9268c2ecf20Sopenharmony_ci	audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
9298c2ecf20Sopenharmony_ci	if (error)
9308c2ecf20Sopenharmony_ci		return error;
9318c2ecf20Sopenharmony_ci	if (IS_APPEND(dir))
9328c2ecf20Sopenharmony_ci		return -EPERM;
9338c2ecf20Sopenharmony_ci	if (check_sticky(dir, d_inode(victim)) || IS_APPEND(d_inode(victim)) ||
9348c2ecf20Sopenharmony_ci	    IS_IMMUTABLE(d_inode(victim)) || IS_SWAPFILE(d_inode(victim)))
9358c2ecf20Sopenharmony_ci		return -EPERM;
9368c2ecf20Sopenharmony_ci	if (isdir) {
9378c2ecf20Sopenharmony_ci		if (!d_is_dir(victim))
9388c2ecf20Sopenharmony_ci			return -ENOTDIR;
9398c2ecf20Sopenharmony_ci		if (IS_ROOT(victim))
9408c2ecf20Sopenharmony_ci			return -EBUSY;
9418c2ecf20Sopenharmony_ci	} else if (d_is_dir(victim))
9428c2ecf20Sopenharmony_ci		return -EISDIR;
9438c2ecf20Sopenharmony_ci	if (IS_DEADDIR(dir))
9448c2ecf20Sopenharmony_ci		return -ENOENT;
9458c2ecf20Sopenharmony_ci	if (victim->d_flags & DCACHE_NFSFS_RENAMED)
9468c2ecf20Sopenharmony_ci		return -EBUSY;
9478c2ecf20Sopenharmony_ci	return 0;
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci/* copy of may_create in fs/namei.c() */
9518c2ecf20Sopenharmony_cistatic inline int btrfs_may_create(struct inode *dir, struct dentry *child)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	if (d_really_is_positive(child))
9548c2ecf20Sopenharmony_ci		return -EEXIST;
9558c2ecf20Sopenharmony_ci	if (IS_DEADDIR(dir))
9568c2ecf20Sopenharmony_ci		return -ENOENT;
9578c2ecf20Sopenharmony_ci	return inode_permission(dir, MAY_WRITE | MAY_EXEC);
9588c2ecf20Sopenharmony_ci}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci/*
9618c2ecf20Sopenharmony_ci * Create a new subvolume below @parent.  This is largely modeled after
9628c2ecf20Sopenharmony_ci * sys_mkdirat and vfs_mkdir, but we only do a single component lookup
9638c2ecf20Sopenharmony_ci * inside this filesystem so it's quite a bit simpler.
9648c2ecf20Sopenharmony_ci */
9658c2ecf20Sopenharmony_cistatic noinline int btrfs_mksubvol(const struct path *parent,
9668c2ecf20Sopenharmony_ci				   const char *name, int namelen,
9678c2ecf20Sopenharmony_ci				   struct btrfs_root *snap_src,
9688c2ecf20Sopenharmony_ci				   bool readonly,
9698c2ecf20Sopenharmony_ci				   struct btrfs_qgroup_inherit *inherit)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	struct inode *dir = d_inode(parent->dentry);
9728c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
9738c2ecf20Sopenharmony_ci	struct dentry *dentry;
9748c2ecf20Sopenharmony_ci	int error;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	error = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
9778c2ecf20Sopenharmony_ci	if (error == -EINTR)
9788c2ecf20Sopenharmony_ci		return error;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	dentry = lookup_one_len(name, parent->dentry, namelen);
9818c2ecf20Sopenharmony_ci	error = PTR_ERR(dentry);
9828c2ecf20Sopenharmony_ci	if (IS_ERR(dentry))
9838c2ecf20Sopenharmony_ci		goto out_unlock;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	error = btrfs_may_create(dir, dentry);
9868c2ecf20Sopenharmony_ci	if (error)
9878c2ecf20Sopenharmony_ci		goto out_dput;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	/*
9908c2ecf20Sopenharmony_ci	 * even if this name doesn't exist, we may get hash collisions.
9918c2ecf20Sopenharmony_ci	 * check for them now when we can safely fail
9928c2ecf20Sopenharmony_ci	 */
9938c2ecf20Sopenharmony_ci	error = btrfs_check_dir_item_collision(BTRFS_I(dir)->root,
9948c2ecf20Sopenharmony_ci					       dir->i_ino, name,
9958c2ecf20Sopenharmony_ci					       namelen);
9968c2ecf20Sopenharmony_ci	if (error)
9978c2ecf20Sopenharmony_ci		goto out_dput;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	down_read(&fs_info->subvol_sem);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0)
10028c2ecf20Sopenharmony_ci		goto out_up_read;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	if (snap_src)
10058c2ecf20Sopenharmony_ci		error = create_snapshot(snap_src, dir, dentry, readonly, inherit);
10068c2ecf20Sopenharmony_ci	else
10078c2ecf20Sopenharmony_ci		error = create_subvol(dir, dentry, name, namelen, inherit);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (!error)
10108c2ecf20Sopenharmony_ci		fsnotify_mkdir(dir, dentry);
10118c2ecf20Sopenharmony_ciout_up_read:
10128c2ecf20Sopenharmony_ci	up_read(&fs_info->subvol_sem);
10138c2ecf20Sopenharmony_ciout_dput:
10148c2ecf20Sopenharmony_ci	dput(dentry);
10158c2ecf20Sopenharmony_ciout_unlock:
10168c2ecf20Sopenharmony_ci	inode_unlock(dir);
10178c2ecf20Sopenharmony_ci	return error;
10188c2ecf20Sopenharmony_ci}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic noinline int btrfs_mksnapshot(const struct path *parent,
10218c2ecf20Sopenharmony_ci				   const char *name, int namelen,
10228c2ecf20Sopenharmony_ci				   struct btrfs_root *root,
10238c2ecf20Sopenharmony_ci				   bool readonly,
10248c2ecf20Sopenharmony_ci				   struct btrfs_qgroup_inherit *inherit)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	int ret;
10278c2ecf20Sopenharmony_ci	bool snapshot_force_cow = false;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	/*
10308c2ecf20Sopenharmony_ci	 * Force new buffered writes to reserve space even when NOCOW is
10318c2ecf20Sopenharmony_ci	 * possible. This is to avoid later writeback (running dealloc) to
10328c2ecf20Sopenharmony_ci	 * fallback to COW mode and unexpectedly fail with ENOSPC.
10338c2ecf20Sopenharmony_ci	 */
10348c2ecf20Sopenharmony_ci	btrfs_drew_read_lock(&root->snapshot_lock);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	ret = btrfs_start_delalloc_snapshot(root);
10378c2ecf20Sopenharmony_ci	if (ret)
10388c2ecf20Sopenharmony_ci		goto out;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	/*
10418c2ecf20Sopenharmony_ci	 * All previous writes have started writeback in NOCOW mode, so now
10428c2ecf20Sopenharmony_ci	 * we force future writes to fallback to COW mode during snapshot
10438c2ecf20Sopenharmony_ci	 * creation.
10448c2ecf20Sopenharmony_ci	 */
10458c2ecf20Sopenharmony_ci	atomic_inc(&root->snapshot_force_cow);
10468c2ecf20Sopenharmony_ci	snapshot_force_cow = true;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	ret = btrfs_mksubvol(parent, name, namelen,
10518c2ecf20Sopenharmony_ci			     root, readonly, inherit);
10528c2ecf20Sopenharmony_ciout:
10538c2ecf20Sopenharmony_ci	if (snapshot_force_cow)
10548c2ecf20Sopenharmony_ci		atomic_dec(&root->snapshot_force_cow);
10558c2ecf20Sopenharmony_ci	btrfs_drew_read_unlock(&root->snapshot_lock);
10568c2ecf20Sopenharmony_ci	return ret;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci/*
10608c2ecf20Sopenharmony_ci * When we're defragging a range, we don't want to kick it off again
10618c2ecf20Sopenharmony_ci * if it is really just waiting for delalloc to send it down.
10628c2ecf20Sopenharmony_ci * If we find a nice big extent or delalloc range for the bytes in the
10638c2ecf20Sopenharmony_ci * file you want to defrag, we return 0 to let you know to skip this
10648c2ecf20Sopenharmony_ci * part of the file
10658c2ecf20Sopenharmony_ci */
10668c2ecf20Sopenharmony_cistatic int check_defrag_in_cache(struct inode *inode, u64 offset, u32 thresh)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
10698c2ecf20Sopenharmony_ci	struct extent_map *em = NULL;
10708c2ecf20Sopenharmony_ci	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
10718c2ecf20Sopenharmony_ci	u64 end;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	read_lock(&em_tree->lock);
10748c2ecf20Sopenharmony_ci	em = lookup_extent_mapping(em_tree, offset, PAGE_SIZE);
10758c2ecf20Sopenharmony_ci	read_unlock(&em_tree->lock);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	if (em) {
10788c2ecf20Sopenharmony_ci		end = extent_map_end(em);
10798c2ecf20Sopenharmony_ci		free_extent_map(em);
10808c2ecf20Sopenharmony_ci		if (end - offset > thresh)
10818c2ecf20Sopenharmony_ci			return 0;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci	/* if we already have a nice delalloc here, just stop */
10848c2ecf20Sopenharmony_ci	thresh /= 2;
10858c2ecf20Sopenharmony_ci	end = count_range_bits(io_tree, &offset, offset + thresh,
10868c2ecf20Sopenharmony_ci			       thresh, EXTENT_DELALLOC, 1);
10878c2ecf20Sopenharmony_ci	if (end >= thresh)
10888c2ecf20Sopenharmony_ci		return 0;
10898c2ecf20Sopenharmony_ci	return 1;
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci/*
10938c2ecf20Sopenharmony_ci * helper function to walk through a file and find extents
10948c2ecf20Sopenharmony_ci * newer than a specific transid, and smaller than thresh.
10958c2ecf20Sopenharmony_ci *
10968c2ecf20Sopenharmony_ci * This is used by the defragging code to find new and small
10978c2ecf20Sopenharmony_ci * extents
10988c2ecf20Sopenharmony_ci */
10998c2ecf20Sopenharmony_cistatic int find_new_extents(struct btrfs_root *root,
11008c2ecf20Sopenharmony_ci			    struct inode *inode, u64 newer_than,
11018c2ecf20Sopenharmony_ci			    u64 *off, u32 thresh)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct btrfs_path *path;
11048c2ecf20Sopenharmony_ci	struct btrfs_key min_key;
11058c2ecf20Sopenharmony_ci	struct extent_buffer *leaf;
11068c2ecf20Sopenharmony_ci	struct btrfs_file_extent_item *extent;
11078c2ecf20Sopenharmony_ci	int type;
11088c2ecf20Sopenharmony_ci	int ret;
11098c2ecf20Sopenharmony_ci	u64 ino = btrfs_ino(BTRFS_I(inode));
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
11128c2ecf20Sopenharmony_ci	if (!path)
11138c2ecf20Sopenharmony_ci		return -ENOMEM;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	min_key.objectid = ino;
11168c2ecf20Sopenharmony_ci	min_key.type = BTRFS_EXTENT_DATA_KEY;
11178c2ecf20Sopenharmony_ci	min_key.offset = *off;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	while (1) {
11208c2ecf20Sopenharmony_ci		ret = btrfs_search_forward(root, &min_key, path, newer_than);
11218c2ecf20Sopenharmony_ci		if (ret != 0)
11228c2ecf20Sopenharmony_ci			goto none;
11238c2ecf20Sopenharmony_ciprocess_slot:
11248c2ecf20Sopenharmony_ci		if (min_key.objectid != ino)
11258c2ecf20Sopenharmony_ci			goto none;
11268c2ecf20Sopenharmony_ci		if (min_key.type != BTRFS_EXTENT_DATA_KEY)
11278c2ecf20Sopenharmony_ci			goto none;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci		leaf = path->nodes[0];
11308c2ecf20Sopenharmony_ci		extent = btrfs_item_ptr(leaf, path->slots[0],
11318c2ecf20Sopenharmony_ci					struct btrfs_file_extent_item);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci		type = btrfs_file_extent_type(leaf, extent);
11348c2ecf20Sopenharmony_ci		if (type == BTRFS_FILE_EXTENT_REG &&
11358c2ecf20Sopenharmony_ci		    btrfs_file_extent_num_bytes(leaf, extent) < thresh &&
11368c2ecf20Sopenharmony_ci		    check_defrag_in_cache(inode, min_key.offset, thresh)) {
11378c2ecf20Sopenharmony_ci			*off = min_key.offset;
11388c2ecf20Sopenharmony_ci			btrfs_free_path(path);
11398c2ecf20Sopenharmony_ci			return 0;
11408c2ecf20Sopenharmony_ci		}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci		path->slots[0]++;
11438c2ecf20Sopenharmony_ci		if (path->slots[0] < btrfs_header_nritems(leaf)) {
11448c2ecf20Sopenharmony_ci			btrfs_item_key_to_cpu(leaf, &min_key, path->slots[0]);
11458c2ecf20Sopenharmony_ci			goto process_slot;
11468c2ecf20Sopenharmony_ci		}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci		if (min_key.offset == (u64)-1)
11498c2ecf20Sopenharmony_ci			goto none;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci		min_key.offset++;
11528c2ecf20Sopenharmony_ci		btrfs_release_path(path);
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_cinone:
11558c2ecf20Sopenharmony_ci	btrfs_free_path(path);
11568c2ecf20Sopenharmony_ci	return -ENOENT;
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistatic struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
11628c2ecf20Sopenharmony_ci	struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
11638c2ecf20Sopenharmony_ci	struct extent_map *em;
11648c2ecf20Sopenharmony_ci	u64 len = PAGE_SIZE;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	/*
11678c2ecf20Sopenharmony_ci	 * hopefully we have this extent in the tree already, try without
11688c2ecf20Sopenharmony_ci	 * the full extent lock
11698c2ecf20Sopenharmony_ci	 */
11708c2ecf20Sopenharmony_ci	read_lock(&em_tree->lock);
11718c2ecf20Sopenharmony_ci	em = lookup_extent_mapping(em_tree, start, len);
11728c2ecf20Sopenharmony_ci	read_unlock(&em_tree->lock);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	if (!em) {
11758c2ecf20Sopenharmony_ci		struct extent_state *cached = NULL;
11768c2ecf20Sopenharmony_ci		u64 end = start + len - 1;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci		/* get the big lock and read metadata off disk */
11798c2ecf20Sopenharmony_ci		lock_extent_bits(io_tree, start, end, &cached);
11808c2ecf20Sopenharmony_ci		em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, start, len);
11818c2ecf20Sopenharmony_ci		unlock_extent_cached(io_tree, start, end, &cached);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci		if (IS_ERR(em))
11848c2ecf20Sopenharmony_ci			return NULL;
11858c2ecf20Sopenharmony_ci	}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	return em;
11888c2ecf20Sopenharmony_ci}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_cistatic bool defrag_check_next_extent(struct inode *inode, struct extent_map *em)
11918c2ecf20Sopenharmony_ci{
11928c2ecf20Sopenharmony_ci	struct extent_map *next;
11938c2ecf20Sopenharmony_ci	bool ret = true;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	/* this is the last extent */
11968c2ecf20Sopenharmony_ci	if (em->start + em->len >= i_size_read(inode))
11978c2ecf20Sopenharmony_ci		return false;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	next = defrag_lookup_extent(inode, em->start + em->len);
12008c2ecf20Sopenharmony_ci	if (!next || next->block_start >= EXTENT_MAP_LAST_BYTE)
12018c2ecf20Sopenharmony_ci		ret = false;
12028c2ecf20Sopenharmony_ci	else if ((em->block_start + em->block_len == next->block_start) &&
12038c2ecf20Sopenharmony_ci		 (em->block_len > SZ_128K && next->block_len > SZ_128K))
12048c2ecf20Sopenharmony_ci		ret = false;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	free_extent_map(next);
12078c2ecf20Sopenharmony_ci	return ret;
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_cistatic int should_defrag_range(struct inode *inode, u64 start, u32 thresh,
12118c2ecf20Sopenharmony_ci			       u64 *last_len, u64 *skip, u64 *defrag_end,
12128c2ecf20Sopenharmony_ci			       int compress)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	struct extent_map *em;
12158c2ecf20Sopenharmony_ci	int ret = 1;
12168c2ecf20Sopenharmony_ci	bool next_mergeable = true;
12178c2ecf20Sopenharmony_ci	bool prev_mergeable = true;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	/*
12208c2ecf20Sopenharmony_ci	 * make sure that once we start defragging an extent, we keep on
12218c2ecf20Sopenharmony_ci	 * defragging it
12228c2ecf20Sopenharmony_ci	 */
12238c2ecf20Sopenharmony_ci	if (start < *defrag_end)
12248c2ecf20Sopenharmony_ci		return 1;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	*skip = 0;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	em = defrag_lookup_extent(inode, start);
12298c2ecf20Sopenharmony_ci	if (!em)
12308c2ecf20Sopenharmony_ci		return 0;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	/* this will cover holes, and inline extents */
12338c2ecf20Sopenharmony_ci	if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
12348c2ecf20Sopenharmony_ci		ret = 0;
12358c2ecf20Sopenharmony_ci		goto out;
12368c2ecf20Sopenharmony_ci	}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	if (!*defrag_end)
12398c2ecf20Sopenharmony_ci		prev_mergeable = false;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	next_mergeable = defrag_check_next_extent(inode, em);
12428c2ecf20Sopenharmony_ci	/*
12438c2ecf20Sopenharmony_ci	 * we hit a real extent, if it is big or the next extent is not a
12448c2ecf20Sopenharmony_ci	 * real extent, don't bother defragging it
12458c2ecf20Sopenharmony_ci	 */
12468c2ecf20Sopenharmony_ci	if (!compress && (*last_len == 0 || *last_len >= thresh) &&
12478c2ecf20Sopenharmony_ci	    (em->len >= thresh || (!next_mergeable && !prev_mergeable)))
12488c2ecf20Sopenharmony_ci		ret = 0;
12498c2ecf20Sopenharmony_ciout:
12508c2ecf20Sopenharmony_ci	/*
12518c2ecf20Sopenharmony_ci	 * last_len ends up being a counter of how many bytes we've defragged.
12528c2ecf20Sopenharmony_ci	 * every time we choose not to defrag an extent, we reset *last_len
12538c2ecf20Sopenharmony_ci	 * so that the next tiny extent will force a defrag.
12548c2ecf20Sopenharmony_ci	 *
12558c2ecf20Sopenharmony_ci	 * The end result of this is that tiny extents before a single big
12568c2ecf20Sopenharmony_ci	 * extent will force at least part of that big extent to be defragged.
12578c2ecf20Sopenharmony_ci	 */
12588c2ecf20Sopenharmony_ci	if (ret) {
12598c2ecf20Sopenharmony_ci		*defrag_end = extent_map_end(em);
12608c2ecf20Sopenharmony_ci	} else {
12618c2ecf20Sopenharmony_ci		*last_len = 0;
12628c2ecf20Sopenharmony_ci		*skip = extent_map_end(em);
12638c2ecf20Sopenharmony_ci		*defrag_end = 0;
12648c2ecf20Sopenharmony_ci	}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	free_extent_map(em);
12678c2ecf20Sopenharmony_ci	return ret;
12688c2ecf20Sopenharmony_ci}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci/*
12718c2ecf20Sopenharmony_ci * it doesn't do much good to defrag one or two pages
12728c2ecf20Sopenharmony_ci * at a time.  This pulls in a nice chunk of pages
12738c2ecf20Sopenharmony_ci * to COW and defrag.
12748c2ecf20Sopenharmony_ci *
12758c2ecf20Sopenharmony_ci * It also makes sure the delalloc code has enough
12768c2ecf20Sopenharmony_ci * dirty data to avoid making new small extents as part
12778c2ecf20Sopenharmony_ci * of the defrag
12788c2ecf20Sopenharmony_ci *
12798c2ecf20Sopenharmony_ci * It's a good idea to start RA on this range
12808c2ecf20Sopenharmony_ci * before calling this.
12818c2ecf20Sopenharmony_ci */
12828c2ecf20Sopenharmony_cistatic int cluster_pages_for_defrag(struct inode *inode,
12838c2ecf20Sopenharmony_ci				    struct page **pages,
12848c2ecf20Sopenharmony_ci				    unsigned long start_index,
12858c2ecf20Sopenharmony_ci				    unsigned long num_pages)
12868c2ecf20Sopenharmony_ci{
12878c2ecf20Sopenharmony_ci	unsigned long file_end;
12888c2ecf20Sopenharmony_ci	u64 isize = i_size_read(inode);
12898c2ecf20Sopenharmony_ci	u64 page_start;
12908c2ecf20Sopenharmony_ci	u64 page_end;
12918c2ecf20Sopenharmony_ci	u64 page_cnt;
12928c2ecf20Sopenharmony_ci	u64 start = (u64)start_index << PAGE_SHIFT;
12938c2ecf20Sopenharmony_ci	u64 search_start;
12948c2ecf20Sopenharmony_ci	int ret;
12958c2ecf20Sopenharmony_ci	int i;
12968c2ecf20Sopenharmony_ci	int i_done;
12978c2ecf20Sopenharmony_ci	struct btrfs_ordered_extent *ordered;
12988c2ecf20Sopenharmony_ci	struct extent_state *cached_state = NULL;
12998c2ecf20Sopenharmony_ci	struct extent_io_tree *tree;
13008c2ecf20Sopenharmony_ci	struct extent_changeset *data_reserved = NULL;
13018c2ecf20Sopenharmony_ci	gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	file_end = (isize - 1) >> PAGE_SHIFT;
13048c2ecf20Sopenharmony_ci	if (!isize || start_index > file_end)
13058c2ecf20Sopenharmony_ci		return 0;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	page_cnt = min_t(u64, (u64)num_pages, (u64)file_end - start_index + 1);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	ret = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
13108c2ecf20Sopenharmony_ci			start, page_cnt << PAGE_SHIFT);
13118c2ecf20Sopenharmony_ci	if (ret)
13128c2ecf20Sopenharmony_ci		return ret;
13138c2ecf20Sopenharmony_ci	i_done = 0;
13148c2ecf20Sopenharmony_ci	tree = &BTRFS_I(inode)->io_tree;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	/* step one, lock all the pages */
13178c2ecf20Sopenharmony_ci	for (i = 0; i < page_cnt; i++) {
13188c2ecf20Sopenharmony_ci		struct page *page;
13198c2ecf20Sopenharmony_ciagain:
13208c2ecf20Sopenharmony_ci		page = find_or_create_page(inode->i_mapping,
13218c2ecf20Sopenharmony_ci					   start_index + i, mask);
13228c2ecf20Sopenharmony_ci		if (!page)
13238c2ecf20Sopenharmony_ci			break;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci		page_start = page_offset(page);
13268c2ecf20Sopenharmony_ci		page_end = page_start + PAGE_SIZE - 1;
13278c2ecf20Sopenharmony_ci		while (1) {
13288c2ecf20Sopenharmony_ci			lock_extent_bits(tree, page_start, page_end,
13298c2ecf20Sopenharmony_ci					 &cached_state);
13308c2ecf20Sopenharmony_ci			ordered = btrfs_lookup_ordered_extent(BTRFS_I(inode),
13318c2ecf20Sopenharmony_ci							      page_start);
13328c2ecf20Sopenharmony_ci			unlock_extent_cached(tree, page_start, page_end,
13338c2ecf20Sopenharmony_ci					     &cached_state);
13348c2ecf20Sopenharmony_ci			if (!ordered)
13358c2ecf20Sopenharmony_ci				break;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci			unlock_page(page);
13388c2ecf20Sopenharmony_ci			btrfs_start_ordered_extent(ordered, 1);
13398c2ecf20Sopenharmony_ci			btrfs_put_ordered_extent(ordered);
13408c2ecf20Sopenharmony_ci			lock_page(page);
13418c2ecf20Sopenharmony_ci			/*
13428c2ecf20Sopenharmony_ci			 * we unlocked the page above, so we need check if
13438c2ecf20Sopenharmony_ci			 * it was released or not.
13448c2ecf20Sopenharmony_ci			 */
13458c2ecf20Sopenharmony_ci			if (page->mapping != inode->i_mapping) {
13468c2ecf20Sopenharmony_ci				unlock_page(page);
13478c2ecf20Sopenharmony_ci				put_page(page);
13488c2ecf20Sopenharmony_ci				goto again;
13498c2ecf20Sopenharmony_ci			}
13508c2ecf20Sopenharmony_ci		}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci		if (!PageUptodate(page)) {
13538c2ecf20Sopenharmony_ci			btrfs_readpage(NULL, page);
13548c2ecf20Sopenharmony_ci			lock_page(page);
13558c2ecf20Sopenharmony_ci			if (!PageUptodate(page)) {
13568c2ecf20Sopenharmony_ci				unlock_page(page);
13578c2ecf20Sopenharmony_ci				put_page(page);
13588c2ecf20Sopenharmony_ci				ret = -EIO;
13598c2ecf20Sopenharmony_ci				break;
13608c2ecf20Sopenharmony_ci			}
13618c2ecf20Sopenharmony_ci		}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci		if (page->mapping != inode->i_mapping) {
13648c2ecf20Sopenharmony_ci			unlock_page(page);
13658c2ecf20Sopenharmony_ci			put_page(page);
13668c2ecf20Sopenharmony_ci			goto again;
13678c2ecf20Sopenharmony_ci		}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci		pages[i] = page;
13708c2ecf20Sopenharmony_ci		i_done++;
13718c2ecf20Sopenharmony_ci	}
13728c2ecf20Sopenharmony_ci	if (!i_done || ret)
13738c2ecf20Sopenharmony_ci		goto out;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	if (!(inode->i_sb->s_flags & SB_ACTIVE))
13768c2ecf20Sopenharmony_ci		goto out;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	/*
13798c2ecf20Sopenharmony_ci	 * so now we have a nice long stream of locked
13808c2ecf20Sopenharmony_ci	 * and up to date pages, lets wait on them
13818c2ecf20Sopenharmony_ci	 */
13828c2ecf20Sopenharmony_ci	for (i = 0; i < i_done; i++)
13838c2ecf20Sopenharmony_ci		wait_on_page_writeback(pages[i]);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	page_start = page_offset(pages[0]);
13868c2ecf20Sopenharmony_ci	page_end = page_offset(pages[i_done - 1]) + PAGE_SIZE;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	lock_extent_bits(&BTRFS_I(inode)->io_tree,
13898c2ecf20Sopenharmony_ci			 page_start, page_end - 1, &cached_state);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	/*
13928c2ecf20Sopenharmony_ci	 * When defragmenting we skip ranges that have holes or inline extents,
13938c2ecf20Sopenharmony_ci	 * (check should_defrag_range()), to avoid unnecessary IO and wasting
13948c2ecf20Sopenharmony_ci	 * space. At btrfs_defrag_file(), we check if a range should be defragged
13958c2ecf20Sopenharmony_ci	 * before locking the inode and then, if it should, we trigger a sync
13968c2ecf20Sopenharmony_ci	 * page cache readahead - we lock the inode only after that to avoid
13978c2ecf20Sopenharmony_ci	 * blocking for too long other tasks that possibly want to operate on
13988c2ecf20Sopenharmony_ci	 * other file ranges. But before we were able to get the inode lock,
13998c2ecf20Sopenharmony_ci	 * some other task may have punched a hole in the range, or we may have
14008c2ecf20Sopenharmony_ci	 * now an inline extent, in which case we should not defrag. So check
14018c2ecf20Sopenharmony_ci	 * for that here, where we have the inode and the range locked, and bail
14028c2ecf20Sopenharmony_ci	 * out if that happened.
14038c2ecf20Sopenharmony_ci	 */
14048c2ecf20Sopenharmony_ci	search_start = page_start;
14058c2ecf20Sopenharmony_ci	while (search_start < page_end) {
14068c2ecf20Sopenharmony_ci		struct extent_map *em;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci		em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, search_start,
14098c2ecf20Sopenharmony_ci				      page_end - search_start);
14108c2ecf20Sopenharmony_ci		if (IS_ERR(em)) {
14118c2ecf20Sopenharmony_ci			ret = PTR_ERR(em);
14128c2ecf20Sopenharmony_ci			goto out_unlock_range;
14138c2ecf20Sopenharmony_ci		}
14148c2ecf20Sopenharmony_ci		if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
14158c2ecf20Sopenharmony_ci			free_extent_map(em);
14168c2ecf20Sopenharmony_ci			/* Ok, 0 means we did not defrag anything */
14178c2ecf20Sopenharmony_ci			ret = 0;
14188c2ecf20Sopenharmony_ci			goto out_unlock_range;
14198c2ecf20Sopenharmony_ci		}
14208c2ecf20Sopenharmony_ci		search_start = extent_map_end(em);
14218c2ecf20Sopenharmony_ci		free_extent_map(em);
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start,
14258c2ecf20Sopenharmony_ci			  page_end - 1, EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
14268c2ecf20Sopenharmony_ci			  EXTENT_DEFRAG, 0, 0, &cached_state);
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	if (i_done != page_cnt) {
14298c2ecf20Sopenharmony_ci		spin_lock(&BTRFS_I(inode)->lock);
14308c2ecf20Sopenharmony_ci		btrfs_mod_outstanding_extents(BTRFS_I(inode), 1);
14318c2ecf20Sopenharmony_ci		spin_unlock(&BTRFS_I(inode)->lock);
14328c2ecf20Sopenharmony_ci		btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved,
14338c2ecf20Sopenharmony_ci				start, (page_cnt - i_done) << PAGE_SHIFT, true);
14348c2ecf20Sopenharmony_ci	}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	set_extent_defrag(&BTRFS_I(inode)->io_tree, page_start, page_end - 1,
14388c2ecf20Sopenharmony_ci			  &cached_state);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	unlock_extent_cached(&BTRFS_I(inode)->io_tree,
14418c2ecf20Sopenharmony_ci			     page_start, page_end - 1, &cached_state);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	for (i = 0; i < i_done; i++) {
14448c2ecf20Sopenharmony_ci		clear_page_dirty_for_io(pages[i]);
14458c2ecf20Sopenharmony_ci		ClearPageChecked(pages[i]);
14468c2ecf20Sopenharmony_ci		set_page_extent_mapped(pages[i]);
14478c2ecf20Sopenharmony_ci		set_page_dirty(pages[i]);
14488c2ecf20Sopenharmony_ci		unlock_page(pages[i]);
14498c2ecf20Sopenharmony_ci		put_page(pages[i]);
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci	btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT);
14528c2ecf20Sopenharmony_ci	extent_changeset_free(data_reserved);
14538c2ecf20Sopenharmony_ci	return i_done;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ciout_unlock_range:
14568c2ecf20Sopenharmony_ci	unlock_extent_cached(&BTRFS_I(inode)->io_tree,
14578c2ecf20Sopenharmony_ci			     page_start, page_end - 1, &cached_state);
14588c2ecf20Sopenharmony_ciout:
14598c2ecf20Sopenharmony_ci	for (i = 0; i < i_done; i++) {
14608c2ecf20Sopenharmony_ci		unlock_page(pages[i]);
14618c2ecf20Sopenharmony_ci		put_page(pages[i]);
14628c2ecf20Sopenharmony_ci	}
14638c2ecf20Sopenharmony_ci	btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved,
14648c2ecf20Sopenharmony_ci			start, page_cnt << PAGE_SHIFT, true);
14658c2ecf20Sopenharmony_ci	btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT);
14668c2ecf20Sopenharmony_ci	extent_changeset_free(data_reserved);
14678c2ecf20Sopenharmony_ci	return ret;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ciint btrfs_defrag_file(struct inode *inode, struct file *file,
14728c2ecf20Sopenharmony_ci		      struct btrfs_ioctl_defrag_range_args *range,
14738c2ecf20Sopenharmony_ci		      u64 newer_than, unsigned long max_to_defrag)
14748c2ecf20Sopenharmony_ci{
14758c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
14768c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
14778c2ecf20Sopenharmony_ci	struct file_ra_state *ra = NULL;
14788c2ecf20Sopenharmony_ci	unsigned long last_index;
14798c2ecf20Sopenharmony_ci	u64 isize = i_size_read(inode);
14808c2ecf20Sopenharmony_ci	u64 last_len = 0;
14818c2ecf20Sopenharmony_ci	u64 skip = 0;
14828c2ecf20Sopenharmony_ci	u64 defrag_end = 0;
14838c2ecf20Sopenharmony_ci	u64 newer_off = range->start;
14848c2ecf20Sopenharmony_ci	unsigned long i;
14858c2ecf20Sopenharmony_ci	unsigned long ra_index = 0;
14868c2ecf20Sopenharmony_ci	int ret;
14878c2ecf20Sopenharmony_ci	int defrag_count = 0;
14888c2ecf20Sopenharmony_ci	int compress_type = BTRFS_COMPRESS_ZLIB;
14898c2ecf20Sopenharmony_ci	u32 extent_thresh = range->extent_thresh;
14908c2ecf20Sopenharmony_ci	unsigned long max_cluster = SZ_256K >> PAGE_SHIFT;
14918c2ecf20Sopenharmony_ci	unsigned long cluster = max_cluster;
14928c2ecf20Sopenharmony_ci	u64 new_align = ~((u64)SZ_128K - 1);
14938c2ecf20Sopenharmony_ci	struct page **pages = NULL;
14948c2ecf20Sopenharmony_ci	bool do_compress = range->flags & BTRFS_DEFRAG_RANGE_COMPRESS;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	if (isize == 0)
14978c2ecf20Sopenharmony_ci		return 0;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	if (range->start >= isize)
15008c2ecf20Sopenharmony_ci		return -EINVAL;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	if (do_compress) {
15038c2ecf20Sopenharmony_ci		if (range->compress_type >= BTRFS_NR_COMPRESS_TYPES)
15048c2ecf20Sopenharmony_ci			return -EINVAL;
15058c2ecf20Sopenharmony_ci		if (range->compress_type)
15068c2ecf20Sopenharmony_ci			compress_type = range->compress_type;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (extent_thresh == 0)
15108c2ecf20Sopenharmony_ci		extent_thresh = SZ_256K;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	/*
15138c2ecf20Sopenharmony_ci	 * If we were not given a file, allocate a readahead context. As
15148c2ecf20Sopenharmony_ci	 * readahead is just an optimization, defrag will work without it so
15158c2ecf20Sopenharmony_ci	 * we don't error out.
15168c2ecf20Sopenharmony_ci	 */
15178c2ecf20Sopenharmony_ci	if (!file) {
15188c2ecf20Sopenharmony_ci		ra = kzalloc(sizeof(*ra), GFP_KERNEL);
15198c2ecf20Sopenharmony_ci		if (ra)
15208c2ecf20Sopenharmony_ci			file_ra_state_init(ra, inode->i_mapping);
15218c2ecf20Sopenharmony_ci	} else {
15228c2ecf20Sopenharmony_ci		ra = &file->f_ra;
15238c2ecf20Sopenharmony_ci	}
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	pages = kmalloc_array(max_cluster, sizeof(struct page *), GFP_KERNEL);
15268c2ecf20Sopenharmony_ci	if (!pages) {
15278c2ecf20Sopenharmony_ci		ret = -ENOMEM;
15288c2ecf20Sopenharmony_ci		goto out_ra;
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	/* find the last page to defrag */
15328c2ecf20Sopenharmony_ci	if (range->start + range->len > range->start) {
15338c2ecf20Sopenharmony_ci		last_index = min_t(u64, isize - 1,
15348c2ecf20Sopenharmony_ci			 range->start + range->len - 1) >> PAGE_SHIFT;
15358c2ecf20Sopenharmony_ci	} else {
15368c2ecf20Sopenharmony_ci		last_index = (isize - 1) >> PAGE_SHIFT;
15378c2ecf20Sopenharmony_ci	}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	if (newer_than) {
15408c2ecf20Sopenharmony_ci		ret = find_new_extents(root, inode, newer_than,
15418c2ecf20Sopenharmony_ci				       &newer_off, SZ_64K);
15428c2ecf20Sopenharmony_ci		if (!ret) {
15438c2ecf20Sopenharmony_ci			range->start = newer_off;
15448c2ecf20Sopenharmony_ci			/*
15458c2ecf20Sopenharmony_ci			 * we always align our defrag to help keep
15468c2ecf20Sopenharmony_ci			 * the extents in the file evenly spaced
15478c2ecf20Sopenharmony_ci			 */
15488c2ecf20Sopenharmony_ci			i = (newer_off & new_align) >> PAGE_SHIFT;
15498c2ecf20Sopenharmony_ci		} else
15508c2ecf20Sopenharmony_ci			goto out_ra;
15518c2ecf20Sopenharmony_ci	} else {
15528c2ecf20Sopenharmony_ci		i = range->start >> PAGE_SHIFT;
15538c2ecf20Sopenharmony_ci	}
15548c2ecf20Sopenharmony_ci	if (!max_to_defrag)
15558c2ecf20Sopenharmony_ci		max_to_defrag = last_index - i + 1;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	/*
15588c2ecf20Sopenharmony_ci	 * make writeback starts from i, so the defrag range can be
15598c2ecf20Sopenharmony_ci	 * written sequentially.
15608c2ecf20Sopenharmony_ci	 */
15618c2ecf20Sopenharmony_ci	if (i < inode->i_mapping->writeback_index)
15628c2ecf20Sopenharmony_ci		inode->i_mapping->writeback_index = i;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	while (i <= last_index && defrag_count < max_to_defrag &&
15658c2ecf20Sopenharmony_ci	       (i < DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE))) {
15668c2ecf20Sopenharmony_ci		/*
15678c2ecf20Sopenharmony_ci		 * make sure we stop running if someone unmounts
15688c2ecf20Sopenharmony_ci		 * the FS
15698c2ecf20Sopenharmony_ci		 */
15708c2ecf20Sopenharmony_ci		if (!(inode->i_sb->s_flags & SB_ACTIVE))
15718c2ecf20Sopenharmony_ci			break;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci		if (btrfs_defrag_cancelled(fs_info)) {
15748c2ecf20Sopenharmony_ci			btrfs_debug(fs_info, "defrag_file cancelled");
15758c2ecf20Sopenharmony_ci			ret = -EAGAIN;
15768c2ecf20Sopenharmony_ci			break;
15778c2ecf20Sopenharmony_ci		}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci		if (!should_defrag_range(inode, (u64)i << PAGE_SHIFT,
15808c2ecf20Sopenharmony_ci					 extent_thresh, &last_len, &skip,
15818c2ecf20Sopenharmony_ci					 &defrag_end, do_compress)){
15828c2ecf20Sopenharmony_ci			unsigned long next;
15838c2ecf20Sopenharmony_ci			/*
15848c2ecf20Sopenharmony_ci			 * the should_defrag function tells us how much to skip
15858c2ecf20Sopenharmony_ci			 * bump our counter by the suggested amount
15868c2ecf20Sopenharmony_ci			 */
15878c2ecf20Sopenharmony_ci			next = DIV_ROUND_UP(skip, PAGE_SIZE);
15888c2ecf20Sopenharmony_ci			i = max(i + 1, next);
15898c2ecf20Sopenharmony_ci			continue;
15908c2ecf20Sopenharmony_ci		}
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci		if (!newer_than) {
15938c2ecf20Sopenharmony_ci			cluster = (PAGE_ALIGN(defrag_end) >>
15948c2ecf20Sopenharmony_ci				   PAGE_SHIFT) - i;
15958c2ecf20Sopenharmony_ci			cluster = min(cluster, max_cluster);
15968c2ecf20Sopenharmony_ci		} else {
15978c2ecf20Sopenharmony_ci			cluster = max_cluster;
15988c2ecf20Sopenharmony_ci		}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci		if (i + cluster > ra_index) {
16018c2ecf20Sopenharmony_ci			ra_index = max(i, ra_index);
16028c2ecf20Sopenharmony_ci			if (ra)
16038c2ecf20Sopenharmony_ci				page_cache_sync_readahead(inode->i_mapping, ra,
16048c2ecf20Sopenharmony_ci						file, ra_index, cluster);
16058c2ecf20Sopenharmony_ci			ra_index += cluster;
16068c2ecf20Sopenharmony_ci		}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci		inode_lock(inode);
16098c2ecf20Sopenharmony_ci		if (IS_SWAPFILE(inode)) {
16108c2ecf20Sopenharmony_ci			ret = -ETXTBSY;
16118c2ecf20Sopenharmony_ci		} else {
16128c2ecf20Sopenharmony_ci			if (do_compress)
16138c2ecf20Sopenharmony_ci				BTRFS_I(inode)->defrag_compress = compress_type;
16148c2ecf20Sopenharmony_ci			ret = cluster_pages_for_defrag(inode, pages, i, cluster);
16158c2ecf20Sopenharmony_ci		}
16168c2ecf20Sopenharmony_ci		if (ret < 0) {
16178c2ecf20Sopenharmony_ci			inode_unlock(inode);
16188c2ecf20Sopenharmony_ci			goto out_ra;
16198c2ecf20Sopenharmony_ci		}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci		defrag_count += ret;
16228c2ecf20Sopenharmony_ci		balance_dirty_pages_ratelimited(inode->i_mapping);
16238c2ecf20Sopenharmony_ci		inode_unlock(inode);
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci		if (newer_than) {
16268c2ecf20Sopenharmony_ci			if (newer_off == (u64)-1)
16278c2ecf20Sopenharmony_ci				break;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci			if (ret > 0)
16308c2ecf20Sopenharmony_ci				i += ret;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci			newer_off = max(newer_off + 1,
16338c2ecf20Sopenharmony_ci					(u64)i << PAGE_SHIFT);
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci			ret = find_new_extents(root, inode, newer_than,
16368c2ecf20Sopenharmony_ci					       &newer_off, SZ_64K);
16378c2ecf20Sopenharmony_ci			if (!ret) {
16388c2ecf20Sopenharmony_ci				range->start = newer_off;
16398c2ecf20Sopenharmony_ci				i = (newer_off & new_align) >> PAGE_SHIFT;
16408c2ecf20Sopenharmony_ci			} else {
16418c2ecf20Sopenharmony_ci				break;
16428c2ecf20Sopenharmony_ci			}
16438c2ecf20Sopenharmony_ci		} else {
16448c2ecf20Sopenharmony_ci			if (ret > 0) {
16458c2ecf20Sopenharmony_ci				i += ret;
16468c2ecf20Sopenharmony_ci				last_len += ret << PAGE_SHIFT;
16478c2ecf20Sopenharmony_ci			} else {
16488c2ecf20Sopenharmony_ci				i++;
16498c2ecf20Sopenharmony_ci				last_len = 0;
16508c2ecf20Sopenharmony_ci			}
16518c2ecf20Sopenharmony_ci		}
16528c2ecf20Sopenharmony_ci	}
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	if ((range->flags & BTRFS_DEFRAG_RANGE_START_IO)) {
16558c2ecf20Sopenharmony_ci		filemap_flush(inode->i_mapping);
16568c2ecf20Sopenharmony_ci		if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
16578c2ecf20Sopenharmony_ci			     &BTRFS_I(inode)->runtime_flags))
16588c2ecf20Sopenharmony_ci			filemap_flush(inode->i_mapping);
16598c2ecf20Sopenharmony_ci	}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	if (range->compress_type == BTRFS_COMPRESS_LZO) {
16628c2ecf20Sopenharmony_ci		btrfs_set_fs_incompat(fs_info, COMPRESS_LZO);
16638c2ecf20Sopenharmony_ci	} else if (range->compress_type == BTRFS_COMPRESS_ZSTD) {
16648c2ecf20Sopenharmony_ci		btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD);
16658c2ecf20Sopenharmony_ci	}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	ret = defrag_count;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ciout_ra:
16708c2ecf20Sopenharmony_ci	if (do_compress) {
16718c2ecf20Sopenharmony_ci		inode_lock(inode);
16728c2ecf20Sopenharmony_ci		BTRFS_I(inode)->defrag_compress = BTRFS_COMPRESS_NONE;
16738c2ecf20Sopenharmony_ci		inode_unlock(inode);
16748c2ecf20Sopenharmony_ci	}
16758c2ecf20Sopenharmony_ci	if (!file)
16768c2ecf20Sopenharmony_ci		kfree(ra);
16778c2ecf20Sopenharmony_ci	kfree(pages);
16788c2ecf20Sopenharmony_ci	return ret;
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_resize(struct file *file,
16828c2ecf20Sopenharmony_ci					void __user *arg)
16838c2ecf20Sopenharmony_ci{
16848c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
16858c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
16868c2ecf20Sopenharmony_ci	u64 new_size;
16878c2ecf20Sopenharmony_ci	u64 old_size;
16888c2ecf20Sopenharmony_ci	u64 devid = 1;
16898c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
16908c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args *vol_args;
16918c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
16928c2ecf20Sopenharmony_ci	struct btrfs_device *device = NULL;
16938c2ecf20Sopenharmony_ci	char *sizestr;
16948c2ecf20Sopenharmony_ci	char *retptr;
16958c2ecf20Sopenharmony_ci	char *devstr = NULL;
16968c2ecf20Sopenharmony_ci	int ret = 0;
16978c2ecf20Sopenharmony_ci	int mod = 0;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
17008c2ecf20Sopenharmony_ci		return -EPERM;
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
17038c2ecf20Sopenharmony_ci	if (ret)
17048c2ecf20Sopenharmony_ci		return ret;
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_RESIZE)) {
17078c2ecf20Sopenharmony_ci		mnt_drop_write_file(file);
17088c2ecf20Sopenharmony_ci		return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
17098c2ecf20Sopenharmony_ci	}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	vol_args = memdup_user(arg, sizeof(*vol_args));
17128c2ecf20Sopenharmony_ci	if (IS_ERR(vol_args)) {
17138c2ecf20Sopenharmony_ci		ret = PTR_ERR(vol_args);
17148c2ecf20Sopenharmony_ci		goto out;
17158c2ecf20Sopenharmony_ci	}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	sizestr = vol_args->name;
17208c2ecf20Sopenharmony_ci	devstr = strchr(sizestr, ':');
17218c2ecf20Sopenharmony_ci	if (devstr) {
17228c2ecf20Sopenharmony_ci		sizestr = devstr + 1;
17238c2ecf20Sopenharmony_ci		*devstr = '\0';
17248c2ecf20Sopenharmony_ci		devstr = vol_args->name;
17258c2ecf20Sopenharmony_ci		ret = kstrtoull(devstr, 10, &devid);
17268c2ecf20Sopenharmony_ci		if (ret)
17278c2ecf20Sopenharmony_ci			goto out_free;
17288c2ecf20Sopenharmony_ci		if (!devid) {
17298c2ecf20Sopenharmony_ci			ret = -EINVAL;
17308c2ecf20Sopenharmony_ci			goto out_free;
17318c2ecf20Sopenharmony_ci		}
17328c2ecf20Sopenharmony_ci		btrfs_info(fs_info, "resizing devid %llu", devid);
17338c2ecf20Sopenharmony_ci	}
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	device = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
17368c2ecf20Sopenharmony_ci	if (!device) {
17378c2ecf20Sopenharmony_ci		btrfs_info(fs_info, "resizer unable to find device %llu",
17388c2ecf20Sopenharmony_ci			   devid);
17398c2ecf20Sopenharmony_ci		ret = -ENODEV;
17408c2ecf20Sopenharmony_ci		goto out_free;
17418c2ecf20Sopenharmony_ci	}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
17448c2ecf20Sopenharmony_ci		btrfs_info(fs_info,
17458c2ecf20Sopenharmony_ci			   "resizer unable to apply on readonly device %llu",
17468c2ecf20Sopenharmony_ci		       devid);
17478c2ecf20Sopenharmony_ci		ret = -EPERM;
17488c2ecf20Sopenharmony_ci		goto out_free;
17498c2ecf20Sopenharmony_ci	}
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	if (!strcmp(sizestr, "max"))
17528c2ecf20Sopenharmony_ci		new_size = device->bdev->bd_inode->i_size;
17538c2ecf20Sopenharmony_ci	else {
17548c2ecf20Sopenharmony_ci		if (sizestr[0] == '-') {
17558c2ecf20Sopenharmony_ci			mod = -1;
17568c2ecf20Sopenharmony_ci			sizestr++;
17578c2ecf20Sopenharmony_ci		} else if (sizestr[0] == '+') {
17588c2ecf20Sopenharmony_ci			mod = 1;
17598c2ecf20Sopenharmony_ci			sizestr++;
17608c2ecf20Sopenharmony_ci		}
17618c2ecf20Sopenharmony_ci		new_size = memparse(sizestr, &retptr);
17628c2ecf20Sopenharmony_ci		if (*retptr != '\0' || new_size == 0) {
17638c2ecf20Sopenharmony_ci			ret = -EINVAL;
17648c2ecf20Sopenharmony_ci			goto out_free;
17658c2ecf20Sopenharmony_ci		}
17668c2ecf20Sopenharmony_ci	}
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	if (test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) {
17698c2ecf20Sopenharmony_ci		ret = -EPERM;
17708c2ecf20Sopenharmony_ci		goto out_free;
17718c2ecf20Sopenharmony_ci	}
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	old_size = btrfs_device_get_total_bytes(device);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	if (mod < 0) {
17768c2ecf20Sopenharmony_ci		if (new_size > old_size) {
17778c2ecf20Sopenharmony_ci			ret = -EINVAL;
17788c2ecf20Sopenharmony_ci			goto out_free;
17798c2ecf20Sopenharmony_ci		}
17808c2ecf20Sopenharmony_ci		new_size = old_size - new_size;
17818c2ecf20Sopenharmony_ci	} else if (mod > 0) {
17828c2ecf20Sopenharmony_ci		if (new_size > ULLONG_MAX - old_size) {
17838c2ecf20Sopenharmony_ci			ret = -ERANGE;
17848c2ecf20Sopenharmony_ci			goto out_free;
17858c2ecf20Sopenharmony_ci		}
17868c2ecf20Sopenharmony_ci		new_size = old_size + new_size;
17878c2ecf20Sopenharmony_ci	}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	if (new_size < SZ_256M) {
17908c2ecf20Sopenharmony_ci		ret = -EINVAL;
17918c2ecf20Sopenharmony_ci		goto out_free;
17928c2ecf20Sopenharmony_ci	}
17938c2ecf20Sopenharmony_ci	if (new_size > device->bdev->bd_inode->i_size) {
17948c2ecf20Sopenharmony_ci		ret = -EFBIG;
17958c2ecf20Sopenharmony_ci		goto out_free;
17968c2ecf20Sopenharmony_ci	}
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	new_size = round_down(new_size, fs_info->sectorsize);
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	if (new_size > old_size) {
18018c2ecf20Sopenharmony_ci		trans = btrfs_start_transaction(root, 0);
18028c2ecf20Sopenharmony_ci		if (IS_ERR(trans)) {
18038c2ecf20Sopenharmony_ci			ret = PTR_ERR(trans);
18048c2ecf20Sopenharmony_ci			goto out_free;
18058c2ecf20Sopenharmony_ci		}
18068c2ecf20Sopenharmony_ci		ret = btrfs_grow_device(trans, device, new_size);
18078c2ecf20Sopenharmony_ci		btrfs_commit_transaction(trans);
18088c2ecf20Sopenharmony_ci	} else if (new_size < old_size) {
18098c2ecf20Sopenharmony_ci		ret = btrfs_shrink_device(device, new_size);
18108c2ecf20Sopenharmony_ci	} /* equal, nothing need to do */
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	if (ret == 0 && new_size != old_size)
18138c2ecf20Sopenharmony_ci		btrfs_info_in_rcu(fs_info,
18148c2ecf20Sopenharmony_ci			"resize device %s (devid %llu) from %llu to %llu",
18158c2ecf20Sopenharmony_ci			rcu_str_deref(device->name), device->devid,
18168c2ecf20Sopenharmony_ci			old_size, new_size);
18178c2ecf20Sopenharmony_ciout_free:
18188c2ecf20Sopenharmony_ci	kfree(vol_args);
18198c2ecf20Sopenharmony_ciout:
18208c2ecf20Sopenharmony_ci	btrfs_exclop_finish(fs_info);
18218c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
18228c2ecf20Sopenharmony_ci	return ret;
18238c2ecf20Sopenharmony_ci}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_cistatic noinline int __btrfs_ioctl_snap_create(struct file *file,
18268c2ecf20Sopenharmony_ci				const char *name, unsigned long fd, int subvol,
18278c2ecf20Sopenharmony_ci				bool readonly,
18288c2ecf20Sopenharmony_ci				struct btrfs_qgroup_inherit *inherit)
18298c2ecf20Sopenharmony_ci{
18308c2ecf20Sopenharmony_ci	int namelen;
18318c2ecf20Sopenharmony_ci	int ret = 0;
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	if (!S_ISDIR(file_inode(file)->i_mode))
18348c2ecf20Sopenharmony_ci		return -ENOTDIR;
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
18378c2ecf20Sopenharmony_ci	if (ret)
18388c2ecf20Sopenharmony_ci		goto out;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	namelen = strlen(name);
18418c2ecf20Sopenharmony_ci	if (strchr(name, '/')) {
18428c2ecf20Sopenharmony_ci		ret = -EINVAL;
18438c2ecf20Sopenharmony_ci		goto out_drop_write;
18448c2ecf20Sopenharmony_ci	}
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	if (name[0] == '.' &&
18478c2ecf20Sopenharmony_ci	   (namelen == 1 || (name[1] == '.' && namelen == 2))) {
18488c2ecf20Sopenharmony_ci		ret = -EEXIST;
18498c2ecf20Sopenharmony_ci		goto out_drop_write;
18508c2ecf20Sopenharmony_ci	}
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	if (subvol) {
18538c2ecf20Sopenharmony_ci		ret = btrfs_mksubvol(&file->f_path, name, namelen,
18548c2ecf20Sopenharmony_ci				     NULL, readonly, inherit);
18558c2ecf20Sopenharmony_ci	} else {
18568c2ecf20Sopenharmony_ci		struct fd src = fdget(fd);
18578c2ecf20Sopenharmony_ci		struct inode *src_inode;
18588c2ecf20Sopenharmony_ci		if (!src.file) {
18598c2ecf20Sopenharmony_ci			ret = -EINVAL;
18608c2ecf20Sopenharmony_ci			goto out_drop_write;
18618c2ecf20Sopenharmony_ci		}
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci		src_inode = file_inode(src.file);
18648c2ecf20Sopenharmony_ci		if (src_inode->i_sb != file_inode(file)->i_sb) {
18658c2ecf20Sopenharmony_ci			btrfs_info(BTRFS_I(file_inode(file))->root->fs_info,
18668c2ecf20Sopenharmony_ci				   "Snapshot src from another FS");
18678c2ecf20Sopenharmony_ci			ret = -EXDEV;
18688c2ecf20Sopenharmony_ci		} else if (!inode_owner_or_capable(src_inode)) {
18698c2ecf20Sopenharmony_ci			/*
18708c2ecf20Sopenharmony_ci			 * Subvolume creation is not restricted, but snapshots
18718c2ecf20Sopenharmony_ci			 * are limited to own subvolumes only
18728c2ecf20Sopenharmony_ci			 */
18738c2ecf20Sopenharmony_ci			ret = -EPERM;
18748c2ecf20Sopenharmony_ci		} else if (btrfs_ino(BTRFS_I(src_inode)) != BTRFS_FIRST_FREE_OBJECTID) {
18758c2ecf20Sopenharmony_ci			/*
18768c2ecf20Sopenharmony_ci			 * Snapshots must be made with the src_inode referring
18778c2ecf20Sopenharmony_ci			 * to the subvolume inode, otherwise the permission
18788c2ecf20Sopenharmony_ci			 * checking above is useless because we may have
18798c2ecf20Sopenharmony_ci			 * permission on a lower directory but not the subvol
18808c2ecf20Sopenharmony_ci			 * itself.
18818c2ecf20Sopenharmony_ci			 */
18828c2ecf20Sopenharmony_ci			ret = -EINVAL;
18838c2ecf20Sopenharmony_ci		} else {
18848c2ecf20Sopenharmony_ci			ret = btrfs_mksnapshot(&file->f_path, name, namelen,
18858c2ecf20Sopenharmony_ci					     BTRFS_I(src_inode)->root,
18868c2ecf20Sopenharmony_ci					     readonly, inherit);
18878c2ecf20Sopenharmony_ci		}
18888c2ecf20Sopenharmony_ci		fdput(src);
18898c2ecf20Sopenharmony_ci	}
18908c2ecf20Sopenharmony_ciout_drop_write:
18918c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
18928c2ecf20Sopenharmony_ciout:
18938c2ecf20Sopenharmony_ci	return ret;
18948c2ecf20Sopenharmony_ci}
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_snap_create(struct file *file,
18978c2ecf20Sopenharmony_ci					    void __user *arg, int subvol)
18988c2ecf20Sopenharmony_ci{
18998c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args *vol_args;
19008c2ecf20Sopenharmony_ci	int ret;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	if (!S_ISDIR(file_inode(file)->i_mode))
19038c2ecf20Sopenharmony_ci		return -ENOTDIR;
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	vol_args = memdup_user(arg, sizeof(*vol_args));
19068c2ecf20Sopenharmony_ci	if (IS_ERR(vol_args))
19078c2ecf20Sopenharmony_ci		return PTR_ERR(vol_args);
19088c2ecf20Sopenharmony_ci	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd,
19118c2ecf20Sopenharmony_ci					subvol, false, NULL);
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	kfree(vol_args);
19148c2ecf20Sopenharmony_ci	return ret;
19158c2ecf20Sopenharmony_ci}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_snap_create_v2(struct file *file,
19188c2ecf20Sopenharmony_ci					       void __user *arg, int subvol)
19198c2ecf20Sopenharmony_ci{
19208c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args_v2 *vol_args;
19218c2ecf20Sopenharmony_ci	int ret;
19228c2ecf20Sopenharmony_ci	bool readonly = false;
19238c2ecf20Sopenharmony_ci	struct btrfs_qgroup_inherit *inherit = NULL;
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	if (!S_ISDIR(file_inode(file)->i_mode))
19268c2ecf20Sopenharmony_ci		return -ENOTDIR;
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	vol_args = memdup_user(arg, sizeof(*vol_args));
19298c2ecf20Sopenharmony_ci	if (IS_ERR(vol_args))
19308c2ecf20Sopenharmony_ci		return PTR_ERR(vol_args);
19318c2ecf20Sopenharmony_ci	vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	if (vol_args->flags & ~BTRFS_SUBVOL_CREATE_ARGS_MASK) {
19348c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
19358c2ecf20Sopenharmony_ci		goto free_args;
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	if (vol_args->flags & BTRFS_SUBVOL_RDONLY)
19398c2ecf20Sopenharmony_ci		readonly = true;
19408c2ecf20Sopenharmony_ci	if (vol_args->flags & BTRFS_SUBVOL_QGROUP_INHERIT) {
19418c2ecf20Sopenharmony_ci		u64 nums;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci		if (vol_args->size < sizeof(*inherit) ||
19448c2ecf20Sopenharmony_ci		    vol_args->size > PAGE_SIZE) {
19458c2ecf20Sopenharmony_ci			ret = -EINVAL;
19468c2ecf20Sopenharmony_ci			goto free_args;
19478c2ecf20Sopenharmony_ci		}
19488c2ecf20Sopenharmony_ci		inherit = memdup_user(vol_args->qgroup_inherit, vol_args->size);
19498c2ecf20Sopenharmony_ci		if (IS_ERR(inherit)) {
19508c2ecf20Sopenharmony_ci			ret = PTR_ERR(inherit);
19518c2ecf20Sopenharmony_ci			goto free_args;
19528c2ecf20Sopenharmony_ci		}
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci		if (inherit->num_qgroups > PAGE_SIZE ||
19558c2ecf20Sopenharmony_ci		    inherit->num_ref_copies > PAGE_SIZE ||
19568c2ecf20Sopenharmony_ci		    inherit->num_excl_copies > PAGE_SIZE) {
19578c2ecf20Sopenharmony_ci			ret = -EINVAL;
19588c2ecf20Sopenharmony_ci			goto free_inherit;
19598c2ecf20Sopenharmony_ci		}
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci		nums = inherit->num_qgroups + 2 * inherit->num_ref_copies +
19628c2ecf20Sopenharmony_ci		       2 * inherit->num_excl_copies;
19638c2ecf20Sopenharmony_ci		if (vol_args->size != struct_size(inherit, qgroups, nums)) {
19648c2ecf20Sopenharmony_ci			ret = -EINVAL;
19658c2ecf20Sopenharmony_ci			goto free_inherit;
19668c2ecf20Sopenharmony_ci		}
19678c2ecf20Sopenharmony_ci	}
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd,
19708c2ecf20Sopenharmony_ci					subvol, readonly, inherit);
19718c2ecf20Sopenharmony_ci	if (ret)
19728c2ecf20Sopenharmony_ci		goto free_inherit;
19738c2ecf20Sopenharmony_cifree_inherit:
19748c2ecf20Sopenharmony_ci	kfree(inherit);
19758c2ecf20Sopenharmony_cifree_args:
19768c2ecf20Sopenharmony_ci	kfree(vol_args);
19778c2ecf20Sopenharmony_ci	return ret;
19788c2ecf20Sopenharmony_ci}
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_subvol_getflags(struct file *file,
19818c2ecf20Sopenharmony_ci						void __user *arg)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
19848c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
19858c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
19868c2ecf20Sopenharmony_ci	int ret = 0;
19878c2ecf20Sopenharmony_ci	u64 flags = 0;
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID)
19908c2ecf20Sopenharmony_ci		return -EINVAL;
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	down_read(&fs_info->subvol_sem);
19938c2ecf20Sopenharmony_ci	if (btrfs_root_readonly(root))
19948c2ecf20Sopenharmony_ci		flags |= BTRFS_SUBVOL_RDONLY;
19958c2ecf20Sopenharmony_ci	up_read(&fs_info->subvol_sem);
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &flags, sizeof(flags)))
19988c2ecf20Sopenharmony_ci		ret = -EFAULT;
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	return ret;
20018c2ecf20Sopenharmony_ci}
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_subvol_setflags(struct file *file,
20048c2ecf20Sopenharmony_ci					      void __user *arg)
20058c2ecf20Sopenharmony_ci{
20068c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
20078c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
20088c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
20098c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
20108c2ecf20Sopenharmony_ci	u64 root_flags;
20118c2ecf20Sopenharmony_ci	u64 flags;
20128c2ecf20Sopenharmony_ci	int ret = 0;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	if (!inode_owner_or_capable(inode))
20158c2ecf20Sopenharmony_ci		return -EPERM;
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
20188c2ecf20Sopenharmony_ci	if (ret)
20198c2ecf20Sopenharmony_ci		goto out;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) {
20228c2ecf20Sopenharmony_ci		ret = -EINVAL;
20238c2ecf20Sopenharmony_ci		goto out_drop_write;
20248c2ecf20Sopenharmony_ci	}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	if (copy_from_user(&flags, arg, sizeof(flags))) {
20278c2ecf20Sopenharmony_ci		ret = -EFAULT;
20288c2ecf20Sopenharmony_ci		goto out_drop_write;
20298c2ecf20Sopenharmony_ci	}
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	if (flags & ~BTRFS_SUBVOL_RDONLY) {
20328c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
20338c2ecf20Sopenharmony_ci		goto out_drop_write;
20348c2ecf20Sopenharmony_ci	}
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	down_write(&fs_info->subvol_sem);
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	/* nothing to do */
20398c2ecf20Sopenharmony_ci	if (!!(flags & BTRFS_SUBVOL_RDONLY) == btrfs_root_readonly(root))
20408c2ecf20Sopenharmony_ci		goto out_drop_sem;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	root_flags = btrfs_root_flags(&root->root_item);
20438c2ecf20Sopenharmony_ci	if (flags & BTRFS_SUBVOL_RDONLY) {
20448c2ecf20Sopenharmony_ci		btrfs_set_root_flags(&root->root_item,
20458c2ecf20Sopenharmony_ci				     root_flags | BTRFS_ROOT_SUBVOL_RDONLY);
20468c2ecf20Sopenharmony_ci	} else {
20478c2ecf20Sopenharmony_ci		/*
20488c2ecf20Sopenharmony_ci		 * Block RO -> RW transition if this subvolume is involved in
20498c2ecf20Sopenharmony_ci		 * send
20508c2ecf20Sopenharmony_ci		 */
20518c2ecf20Sopenharmony_ci		spin_lock(&root->root_item_lock);
20528c2ecf20Sopenharmony_ci		if (root->send_in_progress == 0) {
20538c2ecf20Sopenharmony_ci			btrfs_set_root_flags(&root->root_item,
20548c2ecf20Sopenharmony_ci				     root_flags & ~BTRFS_ROOT_SUBVOL_RDONLY);
20558c2ecf20Sopenharmony_ci			spin_unlock(&root->root_item_lock);
20568c2ecf20Sopenharmony_ci		} else {
20578c2ecf20Sopenharmony_ci			spin_unlock(&root->root_item_lock);
20588c2ecf20Sopenharmony_ci			btrfs_warn(fs_info,
20598c2ecf20Sopenharmony_ci				   "Attempt to set subvolume %llu read-write during send",
20608c2ecf20Sopenharmony_ci				   root->root_key.objectid);
20618c2ecf20Sopenharmony_ci			ret = -EPERM;
20628c2ecf20Sopenharmony_ci			goto out_drop_sem;
20638c2ecf20Sopenharmony_ci		}
20648c2ecf20Sopenharmony_ci	}
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 1);
20678c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
20688c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
20698c2ecf20Sopenharmony_ci		goto out_reset;
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	ret = btrfs_update_root(trans, fs_info->tree_root,
20738c2ecf20Sopenharmony_ci				&root->root_key, &root->root_item);
20748c2ecf20Sopenharmony_ci	if (ret < 0) {
20758c2ecf20Sopenharmony_ci		btrfs_end_transaction(trans);
20768c2ecf20Sopenharmony_ci		goto out_reset;
20778c2ecf20Sopenharmony_ci	}
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	ret = btrfs_commit_transaction(trans);
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ciout_reset:
20828c2ecf20Sopenharmony_ci	if (ret)
20838c2ecf20Sopenharmony_ci		btrfs_set_root_flags(&root->root_item, root_flags);
20848c2ecf20Sopenharmony_ciout_drop_sem:
20858c2ecf20Sopenharmony_ci	up_write(&fs_info->subvol_sem);
20868c2ecf20Sopenharmony_ciout_drop_write:
20878c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
20888c2ecf20Sopenharmony_ciout:
20898c2ecf20Sopenharmony_ci	return ret;
20908c2ecf20Sopenharmony_ci}
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_cistatic noinline int key_in_sk(struct btrfs_key *key,
20938c2ecf20Sopenharmony_ci			      struct btrfs_ioctl_search_key *sk)
20948c2ecf20Sopenharmony_ci{
20958c2ecf20Sopenharmony_ci	struct btrfs_key test;
20968c2ecf20Sopenharmony_ci	int ret;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	test.objectid = sk->min_objectid;
20998c2ecf20Sopenharmony_ci	test.type = sk->min_type;
21008c2ecf20Sopenharmony_ci	test.offset = sk->min_offset;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	ret = btrfs_comp_cpu_keys(key, &test);
21038c2ecf20Sopenharmony_ci	if (ret < 0)
21048c2ecf20Sopenharmony_ci		return 0;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	test.objectid = sk->max_objectid;
21078c2ecf20Sopenharmony_ci	test.type = sk->max_type;
21088c2ecf20Sopenharmony_ci	test.offset = sk->max_offset;
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	ret = btrfs_comp_cpu_keys(key, &test);
21118c2ecf20Sopenharmony_ci	if (ret > 0)
21128c2ecf20Sopenharmony_ci		return 0;
21138c2ecf20Sopenharmony_ci	return 1;
21148c2ecf20Sopenharmony_ci}
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_cistatic noinline int copy_to_sk(struct btrfs_path *path,
21178c2ecf20Sopenharmony_ci			       struct btrfs_key *key,
21188c2ecf20Sopenharmony_ci			       struct btrfs_ioctl_search_key *sk,
21198c2ecf20Sopenharmony_ci			       u64 *buf_size,
21208c2ecf20Sopenharmony_ci			       char __user *ubuf,
21218c2ecf20Sopenharmony_ci			       unsigned long *sk_offset,
21228c2ecf20Sopenharmony_ci			       int *num_found)
21238c2ecf20Sopenharmony_ci{
21248c2ecf20Sopenharmony_ci	u64 found_transid;
21258c2ecf20Sopenharmony_ci	struct extent_buffer *leaf;
21268c2ecf20Sopenharmony_ci	struct btrfs_ioctl_search_header sh;
21278c2ecf20Sopenharmony_ci	struct btrfs_key test;
21288c2ecf20Sopenharmony_ci	unsigned long item_off;
21298c2ecf20Sopenharmony_ci	unsigned long item_len;
21308c2ecf20Sopenharmony_ci	int nritems;
21318c2ecf20Sopenharmony_ci	int i;
21328c2ecf20Sopenharmony_ci	int slot;
21338c2ecf20Sopenharmony_ci	int ret = 0;
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	leaf = path->nodes[0];
21368c2ecf20Sopenharmony_ci	slot = path->slots[0];
21378c2ecf20Sopenharmony_ci	nritems = btrfs_header_nritems(leaf);
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	if (btrfs_header_generation(leaf) > sk->max_transid) {
21408c2ecf20Sopenharmony_ci		i = nritems;
21418c2ecf20Sopenharmony_ci		goto advance_key;
21428c2ecf20Sopenharmony_ci	}
21438c2ecf20Sopenharmony_ci	found_transid = btrfs_header_generation(leaf);
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	for (i = slot; i < nritems; i++) {
21468c2ecf20Sopenharmony_ci		item_off = btrfs_item_ptr_offset(leaf, i);
21478c2ecf20Sopenharmony_ci		item_len = btrfs_item_size_nr(leaf, i);
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci		btrfs_item_key_to_cpu(leaf, key, i);
21508c2ecf20Sopenharmony_ci		if (!key_in_sk(key, sk))
21518c2ecf20Sopenharmony_ci			continue;
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci		if (sizeof(sh) + item_len > *buf_size) {
21548c2ecf20Sopenharmony_ci			if (*num_found) {
21558c2ecf20Sopenharmony_ci				ret = 1;
21568c2ecf20Sopenharmony_ci				goto out;
21578c2ecf20Sopenharmony_ci			}
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci			/*
21608c2ecf20Sopenharmony_ci			 * return one empty item back for v1, which does not
21618c2ecf20Sopenharmony_ci			 * handle -EOVERFLOW
21628c2ecf20Sopenharmony_ci			 */
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci			*buf_size = sizeof(sh) + item_len;
21658c2ecf20Sopenharmony_ci			item_len = 0;
21668c2ecf20Sopenharmony_ci			ret = -EOVERFLOW;
21678c2ecf20Sopenharmony_ci		}
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci		if (sizeof(sh) + item_len + *sk_offset > *buf_size) {
21708c2ecf20Sopenharmony_ci			ret = 1;
21718c2ecf20Sopenharmony_ci			goto out;
21728c2ecf20Sopenharmony_ci		}
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci		sh.objectid = key->objectid;
21758c2ecf20Sopenharmony_ci		sh.offset = key->offset;
21768c2ecf20Sopenharmony_ci		sh.type = key->type;
21778c2ecf20Sopenharmony_ci		sh.len = item_len;
21788c2ecf20Sopenharmony_ci		sh.transid = found_transid;
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ci		/*
21818c2ecf20Sopenharmony_ci		 * Copy search result header. If we fault then loop again so we
21828c2ecf20Sopenharmony_ci		 * can fault in the pages and -EFAULT there if there's a
21838c2ecf20Sopenharmony_ci		 * problem. Otherwise we'll fault and then copy the buffer in
21848c2ecf20Sopenharmony_ci		 * properly this next time through
21858c2ecf20Sopenharmony_ci		 */
21868c2ecf20Sopenharmony_ci		if (copy_to_user_nofault(ubuf + *sk_offset, &sh, sizeof(sh))) {
21878c2ecf20Sopenharmony_ci			ret = 0;
21888c2ecf20Sopenharmony_ci			goto out;
21898c2ecf20Sopenharmony_ci		}
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci		*sk_offset += sizeof(sh);
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci		if (item_len) {
21948c2ecf20Sopenharmony_ci			char __user *up = ubuf + *sk_offset;
21958c2ecf20Sopenharmony_ci			/*
21968c2ecf20Sopenharmony_ci			 * Copy the item, same behavior as above, but reset the
21978c2ecf20Sopenharmony_ci			 * * sk_offset so we copy the full thing again.
21988c2ecf20Sopenharmony_ci			 */
21998c2ecf20Sopenharmony_ci			if (read_extent_buffer_to_user_nofault(leaf, up,
22008c2ecf20Sopenharmony_ci						item_off, item_len)) {
22018c2ecf20Sopenharmony_ci				ret = 0;
22028c2ecf20Sopenharmony_ci				*sk_offset -= sizeof(sh);
22038c2ecf20Sopenharmony_ci				goto out;
22048c2ecf20Sopenharmony_ci			}
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci			*sk_offset += item_len;
22078c2ecf20Sopenharmony_ci		}
22088c2ecf20Sopenharmony_ci		(*num_found)++;
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci		if (ret) /* -EOVERFLOW from above */
22118c2ecf20Sopenharmony_ci			goto out;
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_ci		if (*num_found >= sk->nr_items) {
22148c2ecf20Sopenharmony_ci			ret = 1;
22158c2ecf20Sopenharmony_ci			goto out;
22168c2ecf20Sopenharmony_ci		}
22178c2ecf20Sopenharmony_ci	}
22188c2ecf20Sopenharmony_ciadvance_key:
22198c2ecf20Sopenharmony_ci	ret = 0;
22208c2ecf20Sopenharmony_ci	test.objectid = sk->max_objectid;
22218c2ecf20Sopenharmony_ci	test.type = sk->max_type;
22228c2ecf20Sopenharmony_ci	test.offset = sk->max_offset;
22238c2ecf20Sopenharmony_ci	if (btrfs_comp_cpu_keys(key, &test) >= 0)
22248c2ecf20Sopenharmony_ci		ret = 1;
22258c2ecf20Sopenharmony_ci	else if (key->offset < (u64)-1)
22268c2ecf20Sopenharmony_ci		key->offset++;
22278c2ecf20Sopenharmony_ci	else if (key->type < (u8)-1) {
22288c2ecf20Sopenharmony_ci		key->offset = 0;
22298c2ecf20Sopenharmony_ci		key->type++;
22308c2ecf20Sopenharmony_ci	} else if (key->objectid < (u64)-1) {
22318c2ecf20Sopenharmony_ci		key->offset = 0;
22328c2ecf20Sopenharmony_ci		key->type = 0;
22338c2ecf20Sopenharmony_ci		key->objectid++;
22348c2ecf20Sopenharmony_ci	} else
22358c2ecf20Sopenharmony_ci		ret = 1;
22368c2ecf20Sopenharmony_ciout:
22378c2ecf20Sopenharmony_ci	/*
22388c2ecf20Sopenharmony_ci	 *  0: all items from this leaf copied, continue with next
22398c2ecf20Sopenharmony_ci	 *  1: * more items can be copied, but unused buffer is too small
22408c2ecf20Sopenharmony_ci	 *     * all items were found
22418c2ecf20Sopenharmony_ci	 *     Either way, it will stops the loop which iterates to the next
22428c2ecf20Sopenharmony_ci	 *     leaf
22438c2ecf20Sopenharmony_ci	 *  -EOVERFLOW: item was to large for buffer
22448c2ecf20Sopenharmony_ci	 *  -EFAULT: could not copy extent buffer back to userspace
22458c2ecf20Sopenharmony_ci	 */
22468c2ecf20Sopenharmony_ci	return ret;
22478c2ecf20Sopenharmony_ci}
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_cistatic noinline int search_ioctl(struct inode *inode,
22508c2ecf20Sopenharmony_ci				 struct btrfs_ioctl_search_key *sk,
22518c2ecf20Sopenharmony_ci				 u64 *buf_size,
22528c2ecf20Sopenharmony_ci				 char __user *ubuf)
22538c2ecf20Sopenharmony_ci{
22548c2ecf20Sopenharmony_ci	struct btrfs_fs_info *info = btrfs_sb(inode->i_sb);
22558c2ecf20Sopenharmony_ci	struct btrfs_root *root;
22568c2ecf20Sopenharmony_ci	struct btrfs_key key;
22578c2ecf20Sopenharmony_ci	struct btrfs_path *path;
22588c2ecf20Sopenharmony_ci	int ret;
22598c2ecf20Sopenharmony_ci	int num_found = 0;
22608c2ecf20Sopenharmony_ci	unsigned long sk_offset = 0;
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci	if (*buf_size < sizeof(struct btrfs_ioctl_search_header)) {
22638c2ecf20Sopenharmony_ci		*buf_size = sizeof(struct btrfs_ioctl_search_header);
22648c2ecf20Sopenharmony_ci		return -EOVERFLOW;
22658c2ecf20Sopenharmony_ci	}
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
22688c2ecf20Sopenharmony_ci	if (!path)
22698c2ecf20Sopenharmony_ci		return -ENOMEM;
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	if (sk->tree_id == 0) {
22728c2ecf20Sopenharmony_ci		/* search the root of the inode that was passed */
22738c2ecf20Sopenharmony_ci		root = btrfs_grab_root(BTRFS_I(inode)->root);
22748c2ecf20Sopenharmony_ci	} else {
22758c2ecf20Sopenharmony_ci		root = btrfs_get_fs_root(info, sk->tree_id, true);
22768c2ecf20Sopenharmony_ci		if (IS_ERR(root)) {
22778c2ecf20Sopenharmony_ci			btrfs_free_path(path);
22788c2ecf20Sopenharmony_ci			return PTR_ERR(root);
22798c2ecf20Sopenharmony_ci		}
22808c2ecf20Sopenharmony_ci	}
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci	key.objectid = sk->min_objectid;
22838c2ecf20Sopenharmony_ci	key.type = sk->min_type;
22848c2ecf20Sopenharmony_ci	key.offset = sk->min_offset;
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci	while (1) {
22878c2ecf20Sopenharmony_ci		ret = fault_in_pages_writeable(ubuf + sk_offset,
22888c2ecf20Sopenharmony_ci					       *buf_size - sk_offset);
22898c2ecf20Sopenharmony_ci		if (ret)
22908c2ecf20Sopenharmony_ci			break;
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci		ret = btrfs_search_forward(root, &key, path, sk->min_transid);
22938c2ecf20Sopenharmony_ci		if (ret != 0) {
22948c2ecf20Sopenharmony_ci			if (ret > 0)
22958c2ecf20Sopenharmony_ci				ret = 0;
22968c2ecf20Sopenharmony_ci			goto err;
22978c2ecf20Sopenharmony_ci		}
22988c2ecf20Sopenharmony_ci		ret = copy_to_sk(path, &key, sk, buf_size, ubuf,
22998c2ecf20Sopenharmony_ci				 &sk_offset, &num_found);
23008c2ecf20Sopenharmony_ci		btrfs_release_path(path);
23018c2ecf20Sopenharmony_ci		if (ret)
23028c2ecf20Sopenharmony_ci			break;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	}
23058c2ecf20Sopenharmony_ci	if (ret > 0)
23068c2ecf20Sopenharmony_ci		ret = 0;
23078c2ecf20Sopenharmony_cierr:
23088c2ecf20Sopenharmony_ci	sk->nr_items = num_found;
23098c2ecf20Sopenharmony_ci	btrfs_put_root(root);
23108c2ecf20Sopenharmony_ci	btrfs_free_path(path);
23118c2ecf20Sopenharmony_ci	return ret;
23128c2ecf20Sopenharmony_ci}
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_tree_search(struct file *file,
23158c2ecf20Sopenharmony_ci					   void __user *argp)
23168c2ecf20Sopenharmony_ci{
23178c2ecf20Sopenharmony_ci	struct btrfs_ioctl_search_args __user *uargs;
23188c2ecf20Sopenharmony_ci	struct btrfs_ioctl_search_key sk;
23198c2ecf20Sopenharmony_ci	struct inode *inode;
23208c2ecf20Sopenharmony_ci	int ret;
23218c2ecf20Sopenharmony_ci	u64 buf_size;
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
23248c2ecf20Sopenharmony_ci		return -EPERM;
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	uargs = (struct btrfs_ioctl_search_args __user *)argp;
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	if (copy_from_user(&sk, &uargs->key, sizeof(sk)))
23298c2ecf20Sopenharmony_ci		return -EFAULT;
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci	buf_size = sizeof(uargs->buf);
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	inode = file_inode(file);
23348c2ecf20Sopenharmony_ci	ret = search_ioctl(inode, &sk, &buf_size, uargs->buf);
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	/*
23378c2ecf20Sopenharmony_ci	 * In the origin implementation an overflow is handled by returning a
23388c2ecf20Sopenharmony_ci	 * search header with a len of zero, so reset ret.
23398c2ecf20Sopenharmony_ci	 */
23408c2ecf20Sopenharmony_ci	if (ret == -EOVERFLOW)
23418c2ecf20Sopenharmony_ci		ret = 0;
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk)))
23448c2ecf20Sopenharmony_ci		ret = -EFAULT;
23458c2ecf20Sopenharmony_ci	return ret;
23468c2ecf20Sopenharmony_ci}
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_tree_search_v2(struct file *file,
23498c2ecf20Sopenharmony_ci					       void __user *argp)
23508c2ecf20Sopenharmony_ci{
23518c2ecf20Sopenharmony_ci	struct btrfs_ioctl_search_args_v2 __user *uarg;
23528c2ecf20Sopenharmony_ci	struct btrfs_ioctl_search_args_v2 args;
23538c2ecf20Sopenharmony_ci	struct inode *inode;
23548c2ecf20Sopenharmony_ci	int ret;
23558c2ecf20Sopenharmony_ci	u64 buf_size;
23568c2ecf20Sopenharmony_ci	const u64 buf_limit = SZ_16M;
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
23598c2ecf20Sopenharmony_ci		return -EPERM;
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	/* copy search header and buffer size */
23628c2ecf20Sopenharmony_ci	uarg = (struct btrfs_ioctl_search_args_v2 __user *)argp;
23638c2ecf20Sopenharmony_ci	if (copy_from_user(&args, uarg, sizeof(args)))
23648c2ecf20Sopenharmony_ci		return -EFAULT;
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	buf_size = args.buf_size;
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci	/* limit result size to 16MB */
23698c2ecf20Sopenharmony_ci	if (buf_size > buf_limit)
23708c2ecf20Sopenharmony_ci		buf_size = buf_limit;
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci	inode = file_inode(file);
23738c2ecf20Sopenharmony_ci	ret = search_ioctl(inode, &args.key, &buf_size,
23748c2ecf20Sopenharmony_ci			   (char __user *)(&uarg->buf[0]));
23758c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(&uarg->key, &args.key, sizeof(args.key)))
23768c2ecf20Sopenharmony_ci		ret = -EFAULT;
23778c2ecf20Sopenharmony_ci	else if (ret == -EOVERFLOW &&
23788c2ecf20Sopenharmony_ci		copy_to_user(&uarg->buf_size, &buf_size, sizeof(buf_size)))
23798c2ecf20Sopenharmony_ci		ret = -EFAULT;
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci	return ret;
23828c2ecf20Sopenharmony_ci}
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci/*
23858c2ecf20Sopenharmony_ci * Search INODE_REFs to identify path name of 'dirid' directory
23868c2ecf20Sopenharmony_ci * in a 'tree_id' tree. and sets path name to 'name'.
23878c2ecf20Sopenharmony_ci */
23888c2ecf20Sopenharmony_cistatic noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
23898c2ecf20Sopenharmony_ci				u64 tree_id, u64 dirid, char *name)
23908c2ecf20Sopenharmony_ci{
23918c2ecf20Sopenharmony_ci	struct btrfs_root *root;
23928c2ecf20Sopenharmony_ci	struct btrfs_key key;
23938c2ecf20Sopenharmony_ci	char *ptr;
23948c2ecf20Sopenharmony_ci	int ret = -1;
23958c2ecf20Sopenharmony_ci	int slot;
23968c2ecf20Sopenharmony_ci	int len;
23978c2ecf20Sopenharmony_ci	int total_len = 0;
23988c2ecf20Sopenharmony_ci	struct btrfs_inode_ref *iref;
23998c2ecf20Sopenharmony_ci	struct extent_buffer *l;
24008c2ecf20Sopenharmony_ci	struct btrfs_path *path;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci	if (dirid == BTRFS_FIRST_FREE_OBJECTID) {
24038c2ecf20Sopenharmony_ci		name[0]='\0';
24048c2ecf20Sopenharmony_ci		return 0;
24058c2ecf20Sopenharmony_ci	}
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
24088c2ecf20Sopenharmony_ci	if (!path)
24098c2ecf20Sopenharmony_ci		return -ENOMEM;
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	ptr = &name[BTRFS_INO_LOOKUP_PATH_MAX - 1];
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci	root = btrfs_get_fs_root(info, tree_id, true);
24148c2ecf20Sopenharmony_ci	if (IS_ERR(root)) {
24158c2ecf20Sopenharmony_ci		ret = PTR_ERR(root);
24168c2ecf20Sopenharmony_ci		root = NULL;
24178c2ecf20Sopenharmony_ci		goto out;
24188c2ecf20Sopenharmony_ci	}
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_ci	key.objectid = dirid;
24218c2ecf20Sopenharmony_ci	key.type = BTRFS_INODE_REF_KEY;
24228c2ecf20Sopenharmony_ci	key.offset = (u64)-1;
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	while (1) {
24258c2ecf20Sopenharmony_ci		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
24268c2ecf20Sopenharmony_ci		if (ret < 0)
24278c2ecf20Sopenharmony_ci			goto out;
24288c2ecf20Sopenharmony_ci		else if (ret > 0) {
24298c2ecf20Sopenharmony_ci			ret = btrfs_previous_item(root, path, dirid,
24308c2ecf20Sopenharmony_ci						  BTRFS_INODE_REF_KEY);
24318c2ecf20Sopenharmony_ci			if (ret < 0)
24328c2ecf20Sopenharmony_ci				goto out;
24338c2ecf20Sopenharmony_ci			else if (ret > 0) {
24348c2ecf20Sopenharmony_ci				ret = -ENOENT;
24358c2ecf20Sopenharmony_ci				goto out;
24368c2ecf20Sopenharmony_ci			}
24378c2ecf20Sopenharmony_ci		}
24388c2ecf20Sopenharmony_ci
24398c2ecf20Sopenharmony_ci		l = path->nodes[0];
24408c2ecf20Sopenharmony_ci		slot = path->slots[0];
24418c2ecf20Sopenharmony_ci		btrfs_item_key_to_cpu(l, &key, slot);
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci		iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref);
24448c2ecf20Sopenharmony_ci		len = btrfs_inode_ref_name_len(l, iref);
24458c2ecf20Sopenharmony_ci		ptr -= len + 1;
24468c2ecf20Sopenharmony_ci		total_len += len + 1;
24478c2ecf20Sopenharmony_ci		if (ptr < name) {
24488c2ecf20Sopenharmony_ci			ret = -ENAMETOOLONG;
24498c2ecf20Sopenharmony_ci			goto out;
24508c2ecf20Sopenharmony_ci		}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci		*(ptr + len) = '/';
24538c2ecf20Sopenharmony_ci		read_extent_buffer(l, ptr, (unsigned long)(iref + 1), len);
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci		if (key.offset == BTRFS_FIRST_FREE_OBJECTID)
24568c2ecf20Sopenharmony_ci			break;
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci		btrfs_release_path(path);
24598c2ecf20Sopenharmony_ci		key.objectid = key.offset;
24608c2ecf20Sopenharmony_ci		key.offset = (u64)-1;
24618c2ecf20Sopenharmony_ci		dirid = key.objectid;
24628c2ecf20Sopenharmony_ci	}
24638c2ecf20Sopenharmony_ci	memmove(name, ptr, total_len);
24648c2ecf20Sopenharmony_ci	name[total_len] = '\0';
24658c2ecf20Sopenharmony_ci	ret = 0;
24668c2ecf20Sopenharmony_ciout:
24678c2ecf20Sopenharmony_ci	btrfs_put_root(root);
24688c2ecf20Sopenharmony_ci	btrfs_free_path(path);
24698c2ecf20Sopenharmony_ci	return ret;
24708c2ecf20Sopenharmony_ci}
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_cistatic int btrfs_search_path_in_tree_user(struct inode *inode,
24738c2ecf20Sopenharmony_ci				struct btrfs_ioctl_ino_lookup_user_args *args)
24748c2ecf20Sopenharmony_ci{
24758c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
24768c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
24778c2ecf20Sopenharmony_ci	struct btrfs_key upper_limit = BTRFS_I(inode)->location;
24788c2ecf20Sopenharmony_ci	u64 treeid = BTRFS_I(inode)->root->root_key.objectid;
24798c2ecf20Sopenharmony_ci	u64 dirid = args->dirid;
24808c2ecf20Sopenharmony_ci	unsigned long item_off;
24818c2ecf20Sopenharmony_ci	unsigned long item_len;
24828c2ecf20Sopenharmony_ci	struct btrfs_inode_ref *iref;
24838c2ecf20Sopenharmony_ci	struct btrfs_root_ref *rref;
24848c2ecf20Sopenharmony_ci	struct btrfs_root *root = NULL;
24858c2ecf20Sopenharmony_ci	struct btrfs_path *path;
24868c2ecf20Sopenharmony_ci	struct btrfs_key key, key2;
24878c2ecf20Sopenharmony_ci	struct extent_buffer *leaf;
24888c2ecf20Sopenharmony_ci	struct inode *temp_inode;
24898c2ecf20Sopenharmony_ci	char *ptr;
24908c2ecf20Sopenharmony_ci	int slot;
24918c2ecf20Sopenharmony_ci	int len;
24928c2ecf20Sopenharmony_ci	int total_len = 0;
24938c2ecf20Sopenharmony_ci	int ret;
24948c2ecf20Sopenharmony_ci
24958c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
24968c2ecf20Sopenharmony_ci	if (!path)
24978c2ecf20Sopenharmony_ci		return -ENOMEM;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	/*
25008c2ecf20Sopenharmony_ci	 * If the bottom subvolume does not exist directly under upper_limit,
25018c2ecf20Sopenharmony_ci	 * construct the path in from the bottom up.
25028c2ecf20Sopenharmony_ci	 */
25038c2ecf20Sopenharmony_ci	if (dirid != upper_limit.objectid) {
25048c2ecf20Sopenharmony_ci		ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1];
25058c2ecf20Sopenharmony_ci
25068c2ecf20Sopenharmony_ci		root = btrfs_get_fs_root(fs_info, treeid, true);
25078c2ecf20Sopenharmony_ci		if (IS_ERR(root)) {
25088c2ecf20Sopenharmony_ci			ret = PTR_ERR(root);
25098c2ecf20Sopenharmony_ci			goto out;
25108c2ecf20Sopenharmony_ci		}
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci		key.objectid = dirid;
25138c2ecf20Sopenharmony_ci		key.type = BTRFS_INODE_REF_KEY;
25148c2ecf20Sopenharmony_ci		key.offset = (u64)-1;
25158c2ecf20Sopenharmony_ci		while (1) {
25168c2ecf20Sopenharmony_ci			ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
25178c2ecf20Sopenharmony_ci			if (ret < 0) {
25188c2ecf20Sopenharmony_ci				goto out_put;
25198c2ecf20Sopenharmony_ci			} else if (ret > 0) {
25208c2ecf20Sopenharmony_ci				ret = btrfs_previous_item(root, path, dirid,
25218c2ecf20Sopenharmony_ci							  BTRFS_INODE_REF_KEY);
25228c2ecf20Sopenharmony_ci				if (ret < 0) {
25238c2ecf20Sopenharmony_ci					goto out_put;
25248c2ecf20Sopenharmony_ci				} else if (ret > 0) {
25258c2ecf20Sopenharmony_ci					ret = -ENOENT;
25268c2ecf20Sopenharmony_ci					goto out_put;
25278c2ecf20Sopenharmony_ci				}
25288c2ecf20Sopenharmony_ci			}
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci			leaf = path->nodes[0];
25318c2ecf20Sopenharmony_ci			slot = path->slots[0];
25328c2ecf20Sopenharmony_ci			btrfs_item_key_to_cpu(leaf, &key, slot);
25338c2ecf20Sopenharmony_ci
25348c2ecf20Sopenharmony_ci			iref = btrfs_item_ptr(leaf, slot, struct btrfs_inode_ref);
25358c2ecf20Sopenharmony_ci			len = btrfs_inode_ref_name_len(leaf, iref);
25368c2ecf20Sopenharmony_ci			ptr -= len + 1;
25378c2ecf20Sopenharmony_ci			total_len += len + 1;
25388c2ecf20Sopenharmony_ci			if (ptr < args->path) {
25398c2ecf20Sopenharmony_ci				ret = -ENAMETOOLONG;
25408c2ecf20Sopenharmony_ci				goto out_put;
25418c2ecf20Sopenharmony_ci			}
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci			*(ptr + len) = '/';
25448c2ecf20Sopenharmony_ci			read_extent_buffer(leaf, ptr,
25458c2ecf20Sopenharmony_ci					(unsigned long)(iref + 1), len);
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci			/* Check the read+exec permission of this directory */
25488c2ecf20Sopenharmony_ci			ret = btrfs_previous_item(root, path, dirid,
25498c2ecf20Sopenharmony_ci						  BTRFS_INODE_ITEM_KEY);
25508c2ecf20Sopenharmony_ci			if (ret < 0) {
25518c2ecf20Sopenharmony_ci				goto out_put;
25528c2ecf20Sopenharmony_ci			} else if (ret > 0) {
25538c2ecf20Sopenharmony_ci				ret = -ENOENT;
25548c2ecf20Sopenharmony_ci				goto out_put;
25558c2ecf20Sopenharmony_ci			}
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci			leaf = path->nodes[0];
25588c2ecf20Sopenharmony_ci			slot = path->slots[0];
25598c2ecf20Sopenharmony_ci			btrfs_item_key_to_cpu(leaf, &key2, slot);
25608c2ecf20Sopenharmony_ci			if (key2.objectid != dirid) {
25618c2ecf20Sopenharmony_ci				ret = -ENOENT;
25628c2ecf20Sopenharmony_ci				goto out_put;
25638c2ecf20Sopenharmony_ci			}
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci			/*
25668c2ecf20Sopenharmony_ci			 * We don't need the path anymore, so release it and
25678c2ecf20Sopenharmony_ci			 * avoid deadlocks and lockdep warnings in case
25688c2ecf20Sopenharmony_ci			 * btrfs_iget() needs to lookup the inode from its root
25698c2ecf20Sopenharmony_ci			 * btree and lock the same leaf.
25708c2ecf20Sopenharmony_ci			 */
25718c2ecf20Sopenharmony_ci			btrfs_release_path(path);
25728c2ecf20Sopenharmony_ci			temp_inode = btrfs_iget(sb, key2.objectid, root);
25738c2ecf20Sopenharmony_ci			if (IS_ERR(temp_inode)) {
25748c2ecf20Sopenharmony_ci				ret = PTR_ERR(temp_inode);
25758c2ecf20Sopenharmony_ci				goto out_put;
25768c2ecf20Sopenharmony_ci			}
25778c2ecf20Sopenharmony_ci			ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
25788c2ecf20Sopenharmony_ci			iput(temp_inode);
25798c2ecf20Sopenharmony_ci			if (ret) {
25808c2ecf20Sopenharmony_ci				ret = -EACCES;
25818c2ecf20Sopenharmony_ci				goto out_put;
25828c2ecf20Sopenharmony_ci			}
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ci			if (key.offset == upper_limit.objectid)
25858c2ecf20Sopenharmony_ci				break;
25868c2ecf20Sopenharmony_ci			if (key.objectid == BTRFS_FIRST_FREE_OBJECTID) {
25878c2ecf20Sopenharmony_ci				ret = -EACCES;
25888c2ecf20Sopenharmony_ci				goto out_put;
25898c2ecf20Sopenharmony_ci			}
25908c2ecf20Sopenharmony_ci
25918c2ecf20Sopenharmony_ci			key.objectid = key.offset;
25928c2ecf20Sopenharmony_ci			key.offset = (u64)-1;
25938c2ecf20Sopenharmony_ci			dirid = key.objectid;
25948c2ecf20Sopenharmony_ci		}
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci		memmove(args->path, ptr, total_len);
25978c2ecf20Sopenharmony_ci		args->path[total_len] = '\0';
25988c2ecf20Sopenharmony_ci		btrfs_put_root(root);
25998c2ecf20Sopenharmony_ci		root = NULL;
26008c2ecf20Sopenharmony_ci		btrfs_release_path(path);
26018c2ecf20Sopenharmony_ci	}
26028c2ecf20Sopenharmony_ci
26038c2ecf20Sopenharmony_ci	/* Get the bottom subvolume's name from ROOT_REF */
26048c2ecf20Sopenharmony_ci	key.objectid = treeid;
26058c2ecf20Sopenharmony_ci	key.type = BTRFS_ROOT_REF_KEY;
26068c2ecf20Sopenharmony_ci	key.offset = args->treeid;
26078c2ecf20Sopenharmony_ci	ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0);
26088c2ecf20Sopenharmony_ci	if (ret < 0) {
26098c2ecf20Sopenharmony_ci		goto out;
26108c2ecf20Sopenharmony_ci	} else if (ret > 0) {
26118c2ecf20Sopenharmony_ci		ret = -ENOENT;
26128c2ecf20Sopenharmony_ci		goto out;
26138c2ecf20Sopenharmony_ci	}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	leaf = path->nodes[0];
26168c2ecf20Sopenharmony_ci	slot = path->slots[0];
26178c2ecf20Sopenharmony_ci	btrfs_item_key_to_cpu(leaf, &key, slot);
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	item_off = btrfs_item_ptr_offset(leaf, slot);
26208c2ecf20Sopenharmony_ci	item_len = btrfs_item_size_nr(leaf, slot);
26218c2ecf20Sopenharmony_ci	/* Check if dirid in ROOT_REF corresponds to passed dirid */
26228c2ecf20Sopenharmony_ci	rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref);
26238c2ecf20Sopenharmony_ci	if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) {
26248c2ecf20Sopenharmony_ci		ret = -EINVAL;
26258c2ecf20Sopenharmony_ci		goto out;
26268c2ecf20Sopenharmony_ci	}
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	/* Copy subvolume's name */
26298c2ecf20Sopenharmony_ci	item_off += sizeof(struct btrfs_root_ref);
26308c2ecf20Sopenharmony_ci	item_len -= sizeof(struct btrfs_root_ref);
26318c2ecf20Sopenharmony_ci	read_extent_buffer(leaf, args->name, item_off, item_len);
26328c2ecf20Sopenharmony_ci	args->name[item_len] = 0;
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ciout_put:
26358c2ecf20Sopenharmony_ci	btrfs_put_root(root);
26368c2ecf20Sopenharmony_ciout:
26378c2ecf20Sopenharmony_ci	btrfs_free_path(path);
26388c2ecf20Sopenharmony_ci	return ret;
26398c2ecf20Sopenharmony_ci}
26408c2ecf20Sopenharmony_ci
26418c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_ino_lookup(struct file *file,
26428c2ecf20Sopenharmony_ci					   void __user *argp)
26438c2ecf20Sopenharmony_ci{
26448c2ecf20Sopenharmony_ci	struct btrfs_ioctl_ino_lookup_args *args;
26458c2ecf20Sopenharmony_ci	struct inode *inode;
26468c2ecf20Sopenharmony_ci	int ret = 0;
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	args = memdup_user(argp, sizeof(*args));
26498c2ecf20Sopenharmony_ci	if (IS_ERR(args))
26508c2ecf20Sopenharmony_ci		return PTR_ERR(args);
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	inode = file_inode(file);
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci	/*
26558c2ecf20Sopenharmony_ci	 * Unprivileged query to obtain the containing subvolume root id. The
26568c2ecf20Sopenharmony_ci	 * path is reset so it's consistent with btrfs_search_path_in_tree.
26578c2ecf20Sopenharmony_ci	 */
26588c2ecf20Sopenharmony_ci	if (args->treeid == 0)
26598c2ecf20Sopenharmony_ci		args->treeid = BTRFS_I(inode)->root->root_key.objectid;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	if (args->objectid == BTRFS_FIRST_FREE_OBJECTID) {
26628c2ecf20Sopenharmony_ci		args->name[0] = 0;
26638c2ecf20Sopenharmony_ci		goto out;
26648c2ecf20Sopenharmony_ci	}
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN)) {
26678c2ecf20Sopenharmony_ci		ret = -EPERM;
26688c2ecf20Sopenharmony_ci		goto out;
26698c2ecf20Sopenharmony_ci	}
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	ret = btrfs_search_path_in_tree(BTRFS_I(inode)->root->fs_info,
26728c2ecf20Sopenharmony_ci					args->treeid, args->objectid,
26738c2ecf20Sopenharmony_ci					args->name);
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ciout:
26768c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
26778c2ecf20Sopenharmony_ci		ret = -EFAULT;
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ci	kfree(args);
26808c2ecf20Sopenharmony_ci	return ret;
26818c2ecf20Sopenharmony_ci}
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci/*
26848c2ecf20Sopenharmony_ci * Version of ino_lookup ioctl (unprivileged)
26858c2ecf20Sopenharmony_ci *
26868c2ecf20Sopenharmony_ci * The main differences from ino_lookup ioctl are:
26878c2ecf20Sopenharmony_ci *
26888c2ecf20Sopenharmony_ci *   1. Read + Exec permission will be checked using inode_permission() during
26898c2ecf20Sopenharmony_ci *      path construction. -EACCES will be returned in case of failure.
26908c2ecf20Sopenharmony_ci *   2. Path construction will be stopped at the inode number which corresponds
26918c2ecf20Sopenharmony_ci *      to the fd with which this ioctl is called. If constructed path does not
26928c2ecf20Sopenharmony_ci *      exist under fd's inode, -EACCES will be returned.
26938c2ecf20Sopenharmony_ci *   3. The name of bottom subvolume is also searched and filled.
26948c2ecf20Sopenharmony_ci */
26958c2ecf20Sopenharmony_cistatic int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp)
26968c2ecf20Sopenharmony_ci{
26978c2ecf20Sopenharmony_ci	struct btrfs_ioctl_ino_lookup_user_args *args;
26988c2ecf20Sopenharmony_ci	struct inode *inode;
26998c2ecf20Sopenharmony_ci	int ret;
27008c2ecf20Sopenharmony_ci
27018c2ecf20Sopenharmony_ci	args = memdup_user(argp, sizeof(*args));
27028c2ecf20Sopenharmony_ci	if (IS_ERR(args))
27038c2ecf20Sopenharmony_ci		return PTR_ERR(args);
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci	inode = file_inode(file);
27068c2ecf20Sopenharmony_ci
27078c2ecf20Sopenharmony_ci	if (args->dirid == BTRFS_FIRST_FREE_OBJECTID &&
27088c2ecf20Sopenharmony_ci	    BTRFS_I(inode)->location.objectid != BTRFS_FIRST_FREE_OBJECTID) {
27098c2ecf20Sopenharmony_ci		/*
27108c2ecf20Sopenharmony_ci		 * The subvolume does not exist under fd with which this is
27118c2ecf20Sopenharmony_ci		 * called
27128c2ecf20Sopenharmony_ci		 */
27138c2ecf20Sopenharmony_ci		kfree(args);
27148c2ecf20Sopenharmony_ci		return -EACCES;
27158c2ecf20Sopenharmony_ci	}
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	ret = btrfs_search_path_in_tree_user(inode, args);
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
27208c2ecf20Sopenharmony_ci		ret = -EFAULT;
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci	kfree(args);
27238c2ecf20Sopenharmony_ci	return ret;
27248c2ecf20Sopenharmony_ci}
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
27278c2ecf20Sopenharmony_cistatic int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp)
27288c2ecf20Sopenharmony_ci{
27298c2ecf20Sopenharmony_ci	struct btrfs_ioctl_get_subvol_info_args *subvol_info;
27308c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info;
27318c2ecf20Sopenharmony_ci	struct btrfs_root *root;
27328c2ecf20Sopenharmony_ci	struct btrfs_path *path;
27338c2ecf20Sopenharmony_ci	struct btrfs_key key;
27348c2ecf20Sopenharmony_ci	struct btrfs_root_item *root_item;
27358c2ecf20Sopenharmony_ci	struct btrfs_root_ref *rref;
27368c2ecf20Sopenharmony_ci	struct extent_buffer *leaf;
27378c2ecf20Sopenharmony_ci	unsigned long item_off;
27388c2ecf20Sopenharmony_ci	unsigned long item_len;
27398c2ecf20Sopenharmony_ci	struct inode *inode;
27408c2ecf20Sopenharmony_ci	int slot;
27418c2ecf20Sopenharmony_ci	int ret = 0;
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
27448c2ecf20Sopenharmony_ci	if (!path)
27458c2ecf20Sopenharmony_ci		return -ENOMEM;
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
27488c2ecf20Sopenharmony_ci	if (!subvol_info) {
27498c2ecf20Sopenharmony_ci		btrfs_free_path(path);
27508c2ecf20Sopenharmony_ci		return -ENOMEM;
27518c2ecf20Sopenharmony_ci	}
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci	inode = file_inode(file);
27548c2ecf20Sopenharmony_ci	fs_info = BTRFS_I(inode)->root->fs_info;
27558c2ecf20Sopenharmony_ci
27568c2ecf20Sopenharmony_ci	/* Get root_item of inode's subvolume */
27578c2ecf20Sopenharmony_ci	key.objectid = BTRFS_I(inode)->root->root_key.objectid;
27588c2ecf20Sopenharmony_ci	root = btrfs_get_fs_root(fs_info, key.objectid, true);
27598c2ecf20Sopenharmony_ci	if (IS_ERR(root)) {
27608c2ecf20Sopenharmony_ci		ret = PTR_ERR(root);
27618c2ecf20Sopenharmony_ci		goto out_free;
27628c2ecf20Sopenharmony_ci	}
27638c2ecf20Sopenharmony_ci	root_item = &root->root_item;
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci	subvol_info->treeid = key.objectid;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci	subvol_info->generation = btrfs_root_generation(root_item);
27688c2ecf20Sopenharmony_ci	subvol_info->flags = btrfs_root_flags(root_item);
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_ci	memcpy(subvol_info->uuid, root_item->uuid, BTRFS_UUID_SIZE);
27718c2ecf20Sopenharmony_ci	memcpy(subvol_info->parent_uuid, root_item->parent_uuid,
27728c2ecf20Sopenharmony_ci						    BTRFS_UUID_SIZE);
27738c2ecf20Sopenharmony_ci	memcpy(subvol_info->received_uuid, root_item->received_uuid,
27748c2ecf20Sopenharmony_ci						    BTRFS_UUID_SIZE);
27758c2ecf20Sopenharmony_ci
27768c2ecf20Sopenharmony_ci	subvol_info->ctransid = btrfs_root_ctransid(root_item);
27778c2ecf20Sopenharmony_ci	subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item->ctime);
27788c2ecf20Sopenharmony_ci	subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item->ctime);
27798c2ecf20Sopenharmony_ci
27808c2ecf20Sopenharmony_ci	subvol_info->otransid = btrfs_root_otransid(root_item);
27818c2ecf20Sopenharmony_ci	subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item->otime);
27828c2ecf20Sopenharmony_ci	subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item->otime);
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci	subvol_info->stransid = btrfs_root_stransid(root_item);
27858c2ecf20Sopenharmony_ci	subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item->stime);
27868c2ecf20Sopenharmony_ci	subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item->stime);
27878c2ecf20Sopenharmony_ci
27888c2ecf20Sopenharmony_ci	subvol_info->rtransid = btrfs_root_rtransid(root_item);
27898c2ecf20Sopenharmony_ci	subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item->rtime);
27908c2ecf20Sopenharmony_ci	subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item->rtime);
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_ci	if (key.objectid != BTRFS_FS_TREE_OBJECTID) {
27938c2ecf20Sopenharmony_ci		/* Search root tree for ROOT_BACKREF of this subvolume */
27948c2ecf20Sopenharmony_ci		key.type = BTRFS_ROOT_BACKREF_KEY;
27958c2ecf20Sopenharmony_ci		key.offset = 0;
27968c2ecf20Sopenharmony_ci		ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0);
27978c2ecf20Sopenharmony_ci		if (ret < 0) {
27988c2ecf20Sopenharmony_ci			goto out;
27998c2ecf20Sopenharmony_ci		} else if (path->slots[0] >=
28008c2ecf20Sopenharmony_ci			   btrfs_header_nritems(path->nodes[0])) {
28018c2ecf20Sopenharmony_ci			ret = btrfs_next_leaf(fs_info->tree_root, path);
28028c2ecf20Sopenharmony_ci			if (ret < 0) {
28038c2ecf20Sopenharmony_ci				goto out;
28048c2ecf20Sopenharmony_ci			} else if (ret > 0) {
28058c2ecf20Sopenharmony_ci				ret = -EUCLEAN;
28068c2ecf20Sopenharmony_ci				goto out;
28078c2ecf20Sopenharmony_ci			}
28088c2ecf20Sopenharmony_ci		}
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci		leaf = path->nodes[0];
28118c2ecf20Sopenharmony_ci		slot = path->slots[0];
28128c2ecf20Sopenharmony_ci		btrfs_item_key_to_cpu(leaf, &key, slot);
28138c2ecf20Sopenharmony_ci		if (key.objectid == subvol_info->treeid &&
28148c2ecf20Sopenharmony_ci		    key.type == BTRFS_ROOT_BACKREF_KEY) {
28158c2ecf20Sopenharmony_ci			subvol_info->parent_id = key.offset;
28168c2ecf20Sopenharmony_ci
28178c2ecf20Sopenharmony_ci			rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref);
28188c2ecf20Sopenharmony_ci			subvol_info->dirid = btrfs_root_ref_dirid(leaf, rref);
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci			item_off = btrfs_item_ptr_offset(leaf, slot)
28218c2ecf20Sopenharmony_ci					+ sizeof(struct btrfs_root_ref);
28228c2ecf20Sopenharmony_ci			item_len = btrfs_item_size_nr(leaf, slot)
28238c2ecf20Sopenharmony_ci					- sizeof(struct btrfs_root_ref);
28248c2ecf20Sopenharmony_ci			read_extent_buffer(leaf, subvol_info->name,
28258c2ecf20Sopenharmony_ci					   item_off, item_len);
28268c2ecf20Sopenharmony_ci		} else {
28278c2ecf20Sopenharmony_ci			ret = -ENOENT;
28288c2ecf20Sopenharmony_ci			goto out;
28298c2ecf20Sopenharmony_ci		}
28308c2ecf20Sopenharmony_ci	}
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	btrfs_free_path(path);
28338c2ecf20Sopenharmony_ci	path = NULL;
28348c2ecf20Sopenharmony_ci	if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
28358c2ecf20Sopenharmony_ci		ret = -EFAULT;
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_ciout:
28388c2ecf20Sopenharmony_ci	btrfs_put_root(root);
28398c2ecf20Sopenharmony_ciout_free:
28408c2ecf20Sopenharmony_ci	btrfs_free_path(path);
28418c2ecf20Sopenharmony_ci	kfree(subvol_info);
28428c2ecf20Sopenharmony_ci	return ret;
28438c2ecf20Sopenharmony_ci}
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci/*
28468c2ecf20Sopenharmony_ci * Return ROOT_REF information of the subvolume containing this inode
28478c2ecf20Sopenharmony_ci * except the subvolume name.
28488c2ecf20Sopenharmony_ci */
28498c2ecf20Sopenharmony_cistatic int btrfs_ioctl_get_subvol_rootref(struct file *file, void __user *argp)
28508c2ecf20Sopenharmony_ci{
28518c2ecf20Sopenharmony_ci	struct btrfs_ioctl_get_subvol_rootref_args *rootrefs;
28528c2ecf20Sopenharmony_ci	struct btrfs_root_ref *rref;
28538c2ecf20Sopenharmony_ci	struct btrfs_root *root;
28548c2ecf20Sopenharmony_ci	struct btrfs_path *path;
28558c2ecf20Sopenharmony_ci	struct btrfs_key key;
28568c2ecf20Sopenharmony_ci	struct extent_buffer *leaf;
28578c2ecf20Sopenharmony_ci	struct inode *inode;
28588c2ecf20Sopenharmony_ci	u64 objectid;
28598c2ecf20Sopenharmony_ci	int slot;
28608c2ecf20Sopenharmony_ci	int ret;
28618c2ecf20Sopenharmony_ci	u8 found;
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
28648c2ecf20Sopenharmony_ci	if (!path)
28658c2ecf20Sopenharmony_ci		return -ENOMEM;
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	rootrefs = memdup_user(argp, sizeof(*rootrefs));
28688c2ecf20Sopenharmony_ci	if (IS_ERR(rootrefs)) {
28698c2ecf20Sopenharmony_ci		btrfs_free_path(path);
28708c2ecf20Sopenharmony_ci		return PTR_ERR(rootrefs);
28718c2ecf20Sopenharmony_ci	}
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	inode = file_inode(file);
28748c2ecf20Sopenharmony_ci	root = BTRFS_I(inode)->root->fs_info->tree_root;
28758c2ecf20Sopenharmony_ci	objectid = BTRFS_I(inode)->root->root_key.objectid;
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci	key.objectid = objectid;
28788c2ecf20Sopenharmony_ci	key.type = BTRFS_ROOT_REF_KEY;
28798c2ecf20Sopenharmony_ci	key.offset = rootrefs->min_treeid;
28808c2ecf20Sopenharmony_ci	found = 0;
28818c2ecf20Sopenharmony_ci
28828c2ecf20Sopenharmony_ci	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
28838c2ecf20Sopenharmony_ci	if (ret < 0) {
28848c2ecf20Sopenharmony_ci		goto out;
28858c2ecf20Sopenharmony_ci	} else if (path->slots[0] >=
28868c2ecf20Sopenharmony_ci		   btrfs_header_nritems(path->nodes[0])) {
28878c2ecf20Sopenharmony_ci		ret = btrfs_next_leaf(root, path);
28888c2ecf20Sopenharmony_ci		if (ret < 0) {
28898c2ecf20Sopenharmony_ci			goto out;
28908c2ecf20Sopenharmony_ci		} else if (ret > 0) {
28918c2ecf20Sopenharmony_ci			ret = -EUCLEAN;
28928c2ecf20Sopenharmony_ci			goto out;
28938c2ecf20Sopenharmony_ci		}
28948c2ecf20Sopenharmony_ci	}
28958c2ecf20Sopenharmony_ci	while (1) {
28968c2ecf20Sopenharmony_ci		leaf = path->nodes[0];
28978c2ecf20Sopenharmony_ci		slot = path->slots[0];
28988c2ecf20Sopenharmony_ci
28998c2ecf20Sopenharmony_ci		btrfs_item_key_to_cpu(leaf, &key, slot);
29008c2ecf20Sopenharmony_ci		if (key.objectid != objectid || key.type != BTRFS_ROOT_REF_KEY) {
29018c2ecf20Sopenharmony_ci			ret = 0;
29028c2ecf20Sopenharmony_ci			goto out;
29038c2ecf20Sopenharmony_ci		}
29048c2ecf20Sopenharmony_ci
29058c2ecf20Sopenharmony_ci		if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) {
29068c2ecf20Sopenharmony_ci			ret = -EOVERFLOW;
29078c2ecf20Sopenharmony_ci			goto out;
29088c2ecf20Sopenharmony_ci		}
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci		rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref);
29118c2ecf20Sopenharmony_ci		rootrefs->rootref[found].treeid = key.offset;
29128c2ecf20Sopenharmony_ci		rootrefs->rootref[found].dirid =
29138c2ecf20Sopenharmony_ci				  btrfs_root_ref_dirid(leaf, rref);
29148c2ecf20Sopenharmony_ci		found++;
29158c2ecf20Sopenharmony_ci
29168c2ecf20Sopenharmony_ci		ret = btrfs_next_item(root, path);
29178c2ecf20Sopenharmony_ci		if (ret < 0) {
29188c2ecf20Sopenharmony_ci			goto out;
29198c2ecf20Sopenharmony_ci		} else if (ret > 0) {
29208c2ecf20Sopenharmony_ci			ret = -EUCLEAN;
29218c2ecf20Sopenharmony_ci			goto out;
29228c2ecf20Sopenharmony_ci		}
29238c2ecf20Sopenharmony_ci	}
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_ciout:
29268c2ecf20Sopenharmony_ci	btrfs_free_path(path);
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci	if (!ret || ret == -EOVERFLOW) {
29298c2ecf20Sopenharmony_ci		rootrefs->num_items = found;
29308c2ecf20Sopenharmony_ci		/* update min_treeid for next search */
29318c2ecf20Sopenharmony_ci		if (found)
29328c2ecf20Sopenharmony_ci			rootrefs->min_treeid =
29338c2ecf20Sopenharmony_ci				rootrefs->rootref[found - 1].treeid + 1;
29348c2ecf20Sopenharmony_ci		if (copy_to_user(argp, rootrefs, sizeof(*rootrefs)))
29358c2ecf20Sopenharmony_ci			ret = -EFAULT;
29368c2ecf20Sopenharmony_ci	}
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci	kfree(rootrefs);
29398c2ecf20Sopenharmony_ci
29408c2ecf20Sopenharmony_ci	return ret;
29418c2ecf20Sopenharmony_ci}
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_cistatic noinline int btrfs_ioctl_snap_destroy(struct file *file,
29448c2ecf20Sopenharmony_ci					     void __user *arg,
29458c2ecf20Sopenharmony_ci					     bool destroy_v2)
29468c2ecf20Sopenharmony_ci{
29478c2ecf20Sopenharmony_ci	struct dentry *parent = file->f_path.dentry;
29488c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(parent->d_sb);
29498c2ecf20Sopenharmony_ci	struct dentry *dentry;
29508c2ecf20Sopenharmony_ci	struct inode *dir = d_inode(parent);
29518c2ecf20Sopenharmony_ci	struct inode *inode;
29528c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(dir)->root;
29538c2ecf20Sopenharmony_ci	struct btrfs_root *dest = NULL;
29548c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args *vol_args = NULL;
29558c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL;
29568c2ecf20Sopenharmony_ci	char *subvol_name, *subvol_name_ptr = NULL;
29578c2ecf20Sopenharmony_ci	int subvol_namelen;
29588c2ecf20Sopenharmony_ci	int err = 0;
29598c2ecf20Sopenharmony_ci	bool destroy_parent = false;
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci	if (destroy_v2) {
29628c2ecf20Sopenharmony_ci		vol_args2 = memdup_user(arg, sizeof(*vol_args2));
29638c2ecf20Sopenharmony_ci		if (IS_ERR(vol_args2))
29648c2ecf20Sopenharmony_ci			return PTR_ERR(vol_args2);
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci		if (vol_args2->flags & ~BTRFS_SUBVOL_DELETE_ARGS_MASK) {
29678c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
29688c2ecf20Sopenharmony_ci			goto out;
29698c2ecf20Sopenharmony_ci		}
29708c2ecf20Sopenharmony_ci
29718c2ecf20Sopenharmony_ci		/*
29728c2ecf20Sopenharmony_ci		 * If SPEC_BY_ID is not set, we are looking for the subvolume by
29738c2ecf20Sopenharmony_ci		 * name, same as v1 currently does.
29748c2ecf20Sopenharmony_ci		 */
29758c2ecf20Sopenharmony_ci		if (!(vol_args2->flags & BTRFS_SUBVOL_SPEC_BY_ID)) {
29768c2ecf20Sopenharmony_ci			vol_args2->name[BTRFS_SUBVOL_NAME_MAX] = 0;
29778c2ecf20Sopenharmony_ci			subvol_name = vol_args2->name;
29788c2ecf20Sopenharmony_ci
29798c2ecf20Sopenharmony_ci			err = mnt_want_write_file(file);
29808c2ecf20Sopenharmony_ci			if (err)
29818c2ecf20Sopenharmony_ci				goto out;
29828c2ecf20Sopenharmony_ci		} else {
29838c2ecf20Sopenharmony_ci			if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) {
29848c2ecf20Sopenharmony_ci				err = -EINVAL;
29858c2ecf20Sopenharmony_ci				goto out;
29868c2ecf20Sopenharmony_ci			}
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci			err = mnt_want_write_file(file);
29898c2ecf20Sopenharmony_ci			if (err)
29908c2ecf20Sopenharmony_ci				goto out;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci			dentry = btrfs_get_dentry(fs_info->sb,
29938c2ecf20Sopenharmony_ci					BTRFS_FIRST_FREE_OBJECTID,
29948c2ecf20Sopenharmony_ci					vol_args2->subvolid, 0, 0);
29958c2ecf20Sopenharmony_ci			if (IS_ERR(dentry)) {
29968c2ecf20Sopenharmony_ci				err = PTR_ERR(dentry);
29978c2ecf20Sopenharmony_ci				goto out_drop_write;
29988c2ecf20Sopenharmony_ci			}
29998c2ecf20Sopenharmony_ci
30008c2ecf20Sopenharmony_ci			/*
30018c2ecf20Sopenharmony_ci			 * Change the default parent since the subvolume being
30028c2ecf20Sopenharmony_ci			 * deleted can be outside of the current mount point.
30038c2ecf20Sopenharmony_ci			 */
30048c2ecf20Sopenharmony_ci			parent = btrfs_get_parent(dentry);
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_ci			/*
30078c2ecf20Sopenharmony_ci			 * At this point dentry->d_name can point to '/' if the
30088c2ecf20Sopenharmony_ci			 * subvolume we want to destroy is outsite of the
30098c2ecf20Sopenharmony_ci			 * current mount point, so we need to release the
30108c2ecf20Sopenharmony_ci			 * current dentry and execute the lookup to return a new
30118c2ecf20Sopenharmony_ci			 * one with ->d_name pointing to the
30128c2ecf20Sopenharmony_ci			 * <mount point>/subvol_name.
30138c2ecf20Sopenharmony_ci			 */
30148c2ecf20Sopenharmony_ci			dput(dentry);
30158c2ecf20Sopenharmony_ci			if (IS_ERR(parent)) {
30168c2ecf20Sopenharmony_ci				err = PTR_ERR(parent);
30178c2ecf20Sopenharmony_ci				goto out_drop_write;
30188c2ecf20Sopenharmony_ci			}
30198c2ecf20Sopenharmony_ci			dir = d_inode(parent);
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_ci			/*
30228c2ecf20Sopenharmony_ci			 * If v2 was used with SPEC_BY_ID, a new parent was
30238c2ecf20Sopenharmony_ci			 * allocated since the subvolume can be outside of the
30248c2ecf20Sopenharmony_ci			 * current mount point. Later on we need to release this
30258c2ecf20Sopenharmony_ci			 * new parent dentry.
30268c2ecf20Sopenharmony_ci			 */
30278c2ecf20Sopenharmony_ci			destroy_parent = true;
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci			subvol_name_ptr = btrfs_get_subvol_name_from_objectid(
30308c2ecf20Sopenharmony_ci						fs_info, vol_args2->subvolid);
30318c2ecf20Sopenharmony_ci			if (IS_ERR(subvol_name_ptr)) {
30328c2ecf20Sopenharmony_ci				err = PTR_ERR(subvol_name_ptr);
30338c2ecf20Sopenharmony_ci				goto free_parent;
30348c2ecf20Sopenharmony_ci			}
30358c2ecf20Sopenharmony_ci			/* subvol_name_ptr is already NULL termined */
30368c2ecf20Sopenharmony_ci			subvol_name = (char *)kbasename(subvol_name_ptr);
30378c2ecf20Sopenharmony_ci		}
30388c2ecf20Sopenharmony_ci	} else {
30398c2ecf20Sopenharmony_ci		vol_args = memdup_user(arg, sizeof(*vol_args));
30408c2ecf20Sopenharmony_ci		if (IS_ERR(vol_args))
30418c2ecf20Sopenharmony_ci			return PTR_ERR(vol_args);
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_ci		vol_args->name[BTRFS_PATH_NAME_MAX] = 0;
30448c2ecf20Sopenharmony_ci		subvol_name = vol_args->name;
30458c2ecf20Sopenharmony_ci
30468c2ecf20Sopenharmony_ci		err = mnt_want_write_file(file);
30478c2ecf20Sopenharmony_ci		if (err)
30488c2ecf20Sopenharmony_ci			goto out;
30498c2ecf20Sopenharmony_ci	}
30508c2ecf20Sopenharmony_ci
30518c2ecf20Sopenharmony_ci	subvol_namelen = strlen(subvol_name);
30528c2ecf20Sopenharmony_ci
30538c2ecf20Sopenharmony_ci	if (strchr(subvol_name, '/') ||
30548c2ecf20Sopenharmony_ci	    strncmp(subvol_name, "..", subvol_namelen) == 0) {
30558c2ecf20Sopenharmony_ci		err = -EINVAL;
30568c2ecf20Sopenharmony_ci		goto free_subvol_name;
30578c2ecf20Sopenharmony_ci	}
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_ci	if (!S_ISDIR(dir->i_mode)) {
30608c2ecf20Sopenharmony_ci		err = -ENOTDIR;
30618c2ecf20Sopenharmony_ci		goto free_subvol_name;
30628c2ecf20Sopenharmony_ci	}
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
30658c2ecf20Sopenharmony_ci	if (err == -EINTR)
30668c2ecf20Sopenharmony_ci		goto free_subvol_name;
30678c2ecf20Sopenharmony_ci	dentry = lookup_one_len(subvol_name, parent, subvol_namelen);
30688c2ecf20Sopenharmony_ci	if (IS_ERR(dentry)) {
30698c2ecf20Sopenharmony_ci		err = PTR_ERR(dentry);
30708c2ecf20Sopenharmony_ci		goto out_unlock_dir;
30718c2ecf20Sopenharmony_ci	}
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_ci	if (d_really_is_negative(dentry)) {
30748c2ecf20Sopenharmony_ci		err = -ENOENT;
30758c2ecf20Sopenharmony_ci		goto out_dput;
30768c2ecf20Sopenharmony_ci	}
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	inode = d_inode(dentry);
30798c2ecf20Sopenharmony_ci	dest = BTRFS_I(inode)->root;
30808c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN)) {
30818c2ecf20Sopenharmony_ci		/*
30828c2ecf20Sopenharmony_ci		 * Regular user.  Only allow this with a special mount
30838c2ecf20Sopenharmony_ci		 * option, when the user has write+exec access to the
30848c2ecf20Sopenharmony_ci		 * subvol root, and when rmdir(2) would have been
30858c2ecf20Sopenharmony_ci		 * allowed.
30868c2ecf20Sopenharmony_ci		 *
30878c2ecf20Sopenharmony_ci		 * Note that this is _not_ check that the subvol is
30888c2ecf20Sopenharmony_ci		 * empty or doesn't contain data that we wouldn't
30898c2ecf20Sopenharmony_ci		 * otherwise be able to delete.
30908c2ecf20Sopenharmony_ci		 *
30918c2ecf20Sopenharmony_ci		 * Users who want to delete empty subvols should try
30928c2ecf20Sopenharmony_ci		 * rmdir(2).
30938c2ecf20Sopenharmony_ci		 */
30948c2ecf20Sopenharmony_ci		err = -EPERM;
30958c2ecf20Sopenharmony_ci		if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED))
30968c2ecf20Sopenharmony_ci			goto out_dput;
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_ci		/*
30998c2ecf20Sopenharmony_ci		 * Do not allow deletion if the parent dir is the same
31008c2ecf20Sopenharmony_ci		 * as the dir to be deleted.  That means the ioctl
31018c2ecf20Sopenharmony_ci		 * must be called on the dentry referencing the root
31028c2ecf20Sopenharmony_ci		 * of the subvol, not a random directory contained
31038c2ecf20Sopenharmony_ci		 * within it.
31048c2ecf20Sopenharmony_ci		 */
31058c2ecf20Sopenharmony_ci		err = -EINVAL;
31068c2ecf20Sopenharmony_ci		if (root == dest)
31078c2ecf20Sopenharmony_ci			goto out_dput;
31088c2ecf20Sopenharmony_ci
31098c2ecf20Sopenharmony_ci		err = inode_permission(inode, MAY_WRITE | MAY_EXEC);
31108c2ecf20Sopenharmony_ci		if (err)
31118c2ecf20Sopenharmony_ci			goto out_dput;
31128c2ecf20Sopenharmony_ci	}
31138c2ecf20Sopenharmony_ci
31148c2ecf20Sopenharmony_ci	/* check if subvolume may be deleted by a user */
31158c2ecf20Sopenharmony_ci	err = btrfs_may_delete(dir, dentry, 1);
31168c2ecf20Sopenharmony_ci	if (err)
31178c2ecf20Sopenharmony_ci		goto out_dput;
31188c2ecf20Sopenharmony_ci
31198c2ecf20Sopenharmony_ci	if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) {
31208c2ecf20Sopenharmony_ci		err = -EINVAL;
31218c2ecf20Sopenharmony_ci		goto out_dput;
31228c2ecf20Sopenharmony_ci	}
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci	inode_lock(inode);
31258c2ecf20Sopenharmony_ci	err = btrfs_delete_subvolume(dir, dentry);
31268c2ecf20Sopenharmony_ci	inode_unlock(inode);
31278c2ecf20Sopenharmony_ci	if (!err)
31288c2ecf20Sopenharmony_ci		d_delete_notify(dir, dentry);
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ciout_dput:
31318c2ecf20Sopenharmony_ci	dput(dentry);
31328c2ecf20Sopenharmony_ciout_unlock_dir:
31338c2ecf20Sopenharmony_ci	inode_unlock(dir);
31348c2ecf20Sopenharmony_cifree_subvol_name:
31358c2ecf20Sopenharmony_ci	kfree(subvol_name_ptr);
31368c2ecf20Sopenharmony_cifree_parent:
31378c2ecf20Sopenharmony_ci	if (destroy_parent)
31388c2ecf20Sopenharmony_ci		dput(parent);
31398c2ecf20Sopenharmony_ciout_drop_write:
31408c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
31418c2ecf20Sopenharmony_ciout:
31428c2ecf20Sopenharmony_ci	kfree(vol_args2);
31438c2ecf20Sopenharmony_ci	kfree(vol_args);
31448c2ecf20Sopenharmony_ci	return err;
31458c2ecf20Sopenharmony_ci}
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_cistatic int btrfs_ioctl_defrag(struct file *file, void __user *argp)
31488c2ecf20Sopenharmony_ci{
31498c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
31508c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
31518c2ecf20Sopenharmony_ci	struct btrfs_ioctl_defrag_range_args *range;
31528c2ecf20Sopenharmony_ci	int ret;
31538c2ecf20Sopenharmony_ci
31548c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
31558c2ecf20Sopenharmony_ci	if (ret)
31568c2ecf20Sopenharmony_ci		return ret;
31578c2ecf20Sopenharmony_ci
31588c2ecf20Sopenharmony_ci	if (btrfs_root_readonly(root)) {
31598c2ecf20Sopenharmony_ci		ret = -EROFS;
31608c2ecf20Sopenharmony_ci		goto out;
31618c2ecf20Sopenharmony_ci	}
31628c2ecf20Sopenharmony_ci
31638c2ecf20Sopenharmony_ci	switch (inode->i_mode & S_IFMT) {
31648c2ecf20Sopenharmony_ci	case S_IFDIR:
31658c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN)) {
31668c2ecf20Sopenharmony_ci			ret = -EPERM;
31678c2ecf20Sopenharmony_ci			goto out;
31688c2ecf20Sopenharmony_ci		}
31698c2ecf20Sopenharmony_ci		ret = btrfs_defrag_root(root);
31708c2ecf20Sopenharmony_ci		break;
31718c2ecf20Sopenharmony_ci	case S_IFREG:
31728c2ecf20Sopenharmony_ci		/*
31738c2ecf20Sopenharmony_ci		 * Note that this does not check the file descriptor for write
31748c2ecf20Sopenharmony_ci		 * access. This prevents defragmenting executables that are
31758c2ecf20Sopenharmony_ci		 * running and allows defrag on files open in read-only mode.
31768c2ecf20Sopenharmony_ci		 */
31778c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN) &&
31788c2ecf20Sopenharmony_ci		    inode_permission(inode, MAY_WRITE)) {
31798c2ecf20Sopenharmony_ci			ret = -EPERM;
31808c2ecf20Sopenharmony_ci			goto out;
31818c2ecf20Sopenharmony_ci		}
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci		range = kzalloc(sizeof(*range), GFP_KERNEL);
31848c2ecf20Sopenharmony_ci		if (!range) {
31858c2ecf20Sopenharmony_ci			ret = -ENOMEM;
31868c2ecf20Sopenharmony_ci			goto out;
31878c2ecf20Sopenharmony_ci		}
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ci		if (argp) {
31908c2ecf20Sopenharmony_ci			if (copy_from_user(range, argp,
31918c2ecf20Sopenharmony_ci					   sizeof(*range))) {
31928c2ecf20Sopenharmony_ci				ret = -EFAULT;
31938c2ecf20Sopenharmony_ci				kfree(range);
31948c2ecf20Sopenharmony_ci				goto out;
31958c2ecf20Sopenharmony_ci			}
31968c2ecf20Sopenharmony_ci			if (range->flags & ~BTRFS_DEFRAG_RANGE_FLAGS_SUPP) {
31978c2ecf20Sopenharmony_ci				ret = -EOPNOTSUPP;
31988c2ecf20Sopenharmony_ci				goto out;
31998c2ecf20Sopenharmony_ci			}
32008c2ecf20Sopenharmony_ci			/* compression requires us to start the IO */
32018c2ecf20Sopenharmony_ci			if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) {
32028c2ecf20Sopenharmony_ci				range->flags |= BTRFS_DEFRAG_RANGE_START_IO;
32038c2ecf20Sopenharmony_ci				range->extent_thresh = (u32)-1;
32048c2ecf20Sopenharmony_ci			}
32058c2ecf20Sopenharmony_ci		} else {
32068c2ecf20Sopenharmony_ci			/* the rest are all set to zero by kzalloc */
32078c2ecf20Sopenharmony_ci			range->len = (u64)-1;
32088c2ecf20Sopenharmony_ci		}
32098c2ecf20Sopenharmony_ci		ret = btrfs_defrag_file(file_inode(file), file,
32108c2ecf20Sopenharmony_ci					range, BTRFS_OLDEST_GENERATION, 0);
32118c2ecf20Sopenharmony_ci		if (ret > 0)
32128c2ecf20Sopenharmony_ci			ret = 0;
32138c2ecf20Sopenharmony_ci		kfree(range);
32148c2ecf20Sopenharmony_ci		break;
32158c2ecf20Sopenharmony_ci	default:
32168c2ecf20Sopenharmony_ci		ret = -EINVAL;
32178c2ecf20Sopenharmony_ci	}
32188c2ecf20Sopenharmony_ciout:
32198c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
32208c2ecf20Sopenharmony_ci	return ret;
32218c2ecf20Sopenharmony_ci}
32228c2ecf20Sopenharmony_ci
32238c2ecf20Sopenharmony_cistatic long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)
32248c2ecf20Sopenharmony_ci{
32258c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args *vol_args;
32268c2ecf20Sopenharmony_ci	int ret;
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
32298c2ecf20Sopenharmony_ci		return -EPERM;
32308c2ecf20Sopenharmony_ci
32318c2ecf20Sopenharmony_ci	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_ADD))
32328c2ecf20Sopenharmony_ci		return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
32338c2ecf20Sopenharmony_ci
32348c2ecf20Sopenharmony_ci	vol_args = memdup_user(arg, sizeof(*vol_args));
32358c2ecf20Sopenharmony_ci	if (IS_ERR(vol_args)) {
32368c2ecf20Sopenharmony_ci		ret = PTR_ERR(vol_args);
32378c2ecf20Sopenharmony_ci		goto out;
32388c2ecf20Sopenharmony_ci	}
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
32418c2ecf20Sopenharmony_ci	ret = btrfs_init_new_device(fs_info, vol_args->name);
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_ci	if (!ret)
32448c2ecf20Sopenharmony_ci		btrfs_info(fs_info, "disk added %s", vol_args->name);
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci	kfree(vol_args);
32478c2ecf20Sopenharmony_ciout:
32488c2ecf20Sopenharmony_ci	btrfs_exclop_finish(fs_info);
32498c2ecf20Sopenharmony_ci	return ret;
32508c2ecf20Sopenharmony_ci}
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_cistatic long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32538c2ecf20Sopenharmony_ci{
32548c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
32558c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
32568c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args_v2 *vol_args;
32578c2ecf20Sopenharmony_ci	int ret;
32588c2ecf20Sopenharmony_ci
32598c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
32608c2ecf20Sopenharmony_ci		return -EPERM;
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
32638c2ecf20Sopenharmony_ci	if (ret)
32648c2ecf20Sopenharmony_ci		return ret;
32658c2ecf20Sopenharmony_ci
32668c2ecf20Sopenharmony_ci	vol_args = memdup_user(arg, sizeof(*vol_args));
32678c2ecf20Sopenharmony_ci	if (IS_ERR(vol_args)) {
32688c2ecf20Sopenharmony_ci		ret = PTR_ERR(vol_args);
32698c2ecf20Sopenharmony_ci		goto err_drop;
32708c2ecf20Sopenharmony_ci	}
32718c2ecf20Sopenharmony_ci
32728c2ecf20Sopenharmony_ci	if (vol_args->flags & ~BTRFS_DEVICE_REMOVE_ARGS_MASK) {
32738c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
32748c2ecf20Sopenharmony_ci		goto out;
32758c2ecf20Sopenharmony_ci	}
32768c2ecf20Sopenharmony_ci
32778c2ecf20Sopenharmony_ci	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_REMOVE)) {
32788c2ecf20Sopenharmony_ci		ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
32798c2ecf20Sopenharmony_ci		goto out;
32808c2ecf20Sopenharmony_ci	}
32818c2ecf20Sopenharmony_ci
32828c2ecf20Sopenharmony_ci	if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) {
32838c2ecf20Sopenharmony_ci		ret = btrfs_rm_device(fs_info, NULL, vol_args->devid);
32848c2ecf20Sopenharmony_ci	} else {
32858c2ecf20Sopenharmony_ci		vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
32868c2ecf20Sopenharmony_ci		ret = btrfs_rm_device(fs_info, vol_args->name, 0);
32878c2ecf20Sopenharmony_ci	}
32888c2ecf20Sopenharmony_ci	btrfs_exclop_finish(fs_info);
32898c2ecf20Sopenharmony_ci
32908c2ecf20Sopenharmony_ci	if (!ret) {
32918c2ecf20Sopenharmony_ci		if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID)
32928c2ecf20Sopenharmony_ci			btrfs_info(fs_info, "device deleted: id %llu",
32938c2ecf20Sopenharmony_ci					vol_args->devid);
32948c2ecf20Sopenharmony_ci		else
32958c2ecf20Sopenharmony_ci			btrfs_info(fs_info, "device deleted: %s",
32968c2ecf20Sopenharmony_ci					vol_args->name);
32978c2ecf20Sopenharmony_ci	}
32988c2ecf20Sopenharmony_ciout:
32998c2ecf20Sopenharmony_ci	kfree(vol_args);
33008c2ecf20Sopenharmony_cierr_drop:
33018c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
33028c2ecf20Sopenharmony_ci	return ret;
33038c2ecf20Sopenharmony_ci}
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_cistatic long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
33068c2ecf20Sopenharmony_ci{
33078c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
33088c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
33098c2ecf20Sopenharmony_ci	struct btrfs_ioctl_vol_args *vol_args;
33108c2ecf20Sopenharmony_ci	int ret;
33118c2ecf20Sopenharmony_ci
33128c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
33138c2ecf20Sopenharmony_ci		return -EPERM;
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
33168c2ecf20Sopenharmony_ci	if (ret)
33178c2ecf20Sopenharmony_ci		return ret;
33188c2ecf20Sopenharmony_ci
33198c2ecf20Sopenharmony_ci	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_REMOVE)) {
33208c2ecf20Sopenharmony_ci		ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
33218c2ecf20Sopenharmony_ci		goto out_drop_write;
33228c2ecf20Sopenharmony_ci	}
33238c2ecf20Sopenharmony_ci
33248c2ecf20Sopenharmony_ci	vol_args = memdup_user(arg, sizeof(*vol_args));
33258c2ecf20Sopenharmony_ci	if (IS_ERR(vol_args)) {
33268c2ecf20Sopenharmony_ci		ret = PTR_ERR(vol_args);
33278c2ecf20Sopenharmony_ci		goto out;
33288c2ecf20Sopenharmony_ci	}
33298c2ecf20Sopenharmony_ci
33308c2ecf20Sopenharmony_ci	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
33318c2ecf20Sopenharmony_ci	ret = btrfs_rm_device(fs_info, vol_args->name, 0);
33328c2ecf20Sopenharmony_ci
33338c2ecf20Sopenharmony_ci	if (!ret)
33348c2ecf20Sopenharmony_ci		btrfs_info(fs_info, "disk deleted %s", vol_args->name);
33358c2ecf20Sopenharmony_ci	kfree(vol_args);
33368c2ecf20Sopenharmony_ciout:
33378c2ecf20Sopenharmony_ci	btrfs_exclop_finish(fs_info);
33388c2ecf20Sopenharmony_ciout_drop_write:
33398c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
33408c2ecf20Sopenharmony_ci
33418c2ecf20Sopenharmony_ci	return ret;
33428c2ecf20Sopenharmony_ci}
33438c2ecf20Sopenharmony_ci
33448c2ecf20Sopenharmony_cistatic long btrfs_ioctl_fs_info(struct btrfs_fs_info *fs_info,
33458c2ecf20Sopenharmony_ci				void __user *arg)
33468c2ecf20Sopenharmony_ci{
33478c2ecf20Sopenharmony_ci	struct btrfs_ioctl_fs_info_args *fi_args;
33488c2ecf20Sopenharmony_ci	struct btrfs_device *device;
33498c2ecf20Sopenharmony_ci	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
33508c2ecf20Sopenharmony_ci	u64 flags_in;
33518c2ecf20Sopenharmony_ci	int ret = 0;
33528c2ecf20Sopenharmony_ci
33538c2ecf20Sopenharmony_ci	fi_args = memdup_user(arg, sizeof(*fi_args));
33548c2ecf20Sopenharmony_ci	if (IS_ERR(fi_args))
33558c2ecf20Sopenharmony_ci		return PTR_ERR(fi_args);
33568c2ecf20Sopenharmony_ci
33578c2ecf20Sopenharmony_ci	flags_in = fi_args->flags;
33588c2ecf20Sopenharmony_ci	memset(fi_args, 0, sizeof(*fi_args));
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_ci	rcu_read_lock();
33618c2ecf20Sopenharmony_ci	fi_args->num_devices = fs_devices->num_devices;
33628c2ecf20Sopenharmony_ci
33638c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) {
33648c2ecf20Sopenharmony_ci		if (device->devid > fi_args->max_id)
33658c2ecf20Sopenharmony_ci			fi_args->max_id = device->devid;
33668c2ecf20Sopenharmony_ci	}
33678c2ecf20Sopenharmony_ci	rcu_read_unlock();
33688c2ecf20Sopenharmony_ci
33698c2ecf20Sopenharmony_ci	memcpy(&fi_args->fsid, fs_devices->fsid, sizeof(fi_args->fsid));
33708c2ecf20Sopenharmony_ci	fi_args->nodesize = fs_info->nodesize;
33718c2ecf20Sopenharmony_ci	fi_args->sectorsize = fs_info->sectorsize;
33728c2ecf20Sopenharmony_ci	fi_args->clone_alignment = fs_info->sectorsize;
33738c2ecf20Sopenharmony_ci
33748c2ecf20Sopenharmony_ci	if (flags_in & BTRFS_FS_INFO_FLAG_CSUM_INFO) {
33758c2ecf20Sopenharmony_ci		fi_args->csum_type = btrfs_super_csum_type(fs_info->super_copy);
33768c2ecf20Sopenharmony_ci		fi_args->csum_size = btrfs_super_csum_size(fs_info->super_copy);
33778c2ecf20Sopenharmony_ci		fi_args->flags |= BTRFS_FS_INFO_FLAG_CSUM_INFO;
33788c2ecf20Sopenharmony_ci	}
33798c2ecf20Sopenharmony_ci
33808c2ecf20Sopenharmony_ci	if (flags_in & BTRFS_FS_INFO_FLAG_GENERATION) {
33818c2ecf20Sopenharmony_ci		fi_args->generation = fs_info->generation;
33828c2ecf20Sopenharmony_ci		fi_args->flags |= BTRFS_FS_INFO_FLAG_GENERATION;
33838c2ecf20Sopenharmony_ci	}
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_ci	if (flags_in & BTRFS_FS_INFO_FLAG_METADATA_UUID) {
33868c2ecf20Sopenharmony_ci		memcpy(&fi_args->metadata_uuid, fs_devices->metadata_uuid,
33878c2ecf20Sopenharmony_ci		       sizeof(fi_args->metadata_uuid));
33888c2ecf20Sopenharmony_ci		fi_args->flags |= BTRFS_FS_INFO_FLAG_METADATA_UUID;
33898c2ecf20Sopenharmony_ci	}
33908c2ecf20Sopenharmony_ci
33918c2ecf20Sopenharmony_ci	if (copy_to_user(arg, fi_args, sizeof(*fi_args)))
33928c2ecf20Sopenharmony_ci		ret = -EFAULT;
33938c2ecf20Sopenharmony_ci
33948c2ecf20Sopenharmony_ci	kfree(fi_args);
33958c2ecf20Sopenharmony_ci	return ret;
33968c2ecf20Sopenharmony_ci}
33978c2ecf20Sopenharmony_ci
33988c2ecf20Sopenharmony_cistatic long btrfs_ioctl_dev_info(struct btrfs_fs_info *fs_info,
33998c2ecf20Sopenharmony_ci				 void __user *arg)
34008c2ecf20Sopenharmony_ci{
34018c2ecf20Sopenharmony_ci	struct btrfs_ioctl_dev_info_args *di_args;
34028c2ecf20Sopenharmony_ci	struct btrfs_device *dev;
34038c2ecf20Sopenharmony_ci	int ret = 0;
34048c2ecf20Sopenharmony_ci	char *s_uuid = NULL;
34058c2ecf20Sopenharmony_ci
34068c2ecf20Sopenharmony_ci	di_args = memdup_user(arg, sizeof(*di_args));
34078c2ecf20Sopenharmony_ci	if (IS_ERR(di_args))
34088c2ecf20Sopenharmony_ci		return PTR_ERR(di_args);
34098c2ecf20Sopenharmony_ci
34108c2ecf20Sopenharmony_ci	if (!btrfs_is_empty_uuid(di_args->uuid))
34118c2ecf20Sopenharmony_ci		s_uuid = di_args->uuid;
34128c2ecf20Sopenharmony_ci
34138c2ecf20Sopenharmony_ci	rcu_read_lock();
34148c2ecf20Sopenharmony_ci	dev = btrfs_find_device(fs_info->fs_devices, di_args->devid, s_uuid,
34158c2ecf20Sopenharmony_ci				NULL, true);
34168c2ecf20Sopenharmony_ci
34178c2ecf20Sopenharmony_ci	if (!dev) {
34188c2ecf20Sopenharmony_ci		ret = -ENODEV;
34198c2ecf20Sopenharmony_ci		goto out;
34208c2ecf20Sopenharmony_ci	}
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_ci	di_args->devid = dev->devid;
34238c2ecf20Sopenharmony_ci	di_args->bytes_used = btrfs_device_get_bytes_used(dev);
34248c2ecf20Sopenharmony_ci	di_args->total_bytes = btrfs_device_get_total_bytes(dev);
34258c2ecf20Sopenharmony_ci	memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
34268c2ecf20Sopenharmony_ci	if (dev->name)
34278c2ecf20Sopenharmony_ci		strscpy(di_args->path, rcu_str_deref(dev->name), sizeof(di_args->path));
34288c2ecf20Sopenharmony_ci	else
34298c2ecf20Sopenharmony_ci		di_args->path[0] = '\0';
34308c2ecf20Sopenharmony_ci
34318c2ecf20Sopenharmony_ciout:
34328c2ecf20Sopenharmony_ci	rcu_read_unlock();
34338c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(arg, di_args, sizeof(*di_args)))
34348c2ecf20Sopenharmony_ci		ret = -EFAULT;
34358c2ecf20Sopenharmony_ci
34368c2ecf20Sopenharmony_ci	kfree(di_args);
34378c2ecf20Sopenharmony_ci	return ret;
34388c2ecf20Sopenharmony_ci}
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_cistatic long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
34418c2ecf20Sopenharmony_ci{
34428c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
34438c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
34448c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
34458c2ecf20Sopenharmony_ci	struct btrfs_root *new_root;
34468c2ecf20Sopenharmony_ci	struct btrfs_dir_item *di;
34478c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
34488c2ecf20Sopenharmony_ci	struct btrfs_path *path = NULL;
34498c2ecf20Sopenharmony_ci	struct btrfs_disk_key disk_key;
34508c2ecf20Sopenharmony_ci	u64 objectid = 0;
34518c2ecf20Sopenharmony_ci	u64 dir_id;
34528c2ecf20Sopenharmony_ci	int ret;
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
34558c2ecf20Sopenharmony_ci		return -EPERM;
34568c2ecf20Sopenharmony_ci
34578c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
34588c2ecf20Sopenharmony_ci	if (ret)
34598c2ecf20Sopenharmony_ci		return ret;
34608c2ecf20Sopenharmony_ci
34618c2ecf20Sopenharmony_ci	if (copy_from_user(&objectid, argp, sizeof(objectid))) {
34628c2ecf20Sopenharmony_ci		ret = -EFAULT;
34638c2ecf20Sopenharmony_ci		goto out;
34648c2ecf20Sopenharmony_ci	}
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_ci	if (!objectid)
34678c2ecf20Sopenharmony_ci		objectid = BTRFS_FS_TREE_OBJECTID;
34688c2ecf20Sopenharmony_ci
34698c2ecf20Sopenharmony_ci	new_root = btrfs_get_fs_root(fs_info, objectid, true);
34708c2ecf20Sopenharmony_ci	if (IS_ERR(new_root)) {
34718c2ecf20Sopenharmony_ci		ret = PTR_ERR(new_root);
34728c2ecf20Sopenharmony_ci		goto out;
34738c2ecf20Sopenharmony_ci	}
34748c2ecf20Sopenharmony_ci	if (!is_fstree(new_root->root_key.objectid)) {
34758c2ecf20Sopenharmony_ci		ret = -ENOENT;
34768c2ecf20Sopenharmony_ci		goto out_free;
34778c2ecf20Sopenharmony_ci	}
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
34808c2ecf20Sopenharmony_ci	if (!path) {
34818c2ecf20Sopenharmony_ci		ret = -ENOMEM;
34828c2ecf20Sopenharmony_ci		goto out_free;
34838c2ecf20Sopenharmony_ci	}
34848c2ecf20Sopenharmony_ci	path->leave_spinning = 1;
34858c2ecf20Sopenharmony_ci
34868c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 1);
34878c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
34888c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
34898c2ecf20Sopenharmony_ci		goto out_free;
34908c2ecf20Sopenharmony_ci	}
34918c2ecf20Sopenharmony_ci
34928c2ecf20Sopenharmony_ci	dir_id = btrfs_super_root_dir(fs_info->super_copy);
34938c2ecf20Sopenharmony_ci	di = btrfs_lookup_dir_item(trans, fs_info->tree_root, path,
34948c2ecf20Sopenharmony_ci				   dir_id, "default", 7, 1);
34958c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(di)) {
34968c2ecf20Sopenharmony_ci		btrfs_release_path(path);
34978c2ecf20Sopenharmony_ci		btrfs_end_transaction(trans);
34988c2ecf20Sopenharmony_ci		btrfs_err(fs_info,
34998c2ecf20Sopenharmony_ci			  "Umm, you don't have the default diritem, this isn't going to work");
35008c2ecf20Sopenharmony_ci		ret = -ENOENT;
35018c2ecf20Sopenharmony_ci		goto out_free;
35028c2ecf20Sopenharmony_ci	}
35038c2ecf20Sopenharmony_ci
35048c2ecf20Sopenharmony_ci	btrfs_cpu_key_to_disk(&disk_key, &new_root->root_key);
35058c2ecf20Sopenharmony_ci	btrfs_set_dir_item_key(path->nodes[0], di, &disk_key);
35068c2ecf20Sopenharmony_ci	btrfs_mark_buffer_dirty(path->nodes[0]);
35078c2ecf20Sopenharmony_ci	btrfs_release_path(path);
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_ci	btrfs_set_fs_incompat(fs_info, DEFAULT_SUBVOL);
35108c2ecf20Sopenharmony_ci	btrfs_end_transaction(trans);
35118c2ecf20Sopenharmony_ciout_free:
35128c2ecf20Sopenharmony_ci	btrfs_put_root(new_root);
35138c2ecf20Sopenharmony_ci	btrfs_free_path(path);
35148c2ecf20Sopenharmony_ciout:
35158c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
35168c2ecf20Sopenharmony_ci	return ret;
35178c2ecf20Sopenharmony_ci}
35188c2ecf20Sopenharmony_ci
35198c2ecf20Sopenharmony_cistatic void get_block_group_info(struct list_head *groups_list,
35208c2ecf20Sopenharmony_ci				 struct btrfs_ioctl_space_info *space)
35218c2ecf20Sopenharmony_ci{
35228c2ecf20Sopenharmony_ci	struct btrfs_block_group *block_group;
35238c2ecf20Sopenharmony_ci
35248c2ecf20Sopenharmony_ci	space->total_bytes = 0;
35258c2ecf20Sopenharmony_ci	space->used_bytes = 0;
35268c2ecf20Sopenharmony_ci	space->flags = 0;
35278c2ecf20Sopenharmony_ci	list_for_each_entry(block_group, groups_list, list) {
35288c2ecf20Sopenharmony_ci		space->flags = block_group->flags;
35298c2ecf20Sopenharmony_ci		space->total_bytes += block_group->length;
35308c2ecf20Sopenharmony_ci		space->used_bytes += block_group->used;
35318c2ecf20Sopenharmony_ci	}
35328c2ecf20Sopenharmony_ci}
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_cistatic long btrfs_ioctl_space_info(struct btrfs_fs_info *fs_info,
35358c2ecf20Sopenharmony_ci				   void __user *arg)
35368c2ecf20Sopenharmony_ci{
35378c2ecf20Sopenharmony_ci	struct btrfs_ioctl_space_args space_args = { 0 };
35388c2ecf20Sopenharmony_ci	struct btrfs_ioctl_space_info space;
35398c2ecf20Sopenharmony_ci	struct btrfs_ioctl_space_info *dest;
35408c2ecf20Sopenharmony_ci	struct btrfs_ioctl_space_info *dest_orig;
35418c2ecf20Sopenharmony_ci	struct btrfs_ioctl_space_info __user *user_dest;
35428c2ecf20Sopenharmony_ci	struct btrfs_space_info *info;
35438c2ecf20Sopenharmony_ci	static const u64 types[] = {
35448c2ecf20Sopenharmony_ci		BTRFS_BLOCK_GROUP_DATA,
35458c2ecf20Sopenharmony_ci		BTRFS_BLOCK_GROUP_SYSTEM,
35468c2ecf20Sopenharmony_ci		BTRFS_BLOCK_GROUP_METADATA,
35478c2ecf20Sopenharmony_ci		BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA
35488c2ecf20Sopenharmony_ci	};
35498c2ecf20Sopenharmony_ci	int num_types = 4;
35508c2ecf20Sopenharmony_ci	int alloc_size;
35518c2ecf20Sopenharmony_ci	int ret = 0;
35528c2ecf20Sopenharmony_ci	u64 slot_count = 0;
35538c2ecf20Sopenharmony_ci	int i, c;
35548c2ecf20Sopenharmony_ci
35558c2ecf20Sopenharmony_ci	if (copy_from_user(&space_args,
35568c2ecf20Sopenharmony_ci			   (struct btrfs_ioctl_space_args __user *)arg,
35578c2ecf20Sopenharmony_ci			   sizeof(space_args)))
35588c2ecf20Sopenharmony_ci		return -EFAULT;
35598c2ecf20Sopenharmony_ci
35608c2ecf20Sopenharmony_ci	for (i = 0; i < num_types; i++) {
35618c2ecf20Sopenharmony_ci		struct btrfs_space_info *tmp;
35628c2ecf20Sopenharmony_ci
35638c2ecf20Sopenharmony_ci		info = NULL;
35648c2ecf20Sopenharmony_ci		list_for_each_entry(tmp, &fs_info->space_info, list) {
35658c2ecf20Sopenharmony_ci			if (tmp->flags == types[i]) {
35668c2ecf20Sopenharmony_ci				info = tmp;
35678c2ecf20Sopenharmony_ci				break;
35688c2ecf20Sopenharmony_ci			}
35698c2ecf20Sopenharmony_ci		}
35708c2ecf20Sopenharmony_ci
35718c2ecf20Sopenharmony_ci		if (!info)
35728c2ecf20Sopenharmony_ci			continue;
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ci		down_read(&info->groups_sem);
35758c2ecf20Sopenharmony_ci		for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) {
35768c2ecf20Sopenharmony_ci			if (!list_empty(&info->block_groups[c]))
35778c2ecf20Sopenharmony_ci				slot_count++;
35788c2ecf20Sopenharmony_ci		}
35798c2ecf20Sopenharmony_ci		up_read(&info->groups_sem);
35808c2ecf20Sopenharmony_ci	}
35818c2ecf20Sopenharmony_ci
35828c2ecf20Sopenharmony_ci	/*
35838c2ecf20Sopenharmony_ci	 * Global block reserve, exported as a space_info
35848c2ecf20Sopenharmony_ci	 */
35858c2ecf20Sopenharmony_ci	slot_count++;
35868c2ecf20Sopenharmony_ci
35878c2ecf20Sopenharmony_ci	/* space_slots == 0 means they are asking for a count */
35888c2ecf20Sopenharmony_ci	if (space_args.space_slots == 0) {
35898c2ecf20Sopenharmony_ci		space_args.total_spaces = slot_count;
35908c2ecf20Sopenharmony_ci		goto out;
35918c2ecf20Sopenharmony_ci	}
35928c2ecf20Sopenharmony_ci
35938c2ecf20Sopenharmony_ci	slot_count = min_t(u64, space_args.space_slots, slot_count);
35948c2ecf20Sopenharmony_ci
35958c2ecf20Sopenharmony_ci	alloc_size = sizeof(*dest) * slot_count;
35968c2ecf20Sopenharmony_ci
35978c2ecf20Sopenharmony_ci	/* we generally have at most 6 or so space infos, one for each raid
35988c2ecf20Sopenharmony_ci	 * level.  So, a whole page should be more than enough for everyone
35998c2ecf20Sopenharmony_ci	 */
36008c2ecf20Sopenharmony_ci	if (alloc_size > PAGE_SIZE)
36018c2ecf20Sopenharmony_ci		return -ENOMEM;
36028c2ecf20Sopenharmony_ci
36038c2ecf20Sopenharmony_ci	space_args.total_spaces = 0;
36048c2ecf20Sopenharmony_ci	dest = kmalloc(alloc_size, GFP_KERNEL);
36058c2ecf20Sopenharmony_ci	if (!dest)
36068c2ecf20Sopenharmony_ci		return -ENOMEM;
36078c2ecf20Sopenharmony_ci	dest_orig = dest;
36088c2ecf20Sopenharmony_ci
36098c2ecf20Sopenharmony_ci	/* now we have a buffer to copy into */
36108c2ecf20Sopenharmony_ci	for (i = 0; i < num_types; i++) {
36118c2ecf20Sopenharmony_ci		struct btrfs_space_info *tmp;
36128c2ecf20Sopenharmony_ci
36138c2ecf20Sopenharmony_ci		if (!slot_count)
36148c2ecf20Sopenharmony_ci			break;
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci		info = NULL;
36178c2ecf20Sopenharmony_ci		list_for_each_entry(tmp, &fs_info->space_info, list) {
36188c2ecf20Sopenharmony_ci			if (tmp->flags == types[i]) {
36198c2ecf20Sopenharmony_ci				info = tmp;
36208c2ecf20Sopenharmony_ci				break;
36218c2ecf20Sopenharmony_ci			}
36228c2ecf20Sopenharmony_ci		}
36238c2ecf20Sopenharmony_ci
36248c2ecf20Sopenharmony_ci		if (!info)
36258c2ecf20Sopenharmony_ci			continue;
36268c2ecf20Sopenharmony_ci		down_read(&info->groups_sem);
36278c2ecf20Sopenharmony_ci		for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) {
36288c2ecf20Sopenharmony_ci			if (!list_empty(&info->block_groups[c])) {
36298c2ecf20Sopenharmony_ci				get_block_group_info(&info->block_groups[c],
36308c2ecf20Sopenharmony_ci						     &space);
36318c2ecf20Sopenharmony_ci				memcpy(dest, &space, sizeof(space));
36328c2ecf20Sopenharmony_ci				dest++;
36338c2ecf20Sopenharmony_ci				space_args.total_spaces++;
36348c2ecf20Sopenharmony_ci				slot_count--;
36358c2ecf20Sopenharmony_ci			}
36368c2ecf20Sopenharmony_ci			if (!slot_count)
36378c2ecf20Sopenharmony_ci				break;
36388c2ecf20Sopenharmony_ci		}
36398c2ecf20Sopenharmony_ci		up_read(&info->groups_sem);
36408c2ecf20Sopenharmony_ci	}
36418c2ecf20Sopenharmony_ci
36428c2ecf20Sopenharmony_ci	/*
36438c2ecf20Sopenharmony_ci	 * Add global block reserve
36448c2ecf20Sopenharmony_ci	 */
36458c2ecf20Sopenharmony_ci	if (slot_count) {
36468c2ecf20Sopenharmony_ci		struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
36478c2ecf20Sopenharmony_ci
36488c2ecf20Sopenharmony_ci		spin_lock(&block_rsv->lock);
36498c2ecf20Sopenharmony_ci		space.total_bytes = block_rsv->size;
36508c2ecf20Sopenharmony_ci		space.used_bytes = block_rsv->size - block_rsv->reserved;
36518c2ecf20Sopenharmony_ci		spin_unlock(&block_rsv->lock);
36528c2ecf20Sopenharmony_ci		space.flags = BTRFS_SPACE_INFO_GLOBAL_RSV;
36538c2ecf20Sopenharmony_ci		memcpy(dest, &space, sizeof(space));
36548c2ecf20Sopenharmony_ci		space_args.total_spaces++;
36558c2ecf20Sopenharmony_ci	}
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_ci	user_dest = (struct btrfs_ioctl_space_info __user *)
36588c2ecf20Sopenharmony_ci		(arg + sizeof(struct btrfs_ioctl_space_args));
36598c2ecf20Sopenharmony_ci
36608c2ecf20Sopenharmony_ci	if (copy_to_user(user_dest, dest_orig, alloc_size))
36618c2ecf20Sopenharmony_ci		ret = -EFAULT;
36628c2ecf20Sopenharmony_ci
36638c2ecf20Sopenharmony_ci	kfree(dest_orig);
36648c2ecf20Sopenharmony_ciout:
36658c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(arg, &space_args, sizeof(space_args)))
36668c2ecf20Sopenharmony_ci		ret = -EFAULT;
36678c2ecf20Sopenharmony_ci
36688c2ecf20Sopenharmony_ci	return ret;
36698c2ecf20Sopenharmony_ci}
36708c2ecf20Sopenharmony_ci
36718c2ecf20Sopenharmony_cistatic noinline long btrfs_ioctl_start_sync(struct btrfs_root *root,
36728c2ecf20Sopenharmony_ci					    void __user *argp)
36738c2ecf20Sopenharmony_ci{
36748c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
36758c2ecf20Sopenharmony_ci	u64 transid;
36768c2ecf20Sopenharmony_ci	int ret;
36778c2ecf20Sopenharmony_ci
36788c2ecf20Sopenharmony_ci	trans = btrfs_attach_transaction_barrier(root);
36798c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
36808c2ecf20Sopenharmony_ci		if (PTR_ERR(trans) != -ENOENT)
36818c2ecf20Sopenharmony_ci			return PTR_ERR(trans);
36828c2ecf20Sopenharmony_ci
36838c2ecf20Sopenharmony_ci		/* No running transaction, don't bother */
36848c2ecf20Sopenharmony_ci		transid = root->fs_info->last_trans_committed;
36858c2ecf20Sopenharmony_ci		goto out;
36868c2ecf20Sopenharmony_ci	}
36878c2ecf20Sopenharmony_ci	transid = trans->transid;
36888c2ecf20Sopenharmony_ci	ret = btrfs_commit_transaction_async(trans, 0);
36898c2ecf20Sopenharmony_ci	if (ret) {
36908c2ecf20Sopenharmony_ci		btrfs_end_transaction(trans);
36918c2ecf20Sopenharmony_ci		return ret;
36928c2ecf20Sopenharmony_ci	}
36938c2ecf20Sopenharmony_ciout:
36948c2ecf20Sopenharmony_ci	if (argp)
36958c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &transid, sizeof(transid)))
36968c2ecf20Sopenharmony_ci			return -EFAULT;
36978c2ecf20Sopenharmony_ci	return 0;
36988c2ecf20Sopenharmony_ci}
36998c2ecf20Sopenharmony_ci
37008c2ecf20Sopenharmony_cistatic noinline long btrfs_ioctl_wait_sync(struct btrfs_fs_info *fs_info,
37018c2ecf20Sopenharmony_ci					   void __user *argp)
37028c2ecf20Sopenharmony_ci{
37038c2ecf20Sopenharmony_ci	u64 transid;
37048c2ecf20Sopenharmony_ci
37058c2ecf20Sopenharmony_ci	if (argp) {
37068c2ecf20Sopenharmony_ci		if (copy_from_user(&transid, argp, sizeof(transid)))
37078c2ecf20Sopenharmony_ci			return -EFAULT;
37088c2ecf20Sopenharmony_ci	} else {
37098c2ecf20Sopenharmony_ci		transid = 0;  /* current trans */
37108c2ecf20Sopenharmony_ci	}
37118c2ecf20Sopenharmony_ci	return btrfs_wait_for_commit(fs_info, transid);
37128c2ecf20Sopenharmony_ci}
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_cistatic long btrfs_ioctl_scrub(struct file *file, void __user *arg)
37158c2ecf20Sopenharmony_ci{
37168c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(file_inode(file)->i_sb);
37178c2ecf20Sopenharmony_ci	struct btrfs_ioctl_scrub_args *sa;
37188c2ecf20Sopenharmony_ci	int ret;
37198c2ecf20Sopenharmony_ci
37208c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
37218c2ecf20Sopenharmony_ci		return -EPERM;
37228c2ecf20Sopenharmony_ci
37238c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
37248c2ecf20Sopenharmony_ci	if (IS_ERR(sa))
37258c2ecf20Sopenharmony_ci		return PTR_ERR(sa);
37268c2ecf20Sopenharmony_ci
37278c2ecf20Sopenharmony_ci	if (sa->flags & ~BTRFS_SCRUB_SUPPORTED_FLAGS) {
37288c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
37298c2ecf20Sopenharmony_ci		goto out;
37308c2ecf20Sopenharmony_ci	}
37318c2ecf20Sopenharmony_ci
37328c2ecf20Sopenharmony_ci	if (!(sa->flags & BTRFS_SCRUB_READONLY)) {
37338c2ecf20Sopenharmony_ci		ret = mnt_want_write_file(file);
37348c2ecf20Sopenharmony_ci		if (ret)
37358c2ecf20Sopenharmony_ci			goto out;
37368c2ecf20Sopenharmony_ci	}
37378c2ecf20Sopenharmony_ci
37388c2ecf20Sopenharmony_ci	ret = btrfs_scrub_dev(fs_info, sa->devid, sa->start, sa->end,
37398c2ecf20Sopenharmony_ci			      &sa->progress, sa->flags & BTRFS_SCRUB_READONLY,
37408c2ecf20Sopenharmony_ci			      0);
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_ci	/*
37438c2ecf20Sopenharmony_ci	 * Copy scrub args to user space even if btrfs_scrub_dev() returned an
37448c2ecf20Sopenharmony_ci	 * error. This is important as it allows user space to know how much
37458c2ecf20Sopenharmony_ci	 * progress scrub has done. For example, if scrub is canceled we get
37468c2ecf20Sopenharmony_ci	 * -ECANCELED from btrfs_scrub_dev() and return that error back to user
37478c2ecf20Sopenharmony_ci	 * space. Later user space can inspect the progress from the structure
37488c2ecf20Sopenharmony_ci	 * btrfs_ioctl_scrub_args and resume scrub from where it left off
37498c2ecf20Sopenharmony_ci	 * previously (btrfs-progs does this).
37508c2ecf20Sopenharmony_ci	 * If we fail to copy the btrfs_ioctl_scrub_args structure to user space
37518c2ecf20Sopenharmony_ci	 * then return -EFAULT to signal the structure was not copied or it may
37528c2ecf20Sopenharmony_ci	 * be corrupt and unreliable due to a partial copy.
37538c2ecf20Sopenharmony_ci	 */
37548c2ecf20Sopenharmony_ci	if (copy_to_user(arg, sa, sizeof(*sa)))
37558c2ecf20Sopenharmony_ci		ret = -EFAULT;
37568c2ecf20Sopenharmony_ci
37578c2ecf20Sopenharmony_ci	if (!(sa->flags & BTRFS_SCRUB_READONLY))
37588c2ecf20Sopenharmony_ci		mnt_drop_write_file(file);
37598c2ecf20Sopenharmony_ciout:
37608c2ecf20Sopenharmony_ci	kfree(sa);
37618c2ecf20Sopenharmony_ci	return ret;
37628c2ecf20Sopenharmony_ci}
37638c2ecf20Sopenharmony_ci
37648c2ecf20Sopenharmony_cistatic long btrfs_ioctl_scrub_cancel(struct btrfs_fs_info *fs_info)
37658c2ecf20Sopenharmony_ci{
37668c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
37678c2ecf20Sopenharmony_ci		return -EPERM;
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_ci	return btrfs_scrub_cancel(fs_info);
37708c2ecf20Sopenharmony_ci}
37718c2ecf20Sopenharmony_ci
37728c2ecf20Sopenharmony_cistatic long btrfs_ioctl_scrub_progress(struct btrfs_fs_info *fs_info,
37738c2ecf20Sopenharmony_ci				       void __user *arg)
37748c2ecf20Sopenharmony_ci{
37758c2ecf20Sopenharmony_ci	struct btrfs_ioctl_scrub_args *sa;
37768c2ecf20Sopenharmony_ci	int ret;
37778c2ecf20Sopenharmony_ci
37788c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
37798c2ecf20Sopenharmony_ci		return -EPERM;
37808c2ecf20Sopenharmony_ci
37818c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
37828c2ecf20Sopenharmony_ci	if (IS_ERR(sa))
37838c2ecf20Sopenharmony_ci		return PTR_ERR(sa);
37848c2ecf20Sopenharmony_ci
37858c2ecf20Sopenharmony_ci	ret = btrfs_scrub_progress(fs_info, sa->devid, &sa->progress);
37868c2ecf20Sopenharmony_ci
37878c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(arg, sa, sizeof(*sa)))
37888c2ecf20Sopenharmony_ci		ret = -EFAULT;
37898c2ecf20Sopenharmony_ci
37908c2ecf20Sopenharmony_ci	kfree(sa);
37918c2ecf20Sopenharmony_ci	return ret;
37928c2ecf20Sopenharmony_ci}
37938c2ecf20Sopenharmony_ci
37948c2ecf20Sopenharmony_cistatic long btrfs_ioctl_get_dev_stats(struct btrfs_fs_info *fs_info,
37958c2ecf20Sopenharmony_ci				      void __user *arg)
37968c2ecf20Sopenharmony_ci{
37978c2ecf20Sopenharmony_ci	struct btrfs_ioctl_get_dev_stats *sa;
37988c2ecf20Sopenharmony_ci	int ret;
37998c2ecf20Sopenharmony_ci
38008c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
38018c2ecf20Sopenharmony_ci	if (IS_ERR(sa))
38028c2ecf20Sopenharmony_ci		return PTR_ERR(sa);
38038c2ecf20Sopenharmony_ci
38048c2ecf20Sopenharmony_ci	if ((sa->flags & BTRFS_DEV_STATS_RESET) && !capable(CAP_SYS_ADMIN)) {
38058c2ecf20Sopenharmony_ci		kfree(sa);
38068c2ecf20Sopenharmony_ci		return -EPERM;
38078c2ecf20Sopenharmony_ci	}
38088c2ecf20Sopenharmony_ci
38098c2ecf20Sopenharmony_ci	ret = btrfs_get_dev_stats(fs_info, sa);
38108c2ecf20Sopenharmony_ci
38118c2ecf20Sopenharmony_ci	if (ret == 0 && copy_to_user(arg, sa, sizeof(*sa)))
38128c2ecf20Sopenharmony_ci		ret = -EFAULT;
38138c2ecf20Sopenharmony_ci
38148c2ecf20Sopenharmony_ci	kfree(sa);
38158c2ecf20Sopenharmony_ci	return ret;
38168c2ecf20Sopenharmony_ci}
38178c2ecf20Sopenharmony_ci
38188c2ecf20Sopenharmony_cistatic long btrfs_ioctl_dev_replace(struct btrfs_fs_info *fs_info,
38198c2ecf20Sopenharmony_ci				    void __user *arg)
38208c2ecf20Sopenharmony_ci{
38218c2ecf20Sopenharmony_ci	struct btrfs_ioctl_dev_replace_args *p;
38228c2ecf20Sopenharmony_ci	int ret;
38238c2ecf20Sopenharmony_ci
38248c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
38258c2ecf20Sopenharmony_ci		return -EPERM;
38268c2ecf20Sopenharmony_ci
38278c2ecf20Sopenharmony_ci	p = memdup_user(arg, sizeof(*p));
38288c2ecf20Sopenharmony_ci	if (IS_ERR(p))
38298c2ecf20Sopenharmony_ci		return PTR_ERR(p);
38308c2ecf20Sopenharmony_ci
38318c2ecf20Sopenharmony_ci	switch (p->cmd) {
38328c2ecf20Sopenharmony_ci	case BTRFS_IOCTL_DEV_REPLACE_CMD_START:
38338c2ecf20Sopenharmony_ci		if (sb_rdonly(fs_info->sb)) {
38348c2ecf20Sopenharmony_ci			ret = -EROFS;
38358c2ecf20Sopenharmony_ci			goto out;
38368c2ecf20Sopenharmony_ci		}
38378c2ecf20Sopenharmony_ci		if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_REPLACE)) {
38388c2ecf20Sopenharmony_ci			ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
38398c2ecf20Sopenharmony_ci		} else {
38408c2ecf20Sopenharmony_ci			ret = btrfs_dev_replace_by_ioctl(fs_info, p);
38418c2ecf20Sopenharmony_ci			btrfs_exclop_finish(fs_info);
38428c2ecf20Sopenharmony_ci		}
38438c2ecf20Sopenharmony_ci		break;
38448c2ecf20Sopenharmony_ci	case BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS:
38458c2ecf20Sopenharmony_ci		btrfs_dev_replace_status(fs_info, p);
38468c2ecf20Sopenharmony_ci		ret = 0;
38478c2ecf20Sopenharmony_ci		break;
38488c2ecf20Sopenharmony_ci	case BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL:
38498c2ecf20Sopenharmony_ci		p->result = btrfs_dev_replace_cancel(fs_info);
38508c2ecf20Sopenharmony_ci		ret = 0;
38518c2ecf20Sopenharmony_ci		break;
38528c2ecf20Sopenharmony_ci	default:
38538c2ecf20Sopenharmony_ci		ret = -EINVAL;
38548c2ecf20Sopenharmony_ci		break;
38558c2ecf20Sopenharmony_ci	}
38568c2ecf20Sopenharmony_ci
38578c2ecf20Sopenharmony_ci	if ((ret == 0 || ret == -ECANCELED) && copy_to_user(arg, p, sizeof(*p)))
38588c2ecf20Sopenharmony_ci		ret = -EFAULT;
38598c2ecf20Sopenharmony_ciout:
38608c2ecf20Sopenharmony_ci	kfree(p);
38618c2ecf20Sopenharmony_ci	return ret;
38628c2ecf20Sopenharmony_ci}
38638c2ecf20Sopenharmony_ci
38648c2ecf20Sopenharmony_cistatic long btrfs_ioctl_ino_to_path(struct btrfs_root *root, void __user *arg)
38658c2ecf20Sopenharmony_ci{
38668c2ecf20Sopenharmony_ci	int ret = 0;
38678c2ecf20Sopenharmony_ci	int i;
38688c2ecf20Sopenharmony_ci	u64 rel_ptr;
38698c2ecf20Sopenharmony_ci	int size;
38708c2ecf20Sopenharmony_ci	struct btrfs_ioctl_ino_path_args *ipa = NULL;
38718c2ecf20Sopenharmony_ci	struct inode_fs_paths *ipath = NULL;
38728c2ecf20Sopenharmony_ci	struct btrfs_path *path;
38738c2ecf20Sopenharmony_ci
38748c2ecf20Sopenharmony_ci	if (!capable(CAP_DAC_READ_SEARCH))
38758c2ecf20Sopenharmony_ci		return -EPERM;
38768c2ecf20Sopenharmony_ci
38778c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
38788c2ecf20Sopenharmony_ci	if (!path) {
38798c2ecf20Sopenharmony_ci		ret = -ENOMEM;
38808c2ecf20Sopenharmony_ci		goto out;
38818c2ecf20Sopenharmony_ci	}
38828c2ecf20Sopenharmony_ci
38838c2ecf20Sopenharmony_ci	ipa = memdup_user(arg, sizeof(*ipa));
38848c2ecf20Sopenharmony_ci	if (IS_ERR(ipa)) {
38858c2ecf20Sopenharmony_ci		ret = PTR_ERR(ipa);
38868c2ecf20Sopenharmony_ci		ipa = NULL;
38878c2ecf20Sopenharmony_ci		goto out;
38888c2ecf20Sopenharmony_ci	}
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_ci	size = min_t(u32, ipa->size, 4096);
38918c2ecf20Sopenharmony_ci	ipath = init_ipath(size, root, path);
38928c2ecf20Sopenharmony_ci	if (IS_ERR(ipath)) {
38938c2ecf20Sopenharmony_ci		ret = PTR_ERR(ipath);
38948c2ecf20Sopenharmony_ci		ipath = NULL;
38958c2ecf20Sopenharmony_ci		goto out;
38968c2ecf20Sopenharmony_ci	}
38978c2ecf20Sopenharmony_ci
38988c2ecf20Sopenharmony_ci	ret = paths_from_inode(ipa->inum, ipath);
38998c2ecf20Sopenharmony_ci	if (ret < 0)
39008c2ecf20Sopenharmony_ci		goto out;
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_ci	for (i = 0; i < ipath->fspath->elem_cnt; ++i) {
39038c2ecf20Sopenharmony_ci		rel_ptr = ipath->fspath->val[i] -
39048c2ecf20Sopenharmony_ci			  (u64)(unsigned long)ipath->fspath->val;
39058c2ecf20Sopenharmony_ci		ipath->fspath->val[i] = rel_ptr;
39068c2ecf20Sopenharmony_ci	}
39078c2ecf20Sopenharmony_ci
39088c2ecf20Sopenharmony_ci	btrfs_free_path(path);
39098c2ecf20Sopenharmony_ci	path = NULL;
39108c2ecf20Sopenharmony_ci	ret = copy_to_user((void __user *)(unsigned long)ipa->fspath,
39118c2ecf20Sopenharmony_ci			   ipath->fspath, size);
39128c2ecf20Sopenharmony_ci	if (ret) {
39138c2ecf20Sopenharmony_ci		ret = -EFAULT;
39148c2ecf20Sopenharmony_ci		goto out;
39158c2ecf20Sopenharmony_ci	}
39168c2ecf20Sopenharmony_ci
39178c2ecf20Sopenharmony_ciout:
39188c2ecf20Sopenharmony_ci	btrfs_free_path(path);
39198c2ecf20Sopenharmony_ci	free_ipath(ipath);
39208c2ecf20Sopenharmony_ci	kfree(ipa);
39218c2ecf20Sopenharmony_ci
39228c2ecf20Sopenharmony_ci	return ret;
39238c2ecf20Sopenharmony_ci}
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_cistatic long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info,
39268c2ecf20Sopenharmony_ci					void __user *arg, int version)
39278c2ecf20Sopenharmony_ci{
39288c2ecf20Sopenharmony_ci	int ret = 0;
39298c2ecf20Sopenharmony_ci	int size;
39308c2ecf20Sopenharmony_ci	struct btrfs_ioctl_logical_ino_args *loi;
39318c2ecf20Sopenharmony_ci	struct btrfs_data_container *inodes = NULL;
39328c2ecf20Sopenharmony_ci	struct btrfs_path *path = NULL;
39338c2ecf20Sopenharmony_ci	bool ignore_offset;
39348c2ecf20Sopenharmony_ci
39358c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
39368c2ecf20Sopenharmony_ci		return -EPERM;
39378c2ecf20Sopenharmony_ci
39388c2ecf20Sopenharmony_ci	loi = memdup_user(arg, sizeof(*loi));
39398c2ecf20Sopenharmony_ci	if (IS_ERR(loi))
39408c2ecf20Sopenharmony_ci		return PTR_ERR(loi);
39418c2ecf20Sopenharmony_ci
39428c2ecf20Sopenharmony_ci	if (version == 1) {
39438c2ecf20Sopenharmony_ci		ignore_offset = false;
39448c2ecf20Sopenharmony_ci		size = min_t(u32, loi->size, SZ_64K);
39458c2ecf20Sopenharmony_ci	} else {
39468c2ecf20Sopenharmony_ci		/* All reserved bits must be 0 for now */
39478c2ecf20Sopenharmony_ci		if (memchr_inv(loi->reserved, 0, sizeof(loi->reserved))) {
39488c2ecf20Sopenharmony_ci			ret = -EINVAL;
39498c2ecf20Sopenharmony_ci			goto out_loi;
39508c2ecf20Sopenharmony_ci		}
39518c2ecf20Sopenharmony_ci		/* Only accept flags we have defined so far */
39528c2ecf20Sopenharmony_ci		if (loi->flags & ~(BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET)) {
39538c2ecf20Sopenharmony_ci			ret = -EINVAL;
39548c2ecf20Sopenharmony_ci			goto out_loi;
39558c2ecf20Sopenharmony_ci		}
39568c2ecf20Sopenharmony_ci		ignore_offset = loi->flags & BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET;
39578c2ecf20Sopenharmony_ci		size = min_t(u32, loi->size, SZ_16M);
39588c2ecf20Sopenharmony_ci	}
39598c2ecf20Sopenharmony_ci
39608c2ecf20Sopenharmony_ci	inodes = init_data_container(size);
39618c2ecf20Sopenharmony_ci	if (IS_ERR(inodes)) {
39628c2ecf20Sopenharmony_ci		ret = PTR_ERR(inodes);
39638c2ecf20Sopenharmony_ci		goto out_loi;
39648c2ecf20Sopenharmony_ci	}
39658c2ecf20Sopenharmony_ci
39668c2ecf20Sopenharmony_ci	path = btrfs_alloc_path();
39678c2ecf20Sopenharmony_ci	if (!path) {
39688c2ecf20Sopenharmony_ci		ret = -ENOMEM;
39698c2ecf20Sopenharmony_ci		goto out;
39708c2ecf20Sopenharmony_ci	}
39718c2ecf20Sopenharmony_ci	ret = iterate_inodes_from_logical(loi->logical, fs_info, path,
39728c2ecf20Sopenharmony_ci					  inodes, ignore_offset);
39738c2ecf20Sopenharmony_ci	btrfs_free_path(path);
39748c2ecf20Sopenharmony_ci	if (ret == -EINVAL)
39758c2ecf20Sopenharmony_ci		ret = -ENOENT;
39768c2ecf20Sopenharmony_ci	if (ret < 0)
39778c2ecf20Sopenharmony_ci		goto out;
39788c2ecf20Sopenharmony_ci
39798c2ecf20Sopenharmony_ci	ret = copy_to_user((void __user *)(unsigned long)loi->inodes, inodes,
39808c2ecf20Sopenharmony_ci			   size);
39818c2ecf20Sopenharmony_ci	if (ret)
39828c2ecf20Sopenharmony_ci		ret = -EFAULT;
39838c2ecf20Sopenharmony_ci
39848c2ecf20Sopenharmony_ciout:
39858c2ecf20Sopenharmony_ci	kvfree(inodes);
39868c2ecf20Sopenharmony_ciout_loi:
39878c2ecf20Sopenharmony_ci	kfree(loi);
39888c2ecf20Sopenharmony_ci
39898c2ecf20Sopenharmony_ci	return ret;
39908c2ecf20Sopenharmony_ci}
39918c2ecf20Sopenharmony_ci
39928c2ecf20Sopenharmony_civoid btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
39938c2ecf20Sopenharmony_ci			       struct btrfs_ioctl_balance_args *bargs)
39948c2ecf20Sopenharmony_ci{
39958c2ecf20Sopenharmony_ci	struct btrfs_balance_control *bctl = fs_info->balance_ctl;
39968c2ecf20Sopenharmony_ci
39978c2ecf20Sopenharmony_ci	bargs->flags = bctl->flags;
39988c2ecf20Sopenharmony_ci
39998c2ecf20Sopenharmony_ci	if (test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags))
40008c2ecf20Sopenharmony_ci		bargs->state |= BTRFS_BALANCE_STATE_RUNNING;
40018c2ecf20Sopenharmony_ci	if (atomic_read(&fs_info->balance_pause_req))
40028c2ecf20Sopenharmony_ci		bargs->state |= BTRFS_BALANCE_STATE_PAUSE_REQ;
40038c2ecf20Sopenharmony_ci	if (atomic_read(&fs_info->balance_cancel_req))
40048c2ecf20Sopenharmony_ci		bargs->state |= BTRFS_BALANCE_STATE_CANCEL_REQ;
40058c2ecf20Sopenharmony_ci
40068c2ecf20Sopenharmony_ci	memcpy(&bargs->data, &bctl->data, sizeof(bargs->data));
40078c2ecf20Sopenharmony_ci	memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta));
40088c2ecf20Sopenharmony_ci	memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys));
40098c2ecf20Sopenharmony_ci
40108c2ecf20Sopenharmony_ci	spin_lock(&fs_info->balance_lock);
40118c2ecf20Sopenharmony_ci	memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat));
40128c2ecf20Sopenharmony_ci	spin_unlock(&fs_info->balance_lock);
40138c2ecf20Sopenharmony_ci}
40148c2ecf20Sopenharmony_ci
40158c2ecf20Sopenharmony_cistatic long btrfs_ioctl_balance(struct file *file, void __user *arg)
40168c2ecf20Sopenharmony_ci{
40178c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
40188c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = root->fs_info;
40198c2ecf20Sopenharmony_ci	struct btrfs_ioctl_balance_args *bargs;
40208c2ecf20Sopenharmony_ci	struct btrfs_balance_control *bctl;
40218c2ecf20Sopenharmony_ci	bool need_unlock; /* for mut. excl. ops lock */
40228c2ecf20Sopenharmony_ci	int ret;
40238c2ecf20Sopenharmony_ci
40248c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
40258c2ecf20Sopenharmony_ci		return -EPERM;
40268c2ecf20Sopenharmony_ci
40278c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
40288c2ecf20Sopenharmony_ci	if (ret)
40298c2ecf20Sopenharmony_ci		return ret;
40308c2ecf20Sopenharmony_ci
40318c2ecf20Sopenharmony_ciagain:
40328c2ecf20Sopenharmony_ci	if (btrfs_exclop_start(fs_info, BTRFS_EXCLOP_BALANCE)) {
40338c2ecf20Sopenharmony_ci		mutex_lock(&fs_info->balance_mutex);
40348c2ecf20Sopenharmony_ci		need_unlock = true;
40358c2ecf20Sopenharmony_ci		goto locked;
40368c2ecf20Sopenharmony_ci	}
40378c2ecf20Sopenharmony_ci
40388c2ecf20Sopenharmony_ci	/*
40398c2ecf20Sopenharmony_ci	 * mut. excl. ops lock is locked.  Three possibilities:
40408c2ecf20Sopenharmony_ci	 *   (1) some other op is running
40418c2ecf20Sopenharmony_ci	 *   (2) balance is running
40428c2ecf20Sopenharmony_ci	 *   (3) balance is paused -- special case (think resume)
40438c2ecf20Sopenharmony_ci	 */
40448c2ecf20Sopenharmony_ci	mutex_lock(&fs_info->balance_mutex);
40458c2ecf20Sopenharmony_ci	if (fs_info->balance_ctl) {
40468c2ecf20Sopenharmony_ci		/* this is either (2) or (3) */
40478c2ecf20Sopenharmony_ci		if (!test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) {
40488c2ecf20Sopenharmony_ci			mutex_unlock(&fs_info->balance_mutex);
40498c2ecf20Sopenharmony_ci			/*
40508c2ecf20Sopenharmony_ci			 * Lock released to allow other waiters to continue,
40518c2ecf20Sopenharmony_ci			 * we'll reexamine the status again.
40528c2ecf20Sopenharmony_ci			 */
40538c2ecf20Sopenharmony_ci			mutex_lock(&fs_info->balance_mutex);
40548c2ecf20Sopenharmony_ci
40558c2ecf20Sopenharmony_ci			if (fs_info->balance_ctl &&
40568c2ecf20Sopenharmony_ci			    !test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) {
40578c2ecf20Sopenharmony_ci				/* this is (3) */
40588c2ecf20Sopenharmony_ci				need_unlock = false;
40598c2ecf20Sopenharmony_ci				goto locked;
40608c2ecf20Sopenharmony_ci			}
40618c2ecf20Sopenharmony_ci
40628c2ecf20Sopenharmony_ci			mutex_unlock(&fs_info->balance_mutex);
40638c2ecf20Sopenharmony_ci			goto again;
40648c2ecf20Sopenharmony_ci		} else {
40658c2ecf20Sopenharmony_ci			/* this is (2) */
40668c2ecf20Sopenharmony_ci			mutex_unlock(&fs_info->balance_mutex);
40678c2ecf20Sopenharmony_ci			ret = -EINPROGRESS;
40688c2ecf20Sopenharmony_ci			goto out;
40698c2ecf20Sopenharmony_ci		}
40708c2ecf20Sopenharmony_ci	} else {
40718c2ecf20Sopenharmony_ci		/* this is (1) */
40728c2ecf20Sopenharmony_ci		mutex_unlock(&fs_info->balance_mutex);
40738c2ecf20Sopenharmony_ci		ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
40748c2ecf20Sopenharmony_ci		goto out;
40758c2ecf20Sopenharmony_ci	}
40768c2ecf20Sopenharmony_ci
40778c2ecf20Sopenharmony_cilocked:
40788c2ecf20Sopenharmony_ci
40798c2ecf20Sopenharmony_ci	if (arg) {
40808c2ecf20Sopenharmony_ci		bargs = memdup_user(arg, sizeof(*bargs));
40818c2ecf20Sopenharmony_ci		if (IS_ERR(bargs)) {
40828c2ecf20Sopenharmony_ci			ret = PTR_ERR(bargs);
40838c2ecf20Sopenharmony_ci			goto out_unlock;
40848c2ecf20Sopenharmony_ci		}
40858c2ecf20Sopenharmony_ci
40868c2ecf20Sopenharmony_ci		if (bargs->flags & BTRFS_BALANCE_RESUME) {
40878c2ecf20Sopenharmony_ci			if (!fs_info->balance_ctl) {
40888c2ecf20Sopenharmony_ci				ret = -ENOTCONN;
40898c2ecf20Sopenharmony_ci				goto out_bargs;
40908c2ecf20Sopenharmony_ci			}
40918c2ecf20Sopenharmony_ci
40928c2ecf20Sopenharmony_ci			bctl = fs_info->balance_ctl;
40938c2ecf20Sopenharmony_ci			spin_lock(&fs_info->balance_lock);
40948c2ecf20Sopenharmony_ci			bctl->flags |= BTRFS_BALANCE_RESUME;
40958c2ecf20Sopenharmony_ci			spin_unlock(&fs_info->balance_lock);
40968c2ecf20Sopenharmony_ci
40978c2ecf20Sopenharmony_ci			goto do_balance;
40988c2ecf20Sopenharmony_ci		}
40998c2ecf20Sopenharmony_ci	} else {
41008c2ecf20Sopenharmony_ci		bargs = NULL;
41018c2ecf20Sopenharmony_ci	}
41028c2ecf20Sopenharmony_ci
41038c2ecf20Sopenharmony_ci	if (fs_info->balance_ctl) {
41048c2ecf20Sopenharmony_ci		ret = -EINPROGRESS;
41058c2ecf20Sopenharmony_ci		goto out_bargs;
41068c2ecf20Sopenharmony_ci	}
41078c2ecf20Sopenharmony_ci
41088c2ecf20Sopenharmony_ci	bctl = kzalloc(sizeof(*bctl), GFP_KERNEL);
41098c2ecf20Sopenharmony_ci	if (!bctl) {
41108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
41118c2ecf20Sopenharmony_ci		goto out_bargs;
41128c2ecf20Sopenharmony_ci	}
41138c2ecf20Sopenharmony_ci
41148c2ecf20Sopenharmony_ci	if (arg) {
41158c2ecf20Sopenharmony_ci		memcpy(&bctl->data, &bargs->data, sizeof(bctl->data));
41168c2ecf20Sopenharmony_ci		memcpy(&bctl->meta, &bargs->meta, sizeof(bctl->meta));
41178c2ecf20Sopenharmony_ci		memcpy(&bctl->sys, &bargs->sys, sizeof(bctl->sys));
41188c2ecf20Sopenharmony_ci
41198c2ecf20Sopenharmony_ci		bctl->flags = bargs->flags;
41208c2ecf20Sopenharmony_ci	} else {
41218c2ecf20Sopenharmony_ci		/* balance everything - no filters */
41228c2ecf20Sopenharmony_ci		bctl->flags |= BTRFS_BALANCE_TYPE_MASK;
41238c2ecf20Sopenharmony_ci	}
41248c2ecf20Sopenharmony_ci
41258c2ecf20Sopenharmony_ci	if (bctl->flags & ~(BTRFS_BALANCE_ARGS_MASK | BTRFS_BALANCE_TYPE_MASK)) {
41268c2ecf20Sopenharmony_ci		ret = -EINVAL;
41278c2ecf20Sopenharmony_ci		goto out_bctl;
41288c2ecf20Sopenharmony_ci	}
41298c2ecf20Sopenharmony_ci
41308c2ecf20Sopenharmony_cido_balance:
41318c2ecf20Sopenharmony_ci	/*
41328c2ecf20Sopenharmony_ci	 * Ownership of bctl and exclusive operation goes to btrfs_balance.
41338c2ecf20Sopenharmony_ci	 * bctl is freed in reset_balance_state, or, if restriper was paused
41348c2ecf20Sopenharmony_ci	 * all the way until unmount, in free_fs_info.  The flag should be
41358c2ecf20Sopenharmony_ci	 * cleared after reset_balance_state.
41368c2ecf20Sopenharmony_ci	 */
41378c2ecf20Sopenharmony_ci	need_unlock = false;
41388c2ecf20Sopenharmony_ci
41398c2ecf20Sopenharmony_ci	ret = btrfs_balance(fs_info, bctl, bargs);
41408c2ecf20Sopenharmony_ci	bctl = NULL;
41418c2ecf20Sopenharmony_ci
41428c2ecf20Sopenharmony_ci	if ((ret == 0 || ret == -ECANCELED) && arg) {
41438c2ecf20Sopenharmony_ci		if (copy_to_user(arg, bargs, sizeof(*bargs)))
41448c2ecf20Sopenharmony_ci			ret = -EFAULT;
41458c2ecf20Sopenharmony_ci	}
41468c2ecf20Sopenharmony_ci
41478c2ecf20Sopenharmony_ciout_bctl:
41488c2ecf20Sopenharmony_ci	kfree(bctl);
41498c2ecf20Sopenharmony_ciout_bargs:
41508c2ecf20Sopenharmony_ci	kfree(bargs);
41518c2ecf20Sopenharmony_ciout_unlock:
41528c2ecf20Sopenharmony_ci	mutex_unlock(&fs_info->balance_mutex);
41538c2ecf20Sopenharmony_ci	if (need_unlock)
41548c2ecf20Sopenharmony_ci		btrfs_exclop_finish(fs_info);
41558c2ecf20Sopenharmony_ciout:
41568c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
41578c2ecf20Sopenharmony_ci	return ret;
41588c2ecf20Sopenharmony_ci}
41598c2ecf20Sopenharmony_ci
41608c2ecf20Sopenharmony_cistatic long btrfs_ioctl_balance_ctl(struct btrfs_fs_info *fs_info, int cmd)
41618c2ecf20Sopenharmony_ci{
41628c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
41638c2ecf20Sopenharmony_ci		return -EPERM;
41648c2ecf20Sopenharmony_ci
41658c2ecf20Sopenharmony_ci	switch (cmd) {
41668c2ecf20Sopenharmony_ci	case BTRFS_BALANCE_CTL_PAUSE:
41678c2ecf20Sopenharmony_ci		return btrfs_pause_balance(fs_info);
41688c2ecf20Sopenharmony_ci	case BTRFS_BALANCE_CTL_CANCEL:
41698c2ecf20Sopenharmony_ci		return btrfs_cancel_balance(fs_info);
41708c2ecf20Sopenharmony_ci	}
41718c2ecf20Sopenharmony_ci
41728c2ecf20Sopenharmony_ci	return -EINVAL;
41738c2ecf20Sopenharmony_ci}
41748c2ecf20Sopenharmony_ci
41758c2ecf20Sopenharmony_cistatic long btrfs_ioctl_balance_progress(struct btrfs_fs_info *fs_info,
41768c2ecf20Sopenharmony_ci					 void __user *arg)
41778c2ecf20Sopenharmony_ci{
41788c2ecf20Sopenharmony_ci	struct btrfs_ioctl_balance_args *bargs;
41798c2ecf20Sopenharmony_ci	int ret = 0;
41808c2ecf20Sopenharmony_ci
41818c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
41828c2ecf20Sopenharmony_ci		return -EPERM;
41838c2ecf20Sopenharmony_ci
41848c2ecf20Sopenharmony_ci	mutex_lock(&fs_info->balance_mutex);
41858c2ecf20Sopenharmony_ci	if (!fs_info->balance_ctl) {
41868c2ecf20Sopenharmony_ci		ret = -ENOTCONN;
41878c2ecf20Sopenharmony_ci		goto out;
41888c2ecf20Sopenharmony_ci	}
41898c2ecf20Sopenharmony_ci
41908c2ecf20Sopenharmony_ci	bargs = kzalloc(sizeof(*bargs), GFP_KERNEL);
41918c2ecf20Sopenharmony_ci	if (!bargs) {
41928c2ecf20Sopenharmony_ci		ret = -ENOMEM;
41938c2ecf20Sopenharmony_ci		goto out;
41948c2ecf20Sopenharmony_ci	}
41958c2ecf20Sopenharmony_ci
41968c2ecf20Sopenharmony_ci	btrfs_update_ioctl_balance_args(fs_info, bargs);
41978c2ecf20Sopenharmony_ci
41988c2ecf20Sopenharmony_ci	if (copy_to_user(arg, bargs, sizeof(*bargs)))
41998c2ecf20Sopenharmony_ci		ret = -EFAULT;
42008c2ecf20Sopenharmony_ci
42018c2ecf20Sopenharmony_ci	kfree(bargs);
42028c2ecf20Sopenharmony_ciout:
42038c2ecf20Sopenharmony_ci	mutex_unlock(&fs_info->balance_mutex);
42048c2ecf20Sopenharmony_ci	return ret;
42058c2ecf20Sopenharmony_ci}
42068c2ecf20Sopenharmony_ci
42078c2ecf20Sopenharmony_cistatic long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
42088c2ecf20Sopenharmony_ci{
42098c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
42108c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
42118c2ecf20Sopenharmony_ci	struct btrfs_ioctl_quota_ctl_args *sa;
42128c2ecf20Sopenharmony_ci	int ret;
42138c2ecf20Sopenharmony_ci
42148c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
42158c2ecf20Sopenharmony_ci		return -EPERM;
42168c2ecf20Sopenharmony_ci
42178c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
42188c2ecf20Sopenharmony_ci	if (ret)
42198c2ecf20Sopenharmony_ci		return ret;
42208c2ecf20Sopenharmony_ci
42218c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
42228c2ecf20Sopenharmony_ci	if (IS_ERR(sa)) {
42238c2ecf20Sopenharmony_ci		ret = PTR_ERR(sa);
42248c2ecf20Sopenharmony_ci		goto drop_write;
42258c2ecf20Sopenharmony_ci	}
42268c2ecf20Sopenharmony_ci
42278c2ecf20Sopenharmony_ci	down_write(&fs_info->subvol_sem);
42288c2ecf20Sopenharmony_ci
42298c2ecf20Sopenharmony_ci	switch (sa->cmd) {
42308c2ecf20Sopenharmony_ci	case BTRFS_QUOTA_CTL_ENABLE:
42318c2ecf20Sopenharmony_ci		ret = btrfs_quota_enable(fs_info);
42328c2ecf20Sopenharmony_ci		break;
42338c2ecf20Sopenharmony_ci	case BTRFS_QUOTA_CTL_DISABLE:
42348c2ecf20Sopenharmony_ci		ret = btrfs_quota_disable(fs_info);
42358c2ecf20Sopenharmony_ci		break;
42368c2ecf20Sopenharmony_ci	default:
42378c2ecf20Sopenharmony_ci		ret = -EINVAL;
42388c2ecf20Sopenharmony_ci		break;
42398c2ecf20Sopenharmony_ci	}
42408c2ecf20Sopenharmony_ci
42418c2ecf20Sopenharmony_ci	kfree(sa);
42428c2ecf20Sopenharmony_ci	up_write(&fs_info->subvol_sem);
42438c2ecf20Sopenharmony_cidrop_write:
42448c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
42458c2ecf20Sopenharmony_ci	return ret;
42468c2ecf20Sopenharmony_ci}
42478c2ecf20Sopenharmony_ci
42488c2ecf20Sopenharmony_cistatic long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
42498c2ecf20Sopenharmony_ci{
42508c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
42518c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
42528c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
42538c2ecf20Sopenharmony_ci	struct btrfs_ioctl_qgroup_assign_args *sa;
42548c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
42558c2ecf20Sopenharmony_ci	int ret;
42568c2ecf20Sopenharmony_ci	int err;
42578c2ecf20Sopenharmony_ci
42588c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
42598c2ecf20Sopenharmony_ci		return -EPERM;
42608c2ecf20Sopenharmony_ci
42618c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
42628c2ecf20Sopenharmony_ci	if (ret)
42638c2ecf20Sopenharmony_ci		return ret;
42648c2ecf20Sopenharmony_ci
42658c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
42668c2ecf20Sopenharmony_ci	if (IS_ERR(sa)) {
42678c2ecf20Sopenharmony_ci		ret = PTR_ERR(sa);
42688c2ecf20Sopenharmony_ci		goto drop_write;
42698c2ecf20Sopenharmony_ci	}
42708c2ecf20Sopenharmony_ci
42718c2ecf20Sopenharmony_ci	trans = btrfs_join_transaction(root);
42728c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
42738c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
42748c2ecf20Sopenharmony_ci		goto out;
42758c2ecf20Sopenharmony_ci	}
42768c2ecf20Sopenharmony_ci
42778c2ecf20Sopenharmony_ci	if (sa->assign) {
42788c2ecf20Sopenharmony_ci		ret = btrfs_add_qgroup_relation(trans, sa->src, sa->dst);
42798c2ecf20Sopenharmony_ci	} else {
42808c2ecf20Sopenharmony_ci		ret = btrfs_del_qgroup_relation(trans, sa->src, sa->dst);
42818c2ecf20Sopenharmony_ci	}
42828c2ecf20Sopenharmony_ci
42838c2ecf20Sopenharmony_ci	/* update qgroup status and info */
42848c2ecf20Sopenharmony_ci	mutex_lock(&fs_info->qgroup_ioctl_lock);
42858c2ecf20Sopenharmony_ci	err = btrfs_run_qgroups(trans);
42868c2ecf20Sopenharmony_ci	mutex_unlock(&fs_info->qgroup_ioctl_lock);
42878c2ecf20Sopenharmony_ci	if (err < 0)
42888c2ecf20Sopenharmony_ci		btrfs_handle_fs_error(fs_info, err,
42898c2ecf20Sopenharmony_ci				      "failed to update qgroup status and info");
42908c2ecf20Sopenharmony_ci	err = btrfs_end_transaction(trans);
42918c2ecf20Sopenharmony_ci	if (err && !ret)
42928c2ecf20Sopenharmony_ci		ret = err;
42938c2ecf20Sopenharmony_ci
42948c2ecf20Sopenharmony_ciout:
42958c2ecf20Sopenharmony_ci	kfree(sa);
42968c2ecf20Sopenharmony_cidrop_write:
42978c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
42988c2ecf20Sopenharmony_ci	return ret;
42998c2ecf20Sopenharmony_ci}
43008c2ecf20Sopenharmony_ci
43018c2ecf20Sopenharmony_cistatic long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
43028c2ecf20Sopenharmony_ci{
43038c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
43048c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
43058c2ecf20Sopenharmony_ci	struct btrfs_ioctl_qgroup_create_args *sa;
43068c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
43078c2ecf20Sopenharmony_ci	int ret;
43088c2ecf20Sopenharmony_ci	int err;
43098c2ecf20Sopenharmony_ci
43108c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
43118c2ecf20Sopenharmony_ci		return -EPERM;
43128c2ecf20Sopenharmony_ci
43138c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
43148c2ecf20Sopenharmony_ci	if (ret)
43158c2ecf20Sopenharmony_ci		return ret;
43168c2ecf20Sopenharmony_ci
43178c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
43188c2ecf20Sopenharmony_ci	if (IS_ERR(sa)) {
43198c2ecf20Sopenharmony_ci		ret = PTR_ERR(sa);
43208c2ecf20Sopenharmony_ci		goto drop_write;
43218c2ecf20Sopenharmony_ci	}
43228c2ecf20Sopenharmony_ci
43238c2ecf20Sopenharmony_ci	if (!sa->qgroupid) {
43248c2ecf20Sopenharmony_ci		ret = -EINVAL;
43258c2ecf20Sopenharmony_ci		goto out;
43268c2ecf20Sopenharmony_ci	}
43278c2ecf20Sopenharmony_ci
43288c2ecf20Sopenharmony_ci	if (sa->create && is_fstree(sa->qgroupid)) {
43298c2ecf20Sopenharmony_ci		ret = -EINVAL;
43308c2ecf20Sopenharmony_ci		goto out;
43318c2ecf20Sopenharmony_ci	}
43328c2ecf20Sopenharmony_ci
43338c2ecf20Sopenharmony_ci	trans = btrfs_join_transaction(root);
43348c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
43358c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
43368c2ecf20Sopenharmony_ci		goto out;
43378c2ecf20Sopenharmony_ci	}
43388c2ecf20Sopenharmony_ci
43398c2ecf20Sopenharmony_ci	if (sa->create) {
43408c2ecf20Sopenharmony_ci		ret = btrfs_create_qgroup(trans, sa->qgroupid);
43418c2ecf20Sopenharmony_ci	} else {
43428c2ecf20Sopenharmony_ci		ret = btrfs_remove_qgroup(trans, sa->qgroupid);
43438c2ecf20Sopenharmony_ci	}
43448c2ecf20Sopenharmony_ci
43458c2ecf20Sopenharmony_ci	err = btrfs_end_transaction(trans);
43468c2ecf20Sopenharmony_ci	if (err && !ret)
43478c2ecf20Sopenharmony_ci		ret = err;
43488c2ecf20Sopenharmony_ci
43498c2ecf20Sopenharmony_ciout:
43508c2ecf20Sopenharmony_ci	kfree(sa);
43518c2ecf20Sopenharmony_cidrop_write:
43528c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
43538c2ecf20Sopenharmony_ci	return ret;
43548c2ecf20Sopenharmony_ci}
43558c2ecf20Sopenharmony_ci
43568c2ecf20Sopenharmony_cistatic long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
43578c2ecf20Sopenharmony_ci{
43588c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
43598c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
43608c2ecf20Sopenharmony_ci	struct btrfs_ioctl_qgroup_limit_args *sa;
43618c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
43628c2ecf20Sopenharmony_ci	int ret;
43638c2ecf20Sopenharmony_ci	int err;
43648c2ecf20Sopenharmony_ci	u64 qgroupid;
43658c2ecf20Sopenharmony_ci
43668c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
43678c2ecf20Sopenharmony_ci		return -EPERM;
43688c2ecf20Sopenharmony_ci
43698c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
43708c2ecf20Sopenharmony_ci	if (ret)
43718c2ecf20Sopenharmony_ci		return ret;
43728c2ecf20Sopenharmony_ci
43738c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
43748c2ecf20Sopenharmony_ci	if (IS_ERR(sa)) {
43758c2ecf20Sopenharmony_ci		ret = PTR_ERR(sa);
43768c2ecf20Sopenharmony_ci		goto drop_write;
43778c2ecf20Sopenharmony_ci	}
43788c2ecf20Sopenharmony_ci
43798c2ecf20Sopenharmony_ci	trans = btrfs_join_transaction(root);
43808c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
43818c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
43828c2ecf20Sopenharmony_ci		goto out;
43838c2ecf20Sopenharmony_ci	}
43848c2ecf20Sopenharmony_ci
43858c2ecf20Sopenharmony_ci	qgroupid = sa->qgroupid;
43868c2ecf20Sopenharmony_ci	if (!qgroupid) {
43878c2ecf20Sopenharmony_ci		/* take the current subvol as qgroup */
43888c2ecf20Sopenharmony_ci		qgroupid = root->root_key.objectid;
43898c2ecf20Sopenharmony_ci	}
43908c2ecf20Sopenharmony_ci
43918c2ecf20Sopenharmony_ci	ret = btrfs_limit_qgroup(trans, qgroupid, &sa->lim);
43928c2ecf20Sopenharmony_ci
43938c2ecf20Sopenharmony_ci	err = btrfs_end_transaction(trans);
43948c2ecf20Sopenharmony_ci	if (err && !ret)
43958c2ecf20Sopenharmony_ci		ret = err;
43968c2ecf20Sopenharmony_ci
43978c2ecf20Sopenharmony_ciout:
43988c2ecf20Sopenharmony_ci	kfree(sa);
43998c2ecf20Sopenharmony_cidrop_write:
44008c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
44018c2ecf20Sopenharmony_ci	return ret;
44028c2ecf20Sopenharmony_ci}
44038c2ecf20Sopenharmony_ci
44048c2ecf20Sopenharmony_cistatic long btrfs_ioctl_quota_rescan(struct file *file, void __user *arg)
44058c2ecf20Sopenharmony_ci{
44068c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
44078c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
44088c2ecf20Sopenharmony_ci	struct btrfs_ioctl_quota_rescan_args *qsa;
44098c2ecf20Sopenharmony_ci	int ret;
44108c2ecf20Sopenharmony_ci
44118c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
44128c2ecf20Sopenharmony_ci		return -EPERM;
44138c2ecf20Sopenharmony_ci
44148c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
44158c2ecf20Sopenharmony_ci	if (ret)
44168c2ecf20Sopenharmony_ci		return ret;
44178c2ecf20Sopenharmony_ci
44188c2ecf20Sopenharmony_ci	qsa = memdup_user(arg, sizeof(*qsa));
44198c2ecf20Sopenharmony_ci	if (IS_ERR(qsa)) {
44208c2ecf20Sopenharmony_ci		ret = PTR_ERR(qsa);
44218c2ecf20Sopenharmony_ci		goto drop_write;
44228c2ecf20Sopenharmony_ci	}
44238c2ecf20Sopenharmony_ci
44248c2ecf20Sopenharmony_ci	if (qsa->flags) {
44258c2ecf20Sopenharmony_ci		ret = -EINVAL;
44268c2ecf20Sopenharmony_ci		goto out;
44278c2ecf20Sopenharmony_ci	}
44288c2ecf20Sopenharmony_ci
44298c2ecf20Sopenharmony_ci	ret = btrfs_qgroup_rescan(fs_info);
44308c2ecf20Sopenharmony_ci
44318c2ecf20Sopenharmony_ciout:
44328c2ecf20Sopenharmony_ci	kfree(qsa);
44338c2ecf20Sopenharmony_cidrop_write:
44348c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
44358c2ecf20Sopenharmony_ci	return ret;
44368c2ecf20Sopenharmony_ci}
44378c2ecf20Sopenharmony_ci
44388c2ecf20Sopenharmony_cistatic long btrfs_ioctl_quota_rescan_status(struct btrfs_fs_info *fs_info,
44398c2ecf20Sopenharmony_ci						void __user *arg)
44408c2ecf20Sopenharmony_ci{
44418c2ecf20Sopenharmony_ci	struct btrfs_ioctl_quota_rescan_args *qsa;
44428c2ecf20Sopenharmony_ci	int ret = 0;
44438c2ecf20Sopenharmony_ci
44448c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
44458c2ecf20Sopenharmony_ci		return -EPERM;
44468c2ecf20Sopenharmony_ci
44478c2ecf20Sopenharmony_ci	qsa = kzalloc(sizeof(*qsa), GFP_KERNEL);
44488c2ecf20Sopenharmony_ci	if (!qsa)
44498c2ecf20Sopenharmony_ci		return -ENOMEM;
44508c2ecf20Sopenharmony_ci
44518c2ecf20Sopenharmony_ci	if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
44528c2ecf20Sopenharmony_ci		qsa->flags = 1;
44538c2ecf20Sopenharmony_ci		qsa->progress = fs_info->qgroup_rescan_progress.objectid;
44548c2ecf20Sopenharmony_ci	}
44558c2ecf20Sopenharmony_ci
44568c2ecf20Sopenharmony_ci	if (copy_to_user(arg, qsa, sizeof(*qsa)))
44578c2ecf20Sopenharmony_ci		ret = -EFAULT;
44588c2ecf20Sopenharmony_ci
44598c2ecf20Sopenharmony_ci	kfree(qsa);
44608c2ecf20Sopenharmony_ci	return ret;
44618c2ecf20Sopenharmony_ci}
44628c2ecf20Sopenharmony_ci
44638c2ecf20Sopenharmony_cistatic long btrfs_ioctl_quota_rescan_wait(struct btrfs_fs_info *fs_info,
44648c2ecf20Sopenharmony_ci						void __user *arg)
44658c2ecf20Sopenharmony_ci{
44668c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
44678c2ecf20Sopenharmony_ci		return -EPERM;
44688c2ecf20Sopenharmony_ci
44698c2ecf20Sopenharmony_ci	return btrfs_qgroup_wait_for_completion(fs_info, true);
44708c2ecf20Sopenharmony_ci}
44718c2ecf20Sopenharmony_ci
44728c2ecf20Sopenharmony_cistatic long _btrfs_ioctl_set_received_subvol(struct file *file,
44738c2ecf20Sopenharmony_ci					    struct btrfs_ioctl_received_subvol_args *sa)
44748c2ecf20Sopenharmony_ci{
44758c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
44768c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
44778c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
44788c2ecf20Sopenharmony_ci	struct btrfs_root_item *root_item = &root->root_item;
44798c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
44808c2ecf20Sopenharmony_ci	struct timespec64 ct = current_time(inode);
44818c2ecf20Sopenharmony_ci	int ret = 0;
44828c2ecf20Sopenharmony_ci	int received_uuid_changed;
44838c2ecf20Sopenharmony_ci
44848c2ecf20Sopenharmony_ci	if (!inode_owner_or_capable(inode))
44858c2ecf20Sopenharmony_ci		return -EPERM;
44868c2ecf20Sopenharmony_ci
44878c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
44888c2ecf20Sopenharmony_ci	if (ret < 0)
44898c2ecf20Sopenharmony_ci		return ret;
44908c2ecf20Sopenharmony_ci
44918c2ecf20Sopenharmony_ci	down_write(&fs_info->subvol_sem);
44928c2ecf20Sopenharmony_ci
44938c2ecf20Sopenharmony_ci	if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) {
44948c2ecf20Sopenharmony_ci		ret = -EINVAL;
44958c2ecf20Sopenharmony_ci		goto out;
44968c2ecf20Sopenharmony_ci	}
44978c2ecf20Sopenharmony_ci
44988c2ecf20Sopenharmony_ci	if (btrfs_root_readonly(root)) {
44998c2ecf20Sopenharmony_ci		ret = -EROFS;
45008c2ecf20Sopenharmony_ci		goto out;
45018c2ecf20Sopenharmony_ci	}
45028c2ecf20Sopenharmony_ci
45038c2ecf20Sopenharmony_ci	/*
45048c2ecf20Sopenharmony_ci	 * 1 - root item
45058c2ecf20Sopenharmony_ci	 * 2 - uuid items (received uuid + subvol uuid)
45068c2ecf20Sopenharmony_ci	 */
45078c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 3);
45088c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
45098c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
45108c2ecf20Sopenharmony_ci		trans = NULL;
45118c2ecf20Sopenharmony_ci		goto out;
45128c2ecf20Sopenharmony_ci	}
45138c2ecf20Sopenharmony_ci
45148c2ecf20Sopenharmony_ci	sa->rtransid = trans->transid;
45158c2ecf20Sopenharmony_ci	sa->rtime.sec = ct.tv_sec;
45168c2ecf20Sopenharmony_ci	sa->rtime.nsec = ct.tv_nsec;
45178c2ecf20Sopenharmony_ci
45188c2ecf20Sopenharmony_ci	received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid,
45198c2ecf20Sopenharmony_ci				       BTRFS_UUID_SIZE);
45208c2ecf20Sopenharmony_ci	if (received_uuid_changed &&
45218c2ecf20Sopenharmony_ci	    !btrfs_is_empty_uuid(root_item->received_uuid)) {
45228c2ecf20Sopenharmony_ci		ret = btrfs_uuid_tree_remove(trans, root_item->received_uuid,
45238c2ecf20Sopenharmony_ci					  BTRFS_UUID_KEY_RECEIVED_SUBVOL,
45248c2ecf20Sopenharmony_ci					  root->root_key.objectid);
45258c2ecf20Sopenharmony_ci		if (ret && ret != -ENOENT) {
45268c2ecf20Sopenharmony_ci		        btrfs_abort_transaction(trans, ret);
45278c2ecf20Sopenharmony_ci		        btrfs_end_transaction(trans);
45288c2ecf20Sopenharmony_ci		        goto out;
45298c2ecf20Sopenharmony_ci		}
45308c2ecf20Sopenharmony_ci	}
45318c2ecf20Sopenharmony_ci	memcpy(root_item->received_uuid, sa->uuid, BTRFS_UUID_SIZE);
45328c2ecf20Sopenharmony_ci	btrfs_set_root_stransid(root_item, sa->stransid);
45338c2ecf20Sopenharmony_ci	btrfs_set_root_rtransid(root_item, sa->rtransid);
45348c2ecf20Sopenharmony_ci	btrfs_set_stack_timespec_sec(&root_item->stime, sa->stime.sec);
45358c2ecf20Sopenharmony_ci	btrfs_set_stack_timespec_nsec(&root_item->stime, sa->stime.nsec);
45368c2ecf20Sopenharmony_ci	btrfs_set_stack_timespec_sec(&root_item->rtime, sa->rtime.sec);
45378c2ecf20Sopenharmony_ci	btrfs_set_stack_timespec_nsec(&root_item->rtime, sa->rtime.nsec);
45388c2ecf20Sopenharmony_ci
45398c2ecf20Sopenharmony_ci	ret = btrfs_update_root(trans, fs_info->tree_root,
45408c2ecf20Sopenharmony_ci				&root->root_key, &root->root_item);
45418c2ecf20Sopenharmony_ci	if (ret < 0) {
45428c2ecf20Sopenharmony_ci		btrfs_end_transaction(trans);
45438c2ecf20Sopenharmony_ci		goto out;
45448c2ecf20Sopenharmony_ci	}
45458c2ecf20Sopenharmony_ci	if (received_uuid_changed && !btrfs_is_empty_uuid(sa->uuid)) {
45468c2ecf20Sopenharmony_ci		ret = btrfs_uuid_tree_add(trans, sa->uuid,
45478c2ecf20Sopenharmony_ci					  BTRFS_UUID_KEY_RECEIVED_SUBVOL,
45488c2ecf20Sopenharmony_ci					  root->root_key.objectid);
45498c2ecf20Sopenharmony_ci		if (ret < 0 && ret != -EEXIST) {
45508c2ecf20Sopenharmony_ci			btrfs_abort_transaction(trans, ret);
45518c2ecf20Sopenharmony_ci			btrfs_end_transaction(trans);
45528c2ecf20Sopenharmony_ci			goto out;
45538c2ecf20Sopenharmony_ci		}
45548c2ecf20Sopenharmony_ci	}
45558c2ecf20Sopenharmony_ci	ret = btrfs_commit_transaction(trans);
45568c2ecf20Sopenharmony_ciout:
45578c2ecf20Sopenharmony_ci	up_write(&fs_info->subvol_sem);
45588c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
45598c2ecf20Sopenharmony_ci	return ret;
45608c2ecf20Sopenharmony_ci}
45618c2ecf20Sopenharmony_ci
45628c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
45638c2ecf20Sopenharmony_cistatic long btrfs_ioctl_set_received_subvol_32(struct file *file,
45648c2ecf20Sopenharmony_ci						void __user *arg)
45658c2ecf20Sopenharmony_ci{
45668c2ecf20Sopenharmony_ci	struct btrfs_ioctl_received_subvol_args_32 *args32 = NULL;
45678c2ecf20Sopenharmony_ci	struct btrfs_ioctl_received_subvol_args *args64 = NULL;
45688c2ecf20Sopenharmony_ci	int ret = 0;
45698c2ecf20Sopenharmony_ci
45708c2ecf20Sopenharmony_ci	args32 = memdup_user(arg, sizeof(*args32));
45718c2ecf20Sopenharmony_ci	if (IS_ERR(args32))
45728c2ecf20Sopenharmony_ci		return PTR_ERR(args32);
45738c2ecf20Sopenharmony_ci
45748c2ecf20Sopenharmony_ci	args64 = kmalloc(sizeof(*args64), GFP_KERNEL);
45758c2ecf20Sopenharmony_ci	if (!args64) {
45768c2ecf20Sopenharmony_ci		ret = -ENOMEM;
45778c2ecf20Sopenharmony_ci		goto out;
45788c2ecf20Sopenharmony_ci	}
45798c2ecf20Sopenharmony_ci
45808c2ecf20Sopenharmony_ci	memcpy(args64->uuid, args32->uuid, BTRFS_UUID_SIZE);
45818c2ecf20Sopenharmony_ci	args64->stransid = args32->stransid;
45828c2ecf20Sopenharmony_ci	args64->rtransid = args32->rtransid;
45838c2ecf20Sopenharmony_ci	args64->stime.sec = args32->stime.sec;
45848c2ecf20Sopenharmony_ci	args64->stime.nsec = args32->stime.nsec;
45858c2ecf20Sopenharmony_ci	args64->rtime.sec = args32->rtime.sec;
45868c2ecf20Sopenharmony_ci	args64->rtime.nsec = args32->rtime.nsec;
45878c2ecf20Sopenharmony_ci	args64->flags = args32->flags;
45888c2ecf20Sopenharmony_ci
45898c2ecf20Sopenharmony_ci	ret = _btrfs_ioctl_set_received_subvol(file, args64);
45908c2ecf20Sopenharmony_ci	if (ret)
45918c2ecf20Sopenharmony_ci		goto out;
45928c2ecf20Sopenharmony_ci
45938c2ecf20Sopenharmony_ci	memcpy(args32->uuid, args64->uuid, BTRFS_UUID_SIZE);
45948c2ecf20Sopenharmony_ci	args32->stransid = args64->stransid;
45958c2ecf20Sopenharmony_ci	args32->rtransid = args64->rtransid;
45968c2ecf20Sopenharmony_ci	args32->stime.sec = args64->stime.sec;
45978c2ecf20Sopenharmony_ci	args32->stime.nsec = args64->stime.nsec;
45988c2ecf20Sopenharmony_ci	args32->rtime.sec = args64->rtime.sec;
45998c2ecf20Sopenharmony_ci	args32->rtime.nsec = args64->rtime.nsec;
46008c2ecf20Sopenharmony_ci	args32->flags = args64->flags;
46018c2ecf20Sopenharmony_ci
46028c2ecf20Sopenharmony_ci	ret = copy_to_user(arg, args32, sizeof(*args32));
46038c2ecf20Sopenharmony_ci	if (ret)
46048c2ecf20Sopenharmony_ci		ret = -EFAULT;
46058c2ecf20Sopenharmony_ci
46068c2ecf20Sopenharmony_ciout:
46078c2ecf20Sopenharmony_ci	kfree(args32);
46088c2ecf20Sopenharmony_ci	kfree(args64);
46098c2ecf20Sopenharmony_ci	return ret;
46108c2ecf20Sopenharmony_ci}
46118c2ecf20Sopenharmony_ci#endif
46128c2ecf20Sopenharmony_ci
46138c2ecf20Sopenharmony_cistatic long btrfs_ioctl_set_received_subvol(struct file *file,
46148c2ecf20Sopenharmony_ci					    void __user *arg)
46158c2ecf20Sopenharmony_ci{
46168c2ecf20Sopenharmony_ci	struct btrfs_ioctl_received_subvol_args *sa = NULL;
46178c2ecf20Sopenharmony_ci	int ret = 0;
46188c2ecf20Sopenharmony_ci
46198c2ecf20Sopenharmony_ci	sa = memdup_user(arg, sizeof(*sa));
46208c2ecf20Sopenharmony_ci	if (IS_ERR(sa))
46218c2ecf20Sopenharmony_ci		return PTR_ERR(sa);
46228c2ecf20Sopenharmony_ci
46238c2ecf20Sopenharmony_ci	ret = _btrfs_ioctl_set_received_subvol(file, sa);
46248c2ecf20Sopenharmony_ci
46258c2ecf20Sopenharmony_ci	if (ret)
46268c2ecf20Sopenharmony_ci		goto out;
46278c2ecf20Sopenharmony_ci
46288c2ecf20Sopenharmony_ci	ret = copy_to_user(arg, sa, sizeof(*sa));
46298c2ecf20Sopenharmony_ci	if (ret)
46308c2ecf20Sopenharmony_ci		ret = -EFAULT;
46318c2ecf20Sopenharmony_ci
46328c2ecf20Sopenharmony_ciout:
46338c2ecf20Sopenharmony_ci	kfree(sa);
46348c2ecf20Sopenharmony_ci	return ret;
46358c2ecf20Sopenharmony_ci}
46368c2ecf20Sopenharmony_ci
46378c2ecf20Sopenharmony_cistatic int btrfs_ioctl_get_fslabel(struct btrfs_fs_info *fs_info,
46388c2ecf20Sopenharmony_ci					void __user *arg)
46398c2ecf20Sopenharmony_ci{
46408c2ecf20Sopenharmony_ci	size_t len;
46418c2ecf20Sopenharmony_ci	int ret;
46428c2ecf20Sopenharmony_ci	char label[BTRFS_LABEL_SIZE];
46438c2ecf20Sopenharmony_ci
46448c2ecf20Sopenharmony_ci	spin_lock(&fs_info->super_lock);
46458c2ecf20Sopenharmony_ci	memcpy(label, fs_info->super_copy->label, BTRFS_LABEL_SIZE);
46468c2ecf20Sopenharmony_ci	spin_unlock(&fs_info->super_lock);
46478c2ecf20Sopenharmony_ci
46488c2ecf20Sopenharmony_ci	len = strnlen(label, BTRFS_LABEL_SIZE);
46498c2ecf20Sopenharmony_ci
46508c2ecf20Sopenharmony_ci	if (len == BTRFS_LABEL_SIZE) {
46518c2ecf20Sopenharmony_ci		btrfs_warn(fs_info,
46528c2ecf20Sopenharmony_ci			   "label is too long, return the first %zu bytes",
46538c2ecf20Sopenharmony_ci			   --len);
46548c2ecf20Sopenharmony_ci	}
46558c2ecf20Sopenharmony_ci
46568c2ecf20Sopenharmony_ci	ret = copy_to_user(arg, label, len);
46578c2ecf20Sopenharmony_ci
46588c2ecf20Sopenharmony_ci	return ret ? -EFAULT : 0;
46598c2ecf20Sopenharmony_ci}
46608c2ecf20Sopenharmony_ci
46618c2ecf20Sopenharmony_cistatic int btrfs_ioctl_set_fslabel(struct file *file, void __user *arg)
46628c2ecf20Sopenharmony_ci{
46638c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
46648c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
46658c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
46668c2ecf20Sopenharmony_ci	struct btrfs_super_block *super_block = fs_info->super_copy;
46678c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
46688c2ecf20Sopenharmony_ci	char label[BTRFS_LABEL_SIZE];
46698c2ecf20Sopenharmony_ci	int ret;
46708c2ecf20Sopenharmony_ci
46718c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
46728c2ecf20Sopenharmony_ci		return -EPERM;
46738c2ecf20Sopenharmony_ci
46748c2ecf20Sopenharmony_ci	if (copy_from_user(label, arg, sizeof(label)))
46758c2ecf20Sopenharmony_ci		return -EFAULT;
46768c2ecf20Sopenharmony_ci
46778c2ecf20Sopenharmony_ci	if (strnlen(label, BTRFS_LABEL_SIZE) == BTRFS_LABEL_SIZE) {
46788c2ecf20Sopenharmony_ci		btrfs_err(fs_info,
46798c2ecf20Sopenharmony_ci			  "unable to set label with more than %d bytes",
46808c2ecf20Sopenharmony_ci			  BTRFS_LABEL_SIZE - 1);
46818c2ecf20Sopenharmony_ci		return -EINVAL;
46828c2ecf20Sopenharmony_ci	}
46838c2ecf20Sopenharmony_ci
46848c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
46858c2ecf20Sopenharmony_ci	if (ret)
46868c2ecf20Sopenharmony_ci		return ret;
46878c2ecf20Sopenharmony_ci
46888c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 0);
46898c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
46908c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
46918c2ecf20Sopenharmony_ci		goto out_unlock;
46928c2ecf20Sopenharmony_ci	}
46938c2ecf20Sopenharmony_ci
46948c2ecf20Sopenharmony_ci	spin_lock(&fs_info->super_lock);
46958c2ecf20Sopenharmony_ci	strcpy(super_block->label, label);
46968c2ecf20Sopenharmony_ci	spin_unlock(&fs_info->super_lock);
46978c2ecf20Sopenharmony_ci	ret = btrfs_commit_transaction(trans);
46988c2ecf20Sopenharmony_ci
46998c2ecf20Sopenharmony_ciout_unlock:
47008c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
47018c2ecf20Sopenharmony_ci	return ret;
47028c2ecf20Sopenharmony_ci}
47038c2ecf20Sopenharmony_ci
47048c2ecf20Sopenharmony_ci#define INIT_FEATURE_FLAGS(suffix) \
47058c2ecf20Sopenharmony_ci	{ .compat_flags = BTRFS_FEATURE_COMPAT_##suffix, \
47068c2ecf20Sopenharmony_ci	  .compat_ro_flags = BTRFS_FEATURE_COMPAT_RO_##suffix, \
47078c2ecf20Sopenharmony_ci	  .incompat_flags = BTRFS_FEATURE_INCOMPAT_##suffix }
47088c2ecf20Sopenharmony_ci
47098c2ecf20Sopenharmony_ciint btrfs_ioctl_get_supported_features(void __user *arg)
47108c2ecf20Sopenharmony_ci{
47118c2ecf20Sopenharmony_ci	static const struct btrfs_ioctl_feature_flags features[3] = {
47128c2ecf20Sopenharmony_ci		INIT_FEATURE_FLAGS(SUPP),
47138c2ecf20Sopenharmony_ci		INIT_FEATURE_FLAGS(SAFE_SET),
47148c2ecf20Sopenharmony_ci		INIT_FEATURE_FLAGS(SAFE_CLEAR)
47158c2ecf20Sopenharmony_ci	};
47168c2ecf20Sopenharmony_ci
47178c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &features, sizeof(features)))
47188c2ecf20Sopenharmony_ci		return -EFAULT;
47198c2ecf20Sopenharmony_ci
47208c2ecf20Sopenharmony_ci	return 0;
47218c2ecf20Sopenharmony_ci}
47228c2ecf20Sopenharmony_ci
47238c2ecf20Sopenharmony_cistatic int btrfs_ioctl_get_features(struct btrfs_fs_info *fs_info,
47248c2ecf20Sopenharmony_ci					void __user *arg)
47258c2ecf20Sopenharmony_ci{
47268c2ecf20Sopenharmony_ci	struct btrfs_super_block *super_block = fs_info->super_copy;
47278c2ecf20Sopenharmony_ci	struct btrfs_ioctl_feature_flags features;
47288c2ecf20Sopenharmony_ci
47298c2ecf20Sopenharmony_ci	features.compat_flags = btrfs_super_compat_flags(super_block);
47308c2ecf20Sopenharmony_ci	features.compat_ro_flags = btrfs_super_compat_ro_flags(super_block);
47318c2ecf20Sopenharmony_ci	features.incompat_flags = btrfs_super_incompat_flags(super_block);
47328c2ecf20Sopenharmony_ci
47338c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &features, sizeof(features)))
47348c2ecf20Sopenharmony_ci		return -EFAULT;
47358c2ecf20Sopenharmony_ci
47368c2ecf20Sopenharmony_ci	return 0;
47378c2ecf20Sopenharmony_ci}
47388c2ecf20Sopenharmony_ci
47398c2ecf20Sopenharmony_cistatic int check_feature_bits(struct btrfs_fs_info *fs_info,
47408c2ecf20Sopenharmony_ci			      enum btrfs_feature_set set,
47418c2ecf20Sopenharmony_ci			      u64 change_mask, u64 flags, u64 supported_flags,
47428c2ecf20Sopenharmony_ci			      u64 safe_set, u64 safe_clear)
47438c2ecf20Sopenharmony_ci{
47448c2ecf20Sopenharmony_ci	const char *type = btrfs_feature_set_name(set);
47458c2ecf20Sopenharmony_ci	char *names;
47468c2ecf20Sopenharmony_ci	u64 disallowed, unsupported;
47478c2ecf20Sopenharmony_ci	u64 set_mask = flags & change_mask;
47488c2ecf20Sopenharmony_ci	u64 clear_mask = ~flags & change_mask;
47498c2ecf20Sopenharmony_ci
47508c2ecf20Sopenharmony_ci	unsupported = set_mask & ~supported_flags;
47518c2ecf20Sopenharmony_ci	if (unsupported) {
47528c2ecf20Sopenharmony_ci		names = btrfs_printable_features(set, unsupported);
47538c2ecf20Sopenharmony_ci		if (names) {
47548c2ecf20Sopenharmony_ci			btrfs_warn(fs_info,
47558c2ecf20Sopenharmony_ci				   "this kernel does not support the %s feature bit%s",
47568c2ecf20Sopenharmony_ci				   names, strchr(names, ',') ? "s" : "");
47578c2ecf20Sopenharmony_ci			kfree(names);
47588c2ecf20Sopenharmony_ci		} else
47598c2ecf20Sopenharmony_ci			btrfs_warn(fs_info,
47608c2ecf20Sopenharmony_ci				   "this kernel does not support %s bits 0x%llx",
47618c2ecf20Sopenharmony_ci				   type, unsupported);
47628c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
47638c2ecf20Sopenharmony_ci	}
47648c2ecf20Sopenharmony_ci
47658c2ecf20Sopenharmony_ci	disallowed = set_mask & ~safe_set;
47668c2ecf20Sopenharmony_ci	if (disallowed) {
47678c2ecf20Sopenharmony_ci		names = btrfs_printable_features(set, disallowed);
47688c2ecf20Sopenharmony_ci		if (names) {
47698c2ecf20Sopenharmony_ci			btrfs_warn(fs_info,
47708c2ecf20Sopenharmony_ci				   "can't set the %s feature bit%s while mounted",
47718c2ecf20Sopenharmony_ci				   names, strchr(names, ',') ? "s" : "");
47728c2ecf20Sopenharmony_ci			kfree(names);
47738c2ecf20Sopenharmony_ci		} else
47748c2ecf20Sopenharmony_ci			btrfs_warn(fs_info,
47758c2ecf20Sopenharmony_ci				   "can't set %s bits 0x%llx while mounted",
47768c2ecf20Sopenharmony_ci				   type, disallowed);
47778c2ecf20Sopenharmony_ci		return -EPERM;
47788c2ecf20Sopenharmony_ci	}
47798c2ecf20Sopenharmony_ci
47808c2ecf20Sopenharmony_ci	disallowed = clear_mask & ~safe_clear;
47818c2ecf20Sopenharmony_ci	if (disallowed) {
47828c2ecf20Sopenharmony_ci		names = btrfs_printable_features(set, disallowed);
47838c2ecf20Sopenharmony_ci		if (names) {
47848c2ecf20Sopenharmony_ci			btrfs_warn(fs_info,
47858c2ecf20Sopenharmony_ci				   "can't clear the %s feature bit%s while mounted",
47868c2ecf20Sopenharmony_ci				   names, strchr(names, ',') ? "s" : "");
47878c2ecf20Sopenharmony_ci			kfree(names);
47888c2ecf20Sopenharmony_ci		} else
47898c2ecf20Sopenharmony_ci			btrfs_warn(fs_info,
47908c2ecf20Sopenharmony_ci				   "can't clear %s bits 0x%llx while mounted",
47918c2ecf20Sopenharmony_ci				   type, disallowed);
47928c2ecf20Sopenharmony_ci		return -EPERM;
47938c2ecf20Sopenharmony_ci	}
47948c2ecf20Sopenharmony_ci
47958c2ecf20Sopenharmony_ci	return 0;
47968c2ecf20Sopenharmony_ci}
47978c2ecf20Sopenharmony_ci
47988c2ecf20Sopenharmony_ci#define check_feature(fs_info, change_mask, flags, mask_base)	\
47998c2ecf20Sopenharmony_cicheck_feature_bits(fs_info, FEAT_##mask_base, change_mask, flags,	\
48008c2ecf20Sopenharmony_ci		   BTRFS_FEATURE_ ## mask_base ## _SUPP,	\
48018c2ecf20Sopenharmony_ci		   BTRFS_FEATURE_ ## mask_base ## _SAFE_SET,	\
48028c2ecf20Sopenharmony_ci		   BTRFS_FEATURE_ ## mask_base ## _SAFE_CLEAR)
48038c2ecf20Sopenharmony_ci
48048c2ecf20Sopenharmony_cistatic int btrfs_ioctl_set_features(struct file *file, void __user *arg)
48058c2ecf20Sopenharmony_ci{
48068c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
48078c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
48088c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
48098c2ecf20Sopenharmony_ci	struct btrfs_super_block *super_block = fs_info->super_copy;
48108c2ecf20Sopenharmony_ci	struct btrfs_ioctl_feature_flags flags[2];
48118c2ecf20Sopenharmony_ci	struct btrfs_trans_handle *trans;
48128c2ecf20Sopenharmony_ci	u64 newflags;
48138c2ecf20Sopenharmony_ci	int ret;
48148c2ecf20Sopenharmony_ci
48158c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
48168c2ecf20Sopenharmony_ci		return -EPERM;
48178c2ecf20Sopenharmony_ci
48188c2ecf20Sopenharmony_ci	if (copy_from_user(flags, arg, sizeof(flags)))
48198c2ecf20Sopenharmony_ci		return -EFAULT;
48208c2ecf20Sopenharmony_ci
48218c2ecf20Sopenharmony_ci	/* Nothing to do */
48228c2ecf20Sopenharmony_ci	if (!flags[0].compat_flags && !flags[0].compat_ro_flags &&
48238c2ecf20Sopenharmony_ci	    !flags[0].incompat_flags)
48248c2ecf20Sopenharmony_ci		return 0;
48258c2ecf20Sopenharmony_ci
48268c2ecf20Sopenharmony_ci	ret = check_feature(fs_info, flags[0].compat_flags,
48278c2ecf20Sopenharmony_ci			    flags[1].compat_flags, COMPAT);
48288c2ecf20Sopenharmony_ci	if (ret)
48298c2ecf20Sopenharmony_ci		return ret;
48308c2ecf20Sopenharmony_ci
48318c2ecf20Sopenharmony_ci	ret = check_feature(fs_info, flags[0].compat_ro_flags,
48328c2ecf20Sopenharmony_ci			    flags[1].compat_ro_flags, COMPAT_RO);
48338c2ecf20Sopenharmony_ci	if (ret)
48348c2ecf20Sopenharmony_ci		return ret;
48358c2ecf20Sopenharmony_ci
48368c2ecf20Sopenharmony_ci	ret = check_feature(fs_info, flags[0].incompat_flags,
48378c2ecf20Sopenharmony_ci			    flags[1].incompat_flags, INCOMPAT);
48388c2ecf20Sopenharmony_ci	if (ret)
48398c2ecf20Sopenharmony_ci		return ret;
48408c2ecf20Sopenharmony_ci
48418c2ecf20Sopenharmony_ci	ret = mnt_want_write_file(file);
48428c2ecf20Sopenharmony_ci	if (ret)
48438c2ecf20Sopenharmony_ci		return ret;
48448c2ecf20Sopenharmony_ci
48458c2ecf20Sopenharmony_ci	trans = btrfs_start_transaction(root, 0);
48468c2ecf20Sopenharmony_ci	if (IS_ERR(trans)) {
48478c2ecf20Sopenharmony_ci		ret = PTR_ERR(trans);
48488c2ecf20Sopenharmony_ci		goto out_drop_write;
48498c2ecf20Sopenharmony_ci	}
48508c2ecf20Sopenharmony_ci
48518c2ecf20Sopenharmony_ci	spin_lock(&fs_info->super_lock);
48528c2ecf20Sopenharmony_ci	newflags = btrfs_super_compat_flags(super_block);
48538c2ecf20Sopenharmony_ci	newflags |= flags[0].compat_flags & flags[1].compat_flags;
48548c2ecf20Sopenharmony_ci	newflags &= ~(flags[0].compat_flags & ~flags[1].compat_flags);
48558c2ecf20Sopenharmony_ci	btrfs_set_super_compat_flags(super_block, newflags);
48568c2ecf20Sopenharmony_ci
48578c2ecf20Sopenharmony_ci	newflags = btrfs_super_compat_ro_flags(super_block);
48588c2ecf20Sopenharmony_ci	newflags |= flags[0].compat_ro_flags & flags[1].compat_ro_flags;
48598c2ecf20Sopenharmony_ci	newflags &= ~(flags[0].compat_ro_flags & ~flags[1].compat_ro_flags);
48608c2ecf20Sopenharmony_ci	btrfs_set_super_compat_ro_flags(super_block, newflags);
48618c2ecf20Sopenharmony_ci
48628c2ecf20Sopenharmony_ci	newflags = btrfs_super_incompat_flags(super_block);
48638c2ecf20Sopenharmony_ci	newflags |= flags[0].incompat_flags & flags[1].incompat_flags;
48648c2ecf20Sopenharmony_ci	newflags &= ~(flags[0].incompat_flags & ~flags[1].incompat_flags);
48658c2ecf20Sopenharmony_ci	btrfs_set_super_incompat_flags(super_block, newflags);
48668c2ecf20Sopenharmony_ci	spin_unlock(&fs_info->super_lock);
48678c2ecf20Sopenharmony_ci
48688c2ecf20Sopenharmony_ci	ret = btrfs_commit_transaction(trans);
48698c2ecf20Sopenharmony_ciout_drop_write:
48708c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
48718c2ecf20Sopenharmony_ci
48728c2ecf20Sopenharmony_ci	return ret;
48738c2ecf20Sopenharmony_ci}
48748c2ecf20Sopenharmony_ci
48758c2ecf20Sopenharmony_cistatic int _btrfs_ioctl_send(struct file *file, void __user *argp, bool compat)
48768c2ecf20Sopenharmony_ci{
48778c2ecf20Sopenharmony_ci	struct btrfs_ioctl_send_args *arg;
48788c2ecf20Sopenharmony_ci	int ret;
48798c2ecf20Sopenharmony_ci
48808c2ecf20Sopenharmony_ci	if (compat) {
48818c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
48828c2ecf20Sopenharmony_ci		struct btrfs_ioctl_send_args_32 args32 = { 0 };
48838c2ecf20Sopenharmony_ci
48848c2ecf20Sopenharmony_ci		ret = copy_from_user(&args32, argp, sizeof(args32));
48858c2ecf20Sopenharmony_ci		if (ret)
48868c2ecf20Sopenharmony_ci			return -EFAULT;
48878c2ecf20Sopenharmony_ci		arg = kzalloc(sizeof(*arg), GFP_KERNEL);
48888c2ecf20Sopenharmony_ci		if (!arg)
48898c2ecf20Sopenharmony_ci			return -ENOMEM;
48908c2ecf20Sopenharmony_ci		arg->send_fd = args32.send_fd;
48918c2ecf20Sopenharmony_ci		arg->clone_sources_count = args32.clone_sources_count;
48928c2ecf20Sopenharmony_ci		arg->clone_sources = compat_ptr(args32.clone_sources);
48938c2ecf20Sopenharmony_ci		arg->parent_root = args32.parent_root;
48948c2ecf20Sopenharmony_ci		arg->flags = args32.flags;
48958c2ecf20Sopenharmony_ci		memcpy(arg->reserved, args32.reserved,
48968c2ecf20Sopenharmony_ci		       sizeof(args32.reserved));
48978c2ecf20Sopenharmony_ci#else
48988c2ecf20Sopenharmony_ci		return -ENOTTY;
48998c2ecf20Sopenharmony_ci#endif
49008c2ecf20Sopenharmony_ci	} else {
49018c2ecf20Sopenharmony_ci		arg = memdup_user(argp, sizeof(*arg));
49028c2ecf20Sopenharmony_ci		if (IS_ERR(arg))
49038c2ecf20Sopenharmony_ci			return PTR_ERR(arg);
49048c2ecf20Sopenharmony_ci	}
49058c2ecf20Sopenharmony_ci	ret = btrfs_ioctl_send(file, arg);
49068c2ecf20Sopenharmony_ci	kfree(arg);
49078c2ecf20Sopenharmony_ci	return ret;
49088c2ecf20Sopenharmony_ci}
49098c2ecf20Sopenharmony_ci
49108c2ecf20Sopenharmony_cilong btrfs_ioctl(struct file *file, unsigned int
49118c2ecf20Sopenharmony_ci		cmd, unsigned long arg)
49128c2ecf20Sopenharmony_ci{
49138c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
49148c2ecf20Sopenharmony_ci	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
49158c2ecf20Sopenharmony_ci	struct btrfs_root *root = BTRFS_I(inode)->root;
49168c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
49178c2ecf20Sopenharmony_ci
49188c2ecf20Sopenharmony_ci	switch (cmd) {
49198c2ecf20Sopenharmony_ci	case FS_IOC_GETFLAGS:
49208c2ecf20Sopenharmony_ci		return btrfs_ioctl_getflags(file, argp);
49218c2ecf20Sopenharmony_ci	case FS_IOC_SETFLAGS:
49228c2ecf20Sopenharmony_ci		return btrfs_ioctl_setflags(file, argp);
49238c2ecf20Sopenharmony_ci	case FS_IOC_GETVERSION:
49248c2ecf20Sopenharmony_ci		return btrfs_ioctl_getversion(file, argp);
49258c2ecf20Sopenharmony_ci	case FS_IOC_GETFSLABEL:
49268c2ecf20Sopenharmony_ci		return btrfs_ioctl_get_fslabel(fs_info, argp);
49278c2ecf20Sopenharmony_ci	case FS_IOC_SETFSLABEL:
49288c2ecf20Sopenharmony_ci		return btrfs_ioctl_set_fslabel(file, argp);
49298c2ecf20Sopenharmony_ci	case FITRIM:
49308c2ecf20Sopenharmony_ci		return btrfs_ioctl_fitrim(fs_info, argp);
49318c2ecf20Sopenharmony_ci	case BTRFS_IOC_SNAP_CREATE:
49328c2ecf20Sopenharmony_ci		return btrfs_ioctl_snap_create(file, argp, 0);
49338c2ecf20Sopenharmony_ci	case BTRFS_IOC_SNAP_CREATE_V2:
49348c2ecf20Sopenharmony_ci		return btrfs_ioctl_snap_create_v2(file, argp, 0);
49358c2ecf20Sopenharmony_ci	case BTRFS_IOC_SUBVOL_CREATE:
49368c2ecf20Sopenharmony_ci		return btrfs_ioctl_snap_create(file, argp, 1);
49378c2ecf20Sopenharmony_ci	case BTRFS_IOC_SUBVOL_CREATE_V2:
49388c2ecf20Sopenharmony_ci		return btrfs_ioctl_snap_create_v2(file, argp, 1);
49398c2ecf20Sopenharmony_ci	case BTRFS_IOC_SNAP_DESTROY:
49408c2ecf20Sopenharmony_ci		return btrfs_ioctl_snap_destroy(file, argp, false);
49418c2ecf20Sopenharmony_ci	case BTRFS_IOC_SNAP_DESTROY_V2:
49428c2ecf20Sopenharmony_ci		return btrfs_ioctl_snap_destroy(file, argp, true);
49438c2ecf20Sopenharmony_ci	case BTRFS_IOC_SUBVOL_GETFLAGS:
49448c2ecf20Sopenharmony_ci		return btrfs_ioctl_subvol_getflags(file, argp);
49458c2ecf20Sopenharmony_ci	case BTRFS_IOC_SUBVOL_SETFLAGS:
49468c2ecf20Sopenharmony_ci		return btrfs_ioctl_subvol_setflags(file, argp);
49478c2ecf20Sopenharmony_ci	case BTRFS_IOC_DEFAULT_SUBVOL:
49488c2ecf20Sopenharmony_ci		return btrfs_ioctl_default_subvol(file, argp);
49498c2ecf20Sopenharmony_ci	case BTRFS_IOC_DEFRAG:
49508c2ecf20Sopenharmony_ci		return btrfs_ioctl_defrag(file, NULL);
49518c2ecf20Sopenharmony_ci	case BTRFS_IOC_DEFRAG_RANGE:
49528c2ecf20Sopenharmony_ci		return btrfs_ioctl_defrag(file, argp);
49538c2ecf20Sopenharmony_ci	case BTRFS_IOC_RESIZE:
49548c2ecf20Sopenharmony_ci		return btrfs_ioctl_resize(file, argp);
49558c2ecf20Sopenharmony_ci	case BTRFS_IOC_ADD_DEV:
49568c2ecf20Sopenharmony_ci		return btrfs_ioctl_add_dev(fs_info, argp);
49578c2ecf20Sopenharmony_ci	case BTRFS_IOC_RM_DEV:
49588c2ecf20Sopenharmony_ci		return btrfs_ioctl_rm_dev(file, argp);
49598c2ecf20Sopenharmony_ci	case BTRFS_IOC_RM_DEV_V2:
49608c2ecf20Sopenharmony_ci		return btrfs_ioctl_rm_dev_v2(file, argp);
49618c2ecf20Sopenharmony_ci	case BTRFS_IOC_FS_INFO:
49628c2ecf20Sopenharmony_ci		return btrfs_ioctl_fs_info(fs_info, argp);
49638c2ecf20Sopenharmony_ci	case BTRFS_IOC_DEV_INFO:
49648c2ecf20Sopenharmony_ci		return btrfs_ioctl_dev_info(fs_info, argp);
49658c2ecf20Sopenharmony_ci	case BTRFS_IOC_BALANCE:
49668c2ecf20Sopenharmony_ci		return btrfs_ioctl_balance(file, NULL);
49678c2ecf20Sopenharmony_ci	case BTRFS_IOC_TREE_SEARCH:
49688c2ecf20Sopenharmony_ci		return btrfs_ioctl_tree_search(file, argp);
49698c2ecf20Sopenharmony_ci	case BTRFS_IOC_TREE_SEARCH_V2:
49708c2ecf20Sopenharmony_ci		return btrfs_ioctl_tree_search_v2(file, argp);
49718c2ecf20Sopenharmony_ci	case BTRFS_IOC_INO_LOOKUP:
49728c2ecf20Sopenharmony_ci		return btrfs_ioctl_ino_lookup(file, argp);
49738c2ecf20Sopenharmony_ci	case BTRFS_IOC_INO_PATHS:
49748c2ecf20Sopenharmony_ci		return btrfs_ioctl_ino_to_path(root, argp);
49758c2ecf20Sopenharmony_ci	case BTRFS_IOC_LOGICAL_INO:
49768c2ecf20Sopenharmony_ci		return btrfs_ioctl_logical_to_ino(fs_info, argp, 1);
49778c2ecf20Sopenharmony_ci	case BTRFS_IOC_LOGICAL_INO_V2:
49788c2ecf20Sopenharmony_ci		return btrfs_ioctl_logical_to_ino(fs_info, argp, 2);
49798c2ecf20Sopenharmony_ci	case BTRFS_IOC_SPACE_INFO:
49808c2ecf20Sopenharmony_ci		return btrfs_ioctl_space_info(fs_info, argp);
49818c2ecf20Sopenharmony_ci	case BTRFS_IOC_SYNC: {
49828c2ecf20Sopenharmony_ci		int ret;
49838c2ecf20Sopenharmony_ci
49848c2ecf20Sopenharmony_ci		ret = btrfs_start_delalloc_roots(fs_info, U64_MAX, false);
49858c2ecf20Sopenharmony_ci		if (ret)
49868c2ecf20Sopenharmony_ci			return ret;
49878c2ecf20Sopenharmony_ci		ret = btrfs_sync_fs(inode->i_sb, 1);
49888c2ecf20Sopenharmony_ci		/*
49898c2ecf20Sopenharmony_ci		 * The transaction thread may want to do more work,
49908c2ecf20Sopenharmony_ci		 * namely it pokes the cleaner kthread that will start
49918c2ecf20Sopenharmony_ci		 * processing uncleaned subvols.
49928c2ecf20Sopenharmony_ci		 */
49938c2ecf20Sopenharmony_ci		wake_up_process(fs_info->transaction_kthread);
49948c2ecf20Sopenharmony_ci		return ret;
49958c2ecf20Sopenharmony_ci	}
49968c2ecf20Sopenharmony_ci	case BTRFS_IOC_START_SYNC:
49978c2ecf20Sopenharmony_ci		return btrfs_ioctl_start_sync(root, argp);
49988c2ecf20Sopenharmony_ci	case BTRFS_IOC_WAIT_SYNC:
49998c2ecf20Sopenharmony_ci		return btrfs_ioctl_wait_sync(fs_info, argp);
50008c2ecf20Sopenharmony_ci	case BTRFS_IOC_SCRUB:
50018c2ecf20Sopenharmony_ci		return btrfs_ioctl_scrub(file, argp);
50028c2ecf20Sopenharmony_ci	case BTRFS_IOC_SCRUB_CANCEL:
50038c2ecf20Sopenharmony_ci		return btrfs_ioctl_scrub_cancel(fs_info);
50048c2ecf20Sopenharmony_ci	case BTRFS_IOC_SCRUB_PROGRESS:
50058c2ecf20Sopenharmony_ci		return btrfs_ioctl_scrub_progress(fs_info, argp);
50068c2ecf20Sopenharmony_ci	case BTRFS_IOC_BALANCE_V2:
50078c2ecf20Sopenharmony_ci		return btrfs_ioctl_balance(file, argp);
50088c2ecf20Sopenharmony_ci	case BTRFS_IOC_BALANCE_CTL:
50098c2ecf20Sopenharmony_ci		return btrfs_ioctl_balance_ctl(fs_info, arg);
50108c2ecf20Sopenharmony_ci	case BTRFS_IOC_BALANCE_PROGRESS:
50118c2ecf20Sopenharmony_ci		return btrfs_ioctl_balance_progress(fs_info, argp);
50128c2ecf20Sopenharmony_ci	case BTRFS_IOC_SET_RECEIVED_SUBVOL:
50138c2ecf20Sopenharmony_ci		return btrfs_ioctl_set_received_subvol(file, argp);
50148c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
50158c2ecf20Sopenharmony_ci	case BTRFS_IOC_SET_RECEIVED_SUBVOL_32:
50168c2ecf20Sopenharmony_ci		return btrfs_ioctl_set_received_subvol_32(file, argp);
50178c2ecf20Sopenharmony_ci#endif
50188c2ecf20Sopenharmony_ci	case BTRFS_IOC_SEND:
50198c2ecf20Sopenharmony_ci		return _btrfs_ioctl_send(file, argp, false);
50208c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
50218c2ecf20Sopenharmony_ci	case BTRFS_IOC_SEND_32:
50228c2ecf20Sopenharmony_ci		return _btrfs_ioctl_send(file, argp, true);
50238c2ecf20Sopenharmony_ci#endif
50248c2ecf20Sopenharmony_ci	case BTRFS_IOC_GET_DEV_STATS:
50258c2ecf20Sopenharmony_ci		return btrfs_ioctl_get_dev_stats(fs_info, argp);
50268c2ecf20Sopenharmony_ci	case BTRFS_IOC_QUOTA_CTL:
50278c2ecf20Sopenharmony_ci		return btrfs_ioctl_quota_ctl(file, argp);
50288c2ecf20Sopenharmony_ci	case BTRFS_IOC_QGROUP_ASSIGN:
50298c2ecf20Sopenharmony_ci		return btrfs_ioctl_qgroup_assign(file, argp);
50308c2ecf20Sopenharmony_ci	case BTRFS_IOC_QGROUP_CREATE:
50318c2ecf20Sopenharmony_ci		return btrfs_ioctl_qgroup_create(file, argp);
50328c2ecf20Sopenharmony_ci	case BTRFS_IOC_QGROUP_LIMIT:
50338c2ecf20Sopenharmony_ci		return btrfs_ioctl_qgroup_limit(file, argp);
50348c2ecf20Sopenharmony_ci	case BTRFS_IOC_QUOTA_RESCAN:
50358c2ecf20Sopenharmony_ci		return btrfs_ioctl_quota_rescan(file, argp);
50368c2ecf20Sopenharmony_ci	case BTRFS_IOC_QUOTA_RESCAN_STATUS:
50378c2ecf20Sopenharmony_ci		return btrfs_ioctl_quota_rescan_status(fs_info, argp);
50388c2ecf20Sopenharmony_ci	case BTRFS_IOC_QUOTA_RESCAN_WAIT:
50398c2ecf20Sopenharmony_ci		return btrfs_ioctl_quota_rescan_wait(fs_info, argp);
50408c2ecf20Sopenharmony_ci	case BTRFS_IOC_DEV_REPLACE:
50418c2ecf20Sopenharmony_ci		return btrfs_ioctl_dev_replace(fs_info, argp);
50428c2ecf20Sopenharmony_ci	case BTRFS_IOC_GET_SUPPORTED_FEATURES:
50438c2ecf20Sopenharmony_ci		return btrfs_ioctl_get_supported_features(argp);
50448c2ecf20Sopenharmony_ci	case BTRFS_IOC_GET_FEATURES:
50458c2ecf20Sopenharmony_ci		return btrfs_ioctl_get_features(fs_info, argp);
50468c2ecf20Sopenharmony_ci	case BTRFS_IOC_SET_FEATURES:
50478c2ecf20Sopenharmony_ci		return btrfs_ioctl_set_features(file, argp);
50488c2ecf20Sopenharmony_ci	case FS_IOC_FSGETXATTR:
50498c2ecf20Sopenharmony_ci		return btrfs_ioctl_fsgetxattr(file, argp);
50508c2ecf20Sopenharmony_ci	case FS_IOC_FSSETXATTR:
50518c2ecf20Sopenharmony_ci		return btrfs_ioctl_fssetxattr(file, argp);
50528c2ecf20Sopenharmony_ci	case BTRFS_IOC_GET_SUBVOL_INFO:
50538c2ecf20Sopenharmony_ci		return btrfs_ioctl_get_subvol_info(file, argp);
50548c2ecf20Sopenharmony_ci	case BTRFS_IOC_GET_SUBVOL_ROOTREF:
50558c2ecf20Sopenharmony_ci		return btrfs_ioctl_get_subvol_rootref(file, argp);
50568c2ecf20Sopenharmony_ci	case BTRFS_IOC_INO_LOOKUP_USER:
50578c2ecf20Sopenharmony_ci		return btrfs_ioctl_ino_lookup_user(file, argp);
50588c2ecf20Sopenharmony_ci	}
50598c2ecf20Sopenharmony_ci
50608c2ecf20Sopenharmony_ci	return -ENOTTY;
50618c2ecf20Sopenharmony_ci}
50628c2ecf20Sopenharmony_ci
50638c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
50648c2ecf20Sopenharmony_cilong btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
50658c2ecf20Sopenharmony_ci{
50668c2ecf20Sopenharmony_ci	/*
50678c2ecf20Sopenharmony_ci	 * These all access 32-bit values anyway so no further
50688c2ecf20Sopenharmony_ci	 * handling is necessary.
50698c2ecf20Sopenharmony_ci	 */
50708c2ecf20Sopenharmony_ci	switch (cmd) {
50718c2ecf20Sopenharmony_ci	case FS_IOC32_GETFLAGS:
50728c2ecf20Sopenharmony_ci		cmd = FS_IOC_GETFLAGS;
50738c2ecf20Sopenharmony_ci		break;
50748c2ecf20Sopenharmony_ci	case FS_IOC32_SETFLAGS:
50758c2ecf20Sopenharmony_ci		cmd = FS_IOC_SETFLAGS;
50768c2ecf20Sopenharmony_ci		break;
50778c2ecf20Sopenharmony_ci	case FS_IOC32_GETVERSION:
50788c2ecf20Sopenharmony_ci		cmd = FS_IOC_GETVERSION;
50798c2ecf20Sopenharmony_ci		break;
50808c2ecf20Sopenharmony_ci	}
50818c2ecf20Sopenharmony_ci
50828c2ecf20Sopenharmony_ci	return btrfs_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
50838c2ecf20Sopenharmony_ci}
50848c2ecf20Sopenharmony_ci#endif
5085