* 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(a)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