diff options
author | Peter Maydell | 2019-01-22 18:56:21 +0100 |
---|---|---|
committer | Peter Maydell | 2019-01-22 18:56:21 +0100 |
commit | 952bc8b3c2cbba78261923a1e8ca55cda261dee9 (patch) | |
tree | d7994015b039f0c971109053e29992d459958b14 /qemu-nbd.c | |
parent | Merge remote-tracking branch 'remotes/huth-gitlab/tags/pull-request-2019-01-2... (diff) | |
parent | iotests: Enhance 223, 233 to cover 'qemu-nbd --list' (diff) | |
download | qemu-952bc8b3c2cbba78261923a1e8ca55cda261dee9.tar.gz qemu-952bc8b3c2cbba78261923a1e8ca55cda261dee9.tar.xz qemu-952bc8b3c2cbba78261923a1e8ca55cda261dee9.zip |
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>
Diffstat (limited to 'qemu-nbd.c')
-rw-r--r-- | qemu-nbd.c | 224 |
1 files changed, 179 insertions, 45 deletions
diff --git a/qemu-nbd.c b/qemu-nbd.c index 51b55f2e06..1f7b2a03f5 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -76,7 +76,8 @@ static void usage(const char *name) { (printf) ( "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" " -h, --help display this help 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" "\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" " passwords and/or encryption keys\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" "\n" QEMU_HELP_BOTTOM "\n" - , name, NBD_DEFAULT_PORT, "DEVICE"); + , name, name, NBD_DEFAULT_PORT, "DEVICE"); } 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, - off_t *offset, off_t *size) + uint64_t *offset, uint64_t *size) { struct partition_record mbr[4]; 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 static void *show_parts(void *arg) { @@ -264,7 +351,7 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { char *device = arg; - NBDExportInfo info = { .request_sizes = false, }; + NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") }; QIOChannelSocket *sioc; int fd; int ret; @@ -279,7 +366,7 @@ static void *nbd_client_thread(void *arg) goto out; } - ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, NULL, NULL, &info, &local_error); if (ret < 0) { if (local_error) { @@ -318,6 +405,7 @@ static void *nbd_client_thread(void *arg) } close(fd); object_unref(OBJECT(sioc)); + g_free(info.name); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; @@ -326,6 +414,7 @@ out_fd: out_socket: object_unref(OBJECT(sioc)); out: + g_free(info.name); kill(getpid(), SIGTERM); 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; QCryptoTLSCreds *creds; @@ -443,10 +533,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) return NULL; } - if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { - error_setg(errp, - "Expecting TLS credentials with a server endpoint"); - return NULL; + if (list) { + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, + "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); 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, const char *sockpath, const char *address, - const char *port) + const char *port, + bool list) { if (device != NULL) { 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"; } + if (list) { + return "List mode is incompatible with socket activation"; + } + return NULL; } @@ -500,17 +603,17 @@ int main(int argc, char **argv) { BlockBackend *blk; BlockDriverState *bs; - off_t dev_offset = 0; + uint64_t dev_offset = 0; uint16_t nbdflags = 0; bool disconnect = false; const char *bindto = NULL; const char *port = NULL; char *sockpath = NULL; char *device = NULL; - off_t fd_size; + int64_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:x:T:D:B:"; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L"; struct option lopt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -523,6 +626,7 @@ int main(int argc, char **argv) { "bitmap", required_argument, NULL, 'B' }, { "connect", required_argument, NULL, 'c' }, { "disconnect", no_argument, NULL, 'd' }, + { "list", no_argument, NULL, 'L' }, { "snapshot", no_argument, NULL, 's' }, { "load-snapshot", required_argument, NULL, 'l' }, { "nocache", no_argument, NULL, 'n' }, @@ -546,9 +650,8 @@ int main(int argc, char **argv) }; int ch; int opt_ind = 0; - char *end; int flags = BDRV_O_RDWR; - int partition = -1; + int partition = 0; int ret = 0; bool seen_cache = false; bool seen_discard = false; @@ -558,7 +661,7 @@ 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 = ""; /* Default export name */ + const char *export_name = NULL; /* defaults to "" later for server mode */ const char *export_description = NULL; const char *bitmap = NULL; const char *tlscredsid = NULL; @@ -566,6 +669,7 @@ int main(int argc, char **argv) bool writethrough = true; char *trace_file = NULL; bool fork_process = false; + bool list = false; int old_stderr = -1; unsigned socket_activation; @@ -660,13 +764,8 @@ int main(int argc, char **argv) port = optarg; break; case 'o': - dev_offset = strtoll (optarg, &end, 0); - if (*end) { - error_report("Invalid offset `%s'", optarg); - exit(EXIT_FAILURE); - } - if (dev_offset < 0) { - error_report("Offset must be positive `%s'", optarg); + if (qemu_strtou64(optarg, NULL, 0, &dev_offset) < 0) { + error_report("Invalid offset '%s'", optarg); exit(EXIT_FAILURE); } break; @@ -688,13 +787,9 @@ int main(int argc, char **argv) flags &= ~BDRV_O_RDWR; break; case 'P': - partition = strtol(optarg, &end, 0); - if (*end) { - error_report("Invalid partition `%s'", optarg); - exit(EXIT_FAILURE); - } - if (partition < 1 || partition > 8) { - error_report("Invalid partition %d", partition); + if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 || + partition < 1 || partition > 8) { + error_report("Invalid partition '%s'", optarg); exit(EXIT_FAILURE); } break; @@ -715,15 +810,11 @@ int main(int argc, char **argv) device = optarg; break; case 'e': - shared = strtol(optarg, &end, 0); - if (*end) { + if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 || + shared < 1) { error_report("Invalid shared device number '%s'", optarg); exit(EXIT_FAILURE); } - if (shared < 1) { - error_report("Shared device number must be greater than 0"); - exit(EXIT_FAILURE); - } break; case 'f': fmt = optarg; @@ -772,13 +863,33 @@ int main(int argc, char **argv) case QEMU_NBD_OPT_FORK: fork_process = true; 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_printf("Try `%s --help' for more information.\n", argv[0]); exit(EXIT_FAILURE); + } else if (!export_name) { + export_name = ""; } qemu_opts_foreach(&qemu_object_opts, @@ -797,7 +908,8 @@ int main(int argc, char **argv) } else { /* Using socket activation - check user didn't use -p etc. */ const char *err_msg = socket_activation_validate_opts(device, sockpath, - bindto, port); + bindto, port, + list); if (err_msg != NULL) { error_report("%s", err_msg); exit(EXIT_FAILURE); @@ -820,7 +932,7 @@ int main(int argc, char **argv) error_report("TLS is not supported with a host device"); exit(EXIT_FAILURE); } - tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); + tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err); if (local_err) { error_report("Failed to get TLS creds %s", 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 (disconnect || device) { error_report("Kernel /dev/nbdN support not available"); @@ -1005,20 +1122,37 @@ int main(int argc, char **argv) } if (dev_offset >= fd_size) { - error_report("Offset (%lld) has to be smaller than the image size " - "(%lld)", - (long long int)dev_offset, (long long int)fd_size); + error_report("Offset (%" PRIu64 ") has to be smaller than the image " + "size (%" PRId64 ")", dev_offset, fd_size); exit(EXIT_FAILURE); } fd_size -= dev_offset; - if (partition != -1) { - ret = find_partition(blk, partition, &dev_offset, &fd_size); + if (partition) { + 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) { error_report("Could not find partition %d: %s", partition, strerror(-ret)); 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, |