nbd patches for 2019-01-21

Add 'qemu-nbd --list' for probing a remote NBD server's advertisements.
 
 - Eric Blake: 0/21 nbd: add qemu-nbd --list
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABCAAGBQJcRktLAAoJEKeha0olJ0NqY8cIAJqcHDAuk1GwcDE/NIAASY9E
 2PsaEL2pM65bEWUwsnVkFyRb4y4S4QX9VtAR/c9G6jR0YHuDvCCz7VGjiHh3eGso
 9AWKzuh5C4mayAN5dTOlbjryOx7hlcz8wDDjP5OQNqIlBvmWVAnLeh5Kkqlc7KDk
 f1SH8kJfqegDAhKDeDhi/HGlL1UVzMr5A2sR6I1wbb5IbBru5JtAKzgqAmdBH893
 lWZ9EDXqajwPCo8ASwZNyawmtmjx+VBeFjO2juA3qTWvf262roiB3XT5W/3n/CCb
 Er5CBwQQvSFBUfF9bRYDziyjwQwTXoIQJ6nuJLzPGx2cUpQbJ/svphoRQ6rBOjY=
 =gywW
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2019-01-21' into staging

nbd patches for 2019-01-21

Add 'qemu-nbd --list' for probing a remote NBD server's advertisements.

- Eric Blake: 0/21 nbd: add qemu-nbd --list

# gpg: Signature made Mon 21 Jan 2019 22:44:27 GMT
# gpg:                using RSA key A7A16B4A2527436A
# gpg: Good signature from "Eric Blake <eblake@redhat.com>"
# gpg:                 aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>"
# gpg:                 aka "[jpeg image of size 6874]"
# Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2  F3AA A7A1 6B4A 2527 436A

* remotes/ericb/tags/pull-nbd-2019-01-21: (21 commits)
  iotests: Enhance 223, 233 to cover 'qemu-nbd --list'
  nbd/client: Work around 3.0 bug for listing meta contexts
  qemu-nbd: Add --list option
  nbd/client: Add meta contexts to nbd_receive_export_list()
  nbd/client: Add nbd_receive_export_list()
  nbd/client: Refactor nbd_opt_go() to support NBD_OPT_INFO
  nbd/client: Pull out oldstyle size determination
  nbd/client: Split handshake into two functions
  nbd/client: Refactor return of nbd_receive_negotiate()
  nbd/client: Split out nbd_receive_one_meta_context()
  nbd/client: Split out nbd_send_meta_query()
  nbd/client: Change signature of nbd_negotiate_simple_meta_context()
  nbd/client: Move export name into NBDExportInfo
  nbd/client: Refactor nbd_receive_list()
  qemu-nbd: Avoid strtol open-coding
  nbd/server: Favor [u]int64_t over off_t
  nbd/server: Hoist length check to qmp_nbd_server_add
  qemu-nbd: Sanity check partition bounds
  qemu-nbd: Enhance man page
  maint: Allow for EXAMPLES in texi2pod
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-01-22 17:56:21 +00:00
commit 952bc8b3c2
14 changed files with 951 additions and 345 deletions

View File

@ -860,6 +860,8 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
# Reports/Analysis # Reports/Analysis
%/coverage-report.html: %/coverage-report.html:

View File

