libbb: new function bb_getgroups() - allocating wrapper around getgroups()

function                                             old     new   delta
bb_getgroups                                           -     111    +111
nexpr                                                843     757     -86
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 0/1 up/down: 111/-86)            Total: 25 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-07-04 18:49:24 +02:00
parent 6a3bcf340a
commit a8cf9c5a3f
3 changed files with 63 additions and 26 deletions

View File

@ -563,26 +563,11 @@ static int binop(void)
/*return 1; - NOTREACHED */ /*return 1; - NOTREACHED */
} }
static void initialize_group_array(void) static void initialize_group_array(void)
{ {
int n; group_array = bb_getgroups(&ngroups, NULL);
/* 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);
} }
/* Return non-zero if GID is one that we have in our groups list. */ /* Return non-zero if GID is one that we have in our groups list. */
//XXX: FIXME: duplicate of existing libbb function? //XXX: FIXME: duplicate of existing libbb function?
// see toplevel TODO file: // 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, /* 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 and don't make the mistake of telling root that any file is
executable. */ 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(); unsigned int euid = geteuid();
if (stat(path, &st) < 0)
return -1;
if (euid == 0) { if (euid == 0) {
/* Root can read or write any file. */ /* Root can read or write any file. */
if (mode != X_OK) 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 /* Root can execute any file that has any one of the execute
* bits set. */ * bits set. */
if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
return 0; return 0;
} }
if (st.st_uid == euid) /* owner */ if (st->st_uid == euid) /* owner */
mode <<= 6; mode <<= 6;
else if (is_a_group_member(st.st_gid)) else if (is_a_group_member(st->st_gid))
mode <<= 3; mode <<= 3;
if (st.st_mode & mode) if (st->st_mode & mode)
return 0; return 0;
return -1; return -1;
@ -667,7 +648,7 @@ static int filstat(char *nm, enum token mode)
i = W_OK; i = W_OK;
if (mode == FILEX) if (mode == FILEX)
i = X_OK; i = X_OK;
return test_eaccess(nm, i) == 0; return test_eaccess(&s, i) == 0;
} }
if (is_file_type(mode)) { if (is_file_type(mode)) {
if (mode == FILREG) if (mode == FILREG)

View File

@ -1033,6 +1033,15 @@ void die_if_bad_username(const char* name) FAST_FUNC;
#else #else
#define die_if_bad_username(name) ((void)(name)) #define die_if_bad_username(name) ((void)(name))
#endif #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 #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); void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);

47
libbb/bb_getgroups.c Normal file
View File

@ -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;
}