Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-drm-fsl-dcu.git] / fs / btrfs / ioctl.c
index cd93eb530b743742a8461de356fc3f5eb28f74e5..bc2f6ffff3cfb004099a5d4eb84f8a9aa688d7cd 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
+#include <linux/uuid.h>
 #include "compat.h"
 #include "ctree.h"
 #include "disk-io.h"
@@ -53,6 +54,7 @@
 #include "inode-map.h"
 #include "backref.h"
 #include "rcu-string.h"
+#include "send.h"
 
 /* Mask out flags that are inappropriate for the given type of inode. */
 static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
@@ -336,7 +338,8 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
 static noinline int create_subvol(struct btrfs_root *root,
                                  struct dentry *dentry,
                                  char *name, int namelen,
-                                 u64 *async_transid)
+                                 u64 *async_transid,
+                                 struct btrfs_qgroup_inherit **inherit)
 {
        struct btrfs_trans_handle *trans;
        struct btrfs_key key;
@@ -346,11 +349,13 @@ static noinline int create_subvol(struct btrfs_root *root,
        struct btrfs_root *new_root;
        struct dentry *parent = dentry->d_parent;
        struct inode *dir;
+       struct timespec cur_time = CURRENT_TIME;
        int ret;
        int err;
        u64 objectid;
        u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
        u64 index = 0;
+       uuid_le new_uuid;
 
        ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);
        if (ret)
@@ -368,6 +373,11 @@ static noinline int create_subvol(struct btrfs_root *root,
        if (IS_ERR(trans))
                return PTR_ERR(trans);
 
+       ret = btrfs_qgroup_inherit(trans, root->fs_info, 0, objectid,
+                                  inherit ? *inherit : NULL);
+       if (ret)
+               goto fail;
+
        leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
                                      0, objectid, NULL, 0, 0, 0);
        if (IS_ERR(leaf)) {
@@ -389,8 +399,9 @@ static noinline int create_subvol(struct btrfs_root *root,
                            BTRFS_UUID_SIZE);
        btrfs_mark_buffer_dirty(leaf);
 
+       memset(&root_item, 0, sizeof(root_item));
+
        inode_item = &root_item.inode;
-       memset(inode_item, 0, sizeof(*inode_item));
        inode_item->generation = cpu_to_le64(1);
        inode_item->size = cpu_to_le64(3);
        inode_item->nlink = cpu_to_le32(1);
@@ -408,8 +419,15 @@ static noinline int create_subvol(struct btrfs_root *root,
        btrfs_set_root_used(&root_item, leaf->len);
        btrfs_set_root_last_snapshot(&root_item, 0);
 
-       memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress));
-       root_item.drop_level = 0;
+       btrfs_set_root_generation_v2(&root_item,
+                       btrfs_root_generation(&root_item));
+       uuid_le_gen(&new_uuid);
+       memcpy(root_item.uuid, new_uuid.b, BTRFS_UUID_SIZE);
+       root_item.otime.sec = cpu_to_le64(cur_time.tv_sec);
+       root_item.otime.nsec = cpu_to_le64(cur_time.tv_nsec);
+       root_item.ctime = root_item.otime;
+       btrfs_set_root_ctransid(&root_item, trans->transid);
+       btrfs_set_root_otransid(&root_item, trans->transid);
 
        btrfs_tree_unlock(leaf);
        free_extent_buffer(leaf);
@@ -484,7 +502,7 @@ fail:
 
 static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,
                           char *name, int namelen, u64 *async_transid,
-                          bool readonly)
+                          bool readonly, struct btrfs_qgroup_inherit **inherit)
 {
        struct inode *inode;
        struct btrfs_pending_snapshot *pending_snapshot;
@@ -502,6 +520,10 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,
        pending_snapshot->dentry = dentry;
        pending_snapshot->root = root;
        pending_snapshot->readonly = readonly;
+       if (inherit) {
+               pending_snapshot->inherit = *inherit;
+               *inherit = NULL;        /* take responsibility to free it */
+       }
 
        trans = btrfs_start_transaction(root->fs_info->extent_root, 5);
        if (IS_ERR(trans)) {
@@ -635,7 +657,8 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
 static noinline int btrfs_mksubvol(struct path *parent,
                                   char *name, int namelen,
                                   struct btrfs_root *snap_src,
-                                  u64 *async_transid, bool readonly)
+                                  u64 *async_transid, bool readonly,
+                                  struct btrfs_qgroup_inherit **inherit)
 {
        struct inode *dir  = parent->dentry->d_inode;
        struct dentry *dentry;
@@ -666,11 +689,11 @@ static noinline int btrfs_mksubvol(struct path *parent,
                goto out_up_read;
 
        if (snap_src) {
-               error = create_snapshot(snap_src, dentry,
-                                       name, namelen, async_transid, readonly);
+               error = create_snapshot(snap_src, dentry, name, namelen,
+                                       async_transid, readonly, inherit);
        } else {
                error = create_subvol(BTRFS_I(dir)->root, dentry,
-                                     name, namelen, async_transid);
+                                     name, namelen, async_transid, inherit);
        }
        if (!error)
                fsnotify_mkdir(dir, dentry);
@@ -831,7 +854,8 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em)
 }
 
 static int should_defrag_range(struct inode *inode, u64 start, int thresh,
-                              u64 *last_len, u64 *skip, u64 *defrag_end)
+                              u64 *last_len, u64 *skip, u64 *defrag_end,
+                              int compress)
 {
        struct extent_map *em;
        int ret = 1;
@@ -862,7 +886,7 @@ static int should_defrag_range(struct inode *inode, u64 start, int thresh,
         * we hit a real extent, if it is big or the next extent is not a
         * real extent, don't bother defragging it
         */
-       if ((*last_len == 0 || *last_len >= thresh) &&
+       if (!compress && (*last_len == 0 || *last_len >= thresh) &&
            (em->len >= thresh || !next_mergeable))
                ret = 0;
 out:
@@ -1046,11 +1070,9 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
                      u64 newer_than, unsigned long max_to_defrag)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct btrfs_super_block *disk_super;
        struct file_ra_state *ra = NULL;
        unsigned long last_index;
        u64 isize = i_size_read(inode);
-       u64 features;
        u64 last_len = 0;
        u64 skip = 0;
        u64 defrag_end = 0;
@@ -1144,7 +1166,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
 
                if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
                                         extent_thresh, &last_len, &skip,
-                                        &defrag_end)) {
+                                        &defrag_end, range->flags &
+                                        BTRFS_DEFRAG_RANGE_COMPRESS)) {
                        unsigned long next;
                        /*
                         * the should_defrag function tells us how much to skip
@@ -1236,11 +1259,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
                mutex_unlock(&inode->i_mutex);
        }
 
-       disk_super = root->fs_info->super_copy;
-       features = btrfs_super_incompat_flags(disk_super);
        if (range->compress_type == BTRFS_COMPRESS_LZO) {
-               features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO;
-               btrfs_set_super_incompat_flags(disk_super, features);
+               btrfs_set_fs_incompat(root->fs_info, COMPRESS_LZO);
        }
 
        ret = defrag_count;
@@ -1378,41 +1398,39 @@ out:
 }
 
 static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
-                                                   char *name,
-                                                   unsigned long fd,
-                                                   int subvol,
-                                                   u64 *transid,
-                                                   bool readonly)
+                               char *name, unsigned long fd, int subvol,
+                               u64 *transid, bool readonly,
+                               struct btrfs_qgroup_inherit **inherit)
 {
-       struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
        struct file *src_file;
        int namelen;
        int ret = 0;
 
-       if (root->fs_info->sb->s_flags & MS_RDONLY)
-               return -EROFS;
+       ret = mnt_want_write_file(file);
+       if (ret)
+               goto out;
 
        namelen = strlen(name);
        if (strchr(name, '/')) {
                ret = -EINVAL;
-               goto out;
+               goto out_drop_write;
        }
 
        if (name[0] == '.' &&
           (namelen == 1 || (name[1] == '.' && namelen == 2))) {
                ret = -EEXIST;
-               goto out;
+               goto out_drop_write;
        }
 
        if (subvol) {
                ret = btrfs_mksubvol(&file->f_path, name, namelen,
-                                    NULL, transid, readonly);
+                                    NULL, transid, readonly, inherit);
        } else {
                struct inode *src_inode;
                src_file = fget(fd);
                if (!src_file) {
                        ret = -EINVAL;
-                       goto out;
+                       goto out_drop_write;
                }
 
                src_inode = src_file->f_path.dentry->d_inode;
@@ -1421,13 +1439,15 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
                               "another FS\n");
                        ret = -EINVAL;
                        fput(src_file);
-                       goto out;
+                       goto out_drop_write;
                }
                ret = btrfs_mksubvol(&file->f_path, name, namelen,
                                     BTRFS_I(src_inode)->root,
-                                    transid, readonly);
+                                    transid, readonly, inherit);
                fput(src_file);
        }
+out_drop_write:
+       mnt_drop_write_file(file);
 out:
        return ret;
 }
@@ -1445,7 +1465,7 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
 
        ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,
                                              vol_args->fd, subvol,
-                                             NULL, false);
+                                             NULL, false, NULL);
 
        kfree(vol_args);
        return ret;
@@ -1459,6 +1479,7 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
        u64 transid = 0;
        u64 *ptr = NULL;
        bool readonly = false;
+       struct btrfs_qgroup_inherit *inherit = NULL;
 
        vol_args = memdup_user(arg, sizeof(*vol_args));
        if (IS_ERR(vol_args))
@@ -1466,7 +1487,8 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
        vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
 
        if (vol_args->flags &
-           ~(BTRFS_SUBVOL_CREATE_ASYNC | BTRFS_SUBVOL_RDONLY)) {
+           ~(BTRFS_SUBVOL_CREATE_ASYNC | BTRFS_SUBVOL_RDONLY |
+             BTRFS_SUBVOL_QGROUP_INHERIT)) {
                ret = -EOPNOTSUPP;
                goto out;
        }
@@ -1475,10 +1497,21 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
                ptr = &transid;
        if (vol_args->flags & BTRFS_SUBVOL_RDONLY)
                readonly = true;
+       if (vol_args->flags & BTRFS_SUBVOL_QGROUP_INHERIT) {
+               if (vol_args->size > PAGE_CACHE_SIZE) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+               inherit = memdup_user(vol_args->qgroup_inherit, vol_args->size);
+               if (IS_ERR(inherit)) {
+                       ret = PTR_ERR(inherit);
+                       goto out;
+               }
+       }
 
        ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,
-                                             vol_args->fd, subvol,
-                                             ptr, readonly);
+                                             vol_args->fd, subvol, ptr,
+                                             readonly, &inherit);
 
        if (ret == 0 && ptr &&
            copy_to_user(arg +
@@ -1487,6 +1520,7 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
                ret = -EFAULT;
 out:
        kfree(vol_args);
+       kfree(inherit);
        return ret;
 }
 
@@ -1522,29 +1556,40 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
        u64 flags;
        int ret = 0;
 
-       if (root->fs_info->sb->s_flags & MS_RDONLY)
-               return -EROFS;
+       ret = mnt_want_write_file(file);
+       if (ret)
+               goto out;
 
-       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID)
-               return -EINVAL;
+       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {
+               ret = -EINVAL;
+               goto out_drop_write;
+       }
 
-       if (copy_from_user(&flags, arg, sizeof(flags)))
-               return -EFAULT;
+       if (copy_from_user(&flags, arg, sizeof(flags))) {
+               ret = -EFAULT;
+               goto out_drop_write;
+       }
 
-       if (flags & BTRFS_SUBVOL_CREATE_ASYNC)
-               return -EINVAL;
+       if (flags & BTRFS_SUBVOL_CREATE_ASYNC) {
+               ret = -EINVAL;
+               goto out_drop_write;
+       }
 
-       if (flags & ~BTRFS_SUBVOL_RDONLY)
-               return -EOPNOTSUPP;
+       if (flags & ~BTRFS_SUBVOL_RDONLY) {
+               ret = -EOPNOTSUPP;
+               goto out_drop_write;
+       }
 
-       if (!inode_owner_or_capable(inode))
-               return -EACCES;
+       if (!inode_owner_or_capable(inode)) {
+               ret = -EACCES;
+               goto out_drop_write;
+       }
 
        down_write(&root->fs_info->subvol_sem);
 
        /* nothing to do */
        if (!!(flags & BTRFS_SUBVOL_RDONLY) == btrfs_root_readonly(root))
-               goto out;
+               goto out_drop_sem;
 
        root_flags = btrfs_root_flags(&root->root_item);
        if (flags & BTRFS_SUBVOL_RDONLY)
@@ -1567,8 +1612,11 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
 out_reset:
        if (ret)
                btrfs_set_root_flags(&root->root_item, root_flags);
-out:
+out_drop_sem:
        up_write(&root->fs_info->subvol_sem);
+out_drop_write:
+       mnt_drop_write_file(file);
+out:
        return ret;
 }
 
