---
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