libbb: new function bb_getgroups() - allocating wrapper around getgroups()
No binary (build failed)
—
(log yok)
| @@ -563,26 +563,11 @@ static int binop(void) | |
| /*return 1; - NOTREACHED */ | |
| } | |
| - | |
| static void initialize_group_array(void) | |
| { | |
| - | int n; |
| - | |
| - | /* getgroups may be expensive, try to use it only once */ |
| - | ngroups = 32; |
| - | do { |
| - | /* FIXME: ash tries so hard to not die on OOM, |
| - | * and we spoil it with just one xrealloc here */ |
| - | /* We realloc, because test_main can be entered repeatedly by shell. |
| - | * Testcase (ash): 'while true; do test -x some_file; done' |
| - | * and watch top. (some_file must have owner != you) */ |
| - | n = ngroups; |
| - | group_array = xrealloc(group_array, n * sizeof(gid_t)); |
| - | ngroups = getgroups(n, group_array); |
| - | } while (ngroups > n); |
| + | group_array = bb_getgroups(&ngroups, NULL); |
| } | |
| - | |
| /* Return non-zero if GID is one that we have in our groups list. */ | |
| //XXX: FIXME: duplicate of existing libbb function? | |
| // see toplevel TODO file: | |
| @@ -610,14 +595,10 @@ static int is_a_group_member(gid_t gid) | |
| /* Do the same thing access(2) does, but use the effective uid and gid, | |
| and don't make the mistake of telling root that any file is | |
| executable. */ | |
| - | static int test_eaccess(char *path, int mode) |
| + | static int test_eaccess(struct stat *st, int mode) |
| { | |
| - | struct stat st; |
| unsigned int euid = geteuid(); | |
| - | if (stat(path, &st) < 0) |
| - | return -1; |
| - | |
| if (euid == 0) { | |
| /* Root can read or write any file. */ | |
| if (mode != X_OK) | |
| @@ -625,16 +606,16 @@ static int test_eaccess(char *path, int mode) | |
| /* Root can execute any file that has any one of the execute | |
| * bits set. */ | |
| - | if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) |
| + | if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) |
| return 0; | |
| } | |
| - | if (st.st_uid == euid) /* owner */ |
| + | if (st->st_uid == euid) /* owner */ |
| mode <<= 6; | |
| - | else if (is_a_group_member(st.st_gid)) |
| + | else if (is_a_group_member(st->st_gid)) |
| mode <<= 3; | |
| - | if (st.st_mode & mode) |
| + | if (st->st_mode & mode) |
| return 0; | |
| return -1; | |
| @@ -667,7 +648,7 @@ static int filstat(char *nm, enum token mode) | |
| i = W_OK; | |
| if (mode == FILEX) | |
| i = X_OK; | |
| - | return test_eaccess(nm, i) == 0; |
| + | return test_eaccess(&s, i) == 0; |
| } | |
| if (is_file_type(mode)) { | |
| if (mode == FILREG) | |
| @@ -1033,6 +1033,15 @@ void die_if_bad_username(const char* name) FAST_FUNC; | |
| #else | |
| #define die_if_bad_username(name) ((void)(name)) | |
| #endif | |
| + | /* |
| + | * Returns (-1) terminated malloced result of getgroups(). |
| + | * Reallocs group_array (useful for repeated calls). |
| + | * ngroups is an initial size of array. It is rounded up to 32 for realloc. |
| + | * ngroups is updated on return. |
| + | * ngroups can be NULL: bb_getgroups(NULL, NULL) is valid usage. |
| + | * Dies on errors (on Linux, only xrealloc can cause this, not internal getgroups call). |
| + | */ |
| + | gid_t *bb_getgroups(int *ngroups, gid_t *group_array) FAST_FUNC; |
| #if ENABLE_FEATURE_UTMP | |
| void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); | |
| @@ -0,0 +1,47 @@ | |
| + | /* |
| + | * Utility routines. |
| + | * |
| + | * Copyright (C) 2017 Denys Vlasenko |
| + | * |
| + | * Licensed under GPLv2, see file LICENSE in this source tree. |
| + | */ |
| + | |
| + | //kbuild:lib-y += bb_getgroups.o |
| + | |
| + | #include "libbb.h" |
| + | |
| + | gid_t* FAST_FUNC bb_getgroups(int *ngroups, gid_t *group_array) |
| + | { |
| + | int n = ngroups ? *ngroups : 0; |
| + | |
| + | /* getgroups may be a bit expensive, try to use it only once */ |
| + | if (n < 32) |
| + | n = 32; |
| + | |
| + | for (;;) { |
| + | // FIXME: ash tries so hard to not die on OOM (when we are called from test), |
| + | // and we spoil it with just one xrealloc here |
| + | group_array = xrealloc(group_array, (n+1) * sizeof(group_array[0])); |
| + | n = getgroups(n, group_array); |
| + | /* |
| + | * If buffer is too small, kernel does not return new_n > n. |
| + | * It returns -1 and EINVAL: |
| + | */ |
| + | if (n >= 0) { |
| + | /* Terminator for bb_getgroups(NULL, NULL) usage */ |
| + | group_array[n] = (gid_t) -1; |
| + | break; |
| + | } |
| + | if (errno == EINVAL) { /* too small? */ |
| + | /* This is the way to ask kernel how big the array is */ |
| + | n = getgroups(0, group_array); |
| + | continue; |
| + | } |
| + | /* Some other error (should never happen on Linux) */ |
| + | bb_perror_msg_and_die("getgroups"); |
| + | } |
| + | |
| + | if (ngroups) |
| + | *ngroups = n; |
| + | return group_array; |
| + | } |