|
@@ -85,6 +85,7 @@ struct send_ctx {
|
|
u32 send_max_size;
|
|
u32 send_max_size;
|
|
u64 total_send_size;
|
|
u64 total_send_size;
|
|
u64 cmd_send_size[BTRFS_SEND_C_MAX + 1];
|
|
u64 cmd_send_size[BTRFS_SEND_C_MAX + 1];
|
|
|
|
+ u64 flags; /* 'flags' member of btrfs_ioctl_send_args is u64 */
|
|
|
|
|
|
struct vfsmount *mnt;
|
|
struct vfsmount *mnt;
|
|
|
|
|
|
@@ -3709,6 +3710,39 @@ out:
|
|
return ret;
|
|
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,
|
|
static int send_write_or_clone(struct send_ctx *sctx,
|
|
struct btrfs_path *path,
|
|
struct btrfs_path *path,
|
|
struct btrfs_key *key,
|
|
struct btrfs_key *key,
|
|
@@ -3744,7 +3778,11 @@ static int send_write_or_clone(struct send_ctx *sctx,
|
|
goto out;
|
|
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) {
|
|
while (pos < len) {
|
|
l = len - pos;
|
|
l = len - pos;
|
|
if (l > BTRFS_SEND_READ_SIZE)
|
|
if (l > BTRFS_SEND_READ_SIZE)
|
|
@@ -3757,10 +3795,7 @@ static int send_write_or_clone(struct send_ctx *sctx,
|
|
pos += ret;
|
|
pos += ret;
|
|
}
|
|
}
|
|
ret = 0;
|
|
ret = 0;
|
|
- } else {
|
|
|
|
- ret = send_clone(sctx, offset, len, clone_root);
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
out:
|
|
out:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -4560,6 +4595,11 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (arg->flags & ~BTRFS_SEND_FLAG_NO_FILE_DATA) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
sctx = kzalloc(sizeof(struct send_ctx), GFP_NOFS);
|
|
sctx = kzalloc(sizeof(struct send_ctx), GFP_NOFS);
|
|
if (!sctx) {
|
|
if (!sctx) {
|
|
ret = -ENOMEM;
|
|
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_RADIX_TREE(&sctx->name_cache, GFP_NOFS);
|
|
INIT_LIST_HEAD(&sctx->name_cache_list);
|
|
INIT_LIST_HEAD(&sctx->name_cache_list);
|
|
|
|
|
|
|
|
+ sctx->flags = arg->flags;
|
|
|
|
+
|
|
sctx->send_filp = fget(arg->send_fd);
|
|
sctx->send_filp = fget(arg->send_fd);
|
|
if (IS_ERR(sctx->send_filp)) {
|
|
if (IS_ERR(sctx->send_filp)) {
|
|
ret = PTR_ERR(sctx->send_filp);
|
|
ret = PTR_ERR(sctx->send_filp);
|