[libvirt] Add virStorageVolResize()
Daniel P. Berrange
berrange at redhat.com
Fri Jan 27 17:08:58 UTC 2012
On Fri, Jan 27, 2012 at 07:29:56AM +0200, Zeeshan Ali (Khattak) wrote:
> From: "Zeeshan Ali (Khattak)" <zeeshanak at gnome.org>
>
> Add a new function to allow changing of capacity of storage volumes.
> diff --git a/src/libvirt.c b/src/libvirt.c
> index e9d638b..44865e8 100644
> --- a/src/libvirt.c
> +++ b/src/libvirt.c
> @@ -12927,6 +12927,56 @@ error:
> return NULL;
> }
>
> +/**
> + * virStorageVolResize:
> + * @vol: pointer to storage volume
> + * @capacity: new capacity
> + * @flags: extra flags; not used yet, so callers should always pass 0
> + *
> + * Changes the capacity of the storage volume @vol to @capacity. The new
> + * capacity must not exceed the sum of current capacity of the volume and
> + * remainining free space of its parent pool. Also the new capacity must
> + * be greater than or equal to current allocation of the volume.
> + *
> + * Returns 0 on success, or -1 on error.
> + */
> +int
> +virStorageVolResize(virStorageVolPtr vol,
> + unsigned long long capacity,
> + unsigned int flags)
> +{
> + virConnectPtr conn;
> + VIR_DEBUG("vol=%p", vol);
Include all of the parameters in the debug line "capacity=%llu flags=%x"
> +
> + virResetLastError();
> +
> + if (!VIR_IS_STORAGE_VOL(vol)) {
> + virLibStorageVolError(VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__);
> + virDispatchError(NULL);
> + return -1;
> + }
> +
> + conn = vol->conn;
> +
> + if (conn->flags & VIR_CONNECT_RO) {
> + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
> + goto error;
> + }
> +
> + if (conn->storageDriver && conn->storageDriver->volResize) {
> + int ret;
> + ret = conn->storageDriver->volResize(vol, capacity, flags);
> + if (ret < 0)
> + goto error;
> + return ret;
> + }
> +
> + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
> +
> +error:
> + virDispatchError(vol->conn);
> + return -1;
> +}
> diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
> index 1340b0c..67c113e 100644
> --- a/src/libvirt_public.syms
> +++ b/src/libvirt_public.syms
> @@ -520,6 +520,7 @@ LIBVIRT_0.9.10 {
> global:
> virDomainShutdownFlags;
> virStorageVolWipePattern;
> + virStorageVolResize;
> } LIBVIRT_0.9.9;
There's a whitespace buglet here - running 'make syntax-check' will
detect this sort of thing for you
> virStorageBackendPtr virStorageBackendForType(int type);
> diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
> index d8dc29c..20f5534 100644
> --- a/src/storage/storage_backend_fs.c
> +++ b/src/storage/storage_backend_fs.c
> @@ -1187,6 +1187,21 @@ virStorageBackendFileSystemVolRefresh(virConnectPtr conn,
> return 0;
> }
>
> +/**
> + * Resize a volume
> + */
> +static int
> +virStorageBackendFileSystemVolResize(virConnectPtr conn ATTRIBUTE_UNUSED,
> + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
> + virStorageVolDefPtr vol,
> + unsigned long long capacity,
> + unsigned int flags ATTRIBUTE_UNUSED)
> +{
> + return virStorageFileResize(vol->target.path,
> + vol->target.format,
> + capacity);
> +}
The virStorageFileResize method only works for raw files. So before
calling this, it is neccessary to check vol->target.format to ensure
it == VIR_STORAGE_FILE_RAW. Raise an error for types == FILE_DIR
and for others we should be able to invoke 'qemu-img resize'.
> diff --git a/src/util/storage_file.c b/src/util/storage_file.c
> index ba9cfc5..f84feab 100644
> --- a/src/util/storage_file.c
> +++ b/src/util/storage_file.c
> @@ -931,6 +931,111 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta)
> VIR_FREE(meta);
> }
>
> +static int
> +virStorageFileResizeForFD(const char *path,
> + int fd,
> + int format,
> + unsigned long long capacity)
> +{
> + unsigned char *head = NULL;
> + ssize_t len = STORAGE_MAX_HEAD;
> + int ret = -1;
> + struct stat sb;
> +
> + if (fstat(fd, &sb) < 0) {
> + virReportSystemError(errno,
> + _("cannot stat file '%s'"),
> + path);
> + return -1;
> + }
> +
> + /* No header to probe for directories */
> + if (S_ISDIR(sb.st_mode)) {
> + return 0;
> + }
> +
> + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
> + virReportSystemError(errno, _("cannot seek to start of '%s'"), path);
> + return -1;
> + }
> +
> + if (VIR_ALLOC_N(head, len) < 0) {
> + virReportOOMError();
> + return -1;
> + }
> +
> + if ((len = read(fd, head, len)) < 0) {
> + virReportSystemError(errno, _("cannot read header '%s'"), path);
> + goto cleanup;
> + }
> +
> + if (format == VIR_STORAGE_FILE_AUTO)
> + format = virStorageFileProbeFormatFromBuf(path, head, len);
> +
> + if (format < 0 ||
> + format >= VIR_STORAGE_FILE_LAST) {
> + virReportSystemError(EINVAL, _("unknown storage file format %d"),
> + format);
> + goto cleanup;
> + }
> +
> + if (fileTypeInfo[format].sizeOffset != -1) {
> + unsigned long long *file_capacity;
> +
> + if ((fileTypeInfo[format].sizeOffset + 8) > len)
> + return -1;
> +
> + file_capacity = (unsigned long long *)(head + fileTypeInfo[format].sizeOffset);
> + if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN)
> + *file_capacity = htole64(capacity);
> + else
> + *file_capacity = htobe64(capacity);
> +
> + *file_capacity /= fileTypeInfo[format].sizeMultiplier;
> + }
> +
> + /* Move to start of file to write the header back with adjusted capacity */
> + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
> + virReportSystemError(errno, _("cannot seek to start of '%s'"), path);
> + return -1;
> + }
Hmm, actually I see now that my earlier comment was wrong - you are coping
with non-raw files here. That said, although you were able to implement
this, I'm afraid I don't think we should be doing this. We can't assume
that just twiddling the header field with the size will suffice, because
some formats may need to actually allocate/enlarge extra metadata tables
elsewhere in their file format. So we really need to call out to qemu-img
> +
> + if ((len = write(fd, head, len)) < 0) {
> + virReportSystemError(errno, _("cannot write header '%s'"), path);
> + goto cleanup;
> + }
> +
> + ret = 0;
> +
> +cleanup:
> + VIR_FREE(head);
> + return ret;
> +}
> +
> +/**
> + * virStorageFileResize:
> + *
> + * Change the capacity of the storage file at 'path'.
> + */
> +int
> +virStorageFileResize(const char *path,
> + int format,
> + unsigned long long capacity)
> +{
> + int fd, ret;
> +
> + if ((fd = open(path, O_RDWR)) < 0) {
> + virReportSystemError(errno, _("cannot open file '%s'"), path);
> + return -1;
> + }
> +
> + ret = virStorageFileResizeForFD(path, fd, format, capacity);
> +
> + VIR_FORCE_CLOSE(fd);
> +
> + return ret;
> +}
In light of my comments above, I think we should simply ftruncate(fd, newsize)
here, and handle non-raw files with a separate function
Hmm, actually I wonder if we need to care about sparse vs non-sparse file
resize (ie writing out zeros for the extra space instead of just truncate).
We could achieve this via a flag, or we could add the 'allocation' paramete
to the new API too. I probably tend towards a flag
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
More information about the libvir-list
mailing list