* 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:
Peter Maydell 2016-02-16 17:31:56 +00:00
commit 3fc63c3f33
31 changed files with 1499 additions and 507 deletions

View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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;
}

View File

@ -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
View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -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");

View File

@ -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) {

View File

@ -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 */

View File

@ -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

View File

@ -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
View File

@ -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)

View File

@ -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 = {

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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"}

View File

@ -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
View File

@ -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);
}