[libvirt] [libvirt-perl][PATCH 0/7] Implement sparse streams

*** BLURB HERE *** Michal Privoznik (7): Fix send_all() callback helper Introduce flags to Stream::recv() Introduce Stream::recvHole() and Stream::sendHole() Introduce Stream::sparse_recv_all() Introduce Stream::sparse_send_all() Register VOL_DOWNLOAD_SPARSE_STREAM & VOL_UPLOAD_SPARSE_STREAM constants examples: Introduce vol-sparse.pl Changes | 9 ++ Virt.xs | 241 ++++++++++++++++++++++++++++++++++++++++++++- examples/vol-sparse.pl | 142 ++++++++++++++++++++++++++ lib/Sys/Virt/StorageVol.pm | 30 +++++- lib/Sys/Virt/Stream.pm | 70 ++++++++++++- t/030-api-coverage.t | 3 + 6 files changed, 483 insertions(+), 12 deletions(-) create mode 100755 examples/vol-sparse.pl -- 2.13.0

Sys::Virt::virStream->send_all() uses virStreamSendAll() under the hood. This function takes one callback to fill the send buffer with stream data. We have a C glue callback that eventually calls the perl one. However, there's a problem with the glue callback mangling the data as it mistakenly uses strcpy() instead of memcpy(). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 1 + Virt.xs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 2e5bfe4..2e4a99e 100644 --- a/Changes +++ b/Changes @@ -3,6 +3,7 @@ Revision history for perl module Sys::Virt 3.4.0 2017-06-00 - Add LIST_CAP_MDEV & LIST_CAP_MDEV_TYPES constants + - Fix send_all() callback helper 3.3.0 2017-05-08 diff --git a/Virt.xs b/Virt.xs index 9ccdc1f..a041c95 100644 --- a/Virt.xs +++ b/Virt.xs @@ -1948,7 +1948,7 @@ _stream_send_all_source(virStreamPtr st, const char *newdata = SvPV_nolen(datasv); if (ret > nbytes) ret = nbytes; - strncpy(data, newdata, nbytes); + memcpy(data, newdata, nbytes); } FREETMPS; -- 2.13.0

