[v2,19/23] nss: Low-level functionality for merging group lists
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
fail
|
Test failed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
fail
|
Test failed
|
Commit Message
Use <stringtable.h> to deduplicate members, with a single-linked
list to preserve ordering.
---
nss/Makefile | 2 +
nss/nss_group_members.c | 80 +++++++++++++++++++++++++++++++++++++
nss/nss_group_members.h | 61 ++++++++++++++++++++++++++++
nss/tst-nss_group_members.c | 77 +++++++++++++++++++++++++++++++++++
4 files changed, 220 insertions(+)
create mode 100644 nss/nss_group_members.c
create mode 100644 nss/nss_group_members.h
create mode 100644 nss/tst-nss_group_members.c
Comments
On 3/20/26 4:43 PM, Florian Weimer wrote:
> Use <stringtable.h> to deduplicate members, with a single-linked
> list to preserve ordering.
LGTM.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> ---
> nss/Makefile | 2 +
> nss/nss_group_members.c | 80 +++++++++++++++++++++++++++++++++++++
> nss/nss_group_members.h | 61 ++++++++++++++++++++++++++++
> nss/tst-nss_group_members.c | 77 +++++++++++++++++++++++++++++++++++
> 4 files changed, 220 insertions(+)
> create mode 100644 nss/nss_group_members.c
> create mode 100644 nss/nss_group_members.h
> create mode 100644 nss/tst-nss_group_members.c
>
> diff --git a/nss/Makefile b/nss/Makefile
> index ec68954f3b..82f5a5d68f 100644
> --- a/nss/Makefile
> +++ b/nss/Makefile
> @@ -89,6 +89,7 @@ routines += \
> getgrnam_r \
> grp-merge \
> initgroups \
> + nss_group_members \
> putgrent \
> # routines
>
> @@ -315,6 +316,7 @@ tests-internal := \
> tst-field \
> tst-nss_generic_copy \
> tst-nss_generic_dup \
> + tst-nss_group_members \
OK. tests-internal.
> tst-rfc3484 \
> tst-rfc3484-2 \
> tst-rfc3484-3 \
> diff --git a/nss/nss_group_members.c b/nss/nss_group_members.c
> new file mode 100644
> index 0000000000..aafbc8e65b
> --- /dev/null
> +++ b/nss/nss_group_members.c
> @@ -0,0 +1,80 @@
> +/* Support for merging group member lists. Implementation.
> + Copyright (C) 2026 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
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <nss_group_members.h>
> +
> +#include <assert.h>
> +#include <grp.h>
> +#include <stdlib.h>
> +
> +void
> +__nss_group_members_init (struct nss_group_members *table)
> +{
> + *table = (struct nss_group_members)
> + {
> + .tail = &table->head,
> + };
> +}
> +
> +void
> +__nss_group_members_free (struct nss_group_members *table)
> +{
> + __nss_group_members__free (table);
> + __nss_group_members_init (table);
> +}
> +
> +bool
> +__nss_group_members_add (struct nss_group_members *table,
> + const struct group *grp)
> +{
> + if (grp->gr_mem == NULL)
> + return true;
> + for (char **p = grp->gr_mem; *p != NULL; ++p)
> + {
> + struct nss_group_members_entry *e = __nss_group_members__add (table, *p);
> + if (e == NULL)
> + return false;
> + /* If the element is freshly inserted, e->order is NULL.
> + However, e->order may be NULL if e is not freshly inserted
> + and e is the last element in the ordering chain, which has no
> + successor. */
> + if (e->order == NULL && &e->order != table->tail)
> + {
> + *table->tail = e;
> + table->tail = &e->order;
> + }
> + }
> + return true;
> +}
> +
> +char **
> +__nss_group_members (const struct nss_group_members *table)
> +{
> + char **list = malloc ((table->T.count + 1) * sizeof (*list));
> + if (list == NULL)
> + return NULL;
> +
> + size_t i = 0;
> + for (struct nss_group_members_entry *p = table->head; p != NULL;
> + p = p->order, ++i)
> + list[i] = p->E.string;
> + assert (i == table->T.count);
> + list[i] = NULL;
> +
> + return list;
> +}
> diff --git a/nss/nss_group_members.h b/nss/nss_group_members.h
> new file mode 100644
> index 0000000000..19bfccfc31
> --- /dev/null
> +++ b/nss/nss_group_members.h
> @@ -0,0 +1,61 @@
> +/* Support for merging group member lists.
> + Copyright (C) 2026 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
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef NSS_GROUP_MEMBERS_H
> +#define NSS_GROUP_MEMBERS_H
> +
> +#include <stdbool.h>
> +#include <stringtable.h>
> +
> +struct nss_group_members_entry
> +{
> + struct nss_group_members_entry *order;
> + struct stringtable_entry E;
> +};
> +
> +struct nss_group_members
> +{
> + struct stringtable T;
> + struct nss_group_members_entry *head;
> + struct nss_group_members_entry **tail;
> +};
> +
> +#define STRINGTABLE_ENTRY nss_group_members_entry
> +#define STRINGTABLE_STRUCT nss_group_members
> +#define STRINGTABLE_PREFIX __nss_group_members__
> +#include <stringtable-skeleton.h>
OK.
> +
> +/* Initialize a group members table. */
> +void __nss_group_members_init (struct nss_group_members *) attribute_hidden;
> +
> +/* Deallocate a group members table. The table is initialized afterwards. */
> +void __nss_group_members_free (struct nss_group_members *) attribute_hidden;
> +
> +/* Add the members from GRP to TABLE. Returns true on success, false
> + on memory allocation failure. */
> +struct group;
> +bool __nss_group_members_add (struct nss_group_members *table,
> + const struct group *grp) attribute_hidden;
> +
> +/* Return a NULL-terminated array of strings, in the order they have
> + been added to the table. The strings point into the table. The
> + returned pointer should be passed to free. Return NULL on memory
> + allocation failure. */
> +char **__nss_group_members (const struct nss_group_members *) attribute_hidden;
> +
> +#endif /* NSS_GROUP_MEMBERS_H */
> diff --git a/nss/tst-nss_group_members.c b/nss/tst-nss_group_members.c
> new file mode 100644
> index 0000000000..43f26ff27b
> --- /dev/null
> +++ b/nss/tst-nss_group_members.c
> @@ -0,0 +1,77 @@
> +/* Test infrastructure for merging group member lists.
> + Copyright (C) 2026 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
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <grp.h>
> +#include <nss_generic.h>
> +#include <nss_group_members.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +
> +static int
> +do_test (void)
> +{
> + struct nss_group_members t;
> + __nss_group_members_init (&t);
> +
> + for (unsigned int count = 0; count < 100; ++count)
> + {
> + struct group grp = { };
> + grp.gr_mem = xcalloc (count + 1, sizeof (*grp.gr_mem));
> + for (unsigned int i = 0; i < count; ++i)
> + grp.gr_mem[i] = xasprintf ("user%d", i);
> +
> + /* This contains the expected list for the result. */
> + struct group *grp_copy = __nss_generic_dup (nss_lookup_getgrgid, &grp);
> +
> + TEST_VERIFY (__nss_group_members_add (&t, &grp));
> +
> + for (unsigned int i = 0; i < count; ++i)
> + free (grp.gr_mem[i]);
> +
> + /* Add some duplicates in a random-looking order. */
> + for (unsigned int i = 0; i < count / 2; ++i)
> + grp.gr_mem[i] = xasprintf ("user%d", (i * 0x9e3779b9) % count);
> + grp.gr_mem[count / 2] = NULL;
> +
> + /* Merge. */
> + TEST_VERIFY (__nss_group_members_add (&t, &grp));
> +
> + for (unsigned int i = 0; i < count / 2; ++i)
> + free (grp.gr_mem[i]);
> + free (grp.gr_mem);
> +
> + /* Compare the results. */
> + char **list = __nss_group_members (&t);
> + for (unsigned int i = 0; i <= count; ++i)
> + TEST_COMPARE_STRING (list[i], grp_copy->gr_mem[i]);
> +
> + free (list);
> + __nss_group_members_free (&t);
> + free (grp_copy);
> + }
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> +
> +/* Re-compile the support code because it is not exported from libc. */
> +#include <nss_generic_copy.c>
> +#include <nss_generic_dup.c>
> +#include <nss_group_members.c>
OK. Interesting to see this work, but it should.
@@ -89,6 +89,7 @@ routines += \
getgrnam_r \
grp-merge \
initgroups \
+ nss_group_members \
putgrent \
# routines
@@ -315,6 +316,7 @@ tests-internal := \
tst-field \
tst-nss_generic_copy \
tst-nss_generic_dup \
+ tst-nss_group_members \
tst-rfc3484 \
tst-rfc3484-2 \
tst-rfc3484-3 \
new file mode 100644
@@ -0,0 +1,80 @@
+/* Support for merging group member lists. Implementation.
+ Copyright (C) 2026 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
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <nss_group_members.h>
+
+#include <assert.h>
+#include <grp.h>
+#include <stdlib.h>
+
+void
+__nss_group_members_init (struct nss_group_members *table)
+{
+ *table = (struct nss_group_members)
+ {
+ .tail = &table->head,
+ };
+}
+
+void
+__nss_group_members_free (struct nss_group_members *table)
+{
+ __nss_group_members__free (table);
+ __nss_group_members_init (table);
+}
+
+bool
+__nss_group_members_add (struct nss_group_members *table,
+ const struct group *grp)
+{
+ if (grp->gr_mem == NULL)
+ return true;
+ for (char **p = grp->gr_mem; *p != NULL; ++p)
+ {
+ struct nss_group_members_entry *e = __nss_group_members__add (table, *p);
+ if (e == NULL)
+ return false;
+ /* If the element is freshly inserted, e->order is NULL.
+ However, e->order may be NULL if e is not freshly inserted
+ and e is the last element in the ordering chain, which has no
+ successor. */
+ if (e->order == NULL && &e->order != table->tail)
+ {
+ *table->tail = e;
+ table->tail = &e->order;
+ }
+ }
+ return true;
+}
+
+char **
+__nss_group_members (const struct nss_group_members *table)
+{
+ char **list = malloc ((table->T.count + 1) * sizeof (*list));
+ if (list == NULL)
+ return NULL;
+
+ size_t i = 0;
+ for (struct nss_group_members_entry *p = table->head; p != NULL;
+ p = p->order, ++i)
+ list[i] = p->E.string;
+ assert (i == table->T.count);
+ list[i] = NULL;
+
+ return list;
+}
new file mode 100644
@@ -0,0 +1,61 @@
+/* Support for merging group member lists.
+ Copyright (C) 2026 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
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef NSS_GROUP_MEMBERS_H
+#define NSS_GROUP_MEMBERS_H
+
+#include <stdbool.h>
+#include <stringtable.h>
+
+struct nss_group_members_entry
+{
+ struct nss_group_members_entry *order;
+ struct stringtable_entry E;
+};
+
+struct nss_group_members
+{
+ struct stringtable T;
+ struct nss_group_members_entry *head;
+ struct nss_group_members_entry **tail;
+};
+
+#define STRINGTABLE_ENTRY nss_group_members_entry
+#define STRINGTABLE_STRUCT nss_group_members
+#define STRINGTABLE_PREFIX __nss_group_members__
+#include <stringtable-skeleton.h>
+
+/* Initialize a group members table. */
+void __nss_group_members_init (struct nss_group_members *) attribute_hidden;
+
+/* Deallocate a group members table. The table is initialized afterwards. */
+void __nss_group_members_free (struct nss_group_members *) attribute_hidden;
+
+/* Add the members from GRP to TABLE. Returns true on success, false
+ on memory allocation failure. */
+struct group;
+bool __nss_group_members_add (struct nss_group_members *table,
+ const struct group *grp) attribute_hidden;
+
+/* Return a NULL-terminated array of strings, in the order they have
+ been added to the table. The strings point into the table. The
+ returned pointer should be passed to free. Return NULL on memory
+ allocation failure. */
+char **__nss_group_members (const struct nss_group_members *) attribute_hidden;
+
+#endif /* NSS_GROUP_MEMBERS_H */
new file mode 100644
@@ -0,0 +1,77 @@
+/* Test infrastructure for merging group member lists.
+ Copyright (C) 2026 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
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <grp.h>
+#include <nss_generic.h>
+#include <nss_group_members.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+
+static int
+do_test (void)
+{
+ struct nss_group_members t;
+ __nss_group_members_init (&t);
+
+ for (unsigned int count = 0; count < 100; ++count)
+ {
+ struct group grp = { };
+ grp.gr_mem = xcalloc (count + 1, sizeof (*grp.gr_mem));
+ for (unsigned int i = 0; i < count; ++i)
+ grp.gr_mem[i] = xasprintf ("user%d", i);
+
+ /* This contains the expected list for the result. */
+ struct group *grp_copy = __nss_generic_dup (nss_lookup_getgrgid, &grp);
+
+ TEST_VERIFY (__nss_group_members_add (&t, &grp));
+
+ for (unsigned int i = 0; i < count; ++i)
+ free (grp.gr_mem[i]);
+
+ /* Add some duplicates in a random-looking order. */
+ for (unsigned int i = 0; i < count / 2; ++i)
+ grp.gr_mem[i] = xasprintf ("user%d", (i * 0x9e3779b9) % count);
+ grp.gr_mem[count / 2] = NULL;
+
+ /* Merge. */
+ TEST_VERIFY (__nss_group_members_add (&t, &grp));
+
+ for (unsigned int i = 0; i < count / 2; ++i)
+ free (grp.gr_mem[i]);
+ free (grp.gr_mem);
+
+ /* Compare the results. */
+ char **list = __nss_group_members (&t);
+ for (unsigned int i = 0; i <= count; ++i)
+ TEST_COMPARE_STRING (list[i], grp_copy->gr_mem[i]);
+
+ free (list);
+ __nss_group_members_free (&t);
+ free (grp_copy);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>
+
+/* Re-compile the support code because it is not exported from libc. */
+#include <nss_generic_copy.c>
+#include <nss_generic_dup.c>
+#include <nss_group_members.c>