@@ -2339,6 +2387,10 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                goto out_drop_write;
        }
 
+       ret = -EXDEV;
+       if (src_file->f_path.mnt != file->f_path.mnt)
+               goto out_fput;
+
        src = src_file->f_dentry->d_inode;
 
        ret = -EINVAL;
@@ -2359,7 +2411,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                goto out_fput;
 
        ret = -EXDEV;
-       if (src->i_sb != inode->i_sb || BTRFS_I(src)->root != root)
+       if (src->i_sb != inode->i_sb)
                goto out_fput;
 
        ret = -ENOMEM;
@@ -2433,13 +2485,14 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                 * note the key will change type as we walk through the
                 * tree.
                 */
-               ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+               ret = btrfs_search_slot(NULL, BTRFS_I(src)->root, &key, path,
+                               0, 0);
                if (ret < 0)
                        goto out;
 
                nritems = btrfs_header_nritems(path->nodes[0]);
                if (path->slots[0] >= nritems) {
-                       ret = btrfs_next_leaf(root, path);
+                       ret = btrfs_next_leaf(BTRFS_I(src)->root, path);
                        if (ret < 0)
                                goto out;
                        if (ret > 0)
@@ -2748,8 +2801,6 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
        struct btrfs_path *path;
        struct btrfs_key location;
        struct btrfs_disk_key disk_key;
-       struct btrfs_super_block *disk_super;
-       u64 features;
        u64 objectid = 0;
        u64 dir_id;
 
@@ -2800,12 +2851,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
        btrfs_mark_buffer_dirty(path->nodes[0]);
        btrfs_free_path(path);
 
-       disk_super = root->fs_info->super_copy;
-       features = btrfs_super_incompat_flags(disk_super);
-       if (!(features & BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL)) {
-               features |= BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL;
-               btrfs_set_super_incompat_flags(disk_super, features);
-       }
+       btrfs_set_fs_incompat(root->fs_info, DEFAULT_SUBVOL);
        btrfs_end_transaction(trans, root);
 
        return 0;
@@ -3062,19 +3108,21 @@ static long btrfs_ioctl_scrub_progress(struct btrfs_root *root,
 }
 
 static long btrfs_ioctl_get_dev_stats(struct btrfs_root *root,
-                                     void __user *arg, int reset_after_read)
+                                     void __user *arg)
 {
        struct btrfs_ioctl_get_dev_stats *sa;
        int ret;
 
-       if (reset_after_read && !capable(CAP_SYS_ADMIN))
-               return -EPERM;
-
        sa = memdup_user(arg, sizeof(*sa));
        if (IS_ERR(sa))
                return PTR_ERR(sa);
 
-       ret = btrfs_get_dev_stats(root, sa, reset_after_read);
+       if ((sa->flags & BTRFS_DEV_STATS_RESET) && !capable(CAP_SYS_ADMIN)) {
+               kfree(sa);
+               return -EPERM;
+       }
+
+       ret = btrfs_get_dev_stats(root, sa);
 
        if (copy_to_user(arg, sa, sizeof(*sa)))
                ret = -EFAULT;
@@ -3264,9 +3312,6 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       if (fs_info->sb->s_flags & MS_RDONLY)
-               return -EROFS;
-
        ret = mnt_want_write_file(file);
        if (ret)
                return ret;
@@ -3389,6 +3434,264 @@ out:
        return ret;
 }
 
