[libvirt] [PATCH v2 29/32] virsh: Implement sparse stream to vol-upload

Michal Privoznik mprivozn at redhat.com
Mon May 23 15:58:21 UTC 2016


Similarly to previous commit, implement sparse streams feature
for vol-upload. This is, however, slightly different approach,
because we must implement a function that will tell us whether
we are in a data section or in a hole. But there's no magic
hidden in here.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 tools/virsh-volume.c | 37 +++++++++++++++--------
 tools/virsh.c        | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/virsh.h        | 14 +++++++++
 tools/virsh.pod      |  3 +-
 4 files changed, 125 insertions(+), 14 deletions(-)

diff --git a/tools/virsh-volume.c b/tools/virsh-volume.c
index 27744c7..d6434d5 100644
--- a/tools/virsh-volume.c
+++ b/tools/virsh-volume.c
@@ -659,18 +659,13 @@ static const vshCmdOptDef opts_vol_upload[] = {
      .type = VSH_OT_INT,
      .help = N_("amount of data to upload")
     },
+    {.name = "sparse",
+     .type = VSH_OT_BOOL,
+     .help = N_("preserve sparseness of volume")
+    },
     {.name = NULL}
 };
 
-static int
-cmdVolUploadSource(virStreamPtr st ATTRIBUTE_UNUSED,
-                   char *bytes, size_t nbytes, void *opaque)
-{
-    int *fd = opaque;
-
-    return saferead(*fd, bytes, nbytes);
-}
-
 static bool
 cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
 {
@@ -682,6 +677,8 @@ cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
     const char *name = NULL;
     unsigned long long offset = 0, length = 0;
     virshControlPtr priv = ctl->privData;
+    unsigned int flags = 0;
+    virshStreamCallbackData cbData;
 
     if (vshCommandOptULongLong(ctl, cmd, "offset", &offset) < 0)
         return false;
@@ -700,19 +697,33 @@ cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
         goto cleanup;
     }
 
+    cbData.ctl = ctl;
+    cbData.fd = fd;
+
+    if (vshCommandOptBool(cmd, "sparse"))
+        flags |= VIR_STORAGE_VOL_UPLOAD_SPARSE_STREAM;
+
     if (!(st = virStreamNew(priv->conn, 0))) {
         vshError(ctl, _("cannot create a new stream"));
         goto cleanup;
     }
 
