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