On Sun, 12 Aug 2018 22:51:24 -0700, Ori Bernstein <o...@eigenstate.org> wrote:

> <qcow2 patch>

This patch adds preliminary support for creating qcow2 disk images. It gives
the 'vmctl create' command  an option '-f', which can be given arguments of
either 'raw' (defualt) or 'qcow2', and it creates a disk of the appropriate
format.

The qcow2 header is duplicated inline, since it seemed better than the other
options (putting it into a header and messing around with include/source paths 
so
that we could share it with vmd, or creating a libqcow2)

That decision may get revisited when I find time to implement snapshotting,
since creating a snapshot will involve relatively deep modifications to the
disk (adjusting refcounts, rewriting L1/L2 tables, etc).

---
 usr.sbin/vmctl/main.c  |  18 +++++--
 usr.sbin/vmctl/vmctl.8 |   7 ++-
 usr.sbin/vmctl/vmctl.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++-
 usr.sbin/vmctl/vmctl.h |   3 +-
 4 files changed, 143 insertions(+), 9 deletions(-)

diff --git usr.sbin/vmctl/main.c usr.sbin/vmctl/main.c
index b7674d0c980..f39ccbdbc1f 100644
--- usr.sbin/vmctl/main.c
+++ usr.sbin/vmctl/main.c
@@ -63,7 +63,8 @@ int            ctl_receive(struct parse_result *, int, char 
*[]);
 
 struct ctl_command ctl_commands[] = {
        { "console",    CMD_CONSOLE,    ctl_console,    "id" },
-       { "create",     CMD_CREATE,     ctl_create,     "\"path\" -s size", 1 },
+       { "create",     CMD_CREATE,     ctl_create,     
+               "\"path\" -s size [-f fmt]", 1 },
        { "load",       CMD_LOAD,       ctl_load,       "\"path\"" },
        { "log",        CMD_LOG,        ctl_log,        "(verbose|brief)" },
        { "reload",     CMD_RELOAD,     ctl_reload,     "" },
@@ -470,24 +471,28 @@ int
 ctl_create(struct parse_result *res, int argc, char *argv[])
 {
        int              ch, ret;
-       const char      *paths[2];
+       const char      *paths[2], *format;
 
        if (argc < 2)
                ctl_usage(res->ctl);
 
        paths[0] = argv[1];
        paths[1] = NULL;
+       format = "raw";
        if (pledge("stdio rpath wpath cpath", NULL) == -1)
                err(1, "pledge");
        argc--;
        argv++;
 
-       while ((ch = getopt(argc, argv, "s:")) != -1) {
+       while ((ch = getopt(argc, argv, "s:f:")) != -1) {
                switch (ch) {
                case 's':
                        if (parse_size(res, optarg, 0) != 0)
                                errx(1, "invalid size: %s", optarg);
                        break;
+               case 'f':
+                       format = optarg;
+                       break;
                default:
                        ctl_usage(res->ctl);
                        /* NOTREACHED */
@@ -498,7 +503,12 @@ ctl_create(struct parse_result *res, int argc, char 
*argv[])
                fprintf(stderr, "missing size argument\n");
                ctl_usage(res->ctl);
        }
-       ret = create_imagefile(paths[0], res->size);
+       if (strcmp(format, "raw") == 0)
+               ret = create_raw_imagefile(paths[0], res->size);
+       else if (strcmp(format, "qcow2") == 0)
+               ret = create_qc2_imagefile(paths[0], res->size);
+       else
+               errx(1, "unknown image format %s", format);
        if (ret != 0) {
                errno = ret;
                err(1, "create imagefile operation failed");
diff --git usr.sbin/vmctl/vmctl.8 usr.sbin/vmctl/vmctl.8
index 81ecbeb6c1d..358928c7ff6 100644
--- usr.sbin/vmctl/vmctl.8
+++ usr.sbin/vmctl/vmctl.8
@@ -50,12 +50,15 @@ Using
 .Xr cu 1
 connect to the console of the VM with the specified
 .Ar id .
-.It Cm create Ar path Fl s Ar size
+.It Cm create Ar path Fl s Ar size Op Fl f Ar format
 Creates a VM disk image file with the specified
 .Ar path
 and
 .Ar size ,
-rounded to megabytes.
+rounded to megabytes. The disk
+.Ar format
+may be specified as either 'raw' or 'qcow2', defaulting to 'raw'
+if left unspecified.
 .It Cm load Ar filename
 Load additional configuration from the specified file.
 .It Cm log brief
diff --git usr.sbin/vmctl/vmctl.c usr.sbin/vmctl/vmctl.c
index 7ab9a9bc60c..b3a4e6d7b14 100644
--- usr.sbin/vmctl/vmctl.c
+++ usr.sbin/vmctl/vmctl.c
@@ -735,7 +735,7 @@ vm_console(struct vmop_info_result *list, size_t ct)
 }
 
 /*
- * create_imagefile
+ * create_raw_imagefile
  *
  * Create an empty imagefile with the specified path and size.
  *
@@ -749,7 +749,7 @@ vm_console(struct vmop_info_result *list, size_t ct)
  *  Exxxx : Various other Exxxx errno codes due to other I/O errors
  */
 int
-create_imagefile(const char *imgfile_path, long imgsize)
+create_raw_imagefile(const char *imgfile_path, long imgsize)
 {
        int fd, ret;
 
@@ -770,3 +770,123 @@ create_imagefile(const char *imgfile_path, long imgsize)
        ret = close(fd);
        return (ret);
 }
+
+/*
+ * create_imagefile
+ *
+ * Create an empty qcow2 imagefile with the specified path and size.
+ *
+ * Parameters:
+ *  imgfile_path: path to the image file to create
+ *  imgsize     : size of the image file to create (in MB)
+ *
+ * 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
+ */
+#define ALIGN(sz, align) \
+       ((sz + align - 1) & ~(align - 1))
+int
+create_qc2_imagefile(const char *imgfile_path, long imgsize)
+{
+       struct qcheader {
+               char magic[4];
+               uint32_t version;
+               uint64_t backingoff;
+               uint32_t backingsz;
+               uint32_t clustershift;
+               uint64_t disksz;
+               uint32_t cryptmethod;
+               uint32_t l1sz;
+               uint64_t l1off;
+               uint64_t refoff;
+               uint32_t refsz;
+               uint32_t snapcount;
+               uint64_t snapsz;
+               /* v3 additions */
+               uint64_t incompatfeatures;
+               uint64_t compatfeatures;
+               uint64_t autoclearfeatures;
+               uint32_t reforder;
+               uint32_t headersz;
+       } __packed hdr;
+       int fd, ret;
+       uint64_t l1sz, refsz, disksz, initsz, clustersz;
+       uint64_t l1off, refoff, v, i;
+       uint16_t refs;
+
+       disksz = 1024*1024*imgsize;
+       clustersz = (1<<16);
+       l1off = ALIGN(sizeof hdr, clustersz);
+       l1sz = disksz / (clustersz*clustersz/8);
+       if (l1sz == 0)
+               l1sz = 1;
+
+       refoff = ALIGN(l1off + 8*l1sz, clustersz);
+       refsz = disksz / (clustersz*clustersz*clustersz/2);
+       if (refsz == 0)
+               refsz = 1;
+
+       initsz = ALIGN(refoff + refsz*clustersz, clustersz);
+
+       memcpy(hdr.magic, "QFI\xfb", 4);
+       hdr.version             = htobe32(3);
+       hdr.backingoff          = htobe64(0);
+       hdr.backingsz           = htobe32(0);
+       hdr.clustershift        = htobe32(16);
+       hdr.disksz              = htobe64(disksz);
+       hdr.cryptmethod         = htobe32(0);
+       hdr.l1sz                = htobe32(l1sz);
+       hdr.l1off               = htobe64(l1off);
+       hdr.refoff              = htobe64(refoff);
+       hdr.refsz               = htobe32(refsz);
+       hdr.snapcount           = htobe32(0);
+       hdr.snapsz              = htobe64(0);
+       hdr.incompatfeatures    = htobe64(0);
+       hdr.compatfeatures      = htobe64(0);
+       hdr.autoclearfeatures   = htobe64(0);
+       hdr.reforder            = htobe32(4);
+       hdr.headersz            = htobe32(sizeof hdr);
+
+       /* Refuse to overwrite an existing image */
+       fd = open(imgfile_path, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
+           S_IRUSR | S_IWUSR);
+       if (fd == -1)
+               return (errno);
+
+       /* Write out the header */
+       if (write(fd, &hdr, sizeof hdr) != sizeof hdr)
+               goto error;
+
+       /* Extend to desired size, and add one refcount cluster */
+       if (ftruncate(fd, (off_t)initsz + clustersz) == -1)
+               goto error;
+
+       /* 
+        * Paranoia: if our disk image takes more than one cluster
+        * to refcount the initial empty image, the disk too big.
+        */
+       if (initsz/clustersz > clustersz/2) {
+               errno = ERANGE;
+               goto error;
+       }
+
+       /* Add a refcount block, and refcount ourselves. */
+       v = htobe64(initsz);
+       if (pwrite(fd, &v, 8, refoff) != 8)
+               goto error;
+       for (i = 0; i < initsz/clustersz + 1; i++) {
+               refs = htobe16(1);
+               if (pwrite(fd, &refs, 2, initsz + 2*i) != 2)
+                       goto error;
+       }
+
+       ret = close(fd);
+       return (ret);
+error:
+       ret = errno;
+       close(fd);
+       unlink(imgfile_path);
+       return (errno);
+}
diff --git usr.sbin/vmctl/vmctl.h usr.sbin/vmctl/vmctl.h
index 91ade10b7d8..87097446a18 100644
--- usr.sbin/vmctl/vmctl.h
+++ usr.sbin/vmctl/vmctl.h
@@ -83,7 +83,8 @@ __dead void
         ctl_openconsole(const char *);
 
 /* vmctl.c */
-int     create_imagefile(const char *, long);
+int     create_raw_imagefile(const char *, long);
+int     create_qc2_imagefile(const char *, long);
 int     vm_start(uint32_t, const char *, int, int, char **, int,
            char **, char *, char *, char *);
 int     vm_start_complete(struct imsg *, int *, int);
-- 
2.16.4

-- 
    Ori Bernstein

Reply via email to