[libvirt] [PATCH v2 2/2] VolumeCreateXMLFrom FS storage backend implementation.

Cole Robinson crobinso at redhat.com
Mon May 18 17:30:33 UTC 2009


Add an extra 'inputvol' parameter to the file volume building routines. This
allows us to easily reuse the duplicate functionality.
---
 src/storage_backend_fs.c |  247 +++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 212 insertions(+), 35 deletions(-)

diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c
index fac43df..4d6f9b5 100644
--- a/src/storage_backend_fs.c
+++ b/src/storage_backend_fs.c
@@ -62,7 +62,9 @@ static int qcowXGetBackingStore(virConnectPtr, char **,
 static int vmdk4GetBackingStore(virConnectPtr, char **,
                                 const unsigned char *, size_t);
 
-typedef int (*createFile)(virConnectPtr conn, virStorageVolDefPtr vol);
+typedef int (*createFile)(virConnectPtr conn,
+                          virStorageVolDefPtr vol,
+                          virStorageVolDefPtr inputvol);
 
 static int track_allocation_progress = 0;
 
@@ -1014,15 +1016,29 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
 }
 
 static int createRaw(virConnectPtr conn,
-                     virStorageVolDefPtr vol) {
-    int fd;
+                     virStorageVolDefPtr vol,
+                     virStorageVolDefPtr inputvol) {
+    int fd = -1;
+    int inputfd = -1;
+    int ret = -1;
+    unsigned long long remain;
+    char *buf = NULL;
 
     if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL,
                    vol->target.perms.mode)) < 0) {
         virReportSystemError(conn, errno,
                              _("cannot create path '%s'"),
                              vol->target.path);
-        return -1;
+        goto cleanup;
+    }
+
+    if (inputvol) {
+        if ((inputfd = open(inputvol->target.path, O_RDONLY)) < 0) {
+            virReportSystemError(conn, errno,
+                                 _("could not open input path '%s'"),
+                                 inputvol->target.path);
+            goto cleanup;
+        }
     }
 
     /* Seek to the final size, so the capacity is available upfront
@@ -1031,15 +1047,66 @@ static int createRaw(virConnectPtr conn,
         virReportSystemError(conn, errno,
                              _("cannot extend file '%s'"),
                              vol->target.path);
-        close(fd);
-        return -1;
+        goto cleanup;
+    }
+
+    remain = vol->capacity;
+
+    if (inputfd != -1) {
+        int amtread = -1;
+        size_t bytes = 1024 * 1024;
+        char zerobuf[512];
+
+        bzero(&zerobuf, sizeof(zerobuf));
+
+        if (VIR_ALLOC_N(buf, bytes) < 0) {
+            virReportOOMError(conn);
+            goto cleanup;
+        }
+
+        while (amtread != 0) {
+            int amtleft;
+
+            if (remain < bytes)
+                bytes = remain;
+
+            if ((amtread = saferead(inputfd, buf, bytes)) < 0) {
+                virReportSystemError(conn, errno,
+                                     _("failed reading from file '%s'"),
+                                     inputvol->target.path);
+                goto cleanup;
+            }
+            remain -= amtread;
+
+            /* Loop over amt read in 512 byte increments, looking for sparse
+             * blocks */
+            amtleft = amtread;
+            do {
+                int interval = ((512 > amtleft) ? amtleft : 512);
+                int offset = amtread - amtleft;
+
+                if (memcmp(buf+offset, zerobuf, interval) == 0) {
+                    if (lseek(fd, interval, SEEK_CUR) < 0) {
+                        virReportSystemError(conn, errno,
+                                             _("cannot extend file '%s'"),
+                                             vol->target.path);
+                        goto cleanup;
+                    }
+                } else if (safewrite(fd, buf+offset, interval) < 0) {
+                    virReportSystemError(conn, errno,
+                                         _("failed writing to file '%s'"),
+                                         vol->target.path);
+                    goto cleanup;
+
+                }
+            } while ((amtleft -= 512) > 0);
+        }
     }
 
     /* Pre-allocate any data if requested */
     /* XXX slooooooooooooooooow on non-extents-based file systems */
