Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
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