From 6315410f25497f5700396f487c5fc2ab6762ae56 Mon Sep 17 00:00:00 2001 From: Stefan Behrens Date: Wed, 10 Apr 2013 19:10:56 +0200 Subject: 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 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): + + + + + + EOF The new format (if the two new flags are used) is this one: + + + Note that the currently existing receivers treat only as an indication that a new is following. This means, you can just skip the sequence 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 is just perfect for this purpose. Signed-off-by: Stefan Behrens Signed-off-by: David Sterba Signed-off-by: Chris Mason --- cmds-send.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'cmds-send.c') diff --git a/cmds-send.c b/cmds-send.c index 0057e6b..72804a9 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) + io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER; + 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, strerror(-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': g_verbose++; break; + 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; + free(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 ] [-c ] ", + "btrfs send [-ve] [-p ] [-c ] ", "Send the subvolume to stdout.", "Sends the subvolume specified by to stdout.", "By default, this will send the whole subvolume. To do an incremental", @@ -679,6 +704,8 @@ const char * const cmd_send_usage[] = { "\n", "-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 Send an incremental stream from to", " .", "-c Use this snapshot as a clone source for an ", -- cgit v1.2.1