-    if (vol->allocation) {
+    if (remain) {
         if (track_allocation_progress) {
-            unsigned long long remain = vol->allocation;
 
             while (remain) {
                 /* Allocate in chunks of 512MiB: big-enough chunk
@@ -1056,20 +1123,18 @@ static int createRaw(virConnectPtr conn,
                     virReportSystemError(conn, r,
                                          _("cannot fill file '%s'"),
                                          vol->target.path);
-                    close(fd);
-                    return -1;
+                    goto cleanup;
                 }
                 remain -= bytes;
             }
         } else { /* No progress bars to be shown */
             int r;
 
-            if ((r = safezero(fd, 0, 0, vol->allocation)) != 0) {
+            if ((r = safezero(fd, 0, 0, remain)) != 0) {
                 virReportSystemError(conn, r,
                                      _("cannot fill file '%s'"),
                                      vol->target.path);
-                close(fd);
-                return -1;
+                goto cleanup;
             }
         }
     }
@@ -1078,14 +1143,39 @@ static int createRaw(virConnectPtr conn,
         virReportSystemError(conn, errno,
                              _("cannot close file '%s'"),
                              vol->target.path);
-        return -1;
+        goto cleanup;
     }
+    fd = -1;
 
-    return 0;
+    if (inputfd != -1 && close(inputfd) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot close file '%s'"),
+                             inputvol->target.path);
+        goto cleanup;
+    }
+    inputfd = -1;
+
+    ret = 0;
+cleanup:
+    if (fd != -1)
+        close(fd);
+    if (inputfd != -1)
+        close(inputfd);
+    VIR_FREE(buf);
+
+    return ret;
 }
 
 static int createFileDir(virConnectPtr conn,
-                         virStorageVolDefPtr vol) {
+                         virStorageVolDefPtr vol,
+                         virStorageVolDefPtr inputvol) {
+    if (inputvol) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s",
+                              _("cannot copy from volume to a directory volume"));
+        return -1;
+    }
+
     if (mkdir(vol->target.path, vol->target.perms.mode) < 0) {
         virReportSystemError(conn, errno,
                              _("cannot create path '%s'"),
@@ -1098,20 +1188,56 @@ static int createFileDir(virConnectPtr conn,
 
 #if HAVE_QEMU_IMG
 static int createQemuImg(virConnectPtr conn,
-                         virStorageVolDefPtr vol) {
+                         virStorageVolDefPtr vol,
+                         virStorageVolDefPtr inputvol) {
+    char size[100];
+
     const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format);
     const char *backingType = vol->backingStore.path ?
         virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL;
-    char size[100];
+
+    const char *inputBackingPath = (inputvol ? inputvol->backingStore.path
+                                             : NULL);
+    const char *inputPath = inputvol ? inputvol->target.path : NULL;
+    /* Treat input block devices as 'raw' format */
+    const char *inputType = inputPath ?
+                            virStorageVolFormatFileSystemTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ? VIR_STORAGE_VOL_FILE_RAW : inputvol->target.format) :
+                            NULL;
+
     const char **imgargv;
     const char *imgargvnormal[] = {
-        QEMU_IMG, "create", "-f", type, vol->target.path, size, NULL,
+        QEMU_IMG, "create",
+        "-f", type,
+        vol->target.path,
+        size,
+        NULL,
     };
     /* XXX including "backingType" here too, once QEMU accepts
      * the patches to specify it. It'll probably be -F backingType */
     const char *imgargvbacking[] = {
-        QEMU_IMG, "create", "-f", type, "-b", vol->backingStore.path, vol->target.path, size, NULL,
+        QEMU_IMG, "create",
+        "-f", type,
+        "-b", vol->backingStore.path,
+        vol->target.path,
+        size,
+        NULL,
     };
+    const char *convargv[] = {
+        QEMU_IMG, "convert",
+        "-f", inputType,
+        "-O", type,
+        inputPath,
+        vol->target.path,
+        NULL,
+    };
+
+    if (inputvol) {
+        imgargv = convargv;
+    } else if (vol->backingStore.path) {
+        imgargv = imgargvbacking;
+    } else {
+        imgargv = imgargvnormal;
+    }
 
     if (type == NULL) {
         virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
@@ -1119,9 +1245,27 @@ static int createQemuImg(virConnectPtr conn,
                               vol->target.format);
         return -1;
     }
-    if (vol->backingStore.path == NULL) {
-        imgargv = imgargvnormal;
-    } else {
+    if (inputvol && inputType == NULL) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("unknown storage vol type %d"),
+                              inputvol->target.format);
+        return -1;
+    }
+
+    if (vol->backingStore.path) {
+
+        /* XXX: Not strictly required: qemu-img has an option a different
+         * backing store, not really sure what use it serves though, and it
+         * may cause issues with lvm. Untested essentially.
+         */
+        if (!inputBackingPath ||
+            !STREQ(inputBackingPath, vol->backingStore.path)) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  "%s", _("a different backing store can not "
+                                          "be specified."));
+            return -1;
+        }
+
         if (backingType == NULL) {
             virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                   _("unknown storage vol backing store type %d"),
@@ -1134,8 +1278,6 @@ static int createQemuImg(virConnectPtr conn,
                                  vol->backingStore.path);
             return -1;
         }
