+#ifdef HAVE_TM_TM_GMTOFF
+static PyObject *bup_localtime(PyObject *self, PyObject *args)
+{
+ long long lltime;
+ time_t ttime;
+ if (!PyArg_ParseTuple(args, "L", &lltime))
+ return NULL;
+ if (!INTEGRAL_ASSIGNMENT_FITS(&ttime, lltime))
+ return PyErr_Format(PyExc_OverflowError, "time value too large");
+
+ struct tm tm;
+ tzset();
+ if(localtime_r(&ttime, &tm) == NULL)
+ return PyErr_SetFromErrno(PyExc_OSError);
+
+ // Match the Python struct_time values.
+ return Py_BuildValue("[i,i,i,i,i,i,i,i,i,i,s]",
+ 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm.tm_wday, tm.tm_yday + 1,
+ tm.tm_isdst, tm.tm_gmtoff, tm.tm_zone);
+}
+#endif /* def HAVE_TM_TM_GMTOFF */
+
+
+#ifdef BUP_MINCORE_BUF_TYPE
+static PyObject *bup_mincore(PyObject *self, PyObject *args)
+{
+ Py_buffer src, dest;
+ PyObject *py_src_n, *py_src_off, *py_dest_off;
+
+ if (!PyArg_ParseTuple(args, cstr_argf "*OOw*O",
+ &src, &py_src_n, &py_src_off,
+ &dest, &py_dest_off))
+ return NULL;
+
+ 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")))
+ goto clean_and_return;
+
+ 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;
+ }
+ assert(src.len >= 0);
+ if (src_region_end > (unsigned long long) src.len) {
+ result = PyErr_Format(PyExc_OverflowError, "region runs off end of src");
+ goto clean_and_return;
+ }
+
+ 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)) {
+ result = PyErr_Format(PyExc_OverflowError, "src_n overflows size_t");
+ goto clean_and_return;
+ }
+ 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);
+ 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 */
+
+static unsigned int vuint_encode(long long val, char *buf)
+{
+ unsigned int len = 0;
+
+ if (val < 0) {
+ PyErr_SetString(PyExc_Exception, "vuints must not be negative");
+ return 0;
+ }
+
+ do {
+ buf[len] = val & 0x7f;
+
+ val >>= 7;
+ if (val)
+ buf[len] |= 0x80;
+
+ len++;
+ } while (val);
+
+ return len;
+}
+
+static unsigned int vint_encode(long long val, char *buf)
+{
+ unsigned int len = 1;
+ char sign = 0;
+
+ if (val < 0) {
+ sign = 0x40;
+ val = -val;
+ }
+
+ buf[0] = (val & 0x3f) | sign;
+ val >>= 6;
+ if (val)
+ buf[0] |= 0x80;
+
+ while (val) {
+ buf[len] = val & 0x7f;
+ val >>= 7;
+ if (val)
+ buf[len] |= 0x80;
+ len++;
+ }
+
+ return len;
+}
+
+static PyObject *bup_vuint_encode(PyObject *self, PyObject *args)
+{
+ long long val;
+ // size the buffer appropriately - need 8 bits to encode each 7
+ char buf[(sizeof(val) + 1) / 7 * 8];
+
+ if (!PyArg_ParseTuple(args, "L", &val))
+ return NULL;
+
+ unsigned int len = vuint_encode(val, buf);
+ if (!len)
+ return NULL;
+
+ return PyBytes_FromStringAndSize(buf, len);
+}
+
+static PyObject *bup_vint_encode(PyObject *self, PyObject *args)
+{
+ long long val;
+ // size the buffer appropriately - need 8 bits to encode each 7
+ char buf[(sizeof(val) + 1) / 7 * 8];
+
+ if (!PyArg_ParseTuple(args, "L", &val))
+ return NULL;
+
+ return PyBytes_FromStringAndSize(buf, vint_encode(val, buf));
+}
+
+static PyObject *tuple_from_cstrs(char **cstrs)
+{
+ // Assumes list is null terminated
+ size_t n = 0;
+ while(cstrs[n] != NULL)
+ n++;
+
+ Py_ssize_t sn;
+ if (!INTEGRAL_ASSIGNMENT_FITS(&sn, n))
+ return PyErr_Format(PyExc_OverflowError, "string array too large");
+
+ PyObject *result = PyTuple_New(sn);
+ Py_ssize_t i = 0;
+ for (i = 0; i < sn; i++)
+ {
+ PyObject *gname = Py_BuildValue(cstr_argf, cstrs[i]);
+ if (gname == NULL)
+ {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(result, i, gname);
+ }
+ 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 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)
+ 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 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");
+
+ 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)
+{
+ PyObject *py_name;
+ if (!PyArg_ParseTuple(args, "S", &py_name))
+ return NULL;
+
+ char *name = PyBytes_AS_STRING(py_name);
+ errno = 0;
+ struct passwd *pwd = getpwnam(name);
+ if (!pwd && errno)
+ return appropriate_errno_ex();
+ return pwd_struct_to_py(pwd);
+}
+
+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)
+ 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 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");
+
+ 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)
+{
+ PyObject *py_name;
+ if (!PyArg_ParseTuple(args, "S", &py_name))
+ return NULL;
+
+ char *name = PyBytes_AS_STRING(py_name);
+ 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
+ char buf[HOST_NAME_MAX + 1] = {};
+#else
+ /* 'SUSv2 guarantees that "Host names are limited to 255 bytes".' */
+ char buf[256] = {};
+#endif
+
+ if (gethostname(buf, sizeof(buf) - 1))
+ return PyErr_SetFromErrno(PyExc_IOError);
+ buf[sizeof(buf) - 1] = 0;
+ return PyBytes_FromString(buf);
+}
+
+
+#ifdef BUP_HAVE_READLINE
+
+static char *cstr_from_bytes(PyObject *bytes)
+{
+ char *buf;
+ Py_ssize_t length;
+ int rc = PyBytes_AsStringAndSize(bytes, &buf, &length);
+ if (rc == -1)
+ return NULL;
+ size_t c_len;
+ if (!INT_ADD_OK(length, 1, &c_len)) {
+ PyErr_Format(PyExc_OverflowError,
+ "Cannot convert ssize_t sized bytes object (%zd) to C string",
+ length);
+ return NULL;
+ }
+ char *result = checked_malloc(c_len, sizeof(char));
+ if (!result)
+ return NULL;
+ memcpy(result, buf, length);
+ result[length] = 0;
+ return result;
+}
+
+static char **cstrs_from_seq(PyObject *seq)
+{
+ char **result = NULL;
+ seq = PySequence_Fast(seq, "Cannot convert sequence items to C strings");
+ if (!seq)
+ return NULL;
+
+ const Py_ssize_t len = PySequence_Fast_GET_SIZE(seq);
+ if (len > PY_SSIZE_T_MAX - 1) {
+ PyErr_Format(PyExc_OverflowError,
+ "Sequence length %zd too large for conversion to C array",
+ len);
+ goto finish;
+ }
+ result = checked_malloc(len + 1, sizeof(char *));
+ if (!result)
+ goto finish;
+ Py_ssize_t i = 0;
+ for (i = 0; i < len; i++)
+ {
+ PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+ if (!item)
+ goto abandon_result;
+ result[i] = cstr_from_bytes(item);
+ if (!result[i]) {
+ i--;
+ goto abandon_result;
+ }
+ }
+ result[len] = NULL;
+ goto finish;
+
+ abandon_result:
+ if (result) {
+ for (; i > 0; i--)
+ free(result[i]);
+ free(result);
+ result = NULL;
+ }
+ finish:
+ Py_DECREF(seq);
+ return result;
+}
+
+static char* our_word_break_chars = NULL;
+
+static PyObject *
+bup_set_completer_word_break_characters(PyObject *self, PyObject *args)
+{
+ char *bytes;
+ if (!PyArg_ParseTuple(args, cstr_argf, &bytes))
+ return NULL;
+ char *prev = our_word_break_chars;
+ char *next = strdup(bytes);
+ if (!next)
+ return PyErr_NoMemory();
+ our_word_break_chars = next;
+ rl_completer_word_break_characters = next;
+ if (prev)
+ free(prev);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+bup_get_completer_word_break_characters(PyObject *self, PyObject *args)
+{
+ return PyBytes_FromString(rl_completer_word_break_characters);
+}
+
+static PyObject *bup_get_line_buffer(PyObject *self, PyObject *args)
+{
+ return PyBytes_FromString(rl_line_buffer);
+}
+
+static PyObject *
+bup_parse_and_bind(PyObject *self, PyObject *args)
+{
+ char *bytes;
+ if (!PyArg_ParseTuple(args, cstr_argf ":parse_and_bind", &bytes))
+ return NULL;
+ char *tmp = strdup(bytes); // Because it may modify the arg
+ if (!tmp)
+ return PyErr_NoMemory();
+ int rc = rl_parse_and_bind(tmp);
+ free(tmp);
+ if (rc != 0)
+ return PyErr_Format(PyExc_OSError,
+ "system rl_parse_and_bind failed (%d)", rc);
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_on_attempted_completion;
+static char **prev_completions;
+
+static char **on_attempted_completion(const char *text, int start, int end)
+{
+ if (!py_on_attempted_completion)
+ return NULL;
+
+ char **result = NULL;
+ PyObject *py_result = PyObject_CallFunction(py_on_attempted_completion,
+ cstr_argf "ii",
+ text, start, end);
+ if (!py_result)
+ return NULL;
+ if (py_result != Py_None) {
+ result = cstrs_from_seq(py_result);
+ free(prev_completions);
+ prev_completions = result;
+ }
+ Py_DECREF(py_result);
+ return result;
+}
+
+static PyObject *
+bup_set_attempted_completion_function(PyObject *self, PyObject *args)
+{
+ PyObject *completer;
+ if (!PyArg_ParseTuple(args, "O", &completer))
+ return NULL;
+
+ PyObject *prev = py_on_attempted_completion;
+ if (completer == Py_None)
+ {
+ py_on_attempted_completion = NULL;
+ rl_attempted_completion_function = NULL;
+ } else {
+ py_on_attempted_completion = completer;
+ rl_attempted_completion_function = on_attempted_completion;
+ Py_INCREF(completer);
+ }
+ Py_XDECREF(prev);
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_on_completion_entry;
+
+static char *on_completion_entry(const char *text, int state)
+{
+ if (!py_on_completion_entry)
+ return NULL;
+
+ PyObject *py_result = PyObject_CallFunction(py_on_completion_entry,
+ cstr_argf "i", text, state);
+ if (!py_result)
+ return NULL;
+ char *result = (py_result == Py_None) ? NULL : cstr_from_bytes(py_result);
+ Py_DECREF(py_result);
+ return result;
+}
+
+static PyObject *
+bup_set_completion_entry_function(PyObject *self, PyObject *args)
+{
+ PyObject *completer;
+ if (!PyArg_ParseTuple(args, "O", &completer))
+ return NULL;
+
+ PyObject *prev = py_on_completion_entry;
+ if (completer == Py_None) {
+ py_on_completion_entry = NULL;
+ rl_completion_entry_function = NULL;
+ } else {
+ py_on_completion_entry = completer;
+ rl_completion_entry_function = on_completion_entry;
+ Py_INCREF(completer);
+ }
+ Py_XDECREF(prev);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+bup_readline(PyObject *self, PyObject *args)
+{
+ char *prompt;
+ if (!PyArg_ParseTuple(args, cstr_argf, &prompt))
+ return NULL;
+ char *line = readline(prompt);
+ if (!line)
+ return PyErr_Format(PyExc_EOFError, "readline EOF");
+ PyObject *result = PyBytes_FromString(line);
+ free(line);
+ return result;
+}
+
+#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 PyObject *bup_limited_vint_pack(PyObject *self, PyObject *args)
+{
+ const char *fmt;
+ PyObject *packargs, *result;
+ Py_ssize_t sz, i, bufsz;
+ char *buf, *pos, *end;
+
+ if (!PyArg_ParseTuple(args, "sO", &fmt, &packargs))
+ return NULL;
+
+ if (!PyTuple_Check(packargs))
+ return PyErr_Format(PyExc_Exception, "pack() arg must be tuple");
+
+ sz = PyTuple_GET_SIZE(packargs);
+ if (sz != (Py_ssize_t)strlen(fmt))
+ return PyErr_Format(PyExc_Exception,
+ "number of arguments (%ld) does not match format string (%ld)",
+ (unsigned long)sz, (unsigned long)strlen(fmt));
+
+ if (sz > INT_MAX / 20)
+ return PyErr_Format(PyExc_Exception, "format is far too long");
+
+ // estimate no more than 20 bytes for each on average, the maximum
+ // vint/vuint we can encode is anyway 10 bytes, so this gives us
+ // some headroom for a few strings before we need to realloc ...
+ bufsz = sz * 20;
+ buf = malloc(bufsz);
+ if (!buf)
+ return PyErr_NoMemory();
+
+ pos = buf;
+ end = buf + bufsz;
+ for (i = 0; i < sz; i++) {
+ PyObject *item = PyTuple_GET_ITEM(packargs, i);
+ const char *bytes;
+
+ switch (fmt[i]) {
+ case 'V': {
+ long long val = PyLong_AsLongLong(item);
+ if (val == -1 && PyErr_Occurred())
+ return PyErr_Format(PyExc_OverflowError,
+ "pack arg %d invalid", (int)i);
+ if (end - pos < 10)
+ goto overflow;
+ pos += vuint_encode(val, pos);
+ break;
+ }
+ case 'v': {
+ long long val = PyLong_AsLongLong(item);
+ if (val == -1 && PyErr_Occurred())
+ return PyErr_Format(PyExc_OverflowError,
+ "pack arg %d invalid", (int)i);
+ if (end - pos < 10)
+ goto overflow;
+ pos += vint_encode(val, pos);
+ break;
+ }
+ case 's': {
+ bytes = PyBytes_AsString(item);
+ if (!bytes)
+ goto error;
+ if (end - pos < 10)
+ goto overflow;
+ Py_ssize_t val = PyBytes_GET_SIZE(item);
+ pos += vuint_encode(val, pos);
+ if (end - pos < val)
+ goto overflow;
+ memcpy(pos, bytes, val);
+ pos += val;
+ break;
+ }
+ default:
+ PyErr_Format(PyExc_Exception, "unknown xpack format string item %c",
+ fmt[i]);
+ goto error;
+ }
+ }
+
+ result = PyBytes_FromStringAndSize(buf, pos - buf);
+ free(buf);
+ return result;
+
+ overflow:
+ PyErr_SetString(PyExc_OverflowError, "buffer (potentially) overflowed");
+ error:
+ free(buf);
+ return NULL;
+}
+