162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007 Oracle. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/bio.h> 862306a36Sopenharmony_ci#include <linux/file.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/fsnotify.h> 1162306a36Sopenharmony_ci#include <linux/pagemap.h> 1262306a36Sopenharmony_ci#include <linux/highmem.h> 1362306a36Sopenharmony_ci#include <linux/time.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/backing-dev.h> 1662306a36Sopenharmony_ci#include <linux/mount.h> 1762306a36Sopenharmony_ci#include <linux/namei.h> 1862306a36Sopenharmony_ci#include <linux/writeback.h> 1962306a36Sopenharmony_ci#include <linux/compat.h> 2062306a36Sopenharmony_ci#include <linux/security.h> 2162306a36Sopenharmony_ci#include <linux/xattr.h> 2262306a36Sopenharmony_ci#include <linux/mm.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/blkdev.h> 2562306a36Sopenharmony_ci#include <linux/uuid.h> 2662306a36Sopenharmony_ci#include <linux/btrfs.h> 2762306a36Sopenharmony_ci#include <linux/uaccess.h> 2862306a36Sopenharmony_ci#include <linux/iversion.h> 2962306a36Sopenharmony_ci#include <linux/fileattr.h> 3062306a36Sopenharmony_ci#include <linux/fsverity.h> 3162306a36Sopenharmony_ci#include <linux/sched/xacct.h> 3262306a36Sopenharmony_ci#include "ctree.h" 3362306a36Sopenharmony_ci#include "disk-io.h" 3462306a36Sopenharmony_ci#include "export.h" 3562306a36Sopenharmony_ci#include "transaction.h" 3662306a36Sopenharmony_ci#include "btrfs_inode.h" 3762306a36Sopenharmony_ci#include "print-tree.h" 3862306a36Sopenharmony_ci#include "volumes.h" 3962306a36Sopenharmony_ci#include "locking.h" 4062306a36Sopenharmony_ci#include "backref.h" 4162306a36Sopenharmony_ci#include "rcu-string.h" 4262306a36Sopenharmony_ci#include "send.h" 4362306a36Sopenharmony_ci#include "dev-replace.h" 4462306a36Sopenharmony_ci#include "props.h" 4562306a36Sopenharmony_ci#include "sysfs.h" 4662306a36Sopenharmony_ci#include "qgroup.h" 4762306a36Sopenharmony_ci#include "tree-log.h" 4862306a36Sopenharmony_ci#include "compression.h" 4962306a36Sopenharmony_ci#include "space-info.h" 5062306a36Sopenharmony_ci#include "delalloc-space.h" 5162306a36Sopenharmony_ci#include "block-group.h" 5262306a36Sopenharmony_ci#include "subpage.h" 5362306a36Sopenharmony_ci#include "fs.h" 5462306a36Sopenharmony_ci#include "accessors.h" 5562306a36Sopenharmony_ci#include "extent-tree.h" 5662306a36Sopenharmony_ci#include "root-tree.h" 5762306a36Sopenharmony_ci#include "defrag.h" 5862306a36Sopenharmony_ci#include "dir-item.h" 5962306a36Sopenharmony_ci#include "uuid-tree.h" 6062306a36Sopenharmony_ci#include "ioctl.h" 6162306a36Sopenharmony_ci#include "file.h" 6262306a36Sopenharmony_ci#include "scrub.h" 6362306a36Sopenharmony_ci#include "super.h" 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#ifdef CONFIG_64BIT 6662306a36Sopenharmony_ci/* If we have a 32-bit userspace and 64-bit kernel, then the UAPI 6762306a36Sopenharmony_ci * structures are incorrect, as the timespec structure from userspace 6862306a36Sopenharmony_ci * is 4 bytes too small. We define these alternatives here to teach 6962306a36Sopenharmony_ci * the kernel about the 32-bit struct packing. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistruct btrfs_ioctl_timespec_32 { 7262306a36Sopenharmony_ci __u64 sec; 7362306a36Sopenharmony_ci __u32 nsec; 7462306a36Sopenharmony_ci} __attribute__ ((__packed__)); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct btrfs_ioctl_received_subvol_args_32 { 7762306a36Sopenharmony_ci char uuid[BTRFS_UUID_SIZE]; /* in */ 7862306a36Sopenharmony_ci __u64 stransid; /* in */ 7962306a36Sopenharmony_ci __u64 rtransid; /* out */ 8062306a36Sopenharmony_ci struct btrfs_ioctl_timespec_32 stime; /* in */ 8162306a36Sopenharmony_ci struct btrfs_ioctl_timespec_32 rtime; /* out */ 8262306a36Sopenharmony_ci __u64 flags; /* in */ 8362306a36Sopenharmony_ci __u64 reserved[16]; /* in */ 8462306a36Sopenharmony_ci} __attribute__ ((__packed__)); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \ 8762306a36Sopenharmony_ci struct btrfs_ioctl_received_subvol_args_32) 8862306a36Sopenharmony_ci#endif 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) 9162306a36Sopenharmony_cistruct btrfs_ioctl_send_args_32 { 9262306a36Sopenharmony_ci __s64 send_fd; /* in */ 9362306a36Sopenharmony_ci __u64 clone_sources_count; /* in */ 9462306a36Sopenharmony_ci compat_uptr_t clone_sources; /* in */ 9562306a36Sopenharmony_ci __u64 parent_root; /* in */ 9662306a36Sopenharmony_ci __u64 flags; /* in */ 9762306a36Sopenharmony_ci __u32 version; /* in */ 9862306a36Sopenharmony_ci __u8 reserved[28]; /* in */ 9962306a36Sopenharmony_ci} __attribute__ ((__packed__)); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define BTRFS_IOC_SEND_32 _IOW(BTRFS_IOCTL_MAGIC, 38, \ 10262306a36Sopenharmony_ci struct btrfs_ioctl_send_args_32) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct btrfs_ioctl_encoded_io_args_32 { 10562306a36Sopenharmony_ci compat_uptr_t iov; 10662306a36Sopenharmony_ci compat_ulong_t iovcnt; 10762306a36Sopenharmony_ci __s64 offset; 10862306a36Sopenharmony_ci __u64 flags; 10962306a36Sopenharmony_ci __u64 len; 11062306a36Sopenharmony_ci __u64 unencoded_len; 11162306a36Sopenharmony_ci __u64 unencoded_offset; 11262306a36Sopenharmony_ci __u32 compression; 11362306a36Sopenharmony_ci __u32 encryption; 11462306a36Sopenharmony_ci __u8 reserved[64]; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define BTRFS_IOC_ENCODED_READ_32 _IOR(BTRFS_IOCTL_MAGIC, 64, \ 11862306a36Sopenharmony_ci struct btrfs_ioctl_encoded_io_args_32) 11962306a36Sopenharmony_ci#define BTRFS_IOC_ENCODED_WRITE_32 _IOW(BTRFS_IOCTL_MAGIC, 64, \ 12062306a36Sopenharmony_ci struct btrfs_ioctl_encoded_io_args_32) 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Mask out flags that are inappropriate for the given type of inode. */ 12462306a36Sopenharmony_cistatic unsigned int btrfs_mask_fsflags_for_type(struct inode *inode, 12562306a36Sopenharmony_ci unsigned int flags) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 12862306a36Sopenharmony_ci return flags; 12962306a36Sopenharmony_ci else if (S_ISREG(inode->i_mode)) 13062306a36Sopenharmony_ci return flags & ~FS_DIRSYNC_FL; 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci return flags & (FS_NODUMP_FL | FS_NOATIME_FL); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS 13762306a36Sopenharmony_ci * ioctl. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci unsigned int iflags = 0; 14262306a36Sopenharmony_ci u32 flags = binode->flags; 14362306a36Sopenharmony_ci u32 ro_flags = binode->ro_flags; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (flags & BTRFS_INODE_SYNC) 14662306a36Sopenharmony_ci iflags |= FS_SYNC_FL; 14762306a36Sopenharmony_ci if (flags & BTRFS_INODE_IMMUTABLE) 14862306a36Sopenharmony_ci iflags |= FS_IMMUTABLE_FL; 14962306a36Sopenharmony_ci if (flags & BTRFS_INODE_APPEND) 15062306a36Sopenharmony_ci iflags |= FS_APPEND_FL; 15162306a36Sopenharmony_ci if (flags & BTRFS_INODE_NODUMP) 15262306a36Sopenharmony_ci iflags |= FS_NODUMP_FL; 15362306a36Sopenharmony_ci if (flags & BTRFS_INODE_NOATIME) 15462306a36Sopenharmony_ci iflags |= FS_NOATIME_FL; 15562306a36Sopenharmony_ci if (flags & BTRFS_INODE_DIRSYNC) 15662306a36Sopenharmony_ci iflags |= FS_DIRSYNC_FL; 15762306a36Sopenharmony_ci if (flags & BTRFS_INODE_NODATACOW) 15862306a36Sopenharmony_ci iflags |= FS_NOCOW_FL; 15962306a36Sopenharmony_ci if (ro_flags & BTRFS_INODE_RO_VERITY) 16062306a36Sopenharmony_ci iflags |= FS_VERITY_FL; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (flags & BTRFS_INODE_NOCOMPRESS) 16362306a36Sopenharmony_ci iflags |= FS_NOCOMP_FL; 16462306a36Sopenharmony_ci else if (flags & BTRFS_INODE_COMPRESS) 16562306a36Sopenharmony_ci iflags |= FS_COMPR_FL; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return iflags; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * Update inode->i_flags based on the btrfs internal flags. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_civoid btrfs_sync_inode_flags_to_i_flags(struct inode *inode) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct btrfs_inode *binode = BTRFS_I(inode); 17662306a36Sopenharmony_ci unsigned int new_fl = 0; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (binode->flags & BTRFS_INODE_SYNC) 17962306a36Sopenharmony_ci new_fl |= S_SYNC; 18062306a36Sopenharmony_ci if (binode->flags & BTRFS_INODE_IMMUTABLE) 18162306a36Sopenharmony_ci new_fl |= S_IMMUTABLE; 18262306a36Sopenharmony_ci if (binode->flags & BTRFS_INODE_APPEND) 18362306a36Sopenharmony_ci new_fl |= S_APPEND; 18462306a36Sopenharmony_ci if (binode->flags & BTRFS_INODE_NOATIME) 18562306a36Sopenharmony_ci new_fl |= S_NOATIME; 18662306a36Sopenharmony_ci if (binode->flags & BTRFS_INODE_DIRSYNC) 18762306a36Sopenharmony_ci new_fl |= S_DIRSYNC; 18862306a36Sopenharmony_ci if (binode->ro_flags & BTRFS_INODE_RO_VERITY) 18962306a36Sopenharmony_ci new_fl |= S_VERITY; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci set_mask_bits(&inode->i_flags, 19262306a36Sopenharmony_ci S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC | 19362306a36Sopenharmony_ci S_VERITY, new_fl); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * Check if @flags are a supported and valid set of FS_*_FL flags and that 19862306a36Sopenharmony_ci * the old and new flags are not conflicting 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic int check_fsflags(unsigned int old_flags, unsigned int flags) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ 20362306a36Sopenharmony_ci FS_NOATIME_FL | FS_NODUMP_FL | \ 20462306a36Sopenharmony_ci FS_SYNC_FL | FS_DIRSYNC_FL | \ 20562306a36Sopenharmony_ci FS_NOCOMP_FL | FS_COMPR_FL | 20662306a36Sopenharmony_ci FS_NOCOW_FL)) 20762306a36Sopenharmony_ci return -EOPNOTSUPP; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* COMPR and NOCOMP on new/old are valid */ 21062306a36Sopenharmony_ci if ((flags & FS_NOCOMP_FL) && (flags & FS_COMPR_FL)) 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if ((flags & FS_COMPR_FL) && (flags & FS_NOCOW_FL)) 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* NOCOW and compression options are mutually exclusive */ 21762306a36Sopenharmony_ci if ((old_flags & FS_NOCOW_FL) && (flags & (FS_COMPR_FL | FS_NOCOMP_FL))) 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci if ((flags & FS_NOCOW_FL) && (old_flags & (FS_COMPR_FL | FS_NOCOMP_FL))) 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int check_fsflags_compatible(struct btrfs_fs_info *fs_info, 22662306a36Sopenharmony_ci unsigned int flags) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci if (btrfs_is_zoned(fs_info) && (flags & FS_NOCOW_FL)) 22962306a36Sopenharmony_ci return -EPERM; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * Set flags/xflags from the internal inode flags. The remaining items of 23662306a36Sopenharmony_ci * fsxattr are zeroed. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ciint btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct btrfs_inode *binode = BTRFS_I(d_inode(dentry)); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode)); 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciint btrfs_fileattr_set(struct mnt_idmap *idmap, 24762306a36Sopenharmony_ci struct dentry *dentry, struct fileattr *fa) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 25062306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 25162306a36Sopenharmony_ci struct btrfs_inode *binode = BTRFS_I(inode); 25262306a36Sopenharmony_ci struct btrfs_root *root = binode->root; 25362306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 25462306a36Sopenharmony_ci unsigned int fsflags, old_fsflags; 25562306a36Sopenharmony_ci int ret; 25662306a36Sopenharmony_ci const char *comp = NULL; 25762306a36Sopenharmony_ci u32 binode_flags; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (btrfs_root_readonly(root)) 26062306a36Sopenharmony_ci return -EROFS; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (fileattr_has_fsx(fa)) 26362306a36Sopenharmony_ci return -EOPNOTSUPP; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci fsflags = btrfs_mask_fsflags_for_type(inode, fa->flags); 26662306a36Sopenharmony_ci old_fsflags = btrfs_inode_flags_to_fsflags(binode); 26762306a36Sopenharmony_ci ret = check_fsflags(old_fsflags, fsflags); 26862306a36Sopenharmony_ci if (ret) 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ret = check_fsflags_compatible(fs_info, fsflags); 27262306a36Sopenharmony_ci if (ret) 27362306a36Sopenharmony_ci return ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci binode_flags = binode->flags; 27662306a36Sopenharmony_ci if (fsflags & FS_SYNC_FL) 27762306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_SYNC; 27862306a36Sopenharmony_ci else 27962306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_SYNC; 28062306a36Sopenharmony_ci if (fsflags & FS_IMMUTABLE_FL) 28162306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_IMMUTABLE; 28262306a36Sopenharmony_ci else 28362306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_IMMUTABLE; 28462306a36Sopenharmony_ci if (fsflags & FS_APPEND_FL) 28562306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_APPEND; 28662306a36Sopenharmony_ci else 28762306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_APPEND; 28862306a36Sopenharmony_ci if (fsflags & FS_NODUMP_FL) 28962306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_NODUMP; 29062306a36Sopenharmony_ci else 29162306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_NODUMP; 29262306a36Sopenharmony_ci if (fsflags & FS_NOATIME_FL) 29362306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_NOATIME; 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_NOATIME; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* If coming from FS_IOC_FSSETXATTR then skip unconverted flags */ 29862306a36Sopenharmony_ci if (!fa->flags_valid) { 29962306a36Sopenharmony_ci /* 1 item for the inode */ 30062306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 1); 30162306a36Sopenharmony_ci if (IS_ERR(trans)) 30262306a36Sopenharmony_ci return PTR_ERR(trans); 30362306a36Sopenharmony_ci goto update_flags; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (fsflags & FS_DIRSYNC_FL) 30762306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_DIRSYNC; 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_DIRSYNC; 31062306a36Sopenharmony_ci if (fsflags & FS_NOCOW_FL) { 31162306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * It's safe to turn csums off here, no extents exist. 31462306a36Sopenharmony_ci * Otherwise we want the flag to reflect the real COW 31562306a36Sopenharmony_ci * status of the file and will not set it. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci if (inode->i_size == 0) 31862306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_NODATACOW | 31962306a36Sopenharmony_ci BTRFS_INODE_NODATASUM; 32062306a36Sopenharmony_ci } else { 32162306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_NODATACOW; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci /* 32562306a36Sopenharmony_ci * Revert back under same assumptions as above 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 32862306a36Sopenharmony_ci if (inode->i_size == 0) 32962306a36Sopenharmony_ci binode_flags &= ~(BTRFS_INODE_NODATACOW | 33062306a36Sopenharmony_ci BTRFS_INODE_NODATASUM); 33162306a36Sopenharmony_ci } else { 33262306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_NODATACOW; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * The COMPRESS flag can only be changed by users, while the NOCOMPRESS 33862306a36Sopenharmony_ci * flag may be changed automatically if compression code won't make 33962306a36Sopenharmony_ci * things smaller. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci if (fsflags & FS_NOCOMP_FL) { 34262306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_COMPRESS; 34362306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_NOCOMPRESS; 34462306a36Sopenharmony_ci } else if (fsflags & FS_COMPR_FL) { 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (IS_SWAPFILE(inode)) 34762306a36Sopenharmony_ci return -ETXTBSY; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci binode_flags |= BTRFS_INODE_COMPRESS; 35062306a36Sopenharmony_ci binode_flags &= ~BTRFS_INODE_NOCOMPRESS; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci comp = btrfs_compress_type2str(fs_info->compress_type); 35362306a36Sopenharmony_ci if (!comp || comp[0] == 0) 35462306a36Sopenharmony_ci comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB); 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci binode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * 1 for inode item 36162306a36Sopenharmony_ci * 2 for properties 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 3); 36462306a36Sopenharmony_ci if (IS_ERR(trans)) 36562306a36Sopenharmony_ci return PTR_ERR(trans); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (comp) { 36862306a36Sopenharmony_ci ret = btrfs_set_prop(trans, inode, "btrfs.compression", comp, 36962306a36Sopenharmony_ci strlen(comp), 0); 37062306a36Sopenharmony_ci if (ret) { 37162306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 37262306a36Sopenharmony_ci goto out_end_trans; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci ret = btrfs_set_prop(trans, inode, "btrfs.compression", NULL, 37662306a36Sopenharmony_ci 0, 0); 37762306a36Sopenharmony_ci if (ret && ret != -ENODATA) { 37862306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 37962306a36Sopenharmony_ci goto out_end_trans; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ciupdate_flags: 38462306a36Sopenharmony_ci binode->flags = binode_flags; 38562306a36Sopenharmony_ci btrfs_sync_inode_flags_to_i_flags(inode); 38662306a36Sopenharmony_ci inode_inc_iversion(inode); 38762306a36Sopenharmony_ci inode_set_ctime_current(inode); 38862306a36Sopenharmony_ci ret = btrfs_update_inode(trans, root, BTRFS_I(inode)); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci out_end_trans: 39162306a36Sopenharmony_ci btrfs_end_transaction(trans); 39262306a36Sopenharmony_ci return ret; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* 39662306a36Sopenharmony_ci * Start exclusive operation @type, return true on success 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_cibool btrfs_exclop_start(struct btrfs_fs_info *fs_info, 39962306a36Sopenharmony_ci enum btrfs_exclusive_operation type) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci bool ret = false; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 40462306a36Sopenharmony_ci if (fs_info->exclusive_operation == BTRFS_EXCLOP_NONE) { 40562306a36Sopenharmony_ci fs_info->exclusive_operation = type; 40662306a36Sopenharmony_ci ret = true; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return ret; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* 41462306a36Sopenharmony_ci * Conditionally allow to enter the exclusive operation in case it's compatible 41562306a36Sopenharmony_ci * with the running one. This must be paired with btrfs_exclop_start_unlock and 41662306a36Sopenharmony_ci * btrfs_exclop_finish. 41762306a36Sopenharmony_ci * 41862306a36Sopenharmony_ci * Compatibility: 41962306a36Sopenharmony_ci * - the same type is already running 42062306a36Sopenharmony_ci * - when trying to add a device and balance has been paused 42162306a36Sopenharmony_ci * - not BTRFS_EXCLOP_NONE - this is intentionally incompatible and the caller 42262306a36Sopenharmony_ci * must check the condition first that would allow none -> @type 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cibool btrfs_exclop_start_try_lock(struct btrfs_fs_info *fs_info, 42562306a36Sopenharmony_ci enum btrfs_exclusive_operation type) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 42862306a36Sopenharmony_ci if (fs_info->exclusive_operation == type || 42962306a36Sopenharmony_ci (fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED && 43062306a36Sopenharmony_ci type == BTRFS_EXCLOP_DEV_ADD)) 43162306a36Sopenharmony_ci return true; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 43462306a36Sopenharmony_ci return false; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_civoid btrfs_exclop_start_unlock(struct btrfs_fs_info *fs_info) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_civoid btrfs_exclop_finish(struct btrfs_fs_info *fs_info) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 44562306a36Sopenharmony_ci WRITE_ONCE(fs_info->exclusive_operation, BTRFS_EXCLOP_NONE); 44662306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 44762306a36Sopenharmony_ci sysfs_notify(&fs_info->fs_devices->fsid_kobj, NULL, "exclusive_operation"); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_civoid btrfs_exclop_balance(struct btrfs_fs_info *fs_info, 45162306a36Sopenharmony_ci enum btrfs_exclusive_operation op) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci switch (op) { 45462306a36Sopenharmony_ci case BTRFS_EXCLOP_BALANCE_PAUSED: 45562306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 45662306a36Sopenharmony_ci ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE || 45762306a36Sopenharmony_ci fs_info->exclusive_operation == BTRFS_EXCLOP_DEV_ADD || 45862306a36Sopenharmony_ci fs_info->exclusive_operation == BTRFS_EXCLOP_NONE || 45962306a36Sopenharmony_ci fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED); 46062306a36Sopenharmony_ci fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE_PAUSED; 46162306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci case BTRFS_EXCLOP_BALANCE: 46462306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 46562306a36Sopenharmony_ci ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED); 46662306a36Sopenharmony_ci fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE; 46762306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci default: 47062306a36Sopenharmony_ci btrfs_warn(fs_info, 47162306a36Sopenharmony_ci "invalid exclop balance operation %d requested", op); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int btrfs_ioctl_getversion(struct inode *inode, int __user *arg) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci return put_user(inode->i_generation, arg); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic noinline int btrfs_ioctl_fitrim(struct btrfs_fs_info *fs_info, 48162306a36Sopenharmony_ci void __user *arg) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct btrfs_device *device; 48462306a36Sopenharmony_ci struct fstrim_range range; 48562306a36Sopenharmony_ci u64 minlen = ULLONG_MAX; 48662306a36Sopenharmony_ci u64 num_devices = 0; 48762306a36Sopenharmony_ci int ret; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 49062306a36Sopenharmony_ci return -EPERM; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* 49362306a36Sopenharmony_ci * btrfs_trim_block_group() depends on space cache, which is not 49462306a36Sopenharmony_ci * available in zoned filesystem. So, disallow fitrim on a zoned 49562306a36Sopenharmony_ci * filesystem for now. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci if (btrfs_is_zoned(fs_info)) 49862306a36Sopenharmony_ci return -EOPNOTSUPP; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* 50162306a36Sopenharmony_ci * If the fs is mounted with nologreplay, which requires it to be 50262306a36Sopenharmony_ci * mounted in RO mode as well, we can not allow discard on free space 50362306a36Sopenharmony_ci * inside block groups, because log trees refer to extents that are not 50462306a36Sopenharmony_ci * pinned in a block group's free space cache (pinning the extents is 50562306a36Sopenharmony_ci * precisely the first phase of replaying a log tree). 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci if (btrfs_test_opt(fs_info, NOLOGREPLAY)) 50862306a36Sopenharmony_ci return -EROFS; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci rcu_read_lock(); 51162306a36Sopenharmony_ci list_for_each_entry_rcu(device, &fs_info->fs_devices->devices, 51262306a36Sopenharmony_ci dev_list) { 51362306a36Sopenharmony_ci if (!device->bdev || !bdev_max_discard_sectors(device->bdev)) 51462306a36Sopenharmony_ci continue; 51562306a36Sopenharmony_ci num_devices++; 51662306a36Sopenharmony_ci minlen = min_t(u64, bdev_discard_granularity(device->bdev), 51762306a36Sopenharmony_ci minlen); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci rcu_read_unlock(); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!num_devices) 52262306a36Sopenharmony_ci return -EOPNOTSUPP; 52362306a36Sopenharmony_ci if (copy_from_user(&range, arg, sizeof(range))) 52462306a36Sopenharmony_ci return -EFAULT; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* 52762306a36Sopenharmony_ci * NOTE: Don't truncate the range using super->total_bytes. Bytenr of 52862306a36Sopenharmony_ci * block group is in the logical address space, which can be any 52962306a36Sopenharmony_ci * sectorsize aligned bytenr in the range [0, U64_MAX]. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci if (range.len < fs_info->sb->s_blocksize) 53262306a36Sopenharmony_ci return -EINVAL; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci range.minlen = max(range.minlen, minlen); 53562306a36Sopenharmony_ci ret = btrfs_trim_fs(fs_info, &range); 53662306a36Sopenharmony_ci if (ret < 0) 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (copy_to_user(arg, &range, sizeof(range))) 54062306a36Sopenharmony_ci return -EFAULT; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ciint __pure btrfs_is_empty_uuid(u8 *uuid) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci int i; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci for (i = 0; i < BTRFS_UUID_SIZE; i++) { 55062306a36Sopenharmony_ci if (uuid[i]) 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci return 1; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* 55762306a36Sopenharmony_ci * Calculate the number of transaction items to reserve for creating a subvolume 55862306a36Sopenharmony_ci * or snapshot, not including the inode, directory entries, or parent directory. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic unsigned int create_subvol_num_items(struct btrfs_qgroup_inherit *inherit) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci * 1 to add root block 56462306a36Sopenharmony_ci * 1 to add root item 56562306a36Sopenharmony_ci * 1 to add root ref 56662306a36Sopenharmony_ci * 1 to add root backref 56762306a36Sopenharmony_ci * 1 to add UUID item 56862306a36Sopenharmony_ci * 1 to add qgroup info 56962306a36Sopenharmony_ci * 1 to add qgroup limit 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci * Ideally the last two would only be accounted if qgroups are enabled, 57262306a36Sopenharmony_ci * but that can change between now and the time we would insert them. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci unsigned int num_items = 7; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (inherit) { 57762306a36Sopenharmony_ci /* 2 to add qgroup relations for each inherited qgroup */ 57862306a36Sopenharmony_ci num_items += 2 * inherit->num_qgroups; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci return num_items; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic noinline int create_subvol(struct mnt_idmap *idmap, 58462306a36Sopenharmony_ci struct inode *dir, struct dentry *dentry, 58562306a36Sopenharmony_ci struct btrfs_qgroup_inherit *inherit) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb); 58862306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 58962306a36Sopenharmony_ci struct btrfs_key key; 59062306a36Sopenharmony_ci struct btrfs_root_item *root_item; 59162306a36Sopenharmony_ci struct btrfs_inode_item *inode_item; 59262306a36Sopenharmony_ci struct extent_buffer *leaf; 59362306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(dir)->root; 59462306a36Sopenharmony_ci struct btrfs_root *new_root; 59562306a36Sopenharmony_ci struct btrfs_block_rsv block_rsv; 59662306a36Sopenharmony_ci struct timespec64 cur_time = current_time(dir); 59762306a36Sopenharmony_ci struct btrfs_new_inode_args new_inode_args = { 59862306a36Sopenharmony_ci .dir = dir, 59962306a36Sopenharmony_ci .dentry = dentry, 60062306a36Sopenharmony_ci .subvol = true, 60162306a36Sopenharmony_ci }; 60262306a36Sopenharmony_ci unsigned int trans_num_items; 60362306a36Sopenharmony_ci int ret; 60462306a36Sopenharmony_ci dev_t anon_dev; 60562306a36Sopenharmony_ci u64 objectid; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci root_item = kzalloc(sizeof(*root_item), GFP_KERNEL); 60862306a36Sopenharmony_ci if (!root_item) 60962306a36Sopenharmony_ci return -ENOMEM; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = btrfs_get_free_objectid(fs_info->tree_root, &objectid); 61262306a36Sopenharmony_ci if (ret) 61362306a36Sopenharmony_ci goto out_root_item; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * Don't create subvolume whose level is not zero. Or qgroup will be 61762306a36Sopenharmony_ci * screwed up since it assumes subvolume qgroup's level to be 0. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci if (btrfs_qgroup_level(objectid)) { 62062306a36Sopenharmony_ci ret = -ENOSPC; 62162306a36Sopenharmony_ci goto out_root_item; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ret = get_anon_bdev(&anon_dev); 62562306a36Sopenharmony_ci if (ret < 0) 62662306a36Sopenharmony_ci goto out_root_item; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci new_inode_args.inode = btrfs_new_subvol_inode(idmap, dir); 62962306a36Sopenharmony_ci if (!new_inode_args.inode) { 63062306a36Sopenharmony_ci ret = -ENOMEM; 63162306a36Sopenharmony_ci goto out_anon_dev; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci ret = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items); 63462306a36Sopenharmony_ci if (ret) 63562306a36Sopenharmony_ci goto out_inode; 63662306a36Sopenharmony_ci trans_num_items += create_subvol_num_items(inherit); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP); 63962306a36Sopenharmony_ci ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 64062306a36Sopenharmony_ci trans_num_items, false); 64162306a36Sopenharmony_ci if (ret) 64262306a36Sopenharmony_ci goto out_new_inode_args; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 0); 64562306a36Sopenharmony_ci if (IS_ERR(trans)) { 64662306a36Sopenharmony_ci ret = PTR_ERR(trans); 64762306a36Sopenharmony_ci btrfs_subvolume_release_metadata(root, &block_rsv); 64862306a36Sopenharmony_ci goto out_new_inode_args; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci trans->block_rsv = &block_rsv; 65162306a36Sopenharmony_ci trans->bytes_reserved = block_rsv.size; 65262306a36Sopenharmony_ci /* Tree log can't currently deal with an inode which is a new root. */ 65362306a36Sopenharmony_ci btrfs_set_log_full_commit(trans); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci ret = btrfs_qgroup_inherit(trans, 0, objectid, inherit); 65662306a36Sopenharmony_ci if (ret) 65762306a36Sopenharmony_ci goto out; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0, 66062306a36Sopenharmony_ci BTRFS_NESTING_NORMAL); 66162306a36Sopenharmony_ci if (IS_ERR(leaf)) { 66262306a36Sopenharmony_ci ret = PTR_ERR(leaf); 66362306a36Sopenharmony_ci goto out; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci btrfs_mark_buffer_dirty(trans, leaf); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci inode_item = &root_item->inode; 66962306a36Sopenharmony_ci btrfs_set_stack_inode_generation(inode_item, 1); 67062306a36Sopenharmony_ci btrfs_set_stack_inode_size(inode_item, 3); 67162306a36Sopenharmony_ci btrfs_set_stack_inode_nlink(inode_item, 1); 67262306a36Sopenharmony_ci btrfs_set_stack_inode_nbytes(inode_item, 67362306a36Sopenharmony_ci fs_info->nodesize); 67462306a36Sopenharmony_ci btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci btrfs_set_root_flags(root_item, 0); 67762306a36Sopenharmony_ci btrfs_set_root_limit(root_item, 0); 67862306a36Sopenharmony_ci btrfs_set_stack_inode_flags(inode_item, BTRFS_INODE_ROOT_ITEM_INIT); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci btrfs_set_root_bytenr(root_item, leaf->start); 68162306a36Sopenharmony_ci btrfs_set_root_generation(root_item, trans->transid); 68262306a36Sopenharmony_ci btrfs_set_root_level(root_item, 0); 68362306a36Sopenharmony_ci btrfs_set_root_refs(root_item, 1); 68462306a36Sopenharmony_ci btrfs_set_root_used(root_item, leaf->len); 68562306a36Sopenharmony_ci btrfs_set_root_last_snapshot(root_item, 0); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci btrfs_set_root_generation_v2(root_item, 68862306a36Sopenharmony_ci btrfs_root_generation(root_item)); 68962306a36Sopenharmony_ci generate_random_guid(root_item->uuid); 69062306a36Sopenharmony_ci btrfs_set_stack_timespec_sec(&root_item->otime, cur_time.tv_sec); 69162306a36Sopenharmony_ci btrfs_set_stack_timespec_nsec(&root_item->otime, cur_time.tv_nsec); 69262306a36Sopenharmony_ci root_item->ctime = root_item->otime; 69362306a36Sopenharmony_ci btrfs_set_root_ctransid(root_item, trans->transid); 69462306a36Sopenharmony_ci btrfs_set_root_otransid(root_item, trans->transid); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci btrfs_tree_unlock(leaf); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci btrfs_set_root_dirid(root_item, BTRFS_FIRST_FREE_OBJECTID); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci key.objectid = objectid; 70162306a36Sopenharmony_ci key.offset = 0; 70262306a36Sopenharmony_ci key.type = BTRFS_ROOT_ITEM_KEY; 70362306a36Sopenharmony_ci ret = btrfs_insert_root(trans, fs_info->tree_root, &key, 70462306a36Sopenharmony_ci root_item); 70562306a36Sopenharmony_ci if (ret) { 70662306a36Sopenharmony_ci /* 70762306a36Sopenharmony_ci * Since we don't abort the transaction in this case, free the 70862306a36Sopenharmony_ci * tree block so that we don't leak space and leave the 70962306a36Sopenharmony_ci * filesystem in an inconsistent state (an extent item in the 71062306a36Sopenharmony_ci * extent tree with a backreference for a root that does not 71162306a36Sopenharmony_ci * exists). 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci btrfs_tree_lock(leaf); 71462306a36Sopenharmony_ci btrfs_clear_buffer_dirty(trans, leaf); 71562306a36Sopenharmony_ci btrfs_tree_unlock(leaf); 71662306a36Sopenharmony_ci btrfs_free_tree_block(trans, objectid, leaf, 0, 1); 71762306a36Sopenharmony_ci free_extent_buffer(leaf); 71862306a36Sopenharmony_ci goto out; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci free_extent_buffer(leaf); 72262306a36Sopenharmony_ci leaf = NULL; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci new_root = btrfs_get_new_fs_root(fs_info, objectid, &anon_dev); 72562306a36Sopenharmony_ci if (IS_ERR(new_root)) { 72662306a36Sopenharmony_ci ret = PTR_ERR(new_root); 72762306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 72862306a36Sopenharmony_ci goto out; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci /* anon_dev is owned by new_root now. */ 73162306a36Sopenharmony_ci anon_dev = 0; 73262306a36Sopenharmony_ci BTRFS_I(new_inode_args.inode)->root = new_root; 73362306a36Sopenharmony_ci /* ... and new_root is owned by new_inode_args.inode now. */ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci ret = btrfs_record_root_in_trans(trans, new_root); 73662306a36Sopenharmony_ci if (ret) { 73762306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 73862306a36Sopenharmony_ci goto out; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret = btrfs_uuid_tree_add(trans, root_item->uuid, 74262306a36Sopenharmony_ci BTRFS_UUID_KEY_SUBVOL, objectid); 74362306a36Sopenharmony_ci if (ret) { 74462306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 74562306a36Sopenharmony_ci goto out; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = btrfs_create_new_inode(trans, &new_inode_args); 74962306a36Sopenharmony_ci if (ret) { 75062306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 75162306a36Sopenharmony_ci goto out; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci d_instantiate_new(dentry, new_inode_args.inode); 75562306a36Sopenharmony_ci new_inode_args.inode = NULL; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ciout: 75862306a36Sopenharmony_ci trans->block_rsv = NULL; 75962306a36Sopenharmony_ci trans->bytes_reserved = 0; 76062306a36Sopenharmony_ci btrfs_subvolume_release_metadata(root, &block_rsv); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci btrfs_end_transaction(trans); 76362306a36Sopenharmony_ciout_new_inode_args: 76462306a36Sopenharmony_ci btrfs_new_inode_args_destroy(&new_inode_args); 76562306a36Sopenharmony_ciout_inode: 76662306a36Sopenharmony_ci iput(new_inode_args.inode); 76762306a36Sopenharmony_ciout_anon_dev: 76862306a36Sopenharmony_ci if (anon_dev) 76962306a36Sopenharmony_ci free_anon_bdev(anon_dev); 77062306a36Sopenharmony_ciout_root_item: 77162306a36Sopenharmony_ci kfree(root_item); 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int create_snapshot(struct btrfs_root *root, struct inode *dir, 77662306a36Sopenharmony_ci struct dentry *dentry, bool readonly, 77762306a36Sopenharmony_ci struct btrfs_qgroup_inherit *inherit) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb); 78062306a36Sopenharmony_ci struct inode *inode; 78162306a36Sopenharmony_ci struct btrfs_pending_snapshot *pending_snapshot; 78262306a36Sopenharmony_ci unsigned int trans_num_items; 78362306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 78462306a36Sopenharmony_ci int ret; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* We do not support snapshotting right now. */ 78762306a36Sopenharmony_ci if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) { 78862306a36Sopenharmony_ci btrfs_warn(fs_info, 78962306a36Sopenharmony_ci "extent tree v2 doesn't support snapshotting yet"); 79062306a36Sopenharmony_ci return -EOPNOTSUPP; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (btrfs_root_refs(&root->root_item) == 0) 79462306a36Sopenharmony_ci return -ENOENT; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (!test_bit(BTRFS_ROOT_SHAREABLE, &root->state)) 79762306a36Sopenharmony_ci return -EINVAL; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (atomic_read(&root->nr_swapfiles)) { 80062306a36Sopenharmony_ci btrfs_warn(fs_info, 80162306a36Sopenharmony_ci "cannot snapshot subvolume with active swapfile"); 80262306a36Sopenharmony_ci return -ETXTBSY; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_KERNEL); 80662306a36Sopenharmony_ci if (!pending_snapshot) 80762306a36Sopenharmony_ci return -ENOMEM; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci ret = get_anon_bdev(&pending_snapshot->anon_dev); 81062306a36Sopenharmony_ci if (ret < 0) 81162306a36Sopenharmony_ci goto free_pending; 81262306a36Sopenharmony_ci pending_snapshot->root_item = kzalloc(sizeof(struct btrfs_root_item), 81362306a36Sopenharmony_ci GFP_KERNEL); 81462306a36Sopenharmony_ci pending_snapshot->path = btrfs_alloc_path(); 81562306a36Sopenharmony_ci if (!pending_snapshot->root_item || !pending_snapshot->path) { 81662306a36Sopenharmony_ci ret = -ENOMEM; 81762306a36Sopenharmony_ci goto free_pending; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci btrfs_init_block_rsv(&pending_snapshot->block_rsv, 82162306a36Sopenharmony_ci BTRFS_BLOCK_RSV_TEMP); 82262306a36Sopenharmony_ci /* 82362306a36Sopenharmony_ci * 1 to add dir item 82462306a36Sopenharmony_ci * 1 to add dir index 82562306a36Sopenharmony_ci * 1 to update parent inode item 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci trans_num_items = create_subvol_num_items(inherit) + 3; 82862306a36Sopenharmony_ci ret = btrfs_subvolume_reserve_metadata(BTRFS_I(dir)->root, 82962306a36Sopenharmony_ci &pending_snapshot->block_rsv, 83062306a36Sopenharmony_ci trans_num_items, false); 83162306a36Sopenharmony_ci if (ret) 83262306a36Sopenharmony_ci goto free_pending; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci pending_snapshot->dentry = dentry; 83562306a36Sopenharmony_ci pending_snapshot->root = root; 83662306a36Sopenharmony_ci pending_snapshot->readonly = readonly; 83762306a36Sopenharmony_ci pending_snapshot->dir = dir; 83862306a36Sopenharmony_ci pending_snapshot->inherit = inherit; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 0); 84162306a36Sopenharmony_ci if (IS_ERR(trans)) { 84262306a36Sopenharmony_ci ret = PTR_ERR(trans); 84362306a36Sopenharmony_ci goto fail; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci trans->pending_snapshot = pending_snapshot; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci ret = btrfs_commit_transaction(trans); 84962306a36Sopenharmony_ci if (ret) 85062306a36Sopenharmony_ci goto fail; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ret = pending_snapshot->error; 85362306a36Sopenharmony_ci if (ret) 85462306a36Sopenharmony_ci goto fail; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = btrfs_orphan_cleanup(pending_snapshot->snap); 85762306a36Sopenharmony_ci if (ret) 85862306a36Sopenharmony_ci goto fail; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci inode = btrfs_lookup_dentry(d_inode(dentry->d_parent), dentry); 86162306a36Sopenharmony_ci if (IS_ERR(inode)) { 86262306a36Sopenharmony_ci ret = PTR_ERR(inode); 86362306a36Sopenharmony_ci goto fail; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci d_instantiate(dentry, inode); 86762306a36Sopenharmony_ci ret = 0; 86862306a36Sopenharmony_ci pending_snapshot->anon_dev = 0; 86962306a36Sopenharmony_cifail: 87062306a36Sopenharmony_ci /* Prevent double freeing of anon_dev */ 87162306a36Sopenharmony_ci if (ret && pending_snapshot->snap) 87262306a36Sopenharmony_ci pending_snapshot->snap->anon_dev = 0; 87362306a36Sopenharmony_ci btrfs_put_root(pending_snapshot->snap); 87462306a36Sopenharmony_ci btrfs_subvolume_release_metadata(root, &pending_snapshot->block_rsv); 87562306a36Sopenharmony_cifree_pending: 87662306a36Sopenharmony_ci if (pending_snapshot->anon_dev) 87762306a36Sopenharmony_ci free_anon_bdev(pending_snapshot->anon_dev); 87862306a36Sopenharmony_ci kfree(pending_snapshot->root_item); 87962306a36Sopenharmony_ci btrfs_free_path(pending_snapshot->path); 88062306a36Sopenharmony_ci kfree(pending_snapshot); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return ret; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/* copy of may_delete in fs/namei.c() 88662306a36Sopenharmony_ci * Check whether we can remove a link victim from directory dir, check 88762306a36Sopenharmony_ci * whether the type of victim is right. 88862306a36Sopenharmony_ci * 1. We can't do it if dir is read-only (done in permission()) 88962306a36Sopenharmony_ci * 2. We should have write and exec permissions on dir 89062306a36Sopenharmony_ci * 3. We can't remove anything from append-only dir 89162306a36Sopenharmony_ci * 4. We can't do anything with immutable dir (done in permission()) 89262306a36Sopenharmony_ci * 5. If the sticky bit on dir is set we should either 89362306a36Sopenharmony_ci * a. be owner of dir, or 89462306a36Sopenharmony_ci * b. be owner of victim, or 89562306a36Sopenharmony_ci * c. have CAP_FOWNER capability 89662306a36Sopenharmony_ci * 6. If the victim is append-only or immutable we can't do anything with 89762306a36Sopenharmony_ci * links pointing to it. 89862306a36Sopenharmony_ci * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR. 89962306a36Sopenharmony_ci * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR. 90062306a36Sopenharmony_ci * 9. We can't remove a root or mountpoint. 90162306a36Sopenharmony_ci * 10. We don't allow removal of NFS sillyrenamed files; it's handled by 90262306a36Sopenharmony_ci * nfs_async_unlink(). 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int btrfs_may_delete(struct mnt_idmap *idmap, 90662306a36Sopenharmony_ci struct inode *dir, struct dentry *victim, int isdir) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci int error; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (d_really_is_negative(victim)) 91162306a36Sopenharmony_ci return -ENOENT; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci BUG_ON(d_inode(victim->d_parent) != dir); 91462306a36Sopenharmony_ci audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci error = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); 91762306a36Sopenharmony_ci if (error) 91862306a36Sopenharmony_ci return error; 91962306a36Sopenharmony_ci if (IS_APPEND(dir)) 92062306a36Sopenharmony_ci return -EPERM; 92162306a36Sopenharmony_ci if (check_sticky(idmap, dir, d_inode(victim)) || 92262306a36Sopenharmony_ci IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) || 92362306a36Sopenharmony_ci IS_SWAPFILE(d_inode(victim))) 92462306a36Sopenharmony_ci return -EPERM; 92562306a36Sopenharmony_ci if (isdir) { 92662306a36Sopenharmony_ci if (!d_is_dir(victim)) 92762306a36Sopenharmony_ci return -ENOTDIR; 92862306a36Sopenharmony_ci if (IS_ROOT(victim)) 92962306a36Sopenharmony_ci return -EBUSY; 93062306a36Sopenharmony_ci } else if (d_is_dir(victim)) 93162306a36Sopenharmony_ci return -EISDIR; 93262306a36Sopenharmony_ci if (IS_DEADDIR(dir)) 93362306a36Sopenharmony_ci return -ENOENT; 93462306a36Sopenharmony_ci if (victim->d_flags & DCACHE_NFSFS_RENAMED) 93562306a36Sopenharmony_ci return -EBUSY; 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/* copy of may_create in fs/namei.c() */ 94062306a36Sopenharmony_cistatic inline int btrfs_may_create(struct mnt_idmap *idmap, 94162306a36Sopenharmony_ci struct inode *dir, struct dentry *child) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci if (d_really_is_positive(child)) 94462306a36Sopenharmony_ci return -EEXIST; 94562306a36Sopenharmony_ci if (IS_DEADDIR(dir)) 94662306a36Sopenharmony_ci return -ENOENT; 94762306a36Sopenharmony_ci if (!fsuidgid_has_mapping(dir->i_sb, idmap)) 94862306a36Sopenharmony_ci return -EOVERFLOW; 94962306a36Sopenharmony_ci return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci/* 95362306a36Sopenharmony_ci * Create a new subvolume below @parent. This is largely modeled after 95462306a36Sopenharmony_ci * sys_mkdirat and vfs_mkdir, but we only do a single component lookup 95562306a36Sopenharmony_ci * inside this filesystem so it's quite a bit simpler. 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_cistatic noinline int btrfs_mksubvol(const struct path *parent, 95862306a36Sopenharmony_ci struct mnt_idmap *idmap, 95962306a36Sopenharmony_ci const char *name, int namelen, 96062306a36Sopenharmony_ci struct btrfs_root *snap_src, 96162306a36Sopenharmony_ci bool readonly, 96262306a36Sopenharmony_ci struct btrfs_qgroup_inherit *inherit) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct inode *dir = d_inode(parent->dentry); 96562306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb); 96662306a36Sopenharmony_ci struct dentry *dentry; 96762306a36Sopenharmony_ci struct fscrypt_str name_str = FSTR_INIT((char *)name, namelen); 96862306a36Sopenharmony_ci int error; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci error = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); 97162306a36Sopenharmony_ci if (error == -EINTR) 97262306a36Sopenharmony_ci return error; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci dentry = lookup_one(idmap, name, parent->dentry, namelen); 97562306a36Sopenharmony_ci error = PTR_ERR(dentry); 97662306a36Sopenharmony_ci if (IS_ERR(dentry)) 97762306a36Sopenharmony_ci goto out_unlock; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci error = btrfs_may_create(idmap, dir, dentry); 98062306a36Sopenharmony_ci if (error) 98162306a36Sopenharmony_ci goto out_dput; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* 98462306a36Sopenharmony_ci * even if this name doesn't exist, we may get hash collisions. 98562306a36Sopenharmony_ci * check for them now when we can safely fail 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_ci error = btrfs_check_dir_item_collision(BTRFS_I(dir)->root, 98862306a36Sopenharmony_ci dir->i_ino, &name_str); 98962306a36Sopenharmony_ci if (error) 99062306a36Sopenharmony_ci goto out_dput; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci down_read(&fs_info->subvol_sem); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0) 99562306a36Sopenharmony_ci goto out_up_read; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (snap_src) 99862306a36Sopenharmony_ci error = create_snapshot(snap_src, dir, dentry, readonly, inherit); 99962306a36Sopenharmony_ci else 100062306a36Sopenharmony_ci error = create_subvol(idmap, dir, dentry, inherit); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (!error) 100362306a36Sopenharmony_ci fsnotify_mkdir(dir, dentry); 100462306a36Sopenharmony_ciout_up_read: 100562306a36Sopenharmony_ci up_read(&fs_info->subvol_sem); 100662306a36Sopenharmony_ciout_dput: 100762306a36Sopenharmony_ci dput(dentry); 100862306a36Sopenharmony_ciout_unlock: 100962306a36Sopenharmony_ci btrfs_inode_unlock(BTRFS_I(dir), 0); 101062306a36Sopenharmony_ci return error; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic noinline int btrfs_mksnapshot(const struct path *parent, 101462306a36Sopenharmony_ci struct mnt_idmap *idmap, 101562306a36Sopenharmony_ci const char *name, int namelen, 101662306a36Sopenharmony_ci struct btrfs_root *root, 101762306a36Sopenharmony_ci bool readonly, 101862306a36Sopenharmony_ci struct btrfs_qgroup_inherit *inherit) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci int ret; 102162306a36Sopenharmony_ci bool snapshot_force_cow = false; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* 102462306a36Sopenharmony_ci * Force new buffered writes to reserve space even when NOCOW is 102562306a36Sopenharmony_ci * possible. This is to avoid later writeback (running dealloc) to 102662306a36Sopenharmony_ci * fallback to COW mode and unexpectedly fail with ENOSPC. 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_ci btrfs_drew_read_lock(&root->snapshot_lock); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci ret = btrfs_start_delalloc_snapshot(root, false); 103162306a36Sopenharmony_ci if (ret) 103262306a36Sopenharmony_ci goto out; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * All previous writes have started writeback in NOCOW mode, so now 103662306a36Sopenharmony_ci * we force future writes to fallback to COW mode during snapshot 103762306a36Sopenharmony_ci * creation. 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci atomic_inc(&root->snapshot_force_cow); 104062306a36Sopenharmony_ci snapshot_force_cow = true; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci ret = btrfs_mksubvol(parent, idmap, name, namelen, 104562306a36Sopenharmony_ci root, readonly, inherit); 104662306a36Sopenharmony_ciout: 104762306a36Sopenharmony_ci if (snapshot_force_cow) 104862306a36Sopenharmony_ci atomic_dec(&root->snapshot_force_cow); 104962306a36Sopenharmony_ci btrfs_drew_read_unlock(&root->snapshot_lock); 105062306a36Sopenharmony_ci return ret; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/* 105462306a36Sopenharmony_ci * Try to start exclusive operation @type or cancel it if it's running. 105562306a36Sopenharmony_ci * 105662306a36Sopenharmony_ci * Return: 105762306a36Sopenharmony_ci * 0 - normal mode, newly claimed op started 105862306a36Sopenharmony_ci * >0 - normal mode, something else is running, 105962306a36Sopenharmony_ci * return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS to user space 106062306a36Sopenharmony_ci * ECANCELED - cancel mode, successful cancel 106162306a36Sopenharmony_ci * ENOTCONN - cancel mode, operation not running anymore 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_cistatic int exclop_start_or_cancel_reloc(struct btrfs_fs_info *fs_info, 106462306a36Sopenharmony_ci enum btrfs_exclusive_operation type, bool cancel) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci if (!cancel) { 106762306a36Sopenharmony_ci /* Start normal op */ 106862306a36Sopenharmony_ci if (!btrfs_exclop_start(fs_info, type)) 106962306a36Sopenharmony_ci return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; 107062306a36Sopenharmony_ci /* Exclusive operation is now claimed */ 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* Cancel running op */ 107562306a36Sopenharmony_ci if (btrfs_exclop_start_try_lock(fs_info, type)) { 107662306a36Sopenharmony_ci /* 107762306a36Sopenharmony_ci * This blocks any exclop finish from setting it to NONE, so we 107862306a36Sopenharmony_ci * request cancellation. Either it runs and we will wait for it, 107962306a36Sopenharmony_ci * or it has finished and no waiting will happen. 108062306a36Sopenharmony_ci */ 108162306a36Sopenharmony_ci atomic_inc(&fs_info->reloc_cancel_req); 108262306a36Sopenharmony_ci btrfs_exclop_start_unlock(fs_info); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)) 108562306a36Sopenharmony_ci wait_on_bit(&fs_info->flags, BTRFS_FS_RELOC_RUNNING, 108662306a36Sopenharmony_ci TASK_INTERRUPTIBLE); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return -ECANCELED; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci /* Something else is running or none */ 109262306a36Sopenharmony_ci return -ENOTCONN; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic noinline int btrfs_ioctl_resize(struct file *file, 109662306a36Sopenharmony_ci void __user *arg) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci BTRFS_DEV_LOOKUP_ARGS(args); 109962306a36Sopenharmony_ci struct inode *inode = file_inode(file); 110062306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 110162306a36Sopenharmony_ci u64 new_size; 110262306a36Sopenharmony_ci u64 old_size; 110362306a36Sopenharmony_ci u64 devid = 1; 110462306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 110562306a36Sopenharmony_ci struct btrfs_ioctl_vol_args *vol_args; 110662306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 110762306a36Sopenharmony_ci struct btrfs_device *device = NULL; 110862306a36Sopenharmony_ci char *sizestr; 110962306a36Sopenharmony_ci char *retptr; 111062306a36Sopenharmony_ci char *devstr = NULL; 111162306a36Sopenharmony_ci int ret = 0; 111262306a36Sopenharmony_ci int mod = 0; 111362306a36Sopenharmony_ci bool cancel; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 111662306a36Sopenharmony_ci return -EPERM; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci ret = mnt_want_write_file(file); 111962306a36Sopenharmony_ci if (ret) 112062306a36Sopenharmony_ci return ret; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* 112362306a36Sopenharmony_ci * Read the arguments before checking exclusivity to be able to 112462306a36Sopenharmony_ci * distinguish regular resize and cancel 112562306a36Sopenharmony_ci */ 112662306a36Sopenharmony_ci vol_args = memdup_user(arg, sizeof(*vol_args)); 112762306a36Sopenharmony_ci if (IS_ERR(vol_args)) { 112862306a36Sopenharmony_ci ret = PTR_ERR(vol_args); 112962306a36Sopenharmony_ci goto out_drop; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; 113262306a36Sopenharmony_ci sizestr = vol_args->name; 113362306a36Sopenharmony_ci cancel = (strcmp("cancel", sizestr) == 0); 113462306a36Sopenharmony_ci ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_RESIZE, cancel); 113562306a36Sopenharmony_ci if (ret) 113662306a36Sopenharmony_ci goto out_free; 113762306a36Sopenharmony_ci /* Exclusive operation is now claimed */ 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci devstr = strchr(sizestr, ':'); 114062306a36Sopenharmony_ci if (devstr) { 114162306a36Sopenharmony_ci sizestr = devstr + 1; 114262306a36Sopenharmony_ci *devstr = '\0'; 114362306a36Sopenharmony_ci devstr = vol_args->name; 114462306a36Sopenharmony_ci ret = kstrtoull(devstr, 10, &devid); 114562306a36Sopenharmony_ci if (ret) 114662306a36Sopenharmony_ci goto out_finish; 114762306a36Sopenharmony_ci if (!devid) { 114862306a36Sopenharmony_ci ret = -EINVAL; 114962306a36Sopenharmony_ci goto out_finish; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci btrfs_info(fs_info, "resizing devid %llu", devid); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci args.devid = devid; 115562306a36Sopenharmony_ci device = btrfs_find_device(fs_info->fs_devices, &args); 115662306a36Sopenharmony_ci if (!device) { 115762306a36Sopenharmony_ci btrfs_info(fs_info, "resizer unable to find device %llu", 115862306a36Sopenharmony_ci devid); 115962306a36Sopenharmony_ci ret = -ENODEV; 116062306a36Sopenharmony_ci goto out_finish; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { 116462306a36Sopenharmony_ci btrfs_info(fs_info, 116562306a36Sopenharmony_ci "resizer unable to apply on readonly device %llu", 116662306a36Sopenharmony_ci devid); 116762306a36Sopenharmony_ci ret = -EPERM; 116862306a36Sopenharmony_ci goto out_finish; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (!strcmp(sizestr, "max")) 117262306a36Sopenharmony_ci new_size = bdev_nr_bytes(device->bdev); 117362306a36Sopenharmony_ci else { 117462306a36Sopenharmony_ci if (sizestr[0] == '-') { 117562306a36Sopenharmony_ci mod = -1; 117662306a36Sopenharmony_ci sizestr++; 117762306a36Sopenharmony_ci } else if (sizestr[0] == '+') { 117862306a36Sopenharmony_ci mod = 1; 117962306a36Sopenharmony_ci sizestr++; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci new_size = memparse(sizestr, &retptr); 118262306a36Sopenharmony_ci if (*retptr != '\0' || new_size == 0) { 118362306a36Sopenharmony_ci ret = -EINVAL; 118462306a36Sopenharmony_ci goto out_finish; 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) { 118962306a36Sopenharmony_ci ret = -EPERM; 119062306a36Sopenharmony_ci goto out_finish; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci old_size = btrfs_device_get_total_bytes(device); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (mod < 0) { 119662306a36Sopenharmony_ci if (new_size > old_size) { 119762306a36Sopenharmony_ci ret = -EINVAL; 119862306a36Sopenharmony_ci goto out_finish; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci new_size = old_size - new_size; 120162306a36Sopenharmony_ci } else if (mod > 0) { 120262306a36Sopenharmony_ci if (new_size > ULLONG_MAX - old_size) { 120362306a36Sopenharmony_ci ret = -ERANGE; 120462306a36Sopenharmony_ci goto out_finish; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci new_size = old_size + new_size; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (new_size < SZ_256M) { 121062306a36Sopenharmony_ci ret = -EINVAL; 121162306a36Sopenharmony_ci goto out_finish; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci if (new_size > bdev_nr_bytes(device->bdev)) { 121462306a36Sopenharmony_ci ret = -EFBIG; 121562306a36Sopenharmony_ci goto out_finish; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci new_size = round_down(new_size, fs_info->sectorsize); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (new_size > old_size) { 122162306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 0); 122262306a36Sopenharmony_ci if (IS_ERR(trans)) { 122362306a36Sopenharmony_ci ret = PTR_ERR(trans); 122462306a36Sopenharmony_ci goto out_finish; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci ret = btrfs_grow_device(trans, device, new_size); 122762306a36Sopenharmony_ci btrfs_commit_transaction(trans); 122862306a36Sopenharmony_ci } else if (new_size < old_size) { 122962306a36Sopenharmony_ci ret = btrfs_shrink_device(device, new_size); 123062306a36Sopenharmony_ci } /* equal, nothing need to do */ 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (ret == 0 && new_size != old_size) 123362306a36Sopenharmony_ci btrfs_info_in_rcu(fs_info, 123462306a36Sopenharmony_ci "resize device %s (devid %llu) from %llu to %llu", 123562306a36Sopenharmony_ci btrfs_dev_name(device), device->devid, 123662306a36Sopenharmony_ci old_size, new_size); 123762306a36Sopenharmony_ciout_finish: 123862306a36Sopenharmony_ci btrfs_exclop_finish(fs_info); 123962306a36Sopenharmony_ciout_free: 124062306a36Sopenharmony_ci kfree(vol_args); 124162306a36Sopenharmony_ciout_drop: 124262306a36Sopenharmony_ci mnt_drop_write_file(file); 124362306a36Sopenharmony_ci return ret; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic noinline int __btrfs_ioctl_snap_create(struct file *file, 124762306a36Sopenharmony_ci struct mnt_idmap *idmap, 124862306a36Sopenharmony_ci const char *name, unsigned long fd, int subvol, 124962306a36Sopenharmony_ci bool readonly, 125062306a36Sopenharmony_ci struct btrfs_qgroup_inherit *inherit) 125162306a36Sopenharmony_ci{ 125262306a36Sopenharmony_ci int namelen; 125362306a36Sopenharmony_ci int ret = 0; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (!S_ISDIR(file_inode(file)->i_mode)) 125662306a36Sopenharmony_ci return -ENOTDIR; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci ret = mnt_want_write_file(file); 125962306a36Sopenharmony_ci if (ret) 126062306a36Sopenharmony_ci goto out; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci namelen = strlen(name); 126362306a36Sopenharmony_ci if (strchr(name, '/')) { 126462306a36Sopenharmony_ci ret = -EINVAL; 126562306a36Sopenharmony_ci goto out_drop_write; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (name[0] == '.' && 126962306a36Sopenharmony_ci (namelen == 1 || (name[1] == '.' && namelen == 2))) { 127062306a36Sopenharmony_ci ret = -EEXIST; 127162306a36Sopenharmony_ci goto out_drop_write; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (subvol) { 127562306a36Sopenharmony_ci ret = btrfs_mksubvol(&file->f_path, idmap, name, 127662306a36Sopenharmony_ci namelen, NULL, readonly, inherit); 127762306a36Sopenharmony_ci } else { 127862306a36Sopenharmony_ci struct fd src = fdget(fd); 127962306a36Sopenharmony_ci struct inode *src_inode; 128062306a36Sopenharmony_ci if (!src.file) { 128162306a36Sopenharmony_ci ret = -EINVAL; 128262306a36Sopenharmony_ci goto out_drop_write; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci src_inode = file_inode(src.file); 128662306a36Sopenharmony_ci if (src_inode->i_sb != file_inode(file)->i_sb) { 128762306a36Sopenharmony_ci btrfs_info(BTRFS_I(file_inode(file))->root->fs_info, 128862306a36Sopenharmony_ci "Snapshot src from another FS"); 128962306a36Sopenharmony_ci ret = -EXDEV; 129062306a36Sopenharmony_ci } else if (!inode_owner_or_capable(idmap, src_inode)) { 129162306a36Sopenharmony_ci /* 129262306a36Sopenharmony_ci * Subvolume creation is not restricted, but snapshots 129362306a36Sopenharmony_ci * are limited to own subvolumes only 129462306a36Sopenharmony_ci */ 129562306a36Sopenharmony_ci ret = -EPERM; 129662306a36Sopenharmony_ci } else if (btrfs_ino(BTRFS_I(src_inode)) != BTRFS_FIRST_FREE_OBJECTID) { 129762306a36Sopenharmony_ci /* 129862306a36Sopenharmony_ci * Snapshots must be made with the src_inode referring 129962306a36Sopenharmony_ci * to the subvolume inode, otherwise the permission 130062306a36Sopenharmony_ci * checking above is useless because we may have 130162306a36Sopenharmony_ci * permission on a lower directory but not the subvol 130262306a36Sopenharmony_ci * itself. 130362306a36Sopenharmony_ci */ 130462306a36Sopenharmony_ci ret = -EINVAL; 130562306a36Sopenharmony_ci } else { 130662306a36Sopenharmony_ci ret = btrfs_mksnapshot(&file->f_path, idmap, 130762306a36Sopenharmony_ci name, namelen, 130862306a36Sopenharmony_ci BTRFS_I(src_inode)->root, 130962306a36Sopenharmony_ci readonly, inherit); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci fdput(src); 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ciout_drop_write: 131462306a36Sopenharmony_ci mnt_drop_write_file(file); 131562306a36Sopenharmony_ciout: 131662306a36Sopenharmony_ci return ret; 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic noinline int btrfs_ioctl_snap_create(struct file *file, 132062306a36Sopenharmony_ci void __user *arg, int subvol) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci struct btrfs_ioctl_vol_args *vol_args; 132362306a36Sopenharmony_ci int ret; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (!S_ISDIR(file_inode(file)->i_mode)) 132662306a36Sopenharmony_ci return -ENOTDIR; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci vol_args = memdup_user(arg, sizeof(*vol_args)); 132962306a36Sopenharmony_ci if (IS_ERR(vol_args)) 133062306a36Sopenharmony_ci return PTR_ERR(vol_args); 133162306a36Sopenharmony_ci vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci ret = __btrfs_ioctl_snap_create(file, file_mnt_idmap(file), 133462306a36Sopenharmony_ci vol_args->name, vol_args->fd, subvol, 133562306a36Sopenharmony_ci false, NULL); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci kfree(vol_args); 133862306a36Sopenharmony_ci return ret; 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic noinline int btrfs_ioctl_snap_create_v2(struct file *file, 134262306a36Sopenharmony_ci void __user *arg, int subvol) 134362306a36Sopenharmony_ci{ 134462306a36Sopenharmony_ci struct btrfs_ioctl_vol_args_v2 *vol_args; 134562306a36Sopenharmony_ci int ret; 134662306a36Sopenharmony_ci bool readonly = false; 134762306a36Sopenharmony_ci struct btrfs_qgroup_inherit *inherit = NULL; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (!S_ISDIR(file_inode(file)->i_mode)) 135062306a36Sopenharmony_ci return -ENOTDIR; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci vol_args = memdup_user(arg, sizeof(*vol_args)); 135362306a36Sopenharmony_ci if (IS_ERR(vol_args)) 135462306a36Sopenharmony_ci return PTR_ERR(vol_args); 135562306a36Sopenharmony_ci vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0'; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci if (vol_args->flags & ~BTRFS_SUBVOL_CREATE_ARGS_MASK) { 135862306a36Sopenharmony_ci ret = -EOPNOTSUPP; 135962306a36Sopenharmony_ci goto free_args; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (vol_args->flags & BTRFS_SUBVOL_RDONLY) 136362306a36Sopenharmony_ci readonly = true; 136462306a36Sopenharmony_ci if (vol_args->flags & BTRFS_SUBVOL_QGROUP_INHERIT) { 136562306a36Sopenharmony_ci u64 nums; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci if (vol_args->size < sizeof(*inherit) || 136862306a36Sopenharmony_ci vol_args->size > PAGE_SIZE) { 136962306a36Sopenharmony_ci ret = -EINVAL; 137062306a36Sopenharmony_ci goto free_args; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci inherit = memdup_user(vol_args->qgroup_inherit, vol_args->size); 137362306a36Sopenharmony_ci if (IS_ERR(inherit)) { 137462306a36Sopenharmony_ci ret = PTR_ERR(inherit); 137562306a36Sopenharmony_ci goto free_args; 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (inherit->num_qgroups > PAGE_SIZE || 137962306a36Sopenharmony_ci inherit->num_ref_copies > PAGE_SIZE || 138062306a36Sopenharmony_ci inherit->num_excl_copies > PAGE_SIZE) { 138162306a36Sopenharmony_ci ret = -EINVAL; 138262306a36Sopenharmony_ci goto free_inherit; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci nums = inherit->num_qgroups + 2 * inherit->num_ref_copies + 138662306a36Sopenharmony_ci 2 * inherit->num_excl_copies; 138762306a36Sopenharmony_ci if (vol_args->size != struct_size(inherit, qgroups, nums)) { 138862306a36Sopenharmony_ci ret = -EINVAL; 138962306a36Sopenharmony_ci goto free_inherit; 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci ret = __btrfs_ioctl_snap_create(file, file_mnt_idmap(file), 139462306a36Sopenharmony_ci vol_args->name, vol_args->fd, subvol, 139562306a36Sopenharmony_ci readonly, inherit); 139662306a36Sopenharmony_ci if (ret) 139762306a36Sopenharmony_ci goto free_inherit; 139862306a36Sopenharmony_cifree_inherit: 139962306a36Sopenharmony_ci kfree(inherit); 140062306a36Sopenharmony_cifree_args: 140162306a36Sopenharmony_ci kfree(vol_args); 140262306a36Sopenharmony_ci return ret; 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_cistatic noinline int btrfs_ioctl_subvol_getflags(struct inode *inode, 140662306a36Sopenharmony_ci void __user *arg) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 140962306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 141062306a36Sopenharmony_ci int ret = 0; 141162306a36Sopenharmony_ci u64 flags = 0; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) 141462306a36Sopenharmony_ci return -EINVAL; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci down_read(&fs_info->subvol_sem); 141762306a36Sopenharmony_ci if (btrfs_root_readonly(root)) 141862306a36Sopenharmony_ci flags |= BTRFS_SUBVOL_RDONLY; 141962306a36Sopenharmony_ci up_read(&fs_info->subvol_sem); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci if (copy_to_user(arg, &flags, sizeof(flags))) 142262306a36Sopenharmony_ci ret = -EFAULT; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci return ret; 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistatic noinline int btrfs_ioctl_subvol_setflags(struct file *file, 142862306a36Sopenharmony_ci void __user *arg) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 143162306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 143262306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 143362306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 143462306a36Sopenharmony_ci u64 root_flags; 143562306a36Sopenharmony_ci u64 flags; 143662306a36Sopenharmony_ci int ret = 0; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (!inode_owner_or_capable(file_mnt_idmap(file), inode)) 143962306a36Sopenharmony_ci return -EPERM; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci ret = mnt_want_write_file(file); 144262306a36Sopenharmony_ci if (ret) 144362306a36Sopenharmony_ci goto out; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) { 144662306a36Sopenharmony_ci ret = -EINVAL; 144762306a36Sopenharmony_ci goto out_drop_write; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (copy_from_user(&flags, arg, sizeof(flags))) { 145162306a36Sopenharmony_ci ret = -EFAULT; 145262306a36Sopenharmony_ci goto out_drop_write; 145362306a36Sopenharmony_ci } 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (flags & ~BTRFS_SUBVOL_RDONLY) { 145662306a36Sopenharmony_ci ret = -EOPNOTSUPP; 145762306a36Sopenharmony_ci goto out_drop_write; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci down_write(&fs_info->subvol_sem); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci /* nothing to do */ 146362306a36Sopenharmony_ci if (!!(flags & BTRFS_SUBVOL_RDONLY) == btrfs_root_readonly(root)) 146462306a36Sopenharmony_ci goto out_drop_sem; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci root_flags = btrfs_root_flags(&root->root_item); 146762306a36Sopenharmony_ci if (flags & BTRFS_SUBVOL_RDONLY) { 146862306a36Sopenharmony_ci btrfs_set_root_flags(&root->root_item, 146962306a36Sopenharmony_ci root_flags | BTRFS_ROOT_SUBVOL_RDONLY); 147062306a36Sopenharmony_ci } else { 147162306a36Sopenharmony_ci /* 147262306a36Sopenharmony_ci * Block RO -> RW transition if this subvolume is involved in 147362306a36Sopenharmony_ci * send 147462306a36Sopenharmony_ci */ 147562306a36Sopenharmony_ci spin_lock(&root->root_item_lock); 147662306a36Sopenharmony_ci if (root->send_in_progress == 0) { 147762306a36Sopenharmony_ci btrfs_set_root_flags(&root->root_item, 147862306a36Sopenharmony_ci root_flags & ~BTRFS_ROOT_SUBVOL_RDONLY); 147962306a36Sopenharmony_ci spin_unlock(&root->root_item_lock); 148062306a36Sopenharmony_ci } else { 148162306a36Sopenharmony_ci spin_unlock(&root->root_item_lock); 148262306a36Sopenharmony_ci btrfs_warn(fs_info, 148362306a36Sopenharmony_ci "Attempt to set subvolume %llu read-write during send", 148462306a36Sopenharmony_ci root->root_key.objectid); 148562306a36Sopenharmony_ci ret = -EPERM; 148662306a36Sopenharmony_ci goto out_drop_sem; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 1); 149162306a36Sopenharmony_ci if (IS_ERR(trans)) { 149262306a36Sopenharmony_ci ret = PTR_ERR(trans); 149362306a36Sopenharmony_ci goto out_reset; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci ret = btrfs_update_root(trans, fs_info->tree_root, 149762306a36Sopenharmony_ci &root->root_key, &root->root_item); 149862306a36Sopenharmony_ci if (ret < 0) { 149962306a36Sopenharmony_ci btrfs_end_transaction(trans); 150062306a36Sopenharmony_ci goto out_reset; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci ret = btrfs_commit_transaction(trans); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ciout_reset: 150662306a36Sopenharmony_ci if (ret) 150762306a36Sopenharmony_ci btrfs_set_root_flags(&root->root_item, root_flags); 150862306a36Sopenharmony_ciout_drop_sem: 150962306a36Sopenharmony_ci up_write(&fs_info->subvol_sem); 151062306a36Sopenharmony_ciout_drop_write: 151162306a36Sopenharmony_ci mnt_drop_write_file(file); 151262306a36Sopenharmony_ciout: 151362306a36Sopenharmony_ci return ret; 151462306a36Sopenharmony_ci} 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_cistatic noinline int key_in_sk(struct btrfs_key *key, 151762306a36Sopenharmony_ci struct btrfs_ioctl_search_key *sk) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci struct btrfs_key test; 152062306a36Sopenharmony_ci int ret; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci test.objectid = sk->min_objectid; 152362306a36Sopenharmony_ci test.type = sk->min_type; 152462306a36Sopenharmony_ci test.offset = sk->min_offset; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci ret = btrfs_comp_cpu_keys(key, &test); 152762306a36Sopenharmony_ci if (ret < 0) 152862306a36Sopenharmony_ci return 0; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci test.objectid = sk->max_objectid; 153162306a36Sopenharmony_ci test.type = sk->max_type; 153262306a36Sopenharmony_ci test.offset = sk->max_offset; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci ret = btrfs_comp_cpu_keys(key, &test); 153562306a36Sopenharmony_ci if (ret > 0) 153662306a36Sopenharmony_ci return 0; 153762306a36Sopenharmony_ci return 1; 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_cistatic noinline int copy_to_sk(struct btrfs_path *path, 154162306a36Sopenharmony_ci struct btrfs_key *key, 154262306a36Sopenharmony_ci struct btrfs_ioctl_search_key *sk, 154362306a36Sopenharmony_ci u64 *buf_size, 154462306a36Sopenharmony_ci char __user *ubuf, 154562306a36Sopenharmony_ci unsigned long *sk_offset, 154662306a36Sopenharmony_ci int *num_found) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci u64 found_transid; 154962306a36Sopenharmony_ci struct extent_buffer *leaf; 155062306a36Sopenharmony_ci struct btrfs_ioctl_search_header sh; 155162306a36Sopenharmony_ci struct btrfs_key test; 155262306a36Sopenharmony_ci unsigned long item_off; 155362306a36Sopenharmony_ci unsigned long item_len; 155462306a36Sopenharmony_ci int nritems; 155562306a36Sopenharmony_ci int i; 155662306a36Sopenharmony_ci int slot; 155762306a36Sopenharmony_ci int ret = 0; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci leaf = path->nodes[0]; 156062306a36Sopenharmony_ci slot = path->slots[0]; 156162306a36Sopenharmony_ci nritems = btrfs_header_nritems(leaf); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if (btrfs_header_generation(leaf) > sk->max_transid) { 156462306a36Sopenharmony_ci i = nritems; 156562306a36Sopenharmony_ci goto advance_key; 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci found_transid = btrfs_header_generation(leaf); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci for (i = slot; i < nritems; i++) { 157062306a36Sopenharmony_ci item_off = btrfs_item_ptr_offset(leaf, i); 157162306a36Sopenharmony_ci item_len = btrfs_item_size(leaf, i); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci btrfs_item_key_to_cpu(leaf, key, i); 157462306a36Sopenharmony_ci if (!key_in_sk(key, sk)) 157562306a36Sopenharmony_ci continue; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci if (sizeof(sh) + item_len > *buf_size) { 157862306a36Sopenharmony_ci if (*num_found) { 157962306a36Sopenharmony_ci ret = 1; 158062306a36Sopenharmony_ci goto out; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci /* 158462306a36Sopenharmony_ci * return one empty item back for v1, which does not 158562306a36Sopenharmony_ci * handle -EOVERFLOW 158662306a36Sopenharmony_ci */ 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci *buf_size = sizeof(sh) + item_len; 158962306a36Sopenharmony_ci item_len = 0; 159062306a36Sopenharmony_ci ret = -EOVERFLOW; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci if (sizeof(sh) + item_len + *sk_offset > *buf_size) { 159462306a36Sopenharmony_ci ret = 1; 159562306a36Sopenharmony_ci goto out; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci sh.objectid = key->objectid; 159962306a36Sopenharmony_ci sh.offset = key->offset; 160062306a36Sopenharmony_ci sh.type = key->type; 160162306a36Sopenharmony_ci sh.len = item_len; 160262306a36Sopenharmony_ci sh.transid = found_transid; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* 160562306a36Sopenharmony_ci * Copy search result header. If we fault then loop again so we 160662306a36Sopenharmony_ci * can fault in the pages and -EFAULT there if there's a 160762306a36Sopenharmony_ci * problem. Otherwise we'll fault and then copy the buffer in 160862306a36Sopenharmony_ci * properly this next time through 160962306a36Sopenharmony_ci */ 161062306a36Sopenharmony_ci if (copy_to_user_nofault(ubuf + *sk_offset, &sh, sizeof(sh))) { 161162306a36Sopenharmony_ci ret = 0; 161262306a36Sopenharmony_ci goto out; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci *sk_offset += sizeof(sh); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (item_len) { 161862306a36Sopenharmony_ci char __user *up = ubuf + *sk_offset; 161962306a36Sopenharmony_ci /* 162062306a36Sopenharmony_ci * Copy the item, same behavior as above, but reset the 162162306a36Sopenharmony_ci * * sk_offset so we copy the full thing again. 162262306a36Sopenharmony_ci */ 162362306a36Sopenharmony_ci if (read_extent_buffer_to_user_nofault(leaf, up, 162462306a36Sopenharmony_ci item_off, item_len)) { 162562306a36Sopenharmony_ci ret = 0; 162662306a36Sopenharmony_ci *sk_offset -= sizeof(sh); 162762306a36Sopenharmony_ci goto out; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci *sk_offset += item_len; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci (*num_found)++; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci if (ret) /* -EOVERFLOW from above */ 163562306a36Sopenharmony_ci goto out; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci if (*num_found >= sk->nr_items) { 163862306a36Sopenharmony_ci ret = 1; 163962306a36Sopenharmony_ci goto out; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci } 164262306a36Sopenharmony_ciadvance_key: 164362306a36Sopenharmony_ci ret = 0; 164462306a36Sopenharmony_ci test.objectid = sk->max_objectid; 164562306a36Sopenharmony_ci test.type = sk->max_type; 164662306a36Sopenharmony_ci test.offset = sk->max_offset; 164762306a36Sopenharmony_ci if (btrfs_comp_cpu_keys(key, &test) >= 0) 164862306a36Sopenharmony_ci ret = 1; 164962306a36Sopenharmony_ci else if (key->offset < (u64)-1) 165062306a36Sopenharmony_ci key->offset++; 165162306a36Sopenharmony_ci else if (key->type < (u8)-1) { 165262306a36Sopenharmony_ci key->offset = 0; 165362306a36Sopenharmony_ci key->type++; 165462306a36Sopenharmony_ci } else if (key->objectid < (u64)-1) { 165562306a36Sopenharmony_ci key->offset = 0; 165662306a36Sopenharmony_ci key->type = 0; 165762306a36Sopenharmony_ci key->objectid++; 165862306a36Sopenharmony_ci } else 165962306a36Sopenharmony_ci ret = 1; 166062306a36Sopenharmony_ciout: 166162306a36Sopenharmony_ci /* 166262306a36Sopenharmony_ci * 0: all items from this leaf copied, continue with next 166362306a36Sopenharmony_ci * 1: * more items can be copied, but unused buffer is too small 166462306a36Sopenharmony_ci * * all items were found 166562306a36Sopenharmony_ci * Either way, it will stops the loop which iterates to the next 166662306a36Sopenharmony_ci * leaf 166762306a36Sopenharmony_ci * -EOVERFLOW: item was to large for buffer 166862306a36Sopenharmony_ci * -EFAULT: could not copy extent buffer back to userspace 166962306a36Sopenharmony_ci */ 167062306a36Sopenharmony_ci return ret; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic noinline int search_ioctl(struct inode *inode, 167462306a36Sopenharmony_ci struct btrfs_ioctl_search_key *sk, 167562306a36Sopenharmony_ci u64 *buf_size, 167662306a36Sopenharmony_ci char __user *ubuf) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci struct btrfs_fs_info *info = btrfs_sb(inode->i_sb); 167962306a36Sopenharmony_ci struct btrfs_root *root; 168062306a36Sopenharmony_ci struct btrfs_key key; 168162306a36Sopenharmony_ci struct btrfs_path *path; 168262306a36Sopenharmony_ci int ret; 168362306a36Sopenharmony_ci int num_found = 0; 168462306a36Sopenharmony_ci unsigned long sk_offset = 0; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (*buf_size < sizeof(struct btrfs_ioctl_search_header)) { 168762306a36Sopenharmony_ci *buf_size = sizeof(struct btrfs_ioctl_search_header); 168862306a36Sopenharmony_ci return -EOVERFLOW; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci path = btrfs_alloc_path(); 169262306a36Sopenharmony_ci if (!path) 169362306a36Sopenharmony_ci return -ENOMEM; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (sk->tree_id == 0) { 169662306a36Sopenharmony_ci /* search the root of the inode that was passed */ 169762306a36Sopenharmony_ci root = btrfs_grab_root(BTRFS_I(inode)->root); 169862306a36Sopenharmony_ci } else { 169962306a36Sopenharmony_ci root = btrfs_get_fs_root(info, sk->tree_id, true); 170062306a36Sopenharmony_ci if (IS_ERR(root)) { 170162306a36Sopenharmony_ci btrfs_free_path(path); 170262306a36Sopenharmony_ci return PTR_ERR(root); 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci key.objectid = sk->min_objectid; 170762306a36Sopenharmony_ci key.type = sk->min_type; 170862306a36Sopenharmony_ci key.offset = sk->min_offset; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci while (1) { 171162306a36Sopenharmony_ci ret = -EFAULT; 171262306a36Sopenharmony_ci /* 171362306a36Sopenharmony_ci * Ensure that the whole user buffer is faulted in at sub-page 171462306a36Sopenharmony_ci * granularity, otherwise the loop may live-lock. 171562306a36Sopenharmony_ci */ 171662306a36Sopenharmony_ci if (fault_in_subpage_writeable(ubuf + sk_offset, 171762306a36Sopenharmony_ci *buf_size - sk_offset)) 171862306a36Sopenharmony_ci break; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci ret = btrfs_search_forward(root, &key, path, sk->min_transid); 172162306a36Sopenharmony_ci if (ret != 0) { 172262306a36Sopenharmony_ci if (ret > 0) 172362306a36Sopenharmony_ci ret = 0; 172462306a36Sopenharmony_ci goto err; 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci ret = copy_to_sk(path, &key, sk, buf_size, ubuf, 172762306a36Sopenharmony_ci &sk_offset, &num_found); 172862306a36Sopenharmony_ci btrfs_release_path(path); 172962306a36Sopenharmony_ci if (ret) 173062306a36Sopenharmony_ci break; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci } 173362306a36Sopenharmony_ci if (ret > 0) 173462306a36Sopenharmony_ci ret = 0; 173562306a36Sopenharmony_cierr: 173662306a36Sopenharmony_ci sk->nr_items = num_found; 173762306a36Sopenharmony_ci btrfs_put_root(root); 173862306a36Sopenharmony_ci btrfs_free_path(path); 173962306a36Sopenharmony_ci return ret; 174062306a36Sopenharmony_ci} 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_cistatic noinline int btrfs_ioctl_tree_search(struct inode *inode, 174362306a36Sopenharmony_ci void __user *argp) 174462306a36Sopenharmony_ci{ 174562306a36Sopenharmony_ci struct btrfs_ioctl_search_args __user *uargs = argp; 174662306a36Sopenharmony_ci struct btrfs_ioctl_search_key sk; 174762306a36Sopenharmony_ci int ret; 174862306a36Sopenharmony_ci u64 buf_size; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 175162306a36Sopenharmony_ci return -EPERM; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (copy_from_user(&sk, &uargs->key, sizeof(sk))) 175462306a36Sopenharmony_ci return -EFAULT; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci buf_size = sizeof(uargs->buf); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci ret = search_ioctl(inode, &sk, &buf_size, uargs->buf); 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci /* 176162306a36Sopenharmony_ci * In the origin implementation an overflow is handled by returning a 176262306a36Sopenharmony_ci * search header with a len of zero, so reset ret. 176362306a36Sopenharmony_ci */ 176462306a36Sopenharmony_ci if (ret == -EOVERFLOW) 176562306a36Sopenharmony_ci ret = 0; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk))) 176862306a36Sopenharmony_ci ret = -EFAULT; 176962306a36Sopenharmony_ci return ret; 177062306a36Sopenharmony_ci} 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_cistatic noinline int btrfs_ioctl_tree_search_v2(struct inode *inode, 177362306a36Sopenharmony_ci void __user *argp) 177462306a36Sopenharmony_ci{ 177562306a36Sopenharmony_ci struct btrfs_ioctl_search_args_v2 __user *uarg = argp; 177662306a36Sopenharmony_ci struct btrfs_ioctl_search_args_v2 args; 177762306a36Sopenharmony_ci int ret; 177862306a36Sopenharmony_ci u64 buf_size; 177962306a36Sopenharmony_ci const u64 buf_limit = SZ_16M; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 178262306a36Sopenharmony_ci return -EPERM; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci /* copy search header and buffer size */ 178562306a36Sopenharmony_ci if (copy_from_user(&args, uarg, sizeof(args))) 178662306a36Sopenharmony_ci return -EFAULT; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci buf_size = args.buf_size; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci /* limit result size to 16MB */ 179162306a36Sopenharmony_ci if (buf_size > buf_limit) 179262306a36Sopenharmony_ci buf_size = buf_limit; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci ret = search_ioctl(inode, &args.key, &buf_size, 179562306a36Sopenharmony_ci (char __user *)(&uarg->buf[0])); 179662306a36Sopenharmony_ci if (ret == 0 && copy_to_user(&uarg->key, &args.key, sizeof(args.key))) 179762306a36Sopenharmony_ci ret = -EFAULT; 179862306a36Sopenharmony_ci else if (ret == -EOVERFLOW && 179962306a36Sopenharmony_ci copy_to_user(&uarg->buf_size, &buf_size, sizeof(buf_size))) 180062306a36Sopenharmony_ci ret = -EFAULT; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci return ret; 180362306a36Sopenharmony_ci} 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci/* 180662306a36Sopenharmony_ci * Search INODE_REFs to identify path name of 'dirid' directory 180762306a36Sopenharmony_ci * in a 'tree_id' tree. and sets path name to 'name'. 180862306a36Sopenharmony_ci */ 180962306a36Sopenharmony_cistatic noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, 181062306a36Sopenharmony_ci u64 tree_id, u64 dirid, char *name) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci struct btrfs_root *root; 181362306a36Sopenharmony_ci struct btrfs_key key; 181462306a36Sopenharmony_ci char *ptr; 181562306a36Sopenharmony_ci int ret = -1; 181662306a36Sopenharmony_ci int slot; 181762306a36Sopenharmony_ci int len; 181862306a36Sopenharmony_ci int total_len = 0; 181962306a36Sopenharmony_ci struct btrfs_inode_ref *iref; 182062306a36Sopenharmony_ci struct extent_buffer *l; 182162306a36Sopenharmony_ci struct btrfs_path *path; 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (dirid == BTRFS_FIRST_FREE_OBJECTID) { 182462306a36Sopenharmony_ci name[0]='\0'; 182562306a36Sopenharmony_ci return 0; 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci path = btrfs_alloc_path(); 182962306a36Sopenharmony_ci if (!path) 183062306a36Sopenharmony_ci return -ENOMEM; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci ptr = &name[BTRFS_INO_LOOKUP_PATH_MAX - 1]; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci root = btrfs_get_fs_root(info, tree_id, true); 183562306a36Sopenharmony_ci if (IS_ERR(root)) { 183662306a36Sopenharmony_ci ret = PTR_ERR(root); 183762306a36Sopenharmony_ci root = NULL; 183862306a36Sopenharmony_ci goto out; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci key.objectid = dirid; 184262306a36Sopenharmony_ci key.type = BTRFS_INODE_REF_KEY; 184362306a36Sopenharmony_ci key.offset = (u64)-1; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci while (1) { 184662306a36Sopenharmony_ci ret = btrfs_search_backwards(root, &key, path); 184762306a36Sopenharmony_ci if (ret < 0) 184862306a36Sopenharmony_ci goto out; 184962306a36Sopenharmony_ci else if (ret > 0) { 185062306a36Sopenharmony_ci ret = -ENOENT; 185162306a36Sopenharmony_ci goto out; 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci l = path->nodes[0]; 185562306a36Sopenharmony_ci slot = path->slots[0]; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref); 185862306a36Sopenharmony_ci len = btrfs_inode_ref_name_len(l, iref); 185962306a36Sopenharmony_ci ptr -= len + 1; 186062306a36Sopenharmony_ci total_len += len + 1; 186162306a36Sopenharmony_ci if (ptr < name) { 186262306a36Sopenharmony_ci ret = -ENAMETOOLONG; 186362306a36Sopenharmony_ci goto out; 186462306a36Sopenharmony_ci } 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci *(ptr + len) = '/'; 186762306a36Sopenharmony_ci read_extent_buffer(l, ptr, (unsigned long)(iref + 1), len); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (key.offset == BTRFS_FIRST_FREE_OBJECTID) 187062306a36Sopenharmony_ci break; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci btrfs_release_path(path); 187362306a36Sopenharmony_ci key.objectid = key.offset; 187462306a36Sopenharmony_ci key.offset = (u64)-1; 187562306a36Sopenharmony_ci dirid = key.objectid; 187662306a36Sopenharmony_ci } 187762306a36Sopenharmony_ci memmove(name, ptr, total_len); 187862306a36Sopenharmony_ci name[total_len] = '\0'; 187962306a36Sopenharmony_ci ret = 0; 188062306a36Sopenharmony_ciout: 188162306a36Sopenharmony_ci btrfs_put_root(root); 188262306a36Sopenharmony_ci btrfs_free_path(path); 188362306a36Sopenharmony_ci return ret; 188462306a36Sopenharmony_ci} 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_cistatic int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, 188762306a36Sopenharmony_ci struct inode *inode, 188862306a36Sopenharmony_ci struct btrfs_ioctl_ino_lookup_user_args *args) 188962306a36Sopenharmony_ci{ 189062306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; 189162306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 189262306a36Sopenharmony_ci struct btrfs_key upper_limit = BTRFS_I(inode)->location; 189362306a36Sopenharmony_ci u64 treeid = BTRFS_I(inode)->root->root_key.objectid; 189462306a36Sopenharmony_ci u64 dirid = args->dirid; 189562306a36Sopenharmony_ci unsigned long item_off; 189662306a36Sopenharmony_ci unsigned long item_len; 189762306a36Sopenharmony_ci struct btrfs_inode_ref *iref; 189862306a36Sopenharmony_ci struct btrfs_root_ref *rref; 189962306a36Sopenharmony_ci struct btrfs_root *root = NULL; 190062306a36Sopenharmony_ci struct btrfs_path *path; 190162306a36Sopenharmony_ci struct btrfs_key key, key2; 190262306a36Sopenharmony_ci struct extent_buffer *leaf; 190362306a36Sopenharmony_ci struct inode *temp_inode; 190462306a36Sopenharmony_ci char *ptr; 190562306a36Sopenharmony_ci int slot; 190662306a36Sopenharmony_ci int len; 190762306a36Sopenharmony_ci int total_len = 0; 190862306a36Sopenharmony_ci int ret; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci path = btrfs_alloc_path(); 191162306a36Sopenharmony_ci if (!path) 191262306a36Sopenharmony_ci return -ENOMEM; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci /* 191562306a36Sopenharmony_ci * If the bottom subvolume does not exist directly under upper_limit, 191662306a36Sopenharmony_ci * construct the path in from the bottom up. 191762306a36Sopenharmony_ci */ 191862306a36Sopenharmony_ci if (dirid != upper_limit.objectid) { 191962306a36Sopenharmony_ci ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1]; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci root = btrfs_get_fs_root(fs_info, treeid, true); 192262306a36Sopenharmony_ci if (IS_ERR(root)) { 192362306a36Sopenharmony_ci ret = PTR_ERR(root); 192462306a36Sopenharmony_ci goto out; 192562306a36Sopenharmony_ci } 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci key.objectid = dirid; 192862306a36Sopenharmony_ci key.type = BTRFS_INODE_REF_KEY; 192962306a36Sopenharmony_ci key.offset = (u64)-1; 193062306a36Sopenharmony_ci while (1) { 193162306a36Sopenharmony_ci ret = btrfs_search_backwards(root, &key, path); 193262306a36Sopenharmony_ci if (ret < 0) 193362306a36Sopenharmony_ci goto out_put; 193462306a36Sopenharmony_ci else if (ret > 0) { 193562306a36Sopenharmony_ci ret = -ENOENT; 193662306a36Sopenharmony_ci goto out_put; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci leaf = path->nodes[0]; 194062306a36Sopenharmony_ci slot = path->slots[0]; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci iref = btrfs_item_ptr(leaf, slot, struct btrfs_inode_ref); 194362306a36Sopenharmony_ci len = btrfs_inode_ref_name_len(leaf, iref); 194462306a36Sopenharmony_ci ptr -= len + 1; 194562306a36Sopenharmony_ci total_len += len + 1; 194662306a36Sopenharmony_ci if (ptr < args->path) { 194762306a36Sopenharmony_ci ret = -ENAMETOOLONG; 194862306a36Sopenharmony_ci goto out_put; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci *(ptr + len) = '/'; 195262306a36Sopenharmony_ci read_extent_buffer(leaf, ptr, 195362306a36Sopenharmony_ci (unsigned long)(iref + 1), len); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci /* Check the read+exec permission of this directory */ 195662306a36Sopenharmony_ci ret = btrfs_previous_item(root, path, dirid, 195762306a36Sopenharmony_ci BTRFS_INODE_ITEM_KEY); 195862306a36Sopenharmony_ci if (ret < 0) { 195962306a36Sopenharmony_ci goto out_put; 196062306a36Sopenharmony_ci } else if (ret > 0) { 196162306a36Sopenharmony_ci ret = -ENOENT; 196262306a36Sopenharmony_ci goto out_put; 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci leaf = path->nodes[0]; 196662306a36Sopenharmony_ci slot = path->slots[0]; 196762306a36Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key2, slot); 196862306a36Sopenharmony_ci if (key2.objectid != dirid) { 196962306a36Sopenharmony_ci ret = -ENOENT; 197062306a36Sopenharmony_ci goto out_put; 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci /* 197462306a36Sopenharmony_ci * We don't need the path anymore, so release it and 197562306a36Sopenharmony_ci * avoid deadlocks and lockdep warnings in case 197662306a36Sopenharmony_ci * btrfs_iget() needs to lookup the inode from its root 197762306a36Sopenharmony_ci * btree and lock the same leaf. 197862306a36Sopenharmony_ci */ 197962306a36Sopenharmony_ci btrfs_release_path(path); 198062306a36Sopenharmony_ci temp_inode = btrfs_iget(sb, key2.objectid, root); 198162306a36Sopenharmony_ci if (IS_ERR(temp_inode)) { 198262306a36Sopenharmony_ci ret = PTR_ERR(temp_inode); 198362306a36Sopenharmony_ci goto out_put; 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci ret = inode_permission(idmap, temp_inode, 198662306a36Sopenharmony_ci MAY_READ | MAY_EXEC); 198762306a36Sopenharmony_ci iput(temp_inode); 198862306a36Sopenharmony_ci if (ret) { 198962306a36Sopenharmony_ci ret = -EACCES; 199062306a36Sopenharmony_ci goto out_put; 199162306a36Sopenharmony_ci } 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci if (key.offset == upper_limit.objectid) 199462306a36Sopenharmony_ci break; 199562306a36Sopenharmony_ci if (key.objectid == BTRFS_FIRST_FREE_OBJECTID) { 199662306a36Sopenharmony_ci ret = -EACCES; 199762306a36Sopenharmony_ci goto out_put; 199862306a36Sopenharmony_ci } 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci key.objectid = key.offset; 200162306a36Sopenharmony_ci key.offset = (u64)-1; 200262306a36Sopenharmony_ci dirid = key.objectid; 200362306a36Sopenharmony_ci } 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci memmove(args->path, ptr, total_len); 200662306a36Sopenharmony_ci args->path[total_len] = '\0'; 200762306a36Sopenharmony_ci btrfs_put_root(root); 200862306a36Sopenharmony_ci root = NULL; 200962306a36Sopenharmony_ci btrfs_release_path(path); 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* Get the bottom subvolume's name from ROOT_REF */ 201362306a36Sopenharmony_ci key.objectid = treeid; 201462306a36Sopenharmony_ci key.type = BTRFS_ROOT_REF_KEY; 201562306a36Sopenharmony_ci key.offset = args->treeid; 201662306a36Sopenharmony_ci ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0); 201762306a36Sopenharmony_ci if (ret < 0) { 201862306a36Sopenharmony_ci goto out; 201962306a36Sopenharmony_ci } else if (ret > 0) { 202062306a36Sopenharmony_ci ret = -ENOENT; 202162306a36Sopenharmony_ci goto out; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci leaf = path->nodes[0]; 202562306a36Sopenharmony_ci slot = path->slots[0]; 202662306a36Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, slot); 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci item_off = btrfs_item_ptr_offset(leaf, slot); 202962306a36Sopenharmony_ci item_len = btrfs_item_size(leaf, slot); 203062306a36Sopenharmony_ci /* Check if dirid in ROOT_REF corresponds to passed dirid */ 203162306a36Sopenharmony_ci rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); 203262306a36Sopenharmony_ci if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) { 203362306a36Sopenharmony_ci ret = -EINVAL; 203462306a36Sopenharmony_ci goto out; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci /* Copy subvolume's name */ 203862306a36Sopenharmony_ci item_off += sizeof(struct btrfs_root_ref); 203962306a36Sopenharmony_ci item_len -= sizeof(struct btrfs_root_ref); 204062306a36Sopenharmony_ci read_extent_buffer(leaf, args->name, item_off, item_len); 204162306a36Sopenharmony_ci args->name[item_len] = 0; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ciout_put: 204462306a36Sopenharmony_ci btrfs_put_root(root); 204562306a36Sopenharmony_ciout: 204662306a36Sopenharmony_ci btrfs_free_path(path); 204762306a36Sopenharmony_ci return ret; 204862306a36Sopenharmony_ci} 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_cistatic noinline int btrfs_ioctl_ino_lookup(struct btrfs_root *root, 205162306a36Sopenharmony_ci void __user *argp) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci struct btrfs_ioctl_ino_lookup_args *args; 205462306a36Sopenharmony_ci int ret = 0; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci args = memdup_user(argp, sizeof(*args)); 205762306a36Sopenharmony_ci if (IS_ERR(args)) 205862306a36Sopenharmony_ci return PTR_ERR(args); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci /* 206162306a36Sopenharmony_ci * Unprivileged query to obtain the containing subvolume root id. The 206262306a36Sopenharmony_ci * path is reset so it's consistent with btrfs_search_path_in_tree. 206362306a36Sopenharmony_ci */ 206462306a36Sopenharmony_ci if (args->treeid == 0) 206562306a36Sopenharmony_ci args->treeid = root->root_key.objectid; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (args->objectid == BTRFS_FIRST_FREE_OBJECTID) { 206862306a36Sopenharmony_ci args->name[0] = 0; 206962306a36Sopenharmony_ci goto out; 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 207362306a36Sopenharmony_ci ret = -EPERM; 207462306a36Sopenharmony_ci goto out; 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci ret = btrfs_search_path_in_tree(root->fs_info, 207862306a36Sopenharmony_ci args->treeid, args->objectid, 207962306a36Sopenharmony_ci args->name); 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ciout: 208262306a36Sopenharmony_ci if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) 208362306a36Sopenharmony_ci ret = -EFAULT; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci kfree(args); 208662306a36Sopenharmony_ci return ret; 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci/* 209062306a36Sopenharmony_ci * Version of ino_lookup ioctl (unprivileged) 209162306a36Sopenharmony_ci * 209262306a36Sopenharmony_ci * The main differences from ino_lookup ioctl are: 209362306a36Sopenharmony_ci * 209462306a36Sopenharmony_ci * 1. Read + Exec permission will be checked using inode_permission() during 209562306a36Sopenharmony_ci * path construction. -EACCES will be returned in case of failure. 209662306a36Sopenharmony_ci * 2. Path construction will be stopped at the inode number which corresponds 209762306a36Sopenharmony_ci * to the fd with which this ioctl is called. If constructed path does not 209862306a36Sopenharmony_ci * exist under fd's inode, -EACCES will be returned. 209962306a36Sopenharmony_ci * 3. The name of bottom subvolume is also searched and filled. 210062306a36Sopenharmony_ci */ 210162306a36Sopenharmony_cistatic int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp) 210262306a36Sopenharmony_ci{ 210362306a36Sopenharmony_ci struct btrfs_ioctl_ino_lookup_user_args *args; 210462306a36Sopenharmony_ci struct inode *inode; 210562306a36Sopenharmony_ci int ret; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci args = memdup_user(argp, sizeof(*args)); 210862306a36Sopenharmony_ci if (IS_ERR(args)) 210962306a36Sopenharmony_ci return PTR_ERR(args); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci inode = file_inode(file); 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci if (args->dirid == BTRFS_FIRST_FREE_OBJECTID && 211462306a36Sopenharmony_ci BTRFS_I(inode)->location.objectid != BTRFS_FIRST_FREE_OBJECTID) { 211562306a36Sopenharmony_ci /* 211662306a36Sopenharmony_ci * The subvolume does not exist under fd with which this is 211762306a36Sopenharmony_ci * called 211862306a36Sopenharmony_ci */ 211962306a36Sopenharmony_ci kfree(args); 212062306a36Sopenharmony_ci return -EACCES; 212162306a36Sopenharmony_ci } 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci ret = btrfs_search_path_in_tree_user(file_mnt_idmap(file), inode, args); 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) 212662306a36Sopenharmony_ci ret = -EFAULT; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci kfree(args); 212962306a36Sopenharmony_ci return ret; 213062306a36Sopenharmony_ci} 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */ 213362306a36Sopenharmony_cistatic int btrfs_ioctl_get_subvol_info(struct inode *inode, void __user *argp) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci struct btrfs_ioctl_get_subvol_info_args *subvol_info; 213662306a36Sopenharmony_ci struct btrfs_fs_info *fs_info; 213762306a36Sopenharmony_ci struct btrfs_root *root; 213862306a36Sopenharmony_ci struct btrfs_path *path; 213962306a36Sopenharmony_ci struct btrfs_key key; 214062306a36Sopenharmony_ci struct btrfs_root_item *root_item; 214162306a36Sopenharmony_ci struct btrfs_root_ref *rref; 214262306a36Sopenharmony_ci struct extent_buffer *leaf; 214362306a36Sopenharmony_ci unsigned long item_off; 214462306a36Sopenharmony_ci unsigned long item_len; 214562306a36Sopenharmony_ci int slot; 214662306a36Sopenharmony_ci int ret = 0; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci path = btrfs_alloc_path(); 214962306a36Sopenharmony_ci if (!path) 215062306a36Sopenharmony_ci return -ENOMEM; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL); 215362306a36Sopenharmony_ci if (!subvol_info) { 215462306a36Sopenharmony_ci btrfs_free_path(path); 215562306a36Sopenharmony_ci return -ENOMEM; 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci fs_info = BTRFS_I(inode)->root->fs_info; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci /* Get root_item of inode's subvolume */ 216162306a36Sopenharmony_ci key.objectid = BTRFS_I(inode)->root->root_key.objectid; 216262306a36Sopenharmony_ci root = btrfs_get_fs_root(fs_info, key.objectid, true); 216362306a36Sopenharmony_ci if (IS_ERR(root)) { 216462306a36Sopenharmony_ci ret = PTR_ERR(root); 216562306a36Sopenharmony_ci goto out_free; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci root_item = &root->root_item; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci subvol_info->treeid = key.objectid; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci subvol_info->generation = btrfs_root_generation(root_item); 217262306a36Sopenharmony_ci subvol_info->flags = btrfs_root_flags(root_item); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci memcpy(subvol_info->uuid, root_item->uuid, BTRFS_UUID_SIZE); 217562306a36Sopenharmony_ci memcpy(subvol_info->parent_uuid, root_item->parent_uuid, 217662306a36Sopenharmony_ci BTRFS_UUID_SIZE); 217762306a36Sopenharmony_ci memcpy(subvol_info->received_uuid, root_item->received_uuid, 217862306a36Sopenharmony_ci BTRFS_UUID_SIZE); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci subvol_info->ctransid = btrfs_root_ctransid(root_item); 218162306a36Sopenharmony_ci subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item->ctime); 218262306a36Sopenharmony_ci subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item->ctime); 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci subvol_info->otransid = btrfs_root_otransid(root_item); 218562306a36Sopenharmony_ci subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item->otime); 218662306a36Sopenharmony_ci subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item->otime); 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci subvol_info->stransid = btrfs_root_stransid(root_item); 218962306a36Sopenharmony_ci subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item->stime); 219062306a36Sopenharmony_ci subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item->stime); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci subvol_info->rtransid = btrfs_root_rtransid(root_item); 219362306a36Sopenharmony_ci subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item->rtime); 219462306a36Sopenharmony_ci subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item->rtime); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci if (key.objectid != BTRFS_FS_TREE_OBJECTID) { 219762306a36Sopenharmony_ci /* Search root tree for ROOT_BACKREF of this subvolume */ 219862306a36Sopenharmony_ci key.type = BTRFS_ROOT_BACKREF_KEY; 219962306a36Sopenharmony_ci key.offset = 0; 220062306a36Sopenharmony_ci ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0); 220162306a36Sopenharmony_ci if (ret < 0) { 220262306a36Sopenharmony_ci goto out; 220362306a36Sopenharmony_ci } else if (path->slots[0] >= 220462306a36Sopenharmony_ci btrfs_header_nritems(path->nodes[0])) { 220562306a36Sopenharmony_ci ret = btrfs_next_leaf(fs_info->tree_root, path); 220662306a36Sopenharmony_ci if (ret < 0) { 220762306a36Sopenharmony_ci goto out; 220862306a36Sopenharmony_ci } else if (ret > 0) { 220962306a36Sopenharmony_ci ret = -EUCLEAN; 221062306a36Sopenharmony_ci goto out; 221162306a36Sopenharmony_ci } 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci leaf = path->nodes[0]; 221562306a36Sopenharmony_ci slot = path->slots[0]; 221662306a36Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, slot); 221762306a36Sopenharmony_ci if (key.objectid == subvol_info->treeid && 221862306a36Sopenharmony_ci key.type == BTRFS_ROOT_BACKREF_KEY) { 221962306a36Sopenharmony_ci subvol_info->parent_id = key.offset; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); 222262306a36Sopenharmony_ci subvol_info->dirid = btrfs_root_ref_dirid(leaf, rref); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci item_off = btrfs_item_ptr_offset(leaf, slot) 222562306a36Sopenharmony_ci + sizeof(struct btrfs_root_ref); 222662306a36Sopenharmony_ci item_len = btrfs_item_size(leaf, slot) 222762306a36Sopenharmony_ci - sizeof(struct btrfs_root_ref); 222862306a36Sopenharmony_ci read_extent_buffer(leaf, subvol_info->name, 222962306a36Sopenharmony_ci item_off, item_len); 223062306a36Sopenharmony_ci } else { 223162306a36Sopenharmony_ci ret = -ENOENT; 223262306a36Sopenharmony_ci goto out; 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci btrfs_free_path(path); 223762306a36Sopenharmony_ci path = NULL; 223862306a36Sopenharmony_ci if (copy_to_user(argp, subvol_info, sizeof(*subvol_info))) 223962306a36Sopenharmony_ci ret = -EFAULT; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ciout: 224262306a36Sopenharmony_ci btrfs_put_root(root); 224362306a36Sopenharmony_ciout_free: 224462306a36Sopenharmony_ci btrfs_free_path(path); 224562306a36Sopenharmony_ci kfree(subvol_info); 224662306a36Sopenharmony_ci return ret; 224762306a36Sopenharmony_ci} 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci/* 225062306a36Sopenharmony_ci * Return ROOT_REF information of the subvolume containing this inode 225162306a36Sopenharmony_ci * except the subvolume name. 225262306a36Sopenharmony_ci */ 225362306a36Sopenharmony_cistatic int btrfs_ioctl_get_subvol_rootref(struct btrfs_root *root, 225462306a36Sopenharmony_ci void __user *argp) 225562306a36Sopenharmony_ci{ 225662306a36Sopenharmony_ci struct btrfs_ioctl_get_subvol_rootref_args *rootrefs; 225762306a36Sopenharmony_ci struct btrfs_root_ref *rref; 225862306a36Sopenharmony_ci struct btrfs_path *path; 225962306a36Sopenharmony_ci struct btrfs_key key; 226062306a36Sopenharmony_ci struct extent_buffer *leaf; 226162306a36Sopenharmony_ci u64 objectid; 226262306a36Sopenharmony_ci int slot; 226362306a36Sopenharmony_ci int ret; 226462306a36Sopenharmony_ci u8 found; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci path = btrfs_alloc_path(); 226762306a36Sopenharmony_ci if (!path) 226862306a36Sopenharmony_ci return -ENOMEM; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci rootrefs = memdup_user(argp, sizeof(*rootrefs)); 227162306a36Sopenharmony_ci if (IS_ERR(rootrefs)) { 227262306a36Sopenharmony_ci btrfs_free_path(path); 227362306a36Sopenharmony_ci return PTR_ERR(rootrefs); 227462306a36Sopenharmony_ci } 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci objectid = root->root_key.objectid; 227762306a36Sopenharmony_ci key.objectid = objectid; 227862306a36Sopenharmony_ci key.type = BTRFS_ROOT_REF_KEY; 227962306a36Sopenharmony_ci key.offset = rootrefs->min_treeid; 228062306a36Sopenharmony_ci found = 0; 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci root = root->fs_info->tree_root; 228362306a36Sopenharmony_ci ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 228462306a36Sopenharmony_ci if (ret < 0) { 228562306a36Sopenharmony_ci goto out; 228662306a36Sopenharmony_ci } else if (path->slots[0] >= 228762306a36Sopenharmony_ci btrfs_header_nritems(path->nodes[0])) { 228862306a36Sopenharmony_ci ret = btrfs_next_leaf(root, path); 228962306a36Sopenharmony_ci if (ret < 0) { 229062306a36Sopenharmony_ci goto out; 229162306a36Sopenharmony_ci } else if (ret > 0) { 229262306a36Sopenharmony_ci ret = -EUCLEAN; 229362306a36Sopenharmony_ci goto out; 229462306a36Sopenharmony_ci } 229562306a36Sopenharmony_ci } 229662306a36Sopenharmony_ci while (1) { 229762306a36Sopenharmony_ci leaf = path->nodes[0]; 229862306a36Sopenharmony_ci slot = path->slots[0]; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, slot); 230162306a36Sopenharmony_ci if (key.objectid != objectid || key.type != BTRFS_ROOT_REF_KEY) { 230262306a36Sopenharmony_ci ret = 0; 230362306a36Sopenharmony_ci goto out; 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) { 230762306a36Sopenharmony_ci ret = -EOVERFLOW; 230862306a36Sopenharmony_ci goto out; 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); 231262306a36Sopenharmony_ci rootrefs->rootref[found].treeid = key.offset; 231362306a36Sopenharmony_ci rootrefs->rootref[found].dirid = 231462306a36Sopenharmony_ci btrfs_root_ref_dirid(leaf, rref); 231562306a36Sopenharmony_ci found++; 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci ret = btrfs_next_item(root, path); 231862306a36Sopenharmony_ci if (ret < 0) { 231962306a36Sopenharmony_ci goto out; 232062306a36Sopenharmony_ci } else if (ret > 0) { 232162306a36Sopenharmony_ci ret = -EUCLEAN; 232262306a36Sopenharmony_ci goto out; 232362306a36Sopenharmony_ci } 232462306a36Sopenharmony_ci } 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ciout: 232762306a36Sopenharmony_ci btrfs_free_path(path); 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci if (!ret || ret == -EOVERFLOW) { 233062306a36Sopenharmony_ci rootrefs->num_items = found; 233162306a36Sopenharmony_ci /* update min_treeid for next search */ 233262306a36Sopenharmony_ci if (found) 233362306a36Sopenharmony_ci rootrefs->min_treeid = 233462306a36Sopenharmony_ci rootrefs->rootref[found - 1].treeid + 1; 233562306a36Sopenharmony_ci if (copy_to_user(argp, rootrefs, sizeof(*rootrefs))) 233662306a36Sopenharmony_ci ret = -EFAULT; 233762306a36Sopenharmony_ci } 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci kfree(rootrefs); 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci return ret; 234262306a36Sopenharmony_ci} 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_cistatic noinline int btrfs_ioctl_snap_destroy(struct file *file, 234562306a36Sopenharmony_ci void __user *arg, 234662306a36Sopenharmony_ci bool destroy_v2) 234762306a36Sopenharmony_ci{ 234862306a36Sopenharmony_ci struct dentry *parent = file->f_path.dentry; 234962306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(parent->d_sb); 235062306a36Sopenharmony_ci struct dentry *dentry; 235162306a36Sopenharmony_ci struct inode *dir = d_inode(parent); 235262306a36Sopenharmony_ci struct inode *inode; 235362306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(dir)->root; 235462306a36Sopenharmony_ci struct btrfs_root *dest = NULL; 235562306a36Sopenharmony_ci struct btrfs_ioctl_vol_args *vol_args = NULL; 235662306a36Sopenharmony_ci struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL; 235762306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(file); 235862306a36Sopenharmony_ci char *subvol_name, *subvol_name_ptr = NULL; 235962306a36Sopenharmony_ci int subvol_namelen; 236062306a36Sopenharmony_ci int err = 0; 236162306a36Sopenharmony_ci bool destroy_parent = false; 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci /* We don't support snapshots with extent tree v2 yet. */ 236462306a36Sopenharmony_ci if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) { 236562306a36Sopenharmony_ci btrfs_err(fs_info, 236662306a36Sopenharmony_ci "extent tree v2 doesn't support snapshot deletion yet"); 236762306a36Sopenharmony_ci return -EOPNOTSUPP; 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci if (destroy_v2) { 237162306a36Sopenharmony_ci vol_args2 = memdup_user(arg, sizeof(*vol_args2)); 237262306a36Sopenharmony_ci if (IS_ERR(vol_args2)) 237362306a36Sopenharmony_ci return PTR_ERR(vol_args2); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci if (vol_args2->flags & ~BTRFS_SUBVOL_DELETE_ARGS_MASK) { 237662306a36Sopenharmony_ci err = -EOPNOTSUPP; 237762306a36Sopenharmony_ci goto out; 237862306a36Sopenharmony_ci } 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci /* 238162306a36Sopenharmony_ci * If SPEC_BY_ID is not set, we are looking for the subvolume by 238262306a36Sopenharmony_ci * name, same as v1 currently does. 238362306a36Sopenharmony_ci */ 238462306a36Sopenharmony_ci if (!(vol_args2->flags & BTRFS_SUBVOL_SPEC_BY_ID)) { 238562306a36Sopenharmony_ci vol_args2->name[BTRFS_SUBVOL_NAME_MAX] = 0; 238662306a36Sopenharmony_ci subvol_name = vol_args2->name; 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci err = mnt_want_write_file(file); 238962306a36Sopenharmony_ci if (err) 239062306a36Sopenharmony_ci goto out; 239162306a36Sopenharmony_ci } else { 239262306a36Sopenharmony_ci struct inode *old_dir; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { 239562306a36Sopenharmony_ci err = -EINVAL; 239662306a36Sopenharmony_ci goto out; 239762306a36Sopenharmony_ci } 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci err = mnt_want_write_file(file); 240062306a36Sopenharmony_ci if (err) 240162306a36Sopenharmony_ci goto out; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci dentry = btrfs_get_dentry(fs_info->sb, 240462306a36Sopenharmony_ci BTRFS_FIRST_FREE_OBJECTID, 240562306a36Sopenharmony_ci vol_args2->subvolid, 0); 240662306a36Sopenharmony_ci if (IS_ERR(dentry)) { 240762306a36Sopenharmony_ci err = PTR_ERR(dentry); 240862306a36Sopenharmony_ci goto out_drop_write; 240962306a36Sopenharmony_ci } 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci /* 241262306a36Sopenharmony_ci * Change the default parent since the subvolume being 241362306a36Sopenharmony_ci * deleted can be outside of the current mount point. 241462306a36Sopenharmony_ci */ 241562306a36Sopenharmony_ci parent = btrfs_get_parent(dentry); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci /* 241862306a36Sopenharmony_ci * At this point dentry->d_name can point to '/' if the 241962306a36Sopenharmony_ci * subvolume we want to destroy is outsite of the 242062306a36Sopenharmony_ci * current mount point, so we need to release the 242162306a36Sopenharmony_ci * current dentry and execute the lookup to return a new 242262306a36Sopenharmony_ci * one with ->d_name pointing to the 242362306a36Sopenharmony_ci * <mount point>/subvol_name. 242462306a36Sopenharmony_ci */ 242562306a36Sopenharmony_ci dput(dentry); 242662306a36Sopenharmony_ci if (IS_ERR(parent)) { 242762306a36Sopenharmony_ci err = PTR_ERR(parent); 242862306a36Sopenharmony_ci goto out_drop_write; 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci old_dir = dir; 243162306a36Sopenharmony_ci dir = d_inode(parent); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci /* 243462306a36Sopenharmony_ci * If v2 was used with SPEC_BY_ID, a new parent was 243562306a36Sopenharmony_ci * allocated since the subvolume can be outside of the 243662306a36Sopenharmony_ci * current mount point. Later on we need to release this 243762306a36Sopenharmony_ci * new parent dentry. 243862306a36Sopenharmony_ci */ 243962306a36Sopenharmony_ci destroy_parent = true; 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci /* 244262306a36Sopenharmony_ci * On idmapped mounts, deletion via subvolid is 244362306a36Sopenharmony_ci * restricted to subvolumes that are immediate 244462306a36Sopenharmony_ci * ancestors of the inode referenced by the file 244562306a36Sopenharmony_ci * descriptor in the ioctl. Otherwise the idmapping 244662306a36Sopenharmony_ci * could potentially be abused to delete subvolumes 244762306a36Sopenharmony_ci * anywhere in the filesystem the user wouldn't be able 244862306a36Sopenharmony_ci * to delete without an idmapped mount. 244962306a36Sopenharmony_ci */ 245062306a36Sopenharmony_ci if (old_dir != dir && idmap != &nop_mnt_idmap) { 245162306a36Sopenharmony_ci err = -EOPNOTSUPP; 245262306a36Sopenharmony_ci goto free_parent; 245362306a36Sopenharmony_ci } 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci subvol_name_ptr = btrfs_get_subvol_name_from_objectid( 245662306a36Sopenharmony_ci fs_info, vol_args2->subvolid); 245762306a36Sopenharmony_ci if (IS_ERR(subvol_name_ptr)) { 245862306a36Sopenharmony_ci err = PTR_ERR(subvol_name_ptr); 245962306a36Sopenharmony_ci goto free_parent; 246062306a36Sopenharmony_ci } 246162306a36Sopenharmony_ci /* subvol_name_ptr is already nul terminated */ 246262306a36Sopenharmony_ci subvol_name = (char *)kbasename(subvol_name_ptr); 246362306a36Sopenharmony_ci } 246462306a36Sopenharmony_ci } else { 246562306a36Sopenharmony_ci vol_args = memdup_user(arg, sizeof(*vol_args)); 246662306a36Sopenharmony_ci if (IS_ERR(vol_args)) 246762306a36Sopenharmony_ci return PTR_ERR(vol_args); 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci vol_args->name[BTRFS_PATH_NAME_MAX] = 0; 247062306a36Sopenharmony_ci subvol_name = vol_args->name; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci err = mnt_want_write_file(file); 247362306a36Sopenharmony_ci if (err) 247462306a36Sopenharmony_ci goto out; 247562306a36Sopenharmony_ci } 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci subvol_namelen = strlen(subvol_name); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci if (strchr(subvol_name, '/') || 248062306a36Sopenharmony_ci strncmp(subvol_name, "..", subvol_namelen) == 0) { 248162306a36Sopenharmony_ci err = -EINVAL; 248262306a36Sopenharmony_ci goto free_subvol_name; 248362306a36Sopenharmony_ci } 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci if (!S_ISDIR(dir->i_mode)) { 248662306a36Sopenharmony_ci err = -ENOTDIR; 248762306a36Sopenharmony_ci goto free_subvol_name; 248862306a36Sopenharmony_ci } 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); 249162306a36Sopenharmony_ci if (err == -EINTR) 249262306a36Sopenharmony_ci goto free_subvol_name; 249362306a36Sopenharmony_ci dentry = lookup_one(idmap, subvol_name, parent, subvol_namelen); 249462306a36Sopenharmony_ci if (IS_ERR(dentry)) { 249562306a36Sopenharmony_ci err = PTR_ERR(dentry); 249662306a36Sopenharmony_ci goto out_unlock_dir; 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci if (d_really_is_negative(dentry)) { 250062306a36Sopenharmony_ci err = -ENOENT; 250162306a36Sopenharmony_ci goto out_dput; 250262306a36Sopenharmony_ci } 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci inode = d_inode(dentry); 250562306a36Sopenharmony_ci dest = BTRFS_I(inode)->root; 250662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 250762306a36Sopenharmony_ci /* 250862306a36Sopenharmony_ci * Regular user. Only allow this with a special mount 250962306a36Sopenharmony_ci * option, when the user has write+exec access to the 251062306a36Sopenharmony_ci * subvol root, and when rmdir(2) would have been 251162306a36Sopenharmony_ci * allowed. 251262306a36Sopenharmony_ci * 251362306a36Sopenharmony_ci * Note that this is _not_ check that the subvol is 251462306a36Sopenharmony_ci * empty or doesn't contain data that we wouldn't 251562306a36Sopenharmony_ci * otherwise be able to delete. 251662306a36Sopenharmony_ci * 251762306a36Sopenharmony_ci * Users who want to delete empty subvols should try 251862306a36Sopenharmony_ci * rmdir(2). 251962306a36Sopenharmony_ci */ 252062306a36Sopenharmony_ci err = -EPERM; 252162306a36Sopenharmony_ci if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED)) 252262306a36Sopenharmony_ci goto out_dput; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci /* 252562306a36Sopenharmony_ci * Do not allow deletion if the parent dir is the same 252662306a36Sopenharmony_ci * as the dir to be deleted. That means the ioctl 252762306a36Sopenharmony_ci * must be called on the dentry referencing the root 252862306a36Sopenharmony_ci * of the subvol, not a random directory contained 252962306a36Sopenharmony_ci * within it. 253062306a36Sopenharmony_ci */ 253162306a36Sopenharmony_ci err = -EINVAL; 253262306a36Sopenharmony_ci if (root == dest) 253362306a36Sopenharmony_ci goto out_dput; 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci err = inode_permission(idmap, inode, MAY_WRITE | MAY_EXEC); 253662306a36Sopenharmony_ci if (err) 253762306a36Sopenharmony_ci goto out_dput; 253862306a36Sopenharmony_ci } 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_ci /* check if subvolume may be deleted by a user */ 254162306a36Sopenharmony_ci err = btrfs_may_delete(idmap, dir, dentry, 1); 254262306a36Sopenharmony_ci if (err) 254362306a36Sopenharmony_ci goto out_dput; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) { 254662306a36Sopenharmony_ci err = -EINVAL; 254762306a36Sopenharmony_ci goto out_dput; 254862306a36Sopenharmony_ci } 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci btrfs_inode_lock(BTRFS_I(inode), 0); 255162306a36Sopenharmony_ci err = btrfs_delete_subvolume(BTRFS_I(dir), dentry); 255262306a36Sopenharmony_ci btrfs_inode_unlock(BTRFS_I(inode), 0); 255362306a36Sopenharmony_ci if (!err) 255462306a36Sopenharmony_ci d_delete_notify(dir, dentry); 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ciout_dput: 255762306a36Sopenharmony_ci dput(dentry); 255862306a36Sopenharmony_ciout_unlock_dir: 255962306a36Sopenharmony_ci btrfs_inode_unlock(BTRFS_I(dir), 0); 256062306a36Sopenharmony_cifree_subvol_name: 256162306a36Sopenharmony_ci kfree(subvol_name_ptr); 256262306a36Sopenharmony_cifree_parent: 256362306a36Sopenharmony_ci if (destroy_parent) 256462306a36Sopenharmony_ci dput(parent); 256562306a36Sopenharmony_ciout_drop_write: 256662306a36Sopenharmony_ci mnt_drop_write_file(file); 256762306a36Sopenharmony_ciout: 256862306a36Sopenharmony_ci kfree(vol_args2); 256962306a36Sopenharmony_ci kfree(vol_args); 257062306a36Sopenharmony_ci return err; 257162306a36Sopenharmony_ci} 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_cistatic int btrfs_ioctl_defrag(struct file *file, void __user *argp) 257462306a36Sopenharmony_ci{ 257562306a36Sopenharmony_ci struct inode *inode = file_inode(file); 257662306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 257762306a36Sopenharmony_ci struct btrfs_ioctl_defrag_range_args range = {0}; 257862306a36Sopenharmony_ci int ret; 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci ret = mnt_want_write_file(file); 258162306a36Sopenharmony_ci if (ret) 258262306a36Sopenharmony_ci return ret; 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci if (btrfs_root_readonly(root)) { 258562306a36Sopenharmony_ci ret = -EROFS; 258662306a36Sopenharmony_ci goto out; 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci switch (inode->i_mode & S_IFMT) { 259062306a36Sopenharmony_ci case S_IFDIR: 259162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 259262306a36Sopenharmony_ci ret = -EPERM; 259362306a36Sopenharmony_ci goto out; 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ci ret = btrfs_defrag_root(root); 259662306a36Sopenharmony_ci break; 259762306a36Sopenharmony_ci case S_IFREG: 259862306a36Sopenharmony_ci /* 259962306a36Sopenharmony_ci * Note that this does not check the file descriptor for write 260062306a36Sopenharmony_ci * access. This prevents defragmenting executables that are 260162306a36Sopenharmony_ci * running and allows defrag on files open in read-only mode. 260262306a36Sopenharmony_ci */ 260362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) && 260462306a36Sopenharmony_ci inode_permission(&nop_mnt_idmap, inode, MAY_WRITE)) { 260562306a36Sopenharmony_ci ret = -EPERM; 260662306a36Sopenharmony_ci goto out; 260762306a36Sopenharmony_ci } 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci if (argp) { 261062306a36Sopenharmony_ci if (copy_from_user(&range, argp, sizeof(range))) { 261162306a36Sopenharmony_ci ret = -EFAULT; 261262306a36Sopenharmony_ci goto out; 261362306a36Sopenharmony_ci } 261462306a36Sopenharmony_ci if (range.flags & ~BTRFS_DEFRAG_RANGE_FLAGS_SUPP) { 261562306a36Sopenharmony_ci ret = -EOPNOTSUPP; 261662306a36Sopenharmony_ci goto out; 261762306a36Sopenharmony_ci } 261862306a36Sopenharmony_ci /* compression requires us to start the IO */ 261962306a36Sopenharmony_ci if ((range.flags & BTRFS_DEFRAG_RANGE_COMPRESS)) { 262062306a36Sopenharmony_ci range.flags |= BTRFS_DEFRAG_RANGE_START_IO; 262162306a36Sopenharmony_ci range.extent_thresh = (u32)-1; 262262306a36Sopenharmony_ci } 262362306a36Sopenharmony_ci } else { 262462306a36Sopenharmony_ci /* the rest are all set to zero by kzalloc */ 262562306a36Sopenharmony_ci range.len = (u64)-1; 262662306a36Sopenharmony_ci } 262762306a36Sopenharmony_ci ret = btrfs_defrag_file(file_inode(file), &file->f_ra, 262862306a36Sopenharmony_ci &range, BTRFS_OLDEST_GENERATION, 0); 262962306a36Sopenharmony_ci if (ret > 0) 263062306a36Sopenharmony_ci ret = 0; 263162306a36Sopenharmony_ci break; 263262306a36Sopenharmony_ci default: 263362306a36Sopenharmony_ci ret = -EINVAL; 263462306a36Sopenharmony_ci } 263562306a36Sopenharmony_ciout: 263662306a36Sopenharmony_ci mnt_drop_write_file(file); 263762306a36Sopenharmony_ci return ret; 263862306a36Sopenharmony_ci} 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_cistatic long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg) 264162306a36Sopenharmony_ci{ 264262306a36Sopenharmony_ci struct btrfs_ioctl_vol_args *vol_args; 264362306a36Sopenharmony_ci bool restore_op = false; 264462306a36Sopenharmony_ci int ret; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 264762306a36Sopenharmony_ci return -EPERM; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) { 265062306a36Sopenharmony_ci btrfs_err(fs_info, "device add not supported on extent tree v2 yet"); 265162306a36Sopenharmony_ci return -EINVAL; 265262306a36Sopenharmony_ci } 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_ADD)) { 265562306a36Sopenharmony_ci if (!btrfs_exclop_start_try_lock(fs_info, BTRFS_EXCLOP_DEV_ADD)) 265662306a36Sopenharmony_ci return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci /* 265962306a36Sopenharmony_ci * We can do the device add because we have a paused balanced, 266062306a36Sopenharmony_ci * change the exclusive op type and remember we should bring 266162306a36Sopenharmony_ci * back the paused balance 266262306a36Sopenharmony_ci */ 266362306a36Sopenharmony_ci fs_info->exclusive_operation = BTRFS_EXCLOP_DEV_ADD; 266462306a36Sopenharmony_ci btrfs_exclop_start_unlock(fs_info); 266562306a36Sopenharmony_ci restore_op = true; 266662306a36Sopenharmony_ci } 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci vol_args = memdup_user(arg, sizeof(*vol_args)); 266962306a36Sopenharmony_ci if (IS_ERR(vol_args)) { 267062306a36Sopenharmony_ci ret = PTR_ERR(vol_args); 267162306a36Sopenharmony_ci goto out; 267262306a36Sopenharmony_ci } 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; 267562306a36Sopenharmony_ci ret = btrfs_init_new_device(fs_info, vol_args->name); 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci if (!ret) 267862306a36Sopenharmony_ci btrfs_info(fs_info, "disk added %s", vol_args->name); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci kfree(vol_args); 268162306a36Sopenharmony_ciout: 268262306a36Sopenharmony_ci if (restore_op) 268362306a36Sopenharmony_ci btrfs_exclop_balance(fs_info, BTRFS_EXCLOP_BALANCE_PAUSED); 268462306a36Sopenharmony_ci else 268562306a36Sopenharmony_ci btrfs_exclop_finish(fs_info); 268662306a36Sopenharmony_ci return ret; 268762306a36Sopenharmony_ci} 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_cistatic long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) 269062306a36Sopenharmony_ci{ 269162306a36Sopenharmony_ci BTRFS_DEV_LOOKUP_ARGS(args); 269262306a36Sopenharmony_ci struct inode *inode = file_inode(file); 269362306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 269462306a36Sopenharmony_ci struct btrfs_ioctl_vol_args_v2 *vol_args; 269562306a36Sopenharmony_ci struct block_device *bdev = NULL; 269662306a36Sopenharmony_ci void *holder; 269762306a36Sopenharmony_ci int ret; 269862306a36Sopenharmony_ci bool cancel = false; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 270162306a36Sopenharmony_ci return -EPERM; 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci vol_args = memdup_user(arg, sizeof(*vol_args)); 270462306a36Sopenharmony_ci if (IS_ERR(vol_args)) 270562306a36Sopenharmony_ci return PTR_ERR(vol_args); 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci if (vol_args->flags & ~BTRFS_DEVICE_REMOVE_ARGS_MASK) { 270862306a36Sopenharmony_ci ret = -EOPNOTSUPP; 270962306a36Sopenharmony_ci goto out; 271062306a36Sopenharmony_ci } 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0'; 271362306a36Sopenharmony_ci if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) { 271462306a36Sopenharmony_ci args.devid = vol_args->devid; 271562306a36Sopenharmony_ci } else if (!strcmp("cancel", vol_args->name)) { 271662306a36Sopenharmony_ci cancel = true; 271762306a36Sopenharmony_ci } else { 271862306a36Sopenharmony_ci ret = btrfs_get_dev_args_from_path(fs_info, &args, vol_args->name); 271962306a36Sopenharmony_ci if (ret) 272062306a36Sopenharmony_ci goto out; 272162306a36Sopenharmony_ci } 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci ret = mnt_want_write_file(file); 272462306a36Sopenharmony_ci if (ret) 272562306a36Sopenharmony_ci goto out; 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_DEV_REMOVE, 272862306a36Sopenharmony_ci cancel); 272962306a36Sopenharmony_ci if (ret) 273062306a36Sopenharmony_ci goto err_drop; 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci /* Exclusive operation is now claimed */ 273362306a36Sopenharmony_ci ret = btrfs_rm_device(fs_info, &args, &bdev, &holder); 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci btrfs_exclop_finish(fs_info); 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci if (!ret) { 273862306a36Sopenharmony_ci if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) 273962306a36Sopenharmony_ci btrfs_info(fs_info, "device deleted: id %llu", 274062306a36Sopenharmony_ci vol_args->devid); 274162306a36Sopenharmony_ci else 274262306a36Sopenharmony_ci btrfs_info(fs_info, "device deleted: %s", 274362306a36Sopenharmony_ci vol_args->name); 274462306a36Sopenharmony_ci } 274562306a36Sopenharmony_cierr_drop: 274662306a36Sopenharmony_ci mnt_drop_write_file(file); 274762306a36Sopenharmony_ci if (bdev) 274862306a36Sopenharmony_ci blkdev_put(bdev, holder); 274962306a36Sopenharmony_ciout: 275062306a36Sopenharmony_ci btrfs_put_dev_args_from_path(&args); 275162306a36Sopenharmony_ci kfree(vol_args); 275262306a36Sopenharmony_ci return ret; 275362306a36Sopenharmony_ci} 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_cistatic long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) 275662306a36Sopenharmony_ci{ 275762306a36Sopenharmony_ci BTRFS_DEV_LOOKUP_ARGS(args); 275862306a36Sopenharmony_ci struct inode *inode = file_inode(file); 275962306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 276062306a36Sopenharmony_ci struct btrfs_ioctl_vol_args *vol_args; 276162306a36Sopenharmony_ci struct block_device *bdev = NULL; 276262306a36Sopenharmony_ci void *holder; 276362306a36Sopenharmony_ci int ret; 276462306a36Sopenharmony_ci bool cancel = false; 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 276762306a36Sopenharmony_ci return -EPERM; 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci vol_args = memdup_user(arg, sizeof(*vol_args)); 277062306a36Sopenharmony_ci if (IS_ERR(vol_args)) 277162306a36Sopenharmony_ci return PTR_ERR(vol_args); 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; 277462306a36Sopenharmony_ci if (!strcmp("cancel", vol_args->name)) { 277562306a36Sopenharmony_ci cancel = true; 277662306a36Sopenharmony_ci } else { 277762306a36Sopenharmony_ci ret = btrfs_get_dev_args_from_path(fs_info, &args, vol_args->name); 277862306a36Sopenharmony_ci if (ret) 277962306a36Sopenharmony_ci goto out; 278062306a36Sopenharmony_ci } 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci ret = mnt_want_write_file(file); 278362306a36Sopenharmony_ci if (ret) 278462306a36Sopenharmony_ci goto out; 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_DEV_REMOVE, 278762306a36Sopenharmony_ci cancel); 278862306a36Sopenharmony_ci if (ret == 0) { 278962306a36Sopenharmony_ci ret = btrfs_rm_device(fs_info, &args, &bdev, &holder); 279062306a36Sopenharmony_ci if (!ret) 279162306a36Sopenharmony_ci btrfs_info(fs_info, "disk deleted %s", vol_args->name); 279262306a36Sopenharmony_ci btrfs_exclop_finish(fs_info); 279362306a36Sopenharmony_ci } 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci mnt_drop_write_file(file); 279662306a36Sopenharmony_ci if (bdev) 279762306a36Sopenharmony_ci blkdev_put(bdev, holder); 279862306a36Sopenharmony_ciout: 279962306a36Sopenharmony_ci btrfs_put_dev_args_from_path(&args); 280062306a36Sopenharmony_ci kfree(vol_args); 280162306a36Sopenharmony_ci return ret; 280262306a36Sopenharmony_ci} 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_cistatic long btrfs_ioctl_fs_info(struct btrfs_fs_info *fs_info, 280562306a36Sopenharmony_ci void __user *arg) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci struct btrfs_ioctl_fs_info_args *fi_args; 280862306a36Sopenharmony_ci struct btrfs_device *device; 280962306a36Sopenharmony_ci struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; 281062306a36Sopenharmony_ci u64 flags_in; 281162306a36Sopenharmony_ci int ret = 0; 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_ci fi_args = memdup_user(arg, sizeof(*fi_args)); 281462306a36Sopenharmony_ci if (IS_ERR(fi_args)) 281562306a36Sopenharmony_ci return PTR_ERR(fi_args); 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci flags_in = fi_args->flags; 281862306a36Sopenharmony_ci memset(fi_args, 0, sizeof(*fi_args)); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci rcu_read_lock(); 282162306a36Sopenharmony_ci fi_args->num_devices = fs_devices->num_devices; 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) { 282462306a36Sopenharmony_ci if (device->devid > fi_args->max_id) 282562306a36Sopenharmony_ci fi_args->max_id = device->devid; 282662306a36Sopenharmony_ci } 282762306a36Sopenharmony_ci rcu_read_unlock(); 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci memcpy(&fi_args->fsid, fs_devices->fsid, sizeof(fi_args->fsid)); 283062306a36Sopenharmony_ci fi_args->nodesize = fs_info->nodesize; 283162306a36Sopenharmony_ci fi_args->sectorsize = fs_info->sectorsize; 283262306a36Sopenharmony_ci fi_args->clone_alignment = fs_info->sectorsize; 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci if (flags_in & BTRFS_FS_INFO_FLAG_CSUM_INFO) { 283562306a36Sopenharmony_ci fi_args->csum_type = btrfs_super_csum_type(fs_info->super_copy); 283662306a36Sopenharmony_ci fi_args->csum_size = btrfs_super_csum_size(fs_info->super_copy); 283762306a36Sopenharmony_ci fi_args->flags |= BTRFS_FS_INFO_FLAG_CSUM_INFO; 283862306a36Sopenharmony_ci } 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci if (flags_in & BTRFS_FS_INFO_FLAG_GENERATION) { 284162306a36Sopenharmony_ci fi_args->generation = fs_info->generation; 284262306a36Sopenharmony_ci fi_args->flags |= BTRFS_FS_INFO_FLAG_GENERATION; 284362306a36Sopenharmony_ci } 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci if (flags_in & BTRFS_FS_INFO_FLAG_METADATA_UUID) { 284662306a36Sopenharmony_ci memcpy(&fi_args->metadata_uuid, fs_devices->metadata_uuid, 284762306a36Sopenharmony_ci sizeof(fi_args->metadata_uuid)); 284862306a36Sopenharmony_ci fi_args->flags |= BTRFS_FS_INFO_FLAG_METADATA_UUID; 284962306a36Sopenharmony_ci } 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci if (copy_to_user(arg, fi_args, sizeof(*fi_args))) 285262306a36Sopenharmony_ci ret = -EFAULT; 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci kfree(fi_args); 285562306a36Sopenharmony_ci return ret; 285662306a36Sopenharmony_ci} 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_cistatic long btrfs_ioctl_dev_info(struct btrfs_fs_info *fs_info, 285962306a36Sopenharmony_ci void __user *arg) 286062306a36Sopenharmony_ci{ 286162306a36Sopenharmony_ci BTRFS_DEV_LOOKUP_ARGS(args); 286262306a36Sopenharmony_ci struct btrfs_ioctl_dev_info_args *di_args; 286362306a36Sopenharmony_ci struct btrfs_device *dev; 286462306a36Sopenharmony_ci int ret = 0; 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci di_args = memdup_user(arg, sizeof(*di_args)); 286762306a36Sopenharmony_ci if (IS_ERR(di_args)) 286862306a36Sopenharmony_ci return PTR_ERR(di_args); 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci args.devid = di_args->devid; 287162306a36Sopenharmony_ci if (!btrfs_is_empty_uuid(di_args->uuid)) 287262306a36Sopenharmony_ci args.uuid = di_args->uuid; 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_ci rcu_read_lock(); 287562306a36Sopenharmony_ci dev = btrfs_find_device(fs_info->fs_devices, &args); 287662306a36Sopenharmony_ci if (!dev) { 287762306a36Sopenharmony_ci ret = -ENODEV; 287862306a36Sopenharmony_ci goto out; 287962306a36Sopenharmony_ci } 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci di_args->devid = dev->devid; 288262306a36Sopenharmony_ci di_args->bytes_used = btrfs_device_get_bytes_used(dev); 288362306a36Sopenharmony_ci di_args->total_bytes = btrfs_device_get_total_bytes(dev); 288462306a36Sopenharmony_ci memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid)); 288562306a36Sopenharmony_ci memcpy(di_args->fsid, dev->fs_devices->fsid, BTRFS_UUID_SIZE); 288662306a36Sopenharmony_ci if (dev->name) 288762306a36Sopenharmony_ci strscpy(di_args->path, btrfs_dev_name(dev), sizeof(di_args->path)); 288862306a36Sopenharmony_ci else 288962306a36Sopenharmony_ci di_args->path[0] = '\0'; 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ciout: 289262306a36Sopenharmony_ci rcu_read_unlock(); 289362306a36Sopenharmony_ci if (ret == 0 && copy_to_user(arg, di_args, sizeof(*di_args))) 289462306a36Sopenharmony_ci ret = -EFAULT; 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci kfree(di_args); 289762306a36Sopenharmony_ci return ret; 289862306a36Sopenharmony_ci} 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_cistatic long btrfs_ioctl_default_subvol(struct file *file, void __user *argp) 290162306a36Sopenharmony_ci{ 290262306a36Sopenharmony_ci struct inode *inode = file_inode(file); 290362306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 290462306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 290562306a36Sopenharmony_ci struct btrfs_root *new_root; 290662306a36Sopenharmony_ci struct btrfs_dir_item *di; 290762306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 290862306a36Sopenharmony_ci struct btrfs_path *path = NULL; 290962306a36Sopenharmony_ci struct btrfs_disk_key disk_key; 291062306a36Sopenharmony_ci struct fscrypt_str name = FSTR_INIT("default", 7); 291162306a36Sopenharmony_ci u64 objectid = 0; 291262306a36Sopenharmony_ci u64 dir_id; 291362306a36Sopenharmony_ci int ret; 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 291662306a36Sopenharmony_ci return -EPERM; 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci ret = mnt_want_write_file(file); 291962306a36Sopenharmony_ci if (ret) 292062306a36Sopenharmony_ci return ret; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci if (copy_from_user(&objectid, argp, sizeof(objectid))) { 292362306a36Sopenharmony_ci ret = -EFAULT; 292462306a36Sopenharmony_ci goto out; 292562306a36Sopenharmony_ci } 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci if (!objectid) 292862306a36Sopenharmony_ci objectid = BTRFS_FS_TREE_OBJECTID; 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci new_root = btrfs_get_fs_root(fs_info, objectid, true); 293162306a36Sopenharmony_ci if (IS_ERR(new_root)) { 293262306a36Sopenharmony_ci ret = PTR_ERR(new_root); 293362306a36Sopenharmony_ci goto out; 293462306a36Sopenharmony_ci } 293562306a36Sopenharmony_ci if (!is_fstree(new_root->root_key.objectid)) { 293662306a36Sopenharmony_ci ret = -ENOENT; 293762306a36Sopenharmony_ci goto out_free; 293862306a36Sopenharmony_ci } 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci path = btrfs_alloc_path(); 294162306a36Sopenharmony_ci if (!path) { 294262306a36Sopenharmony_ci ret = -ENOMEM; 294362306a36Sopenharmony_ci goto out_free; 294462306a36Sopenharmony_ci } 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 1); 294762306a36Sopenharmony_ci if (IS_ERR(trans)) { 294862306a36Sopenharmony_ci ret = PTR_ERR(trans); 294962306a36Sopenharmony_ci goto out_free; 295062306a36Sopenharmony_ci } 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci dir_id = btrfs_super_root_dir(fs_info->super_copy); 295362306a36Sopenharmony_ci di = btrfs_lookup_dir_item(trans, fs_info->tree_root, path, 295462306a36Sopenharmony_ci dir_id, &name, 1); 295562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(di)) { 295662306a36Sopenharmony_ci btrfs_release_path(path); 295762306a36Sopenharmony_ci btrfs_end_transaction(trans); 295862306a36Sopenharmony_ci btrfs_err(fs_info, 295962306a36Sopenharmony_ci "Umm, you don't have the default diritem, this isn't going to work"); 296062306a36Sopenharmony_ci ret = -ENOENT; 296162306a36Sopenharmony_ci goto out_free; 296262306a36Sopenharmony_ci } 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci btrfs_cpu_key_to_disk(&disk_key, &new_root->root_key); 296562306a36Sopenharmony_ci btrfs_set_dir_item_key(path->nodes[0], di, &disk_key); 296662306a36Sopenharmony_ci btrfs_mark_buffer_dirty(trans, path->nodes[0]); 296762306a36Sopenharmony_ci btrfs_release_path(path); 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci btrfs_set_fs_incompat(fs_info, DEFAULT_SUBVOL); 297062306a36Sopenharmony_ci btrfs_end_transaction(trans); 297162306a36Sopenharmony_ciout_free: 297262306a36Sopenharmony_ci btrfs_put_root(new_root); 297362306a36Sopenharmony_ci btrfs_free_path(path); 297462306a36Sopenharmony_ciout: 297562306a36Sopenharmony_ci mnt_drop_write_file(file); 297662306a36Sopenharmony_ci return ret; 297762306a36Sopenharmony_ci} 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_cistatic void get_block_group_info(struct list_head *groups_list, 298062306a36Sopenharmony_ci struct btrfs_ioctl_space_info *space) 298162306a36Sopenharmony_ci{ 298262306a36Sopenharmony_ci struct btrfs_block_group *block_group; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci space->total_bytes = 0; 298562306a36Sopenharmony_ci space->used_bytes = 0; 298662306a36Sopenharmony_ci space->flags = 0; 298762306a36Sopenharmony_ci list_for_each_entry(block_group, groups_list, list) { 298862306a36Sopenharmony_ci space->flags = block_group->flags; 298962306a36Sopenharmony_ci space->total_bytes += block_group->length; 299062306a36Sopenharmony_ci space->used_bytes += block_group->used; 299162306a36Sopenharmony_ci } 299262306a36Sopenharmony_ci} 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_cistatic long btrfs_ioctl_space_info(struct btrfs_fs_info *fs_info, 299562306a36Sopenharmony_ci void __user *arg) 299662306a36Sopenharmony_ci{ 299762306a36Sopenharmony_ci struct btrfs_ioctl_space_args space_args = { 0 }; 299862306a36Sopenharmony_ci struct btrfs_ioctl_space_info space; 299962306a36Sopenharmony_ci struct btrfs_ioctl_space_info *dest; 300062306a36Sopenharmony_ci struct btrfs_ioctl_space_info *dest_orig; 300162306a36Sopenharmony_ci struct btrfs_ioctl_space_info __user *user_dest; 300262306a36Sopenharmony_ci struct btrfs_space_info *info; 300362306a36Sopenharmony_ci static const u64 types[] = { 300462306a36Sopenharmony_ci BTRFS_BLOCK_GROUP_DATA, 300562306a36Sopenharmony_ci BTRFS_BLOCK_GROUP_SYSTEM, 300662306a36Sopenharmony_ci BTRFS_BLOCK_GROUP_METADATA, 300762306a36Sopenharmony_ci BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA 300862306a36Sopenharmony_ci }; 300962306a36Sopenharmony_ci int num_types = 4; 301062306a36Sopenharmony_ci int alloc_size; 301162306a36Sopenharmony_ci int ret = 0; 301262306a36Sopenharmony_ci u64 slot_count = 0; 301362306a36Sopenharmony_ci int i, c; 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci if (copy_from_user(&space_args, 301662306a36Sopenharmony_ci (struct btrfs_ioctl_space_args __user *)arg, 301762306a36Sopenharmony_ci sizeof(space_args))) 301862306a36Sopenharmony_ci return -EFAULT; 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci for (i = 0; i < num_types; i++) { 302162306a36Sopenharmony_ci struct btrfs_space_info *tmp; 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_ci info = NULL; 302462306a36Sopenharmony_ci list_for_each_entry(tmp, &fs_info->space_info, list) { 302562306a36Sopenharmony_ci if (tmp->flags == types[i]) { 302662306a36Sopenharmony_ci info = tmp; 302762306a36Sopenharmony_ci break; 302862306a36Sopenharmony_ci } 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci if (!info) 303262306a36Sopenharmony_ci continue; 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci down_read(&info->groups_sem); 303562306a36Sopenharmony_ci for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) { 303662306a36Sopenharmony_ci if (!list_empty(&info->block_groups[c])) 303762306a36Sopenharmony_ci slot_count++; 303862306a36Sopenharmony_ci } 303962306a36Sopenharmony_ci up_read(&info->groups_sem); 304062306a36Sopenharmony_ci } 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci /* 304362306a36Sopenharmony_ci * Global block reserve, exported as a space_info 304462306a36Sopenharmony_ci */ 304562306a36Sopenharmony_ci slot_count++; 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci /* space_slots == 0 means they are asking for a count */ 304862306a36Sopenharmony_ci if (space_args.space_slots == 0) { 304962306a36Sopenharmony_ci space_args.total_spaces = slot_count; 305062306a36Sopenharmony_ci goto out; 305162306a36Sopenharmony_ci } 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci slot_count = min_t(u64, space_args.space_slots, slot_count); 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci alloc_size = sizeof(*dest) * slot_count; 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_ci /* we generally have at most 6 or so space infos, one for each raid 305862306a36Sopenharmony_ci * level. So, a whole page should be more than enough for everyone 305962306a36Sopenharmony_ci */ 306062306a36Sopenharmony_ci if (alloc_size > PAGE_SIZE) 306162306a36Sopenharmony_ci return -ENOMEM; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci space_args.total_spaces = 0; 306462306a36Sopenharmony_ci dest = kmalloc(alloc_size, GFP_KERNEL); 306562306a36Sopenharmony_ci if (!dest) 306662306a36Sopenharmony_ci return -ENOMEM; 306762306a36Sopenharmony_ci dest_orig = dest; 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci /* now we have a buffer to copy into */ 307062306a36Sopenharmony_ci for (i = 0; i < num_types; i++) { 307162306a36Sopenharmony_ci struct btrfs_space_info *tmp; 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci if (!slot_count) 307462306a36Sopenharmony_ci break; 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci info = NULL; 307762306a36Sopenharmony_ci list_for_each_entry(tmp, &fs_info->space_info, list) { 307862306a36Sopenharmony_ci if (tmp->flags == types[i]) { 307962306a36Sopenharmony_ci info = tmp; 308062306a36Sopenharmony_ci break; 308162306a36Sopenharmony_ci } 308262306a36Sopenharmony_ci } 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci if (!info) 308562306a36Sopenharmony_ci continue; 308662306a36Sopenharmony_ci down_read(&info->groups_sem); 308762306a36Sopenharmony_ci for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) { 308862306a36Sopenharmony_ci if (!list_empty(&info->block_groups[c])) { 308962306a36Sopenharmony_ci get_block_group_info(&info->block_groups[c], 309062306a36Sopenharmony_ci &space); 309162306a36Sopenharmony_ci memcpy(dest, &space, sizeof(space)); 309262306a36Sopenharmony_ci dest++; 309362306a36Sopenharmony_ci space_args.total_spaces++; 309462306a36Sopenharmony_ci slot_count--; 309562306a36Sopenharmony_ci } 309662306a36Sopenharmony_ci if (!slot_count) 309762306a36Sopenharmony_ci break; 309862306a36Sopenharmony_ci } 309962306a36Sopenharmony_ci up_read(&info->groups_sem); 310062306a36Sopenharmony_ci } 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci /* 310362306a36Sopenharmony_ci * Add global block reserve 310462306a36Sopenharmony_ci */ 310562306a36Sopenharmony_ci if (slot_count) { 310662306a36Sopenharmony_ci struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv; 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci spin_lock(&block_rsv->lock); 310962306a36Sopenharmony_ci space.total_bytes = block_rsv->size; 311062306a36Sopenharmony_ci space.used_bytes = block_rsv->size - block_rsv->reserved; 311162306a36Sopenharmony_ci spin_unlock(&block_rsv->lock); 311262306a36Sopenharmony_ci space.flags = BTRFS_SPACE_INFO_GLOBAL_RSV; 311362306a36Sopenharmony_ci memcpy(dest, &space, sizeof(space)); 311462306a36Sopenharmony_ci space_args.total_spaces++; 311562306a36Sopenharmony_ci } 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci user_dest = (struct btrfs_ioctl_space_info __user *) 311862306a36Sopenharmony_ci (arg + sizeof(struct btrfs_ioctl_space_args)); 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci if (copy_to_user(user_dest, dest_orig, alloc_size)) 312162306a36Sopenharmony_ci ret = -EFAULT; 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci kfree(dest_orig); 312462306a36Sopenharmony_ciout: 312562306a36Sopenharmony_ci if (ret == 0 && copy_to_user(arg, &space_args, sizeof(space_args))) 312662306a36Sopenharmony_ci ret = -EFAULT; 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ci return ret; 312962306a36Sopenharmony_ci} 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_cistatic noinline long btrfs_ioctl_start_sync(struct btrfs_root *root, 313262306a36Sopenharmony_ci void __user *argp) 313362306a36Sopenharmony_ci{ 313462306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 313562306a36Sopenharmony_ci u64 transid; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci /* 313862306a36Sopenharmony_ci * Start orphan cleanup here for the given root in case it hasn't been 313962306a36Sopenharmony_ci * started already by other means. Errors are handled in the other 314062306a36Sopenharmony_ci * functions during transaction commit. 314162306a36Sopenharmony_ci */ 314262306a36Sopenharmony_ci btrfs_orphan_cleanup(root); 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci trans = btrfs_attach_transaction_barrier(root); 314562306a36Sopenharmony_ci if (IS_ERR(trans)) { 314662306a36Sopenharmony_ci if (PTR_ERR(trans) != -ENOENT) 314762306a36Sopenharmony_ci return PTR_ERR(trans); 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci /* No running transaction, don't bother */ 315062306a36Sopenharmony_ci transid = root->fs_info->last_trans_committed; 315162306a36Sopenharmony_ci goto out; 315262306a36Sopenharmony_ci } 315362306a36Sopenharmony_ci transid = trans->transid; 315462306a36Sopenharmony_ci btrfs_commit_transaction_async(trans); 315562306a36Sopenharmony_ciout: 315662306a36Sopenharmony_ci if (argp) 315762306a36Sopenharmony_ci if (copy_to_user(argp, &transid, sizeof(transid))) 315862306a36Sopenharmony_ci return -EFAULT; 315962306a36Sopenharmony_ci return 0; 316062306a36Sopenharmony_ci} 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_cistatic noinline long btrfs_ioctl_wait_sync(struct btrfs_fs_info *fs_info, 316362306a36Sopenharmony_ci void __user *argp) 316462306a36Sopenharmony_ci{ 316562306a36Sopenharmony_ci /* By default wait for the current transaction. */ 316662306a36Sopenharmony_ci u64 transid = 0; 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci if (argp) 316962306a36Sopenharmony_ci if (copy_from_user(&transid, argp, sizeof(transid))) 317062306a36Sopenharmony_ci return -EFAULT; 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci return btrfs_wait_for_commit(fs_info, transid); 317362306a36Sopenharmony_ci} 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_cistatic long btrfs_ioctl_scrub(struct file *file, void __user *arg) 317662306a36Sopenharmony_ci{ 317762306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(file_inode(file)->i_sb); 317862306a36Sopenharmony_ci struct btrfs_ioctl_scrub_args *sa; 317962306a36Sopenharmony_ci int ret; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 318262306a36Sopenharmony_ci return -EPERM; 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) { 318562306a36Sopenharmony_ci btrfs_err(fs_info, "scrub is not supported on extent tree v2 yet"); 318662306a36Sopenharmony_ci return -EINVAL; 318762306a36Sopenharmony_ci } 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 319062306a36Sopenharmony_ci if (IS_ERR(sa)) 319162306a36Sopenharmony_ci return PTR_ERR(sa); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci if (sa->flags & ~BTRFS_SCRUB_SUPPORTED_FLAGS) { 319462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 319562306a36Sopenharmony_ci goto out; 319662306a36Sopenharmony_ci } 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci if (!(sa->flags & BTRFS_SCRUB_READONLY)) { 319962306a36Sopenharmony_ci ret = mnt_want_write_file(file); 320062306a36Sopenharmony_ci if (ret) 320162306a36Sopenharmony_ci goto out; 320262306a36Sopenharmony_ci } 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci ret = btrfs_scrub_dev(fs_info, sa->devid, sa->start, sa->end, 320562306a36Sopenharmony_ci &sa->progress, sa->flags & BTRFS_SCRUB_READONLY, 320662306a36Sopenharmony_ci 0); 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci /* 320962306a36Sopenharmony_ci * Copy scrub args to user space even if btrfs_scrub_dev() returned an 321062306a36Sopenharmony_ci * error. This is important as it allows user space to know how much 321162306a36Sopenharmony_ci * progress scrub has done. For example, if scrub is canceled we get 321262306a36Sopenharmony_ci * -ECANCELED from btrfs_scrub_dev() and return that error back to user 321362306a36Sopenharmony_ci * space. Later user space can inspect the progress from the structure 321462306a36Sopenharmony_ci * btrfs_ioctl_scrub_args and resume scrub from where it left off 321562306a36Sopenharmony_ci * previously (btrfs-progs does this). 321662306a36Sopenharmony_ci * If we fail to copy the btrfs_ioctl_scrub_args structure to user space 321762306a36Sopenharmony_ci * then return -EFAULT to signal the structure was not copied or it may 321862306a36Sopenharmony_ci * be corrupt and unreliable due to a partial copy. 321962306a36Sopenharmony_ci */ 322062306a36Sopenharmony_ci if (copy_to_user(arg, sa, sizeof(*sa))) 322162306a36Sopenharmony_ci ret = -EFAULT; 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_ci if (!(sa->flags & BTRFS_SCRUB_READONLY)) 322462306a36Sopenharmony_ci mnt_drop_write_file(file); 322562306a36Sopenharmony_ciout: 322662306a36Sopenharmony_ci kfree(sa); 322762306a36Sopenharmony_ci return ret; 322862306a36Sopenharmony_ci} 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_cistatic long btrfs_ioctl_scrub_cancel(struct btrfs_fs_info *fs_info) 323162306a36Sopenharmony_ci{ 323262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 323362306a36Sopenharmony_ci return -EPERM; 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_ci return btrfs_scrub_cancel(fs_info); 323662306a36Sopenharmony_ci} 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_cistatic long btrfs_ioctl_scrub_progress(struct btrfs_fs_info *fs_info, 323962306a36Sopenharmony_ci void __user *arg) 324062306a36Sopenharmony_ci{ 324162306a36Sopenharmony_ci struct btrfs_ioctl_scrub_args *sa; 324262306a36Sopenharmony_ci int ret; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 324562306a36Sopenharmony_ci return -EPERM; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 324862306a36Sopenharmony_ci if (IS_ERR(sa)) 324962306a36Sopenharmony_ci return PTR_ERR(sa); 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci ret = btrfs_scrub_progress(fs_info, sa->devid, &sa->progress); 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci if (ret == 0 && copy_to_user(arg, sa, sizeof(*sa))) 325462306a36Sopenharmony_ci ret = -EFAULT; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci kfree(sa); 325762306a36Sopenharmony_ci return ret; 325862306a36Sopenharmony_ci} 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_cistatic long btrfs_ioctl_get_dev_stats(struct btrfs_fs_info *fs_info, 326162306a36Sopenharmony_ci void __user *arg) 326262306a36Sopenharmony_ci{ 326362306a36Sopenharmony_ci struct btrfs_ioctl_get_dev_stats *sa; 326462306a36Sopenharmony_ci int ret; 326562306a36Sopenharmony_ci 326662306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 326762306a36Sopenharmony_ci if (IS_ERR(sa)) 326862306a36Sopenharmony_ci return PTR_ERR(sa); 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci if ((sa->flags & BTRFS_DEV_STATS_RESET) && !capable(CAP_SYS_ADMIN)) { 327162306a36Sopenharmony_ci kfree(sa); 327262306a36Sopenharmony_ci return -EPERM; 327362306a36Sopenharmony_ci } 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci ret = btrfs_get_dev_stats(fs_info, sa); 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci if (ret == 0 && copy_to_user(arg, sa, sizeof(*sa))) 327862306a36Sopenharmony_ci ret = -EFAULT; 327962306a36Sopenharmony_ci 328062306a36Sopenharmony_ci kfree(sa); 328162306a36Sopenharmony_ci return ret; 328262306a36Sopenharmony_ci} 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_cistatic long btrfs_ioctl_dev_replace(struct btrfs_fs_info *fs_info, 328562306a36Sopenharmony_ci void __user *arg) 328662306a36Sopenharmony_ci{ 328762306a36Sopenharmony_ci struct btrfs_ioctl_dev_replace_args *p; 328862306a36Sopenharmony_ci int ret; 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 329162306a36Sopenharmony_ci return -EPERM; 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) { 329462306a36Sopenharmony_ci btrfs_err(fs_info, "device replace not supported on extent tree v2 yet"); 329562306a36Sopenharmony_ci return -EINVAL; 329662306a36Sopenharmony_ci } 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci p = memdup_user(arg, sizeof(*p)); 329962306a36Sopenharmony_ci if (IS_ERR(p)) 330062306a36Sopenharmony_ci return PTR_ERR(p); 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci switch (p->cmd) { 330362306a36Sopenharmony_ci case BTRFS_IOCTL_DEV_REPLACE_CMD_START: 330462306a36Sopenharmony_ci if (sb_rdonly(fs_info->sb)) { 330562306a36Sopenharmony_ci ret = -EROFS; 330662306a36Sopenharmony_ci goto out; 330762306a36Sopenharmony_ci } 330862306a36Sopenharmony_ci if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_REPLACE)) { 330962306a36Sopenharmony_ci ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; 331062306a36Sopenharmony_ci } else { 331162306a36Sopenharmony_ci ret = btrfs_dev_replace_by_ioctl(fs_info, p); 331262306a36Sopenharmony_ci btrfs_exclop_finish(fs_info); 331362306a36Sopenharmony_ci } 331462306a36Sopenharmony_ci break; 331562306a36Sopenharmony_ci case BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS: 331662306a36Sopenharmony_ci btrfs_dev_replace_status(fs_info, p); 331762306a36Sopenharmony_ci ret = 0; 331862306a36Sopenharmony_ci break; 331962306a36Sopenharmony_ci case BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL: 332062306a36Sopenharmony_ci p->result = btrfs_dev_replace_cancel(fs_info); 332162306a36Sopenharmony_ci ret = 0; 332262306a36Sopenharmony_ci break; 332362306a36Sopenharmony_ci default: 332462306a36Sopenharmony_ci ret = -EINVAL; 332562306a36Sopenharmony_ci break; 332662306a36Sopenharmony_ci } 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci if ((ret == 0 || ret == -ECANCELED) && copy_to_user(arg, p, sizeof(*p))) 332962306a36Sopenharmony_ci ret = -EFAULT; 333062306a36Sopenharmony_ciout: 333162306a36Sopenharmony_ci kfree(p); 333262306a36Sopenharmony_ci return ret; 333362306a36Sopenharmony_ci} 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_cistatic long btrfs_ioctl_ino_to_path(struct btrfs_root *root, void __user *arg) 333662306a36Sopenharmony_ci{ 333762306a36Sopenharmony_ci int ret = 0; 333862306a36Sopenharmony_ci int i; 333962306a36Sopenharmony_ci u64 rel_ptr; 334062306a36Sopenharmony_ci int size; 334162306a36Sopenharmony_ci struct btrfs_ioctl_ino_path_args *ipa = NULL; 334262306a36Sopenharmony_ci struct inode_fs_paths *ipath = NULL; 334362306a36Sopenharmony_ci struct btrfs_path *path; 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci if (!capable(CAP_DAC_READ_SEARCH)) 334662306a36Sopenharmony_ci return -EPERM; 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci path = btrfs_alloc_path(); 334962306a36Sopenharmony_ci if (!path) { 335062306a36Sopenharmony_ci ret = -ENOMEM; 335162306a36Sopenharmony_ci goto out; 335262306a36Sopenharmony_ci } 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci ipa = memdup_user(arg, sizeof(*ipa)); 335562306a36Sopenharmony_ci if (IS_ERR(ipa)) { 335662306a36Sopenharmony_ci ret = PTR_ERR(ipa); 335762306a36Sopenharmony_ci ipa = NULL; 335862306a36Sopenharmony_ci goto out; 335962306a36Sopenharmony_ci } 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci size = min_t(u32, ipa->size, 4096); 336262306a36Sopenharmony_ci ipath = init_ipath(size, root, path); 336362306a36Sopenharmony_ci if (IS_ERR(ipath)) { 336462306a36Sopenharmony_ci ret = PTR_ERR(ipath); 336562306a36Sopenharmony_ci ipath = NULL; 336662306a36Sopenharmony_ci goto out; 336762306a36Sopenharmony_ci } 336862306a36Sopenharmony_ci 336962306a36Sopenharmony_ci ret = paths_from_inode(ipa->inum, ipath); 337062306a36Sopenharmony_ci if (ret < 0) 337162306a36Sopenharmony_ci goto out; 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci for (i = 0; i < ipath->fspath->elem_cnt; ++i) { 337462306a36Sopenharmony_ci rel_ptr = ipath->fspath->val[i] - 337562306a36Sopenharmony_ci (u64)(unsigned long)ipath->fspath->val; 337662306a36Sopenharmony_ci ipath->fspath->val[i] = rel_ptr; 337762306a36Sopenharmony_ci } 337862306a36Sopenharmony_ci 337962306a36Sopenharmony_ci btrfs_free_path(path); 338062306a36Sopenharmony_ci path = NULL; 338162306a36Sopenharmony_ci ret = copy_to_user((void __user *)(unsigned long)ipa->fspath, 338262306a36Sopenharmony_ci ipath->fspath, size); 338362306a36Sopenharmony_ci if (ret) { 338462306a36Sopenharmony_ci ret = -EFAULT; 338562306a36Sopenharmony_ci goto out; 338662306a36Sopenharmony_ci } 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ciout: 338962306a36Sopenharmony_ci btrfs_free_path(path); 339062306a36Sopenharmony_ci free_ipath(ipath); 339162306a36Sopenharmony_ci kfree(ipa); 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci return ret; 339462306a36Sopenharmony_ci} 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_cistatic long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info, 339762306a36Sopenharmony_ci void __user *arg, int version) 339862306a36Sopenharmony_ci{ 339962306a36Sopenharmony_ci int ret = 0; 340062306a36Sopenharmony_ci int size; 340162306a36Sopenharmony_ci struct btrfs_ioctl_logical_ino_args *loi; 340262306a36Sopenharmony_ci struct btrfs_data_container *inodes = NULL; 340362306a36Sopenharmony_ci struct btrfs_path *path = NULL; 340462306a36Sopenharmony_ci bool ignore_offset; 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 340762306a36Sopenharmony_ci return -EPERM; 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci loi = memdup_user(arg, sizeof(*loi)); 341062306a36Sopenharmony_ci if (IS_ERR(loi)) 341162306a36Sopenharmony_ci return PTR_ERR(loi); 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci if (version == 1) { 341462306a36Sopenharmony_ci ignore_offset = false; 341562306a36Sopenharmony_ci size = min_t(u32, loi->size, SZ_64K); 341662306a36Sopenharmony_ci } else { 341762306a36Sopenharmony_ci /* All reserved bits must be 0 for now */ 341862306a36Sopenharmony_ci if (memchr_inv(loi->reserved, 0, sizeof(loi->reserved))) { 341962306a36Sopenharmony_ci ret = -EINVAL; 342062306a36Sopenharmony_ci goto out_loi; 342162306a36Sopenharmony_ci } 342262306a36Sopenharmony_ci /* Only accept flags we have defined so far */ 342362306a36Sopenharmony_ci if (loi->flags & ~(BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET)) { 342462306a36Sopenharmony_ci ret = -EINVAL; 342562306a36Sopenharmony_ci goto out_loi; 342662306a36Sopenharmony_ci } 342762306a36Sopenharmony_ci ignore_offset = loi->flags & BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET; 342862306a36Sopenharmony_ci size = min_t(u32, loi->size, SZ_16M); 342962306a36Sopenharmony_ci } 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci inodes = init_data_container(size); 343262306a36Sopenharmony_ci if (IS_ERR(inodes)) { 343362306a36Sopenharmony_ci ret = PTR_ERR(inodes); 343462306a36Sopenharmony_ci goto out_loi; 343562306a36Sopenharmony_ci } 343662306a36Sopenharmony_ci 343762306a36Sopenharmony_ci path = btrfs_alloc_path(); 343862306a36Sopenharmony_ci if (!path) { 343962306a36Sopenharmony_ci ret = -ENOMEM; 344062306a36Sopenharmony_ci goto out; 344162306a36Sopenharmony_ci } 344262306a36Sopenharmony_ci ret = iterate_inodes_from_logical(loi->logical, fs_info, path, 344362306a36Sopenharmony_ci inodes, ignore_offset); 344462306a36Sopenharmony_ci btrfs_free_path(path); 344562306a36Sopenharmony_ci if (ret == -EINVAL) 344662306a36Sopenharmony_ci ret = -ENOENT; 344762306a36Sopenharmony_ci if (ret < 0) 344862306a36Sopenharmony_ci goto out; 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci ret = copy_to_user((void __user *)(unsigned long)loi->inodes, inodes, 345162306a36Sopenharmony_ci size); 345262306a36Sopenharmony_ci if (ret) 345362306a36Sopenharmony_ci ret = -EFAULT; 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_ciout: 345662306a36Sopenharmony_ci kvfree(inodes); 345762306a36Sopenharmony_ciout_loi: 345862306a36Sopenharmony_ci kfree(loi); 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci return ret; 346162306a36Sopenharmony_ci} 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_civoid btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info, 346462306a36Sopenharmony_ci struct btrfs_ioctl_balance_args *bargs) 346562306a36Sopenharmony_ci{ 346662306a36Sopenharmony_ci struct btrfs_balance_control *bctl = fs_info->balance_ctl; 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci bargs->flags = bctl->flags; 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_ci if (test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) 347162306a36Sopenharmony_ci bargs->state |= BTRFS_BALANCE_STATE_RUNNING; 347262306a36Sopenharmony_ci if (atomic_read(&fs_info->balance_pause_req)) 347362306a36Sopenharmony_ci bargs->state |= BTRFS_BALANCE_STATE_PAUSE_REQ; 347462306a36Sopenharmony_ci if (atomic_read(&fs_info->balance_cancel_req)) 347562306a36Sopenharmony_ci bargs->state |= BTRFS_BALANCE_STATE_CANCEL_REQ; 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci memcpy(&bargs->data, &bctl->data, sizeof(bargs->data)); 347862306a36Sopenharmony_ci memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta)); 347962306a36Sopenharmony_ci memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys)); 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci spin_lock(&fs_info->balance_lock); 348262306a36Sopenharmony_ci memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); 348362306a36Sopenharmony_ci spin_unlock(&fs_info->balance_lock); 348462306a36Sopenharmony_ci} 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_ci/* 348762306a36Sopenharmony_ci * Try to acquire fs_info::balance_mutex as well as set BTRFS_EXLCOP_BALANCE as 348862306a36Sopenharmony_ci * required. 348962306a36Sopenharmony_ci * 349062306a36Sopenharmony_ci * @fs_info: the filesystem 349162306a36Sopenharmony_ci * @excl_acquired: ptr to boolean value which is set to false in case balance 349262306a36Sopenharmony_ci * is being resumed 349362306a36Sopenharmony_ci * 349462306a36Sopenharmony_ci * Return 0 on success in which case both fs_info::balance is acquired as well 349562306a36Sopenharmony_ci * as exclusive ops are blocked. In case of failure return an error code. 349662306a36Sopenharmony_ci */ 349762306a36Sopenharmony_cistatic int btrfs_try_lock_balance(struct btrfs_fs_info *fs_info, bool *excl_acquired) 349862306a36Sopenharmony_ci{ 349962306a36Sopenharmony_ci int ret; 350062306a36Sopenharmony_ci 350162306a36Sopenharmony_ci /* 350262306a36Sopenharmony_ci * Exclusive operation is locked. Three possibilities: 350362306a36Sopenharmony_ci * (1) some other op is running 350462306a36Sopenharmony_ci * (2) balance is running 350562306a36Sopenharmony_ci * (3) balance is paused -- special case (think resume) 350662306a36Sopenharmony_ci */ 350762306a36Sopenharmony_ci while (1) { 350862306a36Sopenharmony_ci if (btrfs_exclop_start(fs_info, BTRFS_EXCLOP_BALANCE)) { 350962306a36Sopenharmony_ci *excl_acquired = true; 351062306a36Sopenharmony_ci mutex_lock(&fs_info->balance_mutex); 351162306a36Sopenharmony_ci return 0; 351262306a36Sopenharmony_ci } 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci mutex_lock(&fs_info->balance_mutex); 351562306a36Sopenharmony_ci if (fs_info->balance_ctl) { 351662306a36Sopenharmony_ci /* This is either (2) or (3) */ 351762306a36Sopenharmony_ci if (test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) { 351862306a36Sopenharmony_ci /* This is (2) */ 351962306a36Sopenharmony_ci ret = -EINPROGRESS; 352062306a36Sopenharmony_ci goto out_failure; 352162306a36Sopenharmony_ci 352262306a36Sopenharmony_ci } else { 352362306a36Sopenharmony_ci mutex_unlock(&fs_info->balance_mutex); 352462306a36Sopenharmony_ci /* 352562306a36Sopenharmony_ci * Lock released to allow other waiters to 352662306a36Sopenharmony_ci * continue, we'll reexamine the status again. 352762306a36Sopenharmony_ci */ 352862306a36Sopenharmony_ci mutex_lock(&fs_info->balance_mutex); 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci if (fs_info->balance_ctl && 353162306a36Sopenharmony_ci !test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) { 353262306a36Sopenharmony_ci /* This is (3) */ 353362306a36Sopenharmony_ci *excl_acquired = false; 353462306a36Sopenharmony_ci return 0; 353562306a36Sopenharmony_ci } 353662306a36Sopenharmony_ci } 353762306a36Sopenharmony_ci } else { 353862306a36Sopenharmony_ci /* This is (1) */ 353962306a36Sopenharmony_ci ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; 354062306a36Sopenharmony_ci goto out_failure; 354162306a36Sopenharmony_ci } 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ci mutex_unlock(&fs_info->balance_mutex); 354462306a36Sopenharmony_ci } 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ciout_failure: 354762306a36Sopenharmony_ci mutex_unlock(&fs_info->balance_mutex); 354862306a36Sopenharmony_ci *excl_acquired = false; 354962306a36Sopenharmony_ci return ret; 355062306a36Sopenharmony_ci} 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_cistatic long btrfs_ioctl_balance(struct file *file, void __user *arg) 355362306a36Sopenharmony_ci{ 355462306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(file_inode(file))->root; 355562306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = root->fs_info; 355662306a36Sopenharmony_ci struct btrfs_ioctl_balance_args *bargs; 355762306a36Sopenharmony_ci struct btrfs_balance_control *bctl; 355862306a36Sopenharmony_ci bool need_unlock = true; 355962306a36Sopenharmony_ci int ret; 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 356262306a36Sopenharmony_ci return -EPERM; 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci ret = mnt_want_write_file(file); 356562306a36Sopenharmony_ci if (ret) 356662306a36Sopenharmony_ci return ret; 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_ci bargs = memdup_user(arg, sizeof(*bargs)); 356962306a36Sopenharmony_ci if (IS_ERR(bargs)) { 357062306a36Sopenharmony_ci ret = PTR_ERR(bargs); 357162306a36Sopenharmony_ci bargs = NULL; 357262306a36Sopenharmony_ci goto out; 357362306a36Sopenharmony_ci } 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci ret = btrfs_try_lock_balance(fs_info, &need_unlock); 357662306a36Sopenharmony_ci if (ret) 357762306a36Sopenharmony_ci goto out; 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci lockdep_assert_held(&fs_info->balance_mutex); 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_ci if (bargs->flags & BTRFS_BALANCE_RESUME) { 358262306a36Sopenharmony_ci if (!fs_info->balance_ctl) { 358362306a36Sopenharmony_ci ret = -ENOTCONN; 358462306a36Sopenharmony_ci goto out_unlock; 358562306a36Sopenharmony_ci } 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci bctl = fs_info->balance_ctl; 358862306a36Sopenharmony_ci spin_lock(&fs_info->balance_lock); 358962306a36Sopenharmony_ci bctl->flags |= BTRFS_BALANCE_RESUME; 359062306a36Sopenharmony_ci spin_unlock(&fs_info->balance_lock); 359162306a36Sopenharmony_ci btrfs_exclop_balance(fs_info, BTRFS_EXCLOP_BALANCE); 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_ci goto do_balance; 359462306a36Sopenharmony_ci } 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci if (bargs->flags & ~(BTRFS_BALANCE_ARGS_MASK | BTRFS_BALANCE_TYPE_MASK)) { 359762306a36Sopenharmony_ci ret = -EINVAL; 359862306a36Sopenharmony_ci goto out_unlock; 359962306a36Sopenharmony_ci } 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci if (fs_info->balance_ctl) { 360262306a36Sopenharmony_ci ret = -EINPROGRESS; 360362306a36Sopenharmony_ci goto out_unlock; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci bctl = kzalloc(sizeof(*bctl), GFP_KERNEL); 360762306a36Sopenharmony_ci if (!bctl) { 360862306a36Sopenharmony_ci ret = -ENOMEM; 360962306a36Sopenharmony_ci goto out_unlock; 361062306a36Sopenharmony_ci } 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci memcpy(&bctl->data, &bargs->data, sizeof(bctl->data)); 361362306a36Sopenharmony_ci memcpy(&bctl->meta, &bargs->meta, sizeof(bctl->meta)); 361462306a36Sopenharmony_ci memcpy(&bctl->sys, &bargs->sys, sizeof(bctl->sys)); 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_ci bctl->flags = bargs->flags; 361762306a36Sopenharmony_cido_balance: 361862306a36Sopenharmony_ci /* 361962306a36Sopenharmony_ci * Ownership of bctl and exclusive operation goes to btrfs_balance. 362062306a36Sopenharmony_ci * bctl is freed in reset_balance_state, or, if restriper was paused 362162306a36Sopenharmony_ci * all the way until unmount, in free_fs_info. The flag should be 362262306a36Sopenharmony_ci * cleared after reset_balance_state. 362362306a36Sopenharmony_ci */ 362462306a36Sopenharmony_ci need_unlock = false; 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_ci ret = btrfs_balance(fs_info, bctl, bargs); 362762306a36Sopenharmony_ci bctl = NULL; 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_ci if (ret == 0 || ret == -ECANCELED) { 363062306a36Sopenharmony_ci if (copy_to_user(arg, bargs, sizeof(*bargs))) 363162306a36Sopenharmony_ci ret = -EFAULT; 363262306a36Sopenharmony_ci } 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ci kfree(bctl); 363562306a36Sopenharmony_ciout_unlock: 363662306a36Sopenharmony_ci mutex_unlock(&fs_info->balance_mutex); 363762306a36Sopenharmony_ci if (need_unlock) 363862306a36Sopenharmony_ci btrfs_exclop_finish(fs_info); 363962306a36Sopenharmony_ciout: 364062306a36Sopenharmony_ci mnt_drop_write_file(file); 364162306a36Sopenharmony_ci kfree(bargs); 364262306a36Sopenharmony_ci return ret; 364362306a36Sopenharmony_ci} 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_cistatic long btrfs_ioctl_balance_ctl(struct btrfs_fs_info *fs_info, int cmd) 364662306a36Sopenharmony_ci{ 364762306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 364862306a36Sopenharmony_ci return -EPERM; 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci switch (cmd) { 365162306a36Sopenharmony_ci case BTRFS_BALANCE_CTL_PAUSE: 365262306a36Sopenharmony_ci return btrfs_pause_balance(fs_info); 365362306a36Sopenharmony_ci case BTRFS_BALANCE_CTL_CANCEL: 365462306a36Sopenharmony_ci return btrfs_cancel_balance(fs_info); 365562306a36Sopenharmony_ci } 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_ci return -EINVAL; 365862306a36Sopenharmony_ci} 365962306a36Sopenharmony_ci 366062306a36Sopenharmony_cistatic long btrfs_ioctl_balance_progress(struct btrfs_fs_info *fs_info, 366162306a36Sopenharmony_ci void __user *arg) 366262306a36Sopenharmony_ci{ 366362306a36Sopenharmony_ci struct btrfs_ioctl_balance_args *bargs; 366462306a36Sopenharmony_ci int ret = 0; 366562306a36Sopenharmony_ci 366662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 366762306a36Sopenharmony_ci return -EPERM; 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci mutex_lock(&fs_info->balance_mutex); 367062306a36Sopenharmony_ci if (!fs_info->balance_ctl) { 367162306a36Sopenharmony_ci ret = -ENOTCONN; 367262306a36Sopenharmony_ci goto out; 367362306a36Sopenharmony_ci } 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_ci bargs = kzalloc(sizeof(*bargs), GFP_KERNEL); 367662306a36Sopenharmony_ci if (!bargs) { 367762306a36Sopenharmony_ci ret = -ENOMEM; 367862306a36Sopenharmony_ci goto out; 367962306a36Sopenharmony_ci } 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_ci btrfs_update_ioctl_balance_args(fs_info, bargs); 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci if (copy_to_user(arg, bargs, sizeof(*bargs))) 368462306a36Sopenharmony_ci ret = -EFAULT; 368562306a36Sopenharmony_ci 368662306a36Sopenharmony_ci kfree(bargs); 368762306a36Sopenharmony_ciout: 368862306a36Sopenharmony_ci mutex_unlock(&fs_info->balance_mutex); 368962306a36Sopenharmony_ci return ret; 369062306a36Sopenharmony_ci} 369162306a36Sopenharmony_ci 369262306a36Sopenharmony_cistatic long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg) 369362306a36Sopenharmony_ci{ 369462306a36Sopenharmony_ci struct inode *inode = file_inode(file); 369562306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 369662306a36Sopenharmony_ci struct btrfs_ioctl_quota_ctl_args *sa; 369762306a36Sopenharmony_ci int ret; 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 370062306a36Sopenharmony_ci return -EPERM; 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci ret = mnt_want_write_file(file); 370362306a36Sopenharmony_ci if (ret) 370462306a36Sopenharmony_ci return ret; 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 370762306a36Sopenharmony_ci if (IS_ERR(sa)) { 370862306a36Sopenharmony_ci ret = PTR_ERR(sa); 370962306a36Sopenharmony_ci goto drop_write; 371062306a36Sopenharmony_ci } 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci down_write(&fs_info->subvol_sem); 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci switch (sa->cmd) { 371562306a36Sopenharmony_ci case BTRFS_QUOTA_CTL_ENABLE: 371662306a36Sopenharmony_ci ret = btrfs_quota_enable(fs_info); 371762306a36Sopenharmony_ci break; 371862306a36Sopenharmony_ci case BTRFS_QUOTA_CTL_DISABLE: 371962306a36Sopenharmony_ci ret = btrfs_quota_disable(fs_info); 372062306a36Sopenharmony_ci break; 372162306a36Sopenharmony_ci default: 372262306a36Sopenharmony_ci ret = -EINVAL; 372362306a36Sopenharmony_ci break; 372462306a36Sopenharmony_ci } 372562306a36Sopenharmony_ci 372662306a36Sopenharmony_ci kfree(sa); 372762306a36Sopenharmony_ci up_write(&fs_info->subvol_sem); 372862306a36Sopenharmony_cidrop_write: 372962306a36Sopenharmony_ci mnt_drop_write_file(file); 373062306a36Sopenharmony_ci return ret; 373162306a36Sopenharmony_ci} 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_cistatic long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg) 373462306a36Sopenharmony_ci{ 373562306a36Sopenharmony_ci struct inode *inode = file_inode(file); 373662306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 373762306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 373862306a36Sopenharmony_ci struct btrfs_ioctl_qgroup_assign_args *sa; 373962306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 374062306a36Sopenharmony_ci int ret; 374162306a36Sopenharmony_ci int err; 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 374462306a36Sopenharmony_ci return -EPERM; 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ci ret = mnt_want_write_file(file); 374762306a36Sopenharmony_ci if (ret) 374862306a36Sopenharmony_ci return ret; 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 375162306a36Sopenharmony_ci if (IS_ERR(sa)) { 375262306a36Sopenharmony_ci ret = PTR_ERR(sa); 375362306a36Sopenharmony_ci goto drop_write; 375462306a36Sopenharmony_ci } 375562306a36Sopenharmony_ci 375662306a36Sopenharmony_ci trans = btrfs_join_transaction(root); 375762306a36Sopenharmony_ci if (IS_ERR(trans)) { 375862306a36Sopenharmony_ci ret = PTR_ERR(trans); 375962306a36Sopenharmony_ci goto out; 376062306a36Sopenharmony_ci } 376162306a36Sopenharmony_ci 376262306a36Sopenharmony_ci if (sa->assign) { 376362306a36Sopenharmony_ci ret = btrfs_add_qgroup_relation(trans, sa->src, sa->dst); 376462306a36Sopenharmony_ci } else { 376562306a36Sopenharmony_ci ret = btrfs_del_qgroup_relation(trans, sa->src, sa->dst); 376662306a36Sopenharmony_ci } 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci /* update qgroup status and info */ 376962306a36Sopenharmony_ci mutex_lock(&fs_info->qgroup_ioctl_lock); 377062306a36Sopenharmony_ci err = btrfs_run_qgroups(trans); 377162306a36Sopenharmony_ci mutex_unlock(&fs_info->qgroup_ioctl_lock); 377262306a36Sopenharmony_ci if (err < 0) 377362306a36Sopenharmony_ci btrfs_handle_fs_error(fs_info, err, 377462306a36Sopenharmony_ci "failed to update qgroup status and info"); 377562306a36Sopenharmony_ci err = btrfs_end_transaction(trans); 377662306a36Sopenharmony_ci if (err && !ret) 377762306a36Sopenharmony_ci ret = err; 377862306a36Sopenharmony_ci 377962306a36Sopenharmony_ciout: 378062306a36Sopenharmony_ci kfree(sa); 378162306a36Sopenharmony_cidrop_write: 378262306a36Sopenharmony_ci mnt_drop_write_file(file); 378362306a36Sopenharmony_ci return ret; 378462306a36Sopenharmony_ci} 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_cistatic long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg) 378762306a36Sopenharmony_ci{ 378862306a36Sopenharmony_ci struct inode *inode = file_inode(file); 378962306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 379062306a36Sopenharmony_ci struct btrfs_ioctl_qgroup_create_args *sa; 379162306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 379262306a36Sopenharmony_ci int ret; 379362306a36Sopenharmony_ci int err; 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 379662306a36Sopenharmony_ci return -EPERM; 379762306a36Sopenharmony_ci 379862306a36Sopenharmony_ci ret = mnt_want_write_file(file); 379962306a36Sopenharmony_ci if (ret) 380062306a36Sopenharmony_ci return ret; 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 380362306a36Sopenharmony_ci if (IS_ERR(sa)) { 380462306a36Sopenharmony_ci ret = PTR_ERR(sa); 380562306a36Sopenharmony_ci goto drop_write; 380662306a36Sopenharmony_ci } 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci if (!sa->qgroupid) { 380962306a36Sopenharmony_ci ret = -EINVAL; 381062306a36Sopenharmony_ci goto out; 381162306a36Sopenharmony_ci } 381262306a36Sopenharmony_ci 381362306a36Sopenharmony_ci if (sa->create && is_fstree(sa->qgroupid)) { 381462306a36Sopenharmony_ci ret = -EINVAL; 381562306a36Sopenharmony_ci goto out; 381662306a36Sopenharmony_ci } 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_ci trans = btrfs_join_transaction(root); 381962306a36Sopenharmony_ci if (IS_ERR(trans)) { 382062306a36Sopenharmony_ci ret = PTR_ERR(trans); 382162306a36Sopenharmony_ci goto out; 382262306a36Sopenharmony_ci } 382362306a36Sopenharmony_ci 382462306a36Sopenharmony_ci if (sa->create) { 382562306a36Sopenharmony_ci ret = btrfs_create_qgroup(trans, sa->qgroupid); 382662306a36Sopenharmony_ci } else { 382762306a36Sopenharmony_ci ret = btrfs_remove_qgroup(trans, sa->qgroupid); 382862306a36Sopenharmony_ci } 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci err = btrfs_end_transaction(trans); 383162306a36Sopenharmony_ci if (err && !ret) 383262306a36Sopenharmony_ci ret = err; 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ciout: 383562306a36Sopenharmony_ci kfree(sa); 383662306a36Sopenharmony_cidrop_write: 383762306a36Sopenharmony_ci mnt_drop_write_file(file); 383862306a36Sopenharmony_ci return ret; 383962306a36Sopenharmony_ci} 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_cistatic long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg) 384262306a36Sopenharmony_ci{ 384362306a36Sopenharmony_ci struct inode *inode = file_inode(file); 384462306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 384562306a36Sopenharmony_ci struct btrfs_ioctl_qgroup_limit_args *sa; 384662306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 384762306a36Sopenharmony_ci int ret; 384862306a36Sopenharmony_ci int err; 384962306a36Sopenharmony_ci u64 qgroupid; 385062306a36Sopenharmony_ci 385162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 385262306a36Sopenharmony_ci return -EPERM; 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci ret = mnt_want_write_file(file); 385562306a36Sopenharmony_ci if (ret) 385662306a36Sopenharmony_ci return ret; 385762306a36Sopenharmony_ci 385862306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 385962306a36Sopenharmony_ci if (IS_ERR(sa)) { 386062306a36Sopenharmony_ci ret = PTR_ERR(sa); 386162306a36Sopenharmony_ci goto drop_write; 386262306a36Sopenharmony_ci } 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci trans = btrfs_join_transaction(root); 386562306a36Sopenharmony_ci if (IS_ERR(trans)) { 386662306a36Sopenharmony_ci ret = PTR_ERR(trans); 386762306a36Sopenharmony_ci goto out; 386862306a36Sopenharmony_ci } 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_ci qgroupid = sa->qgroupid; 387162306a36Sopenharmony_ci if (!qgroupid) { 387262306a36Sopenharmony_ci /* take the current subvol as qgroup */ 387362306a36Sopenharmony_ci qgroupid = root->root_key.objectid; 387462306a36Sopenharmony_ci } 387562306a36Sopenharmony_ci 387662306a36Sopenharmony_ci ret = btrfs_limit_qgroup(trans, qgroupid, &sa->lim); 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci err = btrfs_end_transaction(trans); 387962306a36Sopenharmony_ci if (err && !ret) 388062306a36Sopenharmony_ci ret = err; 388162306a36Sopenharmony_ci 388262306a36Sopenharmony_ciout: 388362306a36Sopenharmony_ci kfree(sa); 388462306a36Sopenharmony_cidrop_write: 388562306a36Sopenharmony_ci mnt_drop_write_file(file); 388662306a36Sopenharmony_ci return ret; 388762306a36Sopenharmony_ci} 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_cistatic long btrfs_ioctl_quota_rescan(struct file *file, void __user *arg) 389062306a36Sopenharmony_ci{ 389162306a36Sopenharmony_ci struct inode *inode = file_inode(file); 389262306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 389362306a36Sopenharmony_ci struct btrfs_ioctl_quota_rescan_args *qsa; 389462306a36Sopenharmony_ci int ret; 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 389762306a36Sopenharmony_ci return -EPERM; 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_ci ret = mnt_want_write_file(file); 390062306a36Sopenharmony_ci if (ret) 390162306a36Sopenharmony_ci return ret; 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci qsa = memdup_user(arg, sizeof(*qsa)); 390462306a36Sopenharmony_ci if (IS_ERR(qsa)) { 390562306a36Sopenharmony_ci ret = PTR_ERR(qsa); 390662306a36Sopenharmony_ci goto drop_write; 390762306a36Sopenharmony_ci } 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci if (qsa->flags) { 391062306a36Sopenharmony_ci ret = -EINVAL; 391162306a36Sopenharmony_ci goto out; 391262306a36Sopenharmony_ci } 391362306a36Sopenharmony_ci 391462306a36Sopenharmony_ci ret = btrfs_qgroup_rescan(fs_info); 391562306a36Sopenharmony_ci 391662306a36Sopenharmony_ciout: 391762306a36Sopenharmony_ci kfree(qsa); 391862306a36Sopenharmony_cidrop_write: 391962306a36Sopenharmony_ci mnt_drop_write_file(file); 392062306a36Sopenharmony_ci return ret; 392162306a36Sopenharmony_ci} 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_cistatic long btrfs_ioctl_quota_rescan_status(struct btrfs_fs_info *fs_info, 392462306a36Sopenharmony_ci void __user *arg) 392562306a36Sopenharmony_ci{ 392662306a36Sopenharmony_ci struct btrfs_ioctl_quota_rescan_args qsa = {0}; 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 392962306a36Sopenharmony_ci return -EPERM; 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_ci if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) { 393262306a36Sopenharmony_ci qsa.flags = 1; 393362306a36Sopenharmony_ci qsa.progress = fs_info->qgroup_rescan_progress.objectid; 393462306a36Sopenharmony_ci } 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci if (copy_to_user(arg, &qsa, sizeof(qsa))) 393762306a36Sopenharmony_ci return -EFAULT; 393862306a36Sopenharmony_ci 393962306a36Sopenharmony_ci return 0; 394062306a36Sopenharmony_ci} 394162306a36Sopenharmony_ci 394262306a36Sopenharmony_cistatic long btrfs_ioctl_quota_rescan_wait(struct btrfs_fs_info *fs_info, 394362306a36Sopenharmony_ci void __user *arg) 394462306a36Sopenharmony_ci{ 394562306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 394662306a36Sopenharmony_ci return -EPERM; 394762306a36Sopenharmony_ci 394862306a36Sopenharmony_ci return btrfs_qgroup_wait_for_completion(fs_info, true); 394962306a36Sopenharmony_ci} 395062306a36Sopenharmony_ci 395162306a36Sopenharmony_cistatic long _btrfs_ioctl_set_received_subvol(struct file *file, 395262306a36Sopenharmony_ci struct mnt_idmap *idmap, 395362306a36Sopenharmony_ci struct btrfs_ioctl_received_subvol_args *sa) 395462306a36Sopenharmony_ci{ 395562306a36Sopenharmony_ci struct inode *inode = file_inode(file); 395662306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 395762306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 395862306a36Sopenharmony_ci struct btrfs_root_item *root_item = &root->root_item; 395962306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 396062306a36Sopenharmony_ci struct timespec64 ct = current_time(inode); 396162306a36Sopenharmony_ci int ret = 0; 396262306a36Sopenharmony_ci int received_uuid_changed; 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 396562306a36Sopenharmony_ci return -EPERM; 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci ret = mnt_want_write_file(file); 396862306a36Sopenharmony_ci if (ret < 0) 396962306a36Sopenharmony_ci return ret; 397062306a36Sopenharmony_ci 397162306a36Sopenharmony_ci down_write(&fs_info->subvol_sem); 397262306a36Sopenharmony_ci 397362306a36Sopenharmony_ci if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) { 397462306a36Sopenharmony_ci ret = -EINVAL; 397562306a36Sopenharmony_ci goto out; 397662306a36Sopenharmony_ci } 397762306a36Sopenharmony_ci 397862306a36Sopenharmony_ci if (btrfs_root_readonly(root)) { 397962306a36Sopenharmony_ci ret = -EROFS; 398062306a36Sopenharmony_ci goto out; 398162306a36Sopenharmony_ci } 398262306a36Sopenharmony_ci 398362306a36Sopenharmony_ci /* 398462306a36Sopenharmony_ci * 1 - root item 398562306a36Sopenharmony_ci * 2 - uuid items (received uuid + subvol uuid) 398662306a36Sopenharmony_ci */ 398762306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 3); 398862306a36Sopenharmony_ci if (IS_ERR(trans)) { 398962306a36Sopenharmony_ci ret = PTR_ERR(trans); 399062306a36Sopenharmony_ci trans = NULL; 399162306a36Sopenharmony_ci goto out; 399262306a36Sopenharmony_ci } 399362306a36Sopenharmony_ci 399462306a36Sopenharmony_ci sa->rtransid = trans->transid; 399562306a36Sopenharmony_ci sa->rtime.sec = ct.tv_sec; 399662306a36Sopenharmony_ci sa->rtime.nsec = ct.tv_nsec; 399762306a36Sopenharmony_ci 399862306a36Sopenharmony_ci received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid, 399962306a36Sopenharmony_ci BTRFS_UUID_SIZE); 400062306a36Sopenharmony_ci if (received_uuid_changed && 400162306a36Sopenharmony_ci !btrfs_is_empty_uuid(root_item->received_uuid)) { 400262306a36Sopenharmony_ci ret = btrfs_uuid_tree_remove(trans, root_item->received_uuid, 400362306a36Sopenharmony_ci BTRFS_UUID_KEY_RECEIVED_SUBVOL, 400462306a36Sopenharmony_ci root->root_key.objectid); 400562306a36Sopenharmony_ci if (ret && ret != -ENOENT) { 400662306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 400762306a36Sopenharmony_ci btrfs_end_transaction(trans); 400862306a36Sopenharmony_ci goto out; 400962306a36Sopenharmony_ci } 401062306a36Sopenharmony_ci } 401162306a36Sopenharmony_ci memcpy(root_item->received_uuid, sa->uuid, BTRFS_UUID_SIZE); 401262306a36Sopenharmony_ci btrfs_set_root_stransid(root_item, sa->stransid); 401362306a36Sopenharmony_ci btrfs_set_root_rtransid(root_item, sa->rtransid); 401462306a36Sopenharmony_ci btrfs_set_stack_timespec_sec(&root_item->stime, sa->stime.sec); 401562306a36Sopenharmony_ci btrfs_set_stack_timespec_nsec(&root_item->stime, sa->stime.nsec); 401662306a36Sopenharmony_ci btrfs_set_stack_timespec_sec(&root_item->rtime, sa->rtime.sec); 401762306a36Sopenharmony_ci btrfs_set_stack_timespec_nsec(&root_item->rtime, sa->rtime.nsec); 401862306a36Sopenharmony_ci 401962306a36Sopenharmony_ci ret = btrfs_update_root(trans, fs_info->tree_root, 402062306a36Sopenharmony_ci &root->root_key, &root->root_item); 402162306a36Sopenharmony_ci if (ret < 0) { 402262306a36Sopenharmony_ci btrfs_end_transaction(trans); 402362306a36Sopenharmony_ci goto out; 402462306a36Sopenharmony_ci } 402562306a36Sopenharmony_ci if (received_uuid_changed && !btrfs_is_empty_uuid(sa->uuid)) { 402662306a36Sopenharmony_ci ret = btrfs_uuid_tree_add(trans, sa->uuid, 402762306a36Sopenharmony_ci BTRFS_UUID_KEY_RECEIVED_SUBVOL, 402862306a36Sopenharmony_ci root->root_key.objectid); 402962306a36Sopenharmony_ci if (ret < 0 && ret != -EEXIST) { 403062306a36Sopenharmony_ci btrfs_abort_transaction(trans, ret); 403162306a36Sopenharmony_ci btrfs_end_transaction(trans); 403262306a36Sopenharmony_ci goto out; 403362306a36Sopenharmony_ci } 403462306a36Sopenharmony_ci } 403562306a36Sopenharmony_ci ret = btrfs_commit_transaction(trans); 403662306a36Sopenharmony_ciout: 403762306a36Sopenharmony_ci up_write(&fs_info->subvol_sem); 403862306a36Sopenharmony_ci mnt_drop_write_file(file); 403962306a36Sopenharmony_ci return ret; 404062306a36Sopenharmony_ci} 404162306a36Sopenharmony_ci 404262306a36Sopenharmony_ci#ifdef CONFIG_64BIT 404362306a36Sopenharmony_cistatic long btrfs_ioctl_set_received_subvol_32(struct file *file, 404462306a36Sopenharmony_ci void __user *arg) 404562306a36Sopenharmony_ci{ 404662306a36Sopenharmony_ci struct btrfs_ioctl_received_subvol_args_32 *args32 = NULL; 404762306a36Sopenharmony_ci struct btrfs_ioctl_received_subvol_args *args64 = NULL; 404862306a36Sopenharmony_ci int ret = 0; 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci args32 = memdup_user(arg, sizeof(*args32)); 405162306a36Sopenharmony_ci if (IS_ERR(args32)) 405262306a36Sopenharmony_ci return PTR_ERR(args32); 405362306a36Sopenharmony_ci 405462306a36Sopenharmony_ci args64 = kmalloc(sizeof(*args64), GFP_KERNEL); 405562306a36Sopenharmony_ci if (!args64) { 405662306a36Sopenharmony_ci ret = -ENOMEM; 405762306a36Sopenharmony_ci goto out; 405862306a36Sopenharmony_ci } 405962306a36Sopenharmony_ci 406062306a36Sopenharmony_ci memcpy(args64->uuid, args32->uuid, BTRFS_UUID_SIZE); 406162306a36Sopenharmony_ci args64->stransid = args32->stransid; 406262306a36Sopenharmony_ci args64->rtransid = args32->rtransid; 406362306a36Sopenharmony_ci args64->stime.sec = args32->stime.sec; 406462306a36Sopenharmony_ci args64->stime.nsec = args32->stime.nsec; 406562306a36Sopenharmony_ci args64->rtime.sec = args32->rtime.sec; 406662306a36Sopenharmony_ci args64->rtime.nsec = args32->rtime.nsec; 406762306a36Sopenharmony_ci args64->flags = args32->flags; 406862306a36Sopenharmony_ci 406962306a36Sopenharmony_ci ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_idmap(file), args64); 407062306a36Sopenharmony_ci if (ret) 407162306a36Sopenharmony_ci goto out; 407262306a36Sopenharmony_ci 407362306a36Sopenharmony_ci memcpy(args32->uuid, args64->uuid, BTRFS_UUID_SIZE); 407462306a36Sopenharmony_ci args32->stransid = args64->stransid; 407562306a36Sopenharmony_ci args32->rtransid = args64->rtransid; 407662306a36Sopenharmony_ci args32->stime.sec = args64->stime.sec; 407762306a36Sopenharmony_ci args32->stime.nsec = args64->stime.nsec; 407862306a36Sopenharmony_ci args32->rtime.sec = args64->rtime.sec; 407962306a36Sopenharmony_ci args32->rtime.nsec = args64->rtime.nsec; 408062306a36Sopenharmony_ci args32->flags = args64->flags; 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_ci ret = copy_to_user(arg, args32, sizeof(*args32)); 408362306a36Sopenharmony_ci if (ret) 408462306a36Sopenharmony_ci ret = -EFAULT; 408562306a36Sopenharmony_ci 408662306a36Sopenharmony_ciout: 408762306a36Sopenharmony_ci kfree(args32); 408862306a36Sopenharmony_ci kfree(args64); 408962306a36Sopenharmony_ci return ret; 409062306a36Sopenharmony_ci} 409162306a36Sopenharmony_ci#endif 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_cistatic long btrfs_ioctl_set_received_subvol(struct file *file, 409462306a36Sopenharmony_ci void __user *arg) 409562306a36Sopenharmony_ci{ 409662306a36Sopenharmony_ci struct btrfs_ioctl_received_subvol_args *sa = NULL; 409762306a36Sopenharmony_ci int ret = 0; 409862306a36Sopenharmony_ci 409962306a36Sopenharmony_ci sa = memdup_user(arg, sizeof(*sa)); 410062306a36Sopenharmony_ci if (IS_ERR(sa)) 410162306a36Sopenharmony_ci return PTR_ERR(sa); 410262306a36Sopenharmony_ci 410362306a36Sopenharmony_ci ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_idmap(file), sa); 410462306a36Sopenharmony_ci 410562306a36Sopenharmony_ci if (ret) 410662306a36Sopenharmony_ci goto out; 410762306a36Sopenharmony_ci 410862306a36Sopenharmony_ci ret = copy_to_user(arg, sa, sizeof(*sa)); 410962306a36Sopenharmony_ci if (ret) 411062306a36Sopenharmony_ci ret = -EFAULT; 411162306a36Sopenharmony_ci 411262306a36Sopenharmony_ciout: 411362306a36Sopenharmony_ci kfree(sa); 411462306a36Sopenharmony_ci return ret; 411562306a36Sopenharmony_ci} 411662306a36Sopenharmony_ci 411762306a36Sopenharmony_cistatic int btrfs_ioctl_get_fslabel(struct btrfs_fs_info *fs_info, 411862306a36Sopenharmony_ci void __user *arg) 411962306a36Sopenharmony_ci{ 412062306a36Sopenharmony_ci size_t len; 412162306a36Sopenharmony_ci int ret; 412262306a36Sopenharmony_ci char label[BTRFS_LABEL_SIZE]; 412362306a36Sopenharmony_ci 412462306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 412562306a36Sopenharmony_ci memcpy(label, fs_info->super_copy->label, BTRFS_LABEL_SIZE); 412662306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 412762306a36Sopenharmony_ci 412862306a36Sopenharmony_ci len = strnlen(label, BTRFS_LABEL_SIZE); 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_ci if (len == BTRFS_LABEL_SIZE) { 413162306a36Sopenharmony_ci btrfs_warn(fs_info, 413262306a36Sopenharmony_ci "label is too long, return the first %zu bytes", 413362306a36Sopenharmony_ci --len); 413462306a36Sopenharmony_ci } 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci ret = copy_to_user(arg, label, len); 413762306a36Sopenharmony_ci 413862306a36Sopenharmony_ci return ret ? -EFAULT : 0; 413962306a36Sopenharmony_ci} 414062306a36Sopenharmony_ci 414162306a36Sopenharmony_cistatic int btrfs_ioctl_set_fslabel(struct file *file, void __user *arg) 414262306a36Sopenharmony_ci{ 414362306a36Sopenharmony_ci struct inode *inode = file_inode(file); 414462306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 414562306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 414662306a36Sopenharmony_ci struct btrfs_super_block *super_block = fs_info->super_copy; 414762306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 414862306a36Sopenharmony_ci char label[BTRFS_LABEL_SIZE]; 414962306a36Sopenharmony_ci int ret; 415062306a36Sopenharmony_ci 415162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 415262306a36Sopenharmony_ci return -EPERM; 415362306a36Sopenharmony_ci 415462306a36Sopenharmony_ci if (copy_from_user(label, arg, sizeof(label))) 415562306a36Sopenharmony_ci return -EFAULT; 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci if (strnlen(label, BTRFS_LABEL_SIZE) == BTRFS_LABEL_SIZE) { 415862306a36Sopenharmony_ci btrfs_err(fs_info, 415962306a36Sopenharmony_ci "unable to set label with more than %d bytes", 416062306a36Sopenharmony_ci BTRFS_LABEL_SIZE - 1); 416162306a36Sopenharmony_ci return -EINVAL; 416262306a36Sopenharmony_ci } 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_ci ret = mnt_want_write_file(file); 416562306a36Sopenharmony_ci if (ret) 416662306a36Sopenharmony_ci return ret; 416762306a36Sopenharmony_ci 416862306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 0); 416962306a36Sopenharmony_ci if (IS_ERR(trans)) { 417062306a36Sopenharmony_ci ret = PTR_ERR(trans); 417162306a36Sopenharmony_ci goto out_unlock; 417262306a36Sopenharmony_ci } 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 417562306a36Sopenharmony_ci strcpy(super_block->label, label); 417662306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 417762306a36Sopenharmony_ci ret = btrfs_commit_transaction(trans); 417862306a36Sopenharmony_ci 417962306a36Sopenharmony_ciout_unlock: 418062306a36Sopenharmony_ci mnt_drop_write_file(file); 418162306a36Sopenharmony_ci return ret; 418262306a36Sopenharmony_ci} 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_ci#define INIT_FEATURE_FLAGS(suffix) \ 418562306a36Sopenharmony_ci { .compat_flags = BTRFS_FEATURE_COMPAT_##suffix, \ 418662306a36Sopenharmony_ci .compat_ro_flags = BTRFS_FEATURE_COMPAT_RO_##suffix, \ 418762306a36Sopenharmony_ci .incompat_flags = BTRFS_FEATURE_INCOMPAT_##suffix } 418862306a36Sopenharmony_ci 418962306a36Sopenharmony_ciint btrfs_ioctl_get_supported_features(void __user *arg) 419062306a36Sopenharmony_ci{ 419162306a36Sopenharmony_ci static const struct btrfs_ioctl_feature_flags features[3] = { 419262306a36Sopenharmony_ci INIT_FEATURE_FLAGS(SUPP), 419362306a36Sopenharmony_ci INIT_FEATURE_FLAGS(SAFE_SET), 419462306a36Sopenharmony_ci INIT_FEATURE_FLAGS(SAFE_CLEAR) 419562306a36Sopenharmony_ci }; 419662306a36Sopenharmony_ci 419762306a36Sopenharmony_ci if (copy_to_user(arg, &features, sizeof(features))) 419862306a36Sopenharmony_ci return -EFAULT; 419962306a36Sopenharmony_ci 420062306a36Sopenharmony_ci return 0; 420162306a36Sopenharmony_ci} 420262306a36Sopenharmony_ci 420362306a36Sopenharmony_cistatic int btrfs_ioctl_get_features(struct btrfs_fs_info *fs_info, 420462306a36Sopenharmony_ci void __user *arg) 420562306a36Sopenharmony_ci{ 420662306a36Sopenharmony_ci struct btrfs_super_block *super_block = fs_info->super_copy; 420762306a36Sopenharmony_ci struct btrfs_ioctl_feature_flags features; 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci features.compat_flags = btrfs_super_compat_flags(super_block); 421062306a36Sopenharmony_ci features.compat_ro_flags = btrfs_super_compat_ro_flags(super_block); 421162306a36Sopenharmony_ci features.incompat_flags = btrfs_super_incompat_flags(super_block); 421262306a36Sopenharmony_ci 421362306a36Sopenharmony_ci if (copy_to_user(arg, &features, sizeof(features))) 421462306a36Sopenharmony_ci return -EFAULT; 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_ci return 0; 421762306a36Sopenharmony_ci} 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_cistatic int check_feature_bits(struct btrfs_fs_info *fs_info, 422062306a36Sopenharmony_ci enum btrfs_feature_set set, 422162306a36Sopenharmony_ci u64 change_mask, u64 flags, u64 supported_flags, 422262306a36Sopenharmony_ci u64 safe_set, u64 safe_clear) 422362306a36Sopenharmony_ci{ 422462306a36Sopenharmony_ci const char *type = btrfs_feature_set_name(set); 422562306a36Sopenharmony_ci char *names; 422662306a36Sopenharmony_ci u64 disallowed, unsupported; 422762306a36Sopenharmony_ci u64 set_mask = flags & change_mask; 422862306a36Sopenharmony_ci u64 clear_mask = ~flags & change_mask; 422962306a36Sopenharmony_ci 423062306a36Sopenharmony_ci unsupported = set_mask & ~supported_flags; 423162306a36Sopenharmony_ci if (unsupported) { 423262306a36Sopenharmony_ci names = btrfs_printable_features(set, unsupported); 423362306a36Sopenharmony_ci if (names) { 423462306a36Sopenharmony_ci btrfs_warn(fs_info, 423562306a36Sopenharmony_ci "this kernel does not support the %s feature bit%s", 423662306a36Sopenharmony_ci names, strchr(names, ',') ? "s" : ""); 423762306a36Sopenharmony_ci kfree(names); 423862306a36Sopenharmony_ci } else 423962306a36Sopenharmony_ci btrfs_warn(fs_info, 424062306a36Sopenharmony_ci "this kernel does not support %s bits 0x%llx", 424162306a36Sopenharmony_ci type, unsupported); 424262306a36Sopenharmony_ci return -EOPNOTSUPP; 424362306a36Sopenharmony_ci } 424462306a36Sopenharmony_ci 424562306a36Sopenharmony_ci disallowed = set_mask & ~safe_set; 424662306a36Sopenharmony_ci if (disallowed) { 424762306a36Sopenharmony_ci names = btrfs_printable_features(set, disallowed); 424862306a36Sopenharmony_ci if (names) { 424962306a36Sopenharmony_ci btrfs_warn(fs_info, 425062306a36Sopenharmony_ci "can't set the %s feature bit%s while mounted", 425162306a36Sopenharmony_ci names, strchr(names, ',') ? "s" : ""); 425262306a36Sopenharmony_ci kfree(names); 425362306a36Sopenharmony_ci } else 425462306a36Sopenharmony_ci btrfs_warn(fs_info, 425562306a36Sopenharmony_ci "can't set %s bits 0x%llx while mounted", 425662306a36Sopenharmony_ci type, disallowed); 425762306a36Sopenharmony_ci return -EPERM; 425862306a36Sopenharmony_ci } 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_ci disallowed = clear_mask & ~safe_clear; 426162306a36Sopenharmony_ci if (disallowed) { 426262306a36Sopenharmony_ci names = btrfs_printable_features(set, disallowed); 426362306a36Sopenharmony_ci if (names) { 426462306a36Sopenharmony_ci btrfs_warn(fs_info, 426562306a36Sopenharmony_ci "can't clear the %s feature bit%s while mounted", 426662306a36Sopenharmony_ci names, strchr(names, ',') ? "s" : ""); 426762306a36Sopenharmony_ci kfree(names); 426862306a36Sopenharmony_ci } else 426962306a36Sopenharmony_ci btrfs_warn(fs_info, 427062306a36Sopenharmony_ci "can't clear %s bits 0x%llx while mounted", 427162306a36Sopenharmony_ci type, disallowed); 427262306a36Sopenharmony_ci return -EPERM; 427362306a36Sopenharmony_ci } 427462306a36Sopenharmony_ci 427562306a36Sopenharmony_ci return 0; 427662306a36Sopenharmony_ci} 427762306a36Sopenharmony_ci 427862306a36Sopenharmony_ci#define check_feature(fs_info, change_mask, flags, mask_base) \ 427962306a36Sopenharmony_cicheck_feature_bits(fs_info, FEAT_##mask_base, change_mask, flags, \ 428062306a36Sopenharmony_ci BTRFS_FEATURE_ ## mask_base ## _SUPP, \ 428162306a36Sopenharmony_ci BTRFS_FEATURE_ ## mask_base ## _SAFE_SET, \ 428262306a36Sopenharmony_ci BTRFS_FEATURE_ ## mask_base ## _SAFE_CLEAR) 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_cistatic int btrfs_ioctl_set_features(struct file *file, void __user *arg) 428562306a36Sopenharmony_ci{ 428662306a36Sopenharmony_ci struct inode *inode = file_inode(file); 428762306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 428862306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 428962306a36Sopenharmony_ci struct btrfs_super_block *super_block = fs_info->super_copy; 429062306a36Sopenharmony_ci struct btrfs_ioctl_feature_flags flags[2]; 429162306a36Sopenharmony_ci struct btrfs_trans_handle *trans; 429262306a36Sopenharmony_ci u64 newflags; 429362306a36Sopenharmony_ci int ret; 429462306a36Sopenharmony_ci 429562306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 429662306a36Sopenharmony_ci return -EPERM; 429762306a36Sopenharmony_ci 429862306a36Sopenharmony_ci if (copy_from_user(flags, arg, sizeof(flags))) 429962306a36Sopenharmony_ci return -EFAULT; 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci /* Nothing to do */ 430262306a36Sopenharmony_ci if (!flags[0].compat_flags && !flags[0].compat_ro_flags && 430362306a36Sopenharmony_ci !flags[0].incompat_flags) 430462306a36Sopenharmony_ci return 0; 430562306a36Sopenharmony_ci 430662306a36Sopenharmony_ci ret = check_feature(fs_info, flags[0].compat_flags, 430762306a36Sopenharmony_ci flags[1].compat_flags, COMPAT); 430862306a36Sopenharmony_ci if (ret) 430962306a36Sopenharmony_ci return ret; 431062306a36Sopenharmony_ci 431162306a36Sopenharmony_ci ret = check_feature(fs_info, flags[0].compat_ro_flags, 431262306a36Sopenharmony_ci flags[1].compat_ro_flags, COMPAT_RO); 431362306a36Sopenharmony_ci if (ret) 431462306a36Sopenharmony_ci return ret; 431562306a36Sopenharmony_ci 431662306a36Sopenharmony_ci ret = check_feature(fs_info, flags[0].incompat_flags, 431762306a36Sopenharmony_ci flags[1].incompat_flags, INCOMPAT); 431862306a36Sopenharmony_ci if (ret) 431962306a36Sopenharmony_ci return ret; 432062306a36Sopenharmony_ci 432162306a36Sopenharmony_ci ret = mnt_want_write_file(file); 432262306a36Sopenharmony_ci if (ret) 432362306a36Sopenharmony_ci return ret; 432462306a36Sopenharmony_ci 432562306a36Sopenharmony_ci trans = btrfs_start_transaction(root, 0); 432662306a36Sopenharmony_ci if (IS_ERR(trans)) { 432762306a36Sopenharmony_ci ret = PTR_ERR(trans); 432862306a36Sopenharmony_ci goto out_drop_write; 432962306a36Sopenharmony_ci } 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci spin_lock(&fs_info->super_lock); 433262306a36Sopenharmony_ci newflags = btrfs_super_compat_flags(super_block); 433362306a36Sopenharmony_ci newflags |= flags[0].compat_flags & flags[1].compat_flags; 433462306a36Sopenharmony_ci newflags &= ~(flags[0].compat_flags & ~flags[1].compat_flags); 433562306a36Sopenharmony_ci btrfs_set_super_compat_flags(super_block, newflags); 433662306a36Sopenharmony_ci 433762306a36Sopenharmony_ci newflags = btrfs_super_compat_ro_flags(super_block); 433862306a36Sopenharmony_ci newflags |= flags[0].compat_ro_flags & flags[1].compat_ro_flags; 433962306a36Sopenharmony_ci newflags &= ~(flags[0].compat_ro_flags & ~flags[1].compat_ro_flags); 434062306a36Sopenharmony_ci btrfs_set_super_compat_ro_flags(super_block, newflags); 434162306a36Sopenharmony_ci 434262306a36Sopenharmony_ci newflags = btrfs_super_incompat_flags(super_block); 434362306a36Sopenharmony_ci newflags |= flags[0].incompat_flags & flags[1].incompat_flags; 434462306a36Sopenharmony_ci newflags &= ~(flags[0].incompat_flags & ~flags[1].incompat_flags); 434562306a36Sopenharmony_ci btrfs_set_super_incompat_flags(super_block, newflags); 434662306a36Sopenharmony_ci spin_unlock(&fs_info->super_lock); 434762306a36Sopenharmony_ci 434862306a36Sopenharmony_ci ret = btrfs_commit_transaction(trans); 434962306a36Sopenharmony_ciout_drop_write: 435062306a36Sopenharmony_ci mnt_drop_write_file(file); 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci return ret; 435362306a36Sopenharmony_ci} 435462306a36Sopenharmony_ci 435562306a36Sopenharmony_cistatic int _btrfs_ioctl_send(struct inode *inode, void __user *argp, bool compat) 435662306a36Sopenharmony_ci{ 435762306a36Sopenharmony_ci struct btrfs_ioctl_send_args *arg; 435862306a36Sopenharmony_ci int ret; 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci if (compat) { 436162306a36Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) 436262306a36Sopenharmony_ci struct btrfs_ioctl_send_args_32 args32 = { 0 }; 436362306a36Sopenharmony_ci 436462306a36Sopenharmony_ci ret = copy_from_user(&args32, argp, sizeof(args32)); 436562306a36Sopenharmony_ci if (ret) 436662306a36Sopenharmony_ci return -EFAULT; 436762306a36Sopenharmony_ci arg = kzalloc(sizeof(*arg), GFP_KERNEL); 436862306a36Sopenharmony_ci if (!arg) 436962306a36Sopenharmony_ci return -ENOMEM; 437062306a36Sopenharmony_ci arg->send_fd = args32.send_fd; 437162306a36Sopenharmony_ci arg->clone_sources_count = args32.clone_sources_count; 437262306a36Sopenharmony_ci arg->clone_sources = compat_ptr(args32.clone_sources); 437362306a36Sopenharmony_ci arg->parent_root = args32.parent_root; 437462306a36Sopenharmony_ci arg->flags = args32.flags; 437562306a36Sopenharmony_ci arg->version = args32.version; 437662306a36Sopenharmony_ci memcpy(arg->reserved, args32.reserved, 437762306a36Sopenharmony_ci sizeof(args32.reserved)); 437862306a36Sopenharmony_ci#else 437962306a36Sopenharmony_ci return -ENOTTY; 438062306a36Sopenharmony_ci#endif 438162306a36Sopenharmony_ci } else { 438262306a36Sopenharmony_ci arg = memdup_user(argp, sizeof(*arg)); 438362306a36Sopenharmony_ci if (IS_ERR(arg)) 438462306a36Sopenharmony_ci return PTR_ERR(arg); 438562306a36Sopenharmony_ci } 438662306a36Sopenharmony_ci ret = btrfs_ioctl_send(inode, arg); 438762306a36Sopenharmony_ci kfree(arg); 438862306a36Sopenharmony_ci return ret; 438962306a36Sopenharmony_ci} 439062306a36Sopenharmony_ci 439162306a36Sopenharmony_cistatic int btrfs_ioctl_encoded_read(struct file *file, void __user *argp, 439262306a36Sopenharmony_ci bool compat) 439362306a36Sopenharmony_ci{ 439462306a36Sopenharmony_ci struct btrfs_ioctl_encoded_io_args args = { 0 }; 439562306a36Sopenharmony_ci size_t copy_end_kernel = offsetofend(struct btrfs_ioctl_encoded_io_args, 439662306a36Sopenharmony_ci flags); 439762306a36Sopenharmony_ci size_t copy_end; 439862306a36Sopenharmony_ci struct iovec iovstack[UIO_FASTIOV]; 439962306a36Sopenharmony_ci struct iovec *iov = iovstack; 440062306a36Sopenharmony_ci struct iov_iter iter; 440162306a36Sopenharmony_ci loff_t pos; 440262306a36Sopenharmony_ci struct kiocb kiocb; 440362306a36Sopenharmony_ci ssize_t ret; 440462306a36Sopenharmony_ci 440562306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 440662306a36Sopenharmony_ci ret = -EPERM; 440762306a36Sopenharmony_ci goto out_acct; 440862306a36Sopenharmony_ci } 440962306a36Sopenharmony_ci 441062306a36Sopenharmony_ci if (compat) { 441162306a36Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) 441262306a36Sopenharmony_ci struct btrfs_ioctl_encoded_io_args_32 args32; 441362306a36Sopenharmony_ci 441462306a36Sopenharmony_ci copy_end = offsetofend(struct btrfs_ioctl_encoded_io_args_32, 441562306a36Sopenharmony_ci flags); 441662306a36Sopenharmony_ci if (copy_from_user(&args32, argp, copy_end)) { 441762306a36Sopenharmony_ci ret = -EFAULT; 441862306a36Sopenharmony_ci goto out_acct; 441962306a36Sopenharmony_ci } 442062306a36Sopenharmony_ci args.iov = compat_ptr(args32.iov); 442162306a36Sopenharmony_ci args.iovcnt = args32.iovcnt; 442262306a36Sopenharmony_ci args.offset = args32.offset; 442362306a36Sopenharmony_ci args.flags = args32.flags; 442462306a36Sopenharmony_ci#else 442562306a36Sopenharmony_ci return -ENOTTY; 442662306a36Sopenharmony_ci#endif 442762306a36Sopenharmony_ci } else { 442862306a36Sopenharmony_ci copy_end = copy_end_kernel; 442962306a36Sopenharmony_ci if (copy_from_user(&args, argp, copy_end)) { 443062306a36Sopenharmony_ci ret = -EFAULT; 443162306a36Sopenharmony_ci goto out_acct; 443262306a36Sopenharmony_ci } 443362306a36Sopenharmony_ci } 443462306a36Sopenharmony_ci if (args.flags != 0) { 443562306a36Sopenharmony_ci ret = -EINVAL; 443662306a36Sopenharmony_ci goto out_acct; 443762306a36Sopenharmony_ci } 443862306a36Sopenharmony_ci 443962306a36Sopenharmony_ci ret = import_iovec(ITER_DEST, args.iov, args.iovcnt, ARRAY_SIZE(iovstack), 444062306a36Sopenharmony_ci &iov, &iter); 444162306a36Sopenharmony_ci if (ret < 0) 444262306a36Sopenharmony_ci goto out_acct; 444362306a36Sopenharmony_ci 444462306a36Sopenharmony_ci if (iov_iter_count(&iter) == 0) { 444562306a36Sopenharmony_ci ret = 0; 444662306a36Sopenharmony_ci goto out_iov; 444762306a36Sopenharmony_ci } 444862306a36Sopenharmony_ci pos = args.offset; 444962306a36Sopenharmony_ci ret = rw_verify_area(READ, file, &pos, args.len); 445062306a36Sopenharmony_ci if (ret < 0) 445162306a36Sopenharmony_ci goto out_iov; 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_ci init_sync_kiocb(&kiocb, file); 445462306a36Sopenharmony_ci kiocb.ki_pos = pos; 445562306a36Sopenharmony_ci 445662306a36Sopenharmony_ci ret = btrfs_encoded_read(&kiocb, &iter, &args); 445762306a36Sopenharmony_ci if (ret >= 0) { 445862306a36Sopenharmony_ci fsnotify_access(file); 445962306a36Sopenharmony_ci if (copy_to_user(argp + copy_end, 446062306a36Sopenharmony_ci (char *)&args + copy_end_kernel, 446162306a36Sopenharmony_ci sizeof(args) - copy_end_kernel)) 446262306a36Sopenharmony_ci ret = -EFAULT; 446362306a36Sopenharmony_ci } 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ciout_iov: 446662306a36Sopenharmony_ci kfree(iov); 446762306a36Sopenharmony_ciout_acct: 446862306a36Sopenharmony_ci if (ret > 0) 446962306a36Sopenharmony_ci add_rchar(current, ret); 447062306a36Sopenharmony_ci inc_syscr(current); 447162306a36Sopenharmony_ci return ret; 447262306a36Sopenharmony_ci} 447362306a36Sopenharmony_ci 447462306a36Sopenharmony_cistatic int btrfs_ioctl_encoded_write(struct file *file, void __user *argp, bool compat) 447562306a36Sopenharmony_ci{ 447662306a36Sopenharmony_ci struct btrfs_ioctl_encoded_io_args args; 447762306a36Sopenharmony_ci struct iovec iovstack[UIO_FASTIOV]; 447862306a36Sopenharmony_ci struct iovec *iov = iovstack; 447962306a36Sopenharmony_ci struct iov_iter iter; 448062306a36Sopenharmony_ci loff_t pos; 448162306a36Sopenharmony_ci struct kiocb kiocb; 448262306a36Sopenharmony_ci ssize_t ret; 448362306a36Sopenharmony_ci 448462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 448562306a36Sopenharmony_ci ret = -EPERM; 448662306a36Sopenharmony_ci goto out_acct; 448762306a36Sopenharmony_ci } 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci if (!(file->f_mode & FMODE_WRITE)) { 449062306a36Sopenharmony_ci ret = -EBADF; 449162306a36Sopenharmony_ci goto out_acct; 449262306a36Sopenharmony_ci } 449362306a36Sopenharmony_ci 449462306a36Sopenharmony_ci if (compat) { 449562306a36Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) 449662306a36Sopenharmony_ci struct btrfs_ioctl_encoded_io_args_32 args32; 449762306a36Sopenharmony_ci 449862306a36Sopenharmony_ci if (copy_from_user(&args32, argp, sizeof(args32))) { 449962306a36Sopenharmony_ci ret = -EFAULT; 450062306a36Sopenharmony_ci goto out_acct; 450162306a36Sopenharmony_ci } 450262306a36Sopenharmony_ci args.iov = compat_ptr(args32.iov); 450362306a36Sopenharmony_ci args.iovcnt = args32.iovcnt; 450462306a36Sopenharmony_ci args.offset = args32.offset; 450562306a36Sopenharmony_ci args.flags = args32.flags; 450662306a36Sopenharmony_ci args.len = args32.len; 450762306a36Sopenharmony_ci args.unencoded_len = args32.unencoded_len; 450862306a36Sopenharmony_ci args.unencoded_offset = args32.unencoded_offset; 450962306a36Sopenharmony_ci args.compression = args32.compression; 451062306a36Sopenharmony_ci args.encryption = args32.encryption; 451162306a36Sopenharmony_ci memcpy(args.reserved, args32.reserved, sizeof(args.reserved)); 451262306a36Sopenharmony_ci#else 451362306a36Sopenharmony_ci return -ENOTTY; 451462306a36Sopenharmony_ci#endif 451562306a36Sopenharmony_ci } else { 451662306a36Sopenharmony_ci if (copy_from_user(&args, argp, sizeof(args))) { 451762306a36Sopenharmony_ci ret = -EFAULT; 451862306a36Sopenharmony_ci goto out_acct; 451962306a36Sopenharmony_ci } 452062306a36Sopenharmony_ci } 452162306a36Sopenharmony_ci 452262306a36Sopenharmony_ci ret = -EINVAL; 452362306a36Sopenharmony_ci if (args.flags != 0) 452462306a36Sopenharmony_ci goto out_acct; 452562306a36Sopenharmony_ci if (memchr_inv(args.reserved, 0, sizeof(args.reserved))) 452662306a36Sopenharmony_ci goto out_acct; 452762306a36Sopenharmony_ci if (args.compression == BTRFS_ENCODED_IO_COMPRESSION_NONE && 452862306a36Sopenharmony_ci args.encryption == BTRFS_ENCODED_IO_ENCRYPTION_NONE) 452962306a36Sopenharmony_ci goto out_acct; 453062306a36Sopenharmony_ci if (args.compression >= BTRFS_ENCODED_IO_COMPRESSION_TYPES || 453162306a36Sopenharmony_ci args.encryption >= BTRFS_ENCODED_IO_ENCRYPTION_TYPES) 453262306a36Sopenharmony_ci goto out_acct; 453362306a36Sopenharmony_ci if (args.unencoded_offset > args.unencoded_len) 453462306a36Sopenharmony_ci goto out_acct; 453562306a36Sopenharmony_ci if (args.len > args.unencoded_len - args.unencoded_offset) 453662306a36Sopenharmony_ci goto out_acct; 453762306a36Sopenharmony_ci 453862306a36Sopenharmony_ci ret = import_iovec(ITER_SOURCE, args.iov, args.iovcnt, ARRAY_SIZE(iovstack), 453962306a36Sopenharmony_ci &iov, &iter); 454062306a36Sopenharmony_ci if (ret < 0) 454162306a36Sopenharmony_ci goto out_acct; 454262306a36Sopenharmony_ci 454362306a36Sopenharmony_ci file_start_write(file); 454462306a36Sopenharmony_ci 454562306a36Sopenharmony_ci if (iov_iter_count(&iter) == 0) { 454662306a36Sopenharmony_ci ret = 0; 454762306a36Sopenharmony_ci goto out_end_write; 454862306a36Sopenharmony_ci } 454962306a36Sopenharmony_ci pos = args.offset; 455062306a36Sopenharmony_ci ret = rw_verify_area(WRITE, file, &pos, args.len); 455162306a36Sopenharmony_ci if (ret < 0) 455262306a36Sopenharmony_ci goto out_end_write; 455362306a36Sopenharmony_ci 455462306a36Sopenharmony_ci init_sync_kiocb(&kiocb, file); 455562306a36Sopenharmony_ci ret = kiocb_set_rw_flags(&kiocb, 0); 455662306a36Sopenharmony_ci if (ret) 455762306a36Sopenharmony_ci goto out_end_write; 455862306a36Sopenharmony_ci kiocb.ki_pos = pos; 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci ret = btrfs_do_write_iter(&kiocb, &iter, &args); 456162306a36Sopenharmony_ci if (ret > 0) 456262306a36Sopenharmony_ci fsnotify_modify(file); 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ciout_end_write: 456562306a36Sopenharmony_ci file_end_write(file); 456662306a36Sopenharmony_ci kfree(iov); 456762306a36Sopenharmony_ciout_acct: 456862306a36Sopenharmony_ci if (ret > 0) 456962306a36Sopenharmony_ci add_wchar(current, ret); 457062306a36Sopenharmony_ci inc_syscw(current); 457162306a36Sopenharmony_ci return ret; 457262306a36Sopenharmony_ci} 457362306a36Sopenharmony_ci 457462306a36Sopenharmony_cilong btrfs_ioctl(struct file *file, unsigned int 457562306a36Sopenharmony_ci cmd, unsigned long arg) 457662306a36Sopenharmony_ci{ 457762306a36Sopenharmony_ci struct inode *inode = file_inode(file); 457862306a36Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 457962306a36Sopenharmony_ci struct btrfs_root *root = BTRFS_I(inode)->root; 458062306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 458162306a36Sopenharmony_ci 458262306a36Sopenharmony_ci switch (cmd) { 458362306a36Sopenharmony_ci case FS_IOC_GETVERSION: 458462306a36Sopenharmony_ci return btrfs_ioctl_getversion(inode, argp); 458562306a36Sopenharmony_ci case FS_IOC_GETFSLABEL: 458662306a36Sopenharmony_ci return btrfs_ioctl_get_fslabel(fs_info, argp); 458762306a36Sopenharmony_ci case FS_IOC_SETFSLABEL: 458862306a36Sopenharmony_ci return btrfs_ioctl_set_fslabel(file, argp); 458962306a36Sopenharmony_ci case FITRIM: 459062306a36Sopenharmony_ci return btrfs_ioctl_fitrim(fs_info, argp); 459162306a36Sopenharmony_ci case BTRFS_IOC_SNAP_CREATE: 459262306a36Sopenharmony_ci return btrfs_ioctl_snap_create(file, argp, 0); 459362306a36Sopenharmony_ci case BTRFS_IOC_SNAP_CREATE_V2: 459462306a36Sopenharmony_ci return btrfs_ioctl_snap_create_v2(file, argp, 0); 459562306a36Sopenharmony_ci case BTRFS_IOC_SUBVOL_CREATE: 459662306a36Sopenharmony_ci return btrfs_ioctl_snap_create(file, argp, 1); 459762306a36Sopenharmony_ci case BTRFS_IOC_SUBVOL_CREATE_V2: 459862306a36Sopenharmony_ci return btrfs_ioctl_snap_create_v2(file, argp, 1); 459962306a36Sopenharmony_ci case BTRFS_IOC_SNAP_DESTROY: 460062306a36Sopenharmony_ci return btrfs_ioctl_snap_destroy(file, argp, false); 460162306a36Sopenharmony_ci case BTRFS_IOC_SNAP_DESTROY_V2: 460262306a36Sopenharmony_ci return btrfs_ioctl_snap_destroy(file, argp, true); 460362306a36Sopenharmony_ci case BTRFS_IOC_SUBVOL_GETFLAGS: 460462306a36Sopenharmony_ci return btrfs_ioctl_subvol_getflags(inode, argp); 460562306a36Sopenharmony_ci case BTRFS_IOC_SUBVOL_SETFLAGS: 460662306a36Sopenharmony_ci return btrfs_ioctl_subvol_setflags(file, argp); 460762306a36Sopenharmony_ci case BTRFS_IOC_DEFAULT_SUBVOL: 460862306a36Sopenharmony_ci return btrfs_ioctl_default_subvol(file, argp); 460962306a36Sopenharmony_ci case BTRFS_IOC_DEFRAG: 461062306a36Sopenharmony_ci return btrfs_ioctl_defrag(file, NULL); 461162306a36Sopenharmony_ci case BTRFS_IOC_DEFRAG_RANGE: 461262306a36Sopenharmony_ci return btrfs_ioctl_defrag(file, argp); 461362306a36Sopenharmony_ci case BTRFS_IOC_RESIZE: 461462306a36Sopenharmony_ci return btrfs_ioctl_resize(file, argp); 461562306a36Sopenharmony_ci case BTRFS_IOC_ADD_DEV: 461662306a36Sopenharmony_ci return btrfs_ioctl_add_dev(fs_info, argp); 461762306a36Sopenharmony_ci case BTRFS_IOC_RM_DEV: 461862306a36Sopenharmony_ci return btrfs_ioctl_rm_dev(file, argp); 461962306a36Sopenharmony_ci case BTRFS_IOC_RM_DEV_V2: 462062306a36Sopenharmony_ci return btrfs_ioctl_rm_dev_v2(file, argp); 462162306a36Sopenharmony_ci case BTRFS_IOC_FS_INFO: 462262306a36Sopenharmony_ci return btrfs_ioctl_fs_info(fs_info, argp); 462362306a36Sopenharmony_ci case BTRFS_IOC_DEV_INFO: 462462306a36Sopenharmony_ci return btrfs_ioctl_dev_info(fs_info, argp); 462562306a36Sopenharmony_ci case BTRFS_IOC_TREE_SEARCH: 462662306a36Sopenharmony_ci return btrfs_ioctl_tree_search(inode, argp); 462762306a36Sopenharmony_ci case BTRFS_IOC_TREE_SEARCH_V2: 462862306a36Sopenharmony_ci return btrfs_ioctl_tree_search_v2(inode, argp); 462962306a36Sopenharmony_ci case BTRFS_IOC_INO_LOOKUP: 463062306a36Sopenharmony_ci return btrfs_ioctl_ino_lookup(root, argp); 463162306a36Sopenharmony_ci case BTRFS_IOC_INO_PATHS: 463262306a36Sopenharmony_ci return btrfs_ioctl_ino_to_path(root, argp); 463362306a36Sopenharmony_ci case BTRFS_IOC_LOGICAL_INO: 463462306a36Sopenharmony_ci return btrfs_ioctl_logical_to_ino(fs_info, argp, 1); 463562306a36Sopenharmony_ci case BTRFS_IOC_LOGICAL_INO_V2: 463662306a36Sopenharmony_ci return btrfs_ioctl_logical_to_ino(fs_info, argp, 2); 463762306a36Sopenharmony_ci case BTRFS_IOC_SPACE_INFO: 463862306a36Sopenharmony_ci return btrfs_ioctl_space_info(fs_info, argp); 463962306a36Sopenharmony_ci case BTRFS_IOC_SYNC: { 464062306a36Sopenharmony_ci int ret; 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_ci ret = btrfs_start_delalloc_roots(fs_info, LONG_MAX, false); 464362306a36Sopenharmony_ci if (ret) 464462306a36Sopenharmony_ci return ret; 464562306a36Sopenharmony_ci ret = btrfs_sync_fs(inode->i_sb, 1); 464662306a36Sopenharmony_ci /* 464762306a36Sopenharmony_ci * The transaction thread may want to do more work, 464862306a36Sopenharmony_ci * namely it pokes the cleaner kthread that will start 464962306a36Sopenharmony_ci * processing uncleaned subvols. 465062306a36Sopenharmony_ci */ 465162306a36Sopenharmony_ci wake_up_process(fs_info->transaction_kthread); 465262306a36Sopenharmony_ci return ret; 465362306a36Sopenharmony_ci } 465462306a36Sopenharmony_ci case BTRFS_IOC_START_SYNC: 465562306a36Sopenharmony_ci return btrfs_ioctl_start_sync(root, argp); 465662306a36Sopenharmony_ci case BTRFS_IOC_WAIT_SYNC: 465762306a36Sopenharmony_ci return btrfs_ioctl_wait_sync(fs_info, argp); 465862306a36Sopenharmony_ci case BTRFS_IOC_SCRUB: 465962306a36Sopenharmony_ci return btrfs_ioctl_scrub(file, argp); 466062306a36Sopenharmony_ci case BTRFS_IOC_SCRUB_CANCEL: 466162306a36Sopenharmony_ci return btrfs_ioctl_scrub_cancel(fs_info); 466262306a36Sopenharmony_ci case BTRFS_IOC_SCRUB_PROGRESS: 466362306a36Sopenharmony_ci return btrfs_ioctl_scrub_progress(fs_info, argp); 466462306a36Sopenharmony_ci case BTRFS_IOC_BALANCE_V2: 466562306a36Sopenharmony_ci return btrfs_ioctl_balance(file, argp); 466662306a36Sopenharmony_ci case BTRFS_IOC_BALANCE_CTL: 466762306a36Sopenharmony_ci return btrfs_ioctl_balance_ctl(fs_info, arg); 466862306a36Sopenharmony_ci case BTRFS_IOC_BALANCE_PROGRESS: 466962306a36Sopenharmony_ci return btrfs_ioctl_balance_progress(fs_info, argp); 467062306a36Sopenharmony_ci case BTRFS_IOC_SET_RECEIVED_SUBVOL: 467162306a36Sopenharmony_ci return btrfs_ioctl_set_received_subvol(file, argp); 467262306a36Sopenharmony_ci#ifdef CONFIG_64BIT 467362306a36Sopenharmony_ci case BTRFS_IOC_SET_RECEIVED_SUBVOL_32: 467462306a36Sopenharmony_ci return btrfs_ioctl_set_received_subvol_32(file, argp); 467562306a36Sopenharmony_ci#endif 467662306a36Sopenharmony_ci case BTRFS_IOC_SEND: 467762306a36Sopenharmony_ci return _btrfs_ioctl_send(inode, argp, false); 467862306a36Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) 467962306a36Sopenharmony_ci case BTRFS_IOC_SEND_32: 468062306a36Sopenharmony_ci return _btrfs_ioctl_send(inode, argp, true); 468162306a36Sopenharmony_ci#endif 468262306a36Sopenharmony_ci case BTRFS_IOC_GET_DEV_STATS: 468362306a36Sopenharmony_ci return btrfs_ioctl_get_dev_stats(fs_info, argp); 468462306a36Sopenharmony_ci case BTRFS_IOC_QUOTA_CTL: 468562306a36Sopenharmony_ci return btrfs_ioctl_quota_ctl(file, argp); 468662306a36Sopenharmony_ci case BTRFS_IOC_QGROUP_ASSIGN: 468762306a36Sopenharmony_ci return btrfs_ioctl_qgroup_assign(file, argp); 468862306a36Sopenharmony_ci case BTRFS_IOC_QGROUP_CREATE: 468962306a36Sopenharmony_ci return btrfs_ioctl_qgroup_create(file, argp); 469062306a36Sopenharmony_ci case BTRFS_IOC_QGROUP_LIMIT: 469162306a36Sopenharmony_ci return btrfs_ioctl_qgroup_limit(file, argp); 469262306a36Sopenharmony_ci case BTRFS_IOC_QUOTA_RESCAN: 469362306a36Sopenharmony_ci return btrfs_ioctl_quota_rescan(file, argp); 469462306a36Sopenharmony_ci case BTRFS_IOC_QUOTA_RESCAN_STATUS: 469562306a36Sopenharmony_ci return btrfs_ioctl_quota_rescan_status(fs_info, argp); 469662306a36Sopenharmony_ci case BTRFS_IOC_QUOTA_RESCAN_WAIT: 469762306a36Sopenharmony_ci return btrfs_ioctl_quota_rescan_wait(fs_info, argp); 469862306a36Sopenharmony_ci case BTRFS_IOC_DEV_REPLACE: 469962306a36Sopenharmony_ci return btrfs_ioctl_dev_replace(fs_info, argp); 470062306a36Sopenharmony_ci case BTRFS_IOC_GET_SUPPORTED_FEATURES: 470162306a36Sopenharmony_ci return btrfs_ioctl_get_supported_features(argp); 470262306a36Sopenharmony_ci case BTRFS_IOC_GET_FEATURES: 470362306a36Sopenharmony_ci return btrfs_ioctl_get_features(fs_info, argp); 470462306a36Sopenharmony_ci case BTRFS_IOC_SET_FEATURES: 470562306a36Sopenharmony_ci return btrfs_ioctl_set_features(file, argp); 470662306a36Sopenharmony_ci case BTRFS_IOC_GET_SUBVOL_INFO: 470762306a36Sopenharmony_ci return btrfs_ioctl_get_subvol_info(inode, argp); 470862306a36Sopenharmony_ci case BTRFS_IOC_GET_SUBVOL_ROOTREF: 470962306a36Sopenharmony_ci return btrfs_ioctl_get_subvol_rootref(root, argp); 471062306a36Sopenharmony_ci case BTRFS_IOC_INO_LOOKUP_USER: 471162306a36Sopenharmony_ci return btrfs_ioctl_ino_lookup_user(file, argp); 471262306a36Sopenharmony_ci case FS_IOC_ENABLE_VERITY: 471362306a36Sopenharmony_ci return fsverity_ioctl_enable(file, (const void __user *)argp); 471462306a36Sopenharmony_ci case FS_IOC_MEASURE_VERITY: 471562306a36Sopenharmony_ci return fsverity_ioctl_measure(file, argp); 471662306a36Sopenharmony_ci case BTRFS_IOC_ENCODED_READ: 471762306a36Sopenharmony_ci return btrfs_ioctl_encoded_read(file, argp, false); 471862306a36Sopenharmony_ci case BTRFS_IOC_ENCODED_WRITE: 471962306a36Sopenharmony_ci return btrfs_ioctl_encoded_write(file, argp, false); 472062306a36Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) 472162306a36Sopenharmony_ci case BTRFS_IOC_ENCODED_READ_32: 472262306a36Sopenharmony_ci return btrfs_ioctl_encoded_read(file, argp, true); 472362306a36Sopenharmony_ci case BTRFS_IOC_ENCODED_WRITE_32: 472462306a36Sopenharmony_ci return btrfs_ioctl_encoded_write(file, argp, true); 472562306a36Sopenharmony_ci#endif 472662306a36Sopenharmony_ci } 472762306a36Sopenharmony_ci 472862306a36Sopenharmony_ci return -ENOTTY; 472962306a36Sopenharmony_ci} 473062306a36Sopenharmony_ci 473162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 473262306a36Sopenharmony_cilong btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 473362306a36Sopenharmony_ci{ 473462306a36Sopenharmony_ci /* 473562306a36Sopenharmony_ci * These all access 32-bit values anyway so no further 473662306a36Sopenharmony_ci * handling is necessary. 473762306a36Sopenharmony_ci */ 473862306a36Sopenharmony_ci switch (cmd) { 473962306a36Sopenharmony_ci case FS_IOC32_GETVERSION: 474062306a36Sopenharmony_ci cmd = FS_IOC_GETVERSION; 474162306a36Sopenharmony_ci break; 474262306a36Sopenharmony_ci } 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_ci return btrfs_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); 474562306a36Sopenharmony_ci} 474662306a36Sopenharmony_ci#endif 4747