-
-        imgargv = imgargvbacking;
     }
 
     /* Size in KB */
@@ -1154,10 +1296,18 @@ static int createQemuImg(virConnectPtr conn,
  * with a partially functional qcow-create. Go figure ??!?
  */
 static int createQemuCreate(virConnectPtr conn,
-                            virStorageVolDefPtr vol) {
+                            virStorageVolDefPtr vol,
+                            virStorageVolDefPtr inputvol) {
     char size[100];
     const char *imgargv[4];
 
+    if (inputvol) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s",
+                              _("cannot copy from volume with qcow-create"));
+        return -1;
+    }
+
     if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) {
         virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
                               _("unsupported storage vol type %d"),
@@ -1187,19 +1337,22 @@ static int createQemuCreate(virConnectPtr conn,
 }
 #endif /* HAVE_QEMU_IMG, elif HAVE_QCOW_CREATE */
 
-/**
- * Allocate a new file as a volume. This is either done directly
- * for raw/sparse files, or by calling qemu-img/qcow-create for
- * special kinds of files
- */
 static int
-virStorageBackendFileSystemVolBuild(virConnectPtr conn,
-                                    virStorageVolDefPtr vol)
+_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
+                                     virStorageVolDefPtr vol,
+                                     virStorageVolDefPtr inputvol)
 {
     int fd;
     createFile create_func;
 
-    if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) {
+    if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW &&
+        (!inputvol ||
+         (inputvol->type == VIR_STORAGE_VOL_BLOCK ||
+          inputvol->target.format == VIR_STORAGE_VOL_FILE_RAW))) {
+          /* Raw file creation
+           * Raw -> Raw copying
+           * Block dev -> Raw copying
+           */
         create_func = createRaw;
     } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) {
         create_func = createFileDir;
@@ -1216,7 +1369,7 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn,
 #endif
     }
 
-    if (create_func(conn, vol) < 0)
+    if (create_func(conn, vol, inputvol) < 0)
         return -1;
 
     if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
@@ -1262,6 +1415,27 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn,
     return 0;
 }
 
+/**
+ * Allocate a new file as a volume. This is either done directly
+ * for raw/sparse files, or by calling qemu-img/qcow-create for
+ * special kinds of files
+ */
+static int
+virStorageBackendFileSystemVolBuild(virConnectPtr conn,
+                                    virStorageVolDefPtr vol) {
+    return _virStorageBackendFileSystemVolBuild(conn, vol, NULL);
+}
+
+/*
+ * Create a storage vol using 'inputvol' as input
+ */
+static int
+virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn,
+                                        virStorageVolDefPtr vol,
+                                        virStorageVolDefPtr inputvol,
+                                        unsigned int flags ATTRIBUTE_UNUSED) {
+    return _virStorageBackendFileSystemVolBuild(conn, vol, inputvol);
+}
 
 /**
  * Remove a volume - just unlinks for now
@@ -1304,6 +1478,7 @@ virStorageBackend virStorageBackendDirectory = {
     .refreshPool = virStorageBackendFileSystemRefresh,
     .deletePool = virStorageBackendFileSystemDelete,
     .buildVol = virStorageBackendFileSystemVolBuild,
+    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
@@ -1319,6 +1494,7 @@ virStorageBackend virStorageBackendFileSystem = {
     .stopPool = virStorageBackendFileSystemStop,
     .deletePool = virStorageBackendFileSystemDelete,
     .buildVol = virStorageBackendFileSystemVolBuild,
+    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
@@ -1333,6 +1509,7 @@ virStorageBackend virStorageBackendNetFileSystem = {
     .stopPool = virStorageBackendFileSystemStop,
     .deletePool = virStorageBackendFileSystemDelete,
     .buildVol = virStorageBackendFileSystemVolBuild,
+    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
-- 
1.6.2.2




More information about the libvir-list mailing list