@ -249,11 +249,11 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
} }
context_id = payload_advance32(&payload); context_id = payload_advance32(&payload);
if (client->info.meta_base_allocation_id != context_id) { if (client->info.context_id != context_id) {
error_setg(errp, "Protocol error: unexpected context id %d for " error_setg(errp, "Protocol error: unexpected context id %d for "
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
"id is %d", context_id, "id is %d", context_id,
client->info.meta_base_allocation_id); client->info.context_id);
return -EINVAL; return -EINVAL;
} }
@ -999,10 +999,11 @@ int nbd_client_init(BlockDriverState *bs,
client->info.structured_reply = true; client->info.structured_reply = true;
client->info.base_allocation = true; client->info.base_allocation = true;
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, client->info.name = g_strdup(export ?: "");
tlscreds, hostname, ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname,
&client->ioc, &client->info, errp); &client->ioc, &client->info, errp);
g_free(client->info.x_dirty_bitmap); g_free(client->info.x_dirty_bitmap);
g_free(client->info.name);
if (ret < 0) { if (ret < 0) {
logout("Failed to negotiate with the NBD server\n"); logout("Failed to negotiate with the NBD server\n");
return ret; return ret;

View File

@ -146,6 +146,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
BlockBackend *on_eject_blk; BlockBackend *on_eject_blk;
NBDExport *exp; NBDExport *exp;
int64_t len;
if (!nbd_server) { if (!nbd_server) {
error_setg(errp, "NBD server not running"); error_setg(errp, "NBD server not running");
@ -168,6 +169,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
return; return;
} }
len = bdrv_getlength(bs);
if (len < 0) {
error_setg_errno(errp, -len,
"Failed to determine the NBD export's length");
return;
}
if (!has_writable) { if (!has_writable) {
writable = false; writable = false;
} }
@ -175,7 +183,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
writable = false; writable = false;
} }
exp = nbd_export_new(bs, 0, -1, name, NULL, bitmap, exp = nbd_export_new(bs, 0, len, name, NULL, bitmap,
writable ? 0 : NBD_FLAG_READ_ONLY, writable ? 0 : NBD_FLAG_READ_ONLY,
NULL, false, on_eject_blk, errp); NULL, false, on_eject_blk, errp);
if (!exp) { if (!exp) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2016-2017 Red Hat, Inc. * Copyright (C) 2016-2019 Red Hat, Inc.
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
* *
* Network Block Device * Network Block Device
@ -263,26 +263,39 @@ struct NBDExportInfo {
bool request_sizes; bool request_sizes;
char *x_dirty_bitmap; char *x_dirty_bitmap;
/* Set by client before nbd_receive_negotiate(), or by server results
* during nbd_receive_export_list() */
char *name; /* must be non-NULL */
/* In-out fields, set by client before nbd_receive_negotiate() and /* In-out fields, set by client before nbd_receive_negotiate() and
* updated by server results during nbd_receive_negotiate() */ * updated by server results during nbd_receive_negotiate() */
bool structured_reply; bool structured_reply;
bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */
/* Set by server results during nbd_receive_negotiate() */ /* Set by server results during nbd_receive_negotiate() and
* nbd_receive_export_list() */
uint64_t size; uint64_t size;
uint16_t flags; uint16_t flags;
uint32_t min_block; uint32_t min_block;
uint32_t opt_block; uint32_t opt_block;
uint32_t max_block; uint32_t max_block;
uint32_t meta_base_allocation_id; uint32_t context_id;
/* Set by server results during nbd_receive_export_list() */
char *description;
int n_contexts;
char **contexts;
}; };
typedef struct NBDExportInfo NBDExportInfo; typedef struct NBDExportInfo NBDExportInfo;
int nbd_receive_negotiate(QIOChannel *ioc, const char *name, int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
QCryptoTLSCreds *tlscreds, const char *hostname, const char *hostname, QIOChannel **outioc,
QIOChannel **outioc, NBDExportInfo *info, NBDExportInfo *info, Error **errp);
Error **errp); void nbd_free_export_list(NBDExportInfo *info, int count);
int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
const char *hostname, NBDExportInfo **info,
Error **errp);
int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info,
Error **errp); Error **errp);
int nbd_send_request(QIOChannel *ioc, NBDRequest *request); int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
@ -294,8 +307,8 @@ int nbd_errno_to_system_errno(int err);
typedef struct NBDExport NBDExport; typedef struct NBDExport NBDExport;
typedef struct NBDClient NBDClient; typedef struct NBDClient NBDClient;
NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
const char *name, const char *description, uint64_t size, const char *name, const char *desc,
const char *bitmap, uint16_t nbdflags, const char *bitmap, uint16_t nbdflags,
void (*close)(NBDExport *), bool writethrough, void (*close)(NBDExport *), bool writethrough,
BlockBackend *on_eject_blk, Error **errp); BlockBackend *on_eject_blk, Error **errp);

File diff suppressed because it is too large Load Diff

View File

@ -77,8 +77,8 @@ struct NBDExport {
BlockBackend *blk; BlockBackend *blk;
char *name; char *name;
char *description; char *description;
off_t dev_offset; uint64_t dev_offset;
off_t size; uint64_t size;
uint16_t nbdflags; uint16_t nbdflags;
QTAILQ_HEAD(, NBDClient) clients; QTAILQ_HEAD(, NBDClient) clients;
QTAILQ_ENTRY(NBDExport) next; QTAILQ_ENTRY(NBDExport) next;
@ -1455,8 +1455,8 @@ static void nbd_eject_notifier(Notifier *n, void *data)
nbd_export_close(exp); nbd_export_close(exp);
} }
NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
const char *name, const char *description, uint64_t size, const char *name, const char *desc,
const char *bitmap, uint16_t nbdflags, const char *bitmap, uint16_t nbdflags,
void (*close)(NBDExport *), bool writethrough, void (*close)(NBDExport *), bool writethrough,
BlockBackend *on_eject_blk, Error **errp) BlockBackend *on_eject_blk, Error **errp)
@ -1495,17 +1495,13 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
exp->refcount = 1; exp->refcount = 1;
QTAILQ_INIT(&exp->clients); QTAILQ_INIT(&exp->clients);
exp->blk = blk; exp->blk = blk;
assert(dev_offset <= INT64_MAX);
exp->dev_offset = dev_offset; exp->dev_offset = dev_offset;
exp->name = g_strdup(name); exp->name = g_strdup(name);
exp->description = g_strdup(description); exp->description = g_strdup(desc);
exp->nbdflags = nbdflags; exp->nbdflags = nbdflags;
exp->size = size < 0 ? blk_getlength(blk) : size; assert(size <= INT64_MAX - dev_offset);
if (exp->size < 0) { exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE);
error_setg_errno(errp, -exp->size,
"Failed to determine the NBD export's length");
goto fail;
}
exp->size -= exp->size % BDRV_SECTOR_SIZE;
if (bitmap) { if (bitmap) {
BdrvDirtyBitmap *bm = NULL; BdrvDirtyBitmap *bm = NULL;
@ -2134,10 +2130,10 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
return -EROFS; return -EROFS;
} }
if (request->from > client->exp->size || if (request->from > client->exp->size ||
request->from + request->len > client->exp->size) { request->len > client->exp->size - request->from) {
error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32 error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
", Size: %" PRIu64, request->from, request->len, ", Size: %" PRIu64, request->from, request->len,
(uint64_t)client->exp->size); client->exp->size);
return (request->type == NBD_CMD_WRITE || return (request->type == NBD_CMD_WRITE ||
request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL; request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL;
} }

View File

