]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/_helpers.c
write_idx: make the 64-bit offset count uint32_t not int
[bup.git] / lib / bup / _helpers.c
index d954f1d3266766d353ca9694aa6865e8225164e5..f47de73a07d0e4a2734f88caa13bb0fc392f6280 100644 (file)
 #include <time.h>
 #endif
 
-#ifdef BUP_HAVE_READLINE
-#if ! defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < BUP_RL_EXPECTED_XOPEN_SOURCE
-# warning "_XOPEN_SOURCE version is too low for readline"
+#if defined(BUP_RL_EXPECTED_XOPEN_SOURCE) \
+    && (!defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < BUP_RL_EXPECTED_XOPEN_SOURCE)
+# warning "_XOPEN_SOURCE version is incorrect for readline"
 #endif
-#include <readline/readline.h>
-#include <readline/history.h>
+
+#ifdef BUP_HAVE_READLINE
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wstrict-prototypes"
+# ifdef BUP_READLINE_INCLUDES_IN_SUBDIR
+#   include <readline/readline.h>
+#   include <readline/history.h>
+# else
+#   include <readline.h>
+#   include <history.h>
+# endif
+# pragma GCC diagnostic pop
 #endif
 
 #include "bupsplit.h"
@@ -119,7 +129,7 @@ static void *checked_malloc(size_t n, size_t size)
     if (__builtin_mul_overflow(n, size, &total))
     {
         PyErr_Format(PyExc_OverflowError,
-                     "request to allocate %lu items of size %lu is too large",
+                     "request to allocate %zu items of size %zu is too large",
                      n, size);
         return NULL;
     }
@@ -155,19 +165,27 @@ static uint64_t htonll(uint64_t value)
     ({                                                                  \
         _Pragma("GCC diagnostic push");                                 \
         _Pragma("GCC diagnostic ignored \"-Wsign-compare\"");           \
+        _Pragma("clang diagnostic push");                               \
+        _Pragma("clang diagnostic ignored \"-Wshorten-64-to-32\"");     \
         *(dest) = (src);                                                \
         int result = *(dest) == (src) && (*(dest) < 1) == ((src) < 1);  \
+        _Pragma("clang diagnostic pop");                                \
         _Pragma("GCC diagnostic pop");                                  \
         result;                                                         \
     })
 
 
-// At the moment any code that calls INTEGER_TO_PY() will have to
-// disable -Wtautological-compare for clang.  See below.
-
-#define INTEGER_TO_PY(x) \
-    (((x) >= 0) ? PyLong_FromUnsignedLongLong(x) : PyLong_FromLongLong(x))
-
+#define INTEGER_TO_PY(x)                                                \
+    ({                                                                  \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wtype-limits\"");   \
+        _Pragma("clang diagnostic push");                               \
+        _Pragma("clang diagnostic ignored \"-Wtautological-compare\""); \
+        PyObject *result = ((x) >= 0) ? PyLong_FromUnsignedLongLong(x) : PyLong_FromLongLong(x); \
+        _Pragma("clang diagnostic pop");                                \
+        _Pragma("GCC diagnostic pop");                                  \
+        result;                                                         \
+    })
 
 
 #if PY_MAJOR_VERSION < 3
@@ -231,7 +249,7 @@ static int bup_uint_from_py(unsigned int *x, PyObject *py, const char *name)
         PyErr_Format(PyExc_OverflowError, "%s too big for unsigned int", name);
         return 0;
     }
-    *x = tmp;
+    *x = (unsigned int) tmp;
     return 1;
 }
 
@@ -655,7 +673,7 @@ static PyObject *splitbuf(PyObject *self, PyObject *args)
         if (!PyArg_ParseTuple(args, "t#", &buf, &len))
             return NULL;
         assert(len <= INT_MAX);
-        out = bupsplit_find_ofs(buf, len, &bits);
+        out = bupsplit_find_ofs(buf, (int) len, &bits);
     }
     if (out) assert(bits >= BUP_BLOBBITS);
     return Py_BuildValue("ii", out, bits);
