diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 8953068c3c..a8fff149ce 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -2072,6 +2072,47 @@ virStorageBackendStablePath(virStoragePoolObjPtr pool, return stablepath; } +/* + * Check whether the ploop image has snapshots. + * return: -1 - failed to check + * 0 - no snapshots + * 1 - at least one snapshot + */ +static int +virStorageBackendPloopHasSnapshots(char *path) +{ + virCommandPtr cmd = NULL; + char *output = NULL; + char *snap_tool = NULL; + int ret = -1; + + snap_tool = virFindFileInPath("ploop"); + if (!snap_tool) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to find ploop, please install " + "ploop tools")); + return ret; + } + + cmd = virCommandNewArgList(snap_tool, "snapshot-list", NULL); + virCommandAddArgFormat(cmd, "%s/DiskDescriptor.xml", path); + virCommandSetOutputBuffer(cmd, &output); + + if ((ret = virCommandRun(cmd, NULL)) < 0) + goto cleanup; + + if (!strstr(output, "root.hds.")) { + ret = 1; + goto cleanup; + } + ret = 0; + + cleanup: + VIR_FREE(output); + virCommandFree(cmd); + return ret; +} + int virStorageBackendVolUploadLocal(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, @@ -2081,12 +2122,41 @@ virStorageBackendVolUploadLocal(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long long len, unsigned int flags) { + char *path = NULL; + char *target_path = vol->target.path; + int ret = -1; + int has_snap = 0; + virCheckFlags(0, -1); + /* if volume has target format VIR_STORAGE_FILE_PLOOP + * we need to restore DiskDescriptor.xml, according to + * new contents of volume. This operation will be perfomed + * when volUpload is fully finished. */ + if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { + /* Fail if the volume contains snapshots or we failed to check it.*/ + has_snap = virStorageBackendPloopHasSnapshots(vol->target.path); + if (has_snap < 0) { + goto cleanup; + } else if (!has_snap) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("can't upload volume, all existing snapshots" + " will be lost")); + goto cleanup; + } + + if (virAsprintf(&path, "%s/root.hds", vol->target.path) < 0) + return -1; + target_path = path; + } /* Not using O_CREAT because the file is required to already exist at * this point */ - return virFDStreamOpenBlockDevice(stream, vol->target.path, - offset, len, O_WRONLY); + ret = virFDStreamOpenBlockDevice(stream, target_path, + offset, len, O_WRONLY); + + cleanup: + VIR_FREE(path); + return ret; } int @@ -2098,10 +2168,33 @@ virStorageBackendVolDownloadLocal(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long long len, unsigned int flags) { - virCheckFlags(0, -1); + char *path = NULL; + char *target_path = vol->target.path; + int ret = -1; + int has_snap = 0; - return virFDStreamOpenBlockDevice(stream, vol->target.path, - offset, len, O_RDONLY); + virCheckFlags(0, -1); + if (vol->target.format == VIR_STORAGE_FILE_PLOOP) { + has_snap = virStorageBackendPloopHasSnapshots(vol->target.path); + if (has_snap < 0) { + goto cleanup; + } else if (!has_snap) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("can't download volume, all existing snapshots" + " will be lost")); + goto cleanup; + } + if (virAsprintf(&path, "%s/root.hds", vol->target.path) < 0) + goto cleanup; + target_path = path; + } + + ret = virFDStreamOpenBlockDevice(stream, target_path, + offset, len, O_RDONLY); + + cleanup: + VIR_FREE(path); + return ret; } diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 1d96618b16..fcc0991a68 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -63,6 +63,7 @@ typedef struct _virStorageVolStreamInfo virStorageVolStreamInfo; typedef virStorageVolStreamInfo *virStorageVolStreamInfoPtr; struct _virStorageVolStreamInfo { char *pool_name; + char *vol_path; }; static void storageDriverLock(void) @@ -2204,6 +2205,48 @@ virStorageVolPoolRefreshDataFree(void *opaque) VIR_FREE(cbdata); } +static int +virStorageBackendPloopRestoreDesc(char *path) +{ + int ret = -1; + virCommandPtr cmd = NULL; + char *refresh_tool = NULL; + char *desc = NULL; + + if (virAsprintf(&desc, "%s/DiskDescriptor.xml", path) < 0) + return ret; + + if (virFileRemove(desc, 0, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("refresh ploop failed:" + " unuble to delete DiskDescriptor.xml")); + goto cleanup; + } + + refresh_tool = virFindFileInPath("ploop"); + if (!refresh_tool) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to find ploop, please install ploop tools")); + goto cleanup; + } + + cmd = virCommandNewArgList(refresh_tool, "restore-descriptor", + path, NULL); + virCommandAddArgFormat(cmd, "%s/root.hds", path); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(refresh_tool); + virCommandFree(cmd); + VIR_FREE(desc); + return ret; +} + + + /** * Thread to handle the pool refresh * @@ -2219,6 +2262,10 @@ virStorageVolPoolRefreshThread(void *opaque) virStorageBackendPtr backend; storageDriverLock(); + if (cbdata->vol_path) { + if (virStorageBackendPloopRestoreDesc(cbdata->vol_path) < 0) + goto cleanup; + } if (!(pool = virStoragePoolObjFindByName(&driver->pools, cbdata->pool_name))) goto cleanup; @@ -2313,6 +2360,9 @@ storageVolUpload(virStorageVolPtr obj, if (VIR_ALLOC(cbdata) < 0 || VIR_STRDUP(cbdata->pool_name, pool->def->name) < 0) goto cleanup; + if (vol->target.type == VIR_STORAGE_VOL_PLOOP && + VIR_STRDUP(cbdata->vol_path, vol->target.path) < 0) + goto cleanup; } if ((ret = backend->uploadVol(obj->conn, pool, vol, stream,