*/
#include "qemu-common.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
+#include "block/block_int.h"
#include "block/nbd.h"
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
#include "block/snapshot.h"
+#include "qapi/util.h"
#include <stdarg.h>
#include <stdio.h>
#include <libgen.h>
#include <pthread.h>
-#define SOCKET_PATH "/var/lock/qemu-nbd-%s"
-#define QEMU_NBD_OPT_CACHE 1
-#define QEMU_NBD_OPT_AIO 2
-#define QEMU_NBD_OPT_DISCARD 3
+#define SOCKET_PATH "/var/lock/qemu-nbd-%s"
+#define QEMU_NBD_OPT_CACHE 1
+#define QEMU_NBD_OPT_AIO 2
+#define QEMU_NBD_OPT_DISCARD 3
+#define QEMU_NBD_OPT_DETECT_ZEROES 4
static NBDExport *exp;
static int verbose;
"Usage: %s [OPTIONS] FILE\n"
"QEMU Disk Network Block Device Server\n"
"\n"
-" -h, --help display this help and exit\n"
-" -V, --version output version information and exit\n"
+" -h, --help display this help and exit\n"
+" -V, --version output version information and exit\n"
"\n"
"Connection properties:\n"
-" -p, --port=PORT port to listen on (default `%d')\n"
-" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n"
-" -k, --socket=PATH path to the unix socket\n"
-" (default '"SOCKET_PATH"')\n"
-" -e, --shared=NUM device can be shared by NUM clients (default '1')\n"
-" -t, --persistent don't exit on the last connection\n"
-" -v, --verbose display extra debugging information\n"
+" -p, --port=PORT port to listen on (default `%d')\n"
+" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n"
+" -k, --socket=PATH path to the unix socket\n"
+" (default '"SOCKET_PATH"')\n"
+" -e, --shared=NUM device can be shared by NUM clients (default '1')\n"
+" -t, --persistent don't exit on the last connection\n"
+" -v, --verbose display extra debugging information\n"
"\n"
"Exposing part of the image:\n"
-" -o, --offset=OFFSET offset into the image\n"
-" -P, --partition=NUM only expose partition NUM\n"
+" -o, --offset=OFFSET offset into the image\n"
+" -P, --partition=NUM only expose partition NUM\n"
"\n"
#ifdef __linux__
"Kernel NBD client support:\n"
-" -c, --connect=DEV connect FILE to the local NBD device DEV\n"
-" -d, --disconnect disconnect the specified device\n"
+" -c, --connect=DEV connect FILE to the local NBD device DEV\n"
+" -d, --disconnect disconnect the specified device\n"
"\n"
#endif
"\n"
"Block device options:\n"
-" -f, --format=FORMAT set image format (raw, qcow2, ...)\n"
-" -r, --read-only export read-only\n"
-" -s, --snapshot use FILE as an external snapshot, create a temporary\n"
-" file with backing_file=FILE, redirect the write to\n"
-" the temporary one\n"
+" -f, --format=FORMAT set image format (raw, qcow2, ...)\n"
+" -r, --read-only export read-only\n"
+" -s, --snapshot use FILE as an external snapshot, create a temporary\n"
+" file with backing_file=FILE, redirect the write to\n"
+" the temporary one\n"
" -l, --load-snapshot=SNAPSHOT_PARAM\n"
-" load an internal snapshot inside FILE and export it\n"
-" as an read-only device, SNAPSHOT_PARAM format is\n"
-" 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
-" '[ID_OR_NAME]'\n"
-" -n, --nocache disable host cache\n"
-" --cache=MODE set cache mode (none, writeback, ...)\n"
+" load an internal snapshot inside FILE and export it\n"
+" as an read-only device, SNAPSHOT_PARAM format is\n"
+" 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
+" '[ID_OR_NAME]'\n"
+" -n, --nocache disable host cache\n"
+" --cache=MODE set cache mode (none, writeback, ...)\n"
#ifdef CONFIG_LINUX_AIO
-" --aio=MODE set AIO mode (native or threads)\n"
+" --aio=MODE set AIO mode (native or threads)\n"
#endif
+" --discard=MODE set discard mode (ignore, unmap)\n"
+" --detect-zeroes=MODE set detect-zeroes mode (off, on, discard)\n"
"\n"
, name, NBD_DEFAULT_PORT, "DEVICE");
r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24;
}
-static int find_partition(BlockDriverState *bs, int partition,
+static int find_partition(BlockBackend *blk, int partition,
off_t *offset, off_t *size)
{
struct partition_record mbr[4];
int ext_partnum = 4;
int ret;
- if ((ret = bdrv_read(bs, 0, data, 1)) < 0) {
+ if ((ret = blk_read(blk, 0, data, 1)) < 0) {
errno = -ret;
err(EXIT_FAILURE, "error while reading");
}
uint8_t data1[512];
int j;
- if ((ret = bdrv_read(bs, mbr[i].start_sector_abs, data1, 1)) < 0) {
+ if ((ret = blk_read(blk, mbr[i].start_sector_abs, data1, 1)) < 0) {
errno = -ret;
err(EXIT_FAILURE, "error while reading");
}
int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
if (local_err != NULL) {
- qerror_report_err(local_err);
+ error_report("%s", error_get_pretty(local_err));
error_free(local_err);
}
return fd;
int fd = unix_listen(path, NULL, 0, &local_err);
if (local_err != NULL) {
- qerror_report_err(local_err);
+ error_report("%s", error_get_pretty(local_err));
error_free(local_err);
}
return fd;
int fd = unix_connect(path, &local_err);
if (local_err != NULL) {
- qerror_report_err(local_err);
+ error_report("%s", error_get_pretty(local_err));
error_free(local_err);
}
return fd;
int fd, sock;
int ret;
pthread_t show_parts_thread;
+ Error *local_error = NULL;
sock = unix_socket_outgoing(sockpath);
if (sock < 0) {
}
ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
- &size, &blocksize);
+ &size, &blocksize, &local_error);
if (ret < 0) {
+ if (local_error) {
+ fprintf(stderr, "%s\n", error_get_pretty(local_error));
+ error_free(local_error);
+ }
goto out_socket;
}
if (nbd_client_new(exp, fd, nbd_client_closed)) {
nb_fds++;
} else {
+ shutdown(fd, 2);
close(fd);
}
}
int main(int argc, char **argv)
{
+ BlockBackend *blk;
BlockDriverState *bs;
- BlockDriver *drv;
off_t dev_offset = 0;
uint32_t nbdflags = 0;
bool disconnect = false;
{ "aio", 1, NULL, QEMU_NBD_OPT_AIO },
#endif
{ "discard", 1, NULL, QEMU_NBD_OPT_DISCARD },
+ { "detect-zeroes", 1, NULL, QEMU_NBD_OPT_DETECT_ZEROES },
{ "shared", 1, NULL, 'e' },
{ "format", 1, NULL, 'f' },
{ "persistent", 0, NULL, 't' },
char *end;
int flags = BDRV_O_RDWR;
int partition = -1;
- int ret;
+ int ret = 0;
int fd;
bool seen_cache = false;
bool seen_discard = false;
pthread_t client_thread;
const char *fmt = NULL;
Error *local_err = NULL;
+ BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
+ QDict *options = 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.
errx(EXIT_FAILURE, "Invalid discard mode `%s'", optarg);
}
break;
+ case QEMU_NBD_OPT_DETECT_ZEROES:
+ detect_zeroes =
+ qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
+ optarg,
+ BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
+ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
+ &local_err);
+ if (local_err) {
+ errx(EXIT_FAILURE, "Failed to parse detect_zeroes mode: %s",
+ error_get_pretty(local_err));
+ }
+ if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
+ !(flags & BDRV_O_UNMAP)) {
+ errx(EXIT_FAILURE, "setting detect-zeroes to unmap is not allowed "
+ "without setting discard operation to unmap");
+ }
+ break;
case 'b':
bindto = optarg;
break;
break;
case 'P':
partition = strtol(optarg, &end, 0);
- if (*end)
+ if (*end) {
errx(EXIT_FAILURE, "Invalid partition `%s'", optarg);
- if (partition < 1 || partition > 8)
+ }
+ if (partition < 1 || partition > 8) {
errx(EXIT_FAILURE, "Invalid partition %d", partition);
+ }
break;
case 'k':
sockpath = optarg;
- if (sockpath[0] != '/')
+ if (sockpath[0] != '/') {
errx(EXIT_FAILURE, "socket path must be absolute\n");
+ }
break;
case 'd':
disconnect = true;
case 'f':
fmt = optarg;
break;
- case 't':
- persistent = 1;
- break;
+ case 't':
+ persistent = 1;
+ break;
case 'v':
verbose = 1;
break;
printf("%s disconnected\n", argv[optind]);
- return 0;
+ return 0;
}
if (device && !verbose) {
snprintf(sockpath, 128, SOCKET_PATH, basename(device));
}
- qemu_init_main_loop();
+ if (qemu_init_main_loop(&local_err)) {
+ error_report("%s", error_get_pretty(local_err));
+ error_free(local_err);
+ exit(EXIT_FAILURE);
+ }
bdrv_init();
atexit(bdrv_close_all);
if (fmt) {
- drv = bdrv_find_format(fmt);
- if (!drv) {
- errx(EXIT_FAILURE, "Unknown file format '%s'", fmt);
- }
- } else {
- drv = NULL;
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str(fmt));
}
- bs = bdrv_new("hda", &error_abort);
-
srcpath = argv[optind];
- ret = bdrv_open(&bs, srcpath, NULL, NULL, flags, drv, &local_err);
- if (ret < 0) {
- errno = -ret;
- err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind],
- error_get_pretty(local_err));
+ blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err);
+ if (!blk) {
+ errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", argv[optind],
+ error_get_pretty(local_err));
}
+ bs = blk_bs(blk);
if (sn_opts) {
ret = bdrv_snapshot_load_tmp(bs,
error_get_pretty(local_err));
}
- fd_size = bdrv_getlength(bs);
+ bs->detect_zeroes = detect_zeroes;
+ fd_size = blk_getlength(blk);
if (partition != -1) {
- ret = find_partition(bs, partition, &dev_offset, &fd_size);
+ ret = find_partition(blk, partition, &dev_offset, &fd_size);
if (ret < 0) {
errno = -ret;
err(EXIT_FAILURE, "Could not find partition %d", partition);
}
}
- exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed);
+ exp = nbd_export_new(blk, dev_offset, fd_size, nbdflags, nbd_export_closed);
if (sockpath) {
fd = unix_socket_incoming(sockpath);
}
} while (state != TERMINATED);
- bdrv_close(bs);
+ blk_unref(blk);
if (sockpath) {
unlink(sockpath);
}
- if (sn_opts) {
- qemu_opts_del(sn_opts);
- }
+ qemu_opts_del(sn_opts);
if (device) {
void *ret;