aboutsummaryrefslogtreecommitdiff
path: root/block/ssh.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/ssh.c')
-rw-r--r--block/ssh.c290
1 files changed, 161 insertions, 129 deletions
diff --git a/block/ssh.c b/block/ssh.c
index ff9929497d..ab3acf0c22 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -35,6 +35,7 @@
#include "qemu/sockets.h"
#include "qemu/uri.h"
#include "qapi/qapi-visit-sockets.h"
+#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-input-visitor.h"
@@ -430,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
}
static int check_host_key(BDRVSSHState *s, const char *host, int port,
- const char *host_key_check, Error **errp)
+ SshHostKeyCheck *hkc, Error **errp)
{
- /* host_key_check=no */
- if (strcmp(host_key_check, "no") == 0) {
- return 0;
- }
-
- /* host_key_check=md5:xx:yy:zz:... */
- if (strncmp(host_key_check, "md5:", 4) == 0) {
- return check_host_key_hash(s, &host_key_check[4],
- LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
- }
+ SshHostKeyCheckMode mode;
- /* host_key_check=sha1:xx:yy:zz:... */
- if (strncmp(host_key_check, "sha1:", 5) == 0) {
- return check_host_key_hash(s, &host_key_check[5],
- LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
+ if (hkc) {
+ mode = hkc->mode;
+ } else {
+ mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS;
}
- /* host_key_check=yes */
- if (strcmp(host_key_check, "yes") == 0) {
+ switch (mode) {
+ case SSH_HOST_KEY_CHECK_MODE_NONE:
+ return 0;
+ case SSH_HOST_KEY_CHECK_MODE_HASH:
+ if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
+ return check_host_key_hash(s, hkc->u.hash.hash,
+ LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
+ } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
+ return check_host_key_hash(s, hkc->u.hash.hash,
+ LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
+ }
+ g_assert_not_reached();
+ break;
+ case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
return check_host_key_knownhosts(s, host, port, errp);
+ default:
+ g_assert_not_reached();
}
- error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
return -EINVAL;
}
@@ -544,16 +549,6 @@ static QemuOptsList ssh_runtime_opts = {
.help = "Port to connect to",
},
{
- .name = "path",
- .type = QEMU_OPT_STRING,
- .help = "Path of the image on the host",
- },
- {
- .name = "user",
- .type = QEMU_OPT_STRING,
- .help = "User as which to connect",
- },
- {
.name = "host_key_check",
.type = QEMU_OPT_STRING,
.help = "Defines how and what to check the host key against",
@@ -562,12 +557,13 @@ static QemuOptsList ssh_runtime_opts = {
},
};
-static bool ssh_process_legacy_socket_options(QDict *output_opts,
- QemuOpts *legacy_opts,
- Error **errp)
+static bool ssh_process_legacy_options(QDict *output_opts,
+ QemuOpts *legacy_opts,
+ Error **errp)
{
const char *host = qemu_opt_get(legacy_opts, "host");
const char *port = qemu_opt_get(legacy_opts, "port");
+ const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check");
if (!host && port) {
error_setg(errp, "port may not be used without host");
@@ -579,26 +575,56 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
qdict_put_str(output_opts, "server.port", port ?: stringify(22));
}
+ if (host_key_check) {
+ if (strcmp(host_key_check, "no") == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "none");
+ } else if (strncmp(host_key_check, "md5:", 4) == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "hash");
+ qdict_put_str(output_opts, "host-key-check.type", "md5");
+ qdict_put_str(output_opts, "host-key-check.hash",
+ &host_key_check[4]);
+ } else if (strncmp(host_key_check, "sha1:", 5) == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "hash");
+ qdict_put_str(output_opts, "host-key-check.type", "sha1");
+ qdict_put_str(output_opts, "host-key-check.hash",
+ &host_key_check[5]);
+ } else if (strcmp(host_key_check, "yes") == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "known_hosts");
+ } else {
+ error_setg(errp, "unknown host_key_check setting (%s)",
+ host_key_check);
+ return false;
+ }
+ }
+
return true;
}
-static InetSocketAddress *ssh_config(QDict *options, Error **errp)
+static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
{
- InetSocketAddress *inet = NULL;
- QDict *addr = NULL;
- QObject *crumpled_addr = NULL;
- Visitor *iv = NULL;
- Error *local_error = NULL;
-
- qdict_extract_subqdict(options, &addr, "server.");
- if (!qdict_size(addr)) {
- error_setg(errp, "SSH server address missing");
- goto out;
+ BlockdevOptionsSsh *result = NULL;
+ QemuOpts *opts = NULL;
+ Error *local_err = NULL;
+ QObject *crumpled;
+ const QDictEntry *e;
+ Visitor *v;
+
+ /* Translate legacy options */
+ opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
}
- crumpled_addr = qdict_crumple(addr, errp);
- if (!crumpled_addr) {
- goto out;
+ if (!ssh_process_legacy_options(options, opts, errp)) {
+ goto fail;
+ }
+
+ /* Create the QAPI object */
+ crumpled = qdict_crumple(options, errp);
+ if (crumpled == NULL) {
+ goto fail;
}
/*
@@ -609,51 +635,37 @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp)
* but when they come from -drive, they're all QString. The
* visitor expects the former.
*/
- iv = qobject_input_visitor_new(crumpled_addr);
- visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
- if (local_error) {
- error_propagate(errp, local_error);
- goto out;
+ v = qobject_input_visitor_new(crumpled);
+ visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
+ visit_free(v);
+ qobject_decref(crumpled);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
}
-out:
- QDECREF(addr);
- qobject_decref(crumpled_addr);
- visit_free(iv);
- return inet;
+ /* Remove the processed options from the QDict (the visitor processes
+ * _all_ options in the QDict) */
+ while ((e = qdict_first(options))) {
+ qdict_del(options, e->key);
+ }
+
+fail:
+ qemu_opts_del(opts);
+ return result;
}
-static int connect_to_ssh(BDRVSSHState *s, QDict *options,
+static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
int ssh_flags, int creat_mode, Error **errp)
{
int r, ret;
- QemuOpts *opts = NULL;
- Error *local_err = NULL;
- const char *user, *path, *host_key_check;
+ const char *user;
long port = 0;
- opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, options, &local_err);
- if (local_err) {
- ret = -EINVAL;
- error_propagate(errp, local_err);
- goto err;
- }
-
- if (!ssh_process_legacy_socket_options(options, opts, errp)) {
- ret = -EINVAL;
- goto err;
- }
-
- path = qemu_opt_get(opts, "path");
- if (!path) {
- ret = -EINVAL;
- error_setg(errp, "No path was specified");
- goto err;
- }
-
- user = qemu_opt_get(opts, "user");
- if (!user) {
+ if (opts->has_user) {
+ user = opts->user;
+ } else {
user = g_get_user_name();
if (!user) {
error_setg_errno(errp, errno, "Can't get user name");
@@ -662,17 +674,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
}
}
- host_key_check = qemu_opt_get(opts, "host_key_check");
- if (!host_key_check) {
- host_key_check = "yes";
- }
-
/* Pop the config into our state object, Exit if invalid */
- s->inet = ssh_config(options, errp);
- if (!s->inet) {
- ret = -EINVAL;
- goto err;
- }
+ s->inet = opts->server;
+ opts->server = NULL;
if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
error_setg(errp, "Use only numeric port value");
@@ -707,8 +711,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
}
/* Check the remote host's key against known_hosts. */
- ret = check_host_key(s, s->inet->host, port, host_key_check,
- errp);
+ ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
if (ret < 0) {
goto err;
}
@@ -729,16 +732,16 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
/* Open the remote file. */
DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
- path, ssh_flags, creat_mode);
- s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
+ opts->path, ssh_flags, creat_mode);
+ s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
+ creat_mode);
if (!s->sftp_handle) {
- session_error_setg(errp, s, "failed to open remote file '%s'", path);
+ session_error_setg(errp, s, "failed to open remote file '%s'",
+ opts->path);
ret = -EINVAL;
goto err;
}
- qemu_opts_del(opts);
-
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
if (r < 0) {
sftp_error_setg(errp, s, "failed to read file attributes");
@@ -764,8 +767,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
}
s->session = NULL;
- qemu_opts_del(opts);
-
return ret;
}
@@ -773,6 +774,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
Error **errp)
{
BDRVSSHState *s = bs->opaque;
+ BlockdevOptionsSsh *opts;
int ret;
int ssh_flags;
@@ -783,8 +785,13 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
ssh_flags |= LIBSSH2_FXF_WRITE;
}
+ opts = ssh_parse_options(options, errp);
+ if (opts == NULL) {
+ return -EINVAL;
+ }
+
/* Start up SSH. */
- ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
+ ret = connect_to_ssh(s, opts, ssh_flags, 0, errp);
if (ret < 0) {
goto err;
}
@@ -792,6 +799,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
/* Go non-blocking. */
libssh2_session_set_blocking(s->session, 0);
+ qapi_free_BlockdevOptionsSsh(opts);
+
return 0;
err:
@@ -800,6 +809,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
}
s->sock = -1;
+ qapi_free_BlockdevOptionsSsh(opts);
+
return ret;
}
@@ -843,51 +854,71 @@ static QemuOptsList ssh_create_opts = {
}
};
+static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
+{
+ BlockdevCreateOptionsSsh *opts = &options->u.ssh;
+ BDRVSSHState s;
+ int ret;
+
+ assert(options->driver == BLOCKDEV_DRIVER_SSH);
+
+ ssh_state_init(&s);
+
+ ret = connect_to_ssh(&s, opts->location,
+ LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
+ LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
+ 0644, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ if (opts->size > 0) {
+ ret = ssh_grow_file(&s, opts->size, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+
+ ret = 0;
+fail:
+ ssh_state_free(&s);
+ return ret;
+}
+
static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
Error **errp)
{
- int r, ret;
- int64_t total_size = 0;
+ BlockdevCreateOptions *create_options;
+ BlockdevCreateOptionsSsh *ssh_opts;
+ int ret;
QDict *uri_options = NULL;
- BDRVSSHState s;
- ssh_state_init(&s);
+ create_options = g_new0(BlockdevCreateOptions, 1);
+ create_options->driver = BLOCKDEV_DRIVER_SSH;
+ ssh_opts = &create_options->u.ssh;
/* Get desired file size. */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- DPRINTF("total_size=%" PRIi64, total_size);
+ ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
+ DPRINTF("total_size=%" PRIi64, ssh_opts->size);
uri_options = qdict_new();
- r = parse_uri(filename, uri_options, errp);
- if (r < 0) {
- ret = r;
+ ret = parse_uri(filename, uri_options, errp);
+ if (ret < 0) {
goto out;
}
- r = connect_to_ssh(&s, uri_options,
- LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
- LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
- 0644, errp);
- if (r < 0) {
- ret = r;
+ ssh_opts->location = ssh_parse_options(uri_options, errp);
+ if (ssh_opts->location == NULL) {
+ ret = -EINVAL;
goto out;
}
- if (total_size > 0) {
- ret = ssh_grow_file(&s, total_size, errp);
- if (ret < 0) {
- goto out;
- }
- }
-
- ret = 0;
+ ret = ssh_co_create(create_options, errp);
out:
- ssh_state_free(&s);
- if (uri_options != NULL) {
- QDECREF(uri_options);
- }
+ QDECREF(uri_options);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
@@ -1249,6 +1280,7 @@ static BlockDriver bdrv_ssh = {
.instance_size = sizeof(BDRVSSHState),
.bdrv_parse_filename = ssh_parse_filename,
.bdrv_file_open = ssh_file_open,
+ .bdrv_co_create = ssh_co_create,
.bdrv_co_create_opts = ssh_co_create_opts,
.bdrv_close = ssh_close,
.bdrv_has_zero_init = ssh_has_zero_init,