/* mgetgroups.c -- return a list of the groups a user or current process is in
- Copyright (C) 2007-2009 Free Software Foundation, Inc.
+ Copyright (C) 2007-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#endif
#include "getugroups.h"
-#include "xalloc.h"
+#include "xalloc-oversized.h"
static gid_t *
realloc_groupbuf (gid_t *g, size_t num)
NULL, store the supplementary groups of the current process, and GID
should be -1 or the effective group ID (getegid). Upon failure,
don't modify *GROUPS, set errno, and return -1. Otherwise, return
- the number of groups. */
+ the number of groups. The resulting list may contain duplicates,
+ but adjacent members will be distinct. */
int
mgetgroups (char const *username, gid_t gid, gid_t **groups)
ng = (username
? getugroups (max_n_groups, g, username, gid)
- : getgroups (max_n_groups, g + (gid != (gid_t) -1)));
+ : getgroups (max_n_groups - (gid != (gid_t) -1),
+ g + (gid != (gid_t) -1)));
if (ng < 0)
{
ng++;
}
*groups = g;
- return ng;
-}
-/* Like mgetgroups, but call xalloc_die on allocation failure. */
+ /* Reduce the number of duplicates. On some systems, getgroups
+ returns the effective gid twice: once as the first element, and
+ once in its position within the supplementary groups. On other
+ systems, getgroups does not return the effective gid at all,
+ which is why we provide a GID argument. Meanwhile, the GID
+ argument, if provided, is typically any member of the
+ supplementary groups, and not necessarily the effective gid. So,
+ the most likely duplicates are the first element with an
+ arbitrary other element, or pair-wise duplication between the
+ first and second elements returned by getgroups. It is possible
+ that this O(n) pass will not remove all duplicates, but it is not
+ worth the effort to slow down to an O(n log n) algorithm that
+ sorts the array in place, nor the extra memory needed for
+ duplicate removal via an O(n) hash-table. Hence, this function
+ is only documented as guaranteeing no pair-wise duplicates,
+ rather than returning the minimal set. */
+ if (1 < ng)
+ {
+ gid_t first = *g;
+ gid_t *next;
+ gid_t *groups_end = g + ng;
-int
-xgetgroups (char const *username, gid_t gid, gid_t **groups)
-{
- int result = mgetgroups (username, gid, groups);
- if (result == -1 && errno == ENOMEM)
- xalloc_die ();
- return result;
+ for (next = g + 1; next < groups_end; next++)
+ {
+ if (*next == first || *next == *g)
+ ng--;
+ else
+ *++g = *next;
+ }
+ }
+
+ return ng;
}