@@ -60,6 +60,28 @@ please see section *SUBVOLUME FLAGS* in ``btrfs-subvolume(8)``.
used to transfer changes. This mode is faster and is useful to show the
differences in metadata.
+--proto <N>
+ use send protocol version N
+
+ The default is 1, which was the original protocol version. Version 2
+ encodes file data slightly more efficiently; it is also required for
+ sending compressed data directly (see *--compressed-data*). Version 2
+ requires at least btrfs-progs 5.18 on both the sender and receiver and
+ at least Linux 5.18 on the sender. Passing 0 means to use the highest
+ version supported by the running kernel.
+
+--compressed-data
+ send data that is compressed on the filesystem directly without
+ decompressing it
+
+ If the receiver supports the *BTRFS_IOC_ENCODED_WRITE* ioctl (added in
+ Linux 5.18), it can also write it directly without decompressing it.
+ Otherwise, the receiver will fall back to decompressing it and writing
+ it normally.
+
+ This requires protocol version 2 or higher. If *--proto* was not used,
+ then *--compressed-data* implies *--proto 2*.
+
-q|--quiet
(deprecated) alias for global *-q* option
@@ -57,6 +57,8 @@ struct btrfs_send {
u64 clone_sources_count;
char *root_path;
+ u32 proto;
+ u32 proto_supported;
};
static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
@@ -259,6 +261,16 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
memset(&io_send, 0, sizeof(io_send));
io_send.send_fd = pipefd[1];
send->send_fd = pipefd[0];
+ io_send.flags = flags;
+
+ if (send->proto_supported > 1) {
+ /*
+ * Versioned stream supported, requesting default or specific
+ * number.
+ */
+ io_send.version = send->proto;
+ io_send.flags |= BTRFS_SEND_FLAG_VERSION;
+ }
if (!ret)
ret = pthread_create(&t_read, NULL, read_sent_data, send);
@@ -269,7 +281,6 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
goto out;
}
- io_send.flags = flags;
io_send.clone_sources = (__u64*)send->clone_sources;
io_send.clone_sources_count = send->clone_sources_count;
io_send.parent_root = parent_root_id;
@@ -421,6 +432,36 @@ static void free_send_info(struct btrfs_send *sctx)
sctx->root_path = NULL;
}
+static u32 get_sysfs_proto_supported(void)
+{
+ int fd;
+ int ret;
+ char buf[32] = {};
+ char *end = NULL;
+ u64 version;
+
+ fd = sysfs_open_file("features/send_stream_version");
+ if (fd < 0) {
+ /*
+ * No file is either no version support or old kernel with just
+ * v1.
+ */
+ return 1;
+ }
+ ret = sysfs_read_file(fd, buf, sizeof(buf));
+ close(fd);
+ if (ret <= 0)
+ return 1;
+ version = strtoull(buf, &end, 10);
+ if (version == ULLONG_MAX && errno == ERANGE)
+ return 1;
+ if (version > U32_MAX) {
+ warning("sysfs/send_stream_version too big: %llu", version);
+ version = 1;
+ }
+ return version;
+}
+
static const char * const cmd_send_usage[] = {
"btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
"Send the subvolume(s) to stdout.",
@@ -449,6 +490,11 @@ static const char * const cmd_send_usage[] = {
" does not contain any file data and thus cannot be used",
" to transfer changes. This mode is faster and useful to",
" show the differences in metadata.",
+ "--proto N use protocol version N, or 0 to use the highest version",
+ " supported by the sending kernel (default: 1)",
+ "--compressed-data",
+ " send data that is compressed on the filesystem directly",
+ " without decompressing it",
"-v|--verbose deprecated, alias for global -v option",
"-q|--quiet deprecated, alias for global -q option",
HELPINFO_INSERT_GLOBALS,
@@ -471,9 +517,11 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
int full_send = 1;
int new_end_cmd_semantic = 0;
u64 send_flags = 0;
+ u64 proto = 0;
memset(&send, 0, sizeof(send));
send.dump_fd = fileno(stdout);
+ send.proto = 1;
outname[0] = 0;
/*
@@ -489,11 +537,17 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
optind = 0;
while (1) {
- enum { GETOPT_VAL_SEND_NO_DATA = 256 };
+ enum {
+ GETOPT_VAL_SEND_NO_DATA = 256,
+ GETOPT_VAL_PROTO,
+ GETOPT_VAL_COMPRESSED_DATA,
+ };
static const struct option long_options[] = {
{ "verbose", no_argument, NULL, 'v' },
{ "quiet", no_argument, NULL, 'q' },
{ "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA },
+ { "proto", required_argument, NULL, GETOPT_VAL_PROTO },
+ { "compressed-data", no_argument, NULL, GETOPT_VAL_COMPRESSED_DATA },
{ NULL, 0, NULL, 0 }
};
int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
@@ -582,6 +636,18 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
case GETOPT_VAL_SEND_NO_DATA:
send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
break;
+ case GETOPT_VAL_PROTO:
+ proto = arg_strtou64(optarg);
+ if (proto > U32_MAX) {
+ error("protocol version number too big %llu", proto);
+ ret = 1;
+ goto out;
+ }
+ send.proto = proto;
+ break;
+ case GETOPT_VAL_COMPRESSED_DATA:
+ send_flags |= BTRFS_SEND_FLAG_COMPRESSED;
+ break;
default:
usage_unknown_option(cmd, argv);
}
@@ -689,6 +755,36 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && bconf.verbose > 1)
if (bconf.verbose > 1)
fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
+ send.proto_supported = get_sysfs_proto_supported();
+ if (send.proto_supported == 1) {
+ if (send.proto > send.proto_supported) {
+ error("requested version %u but kernel supports only %u",
+ send.proto, send.proto_supported);
+ ret = -EPROTO;
+ goto out;
+ }
+ }
+ if (send_flags & BTRFS_SEND_FLAG_COMPRESSED) {
+ /*
+ * If no protocol version was explicitly requested, then
+ * --compressed-data implies --proto 2.
+ */
+ if (send.proto == 1 && !proto)
+ send.proto = 2;
+
+ if (send.proto == 1) {
+ error("--compressed-data requires protocol version >= 2 (requested 1)");
+ ret = -EINVAL;
+ goto out;
+ } else if (send.proto == 0 && send.proto_supported < 2) {
+ error("kernel does not support --compressed-data");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ if (bconf.verbose > 1)
+ fprintf(stderr, "Protocol version requested: %u (supported %u)\n",
+ send.proto, send.proto_supported);
for (i = optind; i < argc; i++) {
int is_first_subvol;
@@ -655,10 +655,24 @@ BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args_32) == 192);
*/
#define BTRFS_SEND_FLAG_OMIT_END_CMD 0x4
+/*
+ * Read the protocol version in the structure
+ */
+#define BTRFS_SEND_FLAG_VERSION 0x8
+
+/*
+ * Send compressed data using the ENCODED_WRITE command instead of decompressing
+ * the data and sending it with the WRITE command. This requires protocol
+ * version >= 2.
+ */
+#define BTRFS_SEND_FLAG_COMPRESSED 0x10
+
#define BTRFS_SEND_FLAG_MASK \
(BTRFS_SEND_FLAG_NO_FILE_DATA | \
BTRFS_SEND_FLAG_OMIT_STREAM_HEADER | \
- BTRFS_SEND_FLAG_OMIT_END_CMD)
+ BTRFS_SEND_FLAG_OMIT_END_CMD | \
+ BTRFS_SEND_FLAG_VERSION | \
+ BTRFS_SEND_FLAG_COMPRESSED)
struct btrfs_ioctl_send_args {
__s64 send_fd; /* in */
@@ -666,7 +680,8 @@ struct btrfs_ioctl_send_args {
__u64 __user *clone_sources; /* in */
__u64 parent_root; /* in */
__u64 flags; /* in */
- __u64 reserved[4]; /* in */
+ __u32 version; /* in */
+ __u8 reserved[28]; /* in */
};
/*
* Size of structure depends on pointer width, was not caught in the early
@@ -31,7 +31,7 @@ extern "C" {
#endif
#define BTRFS_SEND_STREAM_MAGIC "btrfs-stream"
-#define BTRFS_SEND_STREAM_VERSION 1
+#define BTRFS_SEND_STREAM_VERSION 2
/*
* In send stream v1, no command is larger than 64k. In send stream v2, no limit