summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds-send.c35
-rw-r--r--ioctl.h15
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)
+ 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 <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[] = {
"\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 <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.
*/
-#define BTRFS_SEND_FLAG_NO_FILE_DATA 0x1
+#define BTRFS_SEND_FLAG_NO_FILE_DATA 0x1
+
+/*
+ * Do not add the leading stream header. Used when multiple snapshots
+ * are sent back to back.
+ */
+#define BTRFS_SEND_FLAG_OMIT_STREAM_HEADER 0x2
+
+/*
+ * 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.
+ */
+#define BTRFS_SEND_FLAG_OMIT_END_CMD 0x4
struct btrfs_ioctl_send_args {
__s64 send_fd; /* in */