+#include "bupsplit.h"
+
+#if defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS)
+#define BUP_HAVE_FILE_ATTRS 1
+#endif
+
+/*
+ * Check for incomplete UTIMENSAT support (NetBSD 6), and if so,
+ * pretend we don't have it.
+ */
+#if !defined(AT_FDCWD) || !defined(AT_SYMLINK_NOFOLLOW)
+#undef HAVE_UTIMENSAT
+#endif
+
+#ifndef FS_NOCOW_FL
+// Of course, this assumes it's a bitfield value.
+#define FS_NOCOW_FL 0
+#endif
+
+static int istty2 = 0;
+
+
+#ifndef htonll
+// This function should technically be macro'd out if it's going to be used
+// more than ocasionally. As of this writing, it'll actually never be called
+// in real world bup scenarios (because our packs are < MAX_INT bytes).
+static uint64_t htonll(uint64_t value)
+{
+ static const int endian_test = 42;
+
+ if (*(char *)&endian_test == endian_test) // LSB-MSB
+ return ((uint64_t)htonl(value & 0xFFFFFFFF) << 32) | htonl(value >> 32);
+ return value; // already in network byte order MSB-LSB
+}
+#endif
+
+
+#define INTEGRAL_ASSIGNMENT_FITS(dest, src) \
+ ({ \
+ *(dest) = (src); \
+ *(dest) == (src) && (*(dest) < 1) == ((src) < 1); \
+ })
+
+
+// At the moment any code that calls INTGER_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))
+
+
+static int bup_ulong_from_pyint(unsigned long *x, PyObject *py,
+ const char *name)
+{
+ const long tmp = PyInt_AsLong(py);
+ if (tmp == -1 && PyErr_Occurred())
+ {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError))
+ PyErr_Format(PyExc_OverflowError, "%s too big for unsigned long",
+ name);
+ return 0;
+ }
+ if (tmp < 0)
+ {
+ PyErr_Format(PyExc_OverflowError,
+ "negative %s cannot be converted to unsigned long", name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+
+
+static int bup_ulong_from_py(unsigned long *x, PyObject *py, const char *name)
+{
+ if (PyInt_Check(py))
+ return bup_ulong_from_pyint(x, py, name);
+
+ if (!PyLong_Check(py))
+ {
+ PyErr_Format(PyExc_TypeError, "expected integer %s", name);
+ return 0;
+ }
+
+ const unsigned long tmp = PyLong_AsUnsignedLong(py);
+ if (PyErr_Occurred())
+ {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError))
+ PyErr_Format(PyExc_OverflowError, "%s too big for unsigned long",
+ name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+
+
+static int bup_uint_from_py(unsigned int *x, PyObject *py, const char *name)
+{
+ unsigned long tmp;
+ if (!bup_ulong_from_py(&tmp, py, name))
+ return 0;
+
+ if (tmp > UINT_MAX)
+ {
+ PyErr_Format(PyExc_OverflowError, "%s too big for unsigned int", name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+
+static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py,
+ const char *name)
+{
+ if (PyInt_Check(py))
+ {
+ unsigned long tmp;
+ if (bup_ulong_from_pyint(&tmp, py, name))
+ {
+ *x = tmp;
+ return 1;
+ }
+ return 0;
+ }
+
+ if (!PyLong_Check(py))
+ {
+ PyErr_Format(PyExc_TypeError, "integer argument expected for %s", name);
+ return 0;
+ }
+
+ const unsigned PY_LONG_LONG tmp = PyLong_AsUnsignedLongLong(py);
+ if (tmp == (unsigned long long) -1 && PyErr_Occurred())
+ {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError))
+ PyErr_Format(PyExc_OverflowError,
+ "%s too big for unsigned long long", name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+
+
+// Probably we should use autoconf or something and set HAVE_PY_GETARGCARGV...
+#if __WIN32__ || __CYGWIN__
+
+// There's no 'ps' on win32 anyway, and Py_GetArgcArgv() isn't available.
+static void unpythonize_argv(void) { }
+
+#else // not __WIN32__
+
+// For some reason this isn't declared in Python.h
+extern void Py_GetArgcArgv(int *argc, char ***argv);
+
+static void unpythonize_argv(void)
+{
+ int argc, i;
+ char **argv, *arge;
+
+ Py_GetArgcArgv(&argc, &argv);
+
+ for (i = 0; i < argc-1; i++)
+ {
+ if (argv[i] + strlen(argv[i]) + 1 != argv[i+1])
+ {
+ // The argv block doesn't work the way we expected; it's unsafe
+ // to mess with it.
+ return;
+ }
+ }
+
+ arge = argv[argc-1] + strlen(argv[argc-1]) + 1;
+
+ if (strstr(argv[0], "python") && argv[1] == argv[0] + strlen(argv[0]) + 1)
+ {
+ char *p;
+ size_t len, diff;
+ p = strrchr(argv[1], '/');
+ if (p)
+ {
+ p++;
+ diff = p - argv[0];
+ len = arge - p;
+ memmove(argv[0], p, len);
+ memset(arge - diff, 0, diff);
+ for (i = 0; i < argc; i++)
+ argv[i] = argv[i+1] ? argv[i+1]-diff : NULL;
+ }
+ }
+}
+
+#endif // not __WIN32__ or __CYGWIN__
+
+
+static unsigned long long count_leading_zeros(const unsigned char * const buf,
+ unsigned long long len)
+{
+ const unsigned char *cur = buf;
+ while(len-- && *cur == 0)
+ cur++;
+ return cur - buf;
+}
+
+
+static int write_all(int fd, const void *buf, const size_t count)
+{
+ size_t written = 0;
+ while (written < count)
+ {
+ const ssize_t rc = write(fd, buf + written, count - written);
+ if (rc == -1)
+ return -1;
+ written += rc;
+ }
+ return 0;
+}
+
+
+static int uadd(unsigned long long *dest,
+ const unsigned long long x,
+ const unsigned long long y)
+{
+ const unsigned long long result = x + y;
+ if (result < x || result < y)
+ return 0;
+ *dest = result;
+ return 1;
+}
+
+static PyObject *append_sparse_region(const int fd, unsigned long long n)
+{
+ while(n)
+ {
+ off_t new_off;
+ if (!INTEGRAL_ASSIGNMENT_FITS(&new_off, n))
+ new_off = INT_MAX;
+ const off_t off = lseek(fd, new_off, SEEK_CUR);
+ if (off == (off_t) -1)
+ return PyErr_SetFromErrno(PyExc_IOError);
+ n -= new_off;
+ }
+ return NULL;
+}
+
+
+static PyObject *bup_write_sparsely(PyObject *self, PyObject *args)
+{
+ int fd;
+ unsigned char *buf = NULL;
+ Py_ssize_t sbuf_len;
+ PyObject *py_min_sparse_len, *py_prev_sparse_len;
+ if (!PyArg_ParseTuple(args, "it#OO",
+ &fd, &buf, &sbuf_len,
+ &py_min_sparse_len, &py_prev_sparse_len))
+ return NULL;
+ unsigned long long min_sparse_len, prev_sparse_len, buf_len;
+ if (!bup_ullong_from_py(&min_sparse_len, py_min_sparse_len, "min_sparse_len"))
+ return NULL;
+ if (!bup_ullong_from_py(&prev_sparse_len, py_prev_sparse_len, "prev_sparse_len"))
+ return NULL;
+ if (sbuf_len < 0)
+ return PyErr_Format(PyExc_ValueError, "negative bufer length");
+ if (!INTEGRAL_ASSIGNMENT_FITS(&buf_len, sbuf_len))
+ return PyErr_Format(PyExc_OverflowError, "buffer length too large");
+
+ // The value of zeros_read indicates the number of zeros read from
+ // buf that haven't been accounted for yet (with respect to cur),
+ // while zeros indicates the total number of pending zeros, which
+ // could be larger in the first iteration if prev_sparse_len
+ // wasn't zero.
+ int rc;
+ unsigned long long unexamined = buf_len;
+ unsigned char *block_start = buf, *cur = buf;
+ unsigned long long zeros, zeros_read = count_leading_zeros(cur, unexamined);
+ assert(zeros_read <= unexamined);
+ unexamined -= zeros_read;
+ if (!uadd(&zeros, prev_sparse_len, zeros_read))
+ {
+ PyObject *err = append_sparse_region(fd, prev_sparse_len);
+ if (err != NULL)
+ return err;
+ zeros = zeros_read;
+ }
+
+ while(unexamined)
+ {
+ if (zeros < min_sparse_len)
+ cur += zeros_read;
+ else
+ {
+ rc = write_all(fd, block_start, cur - block_start);
+ if (rc)
+ return PyErr_SetFromErrno(PyExc_IOError);
+ PyObject *err = append_sparse_region(fd, zeros);
+ if (err != NULL)
+ return err;
+ cur += zeros_read;
+ block_start = cur;
+ }
+ // Pending zeros have ether been made sparse, or are going to
+ // be rolled into the next non-sparse block since we know we
+ // now have at least one unexamined non-zero byte.
+ assert(unexamined && *cur != 0);
+ zeros = zeros_read = 0;
+ while (unexamined && *cur != 0)
+ {
+ cur++; unexamined--;
+ }
+ if (unexamined)
+ {
+ zeros_read = count_leading_zeros(cur, unexamined);
+ assert(zeros_read <= unexamined);
+ unexamined -= zeros_read;
+ zeros = zeros_read;
+ }
+ }
+ rc = write_all(fd, block_start, cur - block_start);
+ if (rc)
+ return PyErr_SetFromErrno(PyExc_IOError);
+ return PyLong_FromUnsignedLongLong(zeros);
+}
+