[PATCH v2 21/23] nss: Convert group database to new NSS framework
Florian Weimer
fweimer@redhat.com
Fri Mar 20 20:43:12 GMT 2026
Group merging is reimplemented and includes deduplication, which is
why nss/tst-nss-test4 needs to be adjusted.
---
include/set-freeres.h | 2 -
nss/Makefile | 1 +
nss/getgrgid.c | 14 ++-
nss/getgrgid_r.c | 36 +++++---
nss/getgrnam.c | 14 ++-
nss/getgrnam_r.c | 40 ++++++---
nss/nss_generic_storage.h | 1 +
nss/nss_getXinfo.c | 10 +++
nss/nss_getgrXinfo.c | 176 ++++++++++++++++++++++++++++++++++++++
nss/tst-nss-test4.c | 12 +--
10 files changed, 260 insertions(+), 46 deletions(-)
create mode 100644 nss/nss_getgrXinfo.c
diff --git a/include/set-freeres.h b/include/set-freeres.h
index c9d2c8ae55..7806266077 100644
--- a/include/set-freeres.h
+++ b/include/set-freeres.h
@@ -102,8 +102,6 @@ extern printf_arginfo_size_function ** __libc_reg_printf_freemem_ptr
extern printf_va_arg_function ** __libc_reg_type_freemem_ptr
attribute_hidden;
/* From nss/getXXbyYY.c */
-extern char * __libc_getgrgid_freemem_ptr attribute_hidden;
-extern char * __libc_getgrnam_freemem_ptr attribute_hidden;
extern char * __libc_getspnam_freemem_ptr attribute_hidden;
extern char * __libc_getaliasbyname_freemem_ptr attribute_hidden;
extern char * __libc_gethostbyaddr_freemem_ptr attribute_hidden;
diff --git a/nss/Makefile b/nss/Makefile
index a13f6fa10c..504f573478 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -55,6 +55,7 @@ routines = \
nss_getX \
nss_getX_r \
nss_getXinfo \
+ nss_getgrXinfo \
nss_hash \
nss_module \
nss_parse_line_result \
diff --git a/nss/getgrgid.c b/nss/getgrgid.c
index 3053e22517..9a15e3c505 100644
--- a/nss/getgrgid.c
+++ b/nss/getgrgid.c
@@ -17,12 +17,10 @@
#include <grp.h>
+#include <nss_generic.h>
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrgid
-#define DATABASE_NAME group
-#define ADD_PARAMS gid_t gid
-#define ADD_VARIABLES gid
-#define BUFLEN NSS_BUFLEN_GROUP
-
-#include "../nss/getXXbyYY.c"
+struct group *
+getgrgid (gid_t gid)
+{
+ return __nss_getX (nss_lookup_getgrgid, &gid);
+}
diff --git a/nss/getgrgid_r.c b/nss/getgrgid_r.c
index af15accd10..7d4e693ccc 100644
--- a/nss/getgrgid_r.c
+++ b/nss/getgrgid_r.c
@@ -17,15 +17,31 @@
#include <grp.h>
-#include <grp-merge.h>
+#include <nss_generic.h>
+#include <shlib-compat.h>
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrgid
-#define DATABASE_NAME group
-#define ADD_PARAMS gid_t gid
-#define ADD_VARIABLES gid
-#define BUFLEN NSS_BUFLEN_GROUP
-#define DEEPCOPY_FN __copy_grp
-#define MERGE_FN __merge_grp
+int
+___getgrgid_r (gid_t gid, struct group *grp,
+ char *buffer, size_t length, struct group **result)
+{
+ void *ptr = grp;
+ int ret = __nss_getX_r (nss_lookup_getgrgid, &gid, &ptr, buffer, length);
+ *result = ptr;
+ return ret;
+}
+strong_alias (___getgrgid_r, __getgrgid_r)
+versioned_symbol (libc, ___getgrgid_r, getgrgid_r, GLIBC_2_1_2);
-#include <nss/getXXbyYY_r.c>
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
+int
+attribute_compat_text_section
+__old_getgrgid_r (gid_t gid, struct group *grp,
+ char *buffer, size_t length, struct group **result)
+{
+ int ret = __getgrgid_r (gid, grp, buffer, length, result);
+ if (ret != 0 || result == NULL)
+ ret = -1;
+ return ret;
+}
+compat_symbol (libc, __old_getgrgid_r, getgrgid_r, GLIBC_2_0);
+#endif
diff --git a/nss/getgrnam.c b/nss/getgrnam.c
index f10e79d50d..19e990a7cd 100644
--- a/nss/getgrnam.c
+++ b/nss/getgrnam.c
@@ -17,12 +17,10 @@
#include <grp.h>
+#include <nss_generic.h>
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrnam
-#define DATABASE_NAME group
-#define ADD_PARAMS const char *name
-#define ADD_VARIABLES name
-#define BUFLEN NSS_BUFLEN_GROUP
-
-#include "../nss/getXXbyYY.c"
+struct group *
+getgrnam (const char *name)
+{
+ return __nss_getX (nss_lookup_getgrnam, name);
+}
diff --git a/nss/getgrnam_r.c b/nss/getgrnam_r.c
index 44c7b87a34..358046c5db 100644
--- a/nss/getgrnam_r.c
+++ b/nss/getgrnam_r.c
@@ -17,15 +17,31 @@
#include <grp.h>
-#include <grp-merge.h>
-
-#define LOOKUP_TYPE struct group
-#define FUNCTION_NAME getgrnam
-#define DATABASE_NAME group
-#define ADD_PARAMS const char *name
-#define ADD_VARIABLES name
-
-#define DEEPCOPY_FN __copy_grp
-#define MERGE_FN __merge_grp
-
-#include <nss/getXXbyYY_r.c>
+#include <nss_generic.h>
+#include <shlib-compat.h>
+
+int
+___getgrnam_r (const char *name, struct group *grp,
+ char *buffer, size_t length, struct group **result)
+{
+ void *ptr = grp;
+ int ret = __nss_getX_r (nss_lookup_getgrnam, name, &ptr, buffer, length);
+ *result = ptr;
+ return ret;
+}
+strong_alias (___getgrnam_r, __getgrnam_r)
+versioned_symbol (libc, ___getgrnam_r, getgrnam_r, GLIBC_2_1_2);
+
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
+int
+attribute_compat_text_section
+__old_getgrnam_r (const char *name, struct group *grp,
+ char *buffer, size_t length, struct group **result)
+{
+ int ret = __getgrnam_r (name, grp, buffer, length, result);
+ if (ret != 0 || result == NULL)
+ ret = -1;
+ return ret;
+}
+compat_symbol (libc, __old_getgrnam_r, getgrnam_r, GLIBC_2_0);
+#endif
diff --git a/nss/nss_generic_storage.h b/nss/nss_generic_storage.h
index 334f51be70..2d46136c9a 100644
--- a/nss/nss_generic_storage.h
+++ b/nss/nss_generic_storage.h
@@ -24,6 +24,7 @@
union nss_generic_storage
{
+ struct group grp;
struct passwd pwd;
};
diff --git a/nss/nss_getXinfo.c b/nss/nss_getXinfo.c
index 0b2588c9fd..96836dc4c2 100644
--- a/nss/nss_getXinfo.c
+++ b/nss/nss_getXinfo.c
@@ -26,6 +26,16 @@ __nss_getXinfo (enum nss_lookup_type lt, nss_lookup_key key, void **result)
return 0;
#endif
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ case nss_lookup_getgrnam:
+ /* Group lookups are handled separately to implement merging. */
+ return __nss_getgrXinfo (lt, key, result);
+ default:
+ break;
+ }
+
nss_action_list nip = NULL;
void *fct;
int no_more = __nss_generic_next (lt, &nip, &fct, 0, 0);
diff --git a/nss/nss_getgrXinfo.c b/nss/nss_getgrXinfo.c
new file mode 100644
index 0000000000..795d12d63e
--- /dev/null
+++ b/nss/nss_getgrXinfo.c
@@ -0,0 +1,176 @@
+/* Implementation of the getgrXinfo family of functions.
+ Copyright (C) 1996-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 <assert.h>
+#include <errno.h>
+#include <grp.h>
+#include <nss_generic.h>
+#include <nss.h>
+#include <nss_group_members.h>
+#include <nsswitch.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stringtable.h>
+
+static inline bool
+__nss_status_has_result (enum nss_status status)
+{
+ return status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND;
+}
+
+static int
+__nss_status_to_posix_result (enum nss_status status)
+{
+ /* Treat NSS_STATUS_UNAVAIL like NSS_STATUS_NOTFOUND. Some NSS
+ modules return NSS_STATUS_UNAVAIL if they have disabled
+ themselves. */
+ if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND
+ || status == NSS_STATUS_UNAVAIL)
+ return 0;
+ else
+ {
+ /* Paranoia, to avoid endless loops. */
+ if (errno == ERANGE)
+ __set_errno (EINVAL);
+ return -1;
+ }
+}
+
+int
+__nss_getgrXinfo (enum nss_lookup_type lt,
+ nss_lookup_key key,
+ void **result)
+{
+ /* Default if no service modules are available. */
+ enum nss_status status = NSS_STATUS_UNAVAIL;
+ *result = NULL;
+
+ /* For merging. If table is not empty, then it supersedes the group
+ members in first_result. */
+ struct group *first_result = NULL;
+ struct nss_group_members table = { };
+ __nss_group_members_init (&table);
+
+ /* With better error reporting (especially from dlopen), we could
+ check for NSS initialization errors here and report them. */
+ nss_action_list nip;
+ void *fct = __nss_generic_lookup (lt, &nip);
+
+ int no_more = fct == NULL;
+ bool do_merge = false;
+ while (no_more == 0)
+ {
+ void *ptr;
+ status = __nss_generic_get (lt, key, fct, &ptr);
+ struct group *grp = ptr;
+
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ assert (grp != NULL);
+ /* We have result data. It may need to be merged. We only
+ support merging members of groups with identical names
+ and GID values. If we hit this, the grp result overrides
+ the first result. */
+ if (do_merge && first_result != NULL
+ && grp->gr_gid == first_result->gr_gid
+ && strcmp (grp->gr_name, first_result->gr_name) == 0)
+ {
+ /* Perform the merge. If no members have been merged yet,
+ process first_result as well. */
+ if ((table.T.count == 0
+ && !__nss_group_members_add (&table, first_result))
+ || !__nss_group_members_add (&table, grp))
+ {
+ __nss_group_members_free (&table);
+ free (grp);
+ free (first_result);
+ return -1;
+ }
+ free (grp);
+ }
+ else
+ {
+ /* No merge or different data. New result replaces
+ previous result. */
+ free (first_result);
+ first_result = grp;
+ __nss_group_members_free (&table);
+ }
+ }
+ else if (status == NSS_STATUS_TRYAGAIN)
+ {
+ free (first_result);
+ __nss_group_members_free (&table);
+ return -1;
+ }
+ else if (first_result != NULL)
+ {
+ /* If the result wasn't SUCCESS, use the stored data in
+ first_result/table and set the status back to
+ NSS_STATUS_SUCCESS to match the previous pass through
+ the loop.
+
+ * If the next action is CONTINUE, it will overwrite the value
+ currently in the buffer and return the new value.
+ * If the next action is RETURN, we'll return the previously-
+ acquired values.
+ * If the next action is MERGE, then it will be added to the
+ buffer saved from the previous source. */
+ status = NSS_STATUS_SUCCESS;
+ }
+
+ do_merge = (nss_next_action (nip, status) == NSS_ACTION_MERGE
+ && status == NSS_STATUS_SUCCESS);
+ no_more = __nss_generic_next (lt, &nip, &fct, status, 0);
+ }
+
+
+ if (table.T.count > 0)
+ {
+ /* We have something to merge. */
+ struct group *merged_allocated;
+ {
+ struct group merged = *first_result;
+ merged.gr_mem = __nss_group_members (&table);
+ if (merged.gr_mem == NULL)
+ {
+ __nss_group_members_free (&table);
+ free (first_result);
+ return -1;
+ }
+
+ /* Make a consolidated copy of the entire group information. */
+ merged_allocated = __nss_generic_dup (lt, &merged);
+
+ free (merged.gr_mem);
+ }
+
+ __nss_group_members_free (&table);
+ free (first_result);
+ first_result = merged_allocated;
+
+ if (first_result == NULL)
+ return -1;
+ }
+ else
+ /* No merging necessary. We can use first_result. */
+ __nss_group_members_free (&table);
+
+ *result = first_result;
+ return __nss_status_to_posix_result (status);
+}
diff --git a/nss/tst-nss-test4.c b/nss/tst-nss-test4.c
index 72e33f3d6d..332ec2e217 100644
--- a/nss/tst-nss-test4.c
+++ b/nss/tst-nss-test4.c
@@ -100,9 +100,7 @@ do_test (void)
/* At least 3 service modules are needed to reproduce BZ#33361. */
__nss_configure_lookup ("group", "test1 [SUCCESS=merge] test2 files");
- /* Test increasing sizes of group_2 to see if we fail, starting with
- member_cnt == 1 to ensure we always check for no de-duplication
- e.g. { "foo", NULL } */
+ /* Test increasing sizes of group_2 to see if we fail. */
for (member_cnt = 1; member_cnt < array_length (group_2); member_cnt++)
{
verbose_printf ("Outer loop - member_cnt is %d\n", member_cnt);
@@ -127,15 +125,17 @@ do_test (void)
verbose_printf ("MERGED LIST of [%d] is %s\n", i, merge_1[i]);
}
- /* Add group_2 to the merge list */
- int group2_index = 0;
+ /* Add group_2 to the merge list. Skip the duplicate "foo"
+ group at the start. */
+ int group2_index = 1;
for (i = array_length (group_1) - 1;
i < array_length (group_1) - 1 + member_cnt; i++)
{
merge_1[i] = xasprintf ("%s", group_2[group2_index++]);
verbose_printf ("MERGED LIST of [%d] is %s\n", i, merge_1[i]);
}
- merge_1[array_length(group_1) - 1 + member_cnt]= NULL;
+ /* Skipping the "foo" group reduced the member count by 1. */
+ merge_1[array_length(group_1) - 1 + member_cnt - 1]= NULL;
align_mask = __alignof__ (struct group) - 1;
align_mem_mask = __alignof__ (char *) - 1;
--
2.53.0
More information about the Libc-alpha
mailing list