From: Rob Browning Date: Thu, 28 May 2020 05:44:16 +0000 (-0500) Subject: pwdgrp: add C helpers to get user/group bytes directly X-Git-Tag: 0.31~67 X-Git-Url: https://arthur.barton.de/gitweb/?p=bup.git;a=commitdiff_plain;h=8a846056d493f9f49523deae5a804b9566f14854 pwdgrp: add C helpers to get user/group bytes directly Thanks to Johannes Berg for fixing some bugs in an earlier revision. Signed-off-by: Rob Browning Signed-off-by: Johannes Berg Tested-by: Rob Browning --- diff --git a/lib/bup/_helpers.c b/lib/bup/_helpers.c index 0ea5dff..4a6aaf5 100644 --- a/lib/bup/_helpers.c +++ b/lib/bup/_helpers.c @@ -7,10 +7,12 @@ // http://docs.python.org/2/c-api/intro.html#include-files #include +#include #include #include #include -#include +#include +#include #include #include #include @@ -1712,6 +1714,154 @@ static PyObject *bup_mincore(PyObject *self, PyObject *args) #endif /* def BUP_MINCORE_BUF_TYPE */ +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 long getpw_buf_size; + +static PyObject *pwd_struct_to_py(const struct passwd *pwd, int rc) +{ + // 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); +} + +static PyObject *bup_getpwuid(PyObject *self, PyObject *args) +{ + unsigned long uid; + if (!PyArg_ParseTuple(args, "k", &uid)) + return NULL; + + 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; +} + +static PyObject *bup_getpwnam(PyObject *self, PyObject *args) +{ + PyObject *py_name; + 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; +} + +static long getgr_buf_size; + +static PyObject *grp_struct_to_py(const struct group *grp, int rc) +{ + // 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); +} + +static PyObject *bup_getgrgid(PyObject *self, PyObject *args) +{ + unsigned long gid; + if (!PyArg_ParseTuple(args, "k", &gid)) + return NULL; + + 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; +} + +static PyObject *bup_getgrnam(PyObject *self, PyObject *args) +{ + PyObject *py_name; + 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; +} + static PyMethodDef helper_methods[] = { { "write_sparsely", bup_write_sparsely, METH_VARARGS, "Write buf excepting zeros at the end. Return trailing zero count." }, @@ -1783,6 +1933,22 @@ static PyMethodDef helper_methods[] = { "For mincore(src, src_n, src_off, dest, dest_off)" " call the system mincore(src + src_off, src_n, &dest[dest_off])." }, #endif + { "getpwuid", bup_getpwuid, METH_VARARGS, + "Return the password database entry for the given numeric user id," + " as a tuple with all C strings as bytes(), or None if the user does" + " not exist." }, + { "getpwnam", bup_getpwnam, METH_VARARGS, + "Return the password database entry for the given user name," + " as a tuple with all C strings as bytes(), or None if the user does" + " not exist." }, + { "getgrgid", bup_getgrgid, METH_VARARGS, + "Return the group database entry for the given numeric group id," + " as a tuple with all C strings as bytes(), or None if the group does" + " not exist." }, + { "getgrnam", bup_getgrnam, METH_VARARGS, + "Return the group database entry for the given group name," + " as a tuple with all C strings as bytes(), or None if the group does" + " not exist." }, { NULL, NULL, 0, NULL }, // sentinel }; @@ -1877,6 +2043,14 @@ 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(); diff --git a/lib/bup/pwdgrp.py b/lib/bup/pwdgrp.py index 4374bea..a5b6d8b 100644 --- a/lib/bup/pwdgrp.py +++ b/lib/bup/pwdgrp.py @@ -2,8 +2,7 @@ from __future__ import absolute_import, print_function import os, pwd, grp -from bup import compat # to force the LC_CTYPE check -from bup.compat import py_maj +from bup import _helpers from bup.helpers import cache_key_value @@ -23,23 +22,14 @@ class Passwd: self.pw_gecos, self.pw_dir, self.pw_shell) = \ name, passwd, uid, gid, gecos, dir, shell -def _passwd_from_py(py): - if py_maj < 3: - return py - return Passwd(py.pw_name.encode('iso-8859-1'), - py.pw_passwd.encode("iso-8859-1"), - py.pw_uid, py.pw_gid, - py.pw_gecos.encode('iso-8859-1'), - py.pw_dir.encode('iso-8859-1'), - py.pw_shell.encode('iso-8859-1')) - def getpwuid(uid): - return _passwd_from_py(pwd.getpwuid(uid)) + r = _helpers.getpwuid(uid) + return Passwd(*r) if r else None def getpwnam(name): assert isinstance(name, bytes) - return _passwd_from_py(pwd.getpwnam(name.decode('iso-8859-1') if py_maj > 2 - else name)) + r = _helpers.getpwnam(name) + return Passwd(*r) if r else None class Group: @@ -53,21 +43,14 @@ class Group: self.gr_name, self.gr_passwd, self.gr_gid, self.gr_mem = \ name, passwd, gid, mem -def _group_from_py(py): - if py_maj < 3: - return py - return Group(py.gr_name.encode('iso-8859-1'), - py.gr_passwd.encode('iso-8859-1'), - py.gr_gid, - tuple(x.encode('iso-8859-1') for x in py.gr_mem)) - def getgrgid(uid): - return _group_from_py(grp.getgrgid(uid)) + r = _helpers.getgrgid(uid) + return Group(*r) if r else None def getgrnam(name): assert isinstance(name, bytes) - return _group_from_py(grp.getgrnam(name.decode('iso-8859-1') if py_maj > 2 - else name)) + r = _helpers.getgrnam(name) + return Group(*r) if r else None _uid_to_pwd_cache = {}