diff options
authorStefan Behrens <>2013-04-10 19:10:56 +0200
committerDavid Sterba <>2013-08-09 14:32:29 +0200
commit6315410f25497f5700396f487c5fc2ab6762ae56 (patch)
parentcc94701a735f186295be2617c573ab8f3dc1817d (diff)
Btrfs-progs: add send option for using new end-cmd semantic
This commit adds a command line option to enable sending streams which make use of the new end-cmd semantic if multiple snapshots are sent back-to-back. The goal is to use the <end cmd> as an indication to stop reading the input stream. So far, the receiver could only use EOF to recognize the end. If the new command line option '-e' is set, this commit requires a kernel which is able to support the new flags in the send ioctl. New bits in the flags of the send ioctl will be set which cause EINVAL on old kernels. However, if the option '-e' is not set, it works with old and new kernels without any errors or any changed behavior. This used to be the encoding (with 2 snapshots in this example): <stream header> + <sequence of commands> + <end cmd> + <stream header> + <sequence of commands> + <end cmd> + EOF The new format (if the two new flags are used) is this one: <stream header> + <sequence of commands> + <sequence of commands> + <end cmd> Note that the currently existing receivers treat <end cmd> only as an indication that a new <stream header> is following. This means, you can just skip the sequence <end cmd> <stream header> without loosing compatibility. As long as an EOF is following, the currently existing receivers handle the new format (if the two new flags are used) exactly as the old one. Also note that the kernel interface was changed in a way that is backward compatible to old btrfs-progs tools. You set one or two bits in the flags field of the ioctl to enable the new behavior. Old tools set these flags to zero, thus getting exactly the same as they got with older kernels. And this is exactly what happens if the new '-e' option is not set, the new bits in the flags are not set and thus old kernels and new kernels are both supported. So what is the benefit of this change? The goal is to be able to use a single stream (one TCP connection) to multiplex a request/response handshake plus Btrfs send streams, all in the same stream. In this case you cannot evaluate an EOF condition as an end of the Btrfs send stream. You need something else, and the <end cmd> is just perfect for this purpose. Signed-off-by: Stefan Behrens <> Signed-off-by: David Sterba <> Signed-off-by: Chris Mason <>
2 files changed, 45 insertions, 5 deletions
diff --git a/cmds-send.c b/cmds-send.c
index 0057e6b8..72804a9a 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -244,7 +244,8 @@ out:
return ERR_PTR(ret);
-static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
+static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id,
+ int is_first_subvol, int is_last_subvol)
int ret;
pthread_t t_read;
@@ -298,11 +299,18 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
io_send.clone_sources = (__u64*)send->clone_sources;
io_send.clone_sources_count = send->clone_sources_count;
io_send.parent_root = parent_root_id;
+ if (!is_first_subvol)
+ if (!is_last_subvol)
+ io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
if (ret) {
ret = -errno;
fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
+ if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
+ fprintf(stderr,
+ "Try upgrading your kernel or don't use -e.\n");
goto out;
if (g_verbose > 0)
@@ -435,15 +443,19 @@ int cmd_send_start(int argc, char **argv)
u64 root_id;
u64 parent_root_id = 0;
int full_send = 1;
+ int new_end_cmd_semantic = 0;
memset(&send, 0, sizeof(send));
send.dump_fd = fileno(stdout);
- while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
+ while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
switch (c) {
case 'v':
+ case 'e':
+ new_end_cmd_semantic = 1;
+ break;
case 'c':
subvol = realpath(optarg, NULL);
if (!subvol) {
@@ -594,6 +606,9 @@ int cmd_send_start(int argc, char **argv)
for (i = optind; i < argc; i++) {
+ int is_first_subvol;
+ int is_last_subvol;
subvol = argv[i];
@@ -634,7 +649,17 @@ int cmd_send_start(int argc, char **argv)
goto out;
- ret = do_send(&send, root_id, parent_root_id);
+ if (new_end_cmd_semantic) {
+ /* require new kernel */
+ is_first_subvol = (i == optind);
+ is_last_subvol = (i == argc - 1);
+ } else {
+ /* be compatible to old and new kernel */
+ is_first_subvol = 1;
+ is_last_subvol = 1;
+ }
+ ret = do_send(&send, root_id, parent_root_id,
+ is_first_subvol, is_last_subvol);
if (ret < 0)
goto out;
@@ -664,7 +689,7 @@ static const char * const send_cmd_group_usage[] = {
const char * const cmd_send_usage[] = {
- "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
+ "btrfs send [-ve] [-p <parent>] [-c <clone-src>] <subvol>",
"Send the subvolume to stdout.",
"Sends the subvolume specified by <subvol> to stdout.",
"By default, this will send the whole subvolume. To do an incremental",
@@ -679,6 +704,8 @@ const char * const cmd_send_usage[] = {
"-v Enable verbose debug output. Each occurrence of",
" this option increases the verbose level more.",
+ "-e If sending multiple subvols at once, use the new",
+ " format and omit the end-cmd between the subvols.",
"-p <parent> Send an incremental stream from <parent> to",
" <subvol>.",
"-c <clone-src> Use this snapshot as a clone source for an ",
diff --git a/ioctl.h b/ioctl.h
index abe6dd42..f7864100 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -373,7 +373,20 @@ struct btrfs_ioctl_received_subvol_args {
* search of clone sources doesn't find an extent. UPDATE_EXTENT
* commands will be sent instead of WRITE commands.
+ * Do not add the leading stream header. Used when multiple snapshots
+ * are sent back to back.
+ */
+ * Omit the command at the end of the stream that indicated the end
+ * of the stream. This option is used when multiple snapshots are
+ * sent back to back.
+ */
struct btrfs_ioctl_send_args {
__s64 send_fd; /* in */