Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> --- src/fdstream.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/src/fdstream.c b/src/fdstream.c index 403ddf6..20eda07 100644 --- a/src/fdstream.c +++ b/src/fdstream.c @@ -431,10 +431,15 @@ virFDStreamWriteOffset(virStreamPtr st, return virFDStreamWriteInternal(st, offset, bytes, nbytes); } -static int virFDStreamRead(virStreamPtr st, char *bytes, size_t nbytes) +static int +virFDStreamReadInternal(virStreamPtr st, + off_t *offset, + char *bytes, + size_t nbytes) { struct virFDStreamData *fdst = st->privateData; - int ret; + int ret = -1; + off_t cur, data, hole; if (nbytes > INT_MAX) { virReportSystemError(ERANGE, "%s", @@ -450,10 +455,84 @@ static int virFDStreamRead(virStreamPtr st, char *bytes, size_t nbytes) virMutexLock(&fdst->lock); + if (offset) { + /* Firstly, seek to desired position. */ + if ((cur = lseek(fdst->fd, *offset, SEEK_SET)) == (off_t) -1) { + virReportSystemError(errno, "%s", + _("unable to set stream position")); + goto cleanup; + } + + /* Now try to detect if @cur is in hole or in 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 = lseek(fdst->fd, cur, SEEK_DATA)) == (off_t) -1) { + /* case 3 and 4 */ + if (errno != ENXIO) { + virReportSystemError(errno, "%s", + _("unable to get data position")); + } else { + ret = 0; + } + goto cleanup; + } + + if (data > cur) { + /* case 2 */ + *offset = data; + fdst->offset = *offset; + ret = 0; + goto cleanup; + } else { + /* case 1 */ + } + + /* Now, we must ensure we will not read out of data boundaries. + * Get position of the next hole. Cases are the same as previously. */ + + if ((hole = lseek(fdst->fd, cur, SEEK_HOLE)) == (off_t) -1) { + /* Interesting, so we are in data, but failed to seek to next hole. + * There's an implicit hole at EOF, if no is to be found earlier. + * This is obviously an error so we merge case 3 and 4. */ + virReportSystemError(errno, "%s", + _("unable to get hole position")); + goto cleanup; + } + + if (hole == cur) { + /* Interesting, so the code above ensures @cur is in data, but now + * we found out it's in hole too. This shouldn't happen, but it's + * safer to error out. */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("stream is in both data (%llu) and hole (%llu)"), + (unsigned long long) data, + (unsigned long long) hole); + goto cleanup; + } + + if (nbytes > (hole - cur)) + nbytes = hole - cur; + + /* We may possibly have moved to a hole. Restore original position. */ + if ((cur = lseek(fdst->fd, *offset, SEEK_SET)) == (off_t) -1) { + virReportSystemError(errno, "%s", + _("unable to set stream position")); + goto cleanup; + } + + fdst->offset = *offset; + } + if (fdst->length) { if (fdst->length == fdst->offset) { - virMutexUnlock(&fdst->lock); - return 0; + ret = 0; + goto cleanup; } if ((fdst->length - fdst->offset) < nbytes) @@ -471,20 +550,38 @@ static int virFDStreamRead(virStreamPtr st, char *bytes, size_t nbytes) ret = -1; virReportSystemError(errno, "%s", _("cannot read from stream")); + goto cleanup; } } else if (fdst->length) { fdst->offset += ret; } + cleanup: virMutexUnlock(&fdst->lock); return ret; } +static int +virFDStreamRead(virStreamPtr st, char *bytes, size_t nbytes) +{ + return virFDStreamReadInternal(st, NULL, bytes, nbytes); +} + +static int +virFDStreamReadOffset(virStreamPtr st, + unsigned long long *offset, + char *bytes, + size_t nbytes) +{ + return virFDStreamReadInternal(st, (off_t *) offset, bytes, nbytes); +} + static virStreamDriver virFDStreamDrv = { .streamSend = virFDStreamWrite, .streamSendOffset = virFDStreamWriteOffset, .streamRecv = virFDStreamRead, + .streamRecvOffset = virFDStreamReadOffset, .streamFinish = virFDStreamClose, .streamAbort = virFDStreamAbort, .streamEventAddCallback = virFDStreamAddCallback, -- 2.4.10 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list