At the same time register RECV_STOP_AT_HOLE constant. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 2 ++ Virt.xs | 13 ++++++++++--- lib/Sys/Virt/Stream.pm | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Changes b/Changes index 2e4a99e..b4a493c 100644 --- a/Changes +++ b/Changes @@ -4,6 +4,8 @@ Revision history for perl module Sys::Virt - Add LIST_CAP_MDEV & LIST_CAP_MDEV_TYPES constants - Fix send_all() callback helper + - Introduce flags to Stream::recv() and + register RECV_STOP_AT_HOLE constant 3.3.0 2017-05-08 diff --git a/Virt.xs b/Virt.xs index a041c95..498e711 100644 --- a/Virt.xs +++ b/Virt.xs @@ -7874,16 +7874,21 @@ send(st, data, nbytes) int -recv(st, data, nbytes) +recv(st, data, nbytes, flags=0) virStreamPtr st; SV *data; size_t nbytes; + unsigned int flags; PREINIT: char *rawdata; CODE: Newx(rawdata, nbytes, char); - if ((RETVAL = virStreamRecv(st, rawdata, nbytes)) < 0 && - RETVAL != -2) { + if (flags) + RETVAL = virStreamRecvFlags(st, rawdata, nbytes, flags); + else + RETVAL = virStreamRecv(st, rawdata, nbytes); + + if (RETVAL != -2 && RETVAL != -3) { Safefree(rawdata); _croak_error(); } @@ -9010,6 +9015,8 @@ BOOT: REGISTER_CONSTANT(VIR_STREAM_EVENT_ERROR, EVENT_ERROR); REGISTER_CONSTANT(VIR_STREAM_EVENT_HANGUP, EVENT_HANGUP); + REGISTER_CONSTANT(VIR_STREAM_RECV_STOP_AT_HOLE, RECV_STOP_AT_HOLE); + stash = gv_stashpv( "Sys::Virt::Error", TRUE ); diff --git a/lib/Sys/Virt/Stream.pm b/lib/Sys/Virt/Stream.pm index 4022c84..069895e 100644 --- a/lib/Sys/Virt/Stream.pm +++ b/lib/Sys/Virt/Stream.pm @@ -69,11 +69,23 @@ be called on any stream which has been activated Complete I/O on the stream. Either this function or C<abort> must be called on any stream which has been activated -=item $rv = $st->recv($data, $nbytes) +=item $rv = $st->recv($data, $nbytes, $flags=0) -Receive upto C<$nbytes> worth of data, copying into C<$data>. -Returns the number of bytes read, or -2 if I/O would block, -or -1 on error. +Receive up to C<$nbytes> worth of data, copying into C<$data>. +Returns the number of bytes read, or -3 if hole is reached and +C<$flags> contains RECV_STOP_AT_HOLE, or -2 if I/O would block, +or -1 on error. The C<$flags> parameter accepts the following +flags: + +=over 4 + +=item Sys::Virt::Stream::RECV_STOP_AT_HOLE + +If this flag is set, the C<recv> function will stop reading from +stream if it has reached a hole. In that case, -3 is returned and +C<recvHole> should be called to get the hole size. + +=back =item $rv = $st->send($data, $nbytes) -- 2.13.0

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 1 + Virt.xs | 28 ++++++++++++++++++++++++++++ lib/Sys/Virt/Stream.pm | 17 +++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/Changes b/Changes index b4a493c..c92c271 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,7 @@ Revision history for perl module Sys::Virt - Fix send_all() callback helper - Introduce flags to Stream::recv() and register RECV_STOP_AT_HOLE constant + - Introduce Stream::recvHole() and Stream::sendHole() 3.3.0 2017-05-08 diff --git a/Virt.xs b/Virt.xs index 498e711..d112708 100644 --- a/Virt.xs +++ b/Virt.xs @@ -7900,6 +7900,34 @@ recv(st, data, nbytes, flags=0) RETVAL +SV * +recvHole(st, flags=0) + virStreamPtr st; + unsigned int flags; + PREINIT: + long long length; + CODE: + if (virStreamRecvHole(st, &length, flags) < 0) + _croak_error(); + + RETVAL = virt_newSVll(length); + OUTPUT: + RETVAL + + +void +sendHole(st, lengthSV, flags=0) + virStreamPtr st; + SV *lengthSV; + unsigned int flags; + PREINIT: + long long length; + PPCODE: + length = virt_SvIVll(lengthSV); + if (virStreamSendHole(st, length, flags) < 0) + _croak_error(); + + void send_all(stref, handler) SV *stref; diff --git a/lib/Sys/Virt/Stream.pm b/lib/Sys/Virt/Stream.pm index 069895e..5984fb6 100644 --- a/lib/Sys/Virt/Stream.pm +++ b/lib/Sys/Virt/Stream.pm @@ -93,6 +93,23 @@ Send upto C<$nbytes> worth of data, copying from C<$data>. Returns the number of bytes sent, or -2 if I/O would block, or -1 on error. +=item $rv = $st->recvHole($flags=0) + +Determine the amount of the empty space (in bytes) to be created +in a stream's target file when uploading or downloading sparsely +populated files. This is the counterpart to C<sendHole>. The +optional C<$flags> parameter is currently unused and defaults to +zero if omitted. + +=item $st->sendHole($length, $flags=0) + +Rather than transmitting empty file space, this method directs +the stream target to create C<$length> bytes of empty space. +This method would be used when uploading or downloading sparsely +populated files to avoid the needless copy of empty file space. +The optional C<$flags> parameter is currently unused and defaults +to zero if omitted. + =item $st->recv_all($handler) Receive all data available from the stream, invoking -- 2.13.0

On Tue, May 23, 2017 at 05:05:37PM +0200, Michal Privoznik wrote:
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 1 + Virt.xs | 28 ++++++++++++++++++++++++++++ lib/Sys/Virt/Stream.pm | 17 +++++++++++++++++ 3 files changed, 46 insertions(+)
diff --git a/Changes b/Changes index b4a493c..c92c271 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,7 @@ Revision history for perl module Sys::Virt - Fix send_all() callback helper - Introduce flags to Stream::recv() and register RECV_STOP_AT_HOLE constant + - Introduce Stream::recvHole() and Stream::sendHole()
3.3.0 2017-05-08
diff --git a/Virt.xs b/Virt.xs index 498e711..d112708 100644 --- a/Virt.xs +++ b/Virt.xs @@ -7900,6 +7900,34 @@ recv(st, data, nbytes, flags=0) RETVAL
+SV * +recvHole(st, flags=0) + virStreamPtr st; + unsigned int flags; + PREINIT: + long long length; + CODE: + if (virStreamRecvHole(st, &length, flags) < 0) + _croak_error(); + + RETVAL = virt_newSVll(length); + OUTPUT: + RETVAL + + +void +sendHole(st, lengthSV, flags=0) + virStreamPtr st; + SV *lengthSV; + unsigned int flags; + PREINIT: + long long length; + PPCODE: + length = virt_SvIVll(lengthSV); + if (virStreamSendHole(st, length, flags) < 0) + _croak_error(); + + void send_all(stref, handler) SV *stref; diff --git a/lib/Sys/Virt/Stream.pm b/lib/Sys/Virt/Stream.pm index 069895e..5984fb6 100644 --- a/lib/Sys/Virt/Stream.pm +++ b/lib/Sys/Virt/Stream.pm @@ -93,6 +93,23 @@ Send upto C<$nbytes> worth of data, copying from C<$data>. Returns the number of bytes sent, or -2 if I/O would block, or -1 on error.
+=item $rv = $st->recvHole($flags=0) + +Determine the amount of the empty space (in bytes) to be created +in a stream's target file when uploading or downloading sparsely +populated files. This is the counterpart to C<sendHole>. The +optional C<$flags> parameter is currently unused and defaults to +zero if omitted. + +=item $st->sendHole($length, $flags=0) + +Rather than transmitting empty file space, this method directs +the stream target to create C<$length> bytes of empty space. +This method would be used when uploading or downloading sparsely +populated files to avoid the needless copy of empty file space. +The optional C<$flags> parameter is currently unused and defaults +to zero if omitted.
These methods should use '_' rather than camelCase in naming. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 1 + Virt.xs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/Sys/Virt/Stream.pm | 14 ++++++++++ t/030-api-coverage.t | 1 + 4 files changed, 86 insertions(+) diff --git a/Changes b/Changes index c92c271..4c1e071 100644 --- a/Changes +++ b/Changes @@ -7,6 +7,7 @@ Revision history for perl module Sys::Virt - Introduce flags to Stream::recv() and register RECV_STOP_AT_HOLE constant - Introduce Stream::recvHole() and Stream::sendHole() + - Introduce Stream::sparse_recv_all() 3.3.0 2017-05-08 diff --git a/Virt.xs b/Virt.xs index d112708..002bd6a 100644 --- a/Virt.xs +++ b/Virt.xs @@ -2008,6 +2008,48 @@ _stream_recv_all_sink(virStreamPtr st, } +static int +_stream_sparse_recv_holeHandler(virStreamPtr st, + long long offset, + void *opaque) +{ + AV *av = opaque; + SV **self; + SV **holeHandler; + int rv; + int ret; + dSP; + + self = av_fetch(av, 0, 0); + holeHandler = av_fetch(av, 2, 0); + + SvREFCNT_inc(*self); + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(*self); + XPUSHs(sv_2mortal(virt_newSVll(offset))); + PUTBACK; + + rv = call_sv((SV*)*holeHandler, G_SCALAR); + + SPAGAIN; + + if (rv == 1) { + ret = POPi; + } else { + ret = -1; + } + + FREETMPS; + LEAVE; + + return ret; +} + + MODULE = Sys::Virt PACKAGE = Sys::Virt PROTOTYPES: ENABLE @@ -7972,6 +8014,34 @@ recv_all(stref, handler) SvREFCNT_dec(opaque); +void +sparse_recv_all(stref, handler, holeHandler) + SV *stref; + SV *handler; + SV *holeHandler; + PREINIT: + AV *opaque; + virStreamPtr st; + CODE: + st = (virStreamPtr)SvIV((SV*)SvRV(stref)); + + opaque = newAV(); + SvREFCNT_inc(stref); + SvREFCNT_inc(handler); + SvREFCNT_inc(holeHandler); + av_push(opaque, stref); + av_push(opaque, handler); + av_push(opaque, holeHandler); + + if (virStreamSparseRecvAll(st, + _stream_recv_all_sink, + _stream_sparse_recv_holeHandler, + opaque) < 0) + _croak_error(); + + SvREFCNT_dec(opaque); + + void add_callback(stref, events, cb) SV* stref; diff --git a/lib/Sys/Virt/Stream.pm b/lib/Sys/Virt/Stream.pm index 5984fb6..c32b957 100644 --- a/lib/Sys/Virt/Stream.pm +++ b/lib/Sys/Virt/Stream.pm @@ -130,6 +130,20 @@ data byte count desired. The function should return the number of bytes filled, 0 on end of file, or -1 upon error +=item $st->sparse_recv_all($handler, $holeHandler) + +Receive all data available from the sparse stream, invoking +C<$handler> to process the data. The C<$handler> parameter must +be a function which expects three arguments, the C<$st> stream +object, a scalar containing the data received and a data byte +count. The function should return the number of bytes processed, +or -1 upon error. The second argument C<$holeHandler> is a +function which expects two arguments: the C<$st> stream and a +scalar, number describing the size of the hole in the stream (in +bytes). The C<$holeHandler> is expected to return a non-negative +number on success (usually 0) and a negative number (usually -1) +otherwise. + =item $st->add_callback($events, $coderef) Register a callback to be invoked whenever the stream has diff --git a/t/030-api-coverage.t b/t/030-api-coverage.t index 3049713..6a281ba 100644 --- a/t/030-api-coverage.t +++ b/t/030-api-coverage.t @@ -114,6 +114,7 @@ virEventUpdateTimeoutFunc virStreamEventCallback virStreamSinkFunc +virStreamSinkHoleFunc virStreamSourceFunc virConnectCloseFunc -- 2.13.0

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 1 + Virt.xs | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/Sys/Virt/Stream.pm | 19 ++++++++ t/030-api-coverage.t | 2 + 4 files changed, 146 insertions(+) diff --git a/Changes b/Changes index 4c1e071..989b795 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,7 @@ Revision history for perl module Sys::Virt register RECV_STOP_AT_HOLE constant - Introduce Stream::recvHole() and Stream::sendHole() - Introduce Stream::sparse_recv_all() + - Introduce Stream::sparse_send_all() 3.3.0 2017-05-08 diff --git a/Virt.xs b/Virt.xs index 002bd6a..fc282b8 100644 --- a/Virt.xs +++ b/Virt.xs @@ -1960,6 +1960,100 @@ _stream_send_all_source(virStreamPtr st, } +static int +_stream_sparse_send_all_holeHandler(virStreamPtr st, + int *inData, + long long *length, + void *opaque) +{ + AV *av = opaque; + SV **self; + SV **holeHandler; + SV *inDataSV; + SV *lengthSV; + int count; + int ret; + dSP; + + self = av_fetch(av, 0, 0); + holeHandler = av_fetch(av, 2, 0); + + SvREFCNT_inc(*self); + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(*self); + PUTBACK; + + count = call_sv((SV*)*holeHandler, G_ARRAY); + + SPAGAIN; + + if (count == 2) { + /* @holeHandler returns (inData, length), but on a stack. + * Therefore the order is reversed. */ + lengthSV = POPs; + inDataSV = POPs; + *inData = virt_SvIVll(inDataSV); + *length = virt_SvIVll(lengthSV); + ret = 0; + } else { + ret = -1; + } + + PUTBACK; + FREETMPS; + LEAVE; + + return ret; +} + + +static int +_stream_sparse_send_all_skipHandler(virStreamPtr st, + long long length, + void *opaque) +{ + AV *av = opaque; + SV **self; + SV **skipHandler; + int rv; + int ret; + dSP; + + self = av_fetch(av, 0, 0); + skipHandler = av_fetch(av, 3, 0); + + SvREFCNT_inc(*self); + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(*self); + XPUSHs(sv_2mortal(virt_newSVll(length))); + PUTBACK; + + rv = call_sv((SV*)*skipHandler, G_SCALAR); + + SPAGAIN; + + if (rv == 1) { + ret = POPi; + } else { + ret = -1; + } + + PUTBACK; + FREETMPS; + LEAVE; + + return ret; +} + + static int _stream_recv_all_sink(virStreamPtr st, const char *data, @@ -8041,6 +8135,36 @@ sparse_recv_all(stref, handler, holeHandler) SvREFCNT_dec(opaque); +void +sparse_send_all(stref, handler, holeHandler, skipHandler) + SV *stref; + SV *handler; + SV *holeHandler; + SV *skipHandler; + PREINIT: + AV *opaque; + virStreamPtr st; + CODE: + st = (virStreamPtr)SvIV((SV*)SvRV(stref)); + + opaque = newAV(); + SvREFCNT_inc(stref); + SvREFCNT_inc(handler); + SvREFCNT_inc(holeHandler); + SvREFCNT_inc(skipHandler); + av_push(opaque, stref); + av_push(opaque, handler); + av_push(opaque, holeHandler); + av_push(opaque, skipHandler); + + if (virStreamSparseSendAll(st, + _stream_send_all_source, + _stream_sparse_send_all_holeHandler, + _stream_sparse_send_all_skipHandler, + opaque) < 0) + _croak_error(); + + SvREFCNT_dec(opaque); void add_callback(stref, events, cb) diff --git a/lib/Sys/Virt/Stream.pm b/lib/Sys/Virt/Stream.pm index c32b957..e3b25a5 100644 --- a/lib/Sys/Virt/Stream.pm +++ b/lib/Sys/Virt/Stream.pm @@ -144,6 +144,25 @@ bytes). The C<$holeHandler> is expected to return a non-negative number on success (usually 0) and a negative number (usually -1) otherwise. +=item $st->sparse_send_all($handler, $holeHandler, $skipHandler) + +Send all data produced by C<$handler> to the stream. The +C<$handler> parameter must be a function which expects three +arguments, the C<$st> stream object, a scalar which must be +filled with data and a maximum data byte count desired. The +function should return the number of bytes filled, 0 on end of +file, or -1 upon error. The second argument C<$holeHandler> is a +function expecting just one argument C<$st> and returning an +array of two elements (C<$inData>, C<$sectionLen>) where +C<$inData> has zero or non-zero value if underlying file is in a +hole or data section respectively. The C<$sectionLen> then is the +number of remaining bytes in the current section in the +underlying file. Finally, the third C<$skipHandler> is a function +expecting two arguments C<$st> and C<$length> which moves cursor +in the underlying file for C<$length> bytes. The C<$skipHandler> +is expected to return a non-negative number on success (usually +0) and a negative number (usually -1) otherwise. + =item $st->add_callback($events, $coderef) Register a callback to be invoked whenever the stream has diff --git a/t/030-api-coverage.t b/t/030-api-coverage.t index 6a281ba..95bbd11 100644 --- a/t/030-api-coverage.t +++ b/t/030-api-coverage.t @@ -116,6 +116,8 @@ virStreamEventCallback virStreamSinkFunc virStreamSinkHoleFunc virStreamSourceFunc +virStreamSourceHoleFunc +virStreamSourceSkipFunc virConnectCloseFunc -- 2.13.0

On Tue, May 23, 2017 at 05:05:39PM +0200, Michal Privoznik wrote:
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 1 + Virt.xs | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/Sys/Virt/Stream.pm | 19 ++++++++ t/030-api-coverage.t | 2 + 4 files changed, 146 insertions(+)
diff --git a/Changes b/Changes index 4c1e071..989b795 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,7 @@ Revision history for perl module Sys::Virt register RECV_STOP_AT_HOLE constant - Introduce Stream::recvHole() and Stream::sendHole() - Introduce Stream::sparse_recv_all() + - Introduce Stream::sparse_send_all()
3.3.0 2017-05-08
diff --git a/Virt.xs b/Virt.xs index 002bd6a..fc282b8 100644 --- a/Virt.xs +++ b/Virt.xs @@ -1960,6 +1960,100 @@ _stream_send_all_source(virStreamPtr st, }
+static int +_stream_sparse_send_all_holeHandler(virStreamPtr st, + int *inData, + long long *length, + void *opaque)
Use _handler, rather than Handler - and the same elsewhere in this patch.
+{ + AV *av = opaque; + SV **self; + SV **holeHandler; + SV *inDataSV; + SV *lengthSV; + int count; + int ret; + dSP; + + self = av_fetch(av, 0, 0); + holeHandler = av_fetch(av, 2, 0); + + SvREFCNT_inc(*self); + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(*self); + PUTBACK; + + count = call_sv((SV*)*holeHandler, G_ARRAY); + + SPAGAIN; + + if (count == 2) { + /* @holeHandler returns (inData, length), but on a stack. + * Therefore the order is reversed. */ + lengthSV = POPs; + inDataSV = POPs; + *inData = virt_SvIVll(inDataSV); + *length = virt_SvIVll(lengthSV); + ret = 0; + } else { + ret = -1; + } + + PUTBACK; + FREETMPS; + LEAVE; + + return ret; +} + + +static int +_stream_sparse_send_all_skipHandler(virStreamPtr st, + long long length, + void *opaque) +{ + AV *av = opaque; + SV **self; + SV **skipHandler; + int rv; + int ret; + dSP; + + self = av_fetch(av, 0, 0); + skipHandler = av_fetch(av, 3, 0); + + SvREFCNT_inc(*self); + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(*self); + XPUSHs(sv_2mortal(virt_newSVll(length))); + PUTBACK; + + rv = call_sv((SV*)*skipHandler, G_SCALAR); + + SPAGAIN; + + if (rv == 1) { + ret = POPi; + } else { + ret = -1; + } + + PUTBACK; + FREETMPS; + LEAVE; + + return ret; +} + + static int _stream_recv_all_sink(virStreamPtr st, const char *data, @@ -8041,6 +8135,36 @@ sparse_recv_all(stref, handler, holeHandler)
SvREFCNT_dec(opaque);
+void +sparse_send_all(stref, handler, holeHandler, skipHandler) + SV *stref; + SV *handler; + SV *holeHandler; + SV *skipHandler; + PREINIT: + AV *opaque; + virStreamPtr st; + CODE: + st = (virStreamPtr)SvIV((SV*)SvRV(stref)); + + opaque = newAV(); + SvREFCNT_inc(stref); + SvREFCNT_inc(handler); + SvREFCNT_inc(holeHandler); + SvREFCNT_inc(skipHandler); + av_push(opaque, stref); + av_push(opaque, handler); + av_push(opaque, holeHandler); + av_push(opaque, skipHandler); + + if (virStreamSparseSendAll(st, + _stream_send_all_source, + _stream_sparse_send_all_holeHandler, + _stream_sparse_send_all_skipHandler, + opaque) < 0) + _croak_error(); + + SvREFCNT_dec(opaque);
void add_callback(stref, events, cb) diff --git a/lib/Sys/Virt/Stream.pm b/lib/Sys/Virt/Stream.pm index c32b957..e3b25a5 100644 --- a/lib/Sys/Virt/Stream.pm +++ b/lib/Sys/Virt/Stream.pm @@ -144,6 +144,25 @@ bytes). The C<$holeHandler> is expected to return a non-negative number on success (usually 0) and a negative number (usually -1) otherwise.
+=item $st->sparse_send_all($handler, $holeHandler, $skipHandler) + +Send all data produced by C<$handler> to the stream. The +C<$handler> parameter must be a function which expects three +arguments, the C<$st> stream object, a scalar which must be +filled with data and a maximum data byte count desired. The +function should return the number of bytes filled, 0 on end of +file, or -1 upon error. The second argument C<$holeHandler> is a +function expecting just one argument C<$st> and returning an +array of two elements (C<$inData>, C<$sectionLen>) where +C<$inData> has zero or non-zero value if underlying file is in a +hole or data section respectively. The C<$sectionLen> then is the +number of remaining bytes in the current section in the +underlying file. Finally, the third C<$skipHandler> is a function +expecting two arguments C<$st> and C<$length> which moves cursor +in the underlying file for C<$length> bytes. The C<$skipHandler> +is expected to return a non-negative number on success (usually +0) and a negative number (usually -1) otherwise. + =item $st->add_callback($events, $coderef)
Register a callback to be invoked whenever the stream has diff --git a/t/030-api-coverage.t b/t/030-api-coverage.t index 6a281ba..95bbd11 100644 --- a/t/030-api-coverage.t +++ b/t/030-api-coverage.t @@ -116,6 +116,8 @@ virStreamEventCallback virStreamSinkFunc virStreamSinkHoleFunc virStreamSourceFunc +virStreamSourceHoleFunc +virStreamSourceSkipFunc
virConnectCloseFunc
-- 2.13.0
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 2 ++ Virt.xs | 4 ++++ lib/Sys/Virt/StorageVol.pm | 30 ++++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Changes b/Changes index 989b795..6c671c0 100644 --- a/Changes +++ b/Changes @@ -9,6 +9,8 @@ Revision history for perl module Sys::Virt - Introduce Stream::recvHole() and Stream::sendHole() - Introduce Stream::sparse_recv_all() - Introduce Stream::sparse_send_all() + - Register VOL_DOWNLOAD_SPARSE_STREAM & + VOL_UPLOAD_SPARSE_STREAM constants 3.3.0 2017-05-08 diff --git a/Virt.xs b/Virt.xs index fc282b8..663571c 100644 --- a/Virt.xs +++ b/Virt.xs @@ -9209,6 +9209,10 @@ BOOT: REGISTER_CONSTANT(VIR_STORAGE_VOL_USE_ALLOCATION, USE_ALLOCATION); REGISTER_CONSTANT(VIR_STORAGE_VOL_GET_PHYSICAL, GET_PHYSICAL); + REGISTER_CONSTANT(VIR_STORAGE_VOL_DOWNLOAD_SPARSE_STREAM, VOL_DOWNLOAD_SPARSE_STREAM); + + REGISTER_CONSTANT(VIR_STORAGE_VOL_UPLOAD_SPARSE_STREAM, VOL_UPLOAD_SPARSE_STREAM); + stash = gv_stashpv( "Sys::Virt::Secret", TRUE ); REGISTER_CONSTANT(VIR_SECRET_USAGE_TYPE_NONE, USAGE_TYPE_NONE); REGISTER_CONSTANT(VIR_SECRET_USAGE_TYPE_VOLUME, USAGE_TYPE_VOLUME); diff --git a/lib/Sys/Virt/StorageVol.pm b/lib/Sys/Virt/StorageVol.pm index 42f10e8..82c3df2 100644 --- a/lib/Sys/Virt/StorageVol.pm +++ b/lib/Sys/Virt/StorageVol.pm @@ -141,17 +141,39 @@ Return the physical size in allocation =back -=item $vol->download($st, $offset, $length); +=item $vol->download($st, $offset, $length, $flags=0); Download data from C<$vol> using the stream C<$st>. If C<$offset> and C<$length> are non-zero, then restrict data to the specified -volume byte range. +volume byte range. The C<$flags> accept the following values: -=item $vol->upload($st, $offset, $length); +=over 4 + +=item Sys::Virt::StorageVol::VOL_DOWNLOAD_SPARSE_STREAM + +If this flag is is set in @flags effective transmission of holes +is enabled. This assumes using the stream C<$st> with combination of +C<sparse_recv_all> or C<recv($flags = +VIR_STREAM_RECV_STOP_AT_HOLE)> for honouring holes sent by server. + +=back + +=item $vol->upload($st, $offset, $length, $flags=0); Upload data to C<$vol> using the stream C<$st>. If C<$offset> and C<$length> are non-zero, then restrict data to the specified -volume byte range. +volume byte range. The C<$flags> accept the following values: + +=over 4 + +=item Sys::Virt::StorageVol::VOL_UPLOAD_SPARSE_STREAM + +If this is set in C<$flags> effective transmission of holes is +enabled. This assumes using the stream C<$st> with combination of +C<sparse_send_all> or C<send_hole> to preserve source file +sparseness. + +=back =back -- 2.13.0

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Changes | 1 + examples/vol-sparse.pl | 142 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100755 examples/vol-sparse.pl diff --git a/Changes b/Changes index 6c671c0..3742a8c 100644 --- a/Changes +++ b/Changes @@ -11,6 +11,7 @@ Revision history for perl module Sys::Virt - Introduce Stream::sparse_send_all() - Register VOL_DOWNLOAD_SPARSE_STREAM & VOL_UPLOAD_SPARSE_STREAM constants + - Add vol-sparse.pl example 3.3.0 2017-05-08 diff --git a/examples/vol-sparse.pl b/examples/vol-sparse.pl new file mode 100755 index 0000000..b99b57f --- /dev/null +++ b/examples/vol-sparse.pl @@ -0,0 +1,142 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Sys::Virt; +use Fcntl; + +my $FILE; + +sub downloadHandler { + my $st = shift; + my $data = shift; + my $nbytes = shift; + return syswrite FILE, $data, $nbytes; +} + +sub downloadHoleHandler { + my $st = shift; + my $offset = shift; + my $pos = sysseek FILE, $offset, Fcntl::SEEK_CUR or die "Unable to seek in $FILE: $!"; + truncate FILE, $pos; +} + + +sub download { + my $vol = shift; + my $st = shift; + my $filename = shift; + my $offset = 0; + my $length = 0; + + open FILE, ">$filename" or die "unable to create $filename: $!"; + eval { + $vol->download($st, $offset, $length, Sys::Virt::StorageVol::VOL_DOWNLOAD_SPARSE_STREAM); + $st->sparse_recv_all(\&downloadHandler, \&downloadHoleHandler); + $st->finish(); + }; + if ($@) { + unlink $filename if $@; + close FILE; + die $@; + } + + close FILE or die "cannot save $filename: $!" +} + +sub uploadHandler { + my $st = $_[0]; + my $nbytes = $_[2]; + return sysread FILE, $_[1], $nbytes; +} + +sub uploadHoleHandler { + my $st = shift; + my $inData; + my $sectionLen; + + # HACK, Perl lacks SEEK_DATA and SEEK_HOLE. + my $SEEK_DATA = 3; + my $SEEK_HOLE = 4; + + my $cur = sysseek FILE, 0, Fcntl::SEEK_CUR; + eval { + my $data = sysseek FILE, $cur, $SEEK_DATA; + # There are three options: + # 1) $data == $cur; $cur is in data + # 2) $data > $cur; $cur is in a hole, next data at $data + # 3) $data < 0; either $cur is in trailing hole, or $cur is beyond EOF. + + if (!defined($data)) { + # case 3 + $inData = 0; + my $end = sysseek FILE, 0, Fcntl::SEEK_END or die "Unable to get EOF position: $!"; + $sectionLen = $end - $cur; + } elsif ($data > $cur) { + #case 2 + $inData = 0; + $sectionLen = $data - $cur; + } else { + #case 1 + my $hole = sysseek FILE, $data, $SEEK_HOLE; + if (!defined($hole) or $hole eq $data) { + die "Blah"; + } + $inData = 1; + $sectionLen = $hole - $data; + } + }; + + # reposition file back + sysseek FILE, $cur, Fcntl::SEEK_SET; + + return ($inData, $sectionLen); +} + +sub uploadSkipHandler { + my $st = shift; + my $offset = shift; + print("uploadSkipHandler: $offset\n"); + sysseek FILE, $offset, Fcntl::SEEK_CUR or die "Unable to seek in $FILE"; +} + +sub upload { + my $vol = shift; + my $st = shift; + my $filename = shift; + my $offset = 0; + my $length = 0; + + open FILE, "<$filename" or die "unable to open $filename: $!"; + eval { + $vol->upload($st, $offset, $length, Sys::Virt::StorageVol::VOL_UPLOAD_SPARSE_STREAM); + $st->sparse_send_all(\&uploadHandler, \&uploadHoleHandler, \&uploadSkipHandler); + $st->finish(); + }; + if ($@) { + close FILE; + die $@; + } + + close FILE or die "cannot close $filename: $!" +} + +die "syntax: $0 URI --download/--upload VOLUME FILE" unless int(@ARGV) == 4; + +my $uri = shift @ARGV; +my $action = shift @ARGV; +my $volpath = shift @ARGV; +my $filename = shift @ARGV; + +my $c = Sys::Virt->new(uri => $uri) or die "Unable to connect to $uri"; +my $vol = $c->get_storage_volume_by_key($volpath) or die "No such volume"; +my $st = $c->new_stream(); + +if ($action eq "--download") { + download($vol, $st, $filename); +} elsif ($action eq "--upload") { + upload($vol, $st, $filename); +} else { + die "unknown action $action"; +} -- 2.13.0

On Tue, May 23, 2017 at 05:05:34PM +0200, Michal Privoznik wrote:
*** BLURB HERE ***
Michal Privoznik (7): Fix send_all() callback helper Introduce flags to Stream::recv() Introduce Stream::recvHole() and Stream::sendHole() Introduce Stream::sparse_recv_all() Introduce Stream::sparse_send_all() Register VOL_DOWNLOAD_SPARSE_STREAM & VOL_UPLOAD_SPARSE_STREAM constants examples: Introduce vol-sparse.pl
Changes | 9 ++ Virt.xs | 241 ++++++++++++++++++++++++++++++++++++++++++++- examples/vol-sparse.pl | 142 ++++++++++++++++++++++++++ lib/Sys/Virt/StorageVol.pm | 30 +++++- lib/Sys/Virt/Stream.pm | 70 ++++++++++++- t/030-api-coverage.t | 3 + 6 files changed, 483 insertions(+), 12 deletions(-) create mode 100755 examples/vol-sparse.pl
ACK with the couple of renames suggested Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 05/23/2017 06:16 PM, Daniel P. Berrange wrote:
On Tue, May 23, 2017 at 05:05:34PM +0200, Michal Privoznik wrote:
*** BLURB HERE ***
Michal Privoznik (7): Fix send_all() callback helper Introduce flags to Stream::recv() Introduce Stream::recvHole() and Stream::sendHole() Introduce Stream::sparse_recv_all() Introduce Stream::sparse_send_all() Register VOL_DOWNLOAD_SPARSE_STREAM & VOL_UPLOAD_SPARSE_STREAM constants examples: Introduce vol-sparse.pl
Changes | 9 ++ Virt.xs | 241 ++++++++++++++++++++++++++++++++++++++++++++- examples/vol-sparse.pl | 142 ++++++++++++++++++++++++++ lib/Sys/Virt/StorageVol.pm | 30 +++++- lib/Sys/Virt/Stream.pm | 70 ++++++++++++- t/030-api-coverage.t | 3 + 6 files changed, 483 insertions(+), 12 deletions(-) create mode 100755 examples/vol-sparse.pl
ACK with the couple of renames suggested
Thank you pushed. With a small bug fix too. In 7/7 in the example in upload_skip_handler() I cannot return the retval of sysseek as the return value is read as an integer and when trying to upload large file, int wraps (at 2GiB) and the file transfer breaks. Obviously. Michal
participants (2)
-
Daniel P. Berrange
-
Michal Privoznik