This function takes a FD and determines whether the current
position is in data section or in a hole. In addition to that,
it also determines how much bytes are there remaining till the
current section ends.

Signed-off-by: Michal Privoznik <mpriv...@redhat.com>
---
 src/libvirt_private.syms |   1 +
 src/util/virfile.c       |  82 +++++++++++++++++++
 src/util/virfile.h       |   4 +
 tests/virfiletest.c      | 203 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 290 insertions(+)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index bbe283529..4102a002b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1628,6 +1628,7 @@ virFileGetHugepageSize;
 virFileGetMountReverseSubtree;
 virFileGetMountSubtree;
 virFileHasSuffix;
+virFileInData;
 virFileIsAbsPath;
 virFileIsDir;
 virFileIsExecutable;
diff --git a/src/util/virfile.c b/src/util/virfile.c
index ea44a647c..5b10f9489 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -3793,6 +3793,88 @@ virFileComparePaths(const char *p1, const char *p2)
  cleanup:
     VIR_FREE(res1);
     VIR_FREE(res2);
+
+    return ret;
+}
+
+
+int
+virFileInData(int fd,
+              int *inData,
+              long long *length)
+{
+    int ret = -1;
+    off_t cur, data, hole, end;
+
+    /* Get current position */
+    cur = lseek(fd, 0, SEEK_CUR);
+    if (cur == (off_t) -1) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to get current position in file"));
+        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) {
+            virReportSystemError(errno, "%s",
+                                 _("Unable to seek to data"));
+            goto cleanup;
+        }
+
+        *inData = 0;
+        /* There are two situations now. There is always an
+         * implicit hole at EOF. However, there might be a
+         * trailing hole just before EOF too. If that's the case
+         * report it. */
+        if ((end = lseek(fd, 0, SEEK_END)) == (off_t) -1) {
+            virReportSystemError(errno, "%s",
+                                 _("Unable to seek to EOF"));
+            goto cleanup;
+        }
+        *length = end - cur;
+    } else if (data > cur) {
+        /* case 2 */
+        *inData = 0;
+        *length = 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. */
+            virReportSystemError(errno, "%s",
+                                 _("unable to seek to hole"));
+            goto cleanup;
+        } else {
+            /* case 2 */
+            *length = (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;
 }
 
diff --git a/src/util/virfile.h b/src/util/virfile.h
index 38e938f87..57ceb8072 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -348,4 +348,8 @@ int virFileReadValueString(char **value, const char 
*format, ...)
  ATTRIBUTE_FMT_PRINTF(2, 3);
 
 
+int virFileInData(int fd,
+                  int *inData,
+                  long long *length);
+
 #endif /* __VIR_FILE_H */
diff --git a/tests/virfiletest.c b/tests/virfiletest.c
index 702a76a50..a93bee01a 100644
--- a/tests/virfiletest.c
+++ b/tests/virfiletest.c
@@ -21,6 +21,7 @@
 #include <config.h>
 
 #include <stdlib.h>
+#include <fcntl.h>
 
 #include "testutils.h"
 #include "virfile.h"
@@ -118,6 +119,190 @@ testFileSanitizePath(const void *opaque)
 }
 
 
