>From bd0c8e57538d4b52f9602333794a1a1fb6582c05 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Tue, 3 Jan 2012 02:46:40 -0500
Subject: [PATCH 15/15] sss_client: shared memory cache group map support

---
 Makefile.am                   |    1 +
 src/sss_client/nss_group.c    |   43 ++++++++
 src/sss_client/nss_mc.h       |    9 ++
 src/sss_client/nss_mc_group.c |  216 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 269 insertions(+), 0 deletions(-)
 create mode 100644 src/sss_client/nss_mc_group.c

diff --git a/Makefile.am b/Makefile.am
index ed2b56c7fceee908cd5399fd3d93dd631d009633..f00691c6909695d32048b2ba218054f8e7c58334 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -858,6 +858,7 @@ libnss_sss_la_SOURCES = \
     src/sss_client/nss_mc_common.c \
     src/util/murmurhash3.c \
     src/sss_client/nss_mc_passwd.c \
+    src/sss_client/nss_mc_group.c \
     src/sss_client/nss_mc.h
 libnss_sss_la_LDFLAGS = \
     -module \
diff --git a/src/sss_client/nss_group.c b/src/sss_client/nss_group.c
index 9e308c9298e11613d5f53b4a297dad0d08eac762..0110effabb96ca1b0d6b723433f222e9bfbc8624 100644
--- a/src/sss_client/nss_group.c
+++ b/src/sss_client/nss_group.c
@@ -28,6 +28,7 @@
 #include <stdint.h>
 #include <string.h>
 #include "sss_cli.h"
