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(a)redhat.com>
---
tools/virsh-volume.c | 25 +++++++++++++++++-
tools/virsh.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh.h | 9 +++++++
tools/virsh.pod | 3 ++-
4 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/tools/virsh-volume.c b/tools/virsh-volume.c
index 018e0f3..9a6a695 100644
--- a/tools/virsh-volume.c
+++ b/tools/virsh-volume.c
@@ -659,6 +659,10 @@ 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}
};
@@ -682,6 +686,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;
+ struct _virshStreamInData cbData;
if (vshCommandOptULongLong(ctl, cmd, "offset", &offset) < 0)
return false;
@@ -705,7 +711,24 @@ cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
- if (virStorageVolUpload(vol, st, offset, length, 0) < 0) {
+ if (vshCommandOptBool(cmd, "sparse")) {
+ flags |= VIR_STORAGE_VOL_UPLOAD_SPARSE_STREAM;
+
+ cbData.ctl = ctl;
+ cbData.fd = fd;
+
+ if (virStreamRegisterInData(st, virshStreamInData, &cbData) < 0) {
+ vshError(ctl, _("cannot register stream inData callback"));
+ goto cleanup;
+ }
+
+ if (virStreamRegisterSkip(st, virshStreamSkip, &fd) < 0) {
+ vshError(ctl, _("cannot register stream skip handling function"));
+ goto cleanup;
+ }
+ }
+
+ if (virStorageVolUpload(vol, st, offset, length, flags) < 0) {
vshError(ctl, _("cannot upload to volume %s"), name);
goto cleanup;
}
diff --git a/tools/virsh.c b/tools/virsh.c
index af4f9d1..b68b8a3 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -265,6 +265,77 @@ 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)
+{
+ struct _virshStreamInData *cbData = opaque;
+ vshControl *ctl = cbData->ctl;
+ int fd = cbData->fd;
+ off_t cur, data, hole;
+ int ret = -1;
+
+ /* 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:
+ /* At any rate, reposition back to where we started. */
+ if (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..cd70699 100644
--- a/tools/virsh.h
+++ b/tools/virsh.h
@@ -153,4 +153,13 @@ int virshStreamSink(virStreamPtr st, const char *bytes, size_t
nbytes,
int virshStreamSkip(virStreamPtr st,
unsigned long long offset, void *opaque);
+struct _virshStreamInData {
+ 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 64f0f5f..56eda28 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -3557,13 +3557,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.1