From patchwork Fri Mar 20 20:43:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 132120 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 94A574AB0FBC for ; Fri, 20 Mar 2026 20:52:39 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 94A574AB0FBC Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=fL61vG/B X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 908F54C515F4 for ; Fri, 20 Mar 2026 20:43:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 908F54C515F4 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 908F54C515F4 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774039388; cv=none; b=H9FkabDzImzQsOxjkfjgBzaMb4WkmJDlVUKLCngJPpy0qN9A+3Y0/FLQ6QLYiBejdYoaFB7xnXx+RHoB2mUPy9kytVgkxYdHvwf4JIVTP3aj2RPOuO8Hsbj5ELF459U0ymKX1Djt+1JCC/ytO9kOA2OwI+GokT5CuiZIJAj48kU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774039388; c=relaxed/simple; bh=ypsTDXa/rgfrarGbWGel82OHkQuE/fpbhkELxhOS4wg=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=xx6w+f9I3AHDLY1y/4RLKbOBdu7uVEnupEAV9mZ94y3RalxpgVhrUtVpHvcyonUww0TU7oxZcreYXi62sMelHs2hYi/5PeWX+3meMxLMMcM6pitC+NnNB39NF56D8WVBaVpIJdKF4J1wOeIuPS+zJK2RqtDeRfD0HiuzeE2JiXQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 908F54C515F4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774039388; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Z6mYcQWI9v8e9EMWxx5odeHummT1yc2Wixp7h7O5LPQ=; b=fL61vG/BAmUdKg03D0JSElo3IyV+4auXen+iUlydl5g0pkBFOkF4b3z6bfvD0QB2ivnksT dEsTGJd38cY4OdAc5jFf7hYFnj2cg6zWwYw7W2w3dVSmvMZYACALxBtAd4VZvz1z3sC2tF N4w+qbfJWEaJPYSIL/V7QhzZwmZl99s= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-668-s02eJmWsNQqgo2P1_Fo8Zw-1; Fri, 20 Mar 2026 16:43:06 -0400 X-MC-Unique: s02eJmWsNQqgo2P1_Fo8Zw-1 X-Mimecast-MFC-AGG-ID: s02eJmWsNQqgo2P1_Fo8Zw_1774039386 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 20DF11944F05 for ; Fri, 20 Mar 2026 20:43:06 +0000 (UTC) Received: from fweimer-oldenburg.csb.redhat.com (unknown [10.45.224.63]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 52A331953944 for ; Fri, 20 Mar 2026 20:43:05 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 19/23] nss: Low-level functionality for merging group lists In-Reply-To: Message-ID: <0fe187adee88605e0796966e9ef9b898a34d94f3.1774037705.git.fweimer@redhat.com> References: X-From-Line: 0fe187adee88605e0796966e9ef9b898a34d94f3 Mon Sep 17 00:00:00 2001 Date: Fri, 20 Mar 2026 21:43:02 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: ecMlNWrjA5swuFmMaKd6e93eKyjGdU9hIWYFOZpDycQ_1774039386 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_HOSTKARMA_W, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Use to deduplicate members, with a single-linked list to preserve ordering. Reviewed-by: Carlos O'Donell --- 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 \ 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 + . */ + +#include + +#include +#include +#include + +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 + . */ + +#ifndef NSS_GROUP_MEMBERS_H +#define NSS_GROUP_MEMBERS_H + +#include +#include + +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 + +/* 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 + . */ + +#include +#include +#include +#include +#include +#include + +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 + +/* Re-compile the support code because it is not exported from libc. */ +#include +#include +#include