--- /dev/null	Mon Sep 10 13:31:18 2012
+++ new/usr/src/lib/nsswitch/files/common/getnetgrent.c	Tue Sep 11 18:26:28 2012
@@ -0,0 +1,311 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+/*
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <ctype.h>
+#include <thread.h>
+#include <synch.h>
+#include <time.h>
+#include <revnetgroup.h>
+
+#include "files_common.h"
+
+#define	PATH_NETGROUP		"/etc/netgroup"
+
+static uint_t
+hash_netgr_host(nss_XbyY_args_t *argp, int keyhash, const char *line,
+	int linelen)
+{
+	const char *end;
+	uint_t hash = 0;
+	int power = 0;
+
+	if (keyhash) {
+		struct nss_innetgr_args	*ia;
+
+		ia = (struct nss_innetgr_args *)argp->key.name;
+
+		line = ia->arg[NSS_NETGR_MACHINE].argv[0];
+		end = line + strlen(line);
+	} else {
+		const char *s;
+
+		end = strchr(line, '\t');
+		if (end == NULL)
+			end = line;
+
+		s = strstr(line, ".*");
+		if (s != NULL && s < end) {
+			end = s;
+		}
+
+	}
+
+	while (line < end) {
+		uint_t c = *line++;
+
+		c <<= power++;
+		hash += c;
+	}
+
+	return (hash);
+}
+
+static files_hash_func hash_netgr[] = { hash_netgr_host };
+
+static files_hash_t hashinfo = {
+	DEFAULTMUTEX,
+	0,
+	NSS_BUFLEN_GROUP,
+	1,
+	hash_netgr
+};
+
+/* ARGSUSED */
+static int
+dummy_netgr(const char *in, int inlen,
+    void *ent, char *buf, int buflen)
+{
+	return (NSS_STR_PARSE_SUCCESS);
+}
+
+/*
+ * return parsed or not
+ */
+static boolean_t
+netgr_extract_hostname(char *str, char **hostname, char **next)
+{
+	char *s;
+
+	s = strchr(str, '\t');
+	if (s == NULL || s == str)
+		return (B_FALSE);
+
+	*s = '\0';
+	*hostname = str;
+	*next = s + 1;
+
+	return (B_TRUE);
+}
+
+static boolean_t
+netgr_hosts_match(const char *hostname, const char *host, const char *domain)
+{
+	char buf[NSS_BUFSIZ];
+	uint_t n;
+
+	/*
+	 * hostname can be "a.b.c.*"
+	 */
+	n = strlen(hostname);
+	if (n > 0 && hostname[n - 1] == '*')
+		domain = "*";
+
+	snprintf(buf, sizeof (buf), "%s.%s", host, domain);
+
+	if (strcmp(hostname, buf))
+		return (B_FALSE);
+	else
+		return (B_TRUE);
+}
+
+static boolean_t
+netgr_netgroups_match(const char *netgroups, const char *group)
+{
+	char *s;
+	uint_t len;
+
+	/* walk netgroups */
+
+	s = strstr(netgroups, group);
+	if (s == NULL)
+		return (B_FALSE);
+
+	/* check beginning */
+	if (s != netgroups && s[-1] != ',')
+		return (B_FALSE);
+
+	/* check end */
+	len = strlen(group);
+	if (s[len] != ',' && s[len] != '\0')
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+/*
+ * Parse string: "{hostname} {netgroup1} ... {netgroup2}"
+ */
+static int
+check_netgr(nss_XbyY_args_t *argp, const char *line, int linelen)
+{
+	struct nss_innetgr_args	*ia;
+	char buf[NSS_BUFSIZ];
+	const char *host;
+	const char *domain;
+	const char *group;
+	const char *netgroup;
+	char *hostname, *next;
+
+	if (linelen > NSS_BUFSIZ || linelen < 0)
+		return (0);
+
+	/* use only one passed argument */
+	ia = (struct nss_innetgr_args *)argp->key.name;
+	host = ia->arg[NSS_NETGR_MACHINE].argv[0];
+	domain = ia->arg[NSS_NETGR_DOMAIN].argv[0];
+	netgroup = ia->groups.argv[0];
+
+	strlcpy(buf, line, NSS_BUFSIZ);
+
+	if (!netgr_extract_hostname(buf, &hostname, &next))
+		return (0);
+
+	if (!netgr_hosts_match(hostname, host, domain))
+		return (0);
+
+	if (!netgr_netgroups_match(next, netgroup))
+		return (0);
+
+	return (1);
+}
+
+static nss_status_t
+netgr_in(files_backend_ptr_t be, void *a)
+{
+	struct nss_innetgr_args	*ia = (struct nss_innetgr_args *)a;
+	nss_status_t	rc = (nss_status_t)NSS_NOTFOUND;
+	nss_XbyY_args_t xa = {0};
+	struct stat64 st;
+	int retries;
+
+	retries = 5;
+	while (stat64(PATH_NETGROUP, &st) < 0) {
+		struct timespec delay;
+
+		/* 50 ms */
+		delay.tv_sec = 0;
+		delay.tv_nsec = 50000000;
+
+		nanosleep(&delay, NULL);
+		if (--retries == 0)
+			goto out;
+	}
+
+	mutex_lock(&hashinfo.fh_lock);
+
+	if ((uint_t)st.st_mtim.tv_sec > (uint_t)hashinfo.fh_mtime.tv_sec) {
+		FILE *fin, *fout;
+
+		/*
+		 * netgroup file is more recent than parsed copy
+		 */
+
+		fin = fopen(PATH_NETGROUP, "r");
+		if (fin == NULL)
+			goto out_unlock;
+
+		fout = fopen(be->filename, "w");
+		if (fout == NULL) {
+			fclose(fin);
+			goto out_unlock;
+		}
+
+		revnetgroup_handle(fin, fout, B_FALSE);
+
+		fclose(fout);
+		fclose(fin);
+	}
+
+out_unlock:
+	mutex_unlock(&hashinfo.fh_lock);
+
+	/* some hack to pass arguments to check_netgr */
+	xa.key.name = (char *)ia;
+	xa.str2ent = dummy_netgr;
+	rc = _nss_files_XY_hash(be, &xa, 1, &hashinfo, 0, check_netgr);
+
+out:
+	ia->status = (rc == NSS_SUCCESS) ? NSS_NETGR_FOUND : NSS_NETGR_NO;
+	return (rc);
+}
+
+/*ARGSUSED*/
+nss_status_t
+netgr_files_destr(files_backend_ptr_t be, void *dummy)
+{
+	nss_status_t status;
+
+	unlink(be->filename);
+	free((void *)be->filename);
+	status = _nss_files_destr(be, dummy);
+	return (status);
+}
+
+static char *
+create_uniq_filename(void)
+{
+	char *filename;
+	int dummy_fd;
+
+	filename = malloc(PATH_MAX);
+	if (filename == NULL)
+		return (NULL);
+
+	strlcpy(filename, "/tmp/revnetgroup-XXXXXX", PATH_MAX);
+
+	dummy_fd = mkstemp(filename);
+	close(dummy_fd);
+	if (dummy_fd < 0) {
+		free(filename);
+		return (NULL);
+	}
+	return (filename);
+}
+
+static files_backend_op_t netgr_ops[] = {
+	netgr_files_destr,
+	NULL,			/* No endent, because no setent */
+	NULL,			/* No setent */
+	NULL,			/* No getent_netdb */
+	netgr_in,		/* innetgr */
+	NULL,			/* No setnetgrent */
+};
+
+/*ARGSUSED*/
+nss_backend_t *
+_nss_files_netgroup_constr(const char *dummy1,
+    const char *dummy2, const char *dummy3)
+{
+	char *filename;
+
+	filename = create_uniq_filename();
+	if (filename == NULL)
+		return (NULL);
+
+	return (_nss_files_constr(netgr_ops,
+	    sizeof (netgr_ops) / sizeof (netgr_ops[0]),
+	    filename,
+	    NSS_LINELEN_NETWORKS,
+	    NULL));
+}