+static int
+makeSparseFile(const off_t offsets[],
+               const bool startData);
+
+#ifdef __linux__
+/* Create a sparse file. @offsets in KiB. */
+static int
+makeSparseFile(const off_t offsets[],
+               const bool startData)
+{
+    int fd = -1;
+    char path[] = abs_builddir "fileInData.XXXXXX";
+    off_t len = 0;
+    size_t i;
+
+    if ((fd = mkostemp(path,  O_CLOEXEC|O_RDWR)) < 0)
+        goto error;
+
+    if (unlink(path) < 0)
+        goto error;
+
+    for (i = 0; offsets[i] != (off_t) -1; i++)
+        len += offsets[i] * 1024;
+
+    while (len) {
+        const char buf[] = "abcdefghijklmnopqrstuvwxyz";
+        off_t toWrite = sizeof(buf);
+
+        if (toWrite > len)
+            toWrite = len;
+
+        if (safewrite(fd, buf, toWrite) < 0) {
+            fprintf(stderr, "unable to write to %s (errno=%d)\n", path, errno);
+            goto error;
+        }
+
+        len -= toWrite;
+    }
+
+    len = 0;
+    for (i = 0; offsets[i] != (off_t) -1; i++) {
+        bool inData = startData;
+
+        if (i % 2)
+            inData = !inData;
+
+        if (!inData &&
+            fallocate(fd,
+                      FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+                      len, offsets[i] * 1024) < 0) {
+            fprintf(stderr, "unable to punch a hole at offset %lld length 
%lld\n",
+                    (long long) len, (long long) offsets[i]);
+            goto error;
+        }
+
+        len += offsets[i] * 1024;
+    }
+
+    if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
+        fprintf(stderr, "unable to lseek (errno=%d)\n", errno);
+        goto error;
+    }
+
+    return fd;
+ error:
+    VIR_FORCE_CLOSE(fd);
+    return -1;
+}
+
+#else /* !__linux__ */
+
+static int
+makeSparseFile(const off_t offsets[] ATTRIBUTE_UNUSED,
+               const bool startData ATTRIBUTE_UNUSED)
+{
+    return -1;
+}
+
+#endif /* !__linux__ */
+
+
+#define EXTENT 4
+static bool
+holesSupported(void)
+{
+    off_t offsets[] = {EXTENT, EXTENT, EXTENT, -1};
+    off_t tmp;
+    int fd;
+    bool ret = false;
+
+    if ((fd = makeSparseFile(offsets, true)) < 0)
+        goto cleanup;
+
+    /* The way this works is: there are 4K of data followed by 4K hole followed
+     * by 4K hole again. Check if the filesystem we are running the test suite
+     * on supports holes. */
+    if ((tmp = lseek(fd, 0, SEEK_DATA)) == (off_t) -1)
+        goto cleanup;
+
+    if (tmp != 0)
+        goto cleanup;
+
+    if ((tmp = lseek(fd, tmp, SEEK_HOLE)) == (off_t) -1)
+        goto cleanup;
+
+    if (tmp != EXTENT * 1024)
+        goto cleanup;
+
+    if ((tmp = lseek(fd, tmp, SEEK_DATA)) == (off_t) -1)
+        goto cleanup;
+
+    if (tmp != 2 * EXTENT * 1024)
+        goto cleanup;
+
+    if ((tmp = lseek(fd, tmp, SEEK_HOLE)) == (off_t) -1)
+        goto cleanup;
+
+    if (tmp != 3 * EXTENT * 1024)
+        goto cleanup;
+
+    ret = true;
+ cleanup:
+    VIR_FORCE_CLOSE(fd);
+    return ret;
+}
+
+
+struct testFileInData {
+    bool startData;     /* whether the list of offsets starts with data 
section */
+    off_t *offsets;
+};
+
+
+static int
+testFileInData(const void *opaque)
+{
+    const struct testFileInData *data = opaque;
+    int fd = -1;
+    int ret = -1;
+    size_t i;
+
+    if ((fd = makeSparseFile(data->offsets, data->startData)) < 0)
+        goto cleanup;
+
+    for (i = 0; data->offsets[i] != (off_t) -1; i++) {
+        bool shouldInData = data->startData;
+        int realInData;
+        long long shouldLen;
+        long long realLen;
+
+        if (i % 2)
+            shouldInData = !shouldInData;
+
+        if (virFileInData(fd, &realInData, &realLen) < 0)
+            goto cleanup;
+
+        if (realInData != shouldInData) {
+            fprintf(stderr, "Unexpected data/hole. Expected %s got %s\n",
+                    shouldInData ? "data" : "hole",
+                    realInData ? "data" : "hole");
+            goto cleanup;
+        }
+
+        shouldLen = data->offsets[i] * 1024;
+        if (realLen != shouldLen) {
+            fprintf(stderr, "Unexpected section length. Expected %lld got 
%lld\n",
+                    shouldLen, realLen);
+            goto cleanup;
+        }
+
+        if (lseek(fd, shouldLen, SEEK_CUR) < 0) {
+            fprintf(stderr, "Unable to seek\n");
+            goto cleanup;
+        }
+    }
+
+    ret = 0;
+
+ cleanup:
+    VIR_FORCE_CLOSE(fd);
+    return ret;
+}
+
+
 static int
 mymain(void)
 {
@@ -186,6 +371,24 @@ mymain(void)
     DO_TEST_SANITIZE_PATH_SAME("gluster://bar.baz/fooo//hoo");
     DO_TEST_SANITIZE_PATH_SAME("gluster://bar.baz/fooo///////hoo");
 
+#define DO_TEST_IN_DATA(inData, ...)                                        \
+    do {                                                                    \
+        off_t offsets[] = {__VA_ARGS__, -1};                                \
+        struct testFileInData data = {                                      \
+            .startData = inData, .offsets = offsets,                        \
+        };                                                                  \
+        if (virTestRun(virTestCounterNext(), testFileInData, &data) < 0)    \
+            ret = -1;                                                       \
+    } while (0)
+
+    if (holesSupported()) {
+        DO_TEST_IN_DATA(true, 4, 4, 4);
+        DO_TEST_IN_DATA(false, 4, 4, 4);
+        DO_TEST_IN_DATA(true, 8, 8, 8);
+        DO_TEST_IN_DATA(false, 8, 8, 8);
+        DO_TEST_IN_DATA(true, 8, 16, 32, 64, 128, 256, 512);
+        DO_TEST_IN_DATA(false, 8, 16, 32, 64, 128, 256, 512);
+    }
     return ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
 
-- 
2.13.0

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to