@@ -906,7 +924,8 @@ struct idx {
 static void _fix_idx_order(struct idx **idxs, Py_ssize_t *last_i)
 {
     struct idx *idx;
-    int low, mid, high, c = 0;
+    Py_ssize_t low, mid, high;
+    int c = 0;
 
     idx = idxs[*last_i];
     if (idxs[*last_i]->cur >= idxs[*last_i]->end)
@@ -1068,7 +1087,7 @@ static PyObject *write_idx(PyObject *self, PyObject *args)
     PyObject *part;
     unsigned int total = 0;
     uint32_t count;
-    int i, j, ofs64_count;
+    int i;
     uint32_t *fan_ptr, *crc_ptr, *ofs_ptr;
     uint64_t *ofs64_ptr;
     struct sha *sha_ptr;
@@ -1100,16 +1119,21 @@ static PyObject *write_idx(PyObject *self, PyObject *args)
     ofs64_ptr = (uint64_t *)&ofs_ptr[total];
 
     count = 0;
-    ofs64_count = 0;
+    uint32_t ofs64_count = 0;
     for (i = 0; i < FAN_ENTRIES; ++i)
     {
-       int plen;
        part = PyList_GET_ITEM(idx, i);
        PyList_Sort(part);
-       plen = PyList_GET_SIZE(part);
-       count += plen;
+        uint32_t plen;
+        if (!INTEGRAL_ASSIGNMENT_FITS(&plen, PyList_GET_SIZE(part))
+            || UINT32_MAX - count < plen) {
+            PyErr_Format(PyExc_OverflowError, "too many objects in index part");
+            goto clean_and_return;
+        }
+        count += plen;
        *fan_ptr++ = htonl(count);
-       for (j = 0; j < plen; ++j)
+        uint32_t j;
+        for (j = 0; j < plen; ++j)
        {
            unsigned char *sha = NULL;
            Py_ssize_t sha_len = 0;
@@ -1177,7 +1201,7 @@ static PyObject *write_random(PyObject *self, PyObject *args)
     {
        unsigned i;
        for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
-           buf[i] = random();
+           buf[i] = (uint32_t) random();
        ret = write(fd, buf, sizeof(buf));
        if (ret < 0)
            ret = 0;
@@ -1193,7 +1217,7 @@ static PyObject *write_random(PyObject *self, PyObject *args)
     {
        unsigned i;
        for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
-           buf[i] = random();
+           buf[i] = (uint32_t) random();
        ret = write(fd, buf, len % 1024);
        if (ret < 0)
            ret = 0;
@@ -1215,7 +1239,7 @@ static PyObject *random_sha(PyObject *self, PyObject *args)
     if (!seeded)
     {
        assert(sizeof(shabuf) == 20);
-       srandom(time(NULL));
+       srandom((unsigned int) time(NULL));
        seeded = 1;
     }
     
@@ -1224,7 +1248,7 @@ static PyObject *random_sha(PyObject *self, PyObject *args)
     
     memset(shabuf, 0, sizeof(shabuf));
     for (i=0; i < 20/4; i++)
-       shabuf[i] = random();
+       shabuf[i] = (uint32_t) random();
     return Py_BuildValue(rbuf_argf, shabuf, 20);
 }
 
@@ -1559,9 +1583,6 @@ static PyObject *bup_lutimes(PyObject *self, PyObject *args)
 #endif
 
 
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wtautological-compare" // For INTEGER_TO_PY().
-
 static PyObject *stat_struct_to_py(const struct stat *st,
                                    const char *filename,
                                    int fd)
@@ -1587,7 +1608,6 @@ static PyObject *stat_struct_to_py(const struct stat *st,
                          (long) BUP_STAT_CTIME_NS(st));
 }
 
-#pragma clang diagnostic pop  // ignored "-Wtautological-compare"
 
 static PyObject *bup_stat(PyObject *self, PyObject *args)
 {
@@ -1706,7 +1726,7 @@ static PyObject *bup_mincore(PyObject *self, PyObject *args)
         result = PyErr_Format(PyExc_OverflowError, "src_n overflows size_t");
         goto clean_and_return;
     }
-    int rc = mincore((void *)(src.buf + src_off), src_n,
+    int rc = mincore((void *)(src.buf + src_off), length,
                      (BUP_MINCORE_BUF_TYPE *) (dest.buf + dest_off));
     if (rc != 0) {
         result = PyErr_SetFromErrno(PyExc_OSError);
@@ -1748,48 +1768,54 @@ static PyObject *tuple_from_cstrs(char **cstrs)
     return result;
 }
 
+static PyObject *appropriate_errno_ex(void)
+{
+    switch (errno) {
+    case ENOMEM:
+        return PyErr_NoMemory();
+    case EIO:
+    case EMFILE:
+    case ENFILE:
+        // In 3.3 IOError was merged into OSError.
+        return PyErr_SetFromErrno(PyExc_IOError);
+    default:
+        return PyErr_SetFromErrno(PyExc_OSError);
+    }
+}
 
-static long getpw_buf_size;
 
-static PyObject *pwd_struct_to_py(const struct passwd *pwd, int rc)
+static PyObject *pwd_struct_to_py(const struct passwd *pwd)
 {
     // We can check the known (via POSIX) signed and unsigned types at
     // compile time, but not (easily) the unspecified types, so handle
     // those via INTEGER_TO_PY().
-    if (pwd != NULL)
-        return Py_BuildValue(cstr_argf cstr_argf "OO"
-                             cstr_argf cstr_argf cstr_argf,
-                             pwd->pw_name,
-                             pwd->pw_passwd,
-                             INTEGER_TO_PY(pwd->pw_uid),
-                             INTEGER_TO_PY(pwd->pw_gid),
-                             pwd->pw_gecos,
-                             pwd->pw_dir,
-                             pwd->pw_shell);
-    if (rc == 0)
-        return Py_BuildValue("O", Py_None);
-    if (rc == EIO || rc == EMFILE || rc == ENFILE)
-        return PyErr_SetFromErrno(PyExc_IOError);
-    if (rc < 0)
-        return PyErr_SetFromErrno(PyExc_OSError);
-    assert(0);
+    if (pwd == NULL)
+        Py_RETURN_NONE;
+    return Py_BuildValue(cstr_argf cstr_argf "OO"
+                         cstr_argf cstr_argf cstr_argf,
+                         pwd->pw_name,
+                         pwd->pw_passwd,
+                         INTEGER_TO_PY(pwd->pw_uid),
+                         INTEGER_TO_PY(pwd->pw_gid),
+                         pwd->pw_gecos,
+                         pwd->pw_dir,
+                         pwd->pw_shell);
 }
 
 static PyObject *bup_getpwuid(PyObject *self, PyObject *args)
 {
-    unsigned long uid;
-    if (!PyArg_ParseTuple(args, "k", &uid))
+    unsigned long long py_uid;
+    if (!PyArg_ParseTuple(args, "K", &py_uid))
        return NULL;
+    uid_t uid;
+    if (!INTEGRAL_ASSIGNMENT_FITS(&uid, py_uid))
+        return PyErr_Format(PyExc_OverflowError, "uid too large for uid_t");
 
-    struct passwd pwd, *result_pwd;
-    char *buf = PyMem_Malloc(getpw_buf_size);
-    if (buf == NULL)
-        return NULL;
-
-    int rc = getpwuid_r(uid, &pwd, buf, getpw_buf_size, &result_pwd);
-    PyObject *result = pwd_struct_to_py(result_pwd, rc);
-    PyMem_Free(buf);
-    return result;
+    errno = 0;
+    struct passwd *pwd = getpwuid(uid);
+    if (!pwd && errno)
+        return appropriate_errno_ex();
+    return pwd_struct_to_py(pwd);
 }
 
 static PyObject *bup_getpwnam(PyObject *self, PyObject *args)
@@ -1798,59 +1824,46 @@ static PyObject *bup_getpwnam(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "S", &py_name))
        return NULL;
 
-    struct passwd pwd, *result_pwd;
-    char *buf = PyMem_Malloc(getpw_buf_size);
-    if (buf == NULL)
-        return NULL;
-
     char *name = PyBytes_AS_STRING(py_name);
-    int rc = getpwnam_r(name, &pwd, buf, getpw_buf_size, &result_pwd);
-    PyObject *result = pwd_struct_to_py(result_pwd, rc);
-    PyMem_Free(buf);
-    return result;
+    errno = 0;
+    struct passwd *pwd = getpwnam(name);
+    if (!pwd && errno)
+        return appropriate_errno_ex();
+    return pwd_struct_to_py(pwd);
 }
 
-static long getgr_buf_size;
-
-static PyObject *grp_struct_to_py(const struct group *grp, int rc)
+static PyObject *grp_struct_to_py(const struct group *grp)
 {
     // We can check the known (via POSIX) signed and unsigned types at
     // compile time, but not (easily) the unspecified types, so handle
     // those via INTEGER_TO_PY().
-    if (grp != NULL) {
-        PyObject *members = tuple_from_cstrs(grp->gr_mem);
-        if (members == NULL)
-            return NULL;
-        return Py_BuildValue(cstr_argf cstr_argf "OO",
-                             grp->gr_name,
-                             grp->gr_passwd,
-                             INTEGER_TO_PY(grp->gr_gid),
-                             members);
-    }
-    if (rc == 0)
-        return Py_BuildValue("O", Py_None);
-    if (rc == EIO || rc == EMFILE || rc == ENFILE)
-        return PyErr_SetFromErrno(PyExc_IOError);
-    if (rc < 0)
-        return PyErr_SetFromErrno(PyExc_OSError);
-    assert (0);
+    if (grp == NULL)
+        Py_RETURN_NONE;
+
+    PyObject *members = tuple_from_cstrs(grp->gr_mem);
+    if (members == NULL)
+        return NULL;
+    return Py_BuildValue(cstr_argf cstr_argf "OO",
+                         grp->gr_name,
+                         grp->gr_passwd,
+                         INTEGER_TO_PY(grp->gr_gid),
+                         members);
 }
 
 static PyObject *bup_getgrgid(PyObject *self, PyObject *args)
 {
-    unsigned long gid;
-    if (!PyArg_ParseTuple(args, "k", &gid))
+    unsigned long long py_gid;
+    if (!PyArg_ParseTuple(args, "K", &py_gid))
        return NULL;
+    gid_t gid;
+    if (!INTEGRAL_ASSIGNMENT_FITS(&gid, py_gid))
+        return PyErr_Format(PyExc_OverflowError, "gid too large for gid_t");
 
-    struct group grp, *result_grp;
-    char *buf = PyMem_Malloc(getgr_buf_size);
-    if (buf == NULL)
-        return NULL;
-
-    int rc = getgrgid_r(gid, &grp, buf, getgr_buf_size, &result_grp);
-    PyObject *result = grp_struct_to_py(result_grp, rc);
-    PyMem_Free(buf);
-    return result;
+    errno = 0;
+    struct group *grp = getgrgid(gid);
+    if (!grp && errno)
+        return appropriate_errno_ex();
+    return grp_struct_to_py(grp);
 }
 
 static PyObject *bup_getgrnam(PyObject *self, PyObject *args)
@@ -1859,18 +1872,15 @@ static PyObject *bup_getgrnam(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "S", &py_name))
        return NULL;
 
-    struct group grp, *result_grp;
-    char *buf = PyMem_Malloc(getgr_buf_size);
-    if (buf == NULL)
-        return NULL;
-
     char *name = PyBytes_AS_STRING(py_name);
-    int rc = getgrnam_r(name, &grp, buf, getgr_buf_size, &result_grp);
-    PyObject *result = grp_struct_to_py(result_grp, rc);
-    PyMem_Free(buf);
-    return result;
+    errno = 0;
+    struct group *grp = getgrnam(name);
+    if (!grp && errno)
+        return appropriate_errno_ex();
+    return grp_struct_to_py(grp);
 }
 
+
 static PyObject *bup_gethostname(PyObject *mod, PyObject *ignore)
 {
 #ifdef HOST_NAME_MAX
@@ -2095,6 +2105,137 @@ bup_readline(PyObject *self, PyObject *args)
 
 #endif // defined BUP_HAVE_READLINE
 
+#if defined(HAVE_SYS_ACL_H) && \
+    defined(HAVE_ACL_LIBACL_H) && \
+    defined(HAVE_ACL_EXTENDED_FILE) && \
+    defined(HAVE_ACL_GET_FILE) && \
+    defined(HAVE_ACL_TO_ANY_TEXT) && \
+    defined(HAVE_ACL_FROM_TEXT) && \
+    defined(HAVE_ACL_SET_FILE)
+#define ACL_SUPPORT 1
+#include <sys/acl.h>
+#include <acl/libacl.h>
+
+// Returns
+//   0 for success
+//  -1 for errors, with python exception set
+//  -2 for ignored errors (not supported)
+static int bup_read_acl_to_text(const char *name, acl_type_t type,
+                                char **txt, char **num)
+{
+    acl_t acl;
+
+    acl = acl_get_file(name, type);
+    if (!acl) {
+        if (errno == EOPNOTSUPP || errno == ENOSYS)
+            return -2;
+        PyErr_SetFromErrno(PyExc_IOError);
+        return -1;
+    }
+
+    *num = NULL;
+    *txt = acl_to_any_text(acl, "", '\n', TEXT_ABBREVIATE);
+    if (*txt)
+        *num = acl_to_any_text(acl, "", '\n', TEXT_ABBREVIATE | TEXT_NUMERIC_IDS);
+
+    if (*txt && *num)
+        return 0;
+
+    if (errno == ENOMEM)
+        PyErr_NoMemory();
+    else
+        PyErr_SetFromErrno(PyExc_IOError);
+
+    if (*txt)
+        acl_free((acl_t)*txt);
+    if (*num)
+        acl_free((acl_t)*num);
+
+    return -1;
+}
+
+static PyObject *bup_read_acl(PyObject *self, PyObject *args)
+{
+    char *name;
+    int isdir, rv;
+    PyObject *ret = NULL;
+    char *acl_txt = NULL, *acl_num = NULL;
+
+    if (!PyArg_ParseTuple(args, cstr_argf "i", &name, &isdir))
+       return NULL;
+
+    if (!acl_extended_file(name))
+        Py_RETURN_NONE;
+
+    rv = bup_read_acl_to_text(name, ACL_TYPE_ACCESS, &acl_txt, &acl_num);
+    if (rv)
+        goto out;
+
+    if (isdir) {
+        char *def_txt = NULL, *def_num = NULL;
+
+        rv = bup_read_acl_to_text(name, ACL_TYPE_DEFAULT, &def_txt, &def_num);
+        if (rv)
+            goto out;
+
+        ret = Py_BuildValue("[" cstr_argf cstr_argf cstr_argf cstr_argf "]",
+                            acl_txt, acl_num, def_txt, def_num);
+
+        if (def_txt)
+            acl_free((acl_t)def_txt);
+        if (def_num)
+            acl_free((acl_t)def_num);
+    } else {
+        ret = Py_BuildValue("[" cstr_argf cstr_argf "]",
+                            acl_txt, acl_num);
+    }
+
+out:
+    if (acl_txt)
+        acl_free((acl_t)acl_txt);
+    if (acl_num)
+        acl_free((acl_t)acl_num);
+    if (rv == -2)
+        Py_RETURN_NONE;
+    return ret;
+}
+
+static int bup_apply_acl_string(const char *name, const char *s)
+{
+    acl_t acl = acl_from_text(s);
+    int ret = 0;
+
+    if (!acl) {
+        PyErr_SetFromErrno(PyExc_IOError);
+        return -1;
+    }
+
+    if (acl_set_file(name, ACL_TYPE_ACCESS, acl)) {
+        PyErr_SetFromErrno(PyExc_IOError);
+        ret = -1;
+    }
+
+    acl_free(acl);
+
+    return ret;
+}
+
+static PyObject *bup_apply_acl(PyObject *self, PyObject *args)
+{
+    char *name, *acl, *def = NULL;
+
+    if (!PyArg_ParseTuple(args, cstr_argf cstr_argf "|" cstr_argf, &name, &acl, &def))
+       return NULL;
+
+    if (bup_apply_acl_string(name, acl))
+        return NULL;
+
+    if (def && bup_apply_acl_string(name, def))
+        return NULL;
+
+    Py_RETURN_NONE;
+}
+#endif
 
 static PyMethodDef helper_methods[] = {
     { "write_sparsely", bup_write_sparsely, METH_VARARGS,
@@ -2201,6 +2342,16 @@ static PyMethodDef helper_methods[] = {
     { "readline", bup_readline, METH_VARARGS,
       "Call readline(prompt)." },
 #endif // defined BUP_HAVE_READLINE
+#ifdef ACL_SUPPORT
+    { "read_acl", bup_read_acl, METH_VARARGS,
+      "read_acl(name, isdir)\n\n"
+      "Read ACLs for the given file/dirname and return the correctly encoded"
+      " list [txt, num, def_tx, def_num] (the def_* being empty bytestrings"
+      " unless the second argument 'isdir' is True)." },
+    { "apply_acl", bup_apply_acl, METH_VARARGS,
+      "apply_acl(name, acl, def=None)\n\n"
+      "Given a file/dirname (bytes) and the ACLs to restore, do that." },
+#endif /* HAVE_ACLS */
     { NULL, NULL, 0, NULL },  // sentinel
 };
 
@@ -2260,8 +2411,6 @@ static int setup_module(PyObject *m)
     }
 
     char *e;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wtautological-compare" // For INTEGER_TO_PY().
     {
         PyObject *value;
         value = INTEGER_TO_PY(INT_MAX);
@@ -2293,15 +2442,6 @@ static int setup_module(PyObject *m)
         Py_DECREF(value);
     }
 #endif
-#pragma clang diagnostic pop  // ignored "-Wtautological-compare"
-
-    getpw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
-    if (getpw_buf_size == -1)
-        getpw_buf_size = 16384;
-
-    getgr_buf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
-    if (getgr_buf_size == -1)
-        getgr_buf_size = 16384;
 
     e = getenv("BUP_FORCE_TTY");
     get_state(m)->istty2 = isatty(2) || (atoi(e ? e : "0") & 2);