user-util: add lookup_groups_in_files

This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-07-02 17:19:18 +02:00
parent 2c01b4895e
commit b3749de21c
3 changed files with 100 additions and 0 deletions
+61
View File
@@ -1366,6 +1366,67 @@ int sysconf_ngroups_max(void) {
return ngroups_max;
}
ssize_t lookup_groups_in_files(
char * const *files,
const char *name,
gid_t gid,
gid_t **ret) {
_cleanup_free_ gid_t *arr = NULL;
ssize_t n_arr = 0;
int r;
assert(files);
assert(name);
assert(gid_is_valid(gid));
int ngroups_max = sysconf_ngroups_max();
if (ngroups_max < 0)
return ngroups_max;
if (!GREEDY_REALLOC(arr, n_arr + 1))
return -ENOMEM;
arr[n_arr++] = gid;
STRV_FOREACH(fname, files) {
_cleanup_fclose_ FILE *f = NULL;
struct group *gr;
r = fopen_unlocked(*fname, "re", &f);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
while ((r = fgetgrent_sane(f, &gr)) > 0) {
if (gid == gr->gr_gid)
continue;
/* Deduplicate groups, in case the group is doubly defined in the same or
* different files. */
if (gid_list_has(arr, n_arr, gr->gr_gid))
continue;
if (!strv_contains(gr->gr_mem, name))
continue;
/* Do not allow more than _SC_NGROUPS_MAX. */
if (n_arr >= ngroups_max)
return -ENOBUFS;
if (!GREEDY_REALLOC(arr, n_arr + 1))
return -ENOMEM;
arr[n_arr++] = gr->gr_gid;
}
if (r < 0)
return r;
}
if (ret)
*ret = TAKE_PTR(arr);
return n_arr;
}
static size_t getgr_buffer_size(void) {
long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
return bufsize <= 0 ? 4096U : (size_t) bufsize;
+5
View File
@@ -185,6 +185,11 @@ int lookup_grent_in_files(
const char *name,
gid_t gid,
struct group **ret);
ssize_t lookup_groups_in_files(
char * const *files,
const char *name,
gid_t gid,
gid_t **ret);
int getpwuid_malloc(uid_t uid, struct passwd **ret);
int getpwnam_malloc(const char *name, struct passwd **ret);
+34
View File
@@ -596,4 +596,38 @@ TEST(lookup_grent_in_files) {
ASSERT_STREQ(gr->gr_name, "testgroup1");
}
TEST(lookup_groups_in_files) {
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-user-util-group-XXXXXX";
_cleanup_fclose_ FILE *f = NULL;
ASSERT_OK(fmkostemp_safe(fn, "w", &f));
ASSERT_OK(write_string_stream(
f,
"testgroup1:x:100:testuser1,testuser2\n"
"testgroup2:x:200:testuser1\n"
"testgroup3:x:300:\n"
"testgroup4:x:400:root,somebody,testuser1\n",
/* flags= */ 0));
ASSERT_OK(fflush_and_check(f));
char **files = STRV_MAKE(fn);
/* Lookup by name */
_cleanup_free_ gid_t *arr = NULL;
ASSERT_EQ(lookup_groups_in_files(files, "testuser1", 100, &arr), 3);
gid_t exp1[] = { 100, 200, 400 };
ASSERT_EQ(memcmp(arr, exp1, sizeof(exp1)), 0);
arr = mfree(arr);
ASSERT_EQ(lookup_groups_in_files(files, "testuser1", 0, &arr), 4);
gid_t exp2[] = { 0, 100, 200, 400 };
ASSERT_EQ(memcmp(arr, exp2, sizeof(exp2)), 0);
arr = mfree(arr);
ASSERT_EQ(lookup_groups_in_files(files, "testuser3", 300, &arr), 1);
gid_t exp3[] = { 300 };
ASSERT_EQ(memcmp(arr, exp3, sizeof(exp3)), 0);
}
DEFINE_TEST_MAIN(LOG_INFO);