[v2,15/23] nscd: Add __nscd_generic_get for generic lookup
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-aarch64 |
fail
|
Test failed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
fail
|
Test failed
|
Commit Message
Implement passwd and group handling only at this point.
---
nscd/nscd_helper.c | 318 +++++++++++++++++++++++++++++++++++++++++++++
nss/nss_generic.h | 16 +++
2 files changed, 334 insertions(+)
Comments
On 3/20/26 4:42 PM, Florian Weimer wrote:
> Implement passwd and group handling only at this point.
LGTM.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> ---
> nscd/nscd_helper.c | 318 +++++++++++++++++++++++++++++++++++++++++++++
> nss/nss_generic.h | 16 +++
> 2 files changed, 334 insertions(+)
>
> diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
> index 88bfb463c2..b113403f16 100644
> --- a/nscd/nscd_helper.c
> +++ b/nscd/nscd_helper.c
> @@ -38,6 +38,9 @@
> #include <nss.h>
> #include <struct___timespec64.h>
> #include <scratch_buffer.h>
> +#include <parse_buffer.h>
> +#include <nss/nss_generic.h>
> +#include <grp.h>
>
> #include "nscd-client.h"
> #include "nscd-dbtype.h"
> @@ -802,3 +805,318 @@ __nscd_read_from_socket (const char *key, size_t keylen, request_type type,
>
> return ret;
> }
> +
> +/* Used to implement __nscd_parse_and_copy. */
> +static int __nscd_parse_and_copy_group (enum nss_lookup_type,
> + void *, ssize_t, void **);
> +static int __nscd_parse_and_copy_passwd (enum nss_lookup_type,
> + void *, ssize_t, void **);
> +
> +/* Use __nss_generic_dup to copy NSS data according to LT at BUFFER
> + (of LENGTH bytes) to *RESULT, or NULL if there is no data. Return
> + 0 for success (including a negative result), or a negative error
> + code: -ENOENT for no data available,, -EMSGSIZE for a parse
> + failure, and -ENOMEM for a memory allocation error. */
> +static int
> +__nscd_parse_and_copy (enum nss_lookup_type lt,
> + void *buffer, ssize_t length, void **result)
> +{
> + switch (lt)
> + {
> + case nss_lookup_getgrnam:
> + case nss_lookup_getgrgid:
> + return __nscd_parse_and_copy_group (lt, buffer, length, result);
> + case nss_lookup_getpwuid:
> + case nss_lookup_getpwnam:
> + return __nscd_parse_and_copy_passwd (lt, buffer, length, result);
> + }
> + __builtin_unreachable ();
> +}
> +
> +/* Use the nscd shared memory cache to retrieve data. On success,
> + return true and write a __nss_generic_dup-allocated NSS struct
> + pointer to *RESULT (positive result), or write NULL to *RESULT
> + (negative result). On failurem return false. Failure can mean:
> + the data is not in the mapping, the mapping is being concurrently
> + modified, or there was a resource shortage. *BUFFER can be used
> + for temporary storage (but not for data in the returned result). */
> +static bool
> +__nscd_try_cache (unsigned int db, request_type type,
> + const char *key, size_t keylen, enum nss_lookup_type lt,
> + struct scratch_buffer *buffer, void **result)
> +{
> + assert (db < lastdb);
> + struct mapped_database *mapped = &__nscd_mapped_databases[db];
> + bool found = false;
> +
> + for (int retries = 0; ; ++retries)
> + {
> + __libc_rwlock_rdlock (mapped->lock);
> +
> + /* Check if the mapping is usable. If not, try to replace it. */
> + if (mapped->mapsize == 0
> + || (mapped->head->nscd_certainly_running == 0
> + && mapped->head->timestamp + MAPPING_TIMEOUT < time_now ())
> + || mapped->head->data_size > mapped->datasize)
> + {
> + /* Mapping is not usable as-is. Attempt to replace it. */
> + __libc_rwlock_unlock (mapped->lock);
> + __nscd_get_mapping (db);
> + if (mapped->mapsize == 0)
> + /* Could not get mapping. */
> + break;
> + }
> +
> + ssize_t ret = __nscd_read_from_cache (key, keylen, type, buffer,
> + mapped);
> + if (ret > 0)
> + {
> + ret = __nscd_parse_and_copy (lt, buffer->data, ret, result);
> + if (ret == 0)
> + {
> + /* We got the data (result can be NULL for negative). */
> + found = true;
> + break;
> + }
> + else if (ret != -EMSGSIZE)
> + /* Some other error. Give up. */
> + break;
> + /* Else fall through and retry. */
> + }
> + else if (ret != -EINPROGRESS && ret != -EMSGSIZE)
> + /* An error that does not go away with retrying. */
> + break;
> +
> + if (retries == 5)
> + /* Stop retrying. */
> + break;
> +
> + __libc_rwlock_unlock (mapped->lock);
> + }
> +
> + __libc_rwlock_unlock (mapped->lock);
> + return found;
> +}
> +
> +/* Use the nscd shared memory cache to retrieve data. On success,
> + write a pointer to an NSS struct to *RESULT, allocated using
> + __nss_generic_dup according to LT, or write NULL to *RESULT
> + (negative result) and return true. On failure, return false.
> + Failure can mean failure to connect to nscd, or a parse error in
> + the data returned from nscd. */
> +static bool
> +__nscd_try_socket (request_type type, const char *key, size_t keylen,
> + enum nss_lookup_type lt, struct scratch_buffer *buffer,
> + void **result)
> +{
> + ssize_t length = __nscd_read_from_socket (key, keylen, type, buffer);
> + /* The length can be negative. Rely on the check in the parse function. */
> + int ret = __nscd_parse_and_copy (lt, buffer->data, length, result);
> + return ret == 0;
> +}
> +
> +/* Common tail of __nscd_parse_and_copy_* implementations. Return 0
> + on success, -ENOMEM on failure, and -EMSGSIZE on parse error. */
> +static inline int
> +__nscd_parse_and_copy_tail (enum nss_lookup_type lt, struct parse_buffer pb,
> + const void *nss_struct, void **result)
> +{
> + if (!parse_buffer_has_failed (&pb))
> + {
> + *result = __nss_generic_dup (lt, nss_struct);
> + if (*result != NULL)
> + return 0;
> + else
> + return -ENOMEM;
> + }
> + else
> + /* Parse error. */
> + return -EMSGSIZE;
> +}
> +
> +static int
> +__nscd_parse_and_copy_group (enum nss_lookup_type lt,
> + void *buffer, ssize_t length, void **result)
> +{
> + if (length < 0)
> + {
> + __nscd_defer_database (NSS_DBSIDX_group);
> + return -ENOENT;
> + }
> +
> + struct parse_buffer pb = parse_buffer_create (buffer, length);
> +
> + int32_t gr_found = parse_buffer_field (&pb, 0, gr_response_header, found);
> + if (gr_found == 1)
> + {
> + uint32_t gr_mem_cnt
> + = parse_buffer_field (&pb, 0, gr_response_header, gr_mem_cnt);
> +
> + /* Paranoia check to avoid overflow. */
> + if (gr_mem_cnt >= INT_MAX / 4)
> + return -EMSGSIZE;
> +
> + /* Skip over the array with the group member name lengths. */
> + size_t pos = (sizeof (gr_response_header)
> + + gr_mem_cnt * sizeof (uint32_t));
> + struct group grp;
> + grp.gr_name = parse_buffer_sized_cstring_advance
> + (&pb, &pos,
> + parse_buffer_field (&pb, 0, gr_response_header, gr_name_len));
> + grp.gr_passwd = parse_buffer_sized_cstring_advance
> + (&pb, &pos,
> + parse_buffer_field (&pb, 0, gr_response_header, gr_passwd_len));
> + grp.gr_gid = parse_buffer_field (&pb, 0,
> + gr_response_header, gr_gid);
> + grp.gr_mem = __libc_reallocarray (NULL, gr_mem_cnt + 1,
> + sizeof (*grp.gr_mem));
> + if (grp.gr_mem == NULL)
> + return -ENOMEM;
> +
> + /* Need to free grp.gr_mem before returning after this point. */
> + int ret = 0;
> +
> + size_t lenpos = sizeof (gr_response_header);
> + for (uint32_t i = 0; i < gr_mem_cnt; ++i)
> + grp.gr_mem[i] = parse_buffer_sized_cstring_advance
> + (&pb, &pos, parse_buffer_u32_advance (&pb, &lenpos));
> + grp.gr_mem[gr_mem_cnt] = NULL;
> +
> + ret = __nscd_parse_and_copy_tail (lt, pb, &grp, result);
> + free (grp.gr_mem);
> + return ret;
> + }
> + else if (gr_found == -1)
> + {
> + /* The daemon does not cache this database. */
> + __nscd_defer_database (NSS_DBSIDX_group);
> + return -ENOENT;
> + }
> + else if (parse_buffer_has_failed (&pb))
> + return -EMSGSIZE;
> + else
> + {
> + *result = NULL;
> + return 0;
> + }
> +}
> +
> +
> +/* Common tail of __nscd_generic_get. */
> +static bool
> +__nscd_generic_get_tail (enum nss_lookup_type lt, unsigned int db,
> + request_type type,
> + const char *nscd_key, size_t nscd_keylen,
> + void **result)
> +{
> + bool ok = true;
> + struct scratch_buffer buffer;
> + scratch_buffer_init (&buffer);
> +
> + /* Try the mapping first. */
> + bool cache_hit = __nscd_try_cache (db, type, nscd_key, nscd_keylen,
> + lt, &buffer, result);
> +
> + /* If the cache did not produce data, try the socket. The socket
> + may produce a successful negative result (*result == NULL). */
> + if (!cache_hit)
> + ok = __nscd_try_socket (type, nscd_key, nscd_keylen, lt, &buffer,
> + result);
> +
> + scratch_buffer_free (&buffer);
> + return ok;
> +}
> +
> +#define NSCD_NUMBER_BUFFER_LENGTH 20
> +
> +static int
> +__nscd_fmt_num (char storage[static NSCD_NUMBER_BUFFER_LENGTH], int number)
> +{
> + return __snprintf (storage, NSCD_NUMBER_BUFFER_LENGTH, "%d", number) + 1;
> +}
> +
> +bool
> +__nscd_generic_get (enum nss_lookup_type lt, const void *key, void **result)
> +{
> + /* Storage for keys that need to be converted. */
> + char storage[20];
> +
> + int8_t db = __nscd_database_for_lookup[lt];
> + if (db < 0 || !__nscd_use_database (db))
> + return false;
> +
> + switch (lt)
> + {
> + case nss_lookup_getgrgid:
> + return __nscd_generic_get_tail (lt, db, GETGRBYGID, storage,
> + __nscd_fmt_num (storage,
> + *(const gid_t *) key),
> + result);
> + case nss_lookup_getgrnam:
> + return __nscd_generic_get_tail (lt, db, GETGRBYNAME,
> + key, strlen (key) + 1,
> + result);
> + case nss_lookup_getpwnam:
> + return __nscd_generic_get_tail (lt, db, GETPWBYNAME,
> + key, strlen (key) + 1,
> + result);
> + case nss_lookup_getpwuid:
> + return __nscd_generic_get_tail (lt, db, GETPWBYUID, storage,
> + __nscd_fmt_num (storage,
> + *(const uid_t *) key),
> + result);
> + }
> + __builtin_unreachable ();
> +}
> +
> +static int
> +__nscd_parse_and_copy_passwd (enum nss_lookup_type lt,
> + void *buffer, ssize_t length, void **result)
> +{
> + if (length < 0)
> + {
> + __nscd_defer_database (NSS_DBSIDX_passwd);
> + return -ENOENT;
> + }
> +
> + struct parse_buffer pb = parse_buffer_create (buffer, length);
> +
> + int32_t pw_found = parse_buffer_field (&pb, 0, pw_response_header, found);
> + if (pw_found == 1)
> + {
> + size_t pos = sizeof (pw_response_header);
> + struct passwd pwd;
> + pwd.pw_name = parse_buffer_sized_cstring_advance
> + (&pb, &pos,
> + parse_buffer_field (&pb, 0, pw_response_header, pw_name_len));
> + pwd.pw_passwd = parse_buffer_sized_cstring_advance
> + (&pb, &pos,
> + parse_buffer_field (&pb, 0, pw_response_header, pw_passwd_len));
> + pwd.pw_uid = parse_buffer_field (&pb, 0, pw_response_header, pw_uid);
> + pwd.pw_gid = parse_buffer_field (&pb, 0, pw_response_header, pw_gid);
> + pwd.pw_gecos = parse_buffer_sized_cstring_advance
> + (&pb, &pos,
> + parse_buffer_field (&pb, 0, pw_response_header, pw_gecos_len));
> + pwd.pw_dir = parse_buffer_sized_cstring_advance
> + (&pb, &pos,
> + parse_buffer_field (&pb, 0, pw_response_header, pw_dir_len));
> + pwd.pw_shell = parse_buffer_sized_cstring_advance
> + (&pb, &pos,
> + parse_buffer_field (&pb, 0, pw_response_header, pw_shell_len));
> +
> + return __nscd_parse_and_copy_tail (lt, pb, &pwd, result);
> + }
> + else if (pw_found == -1)
> + {
> + /* The daemon does not cache this database. */
> + __nscd_defer_database (NSS_DBSIDX_passwd);
> + return -ENOENT;
> + }
> + else if (parse_buffer_has_failed (&pb))
> + return -EMSGSIZE;
> + else
> + {
> + *result = NULL;
> + return 0;
> + }
> +}
> diff --git a/nss/nss_generic.h b/nss/nss_generic.h
> index fcddf8bf54..8a5b2cb896 100644
> --- a/nss/nss_generic.h
> +++ b/nss/nss_generic.h
> @@ -64,4 +64,20 @@ void *__nss_generic_dup (enum nss_lookup_type lt, const void *source)
> each lookup type, or -1 if the lookup has no corresponding database. */
> extern int8_t __nscd_database_for_lookup[nss_lookup_MAX] attribute_hidden;
>
> +/* Interpreted according to enum nss_lookup_type. Typically a string
> + or a pointer to an integer. */
> +typedef const void *nss_lookup_key;
> +
> +/* Query the nscd daemon for KEY according to LT. On success, return
> + true, and write a NSS struct pointer allocated using
> + __nss_generic_dup to *RESULT (positive result), or a null pointer
> + (negative result). On failure, return false. The caller is
> + expected to attempt the NSS operation directly after a failure.
> +
> + Failure is also possible if nscd lookup has been deferred; the
> + implementation calls __nscd_use_database as a first step to check
> + if nscd should actually be used. */
> +bool __nscd_generic_get (enum nss_lookup_type lt, nss_lookup_key key,
> + void **result);
> +
> #endif /* NSS_GENERIC_H */
@@ -38,6 +38,9 @@
#include <nss.h>
#include <struct___timespec64.h>
#include <scratch_buffer.h>
+#include <parse_buffer.h>
+#include <nss/nss_generic.h>
+#include <grp.h>
#include "nscd-client.h"
#include "nscd-dbtype.h"
@@ -802,3 +805,318 @@ __nscd_read_from_socket (const char *key, size_t keylen, request_type type,
return ret;
}
+
+/* Used to implement __nscd_parse_and_copy. */
+static int __nscd_parse_and_copy_group (enum nss_lookup_type,
+ void *, ssize_t, void **);
+static int __nscd_parse_and_copy_passwd (enum nss_lookup_type,
+ void *, ssize_t, void **);
+
+/* Use __nss_generic_dup to copy NSS data according to LT at BUFFER
+ (of LENGTH bytes) to *RESULT, or NULL if there is no data. Return
+ 0 for success (including a negative result), or a negative error
+ code: -ENOENT for no data available,, -EMSGSIZE for a parse
+ failure, and -ENOMEM for a memory allocation error. */
+static int
+__nscd_parse_and_copy (enum nss_lookup_type lt,
+ void *buffer, ssize_t length, void **result)
+{
+ switch (lt)
+ {
+ case nss_lookup_getgrnam:
+ case nss_lookup_getgrgid:
+ return __nscd_parse_and_copy_group (lt, buffer, length, result);
+ case nss_lookup_getpwuid:
+ case nss_lookup_getpwnam:
+ return __nscd_parse_and_copy_passwd (lt, buffer, length, result);
+ }
+ __builtin_unreachable ();
+}
+
+/* Use the nscd shared memory cache to retrieve data. On success,
+ return true and write a __nss_generic_dup-allocated NSS struct
+ pointer to *RESULT (positive result), or write NULL to *RESULT
+ (negative result). On failurem return false. Failure can mean:
+ the data is not in the mapping, the mapping is being concurrently
+ modified, or there was a resource shortage. *BUFFER can be used
+ for temporary storage (but not for data in the returned result). */
+static bool
+__nscd_try_cache (unsigned int db, request_type type,
+ const char *key, size_t keylen, enum nss_lookup_type lt,
+ struct scratch_buffer *buffer, void **result)
+{
+ assert (db < lastdb);
+ struct mapped_database *mapped = &__nscd_mapped_databases[db];
+ bool found = false;
+
+ for (int retries = 0; ; ++retries)
+ {
+ __libc_rwlock_rdlock (mapped->lock);
+
+ /* Check if the mapping is usable. If not, try to replace it. */
+ if (mapped->mapsize == 0
+ || (mapped->head->nscd_certainly_running == 0
+ && mapped->head->timestamp + MAPPING_TIMEOUT < time_now ())
+ || mapped->head->data_size > mapped->datasize)
+ {
+ /* Mapping is not usable as-is. Attempt to replace it. */
+ __libc_rwlock_unlock (mapped->lock);
+ __nscd_get_mapping (db);
+ if (mapped->mapsize == 0)
+ /* Could not get mapping. */
+ break;
+ }
+
+ ssize_t ret = __nscd_read_from_cache (key, keylen, type, buffer,
+ mapped);
+ if (ret > 0)
+ {
+ ret = __nscd_parse_and_copy (lt, buffer->data, ret, result);
+ if (ret == 0)
+ {
+ /* We got the data (result can be NULL for negative). */
+ found = true;
+ break;
+ }
+ else if (ret != -EMSGSIZE)
+ /* Some other error. Give up. */
+ break;
+ /* Else fall through and retry. */
+ }
+ else if (ret != -EINPROGRESS && ret != -EMSGSIZE)
+ /* An error that does not go away with retrying. */
+ break;
+
+ if (retries == 5)
+ /* Stop retrying. */
+ break;
+
+ __libc_rwlock_unlock (mapped->lock);
+ }
+
+ __libc_rwlock_unlock (mapped->lock);
+ return found;
+}
+
+/* Use the nscd shared memory cache to retrieve data. On success,
+ write a pointer to an NSS struct to *RESULT, allocated using
+ __nss_generic_dup according to LT, or write NULL to *RESULT
+ (negative result) and return true. On failure, return false.
+ Failure can mean failure to connect to nscd, or a parse error in
+ the data returned from nscd. */
+static bool
+__nscd_try_socket (request_type type, const char *key, size_t keylen,
+ enum nss_lookup_type lt, struct scratch_buffer *buffer,
+ void **result)
+{
+ ssize_t length = __nscd_read_from_socket (key, keylen, type, buffer);
+ /* The length can be negative. Rely on the check in the parse function. */
+ int ret = __nscd_parse_and_copy (lt, buffer->data, length, result);
+ return ret == 0;
+}
+
+/* Common tail of __nscd_parse_and_copy_* implementations. Return 0
+ on success, -ENOMEM on failure, and -EMSGSIZE on parse error. */
+static inline int
+__nscd_parse_and_copy_tail (enum nss_lookup_type lt, struct parse_buffer pb,
+ const void *nss_struct, void **result)
+{
+ if (!parse_buffer_has_failed (&pb))
+ {
+ *result = __nss_generic_dup (lt, nss_struct);
+ if (*result != NULL)
+ return 0;
+ else
+ return -ENOMEM;
+ }
+ else
+ /* Parse error. */
+ return -EMSGSIZE;
+}
+
+static int
+__nscd_parse_and_copy_group (enum nss_lookup_type lt,
+ void *buffer, ssize_t length, void **result)
+{
+ if (length < 0)
+ {
+ __nscd_defer_database (NSS_DBSIDX_group);
+ return -ENOENT;
+ }
+
+ struct parse_buffer pb = parse_buffer_create (buffer, length);
+
+ int32_t gr_found = parse_buffer_field (&pb, 0, gr_response_header, found);
+ if (gr_found == 1)
+ {
+ uint32_t gr_mem_cnt
+ = parse_buffer_field (&pb, 0, gr_response_header, gr_mem_cnt);
+
+ /* Paranoia check to avoid overflow. */
+ if (gr_mem_cnt >= INT_MAX / 4)
+ return -EMSGSIZE;
+
+ /* Skip over the array with the group member name lengths. */
+ size_t pos = (sizeof (gr_response_header)
+ + gr_mem_cnt * sizeof (uint32_t));
+ struct group grp;
+ grp.gr_name = parse_buffer_sized_cstring_advance
+ (&pb, &pos,
+ parse_buffer_field (&pb, 0, gr_response_header, gr_name_len));
+ grp.gr_passwd = parse_buffer_sized_cstring_advance
+ (&pb, &pos,
+ parse_buffer_field (&pb, 0, gr_response_header, gr_passwd_len));
+ grp.gr_gid = parse_buffer_field (&pb, 0,
+ gr_response_header, gr_gid);
+ grp.gr_mem = __libc_reallocarray (NULL, gr_mem_cnt + 1,
+ sizeof (*grp.gr_mem));
+ if (grp.gr_mem == NULL)
+ return -ENOMEM;
+
+ /* Need to free grp.gr_mem before returning after this point. */
+ int ret = 0;
+
+ size_t lenpos = sizeof (gr_response_header);
+ for (uint32_t i = 0; i < gr_mem_cnt; ++i)
+ grp.gr_mem[i] = parse_buffer_sized_cstring_advance
+ (&pb, &pos, parse_buffer_u32_advance (&pb, &lenpos));
+ grp.gr_mem[gr_mem_cnt] = NULL;
+
+ ret = __nscd_parse_and_copy_tail (lt, pb, &grp, result);
+ free (grp.gr_mem);
+ return ret;
+ }
+ else if (gr_found == -1)
+ {
+ /* The daemon does not cache this database. */
+ __nscd_defer_database (NSS_DBSIDX_group);
+ return -ENOENT;
+ }
+ else if (parse_buffer_has_failed (&pb))
+ return -EMSGSIZE;
+ else
+ {
+ *result = NULL;
+ return 0;
+ }
+}
+
+
+/* Common tail of __nscd_generic_get. */
+static bool
+__nscd_generic_get_tail (enum nss_lookup_type lt, unsigned int db,
+ request_type type,
+ const char *nscd_key, size_t nscd_keylen,
+ void **result)
+{
+ bool ok = true;
+ struct scratch_buffer buffer;
+ scratch_buffer_init (&buffer);
+
+ /* Try the mapping first. */
+ bool cache_hit = __nscd_try_cache (db, type, nscd_key, nscd_keylen,
+ lt, &buffer, result);
+
+ /* If the cache did not produce data, try the socket. The socket
+ may produce a successful negative result (*result == NULL). */
+ if (!cache_hit)
+ ok = __nscd_try_socket (type, nscd_key, nscd_keylen, lt, &buffer,
+ result);
+
+ scratch_buffer_free (&buffer);
+ return ok;
+}
+
+#define NSCD_NUMBER_BUFFER_LENGTH 20
+
+static int
+__nscd_fmt_num (char storage[static NSCD_NUMBER_BUFFER_LENGTH], int number)
+{
+ return __snprintf (storage, NSCD_NUMBER_BUFFER_LENGTH, "%d", number) + 1;
+}
+
+bool
+__nscd_generic_get (enum nss_lookup_type lt, const void *key, void **result)
+{
+ /* Storage for keys that need to be converted. */
+ char storage[20];
+
+ int8_t db = __nscd_database_for_lookup[lt];
+ if (db < 0 || !__nscd_use_database (db))
+ return false;
+
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ return __nscd_generic_get_tail (lt, db, GETGRBYGID, storage,
+ __nscd_fmt_num (storage,
+ *(const gid_t *) key),
+ result);
+ case nss_lookup_getgrnam:
+ return __nscd_generic_get_tail (lt, db, GETGRBYNAME,
+ key, strlen (key) + 1,
+ result);
+ case nss_lookup_getpwnam:
+ return __nscd_generic_get_tail (lt, db, GETPWBYNAME,
+ key, strlen (key) + 1,
+ result);
+ case nss_lookup_getpwuid:
+ return __nscd_generic_get_tail (lt, db, GETPWBYUID, storage,
+ __nscd_fmt_num (storage,
+ *(const uid_t *) key),
+ result);
+ }
+ __builtin_unreachable ();
+}
+
+static int
+__nscd_parse_and_copy_passwd (enum nss_lookup_type lt,
+ void *buffer, ssize_t length, void **result)
+{
+ if (length < 0)
+ {
+ __nscd_defer_database (NSS_DBSIDX_passwd);
+ return -ENOENT;
+ }
+
+ struct parse_buffer pb = parse_buffer_create (buffer, length);
+
+ int32_t pw_found = parse_buffer_field (&pb, 0, pw_response_header, found);
+ if (pw_found == 1)
+ {
+ size_t pos = sizeof (pw_response_header);
+ struct passwd pwd;
+ pwd.pw_name = parse_buffer_sized_cstring_advance
+ (&pb, &pos,
+ parse_buffer_field (&pb, 0, pw_response_header, pw_name_len));
+ pwd.pw_passwd = parse_buffer_sized_cstring_advance
+ (&pb, &pos,
+ parse_buffer_field (&pb, 0, pw_response_header, pw_passwd_len));
+ pwd.pw_uid = parse_buffer_field (&pb, 0, pw_response_header, pw_uid);
+ pwd.pw_gid = parse_buffer_field (&pb, 0, pw_response_header, pw_gid);
+ pwd.pw_gecos = parse_buffer_sized_cstring_advance
+ (&pb, &pos,
+ parse_buffer_field (&pb, 0, pw_response_header, pw_gecos_len));
+ pwd.pw_dir = parse_buffer_sized_cstring_advance
+ (&pb, &pos,
+ parse_buffer_field (&pb, 0, pw_response_header, pw_dir_len));
+ pwd.pw_shell = parse_buffer_sized_cstring_advance
+ (&pb, &pos,
+ parse_buffer_field (&pb, 0, pw_response_header, pw_shell_len));
+
+ return __nscd_parse_and_copy_tail (lt, pb, &pwd, result);
+ }
+ else if (pw_found == -1)
+ {
+ /* The daemon does not cache this database. */
+ __nscd_defer_database (NSS_DBSIDX_passwd);
+ return -ENOENT;
+ }
+ else if (parse_buffer_has_failed (&pb))
+ return -EMSGSIZE;
+ else
+ {
+ *result = NULL;
+ return 0;
+ }
+}
@@ -64,4 +64,20 @@ void *__nss_generic_dup (enum nss_lookup_type lt, const void *source)
each lookup type, or -1 if the lookup has no corresponding database. */
extern int8_t __nscd_database_for_lookup[nss_lookup_MAX] attribute_hidden;
+/* Interpreted according to enum nss_lookup_type. Typically a string
+ or a pointer to an integer. */
+typedef const void *nss_lookup_key;
+
+/* Query the nscd daemon for KEY according to LT. On success, return
+ true, and write a NSS struct pointer allocated using
+ __nss_generic_dup to *RESULT (positive result), or a null pointer
+ (negative result). On failure, return false. The caller is
+ expected to attempt the NSS operation directly after a failure.
+
+ Failure is also possible if nscd lookup has been deferred; the
+ implementation calls __nscd_use_database as a first step to check
+ if nscd should actually be used. */
+bool __nscd_generic_get (enum nss_lookup_type lt, nss_lookup_key key,
+ void **result);
+
#endif /* NSS_GENERIC_H */