* 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@xxxxxxxxxx> + */ +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list