@ -3,20 +3,21 @@ nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending o
nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32 nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32
nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s" nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s"
nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback" nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback"
nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" nbd_receive_list(const char *name, const char *desc) "export list includes '%s', description '%s'"
nbd_opt_go_success(void) "Export is good to go" nbd_opt_info_go_start(const char *opt, const char *name) "Attempting %s for export '%s'"
nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" nbd_opt_info_go_success(const char *opt) "Export is ready after %s request"
nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 nbd_opt_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)"
nbd_opt_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32
nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'" nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'"
nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'"
nbd_receive_starttls_new_client(void) "Setting up TLS" nbd_receive_starttls_new_client(void) "Setting up TLS"
nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake" nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake"
nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s" nbd_opt_meta_request(const char *optname, const char *context, const char *export) "Requesting %s %s for export %s"
nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32 nbd_opt_meta_reply(const char *optname, const char *context, uint32_t id) "Received %s mapping of %s to id %" PRIu32
nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" nbd_start_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64 nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64
nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32 nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32
nbd_receive_negotiate_default_name(void) "Using default NBD export name \"\"" nbd_receive_negotiate_name(const char *name) "Requesting NBD export name '%s'"
nbd_receive_negotiate_size_flags(uint64_t size, uint16_t flags) "Size is %" PRIu64 ", export flags 0x%" PRIx16 nbd_receive_negotiate_size_flags(uint64_t size, uint16_t flags) "Size is %" PRIu64 ", export flags 0x%" PRIx16
nbd_init_set_socket(void) "Setting NBD socket" nbd_init_set_socket(void) "Setting NBD socket"
nbd_init_set_block_size(unsigned long block_size) "Setting block size to %lu" nbd_init_set_block_size(unsigned long block_size) "Setting block size to %lu"

View File

