X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fbup%2F_helpers.c;h=9078be3da723d8e7757662950a7521bdc275754f;hb=544208fae44dfeaccb626c16f3a5c387f318d08f;hp=30c627fa32c3114b4ed51c48807577afca11d9a3;hpb=1d303adf6ed45e852ca63aa99506fb2c38caeaec;p=bup.git diff --git a/lib/bup/_helpers.c b/lib/bup/_helpers.c index 30c627f..9078be3 100644 --- a/lib/bup/_helpers.c +++ b/lib/bup/_helpers.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef HAVE_SYS_MMAN_H #include @@ -28,6 +29,9 @@ #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_SYS_TIME_H +#include +#endif #ifdef HAVE_LINUX_FS_H #include @@ -62,7 +66,19 @@ typedef unsigned char byte; -static int istty2 = 0; + +typedef struct { + int istty2; +} state_t; + +#if PY_MAJOR_VERSION < 3 +static state_t state; +# define get_state(x) (&state) +# define cstr_argf "s" +#else +# define get_state(x) ((state_t *) PyModule_GetState(x)) +# define cstr_argf "y" +#endif // PY_MAJOR_VERSION >= 3 #ifndef htonll @@ -94,6 +110,8 @@ static uint64_t htonll(uint64_t value) (((x) >= 0) ? PyLong_FromUnsignedLongLong(x) : PyLong_FromLongLong(x)) + +#if PY_MAJOR_VERSION < 3 static int bup_ulong_from_pyint(unsigned long *x, PyObject *py, const char *name) { @@ -114,12 +132,15 @@ static int bup_ulong_from_pyint(unsigned long *x, PyObject *py, *x = tmp; return 1; } +#endif static int bup_ulong_from_py(unsigned long *x, PyObject *py, const char *name) { +#if PY_MAJOR_VERSION < 3 if (PyInt_Check(py)) return bup_ulong_from_pyint(x, py, name); +#endif if (!PyLong_Check(py)) { @@ -158,6 +179,7 @@ static int bup_uint_from_py(unsigned int *x, PyObject *py, const char *name) static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py, const char *name) { +#if PY_MAJOR_VERSION < 3 if (PyInt_Check(py)) { unsigned long tmp; @@ -168,6 +190,7 @@ static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py, } return 0; } +#endif if (!PyLong_Check(py)) { @@ -188,6 +211,27 @@ static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py, } +static PyObject *bup_bytescmp(PyObject *self, PyObject *args) +{ + PyObject *py_s1, *py_s2; // This is really a PyBytes/PyString + if (!PyArg_ParseTuple(args, "SS", &py_s1, &py_s2)) + return NULL; + char *s1, *s2; + Py_ssize_t s1_len, s2_len; + if (PyBytes_AsStringAndSize(py_s1, &s1, &s1_len) == -1) + return NULL; + if (PyBytes_AsStringAndSize(py_s2, &s2, &s2_len) == -1) + return NULL; + const Py_ssize_t n = (s1_len < s2_len) ? s1_len : s2_len; + const int cmp = memcmp(s1, s2, n); + if (cmp != 0) + return PyLong_FromLong(cmp); + if (s1_len == s2_len) + return PyLong_FromLong(0);; + return PyLong_FromLong((s1_len < s2_len) ? -1 : 1); +} + + // Probably we should use autoconf or something and set HAVE_PY_GETARGCARGV... #if __WIN32__ || __CYGWIN__ @@ -301,8 +345,7 @@ static PyObject *record_sparse_zeros(unsigned long long *new_pending, } -static const byte * find_not_zero(const byte * const start, - const byte * const end) +static byte* find_not_zero(const byte * const start, const byte * const end) { // Return a pointer to first non-zero byte between start and end, // or end if there isn't one. @@ -310,37 +353,37 @@ static const byte * find_not_zero(const byte * const start, const unsigned char *cur = start; while (cur < end && *cur == 0) cur++; - return cur; + return (byte *) cur; } -static const byte * const find_trailing_zeros(const byte * const start, - const byte * const end) +static byte* find_trailing_zeros(const byte * const start, + const byte * const end) { // Return a pointer to the start of any trailing run of zeros, or // end if there isn't one. assert(start <= end); if (start == end) - return end; + return (byte *) end; const byte * cur = end; while (cur > start && *--cur == 0) {} if (*cur == 0) - return cur; + return (byte *) cur; else - return cur + 1; + return (byte *) (cur + 1); } -static const byte *find_non_sparse_end(const byte * const start, - const byte * const end, - const unsigned long long min_len) +static byte *find_non_sparse_end(const byte * const start, + const byte * const end, + const unsigned long long min_len) { // Return the first pointer to a min_len sparse block in [start, // end) if there is one, otherwise a pointer to the start of any // trailing run of zeros. If there are no trailing zeros, return // end. if (start == end) - return end; + return (byte *) end; assert(start < end); assert(min_len); // Probe in min_len jumps, searching backward from the jump @@ -361,7 +404,7 @@ static const byte *find_non_sparse_end(const byte * const start, assert(candidate >= start); assert(candidate <= end); assert(*candidate == 0); - return candidate; + return (byte *) candidate; } else { @@ -371,7 +414,7 @@ static const byte *find_non_sparse_end(const byte * const start, } if (candidate == end) - return end; + return (byte *) end; // No min_len sparse run found, search backward from end const byte * const trailing_zeros = find_trailing_zeros(end_of_known_zeros, @@ -383,20 +426,20 @@ static const byte *find_non_sparse_end(const byte * const start, assert(candidate < end); assert(*candidate == 0); assert(end - candidate < min_len); - return candidate; + return (byte *) candidate; } if (trailing_zeros == end) { assert(*(end - 1) != 0); - return end; + return (byte *) end; } assert(end - trailing_zeros < min_len); assert(trailing_zeros >= start); assert(trailing_zeros < end); assert(*trailing_zeros == 0); - return trailing_zeros; + return (byte *) trailing_zeros; } @@ -702,6 +745,11 @@ struct sha { unsigned char bytes[20]; }; +static inline int _cmp_sha(const struct sha *sha1, const struct sha *sha2) +{ + return memcmp(sha1->bytes, sha2->bytes, sizeof(sha1->bytes)); +} + struct idx { unsigned char *map; @@ -712,17 +760,6 @@ struct idx { int name_base; }; - -static int _cmp_sha(const struct sha *sha1, const struct sha *sha2) -{ - int i; - for (i = 0; i < sizeof(struct sha); i++) - if (sha1->bytes[i] != sha2->bytes[i]) - return sha1->bytes[i] - sha2->bytes[i]; - return 0; -} - - static void _fix_idx_order(struct idx **idxs, int *last_i) { struct idx *idx; @@ -821,7 +858,7 @@ static PyObject *merge_into(PyObject *self, PyObject *args) { struct idx *idx; uint32_t new_prefix; - if (count % 102424 == 0 && istty2) + if (count % 102424 == 0 && get_state(self)->istty2) fprintf(stderr, "midx: writing %.2f%% (%d/%d)\r", count*100.0/total, count, total); idx = idxs[last_i]; @@ -1138,12 +1175,12 @@ static PyObject *bup_set_linux_file_attr(PyObject *self, PyObject *args) // The extents flag can't be removed, so don't (see chattr(1) and chattr.c). orig_attr = 0; // Handle int/long mismatch (see above) rc = ioctl(fd, FS_IOC_GETFLAGS, &orig_attr); - assert(orig_attr <= UINT_MAX); // Kernel type is actually int if (rc == -1) { close(fd); return PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); } + assert(orig_attr <= UINT_MAX); // Kernel type is actually int attr |= ((unsigned int) orig_attr) & FS_EXTENT_FL; rc = ioctl(fd, FS_IOC_SETFLAGS, &attr); @@ -1213,7 +1250,7 @@ static PyObject *bup_utimensat(PyObject *self, PyObject *args) PyObject *access_py, *modification_py; struct timespec ts[2]; - if (!PyArg_ParseTuple(args, "is((Ol)(Ol))i", + if (!PyArg_ParseTuple(args, "i" cstr_argf "((Ol)(Ol))i", &fd, &path, &access_py, &(ts[0].tv_nsec), @@ -1255,7 +1292,7 @@ static int bup_parse_xutimes_args(char **path, PyObject *access_py, *modification_py; long long access_us, modification_us; // POSIX guarantees tv_usec is signed. - if (!PyArg_ParseTuple(args, "s((OL)(OL))", + if (!PyArg_ParseTuple(args, cstr_argf "((OL)(OL))", path, &access_py, &access_us, &modification_py, &modification_us)) @@ -1371,7 +1408,7 @@ static PyObject *bup_stat(PyObject *self, PyObject *args) int rc; char *filename; - if (!PyArg_ParseTuple(args, "s", &filename)) + if (!PyArg_ParseTuple(args, cstr_argf, &filename)) return NULL; struct stat st; @@ -1387,7 +1424,7 @@ static PyObject *bup_lstat(PyObject *self, PyObject *args) int rc; char *filename; - if (!PyArg_ParseTuple(args, "s", &filename)) + if (!PyArg_ParseTuple(args, cstr_argf, &filename)) return NULL; struct stat st; @@ -1441,43 +1478,59 @@ static PyObject *bup_localtime(PyObject *self, PyObject *args) #ifdef BUP_MINCORE_BUF_TYPE static PyObject *bup_mincore(PyObject *self, PyObject *args) { - const char *src; - Py_ssize_t src_ssize; - Py_buffer dest; + Py_buffer src, dest; PyObject *py_src_n, *py_src_off, *py_dest_off; - if (!PyArg_ParseTuple(args, "s#OOw*O", - &src, &src_ssize, &py_src_n, &py_src_off, + + if (!PyArg_ParseTuple(args, cstr_argf "*OOw*O", + &src, &py_src_n, &py_src_off, &dest, &py_dest_off)) return NULL; - unsigned long long src_size, src_n, src_off, dest_size, dest_off; + PyObject *result = NULL; + + unsigned long long src_n, src_off, dest_off; if (!(bup_ullong_from_py(&src_n, py_src_n, "src_n") && bup_ullong_from_py(&src_off, py_src_off, "src_off") && bup_ullong_from_py(&dest_off, py_dest_off, "dest_off"))) - return NULL; + goto clean_and_return; - if (!INTEGRAL_ASSIGNMENT_FITS(&src_size, src_ssize)) - return PyErr_Format(PyExc_OverflowError, "invalid src size"); unsigned long long src_region_end; + if (!uadd(&src_region_end, src_off, src_n)) { + result = PyErr_Format(PyExc_OverflowError, "(src_off + src_n) too large"); + goto clean_and_return; + } + if (src_region_end > src.len) { + result = PyErr_Format(PyExc_OverflowError, "region runs off end of src"); + goto clean_and_return; + } - if (!uadd(&src_region_end, src_off, src_n)) - return PyErr_Format(PyExc_OverflowError, "(src_off + src_n) too large"); - if (src_region_end > src_size) - return PyErr_Format(PyExc_OverflowError, "region runs off end of src"); - - if (!INTEGRAL_ASSIGNMENT_FITS(&dest_size, dest.len)) - return PyErr_Format(PyExc_OverflowError, "invalid dest size"); - if (dest_off > dest_size) - return PyErr_Format(PyExc_OverflowError, "region runs off end of dest"); + unsigned long long dest_size; + if (!INTEGRAL_ASSIGNMENT_FITS(&dest_size, dest.len)) { + result = PyErr_Format(PyExc_OverflowError, "invalid dest size"); + goto clean_and_return; + } + if (dest_off > dest_size) { + result = PyErr_Format(PyExc_OverflowError, "region runs off end of dest"); + goto clean_and_return; + } size_t length; - if (!INTEGRAL_ASSIGNMENT_FITS(&length, src_n)) - return PyErr_Format(PyExc_OverflowError, "src_n overflows size_t"); - int rc = mincore((void *)(src + src_off), src_n, + if (!INTEGRAL_ASSIGNMENT_FITS(&length, src_n)) { + result = PyErr_Format(PyExc_OverflowError, "src_n overflows size_t"); + goto clean_and_return; + } + int rc = mincore((void *)(src.buf + src_off), src_n, (BUP_MINCORE_BUF_TYPE *) (dest.buf + dest_off)); - if (rc != 0) - return PyErr_SetFromErrno(PyExc_OSError); - return Py_BuildValue("O", Py_None); + if (rc != 0) { + result = PyErr_SetFromErrno(PyExc_OSError); + goto clean_and_return; + } + result = Py_BuildValue("O", Py_None); + + clean_and_return: + PyBuffer_Release(&src); + PyBuffer_Release(&dest); + return result; } #endif /* def BUP_MINCORE_BUF_TYPE */ @@ -1544,6 +1597,8 @@ static PyMethodDef helper_methods[] = { { "localtime", bup_localtime, METH_VARARGS, "Return struct_time elements plus the timezone offset and name." }, #endif + { "bytescmp", bup_bytescmp, METH_VARARGS, + "Return a negative value if x < y, zero if equal, positive otherwise."}, #ifdef BUP_MINCORE_BUF_TYPE { "mincore", bup_mincore, METH_VARARGS, "For mincore(src, src_n, src_off, dest, dest_off)" @@ -1552,12 +1607,12 @@ static PyMethodDef helper_methods[] = { { NULL, NULL, 0, NULL }, // sentinel }; - -PyMODINIT_FUNC init_helpers(void) +static int setup_module(PyObject *m) { - // FIXME: migrate these tests to configure. Check against the - // type we're going to use when passing to python. Other stat - // types are tested at runtime. + // FIXME: migrate these tests to configure, or at least don't + // possibly crash the whole application. Check against the type + // we're going to use when passing to python. Other stat types + // are tested at runtime. assert(sizeof(ino_t) <= sizeof(unsigned PY_LONG_LONG)); assert(sizeof(off_t) <= sizeof(PY_LONG_LONG)); assert(sizeof(blksize_t) <= sizeof(PY_LONG_LONG)); @@ -1577,10 +1632,6 @@ PyMODINIT_FUNC init_helpers(void) } char *e; - PyObject *m = Py_InitModule("_helpers", helper_methods); - if (m == NULL) - return; - #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-compare" // For INTEGER_TO_PY(). { @@ -1617,6 +1668,52 @@ PyMODINIT_FUNC init_helpers(void) #pragma clang diagnostic pop // ignored "-Wtautological-compare" e = getenv("BUP_FORCE_TTY"); - istty2 = isatty(2) || (atoi(e ? e : "0") & 2); + get_state(m)->istty2 = isatty(2) || (atoi(e ? e : "0") & 2); unpythonize_argv(); + return 1; +} + + +#if PY_MAJOR_VERSION < 3 + +PyMODINIT_FUNC init_helpers(void) +{ + PyObject *m = Py_InitModule("_helpers", helper_methods); + if (m == NULL) + return; + + if (!setup_module(m)) + { + Py_DECREF(m); + return; + } +} + +# else // PY_MAJOR_VERSION >= 3 + +static struct PyModuleDef helpers_def = { + PyModuleDef_HEAD_INIT, + "_helpers", + NULL, + sizeof(state_t), + helper_methods, + NULL, + NULL, // helpers_traverse, + NULL, // helpers_clear, + NULL +}; + +PyMODINIT_FUNC PyInit__helpers(void) +{ + PyObject *module = PyModule_Create(&helpers_def); + if (module == NULL) + return NULL; + if (!setup_module(module)) + { + Py_DECREF(module); + return NULL; + } + return module; } + +#endif // PY_MAJOR_VERSION >= 3