Hi everyone,
   In Boxes we'll need to change the size of the storage volumes (we
use qcow2 files) but turns out that there is no virStorageVolResize()
yet[1]. In my chat with Daniel on IRC, he mentioned that this would be
a trivial task so I thought I should try to do it myself. I've been
looking into this for several hours now and haven't gotten very far. I
guess Daniel overestimated my skills of deciphering complicated code.
:) Attached is my very much WIP patch that at least builds but doesn't
exactly work yet:

virsh # vol-resize 'Microsoft Windows XP.qcow2' '4G' gnome-boxes
error: Failed to change size of volume 'Microsoft Windows XP.qcow2' to 4G

error: this function is not supported by the connection driver:
virStorageVolResize
---------------------

If anyone can have a look and tell me if I'm going anywhere towards
the right direction and what level of indirection I'm missing here,
that would be awesome!

-- 
Regards,

Zeeshan Ali (Khattak)
FSF member#5124

[1] Yes, I know there is a virDomainBlockResize but thats limited to
running domains and I haven't yet figured if it can be nicely binded
in libvirt-glib. Probably we'll want one function in libvirt-glib that
will abstract both virDomainBlockResize() and virStorageVolResize().
diff --git a/src/driver.h b/src/driver.h
index 24636a4..4b0b3d9 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -1252,6 +1252,10 @@ typedef int
                                unsigned long long offset,
                                unsigned long long length,
                                unsigned int flags);
+typedef int
+        (*virDrvStorageVolResize) (virStorageVolPtr vol,
+                                   unsigned long long capacity,
+                                   unsigned int flags);
 
 typedef int
         (*virDrvStoragePoolIsActive)(virStoragePoolPtr pool);
@@ -1313,6 +1317,7 @@ struct _virStorageDriver {
     virDrvStorageVolGetInfo volGetInfo;
     virDrvStorageVolGetXMLDesc volGetXMLDesc;
     virDrvStorageVolGetPath volGetPath;
+    virDrvStorageVolResize volResize;
     virDrvStoragePoolIsActive   poolIsActive;
     virDrvStoragePoolIsPersistent   poolIsPersistent;
 };
diff --git a/src/libvirt.c b/src/libvirt.c
index 7b8adf7..2901234 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -12791,6 +12791,7 @@ virStorageVolGetPath(virStorageVolPtr vol)
         return ret;
     }
 
+    VIR_WARN("driver: %s",conn->storageDriver->name);
     virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
 
 error:
@@ -12798,6 +12799,51 @@ 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);
+
+    virResetLastError();
+
+    if (!VIR_IS_STORAGE_VOL(vol)) {
+        virLibStorageVolError(VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__);
+        virDispatchError(NULL);
+        return -1;
+    }
+
+    conn = vol->conn;
+
+    if (conn->storageDriver && conn->storageDriver->volResize) {
+        int ret;
+        ret = conn->storageDriver->volResize(vol, capacity, flags);
+        if (!ret)
+            goto error;
+        return ret;
+    }
+
+    virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+    virDispatchError(vol->conn);
+    return -1;
+}
 
 /**
  * virNodeNumOfDevices:
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 4ca7216..5155022 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -516,4 +516,9 @@ LIBVIRT_0.9.9 {
         virDomainSetNumaParameters;
 } LIBVIRT_0.9.8;
 
+LIBVIRT_0.9.10 {
+    global:
+	virStorageVolResize;
+} LIBVIRT_0.9.9;
+
 # .... define new API here using predicted next version number ....
diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h
index 75ed676..a37bf7c 100644
--- a/src/storage/storage_backend.h
+++ b/src/storage/storage_backend.h
@@ -44,6 +44,11 @@ typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjP
 typedef int (*virStorageBackendBuildVolFrom)(virConnectPtr conn, virStoragePoolObjPtr pool,
                                              virStorageVolDefPtr origvol, virStorageVolDefPtr newvol,
                                              unsigned int flags);
+typedef int (*virStorageBackendVolumeResize)(virConnectPtr conn,
+                                             virStoragePoolObjPtr pool,
+                                             virStorageVolDefPtr vol,
+                                             unsigned long long capacity,
+                                             unsigned int flags);
 
 /* File creation/cloning functions used for cloning between backends */
 int virStorageBackendCreateRaw(virConnectPtr conn,
@@ -78,6 +83,7 @@ struct _virStorageBackend {
     virStorageBackendCreateVol createVol;
     virStorageBackendRefreshVol refreshVol;
     virStorageBackendDeleteVol deleteVol;
+    virStorageBackendVolumeResize resizeVol;
 };
 
 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);
