btrfs: add "no file data" flag to btrfs send ioctl
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / fs / btrfs / send.c
index 614da0d44d56b2dd183221ab51fa85d7d1071918..68da757615ae55b8b9eaec3a68d6ec346e856761 100644 (file)
@@ -85,6 +85,7 @@ struct send_ctx {
        u32 send_max_size;
        u64 total_send_size;
        u64 cmd_send_size[BTRFS_SEND_C_MAX + 1];
+       u64 flags;      /* 'flags' member of btrfs_ioctl_send_args is u64 */
 
        struct vfsmount *mnt;
 
@@ -3709,6 +3710,39 @@ out:
        return ret;
 }
 
+/*
+ * Send an update extent command to user space.
+ */
+static int send_update_extent(struct send_ctx *sctx,
+                             u64 offset, u32 len)
+{
+       int ret = 0;
+       struct fs_path *p;
+
+       p = fs_path_alloc(sctx);
+       if (!p)
+               return -ENOMEM;
+
+       ret = begin_cmd(sctx, BTRFS_SEND_C_UPDATE_EXTENT);
+       if (ret < 0)
+               goto out;
+
+       ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p);
+       if (ret < 0)
+               goto out;
+
+       TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
+       TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset);
+       TLV_PUT_U64(sctx, BTRFS_SEND_A_SIZE, len);
+
+       ret = send_cmd(sctx);
+
+tlv_put_failure:
+out:
+       fs_path_free(sctx, p);
+       return ret;
+}
+
 static int send_write_or_clone(struct send_ctx *sctx,
                               struct btrfs_path *path,
                               struct btrfs_key *key,
@@ -3744,7 +3778,11 @@ static int send_write_or_clone(struct send_ctx *sctx,
                goto out;
        }
 
-       if (!clone_root) {
+       if (clone_root) {
+               ret = send_clone(sctx, offset, len, clone_root);
+       } else if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA) {
+               ret = send_update_extent(sctx, offset, len);
+       } else {
                while (pos < len) {
                        l = len - pos;
                        if (l > BTRFS_SEND_READ_SIZE)
@@ -3757,10 +3795,7 @@ static int send_write_or_clone(struct send_ctx *sctx,
                        pos += ret;
                }
                ret = 0;
-       } else {
-               ret = send_clone(sctx, offset, len, clone_root);
        }
-
 out:
        return ret;
 }
@@ -4560,6 +4595,11 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
                goto out;
        }
 
+       if (arg->flags & ~BTRFS_SEND_FLAG_NO_FILE_DATA) {
+               ret = -EINVAL;
+               goto out;
+       }
+
        sctx = kzalloc(sizeof(struct send_ctx), GFP_NOFS);
        if (!sctx) {
                ret = -ENOMEM;
@@ -4571,6 +4611,8 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
        INIT_RADIX_TREE(&sctx->name_cache, GFP_NOFS);
        INIT_LIST_HEAD(&sctx->name_cache_list);
 
+       sctx->flags = arg->flags;
+
        sctx->send_filp = fget(arg->send_fd);
        if (IS_ERR(sctx->send_filp)) {
                ret = PTR_ERR(sctx->send_filp);