mirror of
https://github.com/qemu/qemu.git
synced 2025-01-21 13:03:26 +08:00
* 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJWw03lAAoJEL/70l94x66D/0MH/3Nctz5y1GKgAX0i6rKErV3/ hvPt6JHdWd7uBtowzO5kOy3fyOnVVST6jNHMQPAmJplUC40s6Ca0hycw9TjdJUdu ULq0Ba7tQ1TAXowDqibtEn+iTkzSrocTJLfEglNscKzJ4y5w0vc5Bt5PgPB65mbn oTo/YR8KyRWS6rXjyNnKb0PCaYEQziBndjuIxp9yJUsLcw1UgQJVcrUNEIiciOWu SlWDxvJJQt5cCrTPnUXeBdjJVGaLxbcpe2llEJnIuf6Pjq4u7J0y+DJ40y0DCi9q v3V4r16HhAGmBZNIlCtbClfhb/sRTVhONQiS9ehhROo8QCaL1psc11HWvMJ0tx0= =CDoZ -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
3fc63c3f33
6
Makefile
6
Makefile
@ -234,9 +234,9 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
|
||||
|
||||
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
|
||||
|
||||
|
@ -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 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
||||
{
|
||||
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 @@ static void nbd_reply_ready(void *opaque)
|
||||
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 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ static void nbd_co_receive_reply(NbdClientSession *s,
|
||||
* 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 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
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 @@ void nbd_client_close(BlockDriverState *bs)
|
||||
.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 @@ int nbd_client_init(BlockDriverState *bs, int sock, const char *export,
|
||||
|
||||
/* 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");
|
||||
|
@ -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 @@ typedef struct NbdClientSession {
|
||||
|
||||
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);
|
||||
|
||||
|
107
block/nbd.c
107
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 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
|
||||
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 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
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 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
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;
|
||||
}
|
||||
|
131
blockdev-nbd.c
131
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 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||
BlockBackend *blk;
|
||||
NBDExport *exp;
|
||||
|
||||
if (server_fd == -1) {
|
||||
if (!nbd_server) {
|
||||
error_setg(errp, "NBD server not running");
|
||||
return;
|
||||
}
|
||||
@ -98,9 +194,6 @@ void qmp_nbd_server_stop(Error **errp)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
56
hmp.c
56
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_netdev_del(Monitor *mon, const QDict *qdict)
|
||||
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 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
|
||||
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 @@ void hmp_object_del(Monitor *mon, const QDict *qdict)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -534,7 +534,7 @@ static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s)
|
||||
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 @@ static void set_sensor_evt_enable(IPMIBmcSim *ibs,
|
||||
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 @@ static void get_sensor_evt_enable(IPMIBmcSim *ibs,
|
||||
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 @@ static void rearm_sensor_evts(IPMIBmcSim *ibs,
|
||||
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 @@ static void get_sensor_evt_status(IPMIBmcSim *ibs,
|
||||
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 @@ static void get_sensor_reading(IPMIBmcSim *ibs,
|
||||
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 @@ static void set_sensor_type(IPMIBmcSim *ibs,
|
||||
|
||||
|
||||
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 @@ static void get_sensor_type(IPMIBmcSim *ibs,
|
||||
|
||||
|
||||
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;
|
||||
|
@ -123,6 +123,7 @@ static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1)
|
||||
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);
|
||||
}
|
||||
|
@ -504,6 +504,7 @@ reply_maybe_async:
|
||||
reply_async->IOCLogInfo = count;
|
||||
return;
|
||||
}
|
||||
g_free(reply_async);
|
||||
reply.TerminationCount = count;
|
||||
break;
|
||||
|
||||
@ -823,7 +824,7 @@ static uint32_t mptsas_doorbell_read(MPTSASState *s)
|
||||
{
|
||||
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:
|
||||
|
@ -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 @@ struct nbd_reply {
|
||||
#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 @@ enum {
|
||||
/* 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 @@ NBDExport *nbd_export_find(const char *name);
|
||||
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);
|
||||
|
||||
|
@ -43,9 +43,6 @@ void monitor_read_command(Monitor *mon, int show_prompt);
|
||||
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);
|
||||
|
@ -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 @@ void user_creatable_complete(Object *obj, Error **errp);
|
||||
* 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
|
||||
|
@ -1920,6 +1920,9 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||
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 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||
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);
|
||||
|
||||
|
440
nbd/client.c
440
nbd/client.c
@ -71,19 +71,307 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
|
||||
|
||||
*/
|
||||
|
||||
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 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
|
||||
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 @@ fail:
|
||||
}
|
||||
|
||||
#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 @@ int nbd_client(int fd)
|
||||
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 @@ int nbd_client(int fd)
|
||||
}
|
||||
#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 @@ ssize_t nbd_send_request(int csock, struct nbd_request *request)
|
||||
"{ .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 @@ ssize_t nbd_send_request(int csock, struct nbd_request *request)
|
||||
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;
|
||||
}
|
||||
|
83
nbd/common.c
83
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);
|
||||
}
|
||||
|
@ -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
|
||||
|
328
nbd/server.c
328
nbd/server.c
@ -76,7 +76,10 @@ struct NBDClient {
|
||||
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_set_handlers(NBDClient *client);
|
||||
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 ssize_t nbd_negotiate_drop_sync(int fd, size_t size)
|
||||
|
||||
*/
|
||||
|
||||
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_send_rep_list(int csock, NBDExport *exp)
|
||||
|
||||
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 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
|
||||
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 @@ fail:
|
||||
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 @@ static int nbd_negotiate_options(NBDClient *client)
|
||||
... 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 @@ static int nbd_negotiate_options(NBDClient *client)
|
||||
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 @@ typedef struct {
|
||||
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 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
|
||||
[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 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
|
||||
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 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
|
||||
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 @@ int nbd_disconnect(int fd)
|
||||
}
|
||||
#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 @@ static ssize_t nbd_receive_request(int csock, struct nbd_request *request)
|
||||
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 @@ static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply)
|
||||
|
||||
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 @@ void nbd_client_put(NBDClient *client)
|
||||
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 @@ static void client_close(NBDClient *client)
|
||||
/* 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 @@ static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply,
|
||||
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_send_reply(NBDRequest *req, struct nbd_reply *reply,
|
||||
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 @@ static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *reque
|
||||
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_restart_write(void *opaque)
|
||||
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_set_handlers(NBDClient *client)
|
||||
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 @@ out:
|
||||
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 @@ void nbd_client_new(NBDExport *exp, int csock, void (*close_fn)(NBDClient *))
|
||||
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;
|
||||
|
||||
|
@ -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:
|
||||
|
10
qemu-char.c
10
qemu-char.c
@ -896,13 +896,13 @@ static int io_channel_send_full(QIOChannel *ioc,
|
||||
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 @@ typedef struct {
|
||||
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 @@ static void pty_chr_close(struct CharDriverState *chr)
|
||||
|
||||
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 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
close(slave_fd);
|
||||
qemu_set_nonblock(master_fd);
|
||||
|
||||
chr = qemu_chr_alloc(common, errp);
|
||||
@ -1400,7 +1399,6 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
|
||||
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;
|
||||
|
@ -3104,6 +3104,7 @@ int main(int argc, char **argv)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
bdrv_init();
|
||||
if (argc < 2) {
|
||||
error_exit("Not enough arguments");
|
||||
|
@ -394,6 +394,7 @@ int main(int argc, char **argv)
|
||||
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) {
|
||||
|
195
qemu-nbd.c
195
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 int persistent = 0;
|
||||
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 @@ static void usage(const char *name)
|
||||
" -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 @@ static void *nbd_client_thread(void *arg)
|
||||
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 @@ static void *nbd_client_thread(void *arg)
|
||||
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 @@ static void *nbd_client_thread(void *arg)
|
||||
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 @@ static void nbd_export_closed(NBDExport *exp)
|
||||
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 @@ static void nbd_client_closed(NBDClient *client)
|
||||
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 SocketAddress *nbd_build_socket_address(const char *sockpath,
|
||||
}
|
||||
|
||||
|
||||
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 @@ int main(int argc, char **argv)
|
||||
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 @@ int main(int argc, char **argv)
|
||||
{ "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 main(int argc, char **argv)
|
||||
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 @@ int main(int argc, char **argv)
|
||||
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 @@ int main(int argc, char **argv)
|
||||
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 @@ int main(int argc, char **argv)
|
||||
case 't':
|
||||
persistent = 1;
|
||||
break;
|
||||
case 'x':
|
||||
export_name = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
@ -588,6 +661,17 @@ int main(int argc, char **argv)
|
||||
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 @@ int main(int argc, char **argv)
|
||||
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 @@ int main(int argc, char **argv)
|
||||
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 @@ int main(int argc, char **argv)
|
||||
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 */
|
||||
|
@ -18,6 +18,13 @@ Export a QEMU disk image using the NBD protocol.
|
||||
@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 @@ Disconnect the device @var{dev}
|
||||
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
|
||||
|
@ -3825,7 +3825,7 @@ EQMP
|
||||
|
||||
{
|
||||
.name = "nbd-server-start",
|
||||
.args_type = "addr:q",
|
||||
.args_type = "addr:q,tls-creds:s?",
|
||||
.mhandler.cmd_new = qmp_marshal_nbd_server_start,
|
||||
},
|
||||
{
|
||||
|
76
qmp.c
76
qmp.c
@ -633,65 +633,13 @@ void qmp_add_client(const char *protocol, const char *fdname,
|
||||
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 @@ void qmp_object_add(const char *type, const char *id,
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -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 @@ bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 = {
|
||||
|
@ -1715,11 +1715,15 @@ sub process {
|
||||
# 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);
|
||||
}
|
||||
}
|
||||
|
@ -390,8 +390,8 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
|
||||
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)
|
||||
|
@ -9,7 +9,7 @@ read 65536/65536 bytes at offset 0
|
||||
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"}
|
||||
|
@ -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
|
||||
|
86
vl.c
86
vl.c
@ -579,6 +579,7 @@ static const RunStateTransition runstate_transitions_def[] = {
|
||||
/* 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 @@ static const RunStateTransition runstate_transitions_def[] = {
|
||||
{ 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 @@ static const RunStateTransition runstate_transitions_def[] = {
|
||||
|
||||
{ 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 @@ static const RunStateTransition runstate_transitions_def[] = {
|
||||
|
||||
{ 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 @@ static bool main_loop_should_exit(void)
|
||||
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 bool object_create_delayed(const char *type)
|
||||
}
|
||||
|
||||
|
||||
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 @@ int main(int argc, char **argv, char **envp)
|
||||
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 @@ int main(int argc, char **argv, char **envp)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user