Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* Coverity fixes for IPMI and mptsas
* qemu-char fixes from Daniel and Marc-André
* Bug fixes that break qemu-iotests
* Changes to fix reset from panicked state
* checkpatch false positives for designated initializers
* TLS support in the NBD servers and clients
# gpg: Signature made Tue 16 Feb 2016 16:27:17 GMT using RSA key ID 78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>"
* remotes/bonzini/tags/for-upstream: (28 commits)
nbd: enable use of TLS with nbd-server-start command
nbd: enable use of TLS with qemu-nbd server
nbd: enable use of TLS with NBD block driver
nbd: implement TLS support in the protocol negotiation
nbd: use "" as a default export name if none provided
nbd: always query export list in fixed new style protocol
nbd: allow setting of an export name for qemu-nbd server
nbd: make client request fixed new style if advertised
nbd: make server compliant with fixed newstyle spec
nbd: invert client logic for negotiating protocol version
nbd: convert to using I/O channels for actual socket I/O
nbd: convert blockdev NBD server to use I/O channels for connection setup
nbd: convert qemu-nbd server to use I/O channels for connection setup
nbd: convert block client to use I/O channels for connection setup
qemu-nbd: add support for --object command line arg
qom: add helpers for UserCreatable object types
ipmi: sensor number should not exceed MAX_SENSORS
mptsas: fix wrong formula
mptsas: fix memory leak
mptsas: add missing va_end
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/Makefile b/Makefile
index f9fae3a..16db398 100644
--- a/Makefile
+++ b/Makefile
@@ -234,9 +234,9 @@
qemu-img.o: qemu-img-cmds.h
-qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
-qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
-qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
diff --git a/block/nbd-client.c b/block/nbd-client.c
index 568c56c..6a9b4c7 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -28,7 +28,6 @@
#include "qemu/osdep.h"
#include "nbd-client.h"
-#include "qemu/sockets.h"
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
@@ -48,13 +47,21 @@
{
NbdClientSession *client = nbd_get_client_session(bs);
+ if (!client->ioc) { /* Already closed */
+ return;
+ }
+
/* finish any pending coroutines */
- shutdown(client->sock, 2);
+ qio_channel_shutdown(client->ioc,
+ QIO_CHANNEL_SHUTDOWN_BOTH,
+ NULL);
nbd_recv_coroutines_enter_all(client);
nbd_client_detach_aio_context(bs);
- closesocket(client->sock);
- client->sock = -1;
+ object_unref(OBJECT(client->sioc));
+ client->sioc = NULL;
+ object_unref(OBJECT(client->ioc));
+ client->ioc = NULL;
}
static void nbd_reply_ready(void *opaque)
@@ -64,12 +71,16 @@
uint64_t i;
int ret;
+ if (!s->ioc) { /* Already closed */
+ return;
+ }
+
if (s->reply.handle == 0) {
/* No reply already in flight. Fetch a header. It is possible
* that another thread has done the same thing in parallel, so
* the socket is not readable anymore.
*/
- ret = nbd_receive_reply(s->sock, &s->reply);
+ ret = nbd_receive_reply(s->ioc, &s->reply);
if (ret == -EAGAIN) {
return;
}
@@ -120,32 +131,35 @@
}
}
+ g_assert(qemu_in_coroutine());
assert(i < MAX_NBD_REQUESTS);
request->handle = INDEX_TO_HANDLE(s, i);
+
+ if (!s->ioc) {
+ qemu_co_mutex_unlock(&s->send_mutex);
+ return -EPIPE;
+ }
+
s->send_coroutine = qemu_coroutine_self();
aio_context = bdrv_get_aio_context(bs);
- aio_set_fd_handler(aio_context, s->sock, false,
+ aio_set_fd_handler(aio_context, s->sioc->fd, false,
nbd_reply_ready, nbd_restart_write, bs);
if (qiov) {
- if (!s->is_unix) {
- socket_set_cork(s->sock, 1);
- }
- rc = nbd_send_request(s->sock, request);
+ qio_channel_set_cork(s->ioc, true);
+ rc = nbd_send_request(s->ioc, request);
if (rc >= 0) {
- ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
- offset, request->len);
+ ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
+ offset, request->len, 0);
if (ret != request->len) {
rc = -EIO;
}
}
- if (!s->is_unix) {
- socket_set_cork(s->sock, 0);
- }
+ qio_channel_set_cork(s->ioc, false);
} else {
- rc = nbd_send_request(s->sock, request);
+ rc = nbd_send_request(s->ioc, request);
}
- aio_set_fd_handler(aio_context, s->sock, false,
+ aio_set_fd_handler(aio_context, s->sioc->fd, false,
nbd_reply_ready, NULL, bs);
s->send_coroutine = NULL;
qemu_co_mutex_unlock(&s->send_mutex);
@@ -162,12 +176,13 @@
* peek at the next reply and avoid yielding if it's ours? */
qemu_coroutine_yield();
*reply = s->reply;
- if (reply->handle != request->handle) {
+ if (reply->handle != request->handle ||
+ !s->ioc) {
reply->error = EIO;
} else {
if (qiov && reply->error == 0) {
- ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
- offset, request->len);
+ ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
+ offset, request->len, 1);
if (ret != request->len) {
reply->error = EIO;
}
@@ -350,14 +365,14 @@
void nbd_client_detach_aio_context(BlockDriverState *bs)
{
aio_set_fd_handler(bdrv_get_aio_context(bs),
- nbd_get_client_session(bs)->sock,
+ nbd_get_client_session(bs)->sioc->fd,
false, NULL, NULL, NULL);
}
void nbd_client_attach_aio_context(BlockDriverState *bs,
AioContext *new_context)
{
- aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock,
+ aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd,
false, nbd_reply_ready, NULL, bs);
}
@@ -370,16 +385,20 @@
.len = 0
};
- if (client->sock == -1) {
+ if (client->ioc == NULL) {
return;
}
- nbd_send_request(client->sock, &request);
+ nbd_send_request(client->ioc, &request);
nbd_teardown_connection(bs);
}
-int nbd_client_init(BlockDriverState *bs, int sock, const char *export,
+int nbd_client_init(BlockDriverState *bs,
+ QIOChannelSocket *sioc,
+ const char *export,
+ QCryptoTLSCreds *tlscreds,
+ const char *hostname,
Error **errp)
{
NbdClientSession *client = nbd_get_client_session(bs);
@@ -387,22 +406,32 @@
/* NBD handshake */
logout("session init %s\n", export);
- qemu_set_block(sock);
- ret = nbd_receive_negotiate(sock, export,
- &client->nbdflags, &client->size, errp);
+ qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
+
+ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
+ &client->nbdflags,
+ tlscreds, hostname,
+ &client->ioc,
+ &client->size, errp);
if (ret < 0) {
logout("Failed to negotiate with the NBD server\n");
- closesocket(sock);
return ret;
}
qemu_co_mutex_init(&client->send_mutex);
qemu_co_mutex_init(&client->free_sema);
- client->sock = sock;
+ client->sioc = sioc;
+ object_ref(OBJECT(client->sioc));
+
+ if (!client->ioc) {
+ client->ioc = QIO_CHANNEL(sioc);
+ object_ref(OBJECT(client->ioc));
+ }
/* Now that we're connected, set the socket to be non-blocking and
* kick the reply mechanism. */
- qemu_set_nonblock(sock);
+ qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
+
nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
logout("Established connection with NBD server\n");
diff --git a/block/nbd-client.h b/block/nbd-client.h
index e841340..53f116d 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -4,6 +4,7 @@
#include "qemu-common.h"
#include "block/nbd.h"
#include "block/block_int.h"
+#include "io/channel-socket.h"
/* #define DEBUG_NBD */
@@ -17,7 +18,8 @@
#define MAX_NBD_REQUESTS 16
typedef struct NbdClientSession {
- int sock;
+ QIOChannelSocket *sioc; /* The master data channel */
+ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
uint32_t nbdflags;
off_t size;
@@ -34,7 +36,11 @@
NbdClientSession *nbd_get_client_session(BlockDriverState *bs);
-int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name,
+int nbd_client_init(BlockDriverState *bs,
+ QIOChannelSocket *sock,
+ const char *export_name,
+ QCryptoTLSCreds *tlscreds,
+ const char *hostname,
Error **errp);
void nbd_client_close(BlockDriverState *bs);
diff --git a/block/nbd.c b/block/nbd.c
index 1a90bc7..db57b49 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -31,7 +31,6 @@
#include "qemu/uri.h"
#include "block/block_int.h"
#include "qemu/module.h"
-#include "qemu/sockets.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qint.h"
@@ -238,55 +237,113 @@
return &s->client;
}
-static int nbd_establish_connection(BlockDriverState *bs,
- SocketAddress *saddr,
- Error **errp)
+static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
+ Error **errp)
{
- BDRVNBDState *s = bs->opaque;
- int sock;
+ QIOChannelSocket *sioc;
+ Error *local_err = NULL;
- sock = socket_connect(saddr, errp, NULL, NULL);
+ sioc = qio_channel_socket_new();
- if (sock < 0) {
- logout("Failed to establish connection to NBD server\n");
- return -EIO;
+ qio_channel_socket_connect_sync(sioc,
+ saddr,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return NULL;
}
- if (!s->client.is_unix) {
- socket_set_nodelay(sock);
- }
+ qio_channel_set_delay(QIO_CHANNEL(sioc), false);
- return sock;
+ return sioc;
}
+
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
+{
+ Object *obj;
+ QCryptoTLSCreds *creds;
+
+ obj = object_resolve_path_component(
+ object_get_objects_root(), id);
+ if (!obj) {
+ error_setg(errp, "No TLS credentials with id '%s'",
+ id);
+ return NULL;
+ }
+ creds = (QCryptoTLSCreds *)
+ object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
+ if (!creds) {
+ error_setg(errp, "Object with id '%s' is not TLS credentials",
+ id);
+ return NULL;
+ }
+
+ if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+ error_setg(errp,
+ "Expecting TLS credentials with a client endpoint");
+ return NULL;
+ }
+ object_ref(obj);
+ return creds;
+}
+
+
static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVNBDState *s = bs->opaque;
char *export = NULL;
- int result, sock;
+ QIOChannelSocket *sioc = NULL;
SocketAddress *saddr;
+ const char *tlscredsid;
+ QCryptoTLSCreds *tlscreds = NULL;
+ const char *hostname = NULL;
+ int ret = -EINVAL;
/* Pop the config into our state object. Exit if invalid. */
saddr = nbd_config(s, options, &export, errp);
if (!saddr) {
- return -EINVAL;
+ goto error;
+ }
+
+ tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds"));
+ if (tlscredsid) {
+ qdict_del(options, "tls-creds");
+ tlscreds = nbd_get_tls_creds(tlscredsid, errp);
+ if (!tlscreds) {
+ goto error;
+ }
+
+ if (saddr->type != SOCKET_ADDRESS_KIND_INET) {
+ error_setg(errp, "TLS only supported over IP sockets");
+ goto error;
+ }
+ hostname = saddr->u.inet->host;
}
/* establish TCP connection, return error if it fails
* TODO: Configurable retry-until-timeout behaviour.
*/
- sock = nbd_establish_connection(bs, saddr, errp);
- qapi_free_SocketAddress(saddr);
- if (sock < 0) {
- g_free(export);
- return sock;
+ sioc = nbd_establish_connection(saddr, errp);
+ if (!sioc) {
+ ret = -ECONNREFUSED;
+ goto error;
}
/* NBD handshake */
- result = nbd_client_init(bs, sock, export, errp);
+ ret = nbd_client_init(bs, sioc, export,
+ tlscreds, hostname, errp);
+ error:
+ if (sioc) {
+ object_unref(OBJECT(sioc));
+ }
+ if (tlscreds) {
+ object_unref(OBJECT(tlscreds));
+ }
+ qapi_free_SocketAddress(saddr);
g_free(export);
- return result;
+ return ret;
}
static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
@@ -348,6 +405,7 @@
const char *host = qdict_get_try_str(options, "host");
const char *port = qdict_get_try_str(options, "port");
const char *export = qdict_get_try_str(options, "export");
+ const char *tlscreds = qdict_get_try_str(options, "tls-creds");
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
@@ -382,6 +440,9 @@
if (export) {
qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
}
+ if (tlscreds) {
+ qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds)));
+ }
bs->full_open_options = opts;
}
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index efc31a4..12cae0e 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -18,32 +18,128 @@
#include "qmp-commands.h"
#include "trace.h"
#include "block/nbd.h"
-#include "qemu/sockets.h"
+#include "io/channel-socket.h"
-static int server_fd = -1;
+typedef struct NBDServerData {
+ QIOChannelSocket *listen_ioc;
+ int watch;
+ QCryptoTLSCreds *tlscreds;
+} NBDServerData;
-static void nbd_accept(void *opaque)
+static NBDServerData *nbd_server;
+
+
+static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
+ gpointer opaque)
{
- struct sockaddr_in addr;
- socklen_t addr_len = sizeof(addr);
+ QIOChannelSocket *cioc;
- int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
- if (fd >= 0) {
- nbd_client_new(NULL, fd, nbd_client_put);
+ if (!nbd_server) {
+ return FALSE;
}
+
+ cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
+ NULL);
+ if (!cioc) {
+ return TRUE;
+ }
+
+ nbd_client_new(NULL, cioc,
+ nbd_server->tlscreds, NULL,
+ nbd_client_put);
+ object_unref(OBJECT(cioc));
+ return TRUE;
}
-void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
+
+static void nbd_server_free(NBDServerData *server)
{
- if (server_fd != -1) {
+ if (!server) {
+ return;
+ }
+
+ if (server->watch != -1) {
+ g_source_remove(server->watch);
+ }
+ object_unref(OBJECT(server->listen_ioc));
+ if (server->tlscreds) {
+ object_unref(OBJECT(server->tlscreds));
+ }
+
+ g_free(server);
+}
+
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
+{
+ Object *obj;
+ QCryptoTLSCreds *creds;
+
+ obj = object_resolve_path_component(
+ object_get_objects_root(), id);
+ if (!obj) {
+ error_setg(errp, "No TLS credentials with id '%s'",
+ id);
+ return NULL;
+ }
+ creds = (QCryptoTLSCreds *)
+ object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
+ if (!creds) {
+ error_setg(errp, "Object with id '%s' is not TLS credentials",
+ id);
+ return NULL;
+ }
+
+ if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ error_setg(errp,
+ "Expecting TLS credentials with a server endpoint");
+ return NULL;
+ }
+ object_ref(obj);
+ return creds;
+}
+
+
+void qmp_nbd_server_start(SocketAddress *addr,
+ bool has_tls_creds, const char *tls_creds,
+ Error **errp)
+{
+ if (nbd_server) {
error_setg(errp, "NBD server already running");
return;
}
- server_fd = socket_listen(addr, errp);
- if (server_fd != -1) {
- qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL);
+ nbd_server = g_new0(NBDServerData, 1);
+ nbd_server->watch = -1;
+ nbd_server->listen_ioc = qio_channel_socket_new();
+ if (qio_channel_socket_listen_sync(
+ nbd_server->listen_ioc, addr, errp) < 0) {
+ goto error;
}
+
+ if (has_tls_creds) {
+ nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
+ if (!nbd_server->tlscreds) {
+ goto error;
+ }
+
+ if (addr->type != SOCKET_ADDRESS_KIND_INET) {
+ error_setg(errp, "TLS is only supported with IPv4/IPv6");
+ goto error;
+ }
+ }
+
+ nbd_server->watch = qio_channel_add_watch(
+ QIO_CHANNEL(nbd_server->listen_ioc),
+ G_IO_IN,
+ nbd_accept,
+ NULL,
+ NULL);
+
+ return;
+
+ error:
+ nbd_server_free(nbd_server);
+ nbd_server = NULL;
}
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
@@ -52,7 +148,7 @@
BlockBackend *blk;
NBDExport *exp;
- if (server_fd == -1) {
+ if (!nbd_server) {
error_setg(errp, "NBD server not running");
return;
}
@@ -98,9 +194,6 @@
{
nbd_export_close_all();
- if (server_fd != -1) {
- qemu_set_fd_handler(server_fd, NULL, NULL, NULL);
- close(server_fd);
- server_fd = -1;
- }
+ nbd_server_free(nbd_server);
+ nbd_server = NULL;
}
diff --git a/hmp.c b/hmp.c
index c6419da..996cb91 100644
--- a/hmp.c
+++ b/hmp.c
@@ -30,6 +30,7 @@
#include "qapi/string-output-visitor.h"
#include "qapi/util.h"
#include "qapi-visit.h"
+#include "qom/object_interfaces.h"
#include "ui/console.h"
#include "block/qapi.h"
#include "qemu-io.h"
@@ -1655,58 +1656,27 @@
void hmp_object_add(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
- Error *err_end = NULL;
QemuOpts *opts;
- char *type = NULL;
- char *id = NULL;
OptsVisitor *ov;
- QDict *pdict;
- Visitor *v;
+ Object *obj = NULL;
opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
if (err) {
- goto out;
+ hmp_handle_error(mon, &err);
+ return;
}
ov = opts_visitor_new(opts);
- pdict = qdict_clone_shallow(qdict);
- v = opts_get_visitor(ov);
-
- visit_start_struct(v, NULL, NULL, 0, &err);
- if (err) {
- goto out_clean;
- }
-
- qdict_del(pdict, "qom-type");
- visit_type_str(v, "qom-type", &type, &err);
- if (err) {
- goto out_end;
- }
-
- qdict_del(pdict, "id");
- visit_type_str(v, "id", &id, &err);
- if (err) {
- goto out_end;
- }
-
- object_add(type, id, pdict, v, &err);
-
-out_end:
- visit_end_struct(v, &err_end);
- if (!err && err_end) {
- qmp_object_del(id, NULL);
- }
- error_propagate(&err, err_end);
-out_clean:
+ obj = user_creatable_add(qdict, opts_get_visitor(ov), &err);
opts_visitor_cleanup(ov);
-
- QDECREF(pdict);
qemu_opts_del(opts);
- g_free(id);
- g_free(type);
-out:
- hmp_handle_error(mon, &err);
+ if (err) {
+ hmp_handle_error(mon, &err);
+ }
+ if (obj) {
+ object_unref(obj);
+ }
}
void hmp_getfd(Monitor *mon, const QDict *qdict)
@@ -1823,7 +1793,7 @@
goto exit;
}
- qmp_nbd_server_start(addr, &local_err);
+ qmp_nbd_server_start(addr, false, NULL, &local_err);
qapi_free_SocketAddress(addr);
if (local_err != NULL) {
goto exit;
@@ -1934,7 +1904,7 @@
const char *id = qdict_get_str(qdict, "id");
Error *err = NULL;
- qmp_object_del(id, &err);
+ user_creatable_del(id, &err);
hmp_handle_error(mon, &err);
}
diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
index f8b2176..51d234a 100644
--- a/hw/ipmi/ipmi_bmc_sim.c
+++ b/hw/ipmi/ipmi_bmc_sim.c
@@ -534,7 +534,7 @@
continue; /* Not a sensor SDR we set from */
}
- if (sdr->sensor_owner_number > MAX_SENSORS) {
+ if (sdr->sensor_owner_number >= MAX_SENSORS) {
continue;
}
sens = s->sensors + sdr->sensor_owner_number;
@@ -1448,7 +1448,7 @@
IPMISensor *sens;
IPMI_CHECK_CMD_LEN(4);
- if ((cmd[2] > MAX_SENSORS) ||
+ if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
return;
@@ -1500,7 +1500,7 @@
IPMISensor *sens;
IPMI_CHECK_CMD_LEN(3);
- if ((cmd[2] > MAX_SENSORS) ||
+ if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
return;
@@ -1521,7 +1521,7 @@
IPMISensor *sens;
IPMI_CHECK_CMD_LEN(4);
- if ((cmd[2] > MAX_SENSORS) ||
+ if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
return;
@@ -1543,7 +1543,7 @@
IPMISensor *sens;
IPMI_CHECK_CMD_LEN(3);
- if ((cmd[2] > MAX_SENSORS) ||
+ if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
return;
@@ -1565,7 +1565,7 @@
IPMISensor *sens;
IPMI_CHECK_CMD_LEN(3);
- if ((cmd[2] > MAX_SENSORS) ||
+ if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
return;
@@ -1588,7 +1588,7 @@
IPMI_CHECK_CMD_LEN(5);
- if ((cmd[2] > MAX_SENSORS) ||
+ if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
return;
@@ -1607,7 +1607,7 @@
IPMI_CHECK_CMD_LEN(3);
- if ((cmd[2] > MAX_SENSORS) ||
+ if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
return;
diff --git a/hw/scsi/mptconfig.c b/hw/scsi/mptconfig.c
index d049825..7071854 100644
--- a/hw/scsi/mptconfig.c
+++ b/hw/scsi/mptconfig.c
@@ -123,6 +123,7 @@
va_copy(ap2, ap1);
size = vfill(NULL, 0, fmt, ap2);
*p_data = data = g_malloc(size);
+ va_end(ap2);
}
return vfill(data, size, fmt, ap1);
}
diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
index 333cc1f..499c146 100644
--- a/hw/scsi/mptsas.c
+++ b/hw/scsi/mptsas.c
@@ -504,6 +504,7 @@
reply_async->IOCLogInfo = count;
return;
}
+ g_free(reply_async);
reply.TerminationCount = count;
break;
@@ -823,7 +824,7 @@
{
uint32_t ret;
- ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_SHIFT;
+ ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_MASK;
ret |= s->state;
switch (s->doorbell_state) {
case DOORBELL_NONE:
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 7eccb41..b197adc 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -23,6 +23,8 @@
#include "qemu-common.h"
#include "qemu/option.h"
+#include "io/channel-socket.h"
+#include "crypto/tlscreds.h"
struct nbd_request {
uint32_t magic;
@@ -55,7 +57,10 @@
#define NBD_REP_ACK (1) /* Data sending finished. */
#define NBD_REP_SERVER (2) /* Export description. */
#define NBD_REP_ERR_UNSUP ((UINT32_C(1) << 31) | 1) /* Unknown option. */
+#define NBD_REP_ERR_POLICY ((UINT32_C(1) << 31) | 2) /* Server denied */
#define NBD_REP_ERR_INVALID ((UINT32_C(1) << 31) | 3) /* Invalid length. */
+#define NBD_REP_ERR_TLS_REQD ((UINT32_C(1) << 31) | 5) /* TLS required */
+
#define NBD_CMD_MASK_COMMAND 0x0000ffff
#define NBD_CMD_FLAG_FUA (1 << 16)
@@ -73,12 +78,19 @@
/* Maximum size of a single READ/WRITE data buffer */
#define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024)
-ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read);
-int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+ssize_t nbd_wr_syncv(QIOChannel *ioc,
+ struct iovec *iov,
+ size_t niov,
+ size_t offset,
+ size_t length,
+ bool do_read);
+int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
+ QCryptoTLSCreds *tlscreds, const char *hostname,
+ QIOChannel **outioc,
off_t *size, Error **errp);
-int nbd_init(int fd, int csock, uint32_t flags, off_t size);
-ssize_t nbd_send_request(int csock, struct nbd_request *request);
-ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply);
+int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size);
+ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request);
+ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply);
int nbd_client(int fd);
int nbd_disconnect(int fd);
@@ -98,7 +110,11 @@
void nbd_export_set_name(NBDExport *exp, const char *name);
void nbd_export_close_all(void);
-void nbd_client_new(NBDExport *exp, int csock, void (*close_fn)(NBDClient *));
+void nbd_client_new(NBDExport *exp,
+ QIOChannelSocket *sioc,
+ QCryptoTLSCreds *tlscreds,
+ const char *tlsaclname,
+ void (*close)(NBDClient *));
void nbd_client_get(NBDClient *client);
void nbd_client_put(NBDClient *client);
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 91b95ae..aa0f373 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -43,9 +43,6 @@
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
void *opaque);
-void object_add(const char *type, const char *id, const QDict *qdict,
- Visitor *v, Error **errp);
-
AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
bool has_opaque, const char *opaque,
Error **errp);
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index 283ae0d..d579746 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -2,6 +2,8 @@
#define OBJECT_INTERFACES_H
#include "qom/object.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/visitor.h"
#define TYPE_USER_CREATABLE "user-creatable"
@@ -72,4 +74,94 @@
* from implements USER_CREATABLE interface.
*/
bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp);
+
+/**
+ * user_creatable_add:
+ * @qdict: the object definition
+ * @v: the visitor
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Create an instance of the user creatable object whose type
+ * is defined in @qdict by the 'qom-type' field, placing it
+ * in the object composition tree with name provided by the
+ * 'id' field. The remaining fields in @qdict are used to
+ * initialize the object properties.
+ *
+ * Returns: the newly created object or NULL on error
+ */
+Object *user_creatable_add(const QDict *qdict,
+ Visitor *v, Error **errp);
+
+/**
+ * user_creatable_add_type:
+ * @type: the object type name
+ * @id: the unique ID for the object
+ * @qdict: the object properties
+ * @v: the visitor
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Create an instance of the user creatable object @type, placing
+ * it in the object composition tree with name @id, initializing
+ * it with properties from @qdict
+ *
+ * Returns: the newly created object or NULL on error
+ */
+Object *user_creatable_add_type(const char *type, const char *id,
+ const QDict *qdict,
+ Visitor *v, Error **errp);
+
+/**
+ * user_creatable_add_opts:
+ * @opts: the object definition
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Create an instance of the user creatable object whose type
+ * is defined in @opts by the 'qom-type' option, placing it
+ * in the object composition tree with name provided by the
+ * 'id' field. The remaining options in @opts are used to
+ * initialize the object properties.
+ *
+ * Returns: the newly created object or NULL on error
+ */
+Object *user_creatable_add_opts(QemuOpts *opts, Error **errp);
+
+
+/**
+ * user_creatable_add_opts_predicate:
+ * @type: the QOM type to be added
+ *
+ * A callback function to determine whether an object
+ * of type @type should be created. Instances of this
+ * callback should be passed to user_creatable_add_opts_foreach
+ */
+typedef bool (*user_creatable_add_opts_predicate)(const char *type);
+
+/**
+ * user_creatable_add_opts_foreach:
+ * @opaque: a user_creatable_add_opts_predicate callback or NULL
+ * @opts: options to create
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * An iterator callback to be used in conjunction with
+ * the qemu_opts_foreach() method for creating a list of
+ * objects from a set of QemuOpts
+ *
+ * The @opaque parameter can be passed a user_creatable_add_opts_predicate
+ * callback to filter which types of object are created during iteration.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int user_creatable_add_opts_foreach(void *opaque,
+ QemuOpts *opts, Error **errp);
+
+/**
+ * user_creatable_del:
+ * @id: the unique ID for the object
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Delete an instance of the user creatable object identified
+ * by @id.
+ */
+void user_creatable_del(const char *id, Error **errp);
+
#endif
diff --git a/migration/ram.c b/migration/ram.c
index 96c749f..704f6a9 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -1920,6 +1920,9 @@
acct_clear();
}
+ /* For memory_global_dirty_log_start below. */
+ qemu_mutex_lock_iothread();
+
qemu_mutex_lock_ramlist();
rcu_read_lock();
bytes_transferred = 0;
@@ -1944,6 +1947,7 @@
memory_global_dirty_log_start();
migration_bitmap_sync();
qemu_mutex_unlock_ramlist();
+ qemu_mutex_unlock_iothread();
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
diff --git a/nbd/client.c b/nbd/client.c
index f07cb48..9e5b651 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -71,19 +71,307 @@
*/
-int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+
+static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp)
+{
+ if (!(type & (1 << 31))) {
+ return 0;
+ }
+
+ switch (type) {
+ case NBD_REP_ERR_UNSUP:
+ error_setg(errp, "Unsupported option type %x", opt);
+ break;
+
+ case NBD_REP_ERR_POLICY:
+ error_setg(errp, "Denied by server for option %x", opt);
+ break;
+
+ case NBD_REP_ERR_INVALID:
+ error_setg(errp, "Invalid data length for option %x", opt);
+ break;
+
+ case NBD_REP_ERR_TLS_REQD:
+ error_setg(errp, "TLS negotiation required before option %x", opt);
+ break;
+
+ default:
+ error_setg(errp, "Unknown error code when asking for option %x", opt);
+ break;
+ }
+
+ return -1;
+}
+
+static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
+{
+ uint64_t magic;
+ uint32_t opt;
+ uint32_t type;
+ uint32_t len;
+ uint32_t namelen;
+
+ *name = NULL;
+ if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+ error_setg(errp, "failed to read list option magic");
+ return -1;
+ }
+ magic = be64_to_cpu(magic);
+ if (magic != NBD_REP_MAGIC) {
+ error_setg(errp, "Unexpected option list magic");
+ return -1;
+ }
+ if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+ error_setg(errp, "failed to read list option");
+ return -1;
+ }
+ opt = be32_to_cpu(opt);
+ if (opt != NBD_OPT_LIST) {
+ error_setg(errp, "Unexpected option type %x expected %x",
+ opt, NBD_OPT_LIST);
+ return -1;
+ }
+
+ if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
+ error_setg(errp, "failed to read list option type");
+ return -1;
+ }
+ type = be32_to_cpu(type);
+ if (type == NBD_REP_ERR_UNSUP) {
+ return 0;
+ }
+ if (nbd_handle_reply_err(opt, type, errp) < 0) {
+ return -1;
+ }
+
+ if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
+ error_setg(errp, "failed to read option length");
+ return -1;
+ }
+ len = be32_to_cpu(len);
+
+ if (type == NBD_REP_ACK) {
+ if (len != 0) {
+ error_setg(errp, "length too long for option end");
+ return -1;
+ }
+ } else if (type == NBD_REP_SERVER) {
+ if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
+ error_setg(errp, "failed to read option name length");
+ return -1;
+ }
+ namelen = be32_to_cpu(namelen);
+ if (len != (namelen + sizeof(namelen))) {
+ error_setg(errp, "incorrect option mame length");
+ return -1;
+ }
+ if (namelen > 255) {
+ error_setg(errp, "export name length too long %d", namelen);
+ return -1;
+ }
+
+ *name = g_new0(char, namelen + 1);
+ if (read_sync(ioc, *name, namelen) != namelen) {
+ error_setg(errp, "failed to read export name");
+ g_free(*name);
+ *name = NULL;
+ return -1;
+ }
+ (*name)[namelen] = '\0';
+ } else {
+ error_setg(errp, "Unexpected reply type %x expected %x",
+ type, NBD_REP_SERVER);
+ return -1;
+ }
+ return 1;
+}
+
+
+static int nbd_receive_query_exports(QIOChannel *ioc,
+ const char *wantname,
+ Error **errp)
+{
+ uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
+ uint32_t opt = cpu_to_be32(NBD_OPT_LIST);
+ uint32_t length = 0;
+ bool foundExport = false;
+
+ TRACE("Querying export list");
+ if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+ error_setg(errp, "Failed to send list option magic");
+ return -1;
+ }
+
+ if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+ error_setg(errp, "Failed to send list option number");
+ return -1;
+ }
+
+ if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
+ error_setg(errp, "Failed to send list option length");
+ return -1;
+ }
+
+ TRACE("Reading available export names");
+ while (1) {
+ char *name = NULL;
+ int ret = nbd_receive_list(ioc, &name, errp);
+
+ if (ret < 0) {
+ g_free(name);
+ name = NULL;
+ return -1;
+ }
+ if (ret == 0) {
+ /* Server doesn't support export listing, so
+ * we will just assume an export with our
+ * wanted name exists */
+ foundExport = true;
+ break;
+ }
+ if (name == NULL) {
+ TRACE("End of export name list");
+ break;
+ }
+ if (g_str_equal(name, wantname)) {
+ foundExport = true;
+ TRACE("Found desired export name '%s'", name);
+ } else {
+ TRACE("Ignored export name '%s'", name);
+ }
+ g_free(name);
+ }
+
+ if (!foundExport) {
+ error_setg(errp, "No export with name '%s' available", wantname);
+ return -1;
+ }
+
+ return 0;
+}
+
+static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
+ QCryptoTLSCreds *tlscreds,
+ const char *hostname, Error **errp)
+{
+ uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
+ uint32_t opt = cpu_to_be32(NBD_OPT_STARTTLS);
+ uint32_t length = 0;
+ uint32_t type;
+ QIOChannelTLS *tioc;
+ struct NBDTLSHandshakeData data = { 0 };
+
+ TRACE("Requesting TLS from server");
+ if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+ error_setg(errp, "Failed to send option magic");
+ return NULL;
+ }
+
+ if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+ error_setg(errp, "Failed to send option number");
+ return NULL;
+ }
+
+ if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
+ error_setg(errp, "Failed to send option length");
+ return NULL;
+ }
+
+ TRACE("Getting TLS reply from server1");
+ if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+ error_setg(errp, "failed to read option magic");
+ return NULL;
+ }
+ magic = be64_to_cpu(magic);
+ if (magic != NBD_REP_MAGIC) {
+ error_setg(errp, "Unexpected option magic");
+ return NULL;
+ }
+ TRACE("Getting TLS reply from server2");
+ if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+ error_setg(errp, "failed to read option");
+ return NULL;
+ }
+ opt = be32_to_cpu(opt);
+ if (opt != NBD_OPT_STARTTLS) {
+ error_setg(errp, "Unexpected option type %x expected %x",
+ opt, NBD_OPT_STARTTLS);
+ return NULL;
+ }
+
+ TRACE("Getting TLS reply from server");
+ if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
+ error_setg(errp, "failed to read option type");
+ return NULL;
+ }
+ type = be32_to_cpu(type);
+ if (type != NBD_REP_ACK) {
+ error_setg(errp, "Server rejected request to start TLS %x",
+ type);
+ return NULL;
+ }
+
+ TRACE("Getting TLS reply from server");
+ if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
+ error_setg(errp, "failed to read option length");
+ return NULL;
+ }
+ length = be32_to_cpu(length);
+ if (length != 0) {
+ error_setg(errp, "Start TLS reponse was not zero %x",
+ length);
+ return NULL;
+ }
+
+ TRACE("TLS request approved, setting up TLS");
+ tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp);
+ if (!tioc) {
+ return NULL;
+ }
+ data.loop = g_main_loop_new(g_main_context_default(), FALSE);
+ TRACE("Starting TLS hanshake");
+ qio_channel_tls_handshake(tioc,
+ nbd_tls_handshake,
+ &data,
+ NULL);
+
+ if (!data.complete) {
+ g_main_loop_run(data.loop);
+ }
+ g_main_loop_unref(data.loop);
+ if (data.error) {
+ error_propagate(errp, data.error);
+ object_unref(OBJECT(tioc));
+ return NULL;
+ }
+
+ return QIO_CHANNEL(tioc);
+}
+
+
+int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
+ QCryptoTLSCreds *tlscreds, const char *hostname,
+ QIOChannel **outioc,
off_t *size, Error **errp)
{
char buf[256];
uint64_t magic, s;
- uint16_t tmp;
int rc;
- TRACE("Receiving negotiation.");
+ TRACE("Receiving negotiation tlscreds=%p hostname=%s.",
+ tlscreds, hostname ? hostname : "<null>");
rc = -EINVAL;
- if (read_sync(csock, buf, 8) != 8) {
+ if (outioc) {
+ *outioc = NULL;
+ }
+ if (tlscreds && !outioc) {
+ error_setg(errp, "Output I/O channel required for TLS");
+ goto fail;
+ }
+
+ if (read_sync(ioc, buf, 8) != 8) {
error_setg(errp, "Failed to read data");
goto fail;
}
@@ -109,93 +397,133 @@
goto fail;
}
- if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
error_setg(errp, "Failed to read magic");
goto fail;
}
magic = be64_to_cpu(magic);
TRACE("Magic is 0x%" PRIx64, magic);
- if (name) {
- uint32_t reserved = 0;
+ if (magic == NBD_OPTS_MAGIC) {
+ uint32_t clientflags = 0;
uint32_t opt;
uint32_t namesize;
+ uint16_t globalflags;
+ uint16_t exportflags;
+ bool fixedNewStyle = false;
- TRACE("Checking magic (opts_magic)");
- if (magic != NBD_OPTS_MAGIC) {
- if (magic == NBD_CLIENT_MAGIC) {
- error_setg(errp, "Server does not support export names");
- } else {
- error_setg(errp, "Bad magic received");
- }
- goto fail;
- }
- if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+ if (read_sync(ioc, &globalflags, sizeof(globalflags)) !=
+ sizeof(globalflags)) {
error_setg(errp, "Failed to read server flags");
goto fail;
}
- *flags = be16_to_cpu(tmp) << 16;
- /* reserved for future use */
- if (write_sync(csock, &reserved, sizeof(reserved)) !=
- sizeof(reserved)) {
- error_setg(errp, "Failed to read reserved field");
+ globalflags = be16_to_cpu(globalflags);
+ *flags = globalflags << 16;
+ TRACE("Global flags are %x", globalflags);
+ if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) {
+ fixedNewStyle = true;
+ TRACE("Server supports fixed new style");
+ clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE;
+ }
+ /* client requested flags */
+ clientflags = cpu_to_be32(clientflags);
+ if (write_sync(ioc, &clientflags, sizeof(clientflags)) !=
+ sizeof(clientflags)) {
+ error_setg(errp, "Failed to send clientflags field");
goto fail;
}
+ if (tlscreds) {
+ if (fixedNewStyle) {
+ *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp);
+ if (!*outioc) {
+ goto fail;
+ }
+ ioc = *outioc;
+ } else {
+ error_setg(errp, "Server does not support STARTTLS");
+ goto fail;
+ }
+ }
+ if (!name) {
+ TRACE("Using default NBD export name \"\"");
+ name = "";
+ }
+ if (fixedNewStyle) {
+ /* Check our desired export is present in the
+ * server export list. Since NBD_OPT_EXPORT_NAME
+ * cannot return an error message, running this
+ * query gives us good error reporting if the
+ * server required TLS
+ */
+ if (nbd_receive_query_exports(ioc, name, errp) < 0) {
+ goto fail;
+ }
+ }
/* write the export name */
magic = cpu_to_be64(magic);
- if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
error_setg(errp, "Failed to send export name magic");
goto fail;
}
opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
- if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+ if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
error_setg(errp, "Failed to send export name option number");
goto fail;
}
namesize = cpu_to_be32(strlen(name));
- if (write_sync(csock, &namesize, sizeof(namesize)) !=
+ if (write_sync(ioc, &namesize, sizeof(namesize)) !=
sizeof(namesize)) {
error_setg(errp, "Failed to send export name length");
goto fail;
}
- if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
+ if (write_sync(ioc, (char *)name, strlen(name)) != strlen(name)) {
error_setg(errp, "Failed to send export name");
goto fail;
}
- } else {
- TRACE("Checking magic (cli_magic)");
- if (magic != NBD_CLIENT_MAGIC) {
- if (magic == NBD_OPTS_MAGIC) {
- error_setg(errp, "Server requires an export name");
- } else {
- error_setg(errp, "Bad magic received");
- }
+ if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
+ error_setg(errp, "Failed to read export length");
goto fail;
}
- }
+ *size = be64_to_cpu(s);
+ TRACE("Size is %" PRIu64, *size);
- if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
- error_setg(errp, "Failed to read export length");
- goto fail;
- }
- *size = be64_to_cpu(s);
- TRACE("Size is %" PRIu64, *size);
+ if (read_sync(ioc, &exportflags, sizeof(exportflags)) !=
+ sizeof(exportflags)) {
+ error_setg(errp, "Failed to read export flags");
+ goto fail;
+ }
+ exportflags = be16_to_cpu(exportflags);
+ *flags |= exportflags;
+ TRACE("Export flags are %x", exportflags);
+ } else if (magic == NBD_CLIENT_MAGIC) {
+ if (name) {
+ error_setg(errp, "Server does not support export names");
+ goto fail;
+ }
+ if (tlscreds) {
+ error_setg(errp, "Server does not support STARTTLS");
+ goto fail;
+ }
- if (!name) {
- if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) {
+ if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
+ error_setg(errp, "Failed to read export length");
+ goto fail;
+ }
+ *size = be64_to_cpu(s);
+ TRACE("Size is %" PRIu64, *size);
+
+ if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) {
error_setg(errp, "Failed to read export flags");
goto fail;
}
*flags = be32_to_cpup(flags);
} else {
- if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
- error_setg(errp, "Failed to read export flags");
- goto fail;
- }
- *flags |= be16_to_cpu(tmp);
+ error_setg(errp, "Bad magic received");
+ goto fail;
}
- if (read_sync(csock, &buf, 124) != 124) {
+
+ if (read_sync(ioc, &buf, 124) != 124) {
error_setg(errp, "Failed to read reserved block");
goto fail;
}
@@ -206,11 +534,11 @@
}
#ifdef __linux__
-int nbd_init(int fd, int csock, uint32_t flags, off_t size)
+int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size)
{
TRACE("Setting NBD socket");
- if (ioctl(fd, NBD_SET_SOCK, csock) < 0) {
+ if (ioctl(fd, NBD_SET_SOCK, sioc->fd) < 0) {
int serrno = errno;
LOG("Failed to set NBD socket");
return -serrno;
@@ -283,7 +611,7 @@
return ret;
}
#else
-int nbd_init(int fd, int csock, uint32_t flags, off_t size)
+int nbd_init(int fd, QIOChannelSocket *ioc, uint32_t flags, off_t size)
{
return -ENOTSUP;
}
@@ -294,7 +622,7 @@
}
#endif
-ssize_t nbd_send_request(int csock, struct nbd_request *request)
+ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request)
{
uint8_t buf[NBD_REQUEST_SIZE];
ssize_t ret;
@@ -309,7 +637,7 @@
"{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}",
request->from, request->len, request->handle, request->type);
- ret = write_sync(csock, buf, sizeof(buf));
+ ret = write_sync(ioc, buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -321,13 +649,13 @@
return 0;
}
-ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply)
+ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply)
{
uint8_t buf[NBD_REPLY_SIZE];
uint32_t magic;
ssize_t ret;
- ret = read_sync(csock, buf, sizeof(buf));
+ ret = read_sync(ioc, buf, sizeof(buf));
if (ret < 0) {
return ret;
}
diff --git a/nbd/common.c b/nbd/common.c
index 178d4a7..bde673a 100644
--- a/nbd/common.c
+++ b/nbd/common.c
@@ -19,47 +19,74 @@
#include "qemu/osdep.h"
#include "nbd-internal.h"
-ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
+ssize_t nbd_wr_syncv(QIOChannel *ioc,
+ struct iovec *iov,
+ size_t niov,
+ size_t offset,
+ size_t length,
+ bool do_read)
{
- size_t offset = 0;
- int err;
+ ssize_t done = 0;
+ Error *local_err = NULL;
+ struct iovec *local_iov = g_new(struct iovec, niov);
+ struct iovec *local_iov_head = local_iov;
+ unsigned int nlocal_iov = niov;
- if (qemu_in_coroutine()) {
- if (do_read) {
- return qemu_co_recv(fd, buffer, size);
- } else {
- return qemu_co_send(fd, buffer, size);
- }
- }
+ nlocal_iov = iov_copy(local_iov, nlocal_iov,
+ iov, niov,
+ offset, length);
- while (offset < size) {
+ while (nlocal_iov > 0) {
ssize_t len;
-
if (do_read) {
- len = qemu_recv(fd, buffer + offset, size - offset, 0);
+ len = qio_channel_readv(ioc, local_iov, nlocal_iov, &local_err);
} else {
- len = send(fd, buffer + offset, size - offset, 0);
+ len = qio_channel_writev(ioc, local_iov, nlocal_iov, &local_err);
}
-
- if (len < 0) {
- err = socket_error();
-
- /* recoverable error */
- if (err == EINTR || (offset > 0 && (err == EAGAIN || err == EWOULDBLOCK))) {
- continue;
+ if (len == QIO_CHANNEL_ERR_BLOCK) {
+ if (qemu_in_coroutine()) {
+ /* XXX figure out if we can create a variant on
+ * qio_channel_yield() that works with AIO contexts
+ * and consider using that in this branch */
+ qemu_coroutine_yield();
+ } else {
+ qio_channel_wait(ioc,
+ do_read ? G_IO_IN : G_IO_OUT);
}
-
- /* unrecoverable error */
- return -err;
+ continue;
+ }
+ if (len < 0) {
+ TRACE("I/O error: %s", error_get_pretty(local_err));
+ error_free(local_err);
+ /* XXX handle Error objects */
+ done = -EIO;
+ goto cleanup;
}
- /* eof */
- if (len == 0) {
+ if (do_read && len == 0) {
break;
}
- offset += len;
+ iov_discard_front(&local_iov, &nlocal_iov, len);
+ done += len;
}
- return offset;
+ cleanup:
+ g_free(local_iov_head);
+ return done;
+}
+
+
+void nbd_tls_handshake(Object *src,
+ Error *err,
+ void *opaque)
+{
+ struct NBDTLSHandshakeData *data = opaque;
+
+ if (err) {
+ TRACE("TLS failed %s", error_get_pretty(err));
+ data->error = error_copy(err);
+ }
+ data->complete = true;
+ g_main_loop_quit(data->loop);
}
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index c0a6575..db6ab65 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -11,8 +11,10 @@
#define NBD_INTERNAL_H
#include "block/nbd.h"
#include "sysemu/block-backend.h"
+#include "io/channel-tls.h"
#include "qemu/coroutine.h"
+#include "qemu/iov.h"
#include <errno.h>
#include <string.h>
@@ -29,7 +31,6 @@
#include <linux/fs.h>
#endif
-#include "qemu/sockets.h"
#include "qemu/queue.h"
#include "qemu/main-loop.h"
@@ -78,6 +79,8 @@
#define NBD_OPT_EXPORT_NAME (1)
#define NBD_OPT_ABORT (2)
#define NBD_OPT_LIST (3)
+#define NBD_OPT_PEEK_EXPORT (4)
+#define NBD_OPT_STARTTLS (5)
/* NBD errors are based on errno numbers, so there is a 1:1 mapping,
* but only a limited set of errno values is specified in the protocol.
@@ -90,24 +93,33 @@
#define NBD_EINVAL 22
#define NBD_ENOSPC 28
-static inline ssize_t read_sync(int fd, void *buffer, size_t size)
+static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
{
+ struct iovec iov = { .iov_base = buffer, .iov_len = size };
/* Sockets are kept in blocking mode in the negotiation phase. After
* that, a non-readable socket simply means that another thread stole
* our request/reply. Synchronization is done with recv_coroutine, so
* that this is coroutine-safe.
*/
- return nbd_wr_sync(fd, buffer, size, true);
+ return nbd_wr_syncv(ioc, &iov, 1, 0, size, true);
}
-static inline ssize_t write_sync(int fd, void *buffer, size_t size)
+static inline ssize_t write_sync(QIOChannel *ioc, void *buffer, size_t size)
{
- int ret;
- do {
- /* For writes, we do expect the socket to be writable. */
- ret = nbd_wr_sync(fd, buffer, size, false);
- } while (ret == -EAGAIN);
- return ret;
+ struct iovec iov = { .iov_base = buffer, .iov_len = size };
+
+ return nbd_wr_syncv(ioc, &iov, 1, 0, size, false);
}
+struct NBDTLSHandshakeData {
+ GMainLoop *loop;
+ bool complete;
+ Error *error;
+};
+
+
+void nbd_tls_handshake(Object *src,
+ Error *err,
+ void *opaque);
+
#endif
diff --git a/nbd/server.c b/nbd/server.c
index dc1d66f..d4225cd 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -76,7 +76,10 @@
void (*close)(NBDClient *client);
NBDExport *exp;
- int sock;
+ QCryptoTLSCreds *tlscreds;
+ char *tlsaclname;
+ QIOChannelSocket *sioc; /* The underlying data channel */
+ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
Coroutine *recv_coroutine;
@@ -96,45 +99,56 @@
static void nbd_unset_handlers(NBDClient *client);
static void nbd_update_can_read(NBDClient *client);
-static void nbd_negotiate_continue(void *opaque)
+static gboolean nbd_negotiate_continue(QIOChannel *ioc,
+ GIOCondition condition,
+ void *opaque)
{
qemu_coroutine_enter(opaque, NULL);
+ return TRUE;
}
-static ssize_t nbd_negotiate_read(int fd, void *buffer, size_t size)
+static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
{
ssize_t ret;
+ guint watch;
assert(qemu_in_coroutine());
/* Negotiation are always in main loop. */
- qemu_set_fd_handler(fd, nbd_negotiate_continue, NULL,
- qemu_coroutine_self());
- ret = read_sync(fd, buffer, size);
- qemu_set_fd_handler(fd, NULL, NULL, NULL);
+ watch = qio_channel_add_watch(ioc,
+ G_IO_IN,
+ nbd_negotiate_continue,
+ qemu_coroutine_self(),
+ NULL);
+ ret = read_sync(ioc, buffer, size);
+ g_source_remove(watch);
return ret;
}
-static ssize_t nbd_negotiate_write(int fd, void *buffer, size_t size)
+static ssize_t nbd_negotiate_write(QIOChannel *ioc, void *buffer, size_t size)
{
ssize_t ret;
+ guint watch;
assert(qemu_in_coroutine());
/* Negotiation are always in main loop. */
- qemu_set_fd_handler(fd, NULL, nbd_negotiate_continue,
- qemu_coroutine_self());
- ret = write_sync(fd, buffer, size);
- qemu_set_fd_handler(fd, NULL, NULL, NULL);
+ watch = qio_channel_add_watch(ioc,
+ G_IO_OUT,
+ nbd_negotiate_continue,
+ qemu_coroutine_self(),
+ NULL);
+ ret = write_sync(ioc, buffer, size);
+ g_source_remove(watch);
return ret;
}
-static ssize_t nbd_negotiate_drop_sync(int fd, size_t size)
+static ssize_t nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
{
ssize_t ret, dropped = size;
uint8_t *buffer = g_malloc(MIN(65536, size));
while (size > 0) {
- ret = nbd_negotiate_read(fd, buffer, MIN(65536, size));
+ ret = nbd_negotiate_read(ioc, buffer, MIN(65536, size));
if (ret < 0) {
g_free(buffer);
return ret;
@@ -175,66 +189,69 @@
*/
-static int nbd_negotiate_send_rep(int csock, uint32_t type, uint32_t opt)
+static int nbd_negotiate_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt)
{
uint64_t magic;
uint32_t len;
+ TRACE("Reply opt=%x type=%x", type, opt);
+
magic = cpu_to_be64(NBD_REP_MAGIC);
- if (nbd_negotiate_write(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
LOG("write failed (rep magic)");
return -EINVAL;
}
opt = cpu_to_be32(opt);
- if (nbd_negotiate_write(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+ if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
LOG("write failed (rep opt)");
return -EINVAL;
}
type = cpu_to_be32(type);
- if (nbd_negotiate_write(csock, &type, sizeof(type)) != sizeof(type)) {
+ if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) {
LOG("write failed (rep type)");
return -EINVAL;
}
len = cpu_to_be32(0);
- if (nbd_negotiate_write(csock, &len, sizeof(len)) != sizeof(len)) {
+ if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
LOG("write failed (rep data length)");
return -EINVAL;
}
return 0;
}
-static int nbd_negotiate_send_rep_list(int csock, NBDExport *exp)
+static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
{
uint64_t magic, name_len;
uint32_t opt, type, len;
+ TRACE("Advertizing export name '%s'", exp->name ? exp->name : "");
name_len = strlen(exp->name);
magic = cpu_to_be64(NBD_REP_MAGIC);
- if (nbd_negotiate_write(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
LOG("write failed (magic)");
return -EINVAL;
}
opt = cpu_to_be32(NBD_OPT_LIST);
- if (nbd_negotiate_write(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+ if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
LOG("write failed (opt)");
return -EINVAL;
}
type = cpu_to_be32(NBD_REP_SERVER);
- if (nbd_negotiate_write(csock, &type, sizeof(type)) != sizeof(type)) {
+ if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) {
LOG("write failed (reply type)");
return -EINVAL;
}
len = cpu_to_be32(name_len + sizeof(len));
- if (nbd_negotiate_write(csock, &len, sizeof(len)) != sizeof(len)) {
+ if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
LOG("write failed (length)");
return -EINVAL;
}
len = cpu_to_be32(name_len);
- if (nbd_negotiate_write(csock, &len, sizeof(len)) != sizeof(len)) {
+ if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
LOG("write failed (length)");
return -EINVAL;
}
- if (nbd_negotiate_write(csock, exp->name, name_len) != name_len) {
+ if (nbd_negotiate_write(ioc, exp->name, name_len) != name_len) {
LOG("write failed (buffer)");
return -EINVAL;
}
@@ -243,30 +260,29 @@
static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
{
- int csock;
NBDExport *exp;
- csock = client->sock;
if (length) {
- if (nbd_negotiate_drop_sync(csock, length) != length) {
+ if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
return -EIO;
}
- return nbd_negotiate_send_rep(csock, NBD_REP_ERR_INVALID, NBD_OPT_LIST);
+ return nbd_negotiate_send_rep(client->ioc,
+ NBD_REP_ERR_INVALID, NBD_OPT_LIST);
}
/* For each export, send a NBD_REP_SERVER reply. */
QTAILQ_FOREACH(exp, &exports, next) {
- if (nbd_negotiate_send_rep_list(csock, exp)) {
+ if (nbd_negotiate_send_rep_list(client->ioc, exp)) {
return -EINVAL;
}
}
/* Finish with a NBD_REP_ACK. */
- return nbd_negotiate_send_rep(csock, NBD_REP_ACK, NBD_OPT_LIST);
+ return nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_LIST);
}
static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
{
- int rc = -EINVAL, csock = client->sock;
+ int rc = -EINVAL;
char name[256];
/* Client sends:
@@ -277,12 +293,14 @@
LOG("Bad length received");
goto fail;
}
- if (nbd_negotiate_read(csock, name, length) != length) {
+ if (nbd_negotiate_read(client->ioc, name, length) != length) {
LOG("read failed");
goto fail;
}
name[length] = '\0';
+ TRACE("Client requested export '%s'", name);
+
client->exp = nbd_export_find(name);
if (!client->exp) {
LOG("export not found");
@@ -296,10 +314,59 @@
return rc;
}
+
+static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
+ uint32_t length)
+{
+ QIOChannel *ioc;
+ QIOChannelTLS *tioc;
+ struct NBDTLSHandshakeData data = { 0 };
+
+ TRACE("Setting up TLS");
+ ioc = client->ioc;
+ if (length) {
+ if (nbd_negotiate_drop_sync(ioc, length) != length) {
+ return NULL;
+ }
+ nbd_negotiate_send_rep(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS);
+ return NULL;
+ }
+
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_STARTTLS);
+
+ tioc = qio_channel_tls_new_server(ioc,
+ client->tlscreds,
+ client->tlsaclname,
+ NULL);
+ if (!tioc) {
+ return NULL;
+ }
+
+ TRACE("Starting TLS handshake");
+ data.loop = g_main_loop_new(g_main_context_default(), FALSE);
+ qio_channel_tls_handshake(tioc,
+ nbd_tls_handshake,
+ &data,
+ NULL);
+
+ if (!data.complete) {
+ g_main_loop_run(data.loop);
+ }
+ g_main_loop_unref(data.loop);
+ if (data.error) {
+ object_unref(OBJECT(tioc));
+ error_free(data.error);
+ return NULL;
+ }
+
+ return QIO_CHANNEL(tioc);
+}
+
+
static int nbd_negotiate_options(NBDClient *client)
{
- int csock = client->sock;
uint32_t flags;
+ bool fixedNewstyle = false;
/* Client sends:
[ 0 .. 3] client flags
@@ -315,23 +382,30 @@
... Rest of request
*/
- if (nbd_negotiate_read(csock, &flags, sizeof(flags)) != sizeof(flags)) {
+ if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) !=
+ sizeof(flags)) {
LOG("read failed");
return -EIO;
}
TRACE("Checking client flags");
be32_to_cpus(&flags);
- if (flags != 0 && flags != NBD_FLAG_C_FIXED_NEWSTYLE) {
- LOG("Bad client flags received");
+ if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) {
+ TRACE("Support supports fixed newstyle handshake");
+ fixedNewstyle = true;
+ flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE;
+ }
+ if (flags != 0) {
+ TRACE("Unknown client flags 0x%x received", flags);
return -EIO;
}
while (1) {
int ret;
- uint32_t tmp, length;
+ uint32_t clientflags, length;
uint64_t magic;
- if (nbd_negotiate_read(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) !=
+ sizeof(magic)) {
LOG("read failed");
return -EINVAL;
}
@@ -341,38 +415,89 @@
return -EINVAL;
}
- if (nbd_negotiate_read(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+ if (nbd_negotiate_read(client->ioc, &clientflags,
+ sizeof(clientflags)) != sizeof(clientflags)) {
LOG("read failed");
return -EINVAL;
}
+ clientflags = be32_to_cpu(clientflags);
- if (nbd_negotiate_read(csock, &length,
- sizeof(length)) != sizeof(length)) {
+ if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) !=
+ sizeof(length)) {
LOG("read failed");
return -EINVAL;
}
length = be32_to_cpu(length);
- TRACE("Checking option");
- switch (be32_to_cpu(tmp)) {
- case NBD_OPT_LIST:
- ret = nbd_negotiate_handle_list(client, length);
- if (ret < 0) {
- return ret;
+ TRACE("Checking option 0x%x", clientflags);
+ if (client->tlscreds &&
+ client->ioc == (QIOChannel *)client->sioc) {
+ QIOChannel *tioc;
+ if (!fixedNewstyle) {
+ TRACE("Unsupported option 0x%x", clientflags);
+ return -EINVAL;
}
- break;
+ switch (clientflags) {
+ case NBD_OPT_STARTTLS:
+ tioc = nbd_negotiate_handle_starttls(client, length);
+ if (!tioc) {
+ return -EIO;
+ }
+ object_unref(OBJECT(client->ioc));
+ client->ioc = QIO_CHANNEL(tioc);
+ break;
- case NBD_OPT_ABORT:
- return -EINVAL;
+ default:
+ TRACE("Option 0x%x not permitted before TLS", clientflags);
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_TLS_REQD,
+ clientflags);
+ return -EINVAL;
+ }
+ } else if (fixedNewstyle) {
+ switch (clientflags) {
+ case NBD_OPT_LIST:
+ ret = nbd_negotiate_handle_list(client, length);
+ if (ret < 0) {
+ return ret;
+ }
+ break;
- case NBD_OPT_EXPORT_NAME:
- return nbd_negotiate_handle_export_name(client, length);
+ case NBD_OPT_ABORT:
+ return -EINVAL;
- default:
- tmp = be32_to_cpu(tmp);
- LOG("Unsupported option 0x%x", tmp);
- nbd_negotiate_send_rep(client->sock, NBD_REP_ERR_UNSUP, tmp);
- return -EINVAL;
+ case NBD_OPT_EXPORT_NAME:
+ return nbd_negotiate_handle_export_name(client, length);
+
+ case NBD_OPT_STARTTLS:
+ if (client->tlscreds) {
+ TRACE("TLS already enabled");
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_INVALID,
+ clientflags);
+ } else {
+ TRACE("TLS not configured");
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_POLICY,
+ clientflags);
+ }
+ return -EINVAL;
+ default:
+ TRACE("Unsupported option 0x%x", clientflags);
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP,
+ clientflags);
+ return -EINVAL;
+ }
+ } else {
+ /*
+ * If broken new-style we should drop the connection
+ * for anything except NBD_OPT_EXPORT_NAME
+ */
+ switch (clientflags) {
+ case NBD_OPT_EXPORT_NAME:
+ return nbd_negotiate_handle_export_name(client, length);
+
+ default:
+ TRACE("Unsupported option 0x%x", clientflags);
+ return -EINVAL;
+ }
}
}
}
@@ -385,13 +510,13 @@
static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
{
NBDClient *client = data->client;
- int csock = client->sock;
char buf[8 + 8 + 8 + 128];
int rc;
const int myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA);
+ bool oldStyle;
- /* Negotiation header without options:
+ /* Old style negotiation header without options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_CLIENT_MAGIC)
[16 .. 23] size
@@ -399,23 +524,25 @@
[26 .. 27] export flags
[28 .. 151] reserved (0)
- Negotiation header with options, part 1:
+ New style negotiation header with options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_OPTS_MAGIC)
[16 .. 17] server flags (0)
-
- part 2 (after options are sent):
+ ....options sent....
[18 .. 25] size
[26 .. 27] export flags
[28 .. 151] reserved (0)
*/
+ qio_channel_set_blocking(client->ioc, false, NULL);
rc = -EINVAL;
TRACE("Beginning negotiation.");
memset(buf, 0, sizeof(buf));
memcpy(buf, "NBDMAGIC", 8);
- if (client->exp) {
+
+ oldStyle = client->exp != NULL && !client->tlscreds;
+ if (oldStyle) {
assert ((client->exp->nbdflags & ~65535) == 0);
stq_be_p(buf + 8, NBD_CLIENT_MAGIC);
stq_be_p(buf + 16, client->exp->size);
@@ -425,13 +552,17 @@
stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE);
}
- if (client->exp) {
- if (nbd_negotiate_write(csock, buf, sizeof(buf)) != sizeof(buf)) {
+ if (oldStyle) {
+ if (client->tlscreds) {
+ TRACE("TLS cannot be enabled with oldstyle protocol");
+ goto fail;
+ }
+ if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) != sizeof(buf)) {
LOG("write failed");
goto fail;
}
} else {
- if (nbd_negotiate_write(csock, buf, 18) != 18) {
+ if (nbd_negotiate_write(client->ioc, buf, 18) != 18) {
LOG("write failed");
goto fail;
}
@@ -444,8 +575,8 @@
assert ((client->exp->nbdflags & ~65535) == 0);
stq_be_p(buf + 18, client->exp->size);
stw_be_p(buf + 26, client->exp->nbdflags | myflags);
- if (nbd_negotiate_write(csock, buf + 18,
- sizeof(buf) - 18) != sizeof(buf) - 18) {
+ if (nbd_negotiate_write(client->ioc, buf + 18, sizeof(buf) - 18) !=
+ sizeof(buf) - 18) {
LOG("write failed");
goto fail;
}
@@ -475,13 +606,13 @@
}
#endif
-static ssize_t nbd_receive_request(int csock, struct nbd_request *request)
+static ssize_t nbd_receive_request(QIOChannel *ioc, struct nbd_request *request)
{
uint8_t buf[NBD_REQUEST_SIZE];
uint32_t magic;
ssize_t ret;
- ret = read_sync(csock, buf, sizeof(buf));
+ ret = read_sync(ioc, buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -516,7 +647,7 @@
return 0;
}
-static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply)
+static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply)
{
uint8_t buf[NBD_REPLY_SIZE];
ssize_t ret;
@@ -534,7 +665,7 @@
TRACE("Sending response to client");
- ret = write_sync(csock, buf, sizeof(buf));
+ ret = write_sync(ioc, buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -562,8 +693,12 @@
assert(client->closing);
nbd_unset_handlers(client);
- close(client->sock);
- client->sock = -1;
+ object_unref(OBJECT(client->sioc));
+ object_unref(OBJECT(client->ioc));
+ if (client->tlscreds) {
+ object_unref(OBJECT(client->tlscreds));
+ }
+ g_free(client->tlsaclname);
if (client->exp) {
QTAILQ_REMOVE(&client->exp->clients, client, next);
nbd_export_put(client->exp);
@@ -583,7 +718,8 @@
/* Force requests to finish. They will drop their own references,
* then we'll close the socket and free the NBDClient.
*/
- shutdown(client->sock, 2);
+ qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH,
+ NULL);
/* Also tell the client, so that they release their reference. */
if (client->close) {
@@ -789,25 +925,25 @@
int len)
{
NBDClient *client = req->client;
- int csock = client->sock;
ssize_t rc, ret;
+ g_assert(qemu_in_coroutine());
qemu_co_mutex_lock(&client->send_lock);
client->send_coroutine = qemu_coroutine_self();
nbd_set_handlers(client);
if (!len) {
- rc = nbd_send_reply(csock, reply);
+ rc = nbd_send_reply(client->ioc, reply);
} else {
- socket_set_cork(csock, 1);
- rc = nbd_send_reply(csock, reply);
+ qio_channel_set_cork(client->ioc, true);
+ rc = nbd_send_reply(client->ioc, reply);
if (rc >= 0) {
- ret = qemu_co_send(csock, req->data, len);
+ ret = write_sync(client->ioc, req->data, len);
if (ret != len) {
rc = -EIO;
}
}
- socket_set_cork(csock, 0);
+ qio_channel_set_cork(client->ioc, false);
}
client->send_coroutine = NULL;
@@ -819,14 +955,14 @@
static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *request)
{
NBDClient *client = req->client;
- int csock = client->sock;
uint32_t command;
ssize_t rc;
+ g_assert(qemu_in_coroutine());
client->recv_coroutine = qemu_coroutine_self();
nbd_update_can_read(client);
- rc = nbd_receive_request(csock, request);
+ rc = nbd_receive_request(client->ioc, request);
if (rc < 0) {
if (rc != -EAGAIN) {
rc = -EIO;
@@ -861,7 +997,7 @@
if (command == NBD_CMD_WRITE) {
TRACE("Reading %u byte(s)", request->len);
- if (qemu_co_recv(csock, req->data, request->len) != request->len) {
+ if (read_sync(client->ioc, req->data, request->len) != request->len) {
LOG("reading from socket failed");
rc = -EIO;
goto out;
@@ -1056,7 +1192,7 @@
static void nbd_set_handlers(NBDClient *client)
{
if (client->exp && client->exp->ctx) {
- aio_set_fd_handler(client->exp->ctx, client->sock,
+ aio_set_fd_handler(client->exp->ctx, client->sioc->fd,
true,
client->can_read ? nbd_read : NULL,
client->send_coroutine ? nbd_restart_write : NULL,
@@ -1067,7 +1203,7 @@
static void nbd_unset_handlers(NBDClient *client)
{
if (client->exp && client->exp->ctx) {
- aio_set_fd_handler(client->exp->ctx, client->sock,
+ aio_set_fd_handler(client->exp->ctx, client->sioc->fd,
true, NULL, NULL, NULL);
}
}
@@ -1109,7 +1245,11 @@
g_free(data);
}
-void nbd_client_new(NBDExport *exp, int csock, void (*close_fn)(NBDClient *))
+void nbd_client_new(NBDExport *exp,
+ QIOChannelSocket *sioc,
+ QCryptoTLSCreds *tlscreds,
+ const char *tlsaclname,
+ void (*close_fn)(NBDClient *))
{
NBDClient *client;
NBDClientNewData *data = g_new(NBDClientNewData, 1);
@@ -1117,7 +1257,15 @@
client = g_malloc0(sizeof(NBDClient));
client->refcount = 1;
client->exp = exp;
- client->sock = csock;
+ client->tlscreds = tlscreds;
+ if (tlscreds) {
+ object_ref(OBJECT(client->tlscreds));
+ }
+ client->tlsaclname = g_strdup(tlsaclname);
+ client->sioc = sioc;
+ object_ref(OBJECT(client->sioc));
+ client->ioc = QIO_CHANNEL(sioc);
+ object_ref(OBJECT(client->ioc));
client->can_read = true;
client->close = close_fn;
diff --git a/qapi/block.json b/qapi/block.json
index ed61f82..58e6b30 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -146,13 +146,15 @@
# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
#
# @addr: Address on which to listen.
+# @tls-creds: (optional) ID of the TLS credentials object. Since 2.6
#
# Returns: error if the server is already running.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-start',
- 'data': { 'addr': 'SocketAddress' } }
+ 'data': { 'addr': 'SocketAddress',
+ '*tls-creds': 'str'} }
##
# @nbd-server-add:
diff --git a/qemu-char.c b/qemu-char.c
index 1b7d5da..ad11b75 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -896,13 +896,13 @@
ioc, &iov, 1,
fds, nfds, NULL);
if (ret == QIO_CHANNEL_ERR_BLOCK) {
- errno = EAGAIN;
- return -1;
- } else if (ret < 0) {
if (offset) {
return offset;
}
+ errno = EAGAIN;
+ return -1;
+ } else if (ret < 0) {
errno = EINVAL;
return -1;
}
@@ -1171,7 +1171,6 @@
int connected;
guint timer_tag;
guint open_tag;
- int slave_fd;
} PtyCharDriver;
static void pty_chr_update_read_handler_locked(CharDriverState *chr);
@@ -1348,7 +1347,6 @@
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_state(chr, 0);
- close(s->slave_fd);
object_unref(OBJECT(s->ioc));
if (s->timer_tag) {
g_source_remove(s->timer_tag);
@@ -1376,6 +1374,7 @@
return NULL;
}
+ close(slave_fd);
qemu_set_nonblock(master_fd);
chr = qemu_chr_alloc(common, errp);
@@ -1400,7 +1399,6 @@
chr->explicit_be_open = true;
s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
- s->slave_fd = slave_fd;
s->timer_tag = 0;
return chr;
diff --git a/qemu-img.c b/qemu-img.c
index 163d8c1..7030107 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -3104,6 +3104,7 @@
exit(EXIT_FAILURE);
}
+ module_call_init(MODULE_INIT_QOM);
bdrv_init();
if (argc < 2) {
error_exit("Not enough arguments");
diff --git a/qemu-io.c b/qemu-io.c
index 83c48f4..6c0c028 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -394,6 +394,7 @@
progname = basename(argv[0]);
qemu_init_exec_dir(argv[0]);
+ module_call_init(MODULE_INIT_QOM);
bdrv_init();
while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
diff --git a/qemu-nbd.c b/qemu-nbd.c
index d374015..933ca4a 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -22,17 +22,17 @@
#include "block/block_int.h"
#include "block/nbd.h"
#include "qemu/main-loop.h"
-#include "qemu/sockets.h"
#include "qemu/error-report.h"
+#include "qemu/config-file.h"
#include "block/snapshot.h"
#include "qapi/util.h"
#include "qapi/qmp/qstring.h"
+#include "qom/object_interfaces.h"
+#include "io/channel-socket.h"
#include <getopt.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
+#include <sys/types.h>
+#include <signal.h>
#include <libgen.h>
#include <pthread.h>
@@ -41,8 +41,11 @@
#define QEMU_NBD_OPT_AIO 2
#define QEMU_NBD_OPT_DISCARD 3
#define QEMU_NBD_OPT_DETECT_ZEROES 4
+#define QEMU_NBD_OPT_OBJECT 5
+#define QEMU_NBD_OPT_TLSCREDS 6
static NBDExport *exp;
+static bool newproto;
static int verbose;
static char *srcpath;
static SocketAddress *saddr;
@@ -50,7 +53,9 @@
static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state;
static int shared = 1;
static int nb_fds;
-static int server_fd;
+static QIOChannelSocket *server_ioc;
+static int server_watch = -1;
+static QCryptoTLSCreds *tlscreds;
static void usage(const char *name)
{
@@ -74,6 +79,9 @@
" -o, --offset=OFFSET offset into the image\n"
" -P, --partition=NUM only expose partition NUM\n"
"\n"
+"General purpose options:\n"
+" --object type,id=ID,... define an object such as 'secret' for providing\n"
+" passwords and/or encryption keys\n"
#ifdef __linux__
"Kernel NBD client support:\n"
" -c, --connect=DEV connect FILE to the local NBD device DEV\n"
@@ -230,19 +238,22 @@
char *device = arg;
off_t size;
uint32_t nbdflags;
- int fd, sock;
+ QIOChannelSocket *sioc;
+ int fd;
int ret;
pthread_t show_parts_thread;
Error *local_error = NULL;
-
- sock = socket_connect(saddr, &local_error, NULL, NULL);
- if (sock < 0) {
+ sioc = qio_channel_socket_new();
+ if (qio_channel_socket_connect_sync(sioc,
+ saddr,
+ &local_error) < 0) {
error_report_err(local_error);
goto out;
}
- ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
+ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags,
+ NULL, NULL, NULL,
&size, &local_error);
if (ret < 0) {
if (local_error) {
@@ -258,7 +269,7 @@
goto out_socket;
}
- ret = nbd_init(fd, sock, nbdflags, size);
+ ret = nbd_init(fd, sioc, nbdflags, size);
if (ret < 0) {
goto out_fd;
}
@@ -279,13 +290,14 @@
goto out_fd;
}
close(fd);
+ object_unref(OBJECT(sioc));
kill(getpid(), SIGTERM);
return (void *) EXIT_SUCCESS;
out_fd:
close(fd);
out_socket:
- closesocket(sock);
+ object_unref(OBJECT(sioc));
out:
kill(getpid(), SIGTERM);
return (void *) EXIT_FAILURE;
@@ -302,7 +314,7 @@
state = TERMINATED;
}
-static void nbd_update_server_fd_handler(int fd);
+static void nbd_update_server_watch(void);
static void nbd_client_closed(NBDClient *client)
{
@@ -310,37 +322,48 @@
if (nb_fds == 0 && !persistent && state == RUNNING) {
state = TERMINATE;
}
- nbd_update_server_fd_handler(server_fd);
+ nbd_update_server_watch();
nbd_client_put(client);
}
-static void nbd_accept(void *opaque)
+static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
{
- struct sockaddr_in addr;
- socklen_t addr_len = sizeof(addr);
+ QIOChannelSocket *cioc;
- int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
- if (fd < 0) {
- perror("accept");
- return;
+ cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
+ NULL);
+ if (!cioc) {
+ return TRUE;
}
if (state >= TERMINATE) {
- close(fd);
- return;
+ object_unref(OBJECT(cioc));
+ return TRUE;
}
nb_fds++;
- nbd_update_server_fd_handler(server_fd);
- nbd_client_new(exp, fd, nbd_client_closed);
+ nbd_update_server_watch();
+ nbd_client_new(newproto ? NULL : exp, cioc,
+ tlscreds, NULL, nbd_client_closed);
+ object_unref(OBJECT(cioc));
+
+ return TRUE;
}
-static void nbd_update_server_fd_handler(int fd)
+static void nbd_update_server_watch(void)
{
if (nbd_can_accept()) {
- qemu_set_fd_handler(fd, nbd_accept, NULL, (void *)(uintptr_t)fd);
+ if (server_watch == -1) {
+ server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
+ G_IO_IN,
+ nbd_accept,
+ NULL, NULL);
+ }
} else {
- qemu_set_fd_handler(fd, NULL, NULL, NULL);
+ if (server_watch != -1) {
+ g_source_remove(server_watch);
+ server_watch = -1;
+ }
}
}
@@ -371,6 +394,47 @@
}
+static QemuOptsList qemu_object_opts = {
+ .name = "object",
+ .implied_opt_name = "qom-type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
+ .desc = {
+ { }
+ },
+};
+
+
+
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
+{
+ Object *obj;
+ QCryptoTLSCreds *creds;
+
+ obj = object_resolve_path_component(
+ object_get_objects_root(), id);
+ if (!obj) {
+ error_setg(errp, "No TLS credentials with id '%s'",
+ id);
+ return NULL;
+ }
+ creds = (QCryptoTLSCreds *)
+ object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
+ if (!creds) {
+ error_setg(errp, "Object with id '%s' is not TLS credentials",
+ id);
+ return NULL;
+ }
+
+ if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ error_setg(errp,
+ "Expecting TLS credentials with a server endpoint");
+ return NULL;
+ }
+ object_ref(obj);
+ return creds;
+}
+
+
int main(int argc, char **argv)
{
BlockBackend *blk;
@@ -385,7 +449,7 @@
off_t fd_size;
QemuOpts *sn_opts = NULL;
const char *sn_id_or_name = NULL;
- const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:";
+ const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:";
struct option lopt[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
@@ -408,6 +472,9 @@
{ "format", 1, NULL, 'f' },
{ "persistent", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' },
+ { "object", 1, NULL, QEMU_NBD_OPT_OBJECT },
+ { "export-name", 1, NULL, 'x' },
+ { "tls-creds", 1, NULL, QEMU_NBD_OPT_TLSCREDS },
{ NULL, 0, NULL, 0 }
};
int ch;
@@ -416,7 +483,6 @@
int flags = BDRV_O_RDWR;
int partition = -1;
int ret = 0;
- int fd;
bool seen_cache = false;
bool seen_discard = false;
bool seen_aio = false;
@@ -425,6 +491,8 @@
Error *local_err = NULL;
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
QDict *options = NULL;
+ const char *export_name = NULL;
+ const char *tlscredsid = NULL;
/* The client thread uses SIGTERM to interrupt the server. A signal
* handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@@ -433,6 +501,8 @@
memset(&sa_sigterm, 0, sizeof(sa_sigterm));
sa_sigterm.sa_handler = termsig_handler;
sigaction(SIGTERM, &sa_sigterm, NULL);
+ module_call_init(MODULE_INIT_QOM);
+ qemu_add_opts(&qemu_object_opts);
qemu_init_exec_dir(argv[0]);
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
@@ -574,6 +644,9 @@
case 't':
persistent = 1;
break;
+ case 'x':
+ export_name = optarg;
+ break;
case 'v':
verbose = 1;
break;
@@ -588,6 +661,17 @@
case '?':
error_report("Try `%s --help' for more information.", argv[0]);
exit(EXIT_FAILURE);
+ case QEMU_NBD_OPT_OBJECT: {
+ QemuOpts *opts;
+ opts = qemu_opts_parse_noisily(&qemu_object_opts,
+ optarg, true);
+ if (!opts) {
+ exit(EXIT_FAILURE);
+ }
+ } break;
+ case QEMU_NBD_OPT_TLSCREDS:
+ tlscredsid = optarg;
+ break;
}
}
@@ -597,16 +681,45 @@
exit(EXIT_FAILURE);
}
+ if (qemu_opts_foreach(&qemu_object_opts,
+ user_creatable_add_opts_foreach,
+ NULL, &local_err)) {
+ error_report_err(local_err);
+ exit(EXIT_FAILURE);
+ }
+
+ if (tlscredsid) {
+ if (sockpath) {
+ error_report("TLS is only supported with IPv4/IPv6");
+ exit(EXIT_FAILURE);
+ }
+ if (device) {
+ error_report("TLS is not supported with a host device");
+ exit(EXIT_FAILURE);
+ }
+ if (!export_name) {
+ /* Set the default NBD protocol export name, since
+ * we *must* use new style protocol for TLS */
+ export_name = "";
+ }
+ tlscreds = nbd_get_tls_creds(tlscredsid, &local_err);
+ if (local_err) {
+ error_report("Failed to get TLS creds %s",
+ error_get_pretty(local_err));
+ exit(EXIT_FAILURE);
+ }
+ }
+
if (disconnect) {
- fd = open(argv[optind], O_RDWR);
- if (fd < 0) {
+ int nbdfd = open(argv[optind], O_RDWR);
+ if (nbdfd < 0) {
error_report("Cannot open %s: %s", argv[optind],
strerror(errno));
exit(EXIT_FAILURE);
}
- nbd_disconnect(fd);
+ nbd_disconnect(nbdfd);
- close(fd);
+ close(nbdfd);
printf("%s disconnected\n", argv[optind]);
@@ -738,9 +851,14 @@
error_report_err(local_err);
exit(EXIT_FAILURE);
}
+ if (export_name) {
+ nbd_export_set_name(exp, export_name);
+ newproto = true;
+ }
- fd = socket_listen(saddr, &local_err);
- if (fd < 0) {
+ server_ioc = qio_channel_socket_new();
+ if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) {
+ object_unref(OBJECT(server_ioc));
error_report_err(local_err);
return 1;
}
@@ -758,8 +876,7 @@
memset(&client_thread, 0, sizeof(client_thread));
}
- server_fd = fd;
- nbd_update_server_fd_handler(fd);
+ nbd_update_server_watch();
/* now when the initialization is (almost) complete, chdir("/")
* to free any busy filesystems */
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 0027841..227a73c 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -18,6 +18,13 @@
@var{dev} is an NBD device.
@table @option
+@item --object type,id=@var{id},...props...
+Define a new instance of the @var{type} object class identified by @var{id}.
+See the @code{qemu(1)} manual page for full details of the properties
+supported. The common object types that it makes sense to define are the
+@code{secret} object, which is used to supply passwords and/or encryption
+keys, and the @code{tls-creds} object, which is used to supply TLS
+credentials for the qemu-nbd server.
@item -p, --port=@var{port}
The TCP port to listen on (default @samp{10809})
@item -o, --offset=@var{offset}
@@ -67,6 +74,13 @@
Allow up to @var{num} clients to share the device (default @samp{1})
@item -t, --persistent
Don't exit on the last connection
+@item -x NAME, --export-name=NAME
+Set the NBD volume export name. This switches the server to use
+the new style NBD protocol negotiation
+@item --tls-creds=ID
+Enable mandatory TLS encryption for the server by setting the ID
+of the TLS credentials object previously created with the --object
+option.
@item -v, --verbose
Display extra debugging information
@item -h, --help
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 020e5ee..9fb0d78 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3825,7 +3825,7 @@
{
.name = "nbd-server-start",
- .args_type = "addr:q",
+ .args_type = "addr:q,tls-creds:s?",
.mhandler.cmd_new = qmp_marshal_nbd_server_start,
},
{
diff --git a/qmp.c b/qmp.c
index 6ae7230..9a93d5e 100644
--- a/qmp.c
+++ b/qmp.c
@@ -633,65 +633,13 @@
close(fd);
}
-void object_add(const char *type, const char *id, const QDict *qdict,
- Visitor *v, Error **errp)
-{
- Object *obj;
- ObjectClass *klass;
- const QDictEntry *e;
- Error *local_err = NULL;
-
- klass = object_class_by_name(type);
- if (!klass) {
- error_setg(errp, "invalid object type: %s", type);
- return;
- }
-
- if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
- error_setg(errp, "object type '%s' isn't supported by object-add",
- type);
- return;
- }
-
- if (object_class_is_abstract(klass)) {
- error_setg(errp, "object type '%s' is abstract", type);
- return;
- }
-
- obj = object_new(type);
- if (qdict) {
- for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
- object_property_set(obj, v, e->key, &local_err);
- if (local_err) {
- goto out;
- }
- }
- }
-
- object_property_add_child(object_get_objects_root(),
- id, obj, &local_err);
- if (local_err) {
- goto out;
- }
-
- user_creatable_complete(obj, &local_err);
- if (local_err) {
- object_property_del(object_get_objects_root(),
- id, &error_abort);
- goto out;
- }
-out:
- if (local_err) {
- error_propagate(errp, local_err);
- }
- object_unref(obj);
-}
void qmp_object_add(const char *type, const char *id,
bool has_props, QObject *props, Error **errp)
{
const QDict *pdict = NULL;
QmpInputVisitor *qiv;
+ Object *obj;
if (props) {
pdict = qobject_to_qdict(props);
@@ -702,27 +650,17 @@
}
qiv = qmp_input_visitor_new(props);
- object_add(type, id, pdict, qmp_input_get_visitor(qiv), errp);
+ obj = user_creatable_add_type(type, id, pdict,
+ qmp_input_get_visitor(qiv), errp);
qmp_input_visitor_cleanup(qiv);
+ if (obj) {
+ object_unref(obj);
+ }
}
void qmp_object_del(const char *id, Error **errp)
{
- Object *container;
- Object *obj;
-
- container = object_get_objects_root();
- obj = object_resolve_path_component(container, id);
- if (!obj) {
- error_setg(errp, "object id not found");
- return;
- }
-
- if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) {
- error_setg(errp, "%s is in use, can not be deleted", id);
- return;
- }
- object_unparent(obj);
+ user_creatable_del(id, errp);
}
MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp)
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index f1218f0..c2f6e29 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -1,6 +1,9 @@
#include "qemu/osdep.h"
#include "qom/object_interfaces.h"
#include "qemu/module.h"
+#include "qapi-visit.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/opts-visitor.h"
void user_creatable_complete(Object *obj, Error **errp)
{
@@ -31,6 +34,177 @@
}
}
+
+Object *user_creatable_add(const QDict *qdict,
+ Visitor *v, Error **errp)
+{
+ char *type = NULL;
+ char *id = NULL;
+ Object *obj = NULL;
+ Error *local_err = NULL, *end_err = NULL;
+ QDict *pdict;
+
+ pdict = qdict_clone_shallow(qdict);
+
+ visit_start_struct(v, NULL, NULL, 0, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ qdict_del(pdict, "qom-type");
+ visit_type_str(v, "qom-type", &type, &local_err);
+ if (local_err) {
+ goto out_visit;
+ }
+
+ qdict_del(pdict, "id");
+ visit_type_str(v, "id", &id, &local_err);
+ if (local_err) {
+ goto out_visit;
+ }
+
+ obj = user_creatable_add_type(type, id, pdict, v, &local_err);
+ if (local_err) {
+ goto out_visit;
+ }
+
+ out_visit:
+ visit_end_struct(v, &end_err);
+ if (end_err) {
+ error_propagate(&local_err, end_err);
+ if (obj) {
+ user_creatable_del(id, NULL);
+ }
+ goto out;
+ }
+
+out:
+ QDECREF(pdict);
+ g_free(id);
+ g_free(type);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ object_unref(obj);
+ return NULL;
+ }
+ return obj;
+}
+
+
+Object *user_creatable_add_type(const char *type, const char *id,
+ const QDict *qdict,
+ Visitor *v, Error **errp)
+{
+ Object *obj;
+ ObjectClass *klass;
+ const QDictEntry *e;
+ Error *local_err = NULL;
+
+ klass = object_class_by_name(type);
+ if (!klass) {
+ error_setg(errp, "invalid object type: %s", type);
+ return NULL;
+ }
+
+ if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
+ error_setg(errp, "object type '%s' isn't supported by object-add",
+ type);
+ return NULL;
+ }
+
+ if (object_class_is_abstract(klass)) {
+ error_setg(errp, "object type '%s' is abstract", type);
+ return NULL;
+ }
+
+ obj = object_new(type);
+ if (qdict) {
+ for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
+ object_property_set(obj, v, e->key, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ }
+ }
+
+ object_property_add_child(object_get_objects_root(),
+ id, obj, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ user_creatable_complete(obj, &local_err);
+ if (local_err) {
+ object_property_del(object_get_objects_root(),
+ id, &error_abort);
+ goto out;
+ }
+out:
+ if (local_err) {
+ error_propagate(errp, local_err);
+ object_unref(obj);
+ return NULL;
+ }
+ return obj;
+}
+
+
+Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
+{
+ OptsVisitor *ov;
+ QDict *pdict;
+ Object *obj = NULL;
+
+ ov = opts_visitor_new(opts);
+ pdict = qemu_opts_to_qdict(opts, NULL);
+
+ obj = user_creatable_add(pdict, opts_get_visitor(ov), errp);
+ opts_visitor_cleanup(ov);
+ QDECREF(pdict);
+ return obj;
+}
+
+
+int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp)
+{
+ bool (*type_predicate)(const char *) = opaque;
+ Object *obj = NULL;
+ const char *type;
+
+ type = qemu_opt_get(opts, "qom-type");
+ if (type && type_predicate &&
+ !type_predicate(type)) {
+ return 0;
+ }
+
+ obj = user_creatable_add_opts(opts, errp);
+ if (!obj) {
+ return -1;
+ }
+ object_unref(obj);
+ return 0;
+}
+
+
+void user_creatable_del(const char *id, Error **errp)
+{
+ Object *container;
+ Object *obj;
+
+ container = object_get_objects_root();
+ obj = object_resolve_path_component(container, id);
+ if (!obj) {
+ error_setg(errp, "object '%s' not found", id);
+ return;
+ }
+
+ if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) {
+ error_setg(errp, "object '%s' is in use, can not be deleted", id);
+ return;
+ }
+ object_unparent(obj);
+}
+
static void register_types(void)
{
static const TypeInfo uc_interface_info = {
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 257126f..c26f76e 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1715,11 +1715,15 @@
# 1. with a type on the left -- int [] a;
# 2. at the beginning of a line for slice initialisers -- [0...10] = 5,
# 3. inside a curly brace -- = { [0...10] = 5 }
+# 4. after a comma -- [1] = 5, [2] = 6
+# 5. in a macro definition -- #define abc(x) [x] = y
while ($line =~ /(.*?\s)\[/g) {
my ($where, $prefix) = ($-[1], $1);
if ($prefix !~ /$Type\s+$/ &&
($where != 0 || $prefix !~ /^.\s+$/) &&
- $prefix !~ /{\s+$/) {
+ $prefix !~ /{\s+$/ &&
+ $prefix !~ /\#\s*define[^(]*\([^)]*\)\s+$/ &&
+ $prefix !~ /,\s+$/) {
ERROR("space prohibited before open square bracket '['\n" . $herecurr);
}
}
diff --git a/tests/Makefile b/tests/Makefile
index 650e654..c1c605f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -390,8 +390,8 @@
tests/test-qapi-event.o tests/test-qmp-introspect.o \
$(test-qom-obj-y)
test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
-test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y)
test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
+test-block-obj-y = $(block-obj-y) $(test-io-obj-y)
tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y)
tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out
index fdedeb3..72f1b4c 100644
--- a/tests/qemu-iotests/140.out
+++ b/tests/qemu-iotests/140.out
@@ -9,7 +9,7 @@
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "drv", "tray-open": true}}
{"return": {}}
-can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: Failed to read export length
+can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: No export with name 'drv' available
no file open, try 'help open'
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out
index dad2024..d24ad20 100644
--- a/tests/qemu-iotests/143.out
+++ b/tests/qemu-iotests/143.out
@@ -1,7 +1,7 @@
QA output created by 143
{"return": {}}
{"return": {}}
-can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Failed to read export length
+can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: No export with name 'no_such_export' available
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
*** done
diff --git a/vl.c b/vl.c
index 175ebcc..18e6086 100644
--- a/vl.c
+++ b/vl.c
@@ -579,6 +579,7 @@
/* from -> to */
{ RUN_STATE_DEBUG, RUN_STATE_RUNNING },
{ RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH },
{ RUN_STATE_INMIGRATE, RUN_STATE_INTERNAL_ERROR },
{ RUN_STATE_INMIGRATE, RUN_STATE_IO_ERROR },
@@ -589,18 +590,24 @@
{ RUN_STATE_INMIGRATE, RUN_STATE_WATCHDOG },
{ RUN_STATE_INMIGRATE, RUN_STATE_GUEST_PANICKED },
{ RUN_STATE_INMIGRATE, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_INMIGRATE, RUN_STATE_PRELAUNCH },
+ { RUN_STATE_INMIGRATE, RUN_STATE_POSTMIGRATE },
{ RUN_STATE_INTERNAL_ERROR, RUN_STATE_PAUSED },
{ RUN_STATE_INTERNAL_ERROR, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_INTERNAL_ERROR, RUN_STATE_PRELAUNCH },
{ RUN_STATE_IO_ERROR, RUN_STATE_RUNNING },
{ RUN_STATE_IO_ERROR, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_IO_ERROR, RUN_STATE_PRELAUNCH },
{ RUN_STATE_PAUSED, RUN_STATE_RUNNING },
{ RUN_STATE_PAUSED, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_PAUSED, RUN_STATE_PRELAUNCH },
{ RUN_STATE_POSTMIGRATE, RUN_STATE_RUNNING },
{ RUN_STATE_POSTMIGRATE, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_POSTMIGRATE, RUN_STATE_PRELAUNCH },
{ RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING },
{ RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE },
@@ -608,8 +615,10 @@
{ RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING },
{ RUN_STATE_FINISH_MIGRATE, RUN_STATE_POSTMIGRATE },
+ { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PRELAUNCH },
{ RUN_STATE_RESTORE_VM, RUN_STATE_RUNNING },
+ { RUN_STATE_RESTORE_VM, RUN_STATE_PRELAUNCH },
{ RUN_STATE_RUNNING, RUN_STATE_DEBUG },
{ RUN_STATE_RUNNING, RUN_STATE_INTERNAL_ERROR },
@@ -626,17 +635,21 @@
{ RUN_STATE_SHUTDOWN, RUN_STATE_PAUSED },
{ RUN_STATE_SHUTDOWN, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_SHUTDOWN, RUN_STATE_PRELAUNCH },
{ RUN_STATE_DEBUG, RUN_STATE_SUSPENDED },
{ RUN_STATE_RUNNING, RUN_STATE_SUSPENDED },
{ RUN_STATE_SUSPENDED, RUN_STATE_RUNNING },
{ RUN_STATE_SUSPENDED, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_SUSPENDED, RUN_STATE_PRELAUNCH },
{ RUN_STATE_WATCHDOG, RUN_STATE_RUNNING },
{ RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_WATCHDOG, RUN_STATE_PRELAUNCH },
{ RUN_STATE_GUEST_PANICKED, RUN_STATE_RUNNING },
{ RUN_STATE_GUEST_PANICKED, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_GUEST_PANICKED, RUN_STATE_PRELAUNCH },
{ RUN_STATE__MAX, RUN_STATE__MAX },
};
@@ -1881,8 +1894,9 @@
cpu_synchronize_all_states();
qemu_system_reset(VMRESET_REPORT);
resume_all_vcpus();
- if (runstate_needs_reset()) {
- runstate_set(RUN_STATE_PAUSED);
+ if (!runstate_check(RUN_STATE_RUNNING) &&
+ !runstate_check(RUN_STATE_INMIGRATE)) {
+ runstate_set(RUN_STATE_PRELAUNCH);
}
}
if (qemu_wakeup_requested()) {
@@ -2816,64 +2830,6 @@
}
-static int object_create(void *opaque, QemuOpts *opts, Error **errp)
-{
- Error *err = NULL;
- Error *err_end = NULL;
- char *type = NULL;
- char *id = NULL;
- OptsVisitor *ov;
- QDict *pdict;
- bool (*type_predicate)(const char *) = opaque;
- Visitor *v;
-
- ov = opts_visitor_new(opts);
- pdict = qemu_opts_to_qdict(opts, NULL);
- v = opts_get_visitor(ov);
-
- visit_start_struct(v, NULL, NULL, 0, &err);
- if (err) {
- goto out;
- }
-
- qdict_del(pdict, "qom-type");
- visit_type_str(v, "qom-type", &type, &err);
- if (err) {
- goto out;
- }
- if (!type_predicate(type)) {
- visit_end_struct(v, NULL);
- goto out;
- }
-
- qdict_del(pdict, "id");
- visit_type_str(v, "id", &id, &err);
- if (err) {
- goto out_end;
- }
-
- object_add(type, id, pdict, v, &err);
-
-out_end:
- visit_end_struct(v, &err_end);
- if (!err && err_end) {
- qmp_object_del(id, NULL);
- }
- error_propagate(&err, err_end);
-
-out:
- opts_visitor_cleanup(ov);
-
- QDECREF(pdict);
- g_free(id);
- g_free(type);
- if (err) {
- error_report_err(err);
- return -1;
- }
- return 0;
-}
-
static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size,
MachineClass *mc)
{
@@ -4299,8 +4255,9 @@
socket_init();
if (qemu_opts_foreach(qemu_find_opts("object"),
- object_create,
- object_create_initial, NULL)) {
+ user_creatable_add_opts_foreach,
+ object_create_initial, &err)) {
+ error_report_err(err);
exit(1);
}
@@ -4417,8 +4374,9 @@
}
if (qemu_opts_foreach(qemu_find_opts("object"),
- object_create,
- object_create_delayed, NULL)) {
+ user_creatable_add_opts_foreach,
+ object_create_delayed, &err)) {
+ error_report_err(err);
exit(1);
}