diff -r e4dc8be9a72f Lib/test/test_posix.py --- a/Lib/test/test_posix.py Sat Jul 14 14:12:35 2012 -0700 +++ b/Lib/test/test_posix.py Sat Jul 14 18:10:24 2012 -0700 @@ -387,20 +387,41 @@ else: self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) - def _test_all_chown_common(self, chown_func, first_param): + def _test_all_chown_common(self, chown_func, stat_func, first_param): """Common code for chown, fchown and lchown tests.""" # test a successful chown call chown_func(first_param, os.getuid(), os.getgid()) if os.getuid() == 0: try: - # Many linux distros have a nfsnobody user as MAX_UID-2 - # that makes a good test case for signedness issues. + # Try an amusingly large uid/gid to make sure we handle + # large unsigned values. (chown lets you use any + # uid/gid you like, even if they aren't defined.) + # + # This problem keeps coming up: # http://bugs.python.org/issue1747858 + # http://bugs.python.org/issue4591 + # http://bugs.python.org/issue15301 + # Hopefully the fix in 15301 fixes it for good! + # # This part of the test only runs when run as root. # Only scary people run their tests as root. - ent = pwd.getpwnam('nfsnobody') - chown_func(first_param, ent.pw_uid, ent.pw_gid) + + big_value = 2**30 + uid = os.getuid() + gid = os.getgid() + chown_func(first_param, big_value, big_value) + st = stat_func(first_param) + self.assertEqual(st.st_uid, big_value) + self.assertEqual(st.st_gid, big_value) + chown_func(first_param, -1, -1) + st = stat_func(first_param) + self.assertEqual(st.st_uid, big_value) + self.assertEqual(st.st_gid, big_value) + chown_func(first_param, uid, gid) + st = stat_func(first_param) + self.assertEqual(st.st_uid, uid) + self.assertEqual(st.st_gid, gid) except KeyError: pass elif platform.system() in ('HP-UX', 'SunOS'): @@ -421,7 +442,7 @@ # re-create the file support.create_empty_file(support.TESTFN) - self._test_all_chown_common(posix.chown, support.TESTFN) + self._test_all_chown_common(posix.chown, posix.stat, support.TESTFN) @unittest.skipUnless(hasattr(posix, 'fchown'), "test needs os.fchown()") def test_fchown(self): @@ -431,7 +452,7 @@ test_file = open(support.TESTFN, 'w') try: fd = test_file.fileno() - self._test_all_chown_common(posix.fchown, fd) + self._test_all_chown_common(posix.fchown, posix.fstat, fd) finally: test_file.close() @@ -440,7 +461,7 @@ os.unlink(support.TESTFN) # create a symlink os.symlink(_DUMMY_SYMLINK, support.TESTFN) - self._test_all_chown_common(posix.lchown, support.TESTFN) + self._test_all_chown_common(posix.lchown, posix.lstat, support.TESTFN) def test_chdir(self): if hasattr(posix, 'chdir'): diff -r e4dc8be9a72f Modules/posixmodule.c --- a/Modules/posixmodule.c Sat Jul 14 14:12:35 2012 -0700 +++ b/Modules/posixmodule.c Sat Jul 14 18:10:24 2012 -0700 @@ -2154,8 +2154,8 @@ PyStructSequence_SET_ITEM(v, 2, PyLong_FromLong((long)st->st_dev)); #endif PyStructSequence_SET_ITEM(v, 3, PyLong_FromLong((long)st->st_nlink)); - PyStructSequence_SET_ITEM(v, 4, PyLong_FromLong((long)st->st_uid)); - PyStructSequence_SET_ITEM(v, 5, PyLong_FromLong((long)st->st_gid)); + PyStructSequence_SET_ITEM(v, 4, PyLong_FromUnsignedLong((long)st->st_uid)); + PyStructSequence_SET_ITEM(v, 5, PyLong_FromUnsignedLong((long)st->st_gid)); #ifdef HAVE_LARGEFILE_SUPPORT PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong((PY_LONG_LONG)st->st_size)); @@ -2956,11 +2956,95 @@ dir_fd and follow_symlinks may not be implemented on your platform.\n\ If they are unavailable, using them will raise a NotImplementedError."); +static int +uid_converter(PyObject *obj, void *p) +{ + PyObject *index; + PyObject *number = NULL; + long sl; + unsigned long ul; + uid_t *output = (uid_t *)p; + uid_t uid; + + index = PyNumber_Index(obj); + if (index != NULL) { + number = PyNumber_Long(index); + Py_DECREF(index); + } + if (number == NULL) { + PyErr_SetString(PyExc_TypeError, "user id must be integer"); + return 0; + } + + sl = PyLong_AsLong(number); + if (PyErr_Occurred()) { + PyErr_Clear(); + } else if (sl == -1) { + Py_DECREF(number); + *output = (uid_t)-1; + return 1; + } + + ul = PyLong_AsUnsignedLong(number); + Py_DECREF(number); + uid = ul; + /* read back the value to ensure it fits in a uid_t */ + if (PyErr_Occurred() || uid != ul) { + PyErr_SetString(PyExc_TypeError, + "user id is not a valid uid_t"); + return 0; + } + *output = uid; + return 1; +} + +static int +gid_converter(PyObject *obj, void *p) +{ + PyObject *index; + PyObject *number = NULL; + long sl; + unsigned long ul; + gid_t *output = (gid_t *)p; + gid_t gid; + + index = PyNumber_Index(obj); + if (index != NULL) { + number = PyNumber_Long(index); + Py_DECREF(index); + } + if (number == NULL) { + PyErr_SetString(PyExc_TypeError, "group id must be integer"); + return 0; + } + + sl = PyLong_AsLong(number); + if (PyErr_Occurred()) { + PyErr_Clear(); + } else if (sl == -1) { + Py_DECREF(number); + *output = (gid_t)-1; + return 1; + } + + ul = PyLong_AsUnsignedLong(number); + Py_DECREF(number); + gid = ul; + /* read back the value to ensure it fits in a gid_t */ + if (PyErr_Occurred() || gid != ul) { + PyErr_SetString(PyExc_TypeError, + "group id is not a valid gid_t"); + return 0; + } + *output = gid; + return 1; +} + + static PyObject * posix_chown(PyObject *self, PyObject *args, PyObject *kwargs) { path_t path; - long uid_l, gid_l; uid_t uid; gid_t gid; int dir_fd = DEFAULT_DIR_FD; @@ -2974,9 +3058,10 @@ #ifdef HAVE_FCHOWN path.allow_fd = 1; #endif - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&ll|$O&p:chown", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&|$O&p:chown", keywords, path_converter, &path, - &uid_l, &gid_l, + uid_converter, &uid, + gid_converter, &gid, #ifdef HAVE_FCHOWNAT dir_fd_converter, &dir_fd, #else @@ -3007,8 +3092,6 @@ #endif Py_BEGIN_ALLOW_THREADS - uid = (uid_t)uid_l; - gid = (uid_t)gid_l; #ifdef HAVE_FCHOWN if (path.fd != -1) result = fchown(path.fd, uid, gid); @@ -3052,12 +3135,15 @@ posix_fchown(PyObject *self, PyObject *args) { int fd; - long uid, gid; + uid_t uid; + gid_t gid; int res; - if (!PyArg_ParseTuple(args, "ill:fchown", &fd, &uid, &gid)) + if (!PyArg_ParseTuple(args, "iO&O&:fchown", &fd, + uid_converter, &uid, + gid_converter, &gid)) return NULL; Py_BEGIN_ALLOW_THREADS - res = fchown(fd, (uid_t) uid, (gid_t) gid); + res = fchown(fd, uid, gid); Py_END_ALLOW_THREADS if (res < 0) return posix_error(); @@ -3077,15 +3163,18 @@ { PyObject *opath; char *path; - long uid, gid; + uid_t uid; + gid_t gid; int res; - if (!PyArg_ParseTuple(args, "O&ll:lchown", + if (!PyArg_ParseTuple(args, "O&O&O&:lchown", PyUnicode_FSConverter, &opath, + uid_converter, &uid, + gid_converter, &gid, &uid, &gid)) return NULL; path = PyBytes_AsString(opath); Py_BEGIN_ALLOW_THREADS - res = lchown(path, (uid_t) uid, (gid_t) gid); + res = lchown(path, uid, gid); Py_END_ALLOW_THREADS if (res < 0) return posix_error_with_allocated_filename(opath);