--- src/storage/storage_driver.c | 218 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 218 insertions(+), 0 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 6b1045a..9e63689 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -26,6 +26,9 @@ #include <stdio.h> #include <unistd.h> #include <sys/types.h> +#include <sys/param.h> +#include <fcntl.h> + #if HAVE_PWD_H #include <pwd.h> #endif @@ -1518,6 +1521,220 @@ cleanup: return ret; } + +/* If the volume we're zeroing is already a sparse file, we simply + * truncate and extend it to its original size, filling it with + * zeroes. This behavior is guaranteed by POSIX: + * + * http://www.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html + * + * If fildes refers to a regular file, the ftruncate() function shall + * cause the size of the file to be truncated to length. If the size + * of the file previously exceeded length, the extra data shall no + * longer be available to reads on the file. If the file previously + * was smaller than this size, ftruncate() shall increase the size of + * the file. If the file size is increased, the extended area shall + * appear as if it were zero-filled. + */ +static int +storageVolumeZeroSparseFile(virStorageVolDefPtr vol, + struct stat *st, + int fd) +{ + int ret = -1; + char errbuf[64]; + + ret = ftruncate(fd, 0); + if (ret == -1) { + virReportSystemError(ret, + _("Failed to truncate volume with " + "path '%s' to 0 bytes: '%s'"), + vol->target.path, + virStrerror(errno, errbuf, sizeof(errbuf))); + goto out; + } + + ret = ftruncate(fd, st->st_size); + if (ret == -1) { + virReportSystemError(ret, + _("Failed to truncate volume with " + "path '%s' to %llu bytes: '%s'\n"), + vol->target.path, st->st_size, + virStrerror(errno, errbuf, sizeof(errbuf))); + } + +out: + return ret; +} + + +static int +storageZeroExtent(virStorageVolDefPtr vol, + struct stat *st, + int fd, + size_t extent_start, + size_t extent_length, + char *writebuf, + size_t *bytes_zeroed) +{ + int ret = -1, written; + size_t remaining, write_size; + char errbuf[64]; + + VIR_DEBUG("extent logical start: %zu len: %zu ", + extent_start, extent_length); + + if ((ret = lseek(fd, extent_start, SEEK_SET)) < 0) { + virReportSystemError(ret, "Failed to seek to position %zu in volume " + "with path '%s': '%s'", + extent_start, vol->target.path, + virStrerror(errno, errbuf, sizeof(errbuf))); + goto out; + } + + remaining = extent_length; + while (remaining > 0) { + + write_size = (st->st_blksize < remaining) ? st->st_blksize : remaining; + written = safewrite(fd, writebuf, write_size); + if (written < 0) { + virReportSystemError(written, + _("Failed to write to storage volume with " + "path '%s': '%s' " + "(attempted to write %d bytes)"), + vol->target.path, + virStrerror(errno, errbuf, sizeof(errbuf)), + write_size); + goto out; + } + + *bytes_zeroed += written; + remaining -= written; + } + + VIR_DEBUG("Wrote %zu bytes to volume with path '%s'", + *bytes_zeroed, vol->target.path); + + ret = 0; + +out: + return ret; +} + + +static int +storageVolumeZeroOutInternal(virStorageVolDefPtr def) +{ + int ret = -1, fd = -1; + char errbuf[64]; + struct stat st; + char *writebuf; + size_t bytes_zeroed = 0; + + VIR_DEBUG("Zeroing out volume with path '%s'", def->target.path); + + fd = open(def->target.path, O_RDWR); + if (fd == -1) { + VIR_ERROR("Failed to open storage volume with path '%s': '%s'", + def->target.path, + virStrerror(errno, errbuf, sizeof(errbuf))); + goto out; + } + + memset(&st, 0, sizeof(st)); + + if (fstat(fd, &st) == -1) { + VIR_ERROR("Failed to stat storage volume with path '%s': '%s'", + def->target.path, + virStrerror(errno, errbuf, sizeof(errbuf))); + goto out; + } + + if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) { + ret = storageVolumeZeroSparseFile(def, &st, fd); + } else { + + if (VIR_ALLOC_N(writebuf, st.st_blksize) != 0) { + virReportOOMError(); + goto out; + } + + ret = storageZeroExtent(def, + &st, + fd, + 0, + def->allocation, + writebuf, + &bytes_zeroed); + } + +out: + VIR_FREE(writebuf); + + if (fd != -1) { + close(fd); + } + + return ret; +} + + +static int +storageVolumeZeroOut(virStorageVolPtr obj, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virStorageDriverStatePtr driver = obj->conn->storagePrivateData; + virStoragePoolObjPtr pool; + virStorageVolDefPtr vol = NULL; + int ret = -1; + + storageDriverLock(driver); + pool = virStoragePoolObjFindByName(&driver->pools, obj->pool); + storageDriverUnlock(driver); + + if (!pool) { + virStorageReportError(VIR_ERR_INVALID_STORAGE_POOL, + "%s", _("no storage pool with matching uuid")); + goto out; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("storage pool is not active")); + goto out; + } + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (vol == NULL) { + virStorageReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching name '%s'"), + obj->name); + goto out; + } + + if (vol->building) { + virStorageReportError(VIR_ERR_INTERNAL_ERROR, + _("volume '%s' is still being allocated."), + vol->name); + goto out; + } + + if (storageVolumeZeroOutInternal(vol) == -1) { + goto out; + } + + ret = 0; + +out: + if (pool) { + virStoragePoolObjUnlock(pool); + } + + return ret; + +} + static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags) { @@ -1775,6 +1992,7 @@ static virStorageDriver storageDriver = { .volCreateXML = storageVolumeCreateXML, .volCreateXMLFrom = storageVolumeCreateXMLFrom, .volDelete = storageVolumeDelete, + .volZeroOut = storageVolumeZeroOut, .volGetInfo = storageVolumeGetInfo, .volGetXMLDesc = storageVolumeGetXMLDesc, .volGetPath = storageVolumeGetPath, -- 1.6.5.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list