LCOV - code coverage report
Current view: top level - lib - mgetgroups.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 13 45 28.9 %
Date: 2018-01-30 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /* mgetgroups.c -- return a list of the groups a user is in
       2             : 
       3             :    Copyright (C) 2007-2008 Free Software Foundation, Inc.
       4             : 
       5             :    This program is free software: you can redistribute it and/or modify
       6             :    it under the terms of the GNU General Public License as published by
       7             :    the Free Software Foundation, either version 3 of the License, or
       8             :    (at your option) any later version.
       9             : 
      10             :    This program is distributed in the hope that it will be useful,
      11             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :    GNU General Public License for more details.
      14             : 
      15             :    You should have received a copy of the GNU General Public License
      16             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      17             : 
      18             : /* Extracted from coreutils' src/id.c. */
      19             : 
      20             : #include <config.h>
      21             : 
      22             : #include "mgetgroups.h"
      23             : 
      24             : #include <stdlib.h>
      25             : #include <unistd.h>
      26             : #include <stdint.h>
      27             : #include <string.h>
      28             : #include <errno.h>
      29             : #if HAVE_GETGROUPLIST
      30             : # include <grp.h>
      31             : #endif
      32             : #include "getugroups.h"
      33             : #include "xalloc.h"
      34             : 
      35             : 
      36             : static void *
      37          12 : allocate_groupbuf (int size)
      38             : {
      39          12 :   if (xalloc_oversized (size, sizeof (GETGROUPS_T)))
      40             :     {
      41           0 :       errno = ENOMEM;
      42           0 :       return NULL;
      43             :     }
      44             : 
      45          12 :   return malloc (size * sizeof (GETGROUPS_T));
      46             : }
      47             : 
      48             : /* Like getugroups, but store the result in malloc'd storage.
      49             :    Set *GROUPS to the malloc'd list of all group IDs of which USERNAME
      50             :    is a member.  If GID is not -1, store it first.  GID should be the
      51             :    group ID (pw_gid) obtained from getpwuid, in case USERNAME is not
      52             :    listed in the groups database (e.g., /etc/groups).  Upon failure,
      53             :    don't modify *GROUPS, set errno, and return -1.  Otherwise, return
      54             :    the number of groups.  */
      55             : 
      56             : int
      57          12 : mgetgroups (char const *username, gid_t gid, GETGROUPS_T **groups)
      58             : {
      59             :   int max_n_groups;
      60             :   int ng;
      61             :   GETGROUPS_T *g;
      62             : 
      63             : #if HAVE_GETGROUPLIST
      64             :   /* We prefer to use getgrouplist if available, because it has better
      65             :      performance characteristics.
      66             : 
      67             :      In glibc 2.3.2, getgrouplist is buggy.  If you pass a zero as the
      68             :      size of the output buffer, getgrouplist will still write to the
      69             :      buffer.  Contrary to what some versions of the getgrouplist
      70             :      manpage say, this doesn't happen with nonzero buffer sizes.
      71             :      Therefore our usage here just avoids a zero sized buffer.  */
      72          12 :   if (username)
      73             :     {
      74             :       enum { N_GROUPS_INIT = 10 };
      75             :       GETGROUPS_T smallbuf[N_GROUPS_INIT];
      76             : 
      77          12 :       max_n_groups = N_GROUPS_INIT;
      78          12 :       ng = getgrouplist (username, gid, smallbuf, &max_n_groups);
      79             : 
      80          12 :       g = allocate_groupbuf (max_n_groups);
      81          12 :       if (g == NULL)
      82           0 :         return -1;
      83             : 
      84          12 :       if (max_n_groups <= N_GROUPS_INIT)
      85             :         {
      86             :           /* smallbuf was big enough, so we already have our data */
      87          12 :           memcpy (g, smallbuf, max_n_groups * sizeof *g);
      88          12 :           *groups = g;
      89          12 :           return max_n_groups;
      90             :         }
      91             : 
      92             :       while (1)
      93           0 :         {
      94             :           GETGROUPS_T *h;
      95           0 :           ng = getgrouplist (username, gid, g, &max_n_groups);
      96           0 :           if (0 <= ng)
      97             :             {
      98           0 :               *groups = g;
      99           0 :               return ng;
     100             :             }
     101             : 
     102             :           /* When getgrouplist fails, it guarantees that
     103             :              max_n_groups reflects the new number of groups.  */
     104             : 
     105           0 :           if (xalloc_oversized (max_n_groups, sizeof *h)
     106           0 :               || (h = realloc (g, max_n_groups * sizeof *h)) == NULL)
     107             :             {
     108           0 :               int saved_errno = errno;
     109           0 :               free (g);
     110           0 :               errno = saved_errno;
     111           0 :               return -1;
     112             :             }
     113           0 :           g = h;
     114             :         }
     115             :     }
     116             :   /* else no username, so fall through and use getgroups. */
     117             : #endif
     118             : 
     119           0 :   max_n_groups = (username
     120             :                   ? getugroups (0, NULL, username, gid)
     121           0 :                   : getgroups (0, NULL));
     122             : 
     123             :   /* If we failed to count groups with NULL for a buffer,
     124             :      try again with a non-NULL one, just in case.  */
     125           0 :   if (max_n_groups < 0)
     126           0 :       max_n_groups = 5;
     127             : 
     128           0 :   g = allocate_groupbuf (max_n_groups);
     129           0 :   if (g == NULL)
     130           0 :     return -1;
     131             : 
     132           0 :   ng = (username
     133           0 :         ? getugroups (max_n_groups, g, username, gid)
     134           0 :         : getgroups (max_n_groups, g));
     135             : 
     136           0 :   if (ng < 0)
     137             :     {
     138           0 :       int saved_errno = errno;
     139           0 :       free (g);
     140           0 :       errno = saved_errno;
     141           0 :       return -1;
     142             :     }
     143             : 
     144           0 :   *groups = g;
     145           0 :   return ng;
     146             : }

Generated by: LCOV version 1.10