Add ability to create qcow2 disk.
authorccardenas <ccardenas@openbsd.org>
Tue, 11 Sep 2018 04:03:16 +0000 (04:03 +0000)
committerccardenas <ccardenas@openbsd.org>
Tue, 11 Sep 2018 04:03:16 +0000 (04:03 +0000)
vmctl create now takes an optional disk format parameter: raw or qcow2.
If format is omitted, raw is used.

Many thanks to Ori Bernstein.

usr.sbin/vmctl/main.c
usr.sbin/vmctl/vmctl.8
usr.sbin/vmctl/vmctl.c
usr.sbin/vmctl/vmctl.h

index 0652490..49d982f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.40 2018/09/09 04:09:32 ccardenas Exp $     */
+/*     $OpenBSD: main.c,v 1.41 2018/09/11 04:03:16 ccardenas Exp $     */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -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,     "" },
@@ -497,24 +498,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 */
@@ -525,7 +530,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");
index 1ca6a0a..d4b06f3 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: vmctl.8,v 1.46 2018/09/09 06:36:43 jmc Exp $
+.\"    $OpenBSD: vmctl.8,v 1.47 2018/09/11 04:03:16 ccardenas Exp $
 .\"
 .\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: September 9 2018 $
+.Dd $Mdocdate: September 11 2018 $
 .Dt VMCTL 8
 .Os
 .Sh NAME
@@ -50,7 +50,7 @@ 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
index 3810bd7..0ef71a5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmctl.c,v 1.56 2018/09/09 04:09:32 ccardenas Exp $    */
+/*     $OpenBSD: vmctl.c,v 1.57 2018/09/11 04:03:16 ccardenas Exp $    */
 
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
@@ -737,7 +737,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.
  *
@@ -751,7 +751,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;
 
@@ -772,3 +772,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 image, fail.
+        */
+       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);
+}
index 92be581..593108f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmctl.h,v 1.22 2018/09/09 04:09:32 ccardenas Exp $    */
+/*     $OpenBSD: vmctl.h,v 1.23 2018/09/11 04:03:16 ccardenas Exp $    */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -85,7 +85,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 **, int *, char *, char *, char *);
 int     vm_start_complete(struct imsg *, int *, int);