-    if (virStorageVolUpload(vol, st, offset, length, 0) < 0) {
+    if (virStorageVolUpload(vol, st, offset, length, flags) < 0) {
         vshError(ctl, _("cannot upload to volume %s"), name);
         goto cleanup;
     }
 
-    if (virStreamSendAll(st, cmdVolUploadSource, &fd) < 0) {
-        vshError(ctl, _("cannot send data to volume %s"), name);
-        goto cleanup;
+    if (flags & VIR_STORAGE_VOL_UPLOAD_SPARSE_STREAM) {
+        if (virStreamSparseSendAll(st, virshStreamSource,
+                                   virshStreamInData, &cbData) < 0) {
+            vshError(ctl, _("cannot send data to volume %s"), name);
+            goto cleanup;
+        }
+    } else {
+        if (virStreamSendAll(st, virshStreamSource, &cbData) < 0) {
+            vshError(ctl, _("cannot send data to volume %s"), name);
+            goto cleanup;
+        }
     }
 
     if (VIR_CLOSE(fd) < 0) {
diff --git a/tools/virsh.c b/tools/virsh.c
index 2f0f46a..12b53d0 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -263,6 +263,17 @@ int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
     return safewrite(*fd, bytes, nbytes);
 }
 
+int
+virshStreamSource(virStreamPtr st ATTRIBUTE_UNUSED,
+                  char *bytes, size_t nbytes, void *opaque)
+{
+    virshStreamCallbackDataPtr cbData = opaque;
+    int fd = cbData->fd;
+
+    return saferead(fd, bytes, nbytes);
+}
+
+
 int virshStreamSkip(virStreamPtr st ATTRIBUTE_UNUSED,
                     unsigned long long offset, void *opaque)
 {
@@ -271,6 +282,80 @@ int virshStreamSkip(virStreamPtr st ATTRIBUTE_UNUSED,
     return lseek(*fd, offset, SEEK_CUR) == (off_t) -1 ? -1 : 0;
 }
 
+int virshStreamInData(virStreamPtr st ATTRIBUTE_UNUSED,
+                      int *inData,
+                      unsigned long long *offset,
+                      void *opaque)
+{
+    virshStreamCallbackDataPtr cbData = opaque;
+    vshControl *ctl = cbData->ctl;
+    int fd = cbData->fd;
+    off_t cur, data, hole;
+    int ret = -1;
+
+    *inData = 0;
+    *offset = 0;
+
+    /* Get current position */
+    cur = lseek(fd, 0, SEEK_CUR);
+    if (cur == (off_t) -1) {
+        vshError(ctl, "%s", _("Unable to get current position in stream"));
+        goto cleanup;
+    }
+
+    /* Now try to get data and hole offsets */
+    data = lseek(fd, cur, SEEK_DATA);
+
+    /* There are four options:
+     * 1) data == cur;  @cur is in data
+     * 2) data > cur; @cur is in a hole, next data at @data
+     * 3) data < 0, errno = ENXIO; either @cur is in trailing hole, or @cur is beyond EOF.
+     * 4) data < 0, errno != ENXIO; we learned nothing
+     */
+
+    if (data == (off_t) -1) {
+        /* cases 3 and 4 */
+        if (errno != ENXIO) {
+            vshError(ctl, "%s", _("Unable to seek to data"));
+            goto cleanup;
+        }
+        *inData = 0;
+        *offset = 0;
+    } else if (data > cur) {
+        /* case 2 */
+        *inData = 0;
+        *offset = data - cur;
+    } else {
+        /* case 1 */
+        *inData = 1;
+
+        /* We don't know where does the next hole start. Let's
+         * find out. Here we get the same 4 possibilities as
+         * described above.*/
+        hole = lseek(fd, data, SEEK_HOLE);
+        if (hole == (off_t) -1 || hole == data) {
+            /* cases 1, 3 and 4 */
+            /* Wait a second. The reason why we are here is
+             * because we are in data. But at the same time we
+             * are in a trailing hole? Wut!? Do the best what we
+             * can do here. */
+            vshError(ctl, "%s", _("unable to seek to hole"));
+            goto cleanup;
+        } else {
+            /* case 2 */
+            *offset = (hole - data);
+        }
+    }
+
+    ret = 0;
+ cleanup:
+    /* If we were in data initially, reposition back. */
+    if (*inData && cur != (off_t) -1)
+        ignore_value(lseek(fd, cur, SEEK_SET));
+    return ret;
+}
+
+
 /* ---------------
  * Command Connect
  * ---------------
diff --git a/tools/virsh.h b/tools/virsh.h
index 5c382ef..1362819 100644
--- a/tools/virsh.h
+++ b/tools/virsh.h
@@ -150,7 +150,21 @@ int virshDomainState(vshControl *ctl, virDomainPtr dom, int *reason);
 int virshStreamSink(virStreamPtr st, const char *bytes, size_t nbytes,
                     void *opaque);
 
+int virshStreamSource(virStreamPtr st,
+                      char *bytes, size_t nbytes, void *opaque);
+
 int virshStreamSkip(virStreamPtr st,
                     unsigned long long offset, void *opaque);
 
+typedef struct _virshStreamCallbackData virshStreamCallbackData;
+typedef virshStreamCallbackData *virshStreamCallbackDataPtr;
+struct _virshStreamCallbackData {
+    vshControl *ctl;
+    int fd;
+};
+
+int virshStreamInData(virStreamPtr st,
+                      int *data,
+                      unsigned long long *offset,
+                      void *opaque);
 #endif /* VIRSH_H */
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 36a15f8..f10fee9 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -3563,13 +3563,14 @@ the storage volume should be deleted as well. Not all storage drivers
 support this option, presently only rbd.
 
 =item B<vol-upload> [I<--pool> I<pool-or-uuid>] [I<--offset> I<bytes>]
-[I<--length> I<bytes>] I<vol-name-or-key-or-path> I<local-file>
+[I<--length> I<bytes>] [I<--sparse>] I<vol-name-or-key-or-path> I<local-file>
 
 Upload the contents of I<local-file> to a storage volume.
 I<--pool> I<pool-or-uuid> is the name or UUID of the storage pool the volume
 is in.
 I<vol-name-or-key-or-path> is the name or key or path of the volume where the
 file will be uploaded.
+If I<--sparse> is specified, this command will preserve volume sparseness.
 I<--offset> is the position in the storage volume at which to start writing
 the data. The value must be 0 or larger. I<--length> is an upper bound
 of the amount of data to be uploaded. A negative value is interpreted
-- 
2.8.3




More information about the libvir-list mailing list