aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/ssh.c78
-rwxr-xr-xconfigure25
-rw-r--r--qemu-doc.texi15
3 files changed, 108 insertions, 10 deletions
diff --git a/block/ssh.c b/block/ssh.c
index 89a9017b63..8f78e2e4b9 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -72,6 +72,10 @@ typedef struct BDRVSSHState {
* updated if it changes (eg by writing at the end of the file).
*/
LIBSSH2_SFTP_ATTRIBUTES attrs;
+
+ /* Used to warn if 'flush' is not supported. */
+ char *hostport;
+ bool unsafe_flush_warning;
} BDRVSSHState;
static void ssh_state_init(BDRVSSHState *s)
@@ -84,6 +88,7 @@ static void ssh_state_init(BDRVSSHState *s)
static void ssh_state_free(BDRVSSHState *s)
{
+ g_free(s->hostport);
if (s->sftp_handle) {
libssh2_sftp_close(s->sftp_handle);
}
@@ -479,7 +484,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
Error *err = NULL;
const char *host, *user, *path, *host_key_check;
int port;
- char *hostport = NULL;
host = qdict_get_str(options, "host");
@@ -507,9 +511,12 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
host_key_check = "yes";
}
+ /* Construct the host:port name for inet_connect. */
+ g_free(s->hostport);
+ s->hostport = g_strdup_printf("%s:%d", host, port);
+
/* Open the socket and connect. */
- hostport = g_strdup_printf("%s:%d", host, port);
- s->sock = inet_connect(hostport, &err);
+ s->sock = inet_connect(s->hostport, &err);
if (err != NULL) {
ret = -errno;
qerror_report_err(err);
@@ -581,7 +588,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
qdict_del(options, "path");
qdict_del(options, "host_key_check");
- g_free(hostport);
return 0;
err:
@@ -600,7 +606,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
libssh2_session_free(s->session);
}
s->session = NULL;
- g_free(hostport);
return ret;
}
@@ -953,6 +958,68 @@ static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
return ret;
}
+static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
+{
+ if (!s->unsafe_flush_warning) {
+ error_report("warning: ssh server %s does not support fsync",
+ s->hostport);
+ if (what) {
+ error_report("to support fsync, you need %s", what);
+ }
+ s->unsafe_flush_warning = true;
+ }
+}
+
+#ifdef HAS_LIBSSH2_SFTP_FSYNC
+
+static coroutine_fn int ssh_flush(BDRVSSHState *s)
+{
+ int r;
+
+ DPRINTF("fsync");
+ again:
+ r = libssh2_sftp_fsync(s->sftp_handle);
+ if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
+ co_yield(s);
+ goto again;
+ }
+ if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
+ libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) {
+ unsafe_flush_warning(s, "OpenSSH >= 6.3");
+ return 0;
+ }
+ if (r < 0) {
+ sftp_error_report(s, "fsync failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
+{
+ BDRVSSHState *s = bs->opaque;
+ int ret;
+
+ qemu_co_mutex_lock(&s->lock);
+ ret = ssh_flush(s);
+ qemu_co_mutex_unlock(&s->lock);
+
+ return ret;
+}
+
+#else /* !HAS_LIBSSH2_SFTP_FSYNC */
+
+static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
+{
+ BDRVSSHState *s = bs->opaque;
+
+ unsafe_flush_warning(s, "libssh2 >= 1.4.4");
+ return 0;
+}
+
+#endif /* !HAS_LIBSSH2_SFTP_FSYNC */
+
static int64_t ssh_getlength(BlockDriverState *bs)
{
BDRVSSHState *s = bs->opaque;
@@ -976,6 +1043,7 @@ static BlockDriver bdrv_ssh = {
.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,
};
diff --git a/configure b/configure
index e6c5d2c3f6..a97bf311d3 100755
--- a/configure
+++ b/configure
@@ -2357,6 +2357,31 @@ EOF
fi
##########################################
+# libssh2_sftp_fsync probe
+
+if test "$libssh2" = "yes"; then
+ cat > $TMPC <<EOF
+#include <stdio.h>
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+int main(void) {
+ LIBSSH2_SESSION *session;
+ LIBSSH2_SFTP *sftp;
+ LIBSSH2_SFTP_HANDLE *sftp_handle;
+ session = libssh2_session_init ();
+ sftp = libssh2_sftp_init (session);
+ sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0);
+ libssh2_sftp_fsync (sftp_handle);
+ return 0;
+}
+EOF
+ # libssh2_cflags/libssh2_libs defined in previous test.
+ if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then
+ QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS"
+ fi
+fi
+
+##########################################
# linux-aio probe
if test "$linux_aio" != "no" ; then
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 5b36004873..dfea4d3799 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -1081,11 +1081,16 @@ tools only use MD5 to print fingerprints).
Currently authentication must be done using ssh-agent. Other
authentication methods may be supported in future.
-Note: The ssh driver does not obey disk flush requests (ie. to commit
-data to the backing disk when the guest requests it). This is because
-the underlying protocol (SFTP) does not support this. Thus there is a
-risk of guest disk corruption if the remote server or network goes
-down during writes.
+Note: Many ssh servers do not support an @code{fsync}-style operation.
+The ssh driver cannot guarantee that disk flush requests are
+obeyed, and this causes a risk of disk corruption if the remote
+server or network goes down during writes. The driver will
+print a warning when @code{fsync} is not supported:
+
+warning: ssh server @code{ssh.example.com:22} does not support fsync
+
+With sufficiently new versions of libssh2 and OpenSSH, @code{fsync} is
+supported.
@node pcsys_network
@section Network emulation