+#include "nss_mc.h"
 
 static struct sss_nss_getgrent_data {
     size_t len;
@@ -261,6 +262,27 @@ enum nss_status _nss_sss_getgrnam_r(const char *name, struct group *result,
     /* Caught once glibc passing in buffer == 0x0 */
     if (!buffer || !buflen) return ERANGE;
 
+    ret = sss_nss_mc_getgrnam(name, result, buffer, buflen);
+    switch (ret) {
+    case 0:
+        *errnop = 0;
+        return NSS_STATUS_SUCCESS;
+    case ERANGE:
+        *errnop = ERANGE;
+        return NSS_STATUS_TRYAGAIN;
+    case ENOENT:
+        /* fall through, we need to actively ask the parent
+         * if no entry is found */
+        break;
+    case EINVAL:
+        /* if using the mmaped cache failed,
+         * fall back to socket based comms */
+        break;
+    default:
+        *errnop = EBADMSG;
+        return NSS_STATUS_TRYAGAIN;
+    }
+
     rd.len = strlen(name) + 1;
     rd.data = name;
 
@@ -321,6 +343,27 @@ enum nss_status _nss_sss_getgrgid_r(gid_t gid, struct group *result,
     /* Caught once glibc passing in buffer == 0x0 */
     if (!buffer || !buflen) return ERANGE;
 
+    ret = sss_nss_mc_getgrgid(gid, result, buffer, buflen);
+    switch (ret) {
+    case 0:
+        *errnop = 0;
+        return NSS_STATUS_SUCCESS;
+    case ERANGE:
+        *errnop = ERANGE;
+        return NSS_STATUS_TRYAGAIN;
+    case ENOENT:
+        /* fall through, we need to actively ask the parent
+         * if no entry is found */
+        break;
+    case EINVAL:
+        /* if using the mmaped cache failed,
+         * fall back to socket based comms */
+        break;
+    default:
+        *errnop = EBADMSG;
+        return NSS_STATUS_TRYAGAIN;
+    }
+
     group_gid = gid;
     rd.len = sizeof(uint32_t);
     rd.data = &group_gid;
diff --git a/src/sss_client/nss_mc.h b/src/sss_client/nss_mc.h
index 8d1deedb26dfa8b282641ae3b86a8349a76789fd..bb49f13920d78e23fe3790d82b956cde87c1b14f 100644
--- a/src/sss_client/nss_mc.h
+++ b/src/sss_client/nss_mc.h
@@ -26,6 +26,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <pwd.h>
+#include <grp.h>
 #include "util/mmap_cache.h"
 
 /* common stuff */
@@ -59,4 +60,12 @@ errno_t sss_nss_mc_getpwuid(uid_t uid,
                             struct passwd *result,
                             char *buffer, size_t buflen);
 
+/* group db */
+errno_t sss_nss_mc_getgrnam(const char *name,
+                            struct group *result,
+                            char *buffer, size_t buflen);
+errno_t sss_nss_mc_getgrgid(gid_t gid,
+                            struct group *result,
+                            char *buffer, size_t buflen);
+
 #endif /* _NSS_MC_H_ */
diff --git a/src/sss_client/nss_mc_group.c b/src/sss_client/nss_mc_group.c
new file mode 100644
index 0000000000000000000000000000000000000000..e7709b47d9dfd5fac87764f3a75ba9b0fb31ce45
--- /dev/null
+++ b/src/sss_client/nss_mc_group.c
@@ -0,0 +1,216 @@
+/*
+ * System Security Services Daemon. NSS client interface
+ *
+ * Copyright (C) Simo Sorce 2011
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* GROUP database NSS interface using mmap cache */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <time.h>
+#include "nss_mc.h"
+
+struct sss_cli_mc_ctx gr_mc_ctx = { false, -1, NULL, 0, NULL, 0, NULL, 0 };
+
+static errno_t sss_nss_mc_parse_result(struct sss_mc_rec *rec,
+                                       struct group *result,
+                                       char *buffer, size_t buflen)
+{
+    struct sss_mc_grp_data *data;
+    time_t expire;
+    void *cookie;
+    char *membuf;
+    size_t memsize;
+    int ret;
+    int i;
+
+    /* additional checks before filling result*/
+    expire = rec->expire;
+    if (expire < time(NULL)) {
+        /* entry is now invalid */
+        return EINVAL;
+    }
+
+    data = (struct sss_mc_grp_data *)rec->data;
+
+    memsize = (data->members + 1) * sizeof(char *);
+    if (data->strs_len + memsize > buflen) {
+        return ERANGE;
+    }
+
+    /* fill in glibc provided structs */
+
+    /* copy in buffer */
+    membuf = buffer + memsize;
+    memcpy(membuf, data->strs, data->strs_len);
+
+    /* fill in group */
+    result->gr_gid = data->gid;
+    result->gr_mem = (char **)buffer;
+    result->gr_mem[data->members] = NULL;
+
+    cookie = NULL;
+    ret = sss_nss_str_ptr_from_buffer(&result->gr_name, &cookie,
+                                      membuf, data->strs_len);
+    if (ret) {
+        return ret;
+    }
+    ret = sss_nss_str_ptr_from_buffer(&result->gr_passwd, &cookie,
+                                      membuf, data->strs_len);
+    if (ret) {
+        return ret;
+    }
+
+    for (i = 0; i < data->members; i++) {
+        ret = sss_nss_str_ptr_from_buffer(&result->gr_mem[i], &cookie,
+                                          membuf, data->strs_len);
+        if (ret) {
+            return ret;
+        }
+    }
+    if (cookie != NULL) {
+        return EINVAL;
+    }
+
+    return 0;
+}
+
+errno_t sss_nss_mc_getgrnam(const char *name,
+                            struct group *result,
+                            char *buffer, size_t buflen)
+{
+    struct sss_mc_rec *rec = NULL;
+    struct sss_mc_grp_data *data;
+    char *rec_name;
+    uint32_t hash;
+    uint32_t slot;
+    int ret;
+
+    ret = sss_nss_mc_get_ctx("group", &gr_mc_ctx);
+    if (ret) {
+        return ret;
+    }
+
+    /* hashes are calculated including the NULL terminator */
+    hash = sss_nss_mc_hash(name, strlen(name)+1, gr_mc_ctx.ht_size);
+    slot = gr_mc_ctx.hash_table[hash];
+    if (slot > MC_SIZE_TO_SLOTS(gr_mc_ctx.dt_size)) {
+        return ENOENT;
+    }
+
+    while (slot != MC_INVALID_VAL) {
+
+        ret = sss_nss_mc_get_record(&gr_mc_ctx, slot, &rec);
+        if (ret) {
+            goto done;
+        }
+
+        /* check record matches what we are searching for */
+        if (hash != rec->hash1) {
+            /* if name hash does not match we can skip this immediately */
+            slot = rec->next;
+            continue;
+        }
+
+        data = (struct sss_mc_grp_data *)rec->data;
+        rec_name = (char *)data + data->name;
+        if (strcmp(name, rec_name) == 0) {
+            break;
+        }
+
+        slot = rec->next;
+    }
+
+    if (slot == MC_INVALID_VAL) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    ret = sss_nss_mc_parse_result(rec, result, buffer, buflen);
+
+done:
+    free(rec);
+    return ret;
+}
+
+errno_t sss_nss_mc_getgrgid(gid_t gid,
+                            struct group *result,
+                            char *buffer, size_t buflen)
+{
+    struct sss_mc_rec *rec = NULL;
+    struct sss_mc_grp_data *data;
+    char gidstr[11];
+    uint32_t hash;
+    uint32_t slot;
+    int len;
+    int ret;
+
+    ret = sss_nss_mc_get_ctx("group", &gr_mc_ctx);
+    if (ret) {
+        return ret;
+    }
+
+    len = snprintf(gidstr, 11, "%ld", (long)gid);
+    if (len > 10) {
+        return EINVAL;
+    }
+
+    /* hashes are calculated including the NULL terminator */
+    hash = sss_nss_mc_hash(gidstr, len+1, gr_mc_ctx.ht_size);
+    slot = gr_mc_ctx.hash_table[hash];
+    if (slot > MC_SIZE_TO_SLOTS(gr_mc_ctx.dt_size)) {
+        return ENOENT;
+    }
+
+    while (slot != MC_INVALID_VAL) {
+
+        ret = sss_nss_mc_get_record(&gr_mc_ctx, slot, &rec);
+        if (ret) {
+            goto done;
+        }
+
+        /* check record matches what we are searching for */
+        if (hash != rec->hash2) {
+            /* if uid hash does not match we can skip this immediately */
+            slot = rec->next;
+            continue;
+        }
+
+        data = (struct sss_mc_grp_data *)rec->data;
+        if (gid == data->gid) {
+            break;
+        }
+
+        slot = rec->next;
+    }
+
+    if (slot == MC_INVALID_VAL) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    ret = sss_nss_mc_parse_result(rec, result, buffer, buflen);
+
+done:
+    free(rec);
+    return ret;
+}
+
-- 
1.7.7.5

