Hi tech@,

there is one more diff in our qcow2 queue which lets you convert disk
images into anoter format.  You create a new image from an input file:

- The "standard" use case:

        vmctl create foo.qcow2 -i foo.raw

- The other way around:

        vmctl create foo.raw -i foo.qcow2

- Using the explicit format:

        vmctl create foo.raw -i qcow2:foo.qc2

- You can also increase the disk size of an existing image :

        vmctl create foo.qcow2 -s 10G
        ...
        vmctl create bar.qcow2 -i foo.qcow2 -s 20G

- Or flatten a qcow2 image that has a base image, converting it back
into a single qcow2 or raw image:

        vmctl create foo.qcow2 -b foo-base.qcow2
        ...
        vmctl create bar.qcow2 -i foo.qcow2

- Or convert a flat raw image back to a sparse file (that doesn't use
disk space for trailing zeroes).

        vmctl create bar.raw -i foo.raw

This was possible by re-using Ori's qcow2 code from vmd in vmctl.

Reyk

Index: usr.sbin/vmd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/config.c,v
retrieving revision 1.51
diff -u -p -u -p -r1.51 config.c
--- usr.sbin/vmd/config.c       8 Oct 2018 16:32:01 -0000       1.51
+++ usr.sbin/vmd/config.c       8 Oct 2018 17:39:36 -0000
@@ -35,7 +35,6 @@
 #include <util.h>
 #include <errno.h>
 #include <imsg.h>
-#include <libgen.h>
 
 #include "proc.h"
 #include "vmd.h"
@@ -191,7 +190,6 @@ config_setvm(struct privsep *ps, struct 
        char                     ifname[IF_NAMESIZE], *s;
        char                     path[PATH_MAX];
        char                     base[PATH_MAX];
-       char                     expanded[PATH_MAX];
        unsigned int             unit;
 
        errno = 0;
@@ -323,7 +321,7 @@ config_setvm(struct privsep *ps, struct 
                        oflags = O_RDONLY|O_NONBLOCK;
                        aflags = R_OK;
                        n = virtio_get_base(diskfds[i][j], base, sizeof base,
-                           vmc->vmc_disktypes[i]);
+                           vmc->vmc_disktypes[i], path);
                        if (n == 0)
                                break;
                        if (n == -1) {
@@ -331,30 +329,6 @@ config_setvm(struct privsep *ps, struct 
                                    "base %s for disk %s", vcp->vcp_name,
                                    base, vcp->vcp_disks[i]);
                                goto fail;
-                       }
-                       /*
-                        * Relative paths should be interpreted relative
-                        * to the disk image, rather than relative to the
-                        * directory vmd happens to be running in, since
-                        * this is the only userful interpretation.
-                        */
-                       if (base[0] == '/') {
-                               if (realpath(base, path) == NULL) {
-                                       log_warn("unable to resolve %s", base);
-                                       goto fail;
-                               }
-                       } else {
-                               s = dirname(path);
-                               if (snprintf(expanded, sizeof(expanded),
-                                   "%s/%s", s, base) >= (int)sizeof(expanded)) 
{
-                                       log_warn("path too long: %s/%s",
-                                           s, base);
-                                       goto fail;
-                               }
-                               if (realpath(expanded, path) == NULL) {
-                                       log_warn("unable to resolve %s", base);
-                                       goto fail;
-                               }
                        }
                }
        }
Index: usr.sbin/vmd/vioqcow2.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vioqcow2.c,v
retrieving revision 1.8
diff -u -p -u -p -r1.8 vioqcow2.c
--- usr.sbin/vmd/vioqcow2.c     8 Oct 2018 16:32:01 -0000       1.8
+++ usr.sbin/vmd/vioqcow2.c     8 Oct 2018 17:39:37 -0000
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <assert.h>
+#include <libgen.h>
 #include <err.h>
 
 #include "vmd.h"
@@ -137,11 +138,13 @@ virtio_init_qcow2(struct virtio_backing 
 }
 
 ssize_t
