@@ -38,6 +38,19 @@ A subvolume is made read-only after the receiving process finishes successfully
-f <FILE>::
read the stream from <FILE> instead of stdin,
+--listen-addr <url>::
+Address to listen on. It can be an IP address or a domain name.
+
+--tcp-port <port>::
+The local port of the TLS connection.
+
+--tls-key <file>::
+Use the key from file; otherwise read key from stdin. Key file is first parsed
+as PEM format; if parsing fails, file content is treated as binary key.
+
+--tls-mode <mode>::
+Use none, tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm.
+
-C|--chroot::
confine the process to 'path' using `chroot`(1)
@@ -49,6 +49,15 @@ use this snapshot as a clone source for an incremental send (multiple allowed)
-f <outfile>::
output is normally written to standard output so it can be, for example, piped
to btrfs receive. Use this option to write it to a file instead.
+--conn-addr <url>::
+Address of remote receiver. It can be an IP address or a domain name.
+--tcp-port <port>::
+The remote port of the TLS connection.
+--tls-key <file>::
+Use the key from file; otherwise read key from stdin. Key file is first parsed
+as PEM format; if parsing fails, file content is treated as binary key.
+--tls-mode <mode>::
+Use none, tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm.
--no-data::
send in 'NO_FILE_DATA' mode
+
@@ -53,8 +53,11 @@
#include "common/help.h"
#include "common/path-utils.h"
-struct btrfs_receive
-{
+#if KTLS_SEND_RECV == 1
+#include "common/ktls.h"
+#endif
+
+struct btrfs_receive {
int mnt_fd;
int dest_dir_fd;
@@ -1216,7 +1219,7 @@ out:
return ret;
}
-static const char * const cmd_receive_usage[] = {
+static const char *const cmd_receive_usage[] = {
"btrfs receive [options] <mount>\n"
"btrfs receive --dump [options]",
"Receive subvolumes from a stream",
@@ -1229,22 +1232,28 @@ static const char * const cmd_receive_usage[] = {
"After receiving a subvolume, it is immediately set to",
"read-only.",
"",
- "-q|--quiet suppress all messages, except errors",
- "-f FILE read the stream from FILE instead of stdin",
- "-e terminate after receiving an <end cmd> marker in the stream.",
- " Without this option the receiver side terminates only in case",
- " of an error on end of file.",
- "-C|--chroot confine the process to <mount> using chroot",
+ "-q|--quiet suppress all messages, except errors",
+ "-f FILE read the stream from FILE instead of stdin",
+#if KTLS_SEND_RECV == 1
+ "--listen-addr <url> Address to listen on for incoming TLS connection.",
+ "--tcp-port <port> The remote port of the TLS connection",
+ "--tls-key <file> Use the key from file; otherwise read key from stdin.",
+ "--tls-mode <mode> Use none, tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm."
+#endif
+ "-e terminate after receiving an <end cmd> marker in the stream.",
+ " Without this option the receiver side terminates only in case",
+ " of an error on end of file.",
+ "-C|--chroot confine the process to <mount> using chroot",
"-E|--max-errors NERR",
- " terminate as soon as NERR errors occur while",
- " stream processing commands from the stream.",
- " Default value is 1. A value of 0 means no limit.",
- "-m ROOTMOUNT the root mount point of the destination filesystem.",
- " If /proc is not accessible, use this to tell us where",
- " this file system is mounted.",
- "--dump dump stream metadata, one line per operation,",
- " does not require the MOUNT parameter",
- "-v deprecated, alias for global -v option",
+ " terminate as soon as NERR errors occur while",
+ " stream processing commands from the stream.",
+ " Default value is 1. A value of 0 means no limit.",
+ "-m ROOTMOUNT the root mount point of the destination filesystem.",
+ " If /proc is not accessible, use this to tell us where",
+ " this file system is mounted.",
+ "--dump dump stream metadata, one line per operation,",
+ " does not require the MOUNT parameter",
+ "-v deprecated, alias for global -v option",
HELPINFO_INSERT_GLOBALS,
HELPINFO_INSERT_VERBOSE,
HELPINFO_INSERT_QUIET,
@@ -1261,6 +1270,23 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
u64 max_errors = 1;
int dump = 0;
int ret = 0;
+#if KTLS_SEND_RECV == 1
+ enum {
+ KTLS_IDX_ADDR = 0,
+ KTLS_IDX_PORT = 1,
+ KTLS_IDX_KEY = 2,
+ KTLS_IDX_TLS_MODE = 3,
+ KTLS_IDX_SIZE
+ };
+ char *ktls_args[KTLS_IDX_SIZE];
+ struct ktls_session *ktls_session = NULL;
+ char ktls_username[LOGIN_NAME_MAX] = "btrfs";
+ u32 i = 0;
+ size_t arg_len = 0;
+ int arg_idx = 0;
+
+ explicit_bzero(ktls_args, sizeof(ktls_args));
+#endif
memset(&rctx, 0, sizeof(rctx));
rctx.mnt_fd = -1;
@@ -1285,10 +1311,25 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
optind = 0;
while (1) {
int c;
- enum { GETOPT_VAL_DUMP = 257 };
+ enum {
+ GETOPT_VAL_DUMP = 257,
+ GETOPT_VAL_ADDR = 300,
+ GETOPT_VAL_PORT = 301,
+ GETOPT_VAL_KEY = 302,
+ GETOPT_VAL_TLS_MODE = 303,
+ };
static const struct option long_opts[] = {
{ "max-errors", required_argument, NULL, 'E' },
{ "chroot", no_argument, NULL, 'C' },
+#if KTLS_SEND_RECV == 1
+ { "listen-addr", required_argument, NULL,
+ GETOPT_VAL_ADDR },
+ { "tcp-port", required_argument, NULL,
+ GETOPT_VAL_PORT },
+ { "tls-key", required_argument, NULL, GETOPT_VAL_KEY },
+ { "tls-mode", required_argument, NULL,
+ GETOPT_VAL_TLS_MODE },
+#endif
{ "dump", no_argument, NULL, GETOPT_VAL_DUMP },
{ "quiet", no_argument, NULL, 'q' },
{ NULL, 0, NULL, 0 }
@@ -1330,6 +1371,27 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
goto out;
}
break;
+#if KTLS_SEND_RECV == 1
+ case GETOPT_VAL_ADDR:
+ case GETOPT_VAL_PORT:
+ case GETOPT_VAL_KEY:
+ case GETOPT_VAL_TLS_MODE:
+ arg_len = strlen(optarg);
+ arg_idx = c - GETOPT_VAL_ADDR;
+ ktls_args[arg_idx] = (char *)malloc(arg_len + 1);
+ if (!ktls_args[arg_idx]) {
+ error("fail to allocate buffer (%zu)", arg_len);
+ ret = 1;
+ goto out;
+ }
+ if (arg_copy_path(ktls_args[arg_idx], optarg,
+ arg_len + 1)) {
+ error("argument too long (%zu)", arg_len);
+ ret = 1;
+ goto out;
+ }
+ break;
+#endif
case GETOPT_VAL_DUMP:
dump = 1;
break;
@@ -1353,6 +1415,50 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
}
}
+#if KTLS_SEND_RECV == 1
+ if (ktls_args[KTLS_IDX_ADDR]) {
+ if (fromfile[0]) {
+ error("cannot send to both ktls and file");
+ ret = 1;
+ goto out;
+ }
+
+ if (!ktls_args[KTLS_IDX_PORT]) {
+ error("no remote ktls port");
+ ret = 1;
+ goto out;
+ }
+
+ ktls_session = ktls_create_session(false);
+
+ if (ktls_args[KTLS_IDX_TLS_MODE]) {
+ if (ktls_set_tls_mode(ktls_session,
+ ktls_args[KTLS_IDX_TLS_MODE]))
+ goto out;
+ }
+
+ if (ktls_args[KTLS_IDX_KEY]) {
+ if (ktls_set_psk_session_from_keyfile(
+ ktls_session, ktls_username,
+ ktls_args[KTLS_IDX_KEY])) {
+ goto out;
+ };
+ } else {
+ if (ktls_set_psk_session_from_password_prompt(
+ ktls_session, ktls_username)) {
+ goto out;
+ }
+ }
+
+ receive_fd = ktls_create_sock_oneshot(ktls_session,
+ ktls_args[KTLS_IDX_ADDR],
+ ktls_args[KTLS_IDX_PORT]);
+
+ /* socket implies honor end cmd*/
+ rctx.honor_end_cmd = 1;
+ }
+#endif
+
if (dump) {
struct btrfs_dump_send_args dump_args;
@@ -1370,9 +1476,16 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
ret = do_receive(&rctx, tomnt, realmnt, receive_fd, max_errors);
}
+out:
+#if KTLS_SEND_RECV == 1
+ for (i = KTLS_IDX_ADDR; i < KTLS_IDX_SIZE; i++) {
+ if (ktls_args[i])
+ free(ktls_args[i]);
+ }
+ ktls_destroy_session(ktls_session);
+#endif
if (receive_fd != fileno(stdin))
close(receive_fd);
-out:
return !!ret;
}
@@ -46,6 +46,10 @@
#include "common/help.h"
#include "common/path-utils.h"
+#if KTLS_SEND_RECV == 1
+#include "common/ktls.h"
+#endif
+
#define SEND_BUFFER_SIZE SZ_64K
@@ -424,7 +428,7 @@ static void free_send_info(struct btrfs_send *sctx)
subvol_uuid_search_finit(&sctx->sus);
}
-static const char * const cmd_send_usage[] = {
+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.",
"Sends the subvolume(s) specified by <subvol> to stdout.",
@@ -439,21 +443,27 @@ static const char * const cmd_send_usage[] = {
"which case 'btrfs send' will determine a suitable parent among the",
"clone sources itself.",
"",
- "-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 ",
- " incremental send (multiple allowed)",
- "-f <outfile> Output is normally written to stdout. To write to",
- " a file, use this option. An alternative would be to",
- " use pipes.",
- "--no-data send in NO_FILE_DATA mode, Note: the output stream",
- " 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.",
- "-v|--verbose deprecated, alias for global -v option",
- "-q|--quiet deprecated, alias for global -q option",
+ "-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 ",
+ " incremental send (multiple allowed)",
+ "-f <outfile> Output is normally written to stdout. To write to",
+ " a file, use this option. An alternative would be to",
+ " use pipes.",
+#if KTLS_SEND_RECV == 1
+ "--conn-addr <url> Address of remote receiver.",
+ "--tcp-port <port> The remote port of the TLS connection",
+ "--tls-key <file> Use the key from file; otherwise read key from stdin.",
+ "--tls-mode <mode> Use none, tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm."
+#endif
+ "--no-data send in NO_FILE_DATA mode, Note: the output stream",
+ " 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.",
+ "-v|--verbose deprecated, alias for global -v option",
+ "-q|--quiet deprecated, alias for global -q option",
HELPINFO_INSERT_GLOBALS,
HELPINFO_INSERT_VERBOSE,
HELPINFO_INSERT_QUIET,
@@ -474,6 +484,22 @@ 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;
+#if KTLS_SEND_RECV == 1
+ enum {
+ KTLS_IDX_ADDR = 0,
+ KTLS_IDX_PORT = 1,
+ KTLS_IDX_KEY = 2,
+ KTLS_IDX_TLS_MODE = 3,
+ KTLS_IDX_SIZE
+ };
+ char *ktls_args[KTLS_IDX_SIZE];
+ struct ktls_session *ktls_session = NULL;
+ char ktls_username[LOGIN_NAME_MAX] = "btrfs";
+ size_t arg_len = 0;
+ int arg_idx = 0;
+
+ explicit_bzero(ktls_args, sizeof(ktls_args));
+#endif
memset(&send, 0, sizeof(send));
send.dump_fd = fileno(stdout);
@@ -492,11 +518,26 @@ 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_ADDR = 300,
+ GETOPT_VAL_PORT = 301,
+ GETOPT_VAL_KEY = 302,
+ GETOPT_VAL_TLS_MODE = 303,
+ };
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 }
+ { "no-data", no_argument, NULL,
+ GETOPT_VAL_SEND_NO_DATA },
+ { "conn-addr", required_argument, NULL,
+ GETOPT_VAL_ADDR },
+ { "tcp-port", required_argument, NULL,
+ GETOPT_VAL_PORT },
+ { "tls-key", required_argument, NULL, GETOPT_VAL_KEY },
+ { "tls-mode", required_argument, NULL,
+ GETOPT_VAL_TLS_MODE },
+ { NULL, 0, NULL, 0 }
};
int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
@@ -581,6 +622,27 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
error("option -i was removed, use -c instead");
ret = 1;
goto out;
+#if KTLS_SEND_RECV == 1
+ case GETOPT_VAL_ADDR:
+ case GETOPT_VAL_PORT:
+ case GETOPT_VAL_KEY:
+ case GETOPT_VAL_TLS_MODE:
+ arg_len = strlen(optarg);
+ arg_idx = c - GETOPT_VAL_ADDR;
+ ktls_args[arg_idx] = (char *)malloc(arg_len + 1);
+ if (!ktls_args[arg_idx]) {
+ error("fail to allocate buffer (%zu)", arg_len);
+ ret = 1;
+ goto out;
+ }
+ if (arg_copy_path(ktls_args[arg_idx], optarg,
+ arg_len + 1)) {
+ error("argument too long (%zu)", arg_len);
+ ret = 1;
+ goto out;
+ }
+ break;
+#endif
case GETOPT_VAL_SEND_NO_DATA:
send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
break;
@@ -613,6 +675,53 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
goto out;
}
}
+#if KTLS_SEND_RECV == 1
+ if (ktls_args[KTLS_IDX_ADDR]) {
+ if (outname[0]) {
+ error("cannot send to both ktls and file");
+ ret = 1;
+ goto out;
+ }
+
+ if (!ktls_args[KTLS_IDX_PORT]) {
+ error("no remote ktls port");
+ ret = 1;
+ goto out;
+ }
+
+ if (!ktls_args[KTLS_IDX_PORT]) {
+ error("fail to create ktls session");
+ ret = 1;
+ goto out;
+ }
+
+ ktls_session = ktls_create_session(true);
+
+ if (ktls_args[KTLS_IDX_TLS_MODE]) {
+ if (ktls_set_tls_mode(ktls_session,
+ ktls_args[KTLS_IDX_TLS_MODE]))
+ goto out;
+ }
+
+ if (ktls_args[KTLS_IDX_KEY]) {
+ if (ktls_set_psk_session_from_keyfile(
+ ktls_session, ktls_username,
+ ktls_args[KTLS_IDX_KEY])) {
+ goto out;
+ };
+ } else {
+ if (ktls_set_psk_session_from_password_prompt(
+ ktls_session, ktls_username)) {
+ goto out;
+ }
+ }
+
+ send.dump_fd =
+ ktls_create_sock_oneshot(ktls_session,
+ ktls_args[KTLS_IDX_ADDR],
+ ktls_args[KTLS_IDX_PORT]);
+ }
+#endif
if (isatty(send.dump_fd)) {
error(
@@ -755,6 +864,16 @@ out:
free(snapshot_parent);
free(send.clone_sources);
free_send_info(&send);
+#if KTLS_SEND_RECV == 1
+ for (i = KTLS_IDX_ADDR; i < KTLS_IDX_SIZE; i++) {
+ if (ktls_args[i])
+ free(ktls_args[i]);
+ }
+ ktls_destroy_session(ktls_session);
+#endif
+ if (send.dump_fd != fileno(stdin))
+ close(send.dump_fd);
+
return !!ret;
}
DEFINE_SIMPLE_COMMAND(send, "send");