diff options
Diffstat (limited to 'libbb')
-rw-r--r-- | libbb/bb_getgroups.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/libbb/bb_getgroups.c b/libbb/bb_getgroups.c new file mode 100644 index 000000000..59ae53738 --- /dev/null +++ b/libbb/bb_getgroups.c | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Utility routines. | ||
3 | * | ||
4 | * Copyright (C) 2017 Denys Vlasenko | ||
5 | * | ||
6 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
7 | */ | ||
8 | |||
9 | //kbuild:lib-y += bb_getgroups.o | ||
10 | |||
11 | #include "libbb.h" | ||
12 | |||
13 | gid_t* FAST_FUNC bb_getgroups(int *ngroups, gid_t *group_array) | ||
14 | { | ||
15 | int n = ngroups ? *ngroups : 0; | ||
16 | |||
17 | /* getgroups may be a bit expensive, try to use it only once */ | ||
18 | if (n < 32) | ||
19 | n = 32; | ||
20 | |||
21 | for (;;) { | ||
22 | // FIXME: ash tries so hard to not die on OOM (when we are called from test), | ||
23 | // and we spoil it with just one xrealloc here | ||
24 | group_array = xrealloc(group_array, (n+1) * sizeof(group_array[0])); | ||
25 | n = getgroups(n, group_array); | ||
26 | /* | ||
27 | * If buffer is too small, kernel does not return new_n > n. | ||
28 | * It returns -1 and EINVAL: | ||
29 | */ | ||
30 | if (n >= 0) { | ||
31 | /* Terminator for bb_getgroups(NULL, NULL) usage */ | ||
32 | group_array[n] = (gid_t) -1; | ||
33 | break; | ||
34 | } | ||
35 | if (errno == EINVAL) { /* too small? */ | ||
36 | /* This is the way to ask kernel how big the array is */ | ||
37 | n = getgroups(0, group_array); | ||
38 | continue; | ||
39 | } | ||
40 | /* Some other error (should never happen on Linux) */ | ||
41 | bb_perror_msg_and_die("getgroups"); | ||
42 | } | ||
43 | |||
44 | if (ngroups) | ||
45 | *ngroups = n; | ||
46 | return group_array; | ||
47 | } | ||