btrfs: add helpers for FS_XFLAG_* conversion
[sfrench/cifs-2.6.git] / fs / btrfs / ioctl.c
index 632e26d6f7ce0cd26284b0e39c140c7f09a1f3d2..054fa20ef193435abec99583194ce99eb030a1b2 100644 (file)
@@ -93,20 +93,22 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
                       int no_time_update);
 
 /* Mask out flags that are inappropriate for the given type of inode. */
-static unsigned int btrfs_mask_flags(umode_t mode, unsigned int flags)
+static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode,
+               unsigned int flags)
 {
-       if (S_ISDIR(mode))
+       if (S_ISDIR(inode->i_mode))
                return flags;
-       else if (S_ISREG(mode))
+       else if (S_ISREG(inode->i_mode))
                return flags & ~FS_DIRSYNC_FL;
        else
                return flags & (FS_NODUMP_FL | FS_NOATIME_FL);
 }
 
 /*
- * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
+ * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS
+ * ioctl.
  */
-static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
+static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags)
 {
        unsigned int iflags = 0;
 
@@ -136,7 +138,7 @@ static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
 /*
  * Update inode->i_flags based on the btrfs internal flags.
  */
-void btrfs_update_iflags(struct inode *inode)
+void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
 {
        struct btrfs_inode *ip = BTRFS_I(inode);
        unsigned int new_fl = 0;
@@ -160,14 +162,15 @@ void btrfs_update_iflags(struct inode *inode)
 static int btrfs_ioctl_getflags(struct file *file, void __user *arg)
 {
        struct btrfs_inode *ip = BTRFS_I(file_inode(file));
-       unsigned int flags = btrfs_flags_to_ioctl(ip->flags);
+       unsigned int flags = btrfs_inode_flags_to_fsflags(ip->flags);
 
        if (copy_to_user(arg, &flags, sizeof(flags)))
                return -EFAULT;
        return 0;
 }
 
-static int check_flags(unsigned int flags)
+/* Check if @flags are a supported and valid set of FS_*_FL flags */
+static int check_fsflags(unsigned int flags)
 {
        if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
                      FS_NOATIME_FL | FS_NODUMP_FL | \
@@ -204,7 +207,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        if (copy_from_user(&flags, arg, sizeof(flags)))
                return -EFAULT;
 
-       ret = check_flags(flags);
+       ret = check_fsflags(flags);
        if (ret)
                return ret;
 
@@ -218,8 +221,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        i_oldflags = inode->i_flags;
        mode = inode->i_mode;
 
-       flags = btrfs_mask_flags(inode->i_mode, flags);
-       oldflags = btrfs_flags_to_ioctl(ip->flags);
+       flags = btrfs_mask_fsflags_for_type(inode, flags);
+       oldflags = btrfs_inode_flags_to_fsflags(ip->flags);
        if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
                if (!capable(CAP_LINUX_IMMUTABLE)) {
                        ret = -EPERM;
@@ -317,7 +320,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                goto out_drop;
        }
 
-       btrfs_update_iflags(inode);
+       btrfs_sync_inode_flags_to_i_flags(inode);
        inode_inc_iversion(inode);
        inode->i_ctime = current_time(inode);
        ret = btrfs_update_inode(trans, root, inode);
@@ -335,6 +338,38 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        return ret;
 }
 
+/*
+ * Translate btrfs internal inode flags to xflags as expected by the
+ * FS_IOC_FSGETXATT ioctl. Filter only the supported ones, unknown flags are
+ * silently dropped.
+ */
+static unsigned int btrfs_inode_flags_to_xflags(unsigned int flags)
+{
+       unsigned int xflags = 0;
+
+       if (flags & BTRFS_INODE_APPEND)
+               xflags |= FS_XFLAG_APPEND;
+       if (flags & BTRFS_INODE_IMMUTABLE)
+               xflags |= FS_XFLAG_IMMUTABLE;
+       if (flags & BTRFS_INODE_NOATIME)
+               xflags |= FS_XFLAG_NOATIME;
+       if (flags & BTRFS_INODE_NODUMP)
+               xflags |= FS_XFLAG_NODUMP;
+       if (flags & BTRFS_INODE_SYNC)
+               xflags |= FS_XFLAG_SYNC;
+
+       return xflags;
+}
+
+/* Check if @flags are a supported and valid set of FS_XFLAGS_* flags */
+static int check_xflags(unsigned int flags)
+{
+       if (flags & ~(FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE | FS_XFLAG_NOATIME |
+                     FS_XFLAG_NODUMP | FS_XFLAG_SYNC))
+               return -EOPNOTSUPP;
+       return 0;
+}
+
 static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
 {
        struct inode *inode = file_inode(file);
@@ -640,7 +675,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
        wait_event(root->subv_writers->wait,
                   percpu_counter_sum(&root->subv_writers->counter) == 0);
 
-       ret = btrfs_start_delalloc_inodes(root, 0);
+       ret = btrfs_start_delalloc_inodes(root);
        if (ret)
                goto dec_and_free;
 
@@ -1457,7 +1492,6 @@ static noinline int btrfs_ioctl_resize(struct file *file,
                return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
        }
 
-       mutex_lock(&fs_info->volume_mutex);
        vol_args = memdup_user(arg, sizeof(*vol_args));
        if (IS_ERR(vol_args)) {
                ret = PTR_ERR(vol_args);
@@ -1565,7 +1599,6 @@ static noinline int btrfs_ioctl_resize(struct file *file,
 out_free:
        kfree(vol_args);
 out:
-       mutex_unlock(&fs_info->volume_mutex);
        clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags);
        mnt_drop_write_file(file);
        return ret;
@@ -1832,60 +1865,6 @@ out:
        return ret;
 }
 
-/*
- * helper to check if the subvolume references other subvolumes
- */
-static noinline int may_destroy_subvol(struct btrfs_root *root)
-{
-       struct btrfs_fs_info *fs_info = root->fs_info;
-       struct btrfs_path *path;
-       struct btrfs_dir_item *di;
-       struct btrfs_key key;
-       u64 dir_id;
-       int ret;
-
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-
-       /* Make sure this root isn't set as the default subvol */
-       dir_id = btrfs_super_root_dir(fs_info->super_copy);
-       di = btrfs_lookup_dir_item(NULL, fs_info->tree_root, path,
-                                  dir_id, "default", 7, 0);
-       if (di && !IS_ERR(di)) {
-               btrfs_dir_item_key_to_cpu(path->nodes[0], di, &key);
-               if (key.objectid == root->root_key.objectid) {
-                       ret = -EPERM;
-                       btrfs_err(fs_info,
-                                 "deleting default subvolume %llu is not allowed",
-                                 key.objectid);
-                       goto out;
-               }
-               btrfs_release_path(path);
-       }
-
-       key.objectid = root->root_key.objectid;
-       key.type = BTRFS_ROOT_REF_KEY;
-       key.offset = (u64)-1;
-
-       ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0);
-       if (ret < 0)
-               goto out;
-       BUG_ON(ret == 0);
-
-       ret = 0;
-       if (path->slots[0] > 0) {
-               path->slots[0]--;
-               btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
-               if (key.objectid == root->root_key.objectid &&
-                   key.type == BTRFS_ROOT_REF_KEY)
-                       ret = -ENOTEMPTY;
-       }
-out:
-       btrfs_free_path(path);
-       return ret;
-}
-
 static noinline int key_in_sk(struct btrfs_key *key,
                              struct btrfs_ioctl_search_key *sk)
 {
@@ -2309,12 +2288,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
        struct btrfs_root *root = BTRFS_I(dir)->root;
        struct btrfs_root *dest = NULL;
        struct btrfs_ioctl_vol_args *vol_args;
-       struct btrfs_trans_handle *trans;
-       struct btrfs_block_rsv block_rsv;
-       u64 root_flags;
-       u64 qgroup_reserved;
        int namelen;
-       int ret;
        int err = 0;
 
        if (!S_ISDIR(dir->i_mode))
@@ -2398,133 +2372,11 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
        }
 
        inode_lock(inode);
-
-       /*
-        * Don't allow to delete a subvolume with send in progress. This is
-        * inside the i_mutex so the error handling that has to drop the bit
-        * again is not run concurrently.
-        */
-       spin_lock(&dest->root_item_lock);
-       root_flags = btrfs_root_flags(&dest->root_item);
-       if (dest->send_in_progress == 0) {
-               btrfs_set_root_flags(&dest->root_item,
-                               root_flags | BTRFS_ROOT_SUBVOL_DEAD);
-               spin_unlock(&dest->root_item_lock);
-       } else {
-               spin_unlock(&dest->root_item_lock);
-               btrfs_warn(fs_info,
-                          "Attempt to delete subvolume %llu during send",
-                          dest->root_key.objectid);
-               err = -EPERM;
-               goto out_unlock_inode;
-       }
-
-       down_write(&fs_info->subvol_sem);
-
-       err = may_destroy_subvol(dest);
-       if (err)
-               goto out_up_write;
-
-       btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
-       /*
-        * One for dir inode, two for dir entries, two for root
-        * ref/backref.
-        */
-       err = btrfs_subvolume_reserve_metadata(root, &block_rsv,
-                                              5, &qgroup_reserved, true);
-       if (err)
-               goto out_up_write;
-
-       trans = btrfs_start_transaction(root, 0);
-       if (IS_ERR(trans)) {
-               err = PTR_ERR(trans);
-               goto out_release;
-       }
-       trans->block_rsv = &block_rsv;
-       trans->bytes_reserved = block_rsv.size;
-
-       btrfs_record_snapshot_destroy(trans, BTRFS_I(dir));
-
-       ret = btrfs_unlink_subvol(trans, root, dir,
-                               dest->root_key.objectid,
-                               dentry->d_name.name,
-                               dentry->d_name.len);
-       if (ret) {
-               err = ret;
-               btrfs_abort_transaction(trans, ret);
-               goto out_end_trans;
-       }
-
-       btrfs_record_root_in_trans(trans, dest);
-
-       memset(&dest->root_item.drop_progress, 0,
-               sizeof(dest->root_item.drop_progress));
-       dest->root_item.drop_level = 0;
-       btrfs_set_root_refs(&dest->root_item, 0);
-
-       if (!test_and_set_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &dest->state)) {
-               ret = btrfs_insert_orphan_item(trans,
-                                       fs_info->tree_root,
-                                       dest->root_key.objectid);
-               if (ret) {
-                       btrfs_abort_transaction(trans, ret);
-                       err = ret;
-                       goto out_end_trans;
-               }
-       }
-
-       ret = btrfs_uuid_tree_rem(trans, fs_info, dest->root_item.uuid,
-                                 BTRFS_UUID_KEY_SUBVOL,
-                                 dest->root_key.objectid);
-       if (ret && ret != -ENOENT) {
-               btrfs_abort_transaction(trans, ret);
-               err = ret;
-               goto out_end_trans;
-       }
-       if (!btrfs_is_empty_uuid(dest->root_item.received_uuid)) {
-               ret = btrfs_uuid_tree_rem(trans, fs_info,
-                                         dest->root_item.received_uuid,
-                                         BTRFS_UUID_KEY_RECEIVED_SUBVOL,
-                                         dest->root_key.objectid);
-               if (ret && ret != -ENOENT) {
-                       btrfs_abort_transaction(trans, ret);
-                       err = ret;
-                       goto out_end_trans;
-               }
-       }
-
-out_end_trans:
-       trans->block_rsv = NULL;
-       trans->bytes_reserved = 0;
-       ret = btrfs_end_transaction(trans);
-       if (ret && !err)
-               err = ret;
-       inode->i_flags |= S_DEAD;
-out_release:
-       btrfs_subvolume_release_metadata(fs_info, &block_rsv);
-out_up_write:
-       up_write(&fs_info->subvol_sem);
-       if (err) {
-               spin_lock(&dest->root_item_lock);
-               root_flags = btrfs_root_flags(&dest->root_item);
-               btrfs_set_root_flags(&dest->root_item,
-                               root_flags & ~BTRFS_ROOT_SUBVOL_DEAD);
-               spin_unlock(&dest->root_item_lock);
-       }
-out_unlock_inode:
+       err = btrfs_delete_subvolume(dir, dentry);
        inode_unlock(inode);
-       if (!err) {
-               d_invalidate(dentry);
-               btrfs_invalidate_inodes(dest);
+       if (!err)
                d_delete(dentry);
-               ASSERT(dest->send_in_progress == 0);
 
-               /* the last ref */
-               if (dest->ino_cache_inode) {
-                       iput(dest->ino_cache_inode);
-                       dest->ino_cache_inode = NULL;
-               }
-       }
 out_dput:
        dput(dentry);
 out_unlock_dir:
@@ -2613,7 +2465,6 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)
        if (test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags))
                return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
 