+static long btrfs_ioctl_quota_ctl(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_quota_ctl_args *sa;
+       struct btrfs_trans_handle *trans = NULL;
+       int ret;
+       int err;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       if (sa->cmd != BTRFS_QUOTA_CTL_RESCAN) {
+               trans = btrfs_start_transaction(root, 2);
+               if (IS_ERR(trans)) {
+                       ret = PTR_ERR(trans);
+                       goto out;
+               }
+       }
+
+       switch (sa->cmd) {
+       case BTRFS_QUOTA_CTL_ENABLE:
+               ret = btrfs_quota_enable(trans, root->fs_info);
+               break;
+       case BTRFS_QUOTA_CTL_DISABLE:
+               ret = btrfs_quota_disable(trans, root->fs_info);
+               break;
+       case BTRFS_QUOTA_CTL_RESCAN:
+               ret = btrfs_quota_rescan(root->fs_info);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (copy_to_user(arg, sa, sizeof(*sa)))
+               ret = -EFAULT;
+
+       if (trans) {
+               err = btrfs_commit_transaction(trans, root);
+               if (err && !ret)
+                       ret = err;
+       }
+
+out:
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_qgroup_assign(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_qgroup_assign_args *sa;
+       struct btrfs_trans_handle *trans;
+       int ret;
+       int err;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out;
+       }
+
+       /* FIXME: check if the IDs really exist */
+       if (sa->assign) {
+               ret = btrfs_add_qgroup_relation(trans, root->fs_info,
+                                               sa->src, sa->dst);
+       } else {
+               ret = btrfs_del_qgroup_relation(trans, root->fs_info,
+                                               sa->src, sa->dst);
+       }
+
+       err = btrfs_end_transaction(trans, root);
+       if (err && !ret)
+               ret = err;
+
+out:
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_qgroup_create(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_qgroup_create_args *sa;
+       struct btrfs_trans_handle *trans;
+       int ret;
+       int err;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out;
+       }
+
+       /* FIXME: check if the IDs really exist */
+       if (sa->create) {
+               ret = btrfs_create_qgroup(trans, root->fs_info, sa->qgroupid,
+                                         NULL);
+       } else {
+               ret = btrfs_remove_qgroup(trans, root->fs_info, sa->qgroupid);
+       }
+
+       err = btrfs_end_transaction(trans, root);
+       if (err && !ret)
+               ret = err;
+
+out:
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_qgroup_limit(struct btrfs_root *root, void __user *arg)
+{
+       struct btrfs_ioctl_qgroup_limit_args *sa;
+       struct btrfs_trans_handle *trans;
+       int ret;
+       int err;
+       u64 qgroupid;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out;
+       }
+
+       qgroupid = sa->qgroupid;
+       if (!qgroupid) {
+               /* take the current subvol as qgroup */
+               qgroupid = root->root_key.objectid;
+       }
+
+       /* FIXME: check if the IDs really exist */
+       ret = btrfs_limit_qgroup(trans, root->fs_info, qgroupid, &sa->lim);
+
+       err = btrfs_end_transaction(trans, root);
+       if (err && !ret)
+               ret = err;
+
+out:
+       kfree(sa);
+       return ret;
+}
+
+static long btrfs_ioctl_set_received_subvol(struct file *file,
+                                           void __user *arg)
+{
+       struct btrfs_ioctl_received_subvol_args *sa = NULL;
+       struct inode *inode = fdentry(file)->d_inode;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_root_item *root_item = &root->root_item;
+       struct btrfs_trans_handle *trans;
+       struct timespec ct = CURRENT_TIME;
+       int ret = 0;
+
+       ret = mnt_want_write_file(file);
+       if (ret < 0)
+               return ret;
+
+       down_write(&root->fs_info->subvol_sem);
+
+       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (btrfs_root_readonly(root)) {
+               ret = -EROFS;
+               goto out;
+       }
+
+       if (!inode_owner_or_capable(inode)) {
+               ret = -EACCES;
+               goto out;
+       }
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa)) {
+               ret = PTR_ERR(sa);
+               sa = NULL;
+               goto out;
+       }
+
+       trans = btrfs_start_transaction(root, 1);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               trans = NULL;
+               goto out;
+       }
+
+       sa->rtransid = trans->transid;
+       sa->rtime.sec = ct.tv_sec;
+       sa->rtime.nsec = ct.tv_nsec;
+
+       memcpy(root_item->received_uuid, sa->uuid, BTRFS_UUID_SIZE);
+       btrfs_set_root_stransid(root_item, sa->stransid);
+       btrfs_set_root_rtransid(root_item, sa->rtransid);
+       root_item->stime.sec = cpu_to_le64(sa->stime.sec);
+       root_item->stime.nsec = cpu_to_le32(sa->stime.nsec);
+       root_item->rtime.sec = cpu_to_le64(sa->rtime.sec);
+       root_item->rtime.nsec = cpu_to_le32(sa->rtime.nsec);
+
+       ret = btrfs_update_root(trans, root->fs_info->tree_root,
+                               &root->root_key, &root->root_item);
+       if (ret < 0) {
+               btrfs_end_transaction(trans, root);
+               trans = NULL;
+               goto out;
+       } else {
+               ret = btrfs_commit_transaction(trans, root);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = copy_to_user(arg, sa, sizeof(*sa));
+       if (ret)
+               ret = -EFAULT;
+
+out:
+       kfree(sa);
+       up_write(&root->fs_info->subvol_sem);
+       mnt_drop_write_file(file);
+       return ret;
+}
+
 long btrfs_ioctl(struct file *file, unsigned int
                cmd, unsigned long arg)
 {
@@ -3410,6 +3713,8 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_snap_create_v2(file, argp, 0);
        case BTRFS_IOC_SUBVOL_CREATE:
                return btrfs_ioctl_snap_create(file, argp, 1);
+       case BTRFS_IOC_SUBVOL_CREATE_V2:
+               return btrfs_ioctl_snap_create_v2(file, argp, 1);
        case BTRFS_IOC_SNAP_DESTROY:
                return btrfs_ioctl_snap_destroy(file, argp);
        case BTRFS_IOC_SUBVOL_GETFLAGS:
@@ -3471,10 +3776,20 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_balance_ctl(root, arg);
        case BTRFS_IOC_BALANCE_PROGRESS:
                return btrfs_ioctl_balance_progress(root, argp);
+       case BTRFS_IOC_SET_RECEIVED_SUBVOL:
+               return btrfs_ioctl_set_received_subvol(file, argp);
+       case BTRFS_IOC_SEND:
+               return btrfs_ioctl_send(file, argp);
        case BTRFS_IOC_GET_DEV_STATS:
-               return btrfs_ioctl_get_dev_stats(root, argp, 0);
-       case BTRFS_IOC_GET_AND_RESET_DEV_STATS:
-               return btrfs_ioctl_get_dev_stats(root, argp, 1);
+               return btrfs_ioctl_get_dev_stats(root, argp);
+       case BTRFS_IOC_QUOTA_CTL:
+               return btrfs_ioctl_quota_ctl(root, argp);
+       case BTRFS_IOC_QGROUP_ASSIGN:
+               return btrfs_ioctl_qgroup_assign(root, argp);
+       case BTRFS_IOC_QGROUP_CREATE:
+               return btrfs_ioctl_qgroup_create(root, argp);
+       case BTRFS_IOC_QGROUP_LIMIT:
+               return btrfs_ioctl_qgroup_limit(root, argp);
        }
 
        return -ENOTTY;