nbd: Add qemu-nbd -D for human-readable description
The NBD protocol allows servers to advertise a human-readable
description alongside an export name during NBD_OPT_LIST. Add
an option to pass through the user's string to the NBD client.
Doing this also makes it easier to test commit 200650d4
, which
is the client counterpart of receiving the description.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1476469998-28592-2-git-send-email-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
1775f111ea
commit
b1a75b3348
@ -115,6 +115,7 @@ BlockBackend *nbd_export_get_blockdev(NBDExport *exp);
|
|||||||
|
|
||||||
NBDExport *nbd_export_find(const char *name);
|
NBDExport *nbd_export_find(const char *name);
|
||||||
void nbd_export_set_name(NBDExport *exp, const char *name);
|
void nbd_export_set_name(NBDExport *exp, const char *name);
|
||||||
|
void nbd_export_set_description(NBDExport *exp, const char *description);
|
||||||
void nbd_export_close_all(void);
|
void nbd_export_close_all(void);
|
||||||
|
|
||||||
void nbd_client_new(NBDExport *exp,
|
void nbd_client_new(NBDExport *exp,
|
||||||
|
@ -104,9 +104,10 @@ static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
|
|||||||
return nbd_wr_syncv(ioc, &iov, 1, size, true);
|
return nbd_wr_syncv(ioc, &iov, 1, size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline ssize_t write_sync(QIOChannel *ioc, void *buffer, size_t size)
|
static inline ssize_t write_sync(QIOChannel *ioc, const void *buffer,
|
||||||
|
size_t size)
|
||||||
{
|
{
|
||||||
struct iovec iov = { .iov_base = buffer, .iov_len = size };
|
struct iovec iov = { .iov_base = (void *) buffer, .iov_len = size };
|
||||||
|
|
||||||
return nbd_wr_syncv(ioc, &iov, 1, size, false);
|
return nbd_wr_syncv(ioc, &iov, 1, size, false);
|
||||||
}
|
}
|
||||||
|
34
nbd/server.c
34
nbd/server.c
@ -61,6 +61,7 @@ struct NBDExport {
|
|||||||
|
|
||||||
BlockBackend *blk;
|
BlockBackend *blk;
|
||||||
char *name;
|
char *name;
|
||||||
|
char *description;
|
||||||
off_t dev_offset;
|
off_t dev_offset;
|
||||||
off_t size;
|
off_t size;
|
||||||
uint16_t nbdflags;
|
uint16_t nbdflags;
|
||||||
@ -129,7 +130,8 @@ static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t nbd_negotiate_write(QIOChannel *ioc, void *buffer, size_t size)
|
static ssize_t nbd_negotiate_write(QIOChannel *ioc, const void *buffer,
|
||||||
|
size_t size)
|
||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
guint watch;
|
guint watch;
|
||||||
@ -225,11 +227,15 @@ static int nbd_negotiate_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt)
|
|||||||
|
|
||||||
static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
|
static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
|
||||||
{
|
{
|
||||||
uint64_t magic, name_len;
|
uint64_t magic;
|
||||||
|
size_t name_len, desc_len;
|
||||||
uint32_t opt, type, len;
|
uint32_t opt, type, len;
|
||||||
|
const char *name = exp->name ? exp->name : "";
|
||||||
|
const char *desc = exp->description ? exp->description : "";
|
||||||
|
|
||||||
TRACE("Advertising export name '%s'", exp->name ? exp->name : "");
|
TRACE("Advertising export name '%s' description '%s'", name, desc);
|
||||||
name_len = strlen(exp->name);
|
name_len = strlen(name);
|
||||||
|
desc_len = strlen(desc);
|
||||||
magic = cpu_to_be64(NBD_REP_MAGIC);
|
magic = cpu_to_be64(NBD_REP_MAGIC);
|
||||||
if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
|
if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
|
||||||
LOG("write failed (magic)");
|
LOG("write failed (magic)");
|
||||||
@ -245,18 +251,22 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
|
|||||||
LOG("write failed (reply type)");
|
LOG("write failed (reply type)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
len = cpu_to_be32(name_len + sizeof(len));
|
len = cpu_to_be32(name_len + desc_len + sizeof(len));
|
||||||
if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
|
if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
|
||||||
LOG("write failed (length)");
|
LOG("write failed (length)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
len = cpu_to_be32(name_len);
|
len = cpu_to_be32(name_len);
|
||||||
if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
|
if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
|
||||||
LOG("write failed (length)");
|
LOG("write failed (name length)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (nbd_negotiate_write(ioc, exp->name, name_len) != name_len) {
|
if (nbd_negotiate_write(ioc, name, name_len) != name_len) {
|
||||||
LOG("write failed (buffer)");
|
LOG("write failed (name buffer)");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (nbd_negotiate_write(ioc, desc, desc_len) != desc_len) {
|
||||||
|
LOG("write failed (description buffer)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -894,6 +904,12 @@ void nbd_export_set_name(NBDExport *exp, const char *name)
|
|||||||
nbd_export_put(exp);
|
nbd_export_put(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nbd_export_set_description(NBDExport *exp, const char *description)
|
||||||
|
{
|
||||||
|
g_free(exp->description);
|
||||||
|
exp->description = g_strdup(description);
|
||||||
|
}
|
||||||
|
|
||||||
void nbd_export_close(NBDExport *exp)
|
void nbd_export_close(NBDExport *exp)
|
||||||
{
|
{
|
||||||
NBDClient *client, *next;
|
NBDClient *client, *next;
|
||||||
@ -903,6 +919,7 @@ void nbd_export_close(NBDExport *exp)
|
|||||||
client_close(client);
|
client_close(client);
|
||||||
}
|
}
|
||||||
nbd_export_set_name(exp, NULL);
|
nbd_export_set_name(exp, NULL);
|
||||||
|
nbd_export_set_description(exp, NULL);
|
||||||
nbd_export_put(exp);
|
nbd_export_put(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -921,6 +938,7 @@ void nbd_export_put(NBDExport *exp)
|
|||||||
|
|
||||||
if (--exp->refcount == 0) {
|
if (--exp->refcount == 0) {
|
||||||
assert(exp->name == NULL);
|
assert(exp->name == NULL);
|
||||||
|
assert(exp->description == NULL);
|
||||||
|
|
||||||
if (exp->close) {
|
if (exp->close) {
|
||||||
exp->close(exp);
|
exp->close(exp);
|
||||||
|
12
qemu-nbd.c
12
qemu-nbd.c
@ -83,6 +83,7 @@ static void usage(const char *name)
|
|||||||
" -t, --persistent don't exit on the last connection\n"
|
" -t, --persistent don't exit on the last connection\n"
|
||||||
" -v, --verbose display extra debugging information\n"
|
" -v, --verbose display extra debugging information\n"
|
||||||
" -x, --export-name=NAME expose export by name\n"
|
" -x, --export-name=NAME expose export by name\n"
|
||||||
|
" -D, --description=TEXT with -x, also export a human-readable description\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Exposing part of the image:\n"
|
"Exposing part of the image:\n"
|
||||||
" -o, --offset=OFFSET offset into the image\n"
|
" -o, --offset=OFFSET offset into the image\n"
|
||||||
@ -477,7 +478,7 @@ int main(int argc, char **argv)
|
|||||||
off_t fd_size;
|
off_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:";
|
const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:";
|
||||||
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' },
|
||||||
@ -503,6 +504,7 @@ int main(int argc, char **argv)
|
|||||||
{ "verbose", no_argument, NULL, 'v' },
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
{ "object", required_argument, NULL, QEMU_NBD_OPT_OBJECT },
|
{ "object", required_argument, NULL, QEMU_NBD_OPT_OBJECT },
|
||||||
{ "export-name", required_argument, NULL, 'x' },
|
{ "export-name", required_argument, NULL, 'x' },
|
||||||
|
{ "description", required_argument, NULL, 'D' },
|
||||||
{ "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS },
|
{ "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS },
|
||||||
{ "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS },
|
{ "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS },
|
||||||
{ "trace", required_argument, NULL, 'T' },
|
{ "trace", required_argument, NULL, 'T' },
|
||||||
@ -524,6 +526,7 @@ int main(int argc, char **argv)
|
|||||||
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 = NULL;
|
const char *export_name = NULL;
|
||||||
|
const char *export_description = NULL;
|
||||||
const char *tlscredsid = NULL;
|
const char *tlscredsid = NULL;
|
||||||
bool imageOpts = false;
|
bool imageOpts = false;
|
||||||
bool writethrough = true;
|
bool writethrough = true;
|
||||||
@ -689,6 +692,9 @@ int main(int argc, char **argv)
|
|||||||
case 'x':
|
case 'x':
|
||||||
export_name = optarg;
|
export_name = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'D':
|
||||||
|
export_description = optarg;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
break;
|
break;
|
||||||
@ -937,7 +943,11 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
if (export_name) {
|
if (export_name) {
|
||||||
nbd_export_set_name(exp, export_name);
|
nbd_export_set_name(exp, export_name);
|
||||||
|
nbd_export_set_description(exp, export_description);
|
||||||
newproto = true;
|
newproto = true;
|
||||||
|
} else if (export_description) {
|
||||||
|
error_report("Export description requires an export name");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
server_ioc = qio_channel_socket_new();
|
server_ioc = qio_channel_socket_new();
|
||||||
|
@ -79,9 +79,12 @@ Disconnect the device @var{dev}
|
|||||||
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})
|
||||||
@item -t, --persistent
|
@item -t, --persistent
|
||||||
Don't exit on the last connection
|
Don't exit on the last connection
|
||||||
@item -x NAME, --export-name=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. This switches the server to use
|
||||||
the new style NBD protocol negotiation
|
the new style NBD protocol negotiation
|
||||||
|
@item -D, --description=@var{description}
|
||||||
|
Set the NBD volume export description, as a human-readable
|
||||||
|
string. Requires the use of @option{-x}
|
||||||
@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
|
||||||
|
Loading…
Reference in New Issue
Block a user