-virtio_qcow2_get_base(int fd, char *path, size_t npath)
+virtio_qcow2_get_base(int fd, char *path, size_t npath, const char *dpath)
 {
+       char expanded[PATH_MAX];
        struct qcheader header;
        uint64_t backingoff;
        uint32_t backingsz;
+       char *s = NULL;
 
        if (pread(fd, &header, sizeof(header), 0) != sizeof(header)) {
                log_warnx("%s: short read on header", __func__);
@@ -153,19 +156,47 @@ virtio_qcow2_get_base(int fd, char *path
        }
        backingoff = be64toh(header.backingoff);
        backingsz = be32toh(header.backingsz);
-       if (backingsz != 0) {
-               if (backingsz >= npath - 1) {
-                       log_warn("%s: snapshot path too long", __func__);
+       if (backingsz == 0)
+               return 0;
+
+       if (backingsz >= npath - 1) {
+               log_warn("%s: snapshot path too long", __func__);
+               return -1;
+       }
+       if (pread(fd, path, backingsz, backingoff) != backingsz) {
+               log_warnx("%s: could not read snapshot base name",
+                   __func__);
+               return -1;
+       }
+       path[backingsz] = '\0';
+
+       /*
+        * Relative paths should be interpreted relative to the disk image,
+        * rather than relative to the directory vmd happens to be running in,
+        * since this is the only userful interpretation.
+        */
+       if (path[0] == '/') {
+               if (realpath(path, expanded) == NULL ||
+                   strlcpy(path, expanded, npath) >= npath) {
+                       log_warn("unable to resolve %s", path);
+                       return -1;
+               }
+       } else {
+               s = dirname(dpath);
+               if (snprintf(expanded, sizeof(expanded),
+                   "%s/%s", s, path) >= (int)sizeof(expanded)) {
+                       log_warn("path too long: %s/%s",
+                           s, path);
                        return -1;
                }
-               if (pread(fd, path, backingsz, backingoff) != backingsz) {
-                       log_warnx("%s: could not read snapshot base name",
-                           __func__);
+               if (npath < PATH_MAX ||
+                   realpath(expanded, path) == NULL) {
+                       log_warn("unable to resolve %s", path);
                        return -1;
                }
-               path[backingsz] = '\0';
        }
-       return backingsz;
+
+       return strlen(path);
 }
 
 static int
Index: usr.sbin/vmd/virtio.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.c,v
retrieving revision 1.72
diff -u -p -u -p -r1.72 virtio.c
--- usr.sbin/vmd/virtio.c       8 Oct 2018 16:32:01 -0000       1.72
+++ usr.sbin/vmd/virtio.c       8 Oct 2018 17:39:37 -0000
@@ -1746,13 +1746,13 @@ vmmci_io(int dir, uint16_t reg, uint32_t
 }
 
 int
-virtio_get_base(int fd, char *path, size_t npath ,int type)
+virtio_get_base(int fd, char *path, size_t npath, int type, const char *dpath)
 {
        switch (type) {
        case VMDF_RAW:
                return 0;
        case VMDF_QCOW2:
-               return virtio_qcow2_get_base(fd, path, npath);
+               return virtio_qcow2_get_base(fd, path, npath, dpath);
        }
        log_warnx("%s: invalid disk format", __func__);
        return -1;
Index: usr.sbin/vmd/virtio.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.h,v
retrieving revision 1.31
diff -u -p -u -p -r1.31 virtio.h
--- usr.sbin/vmd/virtio.h       8 Oct 2018 16:32:01 -0000       1.31
+++ usr.sbin/vmd/virtio.h       8 Oct 2018 17:39:37 -0000
@@ -271,7 +271,7 @@ void viornd_update_qs(void);
 void viornd_update_qa(void);
 int viornd_notifyq(void);
 
-ssize_t virtio_qcow2_get_base(int, char *, size_t);
+ssize_t virtio_qcow2_get_base(int, char *, size_t, const char *);
 int virtio_init_raw(struct virtio_backing *, off_t *, int*, size_t);
 int virtio_init_qcow2(struct virtio_backing *, off_t *, int*, size_t);
 
Index: usr.sbin/vmd/vmd.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.h,v
retrieving revision 1.82
diff -u -p -u -p -r1.82 vmd.h
--- usr.sbin/vmd/vmd.h  8 Oct 2018 16:32:01 -0000       1.82
+++ usr.sbin/vmd/vmd.h  8 Oct 2018 17:39:37 -0000
@@ -417,6 +417,6 @@ int  cmdline_symset(char *);
 int     host(const char *, struct address *);
 
 /* virtio.c */
-int     virtio_get_base(int, char *, size_t, int);
+int     virtio_get_base(int, char *, size_t, int, const char *);
 
 #endif /* VMD_H */
Index: usr.sbin/vmctl/Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/Makefile,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 Makefile
--- usr.sbin/vmctl/Makefile     15 Jul 2017 05:05:36 -0000      1.4
+++ usr.sbin/vmctl/Makefile     8 Oct 2018 17:39:37 -0000
@@ -6,14 +6,15 @@
 
 PROG=  vmctl
 SRCS=  vmctl.c main.c atomicio.c
+SRCS+= vioqcow2.c vioraw.c log.c
 CFLAGS+= -Wall
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
 CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
 CFLAGS+= -Wsign-compare
 CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../vmd
-LDADD+=        -lutil
-DPADD+= ${LIBUTIL}
+LDADD+=        -lutil -lpthread
+DPADD+= ${LIBUTIL} ${LIBPTHREAD}
 PATH+=../vmd
 
 .else
Index: usr.sbin/vmctl/main.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/main.c,v
retrieving revision 1.46
diff -u -p -u -p -r1.46 main.c
--- usr.sbin/vmctl/main.c       8 Oct 2018 16:32:01 -0000       1.46
+++ usr.sbin/vmctl/main.c       8 Oct 2018 17:39:37 -0000
@@ -30,12 +30,14 @@
 #include <stdint.h>
 #include <limits.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <util.h>
 #include <imsg.h>
 
 #include "vmd.h"
+#include "virtio.h"
 #include "proc.h"
 #include "vmctl.h"
 
@@ -52,6 +54,7 @@ __dead void    ctl_usage(struct ctl_comman
 int             vmm_action(struct parse_result *);
 
 int             ctl_console(struct parse_result *, int, char *[]);
+int             ctl_convert(const char *, const char *, int, size_t);
 int             ctl_create(struct parse_result *, int, char *[]);
 int             ctl_load(struct parse_result *, int, char *[]);
 int             ctl_log(struct parse_result *, int, char *[]);
@@ -68,7 +71,7 @@ int            ctl_receive(struct parse_result *,
 struct ctl_command ctl_commands[] = {
        { "console",    CMD_CONSOLE,    ctl_console,    "id" },
        { "create",     CMD_CREATE,     ctl_create,
-               "\"path\" [-s size] [-b base]", 1 },
+               "\"path\" [-b base] [-i disk] [-s size]", 1 },
        { "load",       CMD_LOAD,       ctl_load,       "\"path\"" },
        { "log",        CMD_LOG,        ctl_log,        "[verbose|brief]" },
        { "reload",     CMD_RELOAD,     ctl_reload,     "" },
@@ -92,7 +95,7 @@ usage(void)
        extern char     *__progname;
        int              i;
 
-       fprintf(stderr, "usage:\t%s command [arg ...]\n",
+       fprintf(stderr, "usage:\t%s [-v] command [arg ...]\n",
            __progname);
        for (i = 0; ctl_commands[i].name != NULL; i++) {
                fprintf(stderr, "\t%s %s %s\n", __progname,
@@ -106,7 +109,7 @@ ctl_usage(struct ctl_command *ctl)
 {
        extern char     *__progname;
 
-       fprintf(stderr, "usage:\t%s %s %s\n", __progname,
+       fprintf(stderr, "usage:\t%s [-v] %s %s\n", __progname,
            ctl->name, ctl->usage);
        exit(1);
 }
@@ -114,10 +117,13 @@ ctl_usage(struct ctl_command *ctl)
 int
 main(int argc, char *argv[])
 {
-       int      ch;
+       int      ch, verbose = 1;
 
-       while ((ch = getopt(argc, argv, "")) != -1) {
+       while ((ch = getopt(argc, argv, "v")) != -1) {
                switch (ch) {
+               case 'v':
+                       verbose = 2;
+                       break;
                default:
                        usage();
                        /* NOTREACHED */
@@ -131,6 +137,8 @@ main(int argc, char *argv[])
        if (argc < 1)
                usage();
 
+       log_init(verbose, LOG_DAEMON);
+
        return (parse(argc, argv));
 }
 
@@ -258,6 +266,8 @@ vmmaction(struct parse_result *res)
                break;
        case CMD_CREATE:
        case NONE:
+               /* The action is not expected here */
+               errx(1, "invalid action %u", res->action);
                break;
        }
 
@@ -540,12 +550,12 @@ int
 ctl_create(struct parse_result *res, int argc, char *argv[])
 {
        int              ch, ret, type;
-       const char      *disk, *format, *base;
+       const char      *disk, *format, *base, *input;
 
        if (argc < 2)
                ctl_usage(res->ctl);
 
-       base = NULL;
+       base = input = NULL;
        type = parse_disktype(argv[1], &disk);
 
        if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
@@ -556,22 +566,34 @@ ctl_create(struct parse_result *res, int
        argc--;
        argv++;
 
-       while ((ch = getopt(argc, argv, "s:b:")) != -1) {
+       while ((ch = getopt(argc, argv, "b:i:s:")) != -1) {
                switch (ch) {
-               case 's':
-                       if (parse_size(res, optarg, 0) != 0)
-                               errx(1, "invalid size: %s", optarg);
-                       break;
                case 'b':
                        base = optarg;
                        if (unveil(base, "r") == -1)
                                err(1, "unveil");
                        break;
+               case 'i':
+                       input = optarg;
+                       if (unveil(input, "r") == -1)
+                               err(1, "unveil");
+                       break;
+               case 's':
+                       if (parse_size(res, optarg, 0) != 0)
+                               errx(1, "invalid size: %s", optarg);
+                       break;
                default:
                        ctl_usage(res->ctl);
                        /* NOTREACHED */
                }
        }
+
+       if (input) {
+               if (base && input)
+                       errx(1, "conflicting -b and -i arguments");
+               return ctl_convert(input, disk, type, res->size);
+       }
+
        if (unveil(NULL, NULL))
                err(1, "unveil");
 
@@ -583,18 +605,142 @@ ctl_create(struct parse_result *res, int
                ctl_usage(res->ctl);
        }
 
-       if (type == VMDF_QCOW2) {
-               format = "qcow2";
-               ret = create_qc2_imagefile(disk, base, res->size);
-       } else {
-               format = "raw";
-               ret = create_raw_imagefile(disk, res->size);
-       }
-
-       if (ret != 0) {
+       if ((ret = create_imagefile(type, disk, base, res->size, &format)) != 
0) {
                errno = ret;
                err(1, "create imagefile operation failed");
        } else
+               warnx("%s imagefile created", format);
+
+       return (0);
+}
+
+int
+ctl_convert(const char *srcfile, const char *dstfile, int dsttype, size_t 
dstsize)
+{
+       struct {
+               int                      fd;
+               int                      type;
+               struct virtio_backing    file;
+               const char              *disk;
+               off_t                    size;
+       }        src, dst;
+       int              ret;
+       const char      *format, *errstr = NULL;
+       uint8_t         *buf = NULL, *zerobuf = NULL;
+       size_t           buflen;
+       ssize_t          len, rlen;
+       off_t            off;
+
+       memset(&src, 0, sizeof(src));
+       memset(&dst, 0, sizeof(dst));
+
+       src.type = parse_disktype(srcfile, &src.disk);
+       dst.type = dsttype;
+       dst.disk = dstfile;
+
+       if ((src.fd = open_imagefile(src.type, src.disk, O_RDONLY,
+           &src.file, &src.size)) == -1) {
+               errstr = "failed to open source image file";
+               goto done;
+       }
+
+       /* We can only lock unveil after opening the disk and all base images */
+       if (unveil(NULL, NULL))
+               err(1, "unveil");
+
+       if (dstsize == 0)
+               dstsize = src.size;
+       else
+               dstsize *= 1048576;
+       if (dstsize < (size_t)src.size) {
+               errstr = "size cannot be smaller than input disk size";
+               goto done;
+       }
+
+       /* align to megabytes */
+       dst.size = ALIGN(dstsize, 1048576);
+
+       if ((ret = create_imagefile(dst.type, dst.disk, NULL,
+          dst.size / 1048576, &format)) != 0) {
+               errno = ret;
+               errstr = "failed to create destination image file";
+               goto done;
+       }
+
+       if ((dst.fd = open_imagefile(dst.type, dst.disk, O_RDWR,
+           &dst.file, &dst.size)) == -1) {
+               errstr = "failed to open destination image file";
+               goto done;
+       }
+
+       if (pledge("stdio", NULL) == -1)
+               err(1, "pledge");
+
+       /*
+        * Use 64k buffers by default.  This could also be adjusted to
+        * the backend cluster size.
+        */
+       buflen = 1 << 16;
+       if ((buf = calloc(1, buflen)) == NULL ||
+           (zerobuf = calloc(1, buflen)) == NULL) {
+               errstr = "failed to allocated buffers";
+               goto done;
+       }
+
+       for (off = 0; off < dst.size; off += len) {
+               /* Read input from the source image */
+               if (off < src.size) {
+                       len = MIN((off_t)buflen, src.size - off);
+                       if ((rlen = src.file.pread(src.file.p,
+                           buf, (size_t)len, off)) != len) {
+                               errno = EIO;
+                               errstr = "failed to read from source";
+                               goto done;
+                       }
+               } else
+                       len = 0;
+
+               /* and pad the remaining bytes */
+               if (len < (ssize_t)buflen) {
+                       log_debug("%s: padding %zd zero bytes at offset %lld",
+                           format, buflen - len, off + len);
+                       memset(buf + len, 0, buflen - len);
+                       len = buflen;
+               }
+
+               /*
+                * No need to copy empty buffers.  This allows the backend,
+                * sparse files or QCOW2 images, to save space in the
+                * destination file.
+                */
+               if (memcmp(buf, zerobuf, buflen) == 0)
+                       continue;
+
+               log_debug("%s: writing %zd of %lld bytes at offset %lld",
+                   format, len, dst.size, off);
+
+               if ((rlen = dst.file.pwrite(dst.file.p,
+                   buf, (size_t)len, off)) != len) {
+                       errno = EIO;
+                       errstr = "failed to write to destination";
+                       goto done;
+               }
+       }
+
+       if (dstsize < (size_t)dst.size)
+               warnx("destination size rounded to %lld megabytes",
+                   dst.size / 1048576);
+
+ done:
+       free(buf);
+       free(zerobuf);
+       if (src.file.p != NULL)
+               src.file.close(src.file.p, 0);
+       if (dst.file.p != NULL)
+               dst.file.close(dst.file.p, 0);
+       if (errstr != NULL)
+               errx(1, "%s", errstr);
+       else
                warnx("%s imagefile created", format);
 
        return (0);
Index: usr.sbin/vmctl/vmctl.8
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.8,v
retrieving revision 1.51
diff -u -p -u -p -r1.51 vmctl.8
--- usr.sbin/vmctl/vmctl.8      8 Oct 2018 16:32:01 -0000       1.51
+++ usr.sbin/vmctl/vmctl.8      8 Oct 2018 17:39:37 -0000
@@ -22,6 +22,7 @@
 .Nd control the virtual machine daemon
 .Sh SYNOPSIS
 .Nm
+.Op Fl v
 .Ar command
 .Op Ar arg ...
 .Sh DESCRIPTION
@@ -32,6 +33,9 @@ A VMM manages virtual machines (VMs) on 
 The VMM subsystem is responsible for creating, destroying, and executing
 VMs.
 .Pp
+The
+.Fl v
+option enables verbose mode.
 Within the commands,
 the
 .Ar size
@@ -43,6 +47,19 @@ The
 argument can be either a numeric, non-zero identifier or alternatively
 the name of a virtual machine.
 .Pp
+The
+.Ar disk
+argument is used by commands that take a path to a disk image file.
+It may be prefixed with a format prefix
+.Pf ( raw Ns : Ns Ar disk
+or
+.Pf qcow2 Ns : Ns Ar disk )
+.Sm on
+in order to specify the disk image format.
+If left unspecified, the format defaults to
+.Sq raw
+if it cannot be derived automatically.
+.Pp
 The commands are as follows:
 .Bl -tag -width Ds
 .It Cm console Ar id
@@ -50,31 +67,44 @@ Using
 .Xr cu 1
 connect to the console of the VM with the specified
 .Ar id .
-.It Cm create Ar path Fl s Op Ar size Op Fl b Ar base
+.It Cm create Ar disk Oo Fl s Ar size Oc Op Fl b Ar base | Fl i Ar disk
 Creates a VM disk image file with the specified
-.Ar path
-and
-.Ar size ,
-rounded to megabytes.
-The disk
-.Ar path
-may be prefixed with a format prefix
-.Pf ( Pa raw :
-or
-.Pa qcow2 : )
-in order to specify the disk format.
-If left unspecified, the format defaults to
-.Pa raw
-if it cannot be derived automatically.
-For qcow2, a
+.Ar disk
+path.
+.Bl -tag -width "-i input"
+.It Fl b Ar base
+For
+.Sq qcow2 ,
+a
 .Ar base
 image may be specified.
-The base image is not modified.
-The derived image contains only the changes written by the VM.
-When creating a derived image, the
+The base image is not modified and the derived image contains only the
+changes written by the VM.
+.It Fl i Ar disk
+Copy and convert the input
+.Ar disk
+to the newly created disk.
+This option conflicts with
+.Fl b Ar base .
+.It Fl s Ar size
+Specify the
 .Ar size
-may be omitted, and probed from the base image.
-If it is provided, it must match the base image size.
+of the new disk image, rounded to megabytes.
+If the
+.Fl b
+option is specified, the size must match the size of the
+.Ar base
+image.
+For the
+.Fl i
+option, the size cannot be smaller than the input disk size.
+The size can be ommitted with the
+.Fl b
+and
+.Fl i
+options and will be obtained from the base or input image respectively.
+.El
+.Pp
 .It Cm load Ar filename
 Load additional configuration from the specified file.
 .It Cm log brief
@@ -114,7 +144,7 @@ command.
 .It Xo Cm start Ar name
 .Op Fl cL
 .Op Fl b Ar path
-.Op Fl d Ar path
+.Op Fl d Ar disk
 .Op Fl i Ar count
 .Op Fl m Ar size
 .Op Fl n Ar switch
@@ -131,18 +161,10 @@ If not specified, the default is to boot
 .Pa /etc/firmware/vmm-bios .
 .It Fl c
 Automatically connect to the VM console.
-.It Fl d Ar path
-Disk image file (may be specified multiple times to add multiple disk images).
-The disk
-.Ar path
-may be prefixed with a format prefix
-.Pf ( Pa raw :
-or
-.Pa qcow2 : )
-in order to specify the disk format.
-If left unspecified, the format defaults to
-.Pa raw
-if it cannot be derived automatically.
+.It Fl d Ar disk
+Use a disk image at the specified
+.Ar disk
+path (may be specified multiple times to add multiple disk images).
 .It Fl i Ar count
 Number of network interfaces to add to the VM.
 .It Fl L
@@ -328,6 +350,14 @@ A requested VM-based operation could not
 Create a 4.5 Gigabyte disk image, disk.img:
 .Bd -literal -offset indent
 $ vmctl create disk.img -s 4.5G
+.Ed
+.Pp
+Convert a disk image from the
+.Sq raw
+format to
+.Sq qcow2 :
+.Bd -literal -offset indent
+$ vmctl create disk.qcow2 -i disk.img
 .Ed
 .Pp
 Create a new VM with 1GB memory, one network interface, one disk image
Index: usr.sbin/vmctl/vmctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.c,v
retrieving revision 1.61
diff -u -p -u -p -r1.61 vmctl.c
--- usr.sbin/vmctl/vmctl.c      8 Oct 2018 16:32:01 -0000       1.61
+++ usr.sbin/vmctl/vmctl.c      8 Oct 2018 17:39:37 -0000
@@ -39,6 +39,7 @@
 #include <grp.h>
 
 #include "vmd.h"
+#include "virtio.h"
 #include "vmctl.h"
 #include "atomicio.h"
 
@@ -794,6 +795,116 @@ vm_console(struct vmop_info_result *list
 }
 
 /*
+ * open_imagefile
+ *
+ * Open an imagefile with the specified type, path and size.
+ *
+ * Parameters:
+ *  type        : format of the image file
+ *  imgfile_path: path to the image file to create
+ *  flags       : flags for open(2), e.g. O_RDONLY
+ *  file        : file structure
+ *  sz         : size of the image file
+ *
+ * Return:
+ *  fd          : Returns file descriptor of the new image file
+ *  -1          : Operation failed.  errno is set.
+ */
+int
+open_imagefile(int type, const char *imgfile_path, int flags,
+    struct virtio_backing *file, off_t *sz)
+{
+       int      fd, ret, basefd[VM_MAX_BASE_PER_DISK], nfd, i;
+       char     path[PATH_MAX];
+
+       *sz = 0;
+       if ((fd = open(imgfile_path, flags)) == -1)
+               return (-1);
+
+       basefd[0] = fd;
+       nfd = 1;
+
+       errno = 0;
+       switch (type) {
+       case VMDF_QCOW2:
+               if (strlcpy(path, imgfile_path, sizeof(path)) >= sizeof(path))
+                       return (-1);
+               for (i = 0; i < VM_MAX_BASE_PER_DISK - 1; i++, nfd++) {
+                       if ((ret = virtio_qcow2_get_base(basefd[i],
+                           path, sizeof(path), imgfile_path)) == -1) {
+                               log_debug("%s: failed to get base %d", 
__func__, i);
+                               return -1;
+                       } else if (ret == 0)
+                               break;
+
+                       /*
+                        * This might be called after unveil is already
+                        * locked but it is save to ignore the EPERM error
+                        * here as the subsequent open would fail as well.
+                        */
+                       if ((ret = unveil(path, "r")) != 0 &&
+                           (ret != EPERM))
+                               err(1, "unveil");
+                       if ((basefd[i + 1] = open(path, O_RDONLY)) == -1) {
+                               log_warn("%s: failed to open base %s",
+                                   __func__, path);
+                               return (-1);
+                       }
+               }
+               ret = virtio_init_qcow2(file, sz, basefd, nfd);
+               break;
+       default:
+               ret = virtio_init_raw(file, sz, &fd, 1);
+               break;
+       }
+
+       if (ret == -1) {
+               for (i = 0; i < nfd; i++)
+                       close(basefd[i]);
+               return (-1);
+       }
+
+       return (fd);
+}
+
+/*
+ * create_imagefile
+ *
+ * Create an empty imagefile with the specified type, path and size.
+ *
+ * Parameters:
+ *  type        : format of the image file
+ *  imgfile_path: path to the image file to create
+ *  base_path   : path to the qcow2 base image
+ *  imgsize     : size of the image file to create (in MB)
+ *  format      : string identifying the format
+ *
+ * Return:
+ *  EEXIST: The requested image file already exists
+ *  0     : Image file successfully created
+ *  Exxxx : Various other Exxxx errno codes due to other I/O errors
+ */
+int
+create_imagefile(int type, const char *imgfile_path, const char *base_path,
+    long imgsize, const char **format)
+{
+       int      ret;
+
+       switch (type) {
+       case VMDF_QCOW2:
+               *format = "qcow2";
+               ret = create_qc2_imagefile(imgfile_path, base_path, imgsize);
+               break;
+       default:
+               *format = "raw";
+               ret = create_raw_imagefile(imgfile_path, imgsize);
+               break;
+       }
+
+       return (ret);
+}
+
+/*
  * create_raw_imagefile
  *
  * Create an empty imagefile with the specified path and size.
@@ -831,7 +942,7 @@ create_raw_imagefile(const char *imgfile
 }
 
 /*
- * create_imagefile
+ * create_qc2_imagefile
  *
  * Create an empty qcow2 imagefile with the specified path and size.
  *
@@ -844,8 +955,6 @@ create_raw_imagefile(const char *imgfile
  *  0     : Image file successfully created
  *  Exxxx : Various other Exxxx errno codes due to other I/O errors
  */
-#define ALIGN(sz, align) \
-       ((sz + align - 1) & ~(align - 1))
 int
 create_qc2_imagefile(const char *imgfile_path,
     const char *base_path, long imgsize)
Index: usr.sbin/vmctl/vmctl.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.h,v
retrieving revision 1.26
diff -u -p -u -p -r1.26 vmctl.h
--- usr.sbin/vmctl/vmctl.h      8 Oct 2018 16:32:01 -0000       1.26
+++ usr.sbin/vmctl/vmctl.h      8 Oct 2018 17:39:37 -0000
@@ -71,6 +71,9 @@ struct ctl_command {
 
 struct imsgbuf *ibuf;
 
+#define ALIGN(sz, align)       ((sz + align - 1) & ~(align - 1))
+#define MIN(a,b)               (((a)<(b))?(a):(b))
+
 /* main.c */
 int     vmmaction(struct parse_result *);
 int     parse_ifs(struct parse_result *, char *, int);
@@ -86,6 +89,9 @@ __dead void
         ctl_openconsole(const char *);
 
 /* vmctl.c */
+int     open_imagefile(int, const char *, int,
+           struct virtio_backing *, off_t *);
+int     create_imagefile(int, const char *, const char *, long, const char **);
 int     create_raw_imagefile(const char *, long);
 int     create_qc2_imagefile(const char *, const char *, long);
 int     vm_start(uint32_t, const char *, int, int, char **, int,

Reply via email to