[libvirt] [PATCH RFC 5/8] fdstream: Implement virStreamRecvOffset

Michal Privoznik mprivozn at redhat.com
Fri Jan 29 13:26:56 UTC 2016


Signed-off-by: Michal Privoznik <mprivozn at 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




More information about the libvir-list mailing list