From dd5351bd3ca731fa5caada713ce522af84edef58 Mon Sep 17 00:00:00 2001 From: Rob Browning Date: Tue, 30 Jun 2020 18:50:17 -0500 Subject: [PATCH] Drop pwd/gid reentrant function variants for now From the POSIX pages, it sounded like _SC_GETPW_R_SIZE_MAX, etc. were upper limits, but other docs, and observed behavior (reported ERANGE failures) indicate that it's more likely they're just suggestions. As a result, we'd either need larger hard-coded buffers, or a reallocate/retry loop if we kept them. Instead, since we're not at all thread-safe and aren't likely to be anytime soon, the dynamic allocations are just less efficient and more complex, so back off to the non-reentrant flavors for now. Thanks to kd7spq for reporting the problem and Johannes Berg for tracking down the cause and help settling on this solution. Signed-off-by: Rob Browning Tested-by: Rob Browning --- lib/bup/_helpers.c | 141 +++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 82 deletions(-) diff --git a/lib/bup/_helpers.c b/lib/bup/_helpers.c index ed08aa3..ff4eb7a 100644 --- a/lib/bup/_helpers.c +++ b/lib/bup/_helpers.c @@ -1766,31 +1766,38 @@ 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) @@ -1802,15 +1809,11 @@ static PyObject *bup_getpwuid(PyObject *self, PyObject *args) 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) @@ -1819,41 +1822,30 @@ 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); - errno = rc; - if (rc == EIO || rc == EMFILE || rc == ENFILE) - return PyErr_SetFromErrno(PyExc_IOError); - return PyErr_SetFromErrno(PyExc_OSError); + 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) @@ -1865,15 +1857,11 @@ static PyObject *bup_getgrgid(PyObject *self, PyObject *args) 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) @@ -1882,18 +1870,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 @@ -2459,14 +2444,6 @@ static int setup_module(PyObject *m) #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); unpythonize_argv(); -- 2.39.2