-       mutex_lock(&fs_info->volume_mutex);
        vol_args = memdup_user(arg, sizeof(*vol_args));
        if (IS_ERR(vol_args)) {
                ret = PTR_ERR(vol_args);
@@ -2628,7 +2479,6 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)
 
        kfree(vol_args);
 out:
-       mutex_unlock(&fs_info->volume_mutex);
        clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags);
        return ret;
 }
@@ -4007,8 +3857,8 @@ out:
        return ret;
 }
 
-void btrfs_get_block_group_info(struct list_head *groups_list,
-                               struct btrfs_ioctl_space_info *space)
+static void get_block_group_info(struct list_head *groups_list,
+                                struct btrfs_ioctl_space_info *space)
 {
        struct btrfs_block_group_cache *block_group;
 
@@ -4124,8 +3974,8 @@ static long btrfs_ioctl_space_info(struct btrfs_fs_info *fs_info,
                down_read(&info->groups_sem);
                for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) {
                        if (!list_empty(&info->block_groups[c])) {
-                               btrfs_get_block_group_info(
-                                       &info->block_groups[c], &space);
+                               get_block_group_info(&info->block_groups[c],
+                                                    &space);
                                memcpy(dest, &space, sizeof(space));
                                dest++;
                                space_args.total_spaces++;
@@ -4490,14 +4340,14 @@ out_loi:
        return ret;
 }
 
-void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock,
+void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
                               struct btrfs_ioctl_balance_args *bargs)
 {
        struct btrfs_balance_control *bctl = fs_info->balance_ctl;
 
        bargs->flags = bctl->flags;
 
-       if (atomic_read(&fs_info->balance_running))
+       if (test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags))
                bargs->state |= BTRFS_BALANCE_STATE_RUNNING;
        if (atomic_read(&fs_info->balance_pause_req))
                bargs->state |= BTRFS_BALANCE_STATE_PAUSE_REQ;
@@ -4508,13 +4358,9 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock,
        memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta));
        memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys));
 
