1 /* mgetgroups.c -- return a list of the groups a user or current process is in
3 Copyright (C) 2007-2009 Free Software Foundation, Inc.
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.
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.
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/>. */
18 /* Extracted from coreutils' src/id.c. */
22 #include "mgetgroups.h"
33 #include "getugroups.h"
37 realloc_groupbuf (gid_t *g, size_t num)
39 if (xalloc_oversized (num, sizeof *g))
45 return realloc (g, num * sizeof *g);
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). If USERNAME is
53 NULL, store the supplementary groups of the current process, and GID
54 should be -1 or the effective group ID (getegid). Upon failure,
55 don't modify *GROUPS, set errno, and return -1. Otherwise, return
56 the number of groups. The resulting list may contain duplicates,
57 but adjacent members will be distinct. */
60 mgetgroups (char const *username, gid_t gid, gid_t **groups)
67 /* We prefer to use getgrouplist if available, because it has better
68 performance characteristics.
70 In glibc 2.3.2, getgrouplist is buggy. If you pass a zero as the
71 length of the output buffer, getgrouplist will still write to the
72 buffer. Contrary to what some versions of the getgrouplist
73 manpage say, this doesn't happen with nonzero buffer sizes.
74 Therefore our usage here just avoids a zero sized buffer. */
77 enum { N_GROUPS_INIT = 10 };
78 max_n_groups = N_GROUPS_INIT;
80 g = realloc_groupbuf (NULL, max_n_groups);
87 int last_n_groups = max_n_groups;
89 /* getgrouplist updates max_n_groups to num required. */
90 ng = getgrouplist (username, gid, g, &max_n_groups);
92 /* Some systems (like Darwin) have a bug where they
93 never increase max_n_groups. */
94 if (ng < 0 && last_n_groups == max_n_groups)
97 if ((h = realloc_groupbuf (g, max_n_groups)) == NULL)
99 int saved_errno = errno;
109 /* On success some systems just return 0 from getgrouplist,
110 so return max_n_groups rather than ng. */
115 /* else no username, so fall through and use getgroups. */
118 max_n_groups = (username
119 ? getugroups (0, NULL, username, gid)
120 : getgroups (0, NULL));
122 /* If we failed to count groups because there is no supplemental
123 group support, then return an array containing just GID.
124 Otherwise, we fail for the same reason. */
125 if (max_n_groups < 0)
127 if (errno == ENOSYS && (g = realloc_groupbuf (NULL, 1)))
131 return gid != (gid_t) -1;
136 if (!username && gid != (gid_t) -1)
138 g = realloc_groupbuf (NULL, max_n_groups);
143 ? getugroups (max_n_groups, g, username, gid)
144 : getgroups (max_n_groups, g + (gid != (gid_t) -1)));
148 /* Failure is unexpected, but handle it anyway. */
149 int saved_errno = errno;
155 if (!username && gid != (gid_t) -1)
162 /* Reduce the number of duplicates. On some systems, getgroups
163 returns the effective gid twice: once as the first element, and
164 once in its position within the supplementary groups. On other
165 systems, getgroups does not return the effective gid at all,
166 which is why we provide a GID argument. Meanwhile, the GID
167 argument, if provided, is typically any member of the
168 supplementary groups, and not necessarily the effective gid. So,
169 the most likely duplicates are the first element with an
170 arbitrary other element, or pair-wise duplication between the
171 first and second elements returned by getgroups. It is possible
172 that this O(n) pass will not remove all duplicates, but it is not
173 worth the effort to slow down to an O(n log n) algorithm that
174 sorts the array in place, nor the extra memory needed for
175 duplicate removal via an O(n) hash-table. Hence, this function
176 is only documented as guaranteeing no pair-wise duplicates,
177 rather than returning the minimal set. */
181 gid_t *sentinel = g + ng;
183 for (next = g + 1; next < sentinel; next++)
185 if (*next == first || *next == *g)
195 /* Like mgetgroups, but call xalloc_die on allocation failure. */
198 xgetgroups (char const *username, gid_t gid, gid_t **groups)
200 int result = mgetgroups (username, gid, groups);
201 if (result == -1 && errno == ENOMEM)