]> granicus.if.org Git - python/commitdiff
This fixes issue7900 by adding code that deals
authorRonald Oussoren <ronaldoussoren@mac.com>
Fri, 23 Jul 2010 15:46:03 +0000 (15:46 +0000)
committerRonald Oussoren <ronaldoussoren@mac.com>
Fri, 23 Jul 2010 15:46:03 +0000 (15:46 +0000)
with the fact that getgroups(2) might return
more that MAX_GROUPS on OSX.

See the issue (and python-dev archives) for the
gory details. Summarized: OSX behaves rather oddly
and Apple says this is intentional.

Lib/test/test_posix.py
Misc/NEWS
Modules/posixmodule.c

index 78f7592a1d340d141a29577f22f6741b45063de2..31459ffd3c864fdb40a2a98fd0208e4b7f24955b 100644 (file)
@@ -5,6 +5,7 @@ from test import support
 # Skip these tests if there is no posix module.
 posix = support.import_module('posix')
 
+import sys
 import time
 import os
 import pwd
@@ -273,9 +274,59 @@ class PosixTester(unittest.TestCase):
                 os.chdir(curdir)
                 support.rmtree(base_path)
 
+    def test_getgroups(self):
+        with os.popen('id -G') as idg:
+            groups = idg.read().strip()
+
+        if not groups:
+            raise unittest.SkipTest("need working 'id -G'")
+
+        self.assertEqual([int(x) for x in groups.split()], posix.getgroups())
+
+class PosixGroupsTester(unittest.TestCase):
+
+    def setUp(self):
+        if posix.getuid() != 0:
+            raise unittest.SkipTest("not enough privileges")
+        if not hasattr(posix, 'getgroups'):
+            raise unittest.SkipTest("need posix.getgroups")
+        if sys.platform == 'darwin':
+            raise unittest.SkipTest("getgroups(2) is broken on OSX")
+        self.saved_groups = posix.getgroups()
+
+    def tearDown(self):
+        if hasattr(posix, 'setgroups'):
+            posix.setgroups(self.saved_groups)
+        elif hasattr(posix, 'initgroups'):
+            name = pwd.getpwuid(posix.getuid()).pw_name
+            posix.initgroups(name, self.saved_groups[0])
+
+    @unittest.skipUnless(hasattr(posix, 'initgroups'),
+                         "test needs posix.initgroups()")
+    def test_initgroups(self):
+        # find missing group
+
+        groups = sorted(self.saved_groups)
+        for g1,g2 in zip(groups[:-1], groups[1:]):
+            g = g1 + 1
+            if g < g2:
+                break
+        else:
+            g = g2 + 1
+        name = pwd.getpwuid(posix.getuid()).pw_name
+        posix.initgroups(name, g)
+        self.assertIn(g, posix.getgroups())
+
+    @unittest.skipUnless(hasattr(posix, 'setgroups'),
+                         "test needs posix.setgroups()")
+    def test_setgroups(self):
+        for groups in [[0], range(16)]:
+            posix.setgroups(groups)
+            self.assertListEqual(groups, posix.getgroups())
+
 
 def test_main():
-    support.run_unittest(PosixTester)
+    support.run_unittest(PosixTester, PosixGroupsTester)
 
 if __name__ == '__main__':
     test_main()
index 4756dafea830afc466133fbda8f4045900e80b14..294ced7e7b2b9eda1c4a92df036a5e96fb0221b2 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -316,6 +316,13 @@ Library
 Extension Modules
 -----------------
 
+- Issue #7900: The getgroups(2) system call on MacOSX behaves rather oddly
+  compared to other unix systems. In particular, os.getgroups() does
+  not reflect any changes made using os.setgroups() but basicly always
+  returns the same information as the id command.
+
+  os.getgroups() can now return more than 16 groups on MacOSX.
+
 - Issue #7384: If the system readline library is linked against
   ncurses, do not link the readline module against ncursesw. The
   additional restriction of linking the readline and curses modules
index 65a0a5e916dd24beada2ddfaeaf65abd7bf6f11c..cb3075bdbc975439e95e4a7ed511352e4bee9580 100644 (file)
@@ -3995,17 +3995,49 @@ posix_getgroups(PyObject *self, PyObject *noargs)
 #define MAX_GROUPS 64
 #endif
     gid_t grouplist[MAX_GROUPS];
+
+    /* On MacOSX getgroups(2) can return more than MAX_GROUPS results 
+     * This is a helper variable to store the intermediate result when
+     * that happens.
+     *
+     * To keep the code readable the OSX behaviour is unconditional,
+     * according to the POSIX spec this should be safe on all unix-y
+     * systems.
+     */
+    gid_t* alt_grouplist = grouplist;
     int n;
 
     n = getgroups(MAX_GROUPS, grouplist);
-    if (n < 0)
-        posix_error();
-    else {
-        result = PyList_New(n);
-        if (result != NULL) {
+    if (n < 0) {
+        if (errno == EINVAL) {
+            n = getgroups(0, NULL);
+            if (n == -1) {
+                return posix_error();
+            }
+            if (n == 0) {
+                /* Avoid malloc(0) */
+                alt_grouplist = grouplist;
+            } else {
+                alt_grouplist = PyMem_Malloc(n * sizeof(gid_t));
+                if (alt_grouplist == NULL) {
+                    errno = EINVAL;
+                    return posix_error();
+                }
+                n = getgroups(n, alt_grouplist);
+                if (n == -1) {
+                    PyMem_Free(alt_grouplist);
+                    return posix_error();
+                }
+            }
+        } else {
+            return posix_error();
+        }
+    }
+    result = PyList_New(n);
+    if (result != NULL) {
         int i;
         for (i = 0; i < n; ++i) {
-            PyObject *o = PyLong_FromLong((long)grouplist[i]);
+            PyObject *o = PyLong_FromLong((long)alt_grouplist[i]);
             if (o == NULL) {
             Py_DECREF(result);
             result = NULL;
@@ -4013,7 +4045,10 @@ posix_getgroups(PyObject *self, PyObject *noargs)
             }
             PyList_SET_ITEM(result, i, o);
         }
-        }
+    }
+
+    if (alt_grouplist != grouplist) {
+        PyMem_Free(alt_grouplist);
     }
 
     return result;