@ -76,7 +76,8 @@ static void usage(const char *name)
{ {
(printf) ( (printf) (
"Usage: %s [OPTIONS] FILE\n" "Usage: %s [OPTIONS] FILE\n"
"QEMU Disk Network Block Device Server\n" " or: %s -L [OPTIONS]\n"
"QEMU Disk Network Block Device Utility\n"
"\n" "\n"
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
" -V, --version output version information and exit\n" " -V, --version output version information and exit\n"
@ -98,6 +99,7 @@ static void usage(const char *name)
" -B, --bitmap=NAME expose a persistent dirty bitmap\n" " -B, --bitmap=NAME expose a persistent dirty bitmap\n"
"\n" "\n"
"General purpose options:\n" "General purpose options:\n"
" -L, --list list exports available from another NBD server\n"
" --object type,id=ID,... define an object such as 'secret' for providing\n" " --object type,id=ID,... define an object such as 'secret' for providing\n"
" passwords and/or encryption keys\n" " passwords and/or encryption keys\n"
" --tls-creds=ID use id of an earlier --object to provide TLS\n" " --tls-creds=ID use id of an earlier --object to provide TLS\n"
@ -131,7 +133,7 @@ static void usage(const char *name)
" --image-opts treat FILE as a full set of image options\n" " --image-opts treat FILE as a full set of image options\n"
"\n" "\n"
QEMU_HELP_BOTTOM "\n" QEMU_HELP_BOTTOM "\n"
, name, NBD_DEFAULT_PORT, "DEVICE"); , name, name, NBD_DEFAULT_PORT, "DEVICE");
} }
static void version(const char *name) static void version(const char *name)
@ -176,7 +178,7 @@ static void read_partition(uint8_t *p, struct partition_record *r)
} }
static int find_partition(BlockBackend *blk, int partition, static int find_partition(BlockBackend *blk, int partition,
off_t *offset, off_t *size) uint64_t *offset, uint64_t *size)
{ {
struct partition_record mbr[4]; struct partition_record mbr[4];
uint8_t data[MBR_SIZE]; uint8_t data[MBR_SIZE];
@ -243,6 +245,91 @@ static void termsig_handler(int signum)
} }
static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls,
const char *hostname)
{
int ret = EXIT_FAILURE;
int rc;
Error *err = NULL;
QIOChannelSocket *sioc;
NBDExportInfo *list;
int i, j;
sioc = qio_channel_socket_new();
if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) {
error_report_err(err);
return EXIT_FAILURE;
}
rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list,
&err);
if (rc < 0) {
if (err) {
error_report_err(err);
}
goto out;
}
printf("exports available: %d\n", rc);
for (i = 0; i < rc; i++) {
printf(" export: '%s'\n", list[i].name);
if (list[i].description && *list[i].description) {
printf(" description: %s\n", list[i].description);
}
if (list[i].flags & NBD_FLAG_HAS_FLAGS) {
printf(" size: %" PRIu64 "\n", list[i].size);
printf(" flags: 0x%x (", list[i].flags);
if (list[i].flags & NBD_FLAG_READ_ONLY) {
printf(" readonly");
}
if (list[i].flags & NBD_FLAG_SEND_FLUSH) {
printf(" flush");
}
if (list[i].flags & NBD_FLAG_SEND_FUA) {
printf(" fua");
}
if (list[i].flags & NBD_FLAG_ROTATIONAL) {
printf(" rotational");
}
if (list[i].flags & NBD_FLAG_SEND_TRIM) {
printf(" trim");
}
if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) {
printf(" zeroes");
}
if (list[i].flags & NBD_FLAG_SEND_DF) {
printf(" df");
}
if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) {
printf(" multi");
}
if (list[i].flags & NBD_FLAG_SEND_RESIZE) {
printf(" resize");
}
if (list[i].flags & NBD_FLAG_SEND_CACHE) {
printf(" cache");
}
printf(" )\n");
}
if (list[i].min_block) {
printf(" min block: %u\n", list[i].min_block);
printf(" opt block: %u\n", list[i].opt_block);
printf(" max block: %u\n", list[i].max_block);
}
if (list[i].n_contexts) {
printf(" available meta contexts: %d\n", list[i].n_contexts);
for (j = 0; j < list[i].n_contexts; j++) {
printf(" %s\n", list[i].contexts[j]);
}
}
}
nbd_free_export_list(list, rc);
ret = EXIT_SUCCESS;
out:
object_unref(OBJECT(sioc));
return ret;
}
#if HAVE_NBD_DEVICE #if HAVE_NBD_DEVICE
static void *show_parts(void *arg) static void *show_parts(void *arg)
{ {
@ -264,7 +351,7 @@ static void *show_parts(void *arg)
static void *nbd_client_thread(void *arg) static void *nbd_client_thread(void *arg)
{ {
char *device = arg; char *device = arg;
NBDExportInfo info = { .request_sizes = false, }; NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") };
QIOChannelSocket *sioc; QIOChannelSocket *sioc;
int fd; int fd;
int ret; int ret;
@ -279,7 +366,7 @@ static void *nbd_client_thread(void *arg)
goto out; goto out;
} }
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, ret = nbd_receive_negotiate(QIO_CHANNEL(sioc),
NULL, NULL, NULL, &info, &local_error); NULL, NULL, NULL, &info, &local_error);
if (ret < 0) { if (ret < 0) {
if (local_error) { if (local_error) {
@ -318,6 +405,7 @@ static void *nbd_client_thread(void *arg)
} }
close(fd); close(fd);
object_unref(OBJECT(sioc)); object_unref(OBJECT(sioc));
g_free(info.name);
kill(getpid(), SIGTERM); kill(getpid(), SIGTERM);
return (void *) EXIT_SUCCESS; return (void *) EXIT_SUCCESS;
@ -326,6 +414,7 @@ out_fd:
out_socket: out_socket:
object_unref(OBJECT(sioc)); object_unref(OBJECT(sioc));
out: out:
g_free(info.name);
kill(getpid(), SIGTERM); kill(getpid(), SIGTERM);
return (void *) EXIT_FAILURE; return (void *) EXIT_FAILURE;
} }
@ -423,7 +512,8 @@ static QemuOptsList qemu_object_opts = {
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
Error **errp)
{ {
Object *obj; Object *obj;
QCryptoTLSCreds *creds; QCryptoTLSCreds *creds;
@ -443,10 +533,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
return NULL; return NULL;
} }
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { if (list) {
error_setg(errp, if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
"Expecting TLS credentials with a server endpoint"); error_setg(errp,
return NULL; "Expecting TLS credentials with a client endpoint");
return NULL;
}
} else {
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
error_setg(errp,
"Expecting TLS credentials with a server endpoint");
return NULL;
}
} }
object_ref(obj); object_ref(obj);
return creds; return creds;
@ -469,7 +567,8 @@ static void setup_address_and_port(const char **address, const char **port)
static const char *socket_activation_validate_opts(const char *device, static const char *socket_activation_validate_opts(const char *device,
const char *sockpath, const char *sockpath,
const char *address, const char *address,
const char *port) const char *port,
bool list)
{ {
if (device != NULL) { if (device != NULL) {
return "NBD device can't be set when using socket activation"; return "NBD device can't be set when using socket activation";
@ -487,6 +586,10 @@ static const char *socket_activation_validate_opts(const char *device,
return "TCP port number can't be set when using socket activation"; return "TCP port number can't be set when using socket activation";
} }
if (list) {
return "List mode is incompatible with socket activation";
}
return NULL; return NULL;
} }
@ -500,17 +603,17 @@ int main(int argc, char **argv)
{ {
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
off_t dev_offset = 0; uint64_t dev_offset = 0;
uint16_t nbdflags = 0; uint16_t nbdflags = 0;
bool disconnect = false; bool disconnect = false;
const char *bindto = NULL; const char *bindto = NULL;
const char *port = NULL; const char *port = NULL;
char *sockpath = NULL; char *sockpath = NULL;
char *device = NULL; char *device = NULL;
off_t fd_size; int64_t fd_size;
QemuOpts *sn_opts = NULL; QemuOpts *sn_opts = NULL;
const char *sn_id_or_name = NULL; const char *sn_id_or_name = NULL;
const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:"; const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L";
struct option lopt[] = { struct option lopt[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
@ -523,6 +626,7 @@ int main(int argc, char **argv)
{ "bitmap", required_argument, NULL, 'B' }, { "bitmap", required_argument, NULL, 'B' },
{ "connect", required_argument, NULL, 'c' }, { "connect", required_argument, NULL, 'c' },
{ "disconnect", no_argument, NULL, 'd' }, { "disconnect", no_argument, NULL, 'd' },
{ "list", no_argument, NULL, 'L' },
{ "snapshot", no_argument, NULL, 's' }, { "snapshot", no_argument, NULL, 's' },
{ "load-snapshot", required_argument, NULL, 'l' }, { "load-snapshot", required_argument, NULL, 'l' },
{ "nocache", no_argument, NULL, 'n' }, { "nocache", no_argument, NULL, 'n' },
@ -546,9 +650,8 @@ int main(int argc, char **argv)
}; };
int ch; int ch;
int opt_ind = 0; int opt_ind = 0;
char *end;
int flags = BDRV_O_RDWR; int flags = BDRV_O_RDWR;
int partition = -1; int partition = 0;
int ret = 0; int ret = 0;
bool seen_cache = false; bool seen_cache = false;
bool seen_discard = false; bool seen_discard = false;
@ -558,7 +661,7 @@ int main(int argc, char **argv)
Error *local_err = NULL; Error *local_err = NULL;
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
QDict *options = NULL; QDict *options = NULL;
const char *export_name = ""; /* Default export name */ const char *export_name = NULL; /* defaults to "" later for server mode */
const char *export_description = NULL; const char *export_description = NULL;
const char *bitmap = NULL; const char *bitmap = NULL;
const char *tlscredsid = NULL; const char *tlscredsid = NULL;
@ -566,6 +669,7 @@ int main(int argc, char **argv)
bool writethrough = true; bool writethrough = true;
char *trace_file = NULL; char *trace_file = NULL;
bool fork_process = false; bool fork_process = false;
bool list = false;
int old_stderr = -1; int old_stderr = -1;
unsigned socket_activation; unsigned socket_activation;
@ -660,13 +764,8 @@ int main(int argc, char **argv)
port = optarg; port = optarg;
break; break;
case 'o': case 'o':
dev_offset = strtoll (optarg, &end, 0); if (qemu_strtou64(optarg, NULL, 0, &dev_offset) < 0) {
if (*end) { error_report("Invalid offset '%s'", optarg);
error_report("Invalid offset `%s'", optarg);
exit(EXIT_FAILURE);
}
if (dev_offset < 0) {
error_report("Offset must be positive `%s'", optarg);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
@ -688,13 +787,9 @@ int main(int argc, char **argv)
flags &= ~BDRV_O_RDWR; flags &= ~BDRV_O_RDWR;
break; break;
case 'P': case 'P':
partition = strtol(optarg, &end, 0); if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 ||
if (*end) { partition < 1 || partition > 8) {
error_report("Invalid partition `%s'", optarg); error_report("Invalid partition '%s'", optarg);
exit(EXIT_FAILURE);
}
if (partition < 1 || partition > 8) {
error_report("Invalid partition %d", partition);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
@ -715,15 +810,11 @@ int main(int argc, char **argv)
device = optarg; device = optarg;
break; break;
case 'e': case 'e':
shared = strtol(optarg, &end, 0); if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 ||
if (*end) { shared < 1) {
error_report("Invalid shared device number '%s'", optarg); error_report("Invalid shared device number '%s'", optarg);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (shared < 1) {
error_report("Shared device number must be greater than 0");
exit(EXIT_FAILURE);
}
break; break;
case 'f': case 'f':
fmt = optarg; fmt = optarg;
@ -772,13 +863,33 @@ int main(int argc, char **argv)
case QEMU_NBD_OPT_FORK: case QEMU_NBD_OPT_FORK:
fork_process = true; fork_process = true;
break; break;
case 'L':
list = true;
break;
} }
} }
if ((argc - optind) != 1) { if (list) {
if (argc != optind) {
error_report("List mode is incompatible with a file name");
exit(EXIT_FAILURE);
}
if (export_name || export_description || dev_offset || partition ||
device || disconnect || fmt || sn_id_or_name || bitmap ||
seen_aio || seen_discard || seen_cache) {
error_report("List mode is incompatible with per-device settings");
exit(EXIT_FAILURE);
}
if (fork_process) {
error_report("List mode is incompatible with forking");
exit(EXIT_FAILURE);
}
} else if ((argc - optind) != 1) {
error_report("Invalid number of arguments"); error_report("Invalid number of arguments");
error_printf("Try `%s --help' for more information.\n", argv[0]); error_printf("Try `%s --help' for more information.\n", argv[0]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else if (!export_name) {
export_name = "";
} }
qemu_opts_foreach(&qemu_object_opts, qemu_opts_foreach(&qemu_object_opts,
@ -797,7 +908,8 @@ int main(int argc, char **argv)
} else { } else {
/* Using socket activation - check user didn't use -p etc. */ /* Using socket activation - check user didn't use -p etc. */
const char *err_msg = socket_activation_validate_opts(device, sockpath, const char *err_msg = socket_activation_validate_opts(device, sockpath,
bindto, port); bindto, port,
list);
if (err_msg != NULL) { if (err_msg != NULL) {
error_report("%s", err_msg); error_report("%s", err_msg);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -820,7 +932,7 @@ int main(int argc, char **argv)
error_report("TLS is not supported with a host device"); error_report("TLS is not supported with a host device");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err);
if (local_err) { if (local_err) {
error_report("Failed to get TLS creds %s", error_report("Failed to get TLS creds %s",
error_get_pretty(local_err)); error_get_pretty(local_err));
@ -828,6 +940,11 @@ int main(int argc, char **argv)
} }
} }
if (list) {
saddr = nbd_build_socket_address(sockpath, bindto, port);
return qemu_nbd_client_list(saddr, tlscreds, bindto);
}
#if !HAVE_NBD_DEVICE #if !HAVE_NBD_DEVICE
if (disconnect || device) { if (disconnect || device) {
error_report("Kernel /dev/nbdN support not available"); error_report("Kernel /dev/nbdN support not available");
@ -1005,20 +1122,37 @@ int main(int argc, char **argv)
} }
if (dev_offset >= fd_size) { if (dev_offset >= fd_size) {
error_report("Offset (%lld) has to be smaller than the image size " error_report("Offset (%" PRIu64 ") has to be smaller than the image "
"(%lld)", "size (%" PRId64 ")", dev_offset, fd_size);
(long long int)dev_offset, (long long int)fd_size);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fd_size -= dev_offset; fd_size -= dev_offset;
if (partition != -1) { if (partition) {
ret = find_partition(blk, partition, &dev_offset, &fd_size); uint64_t limit;
if (dev_offset) {
error_report("Cannot request partition and offset together");
exit(EXIT_FAILURE);
}
ret = find_partition(blk, partition, &dev_offset, &limit);
if (ret < 0) { if (ret < 0) {
error_report("Could not find partition %d: %s", partition, error_report("Could not find partition %d: %s", partition,
strerror(-ret)); strerror(-ret));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/*
* MBR partition limits are (32-bit << 9); this assert lets
* the compiler know that we can't overflow 64 bits.
*/
assert(dev_offset + limit >= dev_offset);
if (dev_offset + limit > fd_size) {
error_report("Discovered partition %d at offset %" PRIu64
" size %" PRIu64 ", but size exceeds file length %"
PRId64, partition, dev_offset, limit, fd_size);
exit(EXIT_FAILURE);
}
fd_size = limit;
} }
export = nbd_export_new(bs, dev_offset, fd_size, export_name, export = nbd_export_new(bs, dev_offset, fd_size, export_name,

View File

@ -2,6 +2,8 @@
@c man begin SYNOPSIS @c man begin SYNOPSIS
@command{qemu-nbd} [OPTION]... @var{filename} @command{qemu-nbd} [OPTION]... @var{filename}
@command{qemu-nbd} @option{-L} [OPTION]...
@command{qemu-nbd} @option{-d} @var{dev} @command{qemu-nbd} @option{-d} @var{dev}
@c man end @c man end
@end example @end example
@ -10,11 +12,19 @@
Export a QEMU disk image using the NBD protocol. Export a QEMU disk image using the NBD protocol.
Other uses:
@itemize
@item
Bind a /dev/nbdX block device to a QEMU server (on Linux).
@item
As a client to query exports of a remote NBD server.
@end itemize
@c man end @c man end
@c man begin OPTIONS @c man begin OPTIONS
@var{filename} is a disk image filename, or a set of block @var{filename} is a disk image filename, or a set of block
driver options if @var{--image-opts} is specified. driver options if @option{--image-opts} is specified.
@var{dev} is an NBD device. @var{dev} is an NBD device.
@ -25,26 +35,29 @@ 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 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 @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 keys, and the @code{tls-creds} object, which is used to supply TLS
credentials for the qemu-nbd server. credentials for the qemu-nbd server or client.
@item -p, --port=@var{port} @item -p, --port=@var{port}
The TCP port to listen on (default @samp{10809}) The TCP port to listen on as a server, or connect to as a client
(default @samp{10809}).
@item -o, --offset=@var{offset} @item -o, --offset=@var{offset}
The offset into the image The offset into the image.
@item -b, --bind=@var{iface} @item -b, --bind=@var{iface}
The interface to bind to (default @samp{0.0.0.0}) The interface to bind to as a server, or connect to as a client
(default @samp{0.0.0.0}).
@item -k, --socket=@var{path} @item -k, --socket=@var{path}
Use a unix socket with path @var{path} Use a unix socket with path @var{path}.
@item --image-opts @item --image-opts
Treat @var{filename} as a set of image options, instead of a plain Treat @var{filename} as a set of image options, instead of a plain
filename. If this flag is specified, the @var{-f} flag should filename. If this flag is specified, the @var{-f} flag should
not be used, instead the '@code{format=}' option should be set. not be used, instead the '@code{format=}' option should be set.
@item -f, --format=@var{fmt} @item -f, --format=@var{fmt}
Force the use of the block driver for format @var{fmt} instead of Force the use of the block driver for format @var{fmt} instead of
auto-detecting auto-detecting.
@item -r, --read-only @item -r, --read-only
Export the disk as read-only Export the disk as read-only.
@item -P, --partition=@var{num} @item -P, --partition=@var{num}
Only expose partition @var{num} Only expose MBR partition @var{num}. Understands physical partitions
1-4 and logical partitions 5-8.
@item -B, --bitmap=@var{name} @item -B, --bitmap=@var{name}
If @var{filename} has a qcow2 persistent bitmap @var{name}, expose If @var{filename} has a qcow2 persistent bitmap @var{name}, expose
that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context
@ -52,7 +65,7 @@ accessible through NBD_OPT_SET_META_CONTEXT.
@item -s, --snapshot @item -s, --snapshot
Use @var{filename} as an external snapshot, create a temporary Use @var{filename} as an external snapshot, create a temporary
file with backing_file=@var{filename}, redirect the write to file with backing_file=@var{filename}, redirect the write to
the temporary one the temporary one.
@item -l, --load-snapshot=@var{snapshot_param} @item -l, --load-snapshot=@var{snapshot_param}
Load an internal snapshot inside @var{filename} and export it Load an internal snapshot inside @var{filename} and export it
as an read-only device, @var{snapshot_param} format is as an read-only device, @var{snapshot_param} format is
@ -76,31 +89,38 @@ driver-specific optimized zero write commands. @var{detect-zeroes} is one of
converts a zero write to an unmap operation and can only be used if converts a zero write to an unmap operation and can only be used if
@var{discard} is set to @samp{unmap}. The default is @samp{off}. @var{discard} is set to @samp{unmap}. The default is @samp{off}.
@item -c, --connect=@var{dev} @item -c, --connect=@var{dev}
Connect @var{filename} to NBD device @var{dev} Connect @var{filename} to NBD device @var{dev} (Linux only).
@item -d, --disconnect @item -d, --disconnect
Disconnect the device @var{dev} Disconnect the device @var{dev} (Linux only).
@item -e, --shared=@var{num} @item -e, --shared=@var{num}
Allow up to @var{num} clients to share the device (default @samp{1}) Allow up to @var{num} clients to share the device (default
@samp{1}). Safe for readers, but for now, consistency is not
guaranteed between multiple writers.
@item -t, --persistent @item -t, --persistent
Don't exit on the last connection Don't exit on the last connection.
@item -x, --export-name=@var{name} @item -x, --export-name=@var{name}
Set the NBD volume export name. This switches the server to use Set the NBD volume export name (default of a zero-length string).
the new style NBD protocol negotiation
@item -D, --description=@var{description} @item -D, --description=@var{description}
Set the NBD volume export description, as a human-readable Set the NBD volume export description, as a human-readable
string. Requires the use of @option{-x} string.
@item -L, --list
Connect as a client and list all details about the exports exposed by
a remote NBD server. This enables list mode, and is incompatible
with options that change behavior related to a specific export (such as
@option{--export-name}, @option{--offset}, ...).
@item --tls-creds=ID @item --tls-creds=ID
Enable mandatory TLS encryption for the server by setting the ID Enable mandatory TLS encryption for the server by setting the ID
of the TLS credentials object previously created with the --object of the TLS credentials object previously created with the --object
option. option; or provide the credentials needed for connecting as a client
in list mode.
@item --fork @item --fork
Fork off the server process and exit the parent once the server is running. Fork off the server process and exit the parent once the server is running.
@item -v, --verbose @item -v, --verbose
Display extra debugging information Display extra debugging information.
@item -h, --help @item -h, --help
Display this help and exit Display this help and exit.
@item -V, --version @item -V, --version
Display version information and exit Display version information and exit.
@item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}] @item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}]
@findex --trace @findex --trace
@include qemu-option-trace.texi @include qemu-option-trace.texi
@ -108,6 +128,63 @@ Display version information and exit
@c man end @c man end
@c man begin EXAMPLES
Start a server listening on port 10809 that exposes only the
guest-visible contents of a qcow2 file, with no TLS encryption, and
with the default export name (an empty string). The command is
one-shot, and will block until the first successful client
disconnects:
@example
qemu-nbd -f qcow2 file.qcow2
@end example
Start a long-running server listening with encryption on port 10810,
and require clients to have a correct X.509 certificate to connect to
a 1 megabyte subset of a raw file, using the export name 'subset':
@example
qemu-nbd \
--object tls-creds-x509,id=tls0,endpoint=server,dir=/path/to/qemutls \
--tls-creds tls0 -t -x subset -p 10810 \
--image-opts driver=raw,offset=1M,size=1M,file.driver=file,file.filename=file.raw
@end example
Serve a read-only copy of just the first MBR partition of a guest
image over a Unix socket with as many as 5 simultaneous readers, with
a persistent process forked as a daemon:
@example
qemu-nbd --fork --persistent --shared=5 --socket=/path/to/sock \
--partition=1 --read-only --format=qcow2 file.qcow2
@end example
Expose the guest-visible contents of a qcow2 file via a block device
/dev/nbd0 (and possibly creating /dev/nbd0p1 and friends for
partitions found within), then disconnect the device when done.
Access to bind qemu-nbd to an /dev/nbd device generally requires root
privileges, and may also require the execution of @code{modprobe nbd}
to enable the kernel NBD client module. @emph{CAUTION}: Do not use
this method to mount filesystems from an untrusted guest image - a
malicious guest may have prepared the image to attempt to trigger
kernel bugs in partition probing or file system mounting.
@example
qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2
qemu-nbd -d /dev/nbd0
@end example
Query a remote server to see details about what export(s) it is
serving on port 10809, and authenticating via PSK:
@example
qemu-nbd \
--object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \
--tls-creds tls0 -L -b remote.example.com
@end example
@c man end
@ignore @ignore
@setfilename qemu-nbd @setfilename qemu-nbd

View File

@ -398,7 +398,7 @@ $sects{NAME} = "$fn \- $tl\n";
$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; $sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) { BUGS NOTES FOOTNOTES EXAMPLES SEEALSO AUTHOR COPYRIGHT)) {
if(exists $sects{$sect}) { if(exists $sects{$sect}) {
$head = $sect; $head = $sect;
$head =~ s/SEEALSO/SEE ALSO/; $head =~ s/SEEALSO/SEE ALSO/;

View File

@ -127,6 +127,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
"arguments":{"addr":{"type":"unix", "arguments":{"addr":{"type":"unix",
"data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server
$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n", "bitmap":"b"}}' "return" "arguments":{"device":"n", "bitmap":"b"}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
@ -142,6 +143,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n", "name":"n2", "writable":true, "arguments":{"device":"n", "name":"n2", "writable":true,
"bitmap":"b2"}}' "return" "bitmap":"b2"}}' "return"
$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
echo echo
echo "=== Contrast normal status to large granularity dirty-bitmap ===" echo "=== Contrast normal status to large granularity dirty-bitmap ==="

View File

@ -30,12 +30,32 @@ wrote 2097152/2097152 bytes at offset 2097152
{"error": {"class": "GenericError", "desc": "NBD server not running"}} {"error": {"class": "GenericError", "desc": "NBD server not running"}}
{"return": {}} {"return": {}}
{"error": {"class": "GenericError", "desc": "NBD server already running"}} {"error": {"class": "GenericError", "desc": "NBD server already running"}}
exports available: 0
{"return": {}} {"return": {}}
{"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}} {"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}}
{"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}} {"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}}
{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}} {"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}} {"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}}
{"return": {}} {"return": {}}
exports available: 2
export: 'n'
size: 4194304
flags: 0x4ef ( readonly flush fua trim zeroes df cache )
min block: 512
opt block: 4096
max block: 33554432
available meta contexts: 2
base:allocation
qemu:dirty-bitmap:b
export: 'n2'
size: 4194304
flags: 0x4ed ( flush fua trim zeroes df cache )
min block: 512
opt block: 4096
max block: 33554432
available meta contexts: 2
base:allocation
qemu:dirty-bitmap:b2
=== Contrast normal status to large granularity dirty-bitmap === === Contrast normal status to large granularity dirty-bitmap ===

View File

@ -2,7 +2,7 @@
# #
# Test NBD TLS certificate / authorization integration # Test NBD TLS certificate / authorization integration
# #
# Copyright (C) 2018 Red Hat, Inc. # Copyright (C) 2018-2019 Red Hat, Inc.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -30,6 +30,7 @@ _cleanup()
{ {
nbd_server_stop nbd_server_stop
_cleanup_test_img _cleanup_test_img
rm -f "$TEST_DIR/server.log"
tls_x509_cleanup tls_x509_cleanup
} }
trap "_cleanup; exit \$status" 0 1 2 3 15 trap "_cleanup; exit \$status" 0 1 2 3 15
@ -66,12 +67,14 @@ $QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io
echo echo
echo "== check TLS client to plain server fails ==" echo "== check TLS client to plain server fails =="
nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" 2> "$TEST_DIR/server.log"
$QEMU_IMG info --image-opts \ obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0
--object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ $QEMU_IMG info --image-opts --object $obj \
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | sed "s/$nbd_tcp_port/PORT/g" 2>&1 | sed "s/$nbd_tcp_port/PORT/g"
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
--tls-creds=tls0
nbd_server_stop nbd_server_stop
@ -81,23 +84,28 @@ echo "== check plain client to TLS server fails =="
nbd_server_start_tcp_socket \ nbd_server_start_tcp_socket \
--object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \ --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \
--tls-creds tls0 \ --tls-creds tls0 \
-f $IMGFMT "$TEST_IMG" -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log"
$QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g" $QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g"
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port
echo echo
echo "== check TLS works ==" echo "== check TLS works =="
$QEMU_IMG info --image-opts \ obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0
--object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ $QEMU_IMG info --image-opts --object $obj \
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | sed "s/$nbd_tcp_port/PORT/g" 2>&1 | sed "s/$nbd_tcp_port/PORT/g"
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
--tls-creds=tls0
echo echo
echo "== check TLS with different CA fails ==" echo "== check TLS with different CA fails =="
$QEMU_IMG info --image-opts \ obj=tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0
--object tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 \ $QEMU_IMG info --image-opts --object $obj \
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | sed "s/$nbd_tcp_port/PORT/g" 2>&1 | sed "s/$nbd_tcp_port/PORT/g"
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
--tls-creds=tls0
echo echo
echo "== perform I/O over TLS ==" echo "== perform I/O over TLS =="
@ -109,6 +117,10 @@ $QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \
$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io $QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io
echo
echo "== final server log =="
cat "$TEST_DIR/server.log"
# success, all done # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -15,20 +15,33 @@ wrote 1048576/1048576 bytes at offset 1048576
== check TLS client to plain server fails == == check TLS client to plain server fails ==
qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls) qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls)
server reported: TLS not configured server reported: TLS not configured
qemu-nbd: Denied by server for option 5 (starttls)
server reported: TLS not configured
== check plain client to TLS server fails == == check plain client to TLS server fails ==
qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 8 (structured reply) qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 8 (structured reply)
server reported: Option 0x8 not permitted before TLS server reported: Option 0x8 not permitted before TLS
qemu-nbd: TLS negotiation required before option 8 (structured reply)
server reported: Option 0x8 not permitted before TLS
== check TLS works == == check TLS works ==
image: nbd://127.0.0.1:PORT image: nbd://127.0.0.1:PORT
file format: nbd file format: nbd
virtual size: 64M (67108864 bytes) virtual size: 64M (67108864 bytes)
disk size: unavailable disk size: unavailable
exports available: 1
export: ''
size: 67108864
flags: 0x4ed ( flush fua trim zeroes df cache )
min block: 512
opt block: 4096
max block: 33554432
available meta contexts: 1
base:allocation
== check TLS with different CA fails == == check TLS with different CA fails ==
qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer
qemu-nbd: The certificate hasn't got a known issuer
== perform I/O over TLS == == perform I/O over TLS ==
read 1048576/1048576 bytes at offset 1048576 read 1048576/1048576 bytes at offset 1048576
@ -37,4 +50,8 @@ wrote 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 1048576/1048576 bytes at offset 1048576 read 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== final server log ==
qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
*** done *** done