]> arthur.barton.de Git - bup.git/commitdiff
Add fmincore to return an fd's mincore data
authorRob Browning <rlb@defaultvalue.org>
Tue, 26 May 2015 01:55:29 +0000 (20:55 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sat, 20 Jun 2015 15:52:48 +0000 (10:52 -0500)
Signed-off-by: Rob Browning <rlb@defaultvalue.org>
config/configure
lib/bup/_helpers.c

index ccc02b20559da2a8da40068f51b1708ed2f38e9a..89da205674262c93ecd8b1b3746f3695bf6190b5 100755 (executable)
@@ -65,6 +65,7 @@ if [ -z "$OS_GNU_KFREEBSD" ]; then
 fi
 AC_CHECK_FUNCS utimes
 AC_CHECK_FUNCS lutimes
+AC_CHECK_FUNCS mincore
 
 AC_CHECK_FIELD stat st_atim sys/types.h sys/stat.h unistd.h
 AC_CHECK_FIELD stat st_mtim sys/types.h sys/stat.h unistd.h
index 3ad766667b8d5d6b55fcde9c0000cc0ae12c42d6..cfdbc8afcfbc11171db504ee8dcd0ea6c8320ddf 100644 (file)
@@ -1332,6 +1332,79 @@ static PyObject *bup_localtime(PyObject *self, PyObject *args)
 #endif /* def HAVE_TM_TM_GMTOFF */
 
 
+#ifdef HAVE_MINCORE
+static PyObject *bup_fmincore(PyObject *self, PyObject *args)
+{
+    int fd, rc;
+    if (!PyArg_ParseTuple(args, "i", &fd))
+       return NULL;
+
+    struct stat st;
+    rc = fstat(fd, &st);
+    if (rc != 0)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    if (st.st_size == 0)
+        return Py_BuildValue("s", "");
+
+    const size_t page_size = (size_t) sysconf (_SC_PAGESIZE);
+    const off_t pref_chunk_size = 64 * 1024 * 1024;
+    off_t chunk_size = page_size;
+    if (page_size < pref_chunk_size)
+        chunk_size = page_size * (pref_chunk_size / page_size);
+    const off_t pages_per_chunk = chunk_size / page_size;
+    const off_t page_count = (st.st_size + page_size - 1) / page_size;
+    const off_t chunk_count = page_count / chunk_size > 0 ? page_count / chunk_size : 1;
+    unsigned char * const result = malloc(page_count);
+    if (result == NULL)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    off_t ci;
+    for(ci = 0; ci < chunk_count; ci++)
+    {
+        const off_t pos = chunk_size * ci;
+        const size_t msize = chunk_size < st.st_size - pos ? chunk_size : st.st_size - pos;
+        void *m = mmap(NULL, msize, PROT_NONE, MAP_SHARED, fd, pos);
+        if (m == MAP_FAILED)
+        {
+            free(result);
+            return PyErr_SetFromErrno(PyExc_OSError);
+        }
+        rc = mincore(m, msize, &result[ci * pages_per_chunk]);
+        if (rc != 0)
+        {
+            const int errno_stash = errno;
+            rc = munmap(m, msize);
+            if (rc != 0)
+            {
+                char buf[512];
+                char *msg = strerror_r(errno, buf, 512);
+                if (rc != 0)
+                    fprintf(stderr, "%s:%d: strerror_r failed (%d)\n",
+                            __FILE__, __LINE__, rc < 0 ? errno : rc);
+                else
+                    fprintf(stderr,
+                            "%s:%d: munmap failed after mincore failed (%s)\n",
+                            __FILE__, __LINE__, msg);
+            }
+            free(result);
+            errno = errno_stash;
+            return PyErr_SetFromErrno(PyExc_OSError);
+        }
+        rc = munmap(m, msize);
+        if (rc != 0)
+        {
+            free(result);
+            return PyErr_SetFromErrno(PyExc_OSError);
+        }
+    }
+    PyObject *py_result = Py_BuildValue("s#", result, page_count);
+    free(result);
+    return py_result;
+}
+#endif /* def HAVE_MINCORE */
+
+
 static PyMethodDef helper_methods[] = {
     { "write_sparsely", bup_write_sparsely, METH_VARARGS,
       "Write buf excepting zeros at the end. Return trailing zero count." },
@@ -1393,6 +1466,10 @@ static PyMethodDef helper_methods[] = {
 #ifdef HAVE_TM_TM_GMTOFF
     { "localtime", bup_localtime, METH_VARARGS,
       "Return struct_time elements plus the timezone offset and name." },
+#endif
+#ifdef HAVE_MINCORE
+    { "fmincore", bup_fmincore, METH_VARARGS,
+      "Return mincore() information for the provided file descriptor." },
 #endif
     { NULL, NULL, 0, NULL },  // sentinel
 };