diff options
Diffstat (limited to 'grp')
-rw-r--r-- | grp/Versions | 4 | ||||
-rw-r--r-- | grp/grp.h | 8 | ||||
-rw-r--r-- | grp/initgroups.c | 119 |
3 files changed, 90 insertions, 41 deletions
diff --git a/grp/Versions b/grp/Versions index 36b5b70366..e01360da42 100644 --- a/grp/Versions +++ b/grp/Versions @@ -24,4 +24,8 @@ libc { # g* getgrent_r; getgrgid_r; getgrnam_r; } + GLIBC_2.2.4 { + # g* + getgrouplist; + } } diff --git a/grp/grp.h b/grp/grp.h index fc805c6f46..c70689a097 100644 --- a/grp/grp.h +++ b/grp/grp.h @@ -1,4 +1,4 @@ -/* Copyright (C) 1991,92,95,96,97,98,99,2000 Free Software Foundation, Inc. +/* Copyright (C) 1991,92,95,96,97,98,99,2000,01 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 @@ -136,6 +136,12 @@ extern int fgetgrent_r (FILE *__restrict __stream, /* Set the group set for the current user to GROUPS (N of them). */ extern int setgroups (size_t __n, __const __gid_t *__groups) __THROW; +/* Store at most *NGROUPS members of the group set for USER into + *GROUPS. Also include GROUP. The actual number of groups found is + returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */ +extern int getgrouplist (__const char *__user, __gid_t __group, + __gid_t *__groups, int *__ngroups) __THROW; + /* Initialize the group set for the current user by reading the group database and using all groups of which USER is a member. Also include GROUP. */ diff --git a/grp/initgroups.c b/grp/initgroups.c index 89dc4d64fc..ee809d726e 100644 --- a/grp/initgroups.c +++ b/grp/initgroups.c @@ -136,50 +136,18 @@ compat_call (service_user *nip, const char *user, gid_t group, long int *start, return NSS_STATUS_SUCCESS; } -/* Initialize the group set for the current user - by reading the group database and using all groups - of which USER is a member. Also include GROUP. */ -int -initgroups (user, group) - const char *user; - gid_t group; +static int +internal_getgrouplist (const char *user, gid_t group, long int *size, + gid_t **groupsp, long int limit) { -#if defined NGROUPS_MAX && NGROUPS_MAX == 0 - - /* No extra groups allowed. */ - return 0; - -#else - service_user *nip = NULL; initgroups_dyn_function fct; enum nss_status status = NSS_STATUS_UNAVAIL; int no_more; /* Start is one, because we have the first group as parameter. */ long int start = 1; - long int size; - gid_t *groups; - int result; - /* We always use sysconf even if NGROUPS_MAX is defined. That way, the - limit can be raised in the kernel configuration without having to - recompile libc. */ - long int limit = __sysconf (_SC_NGROUPS_MAX); - - if (limit > 0) - size = limit; - else - { - /* No fixed limit on groups. Pick a starting buffer size. */ - size = 16; - } - - groups = (gid_t *) malloc (size * sizeof (gid_t)); - if (__builtin_expect (groups == NULL, 0)) - /* No more memory. */ - return -1; - - groups[0] = group; + *groupsp[0] = group; if (__nss_group_database != NULL) { @@ -196,14 +164,14 @@ initgroups (user, group) if (fct == NULL) { - status = compat_call (nip, user, group, &start, &size, &groups, + status = compat_call (nip, user, group, &start, size, groupsp, limit, &errno); if (nss_next_action (nip, NSS_STATUS_UNAVAIL) != NSS_ACTION_CONTINUE) break; } else - status = DL_CALL_FCT (fct, (user, group, &start, &size, &groups, + status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp, limit, &errno)); /* This is really only for debugging. */ @@ -220,10 +188,81 @@ initgroups (user, group) nip = nip->next; } + return start; +} + +/* Store at most *NGROUPS members of the group set for USER into + *GROUPS. Also include GROUP. The actual number of groups found is + returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */ +int +getgrouplist (const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + gid_t *newgroups; + long int size = *ngroups; + int result; + + newgroups = (gid_t *) malloc (size * sizeof (gid_t)); + if (__builtin_expect (newgroups == NULL, 0)) + /* No more memory. */ + return -1; + + result = internal_getgrouplist (user, group, &size, &newgroups, -1); + if (result > *ngroups) + { + *ngroups = result; + result = -1; + } + else + *ngroups = result; + + memcpy (groups, newgroups, *ngroups * sizeof (gid_t)); + + free (newgroups); + return result; +} + +/* Initialize the group set for the current user + by reading the group database and using all groups + of which USER is a member. Also include GROUP. */ +int +initgroups (const char *user, gid_t group) +{ +#if defined NGROUPS_MAX && NGROUPS_MAX == 0 + + /* No extra groups allowed. */ + return 0; + +#else + + long int size; + gid_t *groups; + int ngroups; + int result; + + /* We always use sysconf even if NGROUPS_MAX is defined. That way, the + limit can be raised in the kernel configuration without having to + recompile libc. */ + long int limit = __sysconf (_SC_NGROUPS_MAX); + + if (limit > 0) + size = limit; + else + { + /* No fixed limit on groups. Pick a starting buffer size. */ + size = 16; + } + + groups = (gid_t *) malloc (size * sizeof (gid_t)); + if (__builtin_expect (groups == NULL, 0)) + /* No more memory. */ + return -1; + + ngroups = internal_getgrouplist (user, group, &size, &groups, limit); + /* Try to set the maximum number of groups the kernel can handle. */ do - result = setgroups (start, groups); - while (result == -1 && errno == EINVAL && --start > 0); + result = setgroups (ngroups, groups); + while (result == -1 && errno == EINVAL && --ngroups > 0); free (groups); |