+}
+
 virStorageBackend virStorageBackendDirectory = {
     .type = VIR_STORAGE_POOL_DIR,
 
@@ -1199,6 +1214,7 @@ virStorageBackend virStorageBackendDirectory = {
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
+    .resizeVol = virStorageBackendFileSystemVolResize,
 };
 
 #if WITH_STORAGE_FS
@@ -1216,6 +1232,7 @@ virStorageBackend virStorageBackendFileSystem = {
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
+    .resizeVol = virStorageBackendFileSystemVolResize,
 };
 virStorageBackend virStorageBackendNetFileSystem = {
     .type = VIR_STORAGE_POOL_NETFS,
@@ -1232,5 +1249,6 @@ virStorageBackend virStorageBackendNetFileSystem = {
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
+    .resizeVol = virStorageBackendFileSystemVolResize,
 };
 #endif /* WITH_STORAGE_FS */
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index 8c2d6e1..dda1bab 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1695,7 +1695,74 @@ out:
     return ret;
 }
 
+static int
+storageVolumeResize(virStorageVolPtr obj,
+                    unsigned long long capacity,
+                    unsigned int flags)
+{
+    virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
+    virStorageBackendPtr backend;
+    virStoragePoolObjPtr pool = NULL;
+    virStorageVolDefPtr vol = NULL;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    storageDriverLock(driver);
+    pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
+    storageDriverUnlock(driver);
+
+    if (!pool) {
+        virStorageReportError(VIR_ERR_NO_STORAGE_POOL,
+                              "%s", _("no storage pool with matching uuid"));
+        goto out;
+    }
 
+    if (!virStoragePoolObjIsActive(pool)) {
+        virStorageReportError(VIR_ERR_OPERATION_INVALID,
+                              "%s", _("storage pool is not active"));
+        goto out;
+    }
+
+    if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
+        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_OPERATION_INVALID,
+                              _("volume '%s' is still being allocated."),
+                              vol->name);
+        goto out;
+    }
+
+    if (!backend->resizeVol) {
+        virStorageReportError(VIR_ERR_NO_SUPPORT,
+                              "%s", _("storage pool does not support changing "
+                                      "of volume capacity"));
+
+        goto out;
+    }
+
+    if (backend->resizeVol(obj->conn, pool, vol, capacity, flags) < 0)
+        goto out;
+
+   vol->capacity = capacity;
+   ret = 0;
+
+out:
+    if (pool)
+        virStoragePoolObjUnlock(pool);
+
+    return ret;
+}
 
 /* If the volume we're wiping is already a sparse file, we simply
  * truncate and extend it to its original size, filling it with
@@ -2178,6 +2245,7 @@ static virStorageDriver storageDriver = {
     .volGetInfo = storageVolumeGetInfo, /* 0.4.0 */
     .volGetXMLDesc = storageVolumeGetXMLDesc, /* 0.4.0 */
     .volGetPath = storageVolumeGetPath, /* 0.4.0 */
+    .volResize = storageVolumeResize, /* 0.9.10 */
 
     .poolIsActive = storagePoolIsActive, /* 0.7.3 */
     .poolIsPersistent = storagePoolIsPersistent, /* 0.7.3 */
