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