X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/6165daa4c8431d9d60382352864b46f34dd61ab4..0bd6e91a7e00129764afb9bed83ae5519e18a111:/block/ssh.c diff --git a/block/ssh.c b/block/ssh.c index 93a8b53ffd..d35b51f987 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -30,9 +30,11 @@ #include #include "block/block_int.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. @@ -106,30 +108,59 @@ static void ssh_state_free(BDRVSSHState *s) } } -/* 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 . */ - 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 . */ + ssh_err_code = libssh2_session_last_error(s->session, + &ssh_err, NULL, 0); + /* See . */ + 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) @@ -145,9 +176,9 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ...) 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 . */ - ssh_err_code = libssh2_session_last_errno((s)->session); + ssh_err_code = libssh2_session_last_error(s->session, + &ssh_err, NULL, 0); /* See . */ sftp_err_code = libssh2_sftp_last_error((s)->sftp); @@ -162,7 +193,7 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ...) 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); @@ -218,9 +249,6 @@ static int parse_uri(const char *filename, QDict *options, Error **errp) return 0; err: - if (qp) { - query_params_free(qp); - } if (uri) { uri_free(uri); } @@ -243,7 +271,7 @@ static void ssh_parse_filename(const char *filename, QDict *options, } 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; @@ -257,14 +285,15 @@ static int check_host_key_knownhosts(BDRVSSHState *s, 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; } @@ -289,21 +318,23 @@ static int check_host_key_knownhosts(BDRVSSHState *s, 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; } @@ -358,20 +389,20 @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len, 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; } @@ -379,7 +410,7 @@ 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) + const char *host_key_check, Error **errp) { /* host_key_check=no */ if (strcmp(host_key_check, "no") == 0) { @@ -389,25 +420,25 @@ static int check_host_key(BDRVSSHState *s, const char *host, int port, /* 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; @@ -418,7 +449,8 @@ static int authenticate(BDRVSSHState *s, const char *user) 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; } @@ -426,17 +458,18 @@ static int authenticate(BDRVSSHState *s, const char *user) 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; } @@ -447,7 +480,8 @@ static int authenticate(BDRVSSHState *s, const char *user) } 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); @@ -461,8 +495,8 @@ static int authenticate(BDRVSSHState *s, const char *user) } 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) { @@ -476,13 +510,17 @@ static int authenticate(BDRVSSHState *s, const char *user) } 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")) { @@ -491,6 +529,11 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, 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")) { @@ -498,6 +541,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } else { user = g_get_user_name(); if (!user) { + error_setg_errno(errp, errno, "Can't get user name"); ret = -errno; goto err; } @@ -514,11 +558,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, 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; } @@ -526,7 +568,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, 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; } @@ -537,18 +579,18 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, 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; } @@ -556,7 +598,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, /* 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; } @@ -566,14 +608,14 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, 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; } @@ -608,8 +650,8 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, 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; @@ -623,7 +665,7 @@ static int ssh_file_open(BlockDriverState *bs, const char *filename, } /* 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; } @@ -642,19 +684,22 @@ static int ssh_file_open(BlockDriverState *bs, const char *filename, 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; @@ -664,26 +709,21 @@ static int ssh_create(const char *filename, QEMUOptionParameter *options) 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; @@ -693,7 +733,7 @@ static int ssh_create(const char *filename, QEMUOptionParameter *options) 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; } @@ -717,6 +757,21 @@ static void ssh_close(BlockDriverState *bs) 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; @@ -726,15 +781,7 @@ static void restart_coroutine(void *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; @@ -752,24 +799,26 @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s) 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, + 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, 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 @@ -799,7 +848,7 @@ static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) } } -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) { @@ -832,7 +881,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, 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) { @@ -867,14 +916,14 @@ static coroutine_fn int ssh_co_readv(BlockDriverState *bs, 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) { @@ -902,7 +951,7 @@ static int ssh_write(BDRVSSHState *s, 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) { @@ -921,7 +970,7 @@ static int ssh_write(BDRVSSHState *s, */ if (r == 0) { ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE); - co_yield(s); + co_yield(s, bs); goto again; } @@ -949,7 +998,7 @@ static coroutine_fn int ssh_co_writev(BlockDriverState *bs, 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); @@ -970,7 +1019,7 @@ static void unsafe_flush_warning(BDRVSSHState *s, const char *what) #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; @@ -978,7 +1027,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s) 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 && @@ -1000,7 +1049,7 @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs) 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; @@ -1038,11 +1087,12 @@ static BlockDriver bdrv_ssh = { .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)