Bug 16727: Handle ERANGE for getgr; fixes user in many groups
authorMarc Brockschmidt <he@debian.org>
Mon, 28 Jul 2008 20:09:53 +0000 (16:09 -0400)
committerColin Walters <walters@verbum.org>
Mon, 28 Jul 2008 20:14:18 +0000 (16:14 -0400)
Patch originally from Noèl Köthe.
Modified by Colin Walters <walters@verbum.org>

* dbus/dbus-sysdeps-unix.c, dbus/dbus-sysdeps-unix-utils.c:
Use a while() loop to reallocate buffer if we get ERANGE
return.  This fixes the case where a user is in a large
number of groups.

dbus/dbus-sysdeps-unix.c
dbus/dbus-sysdeps-util-unix.c

index 64d925d..c96499a 100644 (file)
@@ -1487,28 +1487,60 @@ fill_user_info (DBusUserInfo       *info,
   {
     struct passwd *p;
     int result;
-    char buf[1024];
+    size_t buflen;
+    char *buf;
     struct passwd p_str;
 
-    p = NULL;
+    /* retrieve maximum needed size for buf */
+    buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
+
+    if (buflen <= 0)
+      buflen = 1024;
+
+    result = -1;
+    while (1)
+      {
+        buf = dbus_malloc (buflen);
+        if (buf == NULL)
+          {
+            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+            return FALSE;
+          }
+
+        p = NULL;
 #ifdef HAVE_POSIX_GETPWNAM_R
-    if (uid != DBUS_UID_UNSET)
-      result = getpwuid_r (uid, &p_str, buf, sizeof (buf),
-                           &p);
-    else
-      result = getpwnam_r (username_c, &p_str, buf, sizeof (buf),
-                           &p);
+        if (uid != DBUS_UID_UNSET)
+          result = getpwuid_r (uid, &p_str, buf, buflen,
+                               &p);
+        else
+          result = getpwnam_r (username_c, &p_str, buf, buflen,
+                               &p);
 #else
-    if (uid != DBUS_UID_UNSET)
-      p = getpwuid_r (uid, &p_str, buf, sizeof (buf));
-    else
-      p = getpwnam_r (username_c, &p_str, buf, sizeof (buf));
-    result = 0;
+        if (uid != DBUS_UID_UNSET)
+          p = getpwuid_r (uid, &p_str, buf, buflen);
+        else
+          p = getpwnam_r (username_c, &p_str, buf, buflen);
+        result = 0;
 #endif /* !HAVE_POSIX_GETPWNAM_R */
+        //Try a bigger buffer if ERANGE was returned
+        if (result == ERANGE && buflen < 512 * 1024)
+          {
+            dbus_free (buf);
+            buflen *= 2;
+          }
+        else
+          {
+            break;
+          }
+      }
     if (result == 0 && p == &p_str)
       {
         if (!fill_user_info_from_passwd (p, info, error))
-          return FALSE;
+          {
+            dbus_free (buf);
+            return FALSE;
+          }
+        dbus_free (buf);
       }
     else
       {
@@ -1516,6 +1548,7 @@ fill_user_info (DBusUserInfo       *info,
                         "User \"%s\" unknown or no memory to allocate password entry\n",
                         username_c ? username_c : "???");
         _dbus_verbose ("User %s unknown\n", username_c ? username_c : "???");
+        dbus_free (buf);
         return FALSE;
       }
   }
@@ -1532,7 +1565,9 @@ fill_user_info (DBusUserInfo       *info,
     if (p != NULL)
       {
         if (!fill_user_info_from_passwd (p, info, error))
-          return FALSE;
+          {
+            return FALSE;
+          }
       }
     else
       {
index 9ff3fbc..55eb934 100644 (file)
@@ -828,31 +828,65 @@ fill_group_info (DBusGroupInfo    *info,
   {
     struct group *g;
     int result;
-    char buf[1024];
+    size_t buflen;
+    char *buf;
     struct group g_str;
+    dbus_bool_t b;
 
-    g = NULL;
-#ifdef HAVE_POSIX_GETPWNAM_R
+    /* retrieve maximum needed size for buf */
+    buflen = sysconf (_SC_GETGR_R_SIZE_MAX);
 
-    if (group_c_str)
-      result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
-                           &g);
-    else
-      result = getgrgid_r (gid, &g_str, buf, sizeof (buf),
-                           &g);
+    if (buflen <= 0)
+      buflen = 1024;
+
+    result = -1;
+    while (1)
+      {
+        buf = dbus_malloc (buflen);
+        if (buf == NULL)
+          {
+            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+            return FALSE;
+          }
+
+        g = NULL;
+#ifdef HAVE_POSIX_GETPWNAM_R
+        if (group_c_str)
+          result = getgrnam_r (group_c_str, &g_str, buf, buflen,
+                               &g);
+        else
+          result = getgrgid_r (gid, &g_str, buf, buflen,
+                               &g);
 #else
-    g = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
-    result = 0;
+        g = getgrnam_r (group_c_str, &g_str, buf, buflen);
+        result = 0;
 #endif /* !HAVE_POSIX_GETPWNAM_R */
+        /* Try a bigger buffer if ERANGE was returned:
+           https://bugs.freedesktop.org/show_bug.cgi?id=16727
+        */
+        if (result == ERANGE && buflen < 512 * 1024)
+          {
+            dbus_free (buf);
+            buflen *= 2;
+          }
+        else
+          {
+            break;
+          }
+      }
+
     if (result == 0 && g == &g_str)
       {
-        return fill_user_info_from_group (g, info, error);
+        b = fill_user_info_from_group (g, info, error);
+        dbus_free (buf);
+        return b;
       }
     else
       {
         dbus_set_error (error, _dbus_error_from_errno (errno),
                         "Group %s unknown or failed to look it up\n",
                         group_c_str ? group_c_str : "???");
+        dbus_free (buf);
         return FALSE;
       }
   }