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;
 
        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,
                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)
                        pos += ret;
                }
                ret = 0;
-       } else {
-               ret = send_clone(sctx, offset, len, clone_root);
        }
-
 out:
        return ret;
 }
                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;
        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);
 
        __u64   reserved[16];           /* in */
 };
 
+/*
+ * Caller doesn't want file data in the send stream, even if the
+ * search of clone sources doesn't find an extent. UPDATE_EXTENT
+ * commands will be sent instead of WRITE commands.
+ */
+#define BTRFS_SEND_FLAG_NO_FILE_DATA     0x1
+
 struct btrfs_ioctl_send_args {
        __s64 send_fd;                  /* in */
        __u64 clone_sources_count;      /* in */