[libvirt] [PATCH 3/3] Add volume zeroing

David Allan dallan at redhat.com
Mon Feb 15 21:29:02 UTC 2010


* If the appropriate flag is specified to vol delete, zero out the volume before deleting it.
* If the volume is a sparse file and the fiemap ioctl is available, use fiemap to locate the volume's extents.
---
 src/storage/storage_driver.c        |  125 +++++++++++++++++++++++++++++++++
 src/storage/storage_driver.h        |   20 +++++
 src/storage/storage_driver_fiemap.c |  132 +++++++++++++++++++++++++++++++++++
 3 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 src/storage/storage_driver_fiemap.c

diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index 6b1045a..661c412 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,118 @@ cleanup:
     return ret;
 }

+
+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 (lseek(fd, extent_start, SEEK_SET) < 0) {
+        VIR_ERROR("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) {
+            VIR_ERROR("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
+storageVolumeZeroOut(virStorageVolDefPtr vol)
+{
+    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'", vol->target.path);
+
+    fd = open(vol->target.path, O_RDWR);
+    if (fd == -1) {
+        VIR_ERROR("Failed to open storage volume with path '%s': '%s'",
+                  vol->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'",
+                  vol->target.path,
+                  virStrerror(errno, errbuf, sizeof(errbuf)));
+        goto out;
+    }
+
+    if (VIR_ALLOC_N(writebuf, st.st_blksize) != 0) {
+        virReportOOMError();
+        goto out;
+    }
+
+    /* Don't attempt to brute force a sparse regular file; doing so
+     * could take an essentially unbounded amount of time.  The user
+     * can always delete and recreate the file to zero it. */
+    if (S_ISREG(st.st_mode) && st.st_blocks < (st.st_size / DEV_BSIZE)) {
+        ret = storageZeroByFiemap(vol, &st, fd, writebuf);
+    } else {
+        ret = storageZeroExtent(vol,
+                                &st,
+                                fd,
+                                0,
+                                vol->allocation,
+                                writebuf,
+                                &bytes_zeroed);
+    }
+
+out:
+    VIR_FREE(writebuf);
+
+    if (fd != -1) {
+        close(fd);
+    }
+
+    return ret;
+}
+
+
 static int
 storageVolumeDelete(virStorageVolPtr obj,
                     unsigned int flags) {
@@ -1563,6 +1678,16 @@ storageVolumeDelete(virStorageVolPtr obj,
         goto cleanup;
     }

+    /* Even if the backend doesn't support volume deletion, we can
+     * still zero it out; indeed, if the backend does support volume
+     * deletion, it's almost certain to be faster to delete & recreate
+     * a volume than it is to zero it out. */
+    if (flags & VIR_STORAGE_VOL_DELETE_ZEROED) {
+        if (storageVolumeZeroOut(vol) == -1) {
+            goto cleanup;
+        }
+    }
+
     if (!backend->deleteVol) {
         virStorageReportError(VIR_ERR_NO_SUPPORT,
                               "%s", _("storage pool does not support vol deletion"));
diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h
index 500ea02..cb837f8 100644
--- a/src/storage/storage_driver.h
+++ b/src/storage/storage_driver.h
@@ -28,4 +28,24 @@

 int storageRegister(void);

+#ifdef HAVE_FIEMAP
+#define storageZeroByFiemap storageZeroByFiemapInternal
+int
+storageZeroByFiemapInternal(virStorageVolDefPtr vol,
+                            struct stat *st,
+                            int fd,
+                            char *writebuf);
+#else // #ifdef HAVE_FIEMAP
+#define storageZeroByFiemap(vol, st, fd, writebuf) -1
+#endif // #ifdef HAVE_FIEMAP
+
+int
+storageZeroExtent(virStorageVolDefPtr vol,
+                  struct stat *st,
+                  int fd,
+                  size_t extent_start,
+                  size_t extent_length,
+                  char *writebuf,
+                  size_t *bytes_zeroed);
+
 #endif /* __VIR_STORAGE_DRIVER_H__ */
diff --git a/src/storage/storage_driver_fiemap.c b/src/storage/storage_driver_fiemap.c
new file mode 100644
index 0000000..1984254
--- /dev/null
+++ b/src/storage/storage_driver_fiemap.c
@@ -0,0 +1,132 @@
+/*
+ * storage_driver_fiemap.c: fiemap specific code for storage driver
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Dave Allan <dallan at redhat.com>
+ */
+#include <config.h>
+
+#include <sys/ioctl.h>
+#include <linux/fiemap.h>
+#include <linux/fs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "virterror_internal.h"
+#include "storage_driver.h"
+#include "memory.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+static int
+storageGetNumExtents(virStorageVolDefPtr vol,
+                     int fd,
+                     int *num_extents)
+{
+    int ret = -1;
+    char errbuf[64];
+    struct fiemap map;
+
+    map.fm_start = 0;
+    map.fm_length = FIEMAP_MAX_OFFSET;
+    map.fm_extent_count = 0;
+    map.fm_flags = FIEMAP_FLAG_SYNC;
+
+    ret = ioctl(fd, FS_IOC_FIEMAP, &map);
+
+    if (ret != 0) {
+        VIR_ERROR("fiemap failed: '%s' (returned %d) "
+                  "Flags: 0x%"PRIx32" volume path: '%s'",
+                  strerror_r(errno, errbuf, sizeof(errbuf)),
+                  ret, map.fm_flags, vol->target.path);
+        goto out;
+    }
+
+    *num_extents = map.fm_mapped_extents;
+
+    VIR_DEBUG("Volume with path '%s' has %d extents",
+              vol->target.path, *num_extents);
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+int
+storageZeroByFiemapInternal(virStorageVolDefPtr vol,
+                            struct stat *st,
+                            int fd,
+                            char *writebuf ATTRIBUTE_UNUSED)
+{
+    int ret = -1, num_extents = 0, i;
+    size_t bytes_zeroed, total_zeroed = 0;
+    struct fiemap *fiemap_data = NULL;
+
+    if (storageGetNumExtents(vol, fd, &num_extents) != 0) {
+        goto out;
+    }
+
+    if (VIR_ALLOC_VAR(fiemap_data, struct fiemap_extent, num_extents) != 0) {
+        virReportOOMError();
+        goto out;
+    }
+
+    fiemap_data->fm_start = 0;
+    fiemap_data->fm_length = FIEMAP_MAX_OFFSET;
+    fiemap_data->fm_extent_count = num_extents;
+    fiemap_data->fm_flags = FIEMAP_FLAG_SYNC;
+
+    ret = ioctl(fd, FS_IOC_FIEMAP, fiemap_data);
+    if (ret == EBADR) {
+        VIR_ERROR("fiemap ioctl returned %d (Flags: 0x%"PRIx32")",
+                  ret, fiemap_data->fm_flags);
+        goto out;
+    }
+
+    VIR_DEBUG("extent count: %"PRIu32" mapped_extents: %"PRIu32,
+              fiemap_data->fm_extent_count, fiemap_data->fm_mapped_extents);
+
+    for (i = 0; i < fiemap_data->fm_mapped_extents; i++) {
+
+        bytes_zeroed = 0;
+        if (storageZeroExtent(vol,
+                              st,
+                              fd,
+                              fiemap_data->fm_extents[i].fe_logical,
+                              fiemap_data->fm_extents[i].fe_length,
+                              writebuf,
+                              &bytes_zeroed) != 0) {
+            goto out;
+        }
+
+        total_zeroed += bytes_zeroed;
+
+    }
+
+    ret = 0;
+
+out:
+    VIR_FREE(fiemap_data);
+
+    return ret;
+}
-- 
1.6.5.5




More information about the libvir-list mailing list