Define initgroups callback for nss_files.
authorUlrich Drepper <drepper@gmail.com>
Tue, 19 Apr 2011 17:43:03 +0000 (13:43 -0400)
committerUlrich Drepper <drepper@gmail.com>
Tue, 19 Apr 2011 17:43:03 +0000 (13:43 -0400)
ChangeLog
nss/Makefile
nss/Versions
nss/nss_files/files-initgroups.c [new file with mode: 0644]

index 1d17002..91ddfb5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2011-04-19  Ulrich Drepper  <drepper@gmail.com>
+
+       * nss/nss_files/files-initgroups.c: New file.
+       * nss/Makefile (libnss_files-routines): Add files-initgroups.
+       * nss/Versions (libnss_files) [GLIBC_PRIVATE]: Export
+       _nss_files_initgroups_dyn.
+
 2011-03-31  Richard Sandiford  <richard.sandiford@linaro.org>
 
        * elf/elf.h (R_ARM_IRELATIVE): Define.
index 2b0432f..c49f375 100644 (file)
@@ -64,7 +64,7 @@ vpath %.c $(subdir-dirs)
 
 
 libnss_files-routines  := $(addprefix files-,$(databases)) \
-                          files-have_o_cloexec
+                          files-initgroups files-have_o_cloexec
 distribute             += files-XXX.c files-parse.c
 
 
index 7a9d67b..1c6fd68 100644 (file)
@@ -95,5 +95,7 @@ libnss_files {
     _nss_netgroup_parseline;
     _nss_files_getpublickey;
     _nss_files_getsecretkey;
+
+    _nss_files_initgroups_dyn;
   }
 }
diff --git a/nss/nss_files/files-initgroups.c b/nss/nss_files/files-initgroups.c
new file mode 100644 (file)
index 0000000..2d84cd8
--- /dev/null
@@ -0,0 +1,132 @@
+/* Initgroups handling in nss_files module.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <alloca.h>
+#include <errno.h>
+#include <grp.h>
+#include <nss.h>
+#include <stdio_ext.h>
+#include <string.h>
+#include <sys/param.h>
+
+enum nss_status
+_nss_files_initgroups_dyn (const char *user, gid_t group, long int *start,
+                          long int *size, gid_t **groupsp, long int limit,
+                          int *errnop)
+{
+  FILE *stream = fopen ("/etc/group", "re");
+  if (stream == NULL)
+    {
+      *errnop = errno;
+      return *errnop == ENOMEM ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
+    }
+
+  /* No other thread using this stream.  */
+  __fsetlocking (stream, FSETLOCKING_BYCALLER);
+
+  char *line = NULL;
+  size_t linelen = 0;
+  enum nss_status status = NSS_STATUS_SUCCESS;
+
+  size_t buflen = 1024;
+  void *buffer = alloca (buflen);
+  bool buffer_use_malloc = false;
+
+  gid_t *groups = *groupsp;
+
+  /* We have to iterate over the entire file.  */
+  while (!feof_unlocked (stream))
+    {
+      ssize_t n = getline (&line, &linelen, stream);
+      if (n < 0)
+       {
+         if (! feof_unlocked (stream))
+           status = ((*errnop = errno) == ENOMEM
+                     ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL);
+         break;
+       }
+
+      struct group grp;
+      while (_nss_files_parse_grent (line, &grp, buffer, buflen, errnop) == -1)
+       {
+         size_t newbuflen = 2 * buflen;
+         if (buffer_use_malloc || ! __libc_use_alloca (buflen + newbuflen))
+           {
+             char *newbuf = realloc (buffer, buflen);
+             if (newbuf == NULL)
+               {
+                 *errnop = ENOMEM;
+                 status = NSS_STATUS_TRYAGAIN;
+                 goto out;
+               }
+             buffer = newbuf;
+             buflen = newbuflen;
+             buffer_use_malloc = true;
+           }
+         else
+           buffer = extend_alloca (buffer, buflen, newbuflen);
+       }
+
+      if (grp.gr_gid != group)
+       for (char **m = grp.gr_mem; *m != NULL; ++m)
+         if (strcmp (*m, user) == 0)
+           {
+             /* Matches user.  Insert this group.  */
+             if (*start == *size)
+               {
+                 /* Need a bigger buffer.  */
+                 if (limit > 0 && *size == limit)
+                   /* We reached the maximum.  */
+                   goto out;
+
+                 long int newsize;
+                 if (limit <= 0)
+                   newsize = 2 * *size;
+                 else
+                   newsize = MIN (limit, 2 * *size);
+
+                 gid_t *newgroups = realloc (groups,
+                                             newsize * sizeof (*groups));
+                 if (newgroups == NULL)
+                   {
+                     *errnop = ENOMEM;
+                     status = NSS_STATUS_TRYAGAIN;
+                     goto out;
+                   }
+                 *groupsp = groups = newgroups;
+                 *size = newsize;
+               }
+
+             groups[*start] = grp.gr_gid;
+             *start += 1;
+
+             break;
+           }
+    }
+
+ out:
+  /* Free memory.  */
+  if (buffer_use_malloc)
+    free (buffer);
+  free (line);
+
+  fclose (stream);
+
+  return status;
+}