-       if (lock) {
-               spin_lock(&fs_info->balance_lock);
-               memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat));
-               spin_unlock(&fs_info->balance_lock);
-       } else {
-               memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat));
-       }
+       spin_lock(&fs_info->balance_lock);
+       memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat));
+       spin_unlock(&fs_info->balance_lock);
 }
 
 static long btrfs_ioctl_balance(struct file *file, void __user *arg)
@@ -4535,7 +4381,6 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
 
 again:
        if (!test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)) {
-               mutex_lock(&fs_info->volume_mutex);
                mutex_lock(&fs_info->balance_mutex);
                need_unlock = true;
                goto locked;
@@ -4550,21 +4395,22 @@ again:
        mutex_lock(&fs_info->balance_mutex);
        if (fs_info->balance_ctl) {
                /* this is either (2) or (3) */
-               if (!atomic_read(&fs_info->balance_running)) {
+               if (!test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) {
                        mutex_unlock(&fs_info->balance_mutex);
-                       if (!mutex_trylock(&fs_info->volume_mutex))
-                               goto again;
+                       /*
+                        * Lock released to allow other waiters to continue,
+                        * we'll reexamine the status again.
+                        */
                        mutex_lock(&fs_info->balance_mutex);
 
                        if (fs_info->balance_ctl &&
-                           !atomic_read(&fs_info->balance_running)) {
+                           !test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) {
                                /* this is (3) */
                                need_unlock = false;
                                goto locked;
                        }
 
                        mutex_unlock(&fs_info->balance_mutex);
-                       mutex_unlock(&fs_info->volume_mutex);
                        goto again;
                } else {
                        /* this is (2) */
@@ -4617,7 +4463,6 @@ locked:
                goto out_bargs;
        }
 
-       bctl->fs_info = fs_info;
        if (arg) {
                memcpy(&bctl->data, &bargs->data, sizeof(bctl->data));
                memcpy(&bctl->meta, &bargs->meta, sizeof(bctl->meta));
@@ -4636,14 +4481,14 @@ locked:
 
 do_balance:
        /*
-        * Ownership of bctl and filesystem flag BTRFS_FS_EXCL_OP
-        * goes to to btrfs_balance.  bctl is freed in __cancel_balance,
-        * or, if restriper was paused all the way until unmount, in
-        * free_fs_info.  The flag is cleared in __cancel_balance.
+        * Ownership of bctl and filesystem flag BTRFS_FS_EXCL_OP goes to
+        * btrfs_balance.  bctl is freed in reset_balance_state, or, if
+        * restriper was paused all the way until unmount, in free_fs_info.
+        * The flag should be cleared after reset_balance_state.
         */
        need_unlock = false;
 
-       ret = btrfs_balance(bctl, bargs);
+       ret = btrfs_balance(fs_info, bctl, bargs);
        bctl = NULL;
 
        if (arg) {
@@ -4657,7 +4502,6 @@ out_bargs:
        kfree(bargs);
 out_unlock:
        mutex_unlock(&fs_info->balance_mutex);
-       mutex_unlock(&fs_info->volume_mutex);
        if (need_unlock)
                clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags);
 out:
@@ -4701,7 +4545,7 @@ static long btrfs_ioctl_balance_progress(struct btrfs_fs_info *fs_info,
                goto out;
        }
 
-       update_ioctl_balance_args(fs_info, 1, bargs);
+       btrfs_update_ioctl_balance_args(fs_info, bargs);
 
        if (copy_to_user(arg, bargs, sizeof(*bargs)))
                ret = -EFAULT;
@@ -5497,7 +5341,7 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_SYNC: {
                int ret;
 
-               ret = btrfs_start_delalloc_roots(fs_info, 0, -1);
+               ret = btrfs_start_delalloc_roots(fs_info, -1);
                if (ret)
                        return ret;
                ret = btrfs_sync_fs(inode->i_sb, 1);