* THE SOFTWARE.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
+#include "qemu/osdep.h"
#include <libssh2.h>
#include <libssh2_sftp.h>
#include "block/block_int.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "qemu/uri.h"
#include "qapi/qmp/qint.h"
+#include "qapi/qmp/qstring.h"
/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in
* this block driver code.
}
}
-/* Wrappers around error_report which make sure to dump as much
- * information from libssh2 as possible.
- */
-static void GCC_FMT_ATTR(2, 3)
-session_error_report(BDRVSSHState *s, const char *fs, ...)
+static void GCC_FMT_ATTR(3, 4)
+session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
{
va_list args;
+ char *msg;
va_start(args, fs);
- error_vprintf(fs, args);
+ msg = g_strdup_vprintf(fs, args);
+ va_end(args);
- if ((s)->session) {
+ if (s->session) {
char *ssh_err;
int ssh_err_code;
- libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
/* This is not an errno. See <libssh2.h>. */
- ssh_err_code = libssh2_session_last_errno((s)->session);
-
- error_printf(": %s (libssh2 error code: %d)", ssh_err, ssh_err_code);
+ ssh_err_code = libssh2_session_last_error(s->session,
+ &ssh_err, NULL, 0);
+ error_setg(errp, "%s: %s (libssh2 error code: %d)",
+ msg, ssh_err, ssh_err_code);
+ } else {
+ error_setg(errp, "%s", msg);
}
+ g_free(msg);
+}
+
+static void GCC_FMT_ATTR(3, 4)
+sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+ va_start(args, fs);
+ msg = g_strdup_vprintf(fs, args);
va_end(args);
- error_printf("\n");
+
+ if (s->sftp) {
+ char *ssh_err;
+ int ssh_err_code;
+ unsigned long sftp_err_code;
+
+ /* This is not an errno. See <libssh2.h>. */
+ ssh_err_code = libssh2_session_last_error(s->session,
+ &ssh_err, NULL, 0);
+ /* See <libssh2_sftp.h>. */
+ sftp_err_code = libssh2_sftp_last_error((s)->sftp);
+
+ error_setg(errp,
+ "%s: %s (libssh2 error code: %d, sftp error code: %lu)",
+ msg, ssh_err, ssh_err_code, sftp_err_code);
+ } else {
+ error_setg(errp, "%s", msg);
+ }
+ g_free(msg);
}
static void GCC_FMT_ATTR(2, 3)
int ssh_err_code;
unsigned long sftp_err_code;
- libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
/* This is not an errno. See <libssh2.h>. */
- ssh_err_code = libssh2_session_last_errno((s)->session);
+ ssh_err_code = libssh2_session_last_error(s->session,
+ &ssh_err, NULL, 0);
/* See <libssh2_sftp.h>. */
sftp_err_code = libssh2_sftp_last_error((s)->sftp);
static int parse_uri(const char *filename, QDict *options, Error **errp)
{
URI *uri = NULL;
- QueryParams *qp = NULL;
+ QueryParams *qp;
int i;
uri = uri_parse(filename);
return 0;
err:
- if (qp) {
- query_params_free(qp);
- }
if (uri) {
uri_free(uri);
}
}
static int check_host_key_knownhosts(BDRVSSHState *s,
- const char *host, int port)
+ const char *host, int port, Error **errp)
{
const char *home;
char *knh_file = NULL;
hostkey = libssh2_session_hostkey(s->session, &len, &type);
if (!hostkey) {
ret = -EINVAL;
- session_error_report(s, "failed to read remote host key");
+ session_error_setg(errp, s, "failed to read remote host key");
goto out;
}
knh = libssh2_knownhost_init(s->session);
if (!knh) {
ret = -EINVAL;
- session_error_report(s, "failed to initialize known hosts support");
+ session_error_setg(errp, s,
+ "failed to initialize known hosts support");
goto out;
}
break;
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
ret = -EINVAL;
- session_error_report(s, "host key does not match the one in known_hosts (found key %s)",
- found->key);
+ session_error_setg(errp, s,
+ "host key does not match the one in known_hosts"
+ " (found key %s)", found->key);
goto out;
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
ret = -EINVAL;
- session_error_report(s, "no host key was found in known_hosts");
+ session_error_setg(errp, s, "no host key was found in known_hosts");
goto out;
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
ret = -EINVAL;
- session_error_report(s, "failure matching the host key with known_hosts");
+ session_error_setg(errp, s,
+ "failure matching the host key with known_hosts");
goto out;
default:
ret = -EINVAL;
- session_error_report(s, "unknown error matching the host key with known_hosts (%d)",
- r);
+ session_error_setg(errp, s, "unknown error matching the host key"
+ " with known_hosts (%d)", r);
goto out;
}
static int
check_host_key_hash(BDRVSSHState *s, const char *hash,
- int hash_type, size_t fingerprint_len)
+ int hash_type, size_t fingerprint_len, Error **errp)
{
const char *fingerprint;
fingerprint = libssh2_hostkey_hash(s->session, hash_type);
if (!fingerprint) {
- session_error_report(s, "failed to read remote host key");
+ session_error_setg(errp, s, "failed to read remote host key");
return -EINVAL;
}
if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
hash) != 0) {
- error_report("remote host key does not match host_key_check '%s'",
- hash);
+ error_setg(errp, "remote host key does not match host_key_check '%s'",
+ hash);
return -EPERM;
}
}
static int check_host_key(BDRVSSHState *s, const char *host, int port,
- const char *host_key_check)
+ const char *host_key_check, Error **errp)
{
/* host_key_check=no */
if (strcmp(host_key_check, "no") == 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);
+ LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
}
/* 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);
+ LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
}
/* host_key_check=yes */
if (strcmp(host_key_check, "yes") == 0) {
- return check_host_key_knownhosts(s, host, port);
+ return check_host_key_knownhosts(s, host, port, errp);
}
- error_report("unknown host_key_check setting (%s)", host_key_check);
+ error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
return -EINVAL;
}
-static int authenticate(BDRVSSHState *s, const char *user)
+static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
{
int r, ret;
const char *userauthlist;
userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
if (strstr(userauthlist, "publickey") == NULL) {
ret = -EPERM;
- error_report("remote server does not support \"publickey\" authentication");
+ error_setg(errp,
+ "remote server does not support \"publickey\" authentication");
goto out;
}
agent = libssh2_agent_init(s->session);
if (!agent) {
ret = -EINVAL;
- session_error_report(s, "failed to initialize ssh-agent support");
+ session_error_setg(errp, s, "failed to initialize ssh-agent support");
goto out;
}
if (libssh2_agent_connect(agent)) {
ret = -ECONNREFUSED;
- session_error_report(s, "failed to connect to ssh-agent");
+ session_error_setg(errp, s, "failed to connect to ssh-agent");
goto out;
}
if (libssh2_agent_list_identities(agent)) {
ret = -EINVAL;
- session_error_report(s, "failed requesting identities from ssh-agent");
+ session_error_setg(errp, s,
+ "failed requesting identities from ssh-agent");
goto out;
}
}
if (r < 0) {
ret = -EINVAL;
- session_error_report(s, "failed to obtain identity from ssh-agent");
+ session_error_setg(errp, s,
+ "failed to obtain identity from ssh-agent");
goto out;
}
r = libssh2_agent_userauth(agent, user, identity);
}
ret = -EPERM;
- error_report("failed to authenticate using publickey authentication "
- "and the identities held by your ssh-agent");
+ error_setg(errp, "failed to authenticate using publickey authentication "
+ "and the identities held by your ssh-agent");
out:
if (agent != NULL) {
}
static int connect_to_ssh(BDRVSSHState *s, QDict *options,
- int ssh_flags, int creat_mode)
+ int ssh_flags, int creat_mode, Error **errp)
{
int r, ret;
- Error *err = NULL;
const char *host, *user, *path, *host_key_check;
int port;
+ if (!qdict_haskey(options, "host")) {
+ ret = -EINVAL;
+ error_setg(errp, "No hostname was specified");
+ goto err;
+ }
host = qdict_get_str(options, "host");
if (qdict_haskey(options, "port")) {
port = 22;
}
+ if (!qdict_haskey(options, "path")) {
+ ret = -EINVAL;
+ error_setg(errp, "No path was specified");
+ goto err;
+ }
path = qdict_get_str(options, "path");
if (qdict_haskey(options, "user")) {
} else {
user = g_get_user_name();
if (!user) {
+ error_setg_errno(errp, errno, "Can't get user name");
ret = -errno;
goto err;
}
s->hostport = g_strdup_printf("%s:%d", host, port);
/* Open the socket and connect. */
- s->sock = inet_connect(s->hostport, &err);
- if (err != NULL) {
- ret = -errno;
- qerror_report_err(err);
- error_free(err);
+ s->sock = inet_connect(s->hostport, errp);
+ if (s->sock < 0) {
+ ret = -EIO;
goto err;
}
s->session = libssh2_session_init();
if (!s->session) {
ret = -EINVAL;
- session_error_report(s, "failed to initialize libssh2 session");
+ session_error_setg(errp, s, "failed to initialize libssh2 session");
goto err;
}
r = libssh2_session_handshake(s->session, s->sock);
if (r != 0) {
ret = -EINVAL;
- session_error_report(s, "failed to establish SSH session");
+ session_error_setg(errp, s, "failed to establish SSH session");
goto err;
}
/* Check the remote host's key against known_hosts. */
- ret = check_host_key(s, host, port, host_key_check);
+ ret = check_host_key(s, host, port, host_key_check, errp);
if (ret < 0) {
goto err;
}
/* Authenticate. */
- ret = authenticate(s, user);
+ ret = authenticate(s, user, errp);
if (ret < 0) {
goto err;
}
/* Start SFTP. */
s->sftp = libssh2_sftp_init(s->session);
if (!s->sftp) {
- session_error_report(s, "failed to initialize sftp handle");
+ session_error_setg(errp, s, "failed to initialize sftp handle");
ret = -EINVAL;
goto err;
}
path, ssh_flags, creat_mode);
s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
if (!s->sftp_handle) {
- session_error_report(s, "failed to open remote file '%s'", path);
+ session_error_setg(errp, s, "failed to open remote file '%s'", path);
ret = -EINVAL;
goto err;
}
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
if (r < 0) {
- sftp_error_report(s, "failed to read file attributes");
+ sftp_error_setg(errp, s, "failed to read file attributes");
return -EINVAL;
}
return ret;
}
-static int ssh_file_open(BlockDriverState *bs, const char *filename,
- QDict *options, int bdrv_flags)
+static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
+ Error **errp)
{
BDRVSSHState *s = bs->opaque;
int ret;
}
/* Start up SSH. */
- ret = connect_to_ssh(s, options, ssh_flags, 0);
+ ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
if (ret < 0) {
goto err;
}
return ret;
}
-static QEMUOptionParameter ssh_create_options[] = {
- {
- .name = BLOCK_OPT_SIZE,
- .type = OPT_SIZE,
- .help = "Virtual disk size"
- },
- { NULL }
+static QemuOptsList ssh_create_opts = {
+ .name = "ssh-create-opts",
+ .head = QTAILQ_HEAD_INITIALIZER(ssh_create_opts.head),
+ .desc = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = QEMU_OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { /* end of list */ }
+ }
};
-static int ssh_create(const char *filename, QEMUOptionParameter *options)
+static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
{
int r, ret;
- Error *local_err = NULL;
int64_t total_size = 0;
QDict *uri_options = NULL;
BDRVSSHState s;
ssh_state_init(&s);
/* Get desired file size. */
- while (options && options->name) {
- if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
- total_size = options->value.n;
- }
- options++;
- }
+ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
DPRINTF("total_size=%" PRIi64, total_size);
uri_options = qdict_new();
- r = parse_uri(filename, uri_options, &local_err);
+ r = parse_uri(filename, uri_options, errp);
if (r < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
ret = r;
goto out;
}
r = connect_to_ssh(&s, uri_options,
LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
- LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 0644);
+ LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
+ 0644, errp);
if (r < 0) {
ret = r;
goto out;
libssh2_sftp_seek64(s.sftp_handle, total_size-1);
r2 = libssh2_sftp_write(s.sftp_handle, c, 1);
if (r2 < 0) {
- sftp_error_report(&s, "truncate failed");
+ sftp_error_setg(errp, &s, "truncate failed");
ret = -EINVAL;
goto out;
}
ssh_state_free(s);
}
+static int ssh_has_zero_init(BlockDriverState *bs)
+{
+ BDRVSSHState *s = bs->opaque;
+ /* Assume false, unless we can positively prove it's true. */
+ int has_zero_init = 0;
+
+ if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
+ if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) {
+ has_zero_init = 1;
+ }
+ }
+
+ return has_zero_init;
+}
+
static void restart_coroutine(void *opaque)
{
Coroutine *co = opaque;
qemu_coroutine_enter(co, NULL);
}
-/* Always true because when we have called set_fd_handler there is
- * always a request being processed.
- */
-static int return_true(void *opaque)
-{
- return 1;
-}
-
-static coroutine_fn void set_fd_handler(BDRVSSHState *s)
+static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
{
int r;
IOHandler *rd_handler = NULL, *wr_handler = NULL;
DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock,
rd_handler, wr_handler);
- qemu_aio_set_fd_handler(s->sock, rd_handler, wr_handler, return_true, co);
+ aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
+ false, rd_handler, wr_handler, co);
}
-static coroutine_fn void clear_fd_handler(BDRVSSHState *s)
+static coroutine_fn void clear_fd_handler(BDRVSSHState *s,
+ BlockDriverState *bs)
{
DPRINTF("s->sock=%d", s->sock);
- qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL);
+ aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
+ false, NULL, NULL, NULL);
}
/* A non-blocking call returned EAGAIN, so yield, ensuring the
* handlers are set up so that we'll be rescheduled when there is an
* interesting event on the socket.
*/
-static coroutine_fn void co_yield(BDRVSSHState *s)
+static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
{
- set_fd_handler(s);
+ set_fd_handler(s, bs);
qemu_coroutine_yield();
- clear_fd_handler(s);
+ clear_fd_handler(s, bs);
}
/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
}
}
-static coroutine_fn int ssh_read(BDRVSSHState *s,
+static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
int64_t offset, size_t size,
QEMUIOVector *qiov)
{
DPRINTF("sftp_read returned %zd", r);
if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
- co_yield(s);
+ co_yield(s, bs);
goto again;
}
if (r < 0) {
int ret;
qemu_co_mutex_lock(&s->lock);
- ret = ssh_read(s, sector_num * BDRV_SECTOR_SIZE,
+ ret = ssh_read(s, bs, sector_num * BDRV_SECTOR_SIZE,
nb_sectors * BDRV_SECTOR_SIZE, qiov);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
-static int ssh_write(BDRVSSHState *s,
+static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
int64_t offset, size_t size,
QEMUIOVector *qiov)
{
DPRINTF("sftp_write returned %zd", r);
if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
- co_yield(s);
+ co_yield(s, bs);
goto again;
}
if (r < 0) {
*/
if (r == 0) {
ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE);
- co_yield(s);
+ co_yield(s, bs);
goto again;
}
int ret;
qemu_co_mutex_lock(&s->lock);
- ret = ssh_write(s, sector_num * BDRV_SECTOR_SIZE,
+ ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
nb_sectors * BDRV_SECTOR_SIZE, qiov);
qemu_co_mutex_unlock(&s->lock);
#ifdef HAS_LIBSSH2_SFTP_FSYNC
-static coroutine_fn int ssh_flush(BDRVSSHState *s)
+static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
{
int r;
again:
r = libssh2_sftp_fsync(s->sftp_handle);
if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
- co_yield(s);
+ co_yield(s, bs);
goto again;
}
if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
int ret;
qemu_co_mutex_lock(&s->lock);
- ret = ssh_flush(s);
+ ret = ssh_flush(s, bs);
qemu_co_mutex_unlock(&s->lock);
return ret;
.bdrv_file_open = ssh_file_open,
.bdrv_create = ssh_create,
.bdrv_close = ssh_close,
+ .bdrv_has_zero_init = ssh_has_zero_init,
.bdrv_co_readv = ssh_co_readv,
.bdrv_co_writev = ssh_co_writev,
.bdrv_getlength = ssh_getlength,
.bdrv_co_flush_to_disk = ssh_co_flush,
- .create_options = ssh_create_options,
+ .create_opts = &ssh_create_opts,
};
static void bdrv_ssh_init(void)