diff --git a/src/util/storage_file.c b/src/util/storage_file.c
index ba9cfc5..6a654a8 100644
--- a/src/util/storage_file.c
+++ b/src/util/storage_file.c
@@ -931,6 +931,107 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta)
     VIR_FREE(meta);
 }
 
+int
+virStorageFileResizeFromFD(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;
+    }
+
+    if ((len = write(fd, head, len)) < 0) {
+        virReportSystemError(errno, _("cannot write header '%s'"), path);
+        goto cleanup;
+    }
+
+cleanup:
+    VIR_FREE(head);
+    return ret;
+}
+
+/**
+ * virStorageFileResize:
+ */
+int
+virStorageFileResize(const char *path,
+                     int format,
+                     unsigned long long capacity)
+{
+    int fd, ret;
+
+    if ((fd = open(path, O_RDONLY)) < 0) {
+        virReportSystemError(errno, _("cannot open file '%s'"), path);
+        return -1;
+    }
+
+    ret = virStorageFileResizeFromFD(path, fd, format, capacity);
+
+    VIR_FORCE_CLOSE(fd);
+
+    return ret;
+}
+
 #ifdef __linux__
 
 # ifndef NFS_SUPER_MAGIC
diff --git a/src/util/storage_file.h b/src/util/storage_file.h
index b8920d0..02716f2 100644
--- a/src/util/storage_file.h
+++ b/src/util/storage_file.h
@@ -72,6 +72,14 @@ int virStorageFileGetMetadataFromFD(const char *path,
 
 void virStorageFileFreeMetadata(virStorageFileMetadata *meta);
 
+int virStorageFileResize(const char *path,
+                         int format,
+                         unsigned long long capacity);
+int virStorageFileResizeFromFD(const char *path,
+                               int fd,
+                               int format,
+                               unsigned long long capacity);
+
 enum {
     VIR_STORAGE_FILE_SHFS_NFS = (1 << 0),
     VIR_STORAGE_FILE_SHFS_GFS2 = (1 << 1),
diff --git a/tools/virsh.c b/tools/virsh.c
index d635b56..e9a40a2 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -11209,6 +11209,58 @@ cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
     return ret;
 }
 
+/*
+ * "vol-resize" command
+ */
+static const vshCmdInfo info_vol_resize[] = {
+    {"help", N_("resize a vol")},
+    {"desc", N_("Resizes a storage vol.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vol_resize[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new capacity for the vol with optional k,M,G,T suffix")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVolResize(vshControl *ctl, const vshCmd *cmd)
+{
+    virStorageVolPtr vol;
+    const char *capacityStr = NULL;
+    unsigned long long capacity = 0;
+    bool ret = true;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
+        goto cleanup;
+    if (cmdVolSize(capacityStr, &capacity) < 0) {
+        vshError(ctl, _("Malformed size %s"), capacityStr);
+        goto cleanup;
+    }
+
+    if (virStorageVolResize(vol, capacity, 0) == 0) {
+        vshPrint(ctl, "Size of volume '%s' successfully changed to %s\n",
+                 virStorageVolGetName(vol), capacityStr);
+        ret = true;
+    } else {
+        vshError(ctl, "Failed to change size of volume '%s' to %s\n",
+                 virStorageVolGetName(vol), capacityStr);
+        ret = false;
+    }
+
+cleanup:
+    virStorageVolFree(vol);
+    return ret;
+}
+
 
 /*
  * "vol-dumpxml" command
@@ -16065,6 +16117,7 @@ static const vshCmdDef storageVolCmds[] = {
     {"vol-pool", cmdVolPool, opts_vol_pool, info_vol_pool, 0},
     {"vol-upload", cmdVolUpload, opts_vol_upload, info_vol_upload, 0},
     {"vol-wipe", cmdVolWipe, opts_vol_wipe, info_vol_wipe, 0},
+    {"vol-resize", cmdVolResize, opts_vol_resize, info_vol_resize, 0},
     {NULL, NULL, NULL, NULL, 0}
 };
 
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to