]> git.proxmox.com Git - mirror_qemu.git/blobdiff - qemu-nbd.c
iotests, parallels: test for newly created parallels image via qemu-img
[mirror_qemu.git] / qemu-nbd.c
index 899e67cfd711db3e9a6666ef7c558ce7554a31c6..7e690fff7e9e5455d9bbdbce31199b5a39029faf 100644 (file)
  */
 
 #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;
@@ -57,45 +60,47 @@ static void usage(const char *name)
 "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"
 "Report bugs to <qemu-devel@nongnu.org>\n"
     , name, NBD_DEFAULT_PORT, "DEVICE");
@@ -137,11 +142,12 @@ static void read_partition(uint8_t *p, struct partition_record *r)
     r->end_head = p[5];
     r->end_cylinder = p[7] | ((p[6] << 2) & 0x300);
     r->end_sector = p[6] & 0x3f;
-    r->start_sector_abs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24;
-    r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24;
+
+    r->start_sector_abs = le32_to_cpup((uint32_t *)(p +  8));
+    r->nb_sectors_abs   = le32_to_cpup((uint32_t *)(p + 12));
 }
 
-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];
@@ -150,7 +156,7 @@ static int find_partition(BlockDriverState *bs, int partition,
     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");
     }
@@ -162,23 +168,25 @@ static int find_partition(BlockDriverState *bs, int partition,
     for (i = 0; i < 4; i++) {
         read_partition(&data[446 + 16 * i], &mbr[i]);
 
-        if (!mbr[i].nb_sectors_abs)
+        if (!mbr[i].system || !mbr[i].nb_sectors_abs) {
             continue;
+        }
 
         if (mbr[i].system == 0xF || mbr[i].system == 0x5) {
             struct partition_record ext[4];
             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");
             }
 
             for (j = 0; j < 4; j++) {
                 read_partition(&data1[446 + 16 * j], &ext[j]);
-                if (!ext[j].nb_sectors_abs)
+                if (!ext[j].system || !ext[j].nb_sectors_abs) {
                     continue;
+                }
 
                 if ((ext_partnum + j + 1) == partition) {
                     *offset = (uint64_t)ext[j].start_sector_abs << 9;
@@ -223,8 +231,7 @@ static int tcp_socket_incoming(const char *address, uint16_t port)
     int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
 
     if (local_err != NULL) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_report_err(local_err);
     }
     return fd;
 }
@@ -235,8 +242,7 @@ static int unix_socket_incoming(const char *path)
     int fd = unix_listen(path, NULL, 0, &local_err);
 
     if (local_err != NULL) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_report_err(local_err);
     }
     return fd;
 }
@@ -247,8 +253,7 @@ static int unix_socket_outgoing(const char *path)
     int fd = unix_connect(path, &local_err);
 
     if (local_err != NULL) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_report_err(local_err);
     }
     return fd;
 }
@@ -274,11 +279,11 @@ static void *nbd_client_thread(void *arg)
 {
     char *device = arg;
     off_t size;
-    size_t blocksize;
     uint32_t nbdflags;
     int fd, sock;
     int ret;
     pthread_t show_parts_thread;
+    Error *local_error = NULL;
 
     sock = unix_socket_outgoing(sockpath);
     if (sock < 0) {
@@ -286,19 +291,23 @@ static void *nbd_client_thread(void *arg)
     }
 
     ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
-                                &size, &blocksize);
+                                &size, &local_error);
     if (ret < 0) {
+        if (local_error) {
+            fprintf(stderr, "%s\n", error_get_pretty(local_error));
+            error_free(local_error);
+        }
         goto out_socket;
     }
 
     fd = open(device, O_RDWR);
     if (fd < 0) {
         /* Linux-only, we can use %m in printf.  */
-        fprintf(stderr, "Failed to open %s: %m", device);
+        fprintf(stderr, "Failed to open %s: %m\n", device);
         goto out_socket;
     }
 
-    ret = nbd_init(fd, sock, nbdflags, size, blocksize);
+    ret = nbd_init(fd, sock, nbdflags, size);
     if (ret < 0) {
         goto out_fd;
     }
@@ -369,15 +378,18 @@ static void nbd_accept(void *opaque)
         return;
     }
 
-    if (fd >= 0 && nbd_client_new(exp, fd, nbd_client_closed)) {
+    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;
@@ -407,6 +419,7 @@ int main(int argc, char **argv)
         { "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' },
@@ -419,7 +432,7 @@ int main(int argc, char **argv)
     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;
@@ -429,6 +442,8 @@ int main(int argc, char **argv)
     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.
@@ -480,6 +495,23 @@ int main(int argc, char **argv)
                 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;
@@ -519,15 +551,18 @@ int main(int argc, char **argv)
             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;
@@ -547,9 +582,9 @@ int main(int argc, char **argv)
         case 'f':
             fmt = optarg;
             break;
-       case 't':
-           persistent = 1;
-           break;
+        case 't':
+            persistent = 1;
+            break;
         case 'v':
             verbose = 1;
             break;
@@ -584,7 +619,7 @@ int main(int argc, char **argv)
 
         printf("%s disconnected\n", argv[optind]);
 
-       return 0;
+        return 0;
     }
 
     if (device && !verbose) {
@@ -600,7 +635,9 @@ int main(int argc, char **argv)
          * print errors and exit with the proper status code.
          */
         pid = fork();
-        if (pid == 0) {
+        if (pid < 0) {
+            err(EXIT_FAILURE, "Failed to fork");
+        } else if (pid == 0) {
             close(stderr_fd[0]);
             ret = qemu_daemon(1, 0);
 
@@ -644,27 +681,25 @@ int main(int argc, char **argv)
         snprintf(sockpath, 128, SOCKET_PATH, basename(device));
     }
 
-    qemu_init_main_loop();
+    if (qemu_init_main_loop(&local_err)) {
+        error_report_err(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");
     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,
@@ -682,17 +717,26 @@ int main(int argc, char **argv)
             error_get_pretty(local_err));
     }
 
-    fd_size = bdrv_getlength(bs);
+    bs->detect_zeroes = detect_zeroes;
+    fd_size = blk_getlength(blk);
+    if (fd_size < 0) {
+        errx(EXIT_FAILURE, "Failed to determine the image length: %s",
+             strerror(-fd_size));
+    }
 
     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,
+                         &local_err);
+    if (!exp) {
+        errx(EXIT_FAILURE, "%s", error_get_pretty(local_err));
+    }
 
     if (sockpath) {
         fd = unix_socket_incoming(sockpath);
@@ -737,14 +781,12 @@ int main(int argc, char **argv)
         }
     } 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;