summaryrefslogtreecommitdiffstats
path: root/xlators/nfs/server/src
diff options
context:
space:
mode:
authorNiels de Vos <ndevos@redhat.com>2015-01-01 13:15:45 +0100
committerVijay Bellur <vbellur@redhat.com>2015-03-11 09:27:50 -0700
commit02d42a5e80f3e2624eba2d00acff0aaebb20b12f (patch)
tree8b70ae4f2e245b6004b6b9dc8caa4c2a7afd9bc8 /xlators/nfs/server/src
parent381abb5bd2b09a4c40b20ddbe6d385f9a849e384 (diff)
nfs: add structures and functions for parsing netgroups
Netgroups are often used by enterprises to group a set of systems. The NFS /etc/exports file support the @netgroup notation, and Gluster/NFS will get extended to support this notation as well. For this, it is needed that Gluster/NFS learns to parse the netgroup format. A change to glusterfsd (Change-Id I24c40d5) will add test cases where the parsing is used for regression testing. BUG: 1143880 Change-Id: Ie04800d4dc26f99df922c9fcc00845f53291cf4f Original-author: Shreyas Siravara <shreyas.siravara@gmail.com> CC: Richard Wareing <rwareing@fb.com> CC: Jiffin Tony Thottan <jthottan@redhat.com> Signed-off-by: Niels de Vos <ndevos@redhat.com> Reviewed-on: http://review.gluster.org/9360 Tested-by: Gluster Build System <jenkins@build.gluster.com> Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'xlators/nfs/server/src')
-rw-r--r--xlators/nfs/server/src/Makefile.am4
-rw-r--r--xlators/nfs/server/src/netgroups.c1141
-rw-r--r--xlators/nfs/server/src/netgroups.h54
-rw-r--r--xlators/nfs/server/src/nfs-mem-types.h1
4 files changed, 1198 insertions, 2 deletions
diff --git a/xlators/nfs/server/src/Makefile.am b/xlators/nfs/server/src/Makefile.am
index 989747a249d..c72b314c458 100644
--- a/xlators/nfs/server/src/Makefile.am
+++ b/xlators/nfs/server/src/Makefile.am
@@ -4,13 +4,13 @@ nfsrpclibdir = $(top_srcdir)/rpc/rpc-lib/src
server_la_LDFLAGS = -module -avoid-version
server_la_SOURCES = nfs.c nfs-common.c nfs-fops.c nfs-inodes.c \
nfs-generics.c mount3.c nfs3-fh.c nfs3.c nfs3-helpers.c nlm4.c \
- nlmcbk_svc.c mount3udp_svc.c acl3.c
+ nlmcbk_svc.c mount3udp_svc.c acl3.c netgroups.c
server_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \
$(top_builddir)/api/src/libgfapi.la
noinst_HEADERS = nfs.h nfs-common.h nfs-fops.h nfs-inodes.h nfs-generics.h \
mount3.h nfs3-fh.h nfs3.h nfs3-helpers.h nfs-mem-types.h nlm4.h \
- acl3.h
+ acl3.h netgroups.h
AM_CPPFLAGS = $(GF_CPPFLAGS) \
-DLIBDIR=\"$(libdir)/glusterfs/$(PACKAGE_VERSION)/auth\" \
diff --git a/xlators/nfs/server/src/netgroups.c b/xlators/nfs/server/src/netgroups.c
new file mode 100644
index 00000000000..c605ca2664c
--- /dev/null
+++ b/xlators/nfs/server/src/netgroups.c
@@ -0,0 +1,1141 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2),in all
+ cases as published by the Free Software Foundation.
+*/
+
+#include "netgroups.h"
+#include "parse-utils.h"
+
+static void _nge_print (const struct netgroup_entry *nge);
+static void _netgroup_entry_deinit (struct netgroup_entry *ptr);
+static void _netgroup_host_deinit (struct netgroup_host *host);
+
+static dict_t *__deleted_entries;
+static struct parser *ng_file_parser;
+static struct parser *ng_host_parser;
+
+/**
+ * _ng_init_parser -- Initialize the parsers used in this file
+ *
+ * @return: success: 0 (on success the parsers are initialized)
+ * failure: -1
+ */
+static int
+_ng_init_parsers ()
+{
+ int ret = -1;
+
+ /* Initialize the parsers. The only reason this should
+ * ever fail is because of 1) memory allocation errors
+ * 2) the regex in netgroups.h has been changed and no
+ * longer compiles.
+ */
+ ng_file_parser = parser_init (NG_FILE_PARSE_REGEX);
+ if (!ng_file_parser)
+ goto out;
+
+ ng_host_parser = parser_init (NG_HOST_PARSE_REGEX);
+ if (!ng_host_parser)
+ goto out;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * _ng_deinit_parsers - Free the parsers used in this file
+ */
+static void
+_ng_deinit_parsers ()
+{
+ parser_deinit (ng_file_parser);
+ parser_deinit (ng_host_parser);
+}
+
+/**
+ * _netgroups_file_init - allocate a netgroup file struct
+ * @return: success: Pointer to an allocated netgroup file struct
+ * failure: NULL
+ *
+ * Not for external use.
+ */
+static struct netgroups_file *
+_netgroups_file_init ()
+{
+ struct netgroups_file *file = GF_MALLOC (sizeof (*file),
+ gf_nfs_mt_netgroups);
+
+ if (!file)
+ goto out;
+
+ file->filename = NULL;
+ file->ng_file_dict = NULL;
+out:
+ return file;
+}
+
+/**
+ * __ngf_free_walk - walk the netgroup file dict and free each element
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngf_free_walk (dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_entry *nge = NULL;
+
+ if (val) {
+ nge = (struct netgroup_entry *)val->data;
+ _netgroup_entry_deinit (nge);
+ val->data = NULL;
+ dict_del (dict, key); /* Remove the key from this dict */
+ }
+ return 0;
+}
+
+/**
+ * __deleted_entries_free_walk - free the strings in the temporary dict
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__deleted_entries_free_walk (dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ dict_del (dict, key);
+ return 0;
+}
+
+/**
+ * ng_file_deinit - Free the netgroup file struct and any memory
+ * that is allocated for its members.
+ *
+ * @ngfile : Pointer to the netgroup file structure that needs to be freed
+ * @return : Nothing
+ *
+ * External facing function.
+ *
+ * Should be called by the caller of ng_file_parse () in order to free
+ * the memory allocated when parsing the file.
+ */
+void
+ng_file_deinit (struct netgroups_file *ngfile)
+{
+ GF_VALIDATE_OR_GOTO (GF_NG, ngfile, out);
+
+ __deleted_entries = dict_new ();
+ GF_VALIDATE_OR_GOTO (GF_NG, __deleted_entries, out);
+
+ GF_FREE (ngfile->filename);
+ dict_foreach (ngfile->ng_file_dict, __ngf_free_walk, NULL);
+ dict_unref (ngfile->ng_file_dict);
+ GF_FREE (ngfile);
+
+ /* Clean up temporary dict we used to store "freed" names */
+ dict_foreach (__deleted_entries, __deleted_entries_free_walk, NULL);
+ dict_unref (__deleted_entries);
+ __deleted_entries = NULL;
+out:
+ return;
+}
+
+/**
+ * _netgroup_entry_init - Initializes a netgroup entry struct.
+ * A netgroup entry struct represents a single line in a netgroups file.
+ *
+ * @return : success: Pointer to a netgroup entry struct
+ * : failure: NULL
+ *
+ * Not for external use.
+ */
+static struct netgroup_entry *
+_netgroup_entry_init ()
+{
+ struct netgroup_entry *entry = GF_CALLOC (1, sizeof (*entry),
+ gf_nfs_mt_netgroups);
+ return entry;
+}
+
+/**
+ * __ngh_free_walk - walk the netgroup host dict and free the host
+ * structure associated with the key.
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngh_free_walk (dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_host *ngh = NULL;
+
+ if (val) {
+ ngh = (struct netgroup_host *)val->data;
+ _netgroup_host_deinit (ngh);
+ val->data = NULL;
+ dict_del (dict, key);
+ }
+ return 0;
+}
+
+/**
+ * __nge_free_walk - walk the netgroup entry dict and free the netgroup entry
+ * structure associated with the key.
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__nge_free_walk (dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_entry *nge = NULL;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, dict, out);
+
+ if (val) {
+ nge = (struct netgroup_entry *)val->data;
+ if (!dict_get (__deleted_entries, key)) {
+ _netgroup_entry_deinit (nge);
+ val->data = NULL;
+ }
+ dict_del (dict, key);
+ }
+
+out:
+ return 0;
+}
+
+/**
+ * _netgroup_entry_deinit - Free memory pointed to by the parameter
+ * and any memory allocated for members
+ * in the struct. This function walks the
+ * netgroups and hosts dicts if they
+ * are allocated and frees them.
+ *
+ * @ngentry: Pointer to a netgroup entry struct that needs to be freed
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static void
+_netgroup_entry_deinit (struct netgroup_entry *ngentry)
+{
+ dict_t *ng_dict = NULL;
+ dict_t *host_dict = NULL;
+ char *name = NULL;
+ data_t *dint = NULL;
+
+ if (!ngentry)
+ return;
+
+ ng_dict = ngentry->netgroup_ngs;
+ host_dict = ngentry->netgroup_hosts;
+
+ if (ng_dict) {
+ /* Free the dict of netgroup entries */
+ dict_foreach (ng_dict, __nge_free_walk, NULL);
+ dict_unref (ng_dict);
+ ngentry->netgroup_ngs = NULL;
+ }
+
+ if (host_dict) {
+ /* Free the dict of host entries */
+ dict_foreach (host_dict, __ngh_free_walk, NULL);
+ dict_unref (host_dict);
+ ngentry->netgroup_hosts = NULL;
+ }
+
+ if (ngentry->netgroup_name) {
+ /* Keep track of the netgroup names we've deallocated
+ * We need to do this because of the nature of this data
+ * structure. This data structure may hold multiple
+ * pointers to an already freed object, but these are
+ * uniquely identifiable by the name. We keep track
+ * of these names so when we encounter a key who has
+ * an association to an already freed object, we don't
+ * free it twice.
+ */
+ name = strdupa (ngentry->netgroup_name);
+
+ dint = int_to_data (1);
+ dict_set (__deleted_entries, name, dint);
+
+ GF_FREE (ngentry->netgroup_name);
+ ngentry->netgroup_name = NULL;
+ }
+
+ GF_FREE (ngentry);
+}
+
+/**
+ * _netgroup_host_init - Initializes a netgroup host structure.
+ * A netgroup host struct represents an item in a line of a netgroups file that
+ * looks like this : (hostname,user,domain)
+ *
+ * @return : success: Pointer to a netgroup host struct
+ * : failure: NULL
+ *
+ * Not for external use.
+ */
+static struct netgroup_host *
+_netgroup_host_init ()
+{
+ struct netgroup_host *host = GF_CALLOC (1, sizeof (*host),
+ gf_nfs_mt_netgroups);
+ return host;
+}
+
+/**
+ * _netgroup_host_deinit - Free memory pointed to by the parameter
+ * and any memory allocated for members in the struct.
+ *
+ * @nghost : Pointer to a netgroup host struct that needs to be freed
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static void
+_netgroup_host_deinit (struct netgroup_host *host)
+{
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO (GF_NG, host, err);
+
+ GF_FREE (host->hostname);
+ host->hostname = NULL;
+
+ GF_FREE (host->user);
+ host->user = NULL;
+
+ GF_FREE (host->domain);
+ host->domain = NULL;
+
+ GF_FREE (host);
+err:
+ return;
+}
+
+/**
+ * _nge_dict_get - Lookup a netgroup entry from the dict based
+ * on the netgroup name.
+ *
+ * @dict : The dict we are looking up from. This function makes the
+ * assumption that the type of underlying data in the dict is of type
+ * struct netgroup_entry. The behavior is not defined otherwise.
+ *
+ * @ngname : Key used to lookup in the dict.
+ *
+ * @return : success: Pointer to a netgroup entry
+ * failure: NULL (if no such key exists in the dict)
+ *
+ * Not for external use.
+ */
+static struct netgroup_entry *
+_nge_dict_get (dict_t *dict, const char *ngname)
+{
+ data_t *ngdata = NULL;
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO (GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO (GF_NG, ngname, err);
+
+ ngdata = dict_get (dict, (char *)ngname);
+ if (ngdata)
+ return (struct netgroup_entry *)ngdata->data;
+err:
+ return NULL;
+}
+
+/**
+ * _nge_dict_insert - Insert a netgroup entry into the dict using
+ * the netgroup name as the key.
+ *
+ * @dict : The dict we are inserting into.
+ *
+ * @nge : The data to insert into the dict.
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_nge_dict_insert (dict_t *dict, struct netgroup_entry *nge)
+{
+ data_t *ngdata = NULL;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO (GF_NG, nge, err);
+
+ ngdata = bin_to_data (nge, sizeof (*nge));
+ dict_set (dict, nge->netgroup_name, ngdata);
+err:
+ return;
+}
+
+/**
+ * _ngh_dict_get - Lookup a netgroup host entry from the dict based
+ * on the hostname.
+ *
+ * @dict : The dict we are looking up from. This function makes the
+ * assumption that the type of underlying data in the dict is of type
+ * struct netgroup_host. The behavior is not defined otherwise.
+ *
+ * @ngname : Key used to lookup in the dict.
+ *
+ * @return : success: Pointer to a netgroup host entry
+ * failure: NULL (if no such key exists in the dict)
+ *
+ * Externally usable.
+ */
+struct netgroup_host *
+ngh_dict_get (dict_t *dict, const char *hostname)
+{
+ data_t *ngdata = NULL;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO (GF_NG, hostname, err);
+
+ ngdata = dict_get (dict, (char *)hostname);
+ if (!ngdata)
+ goto err;
+
+ return (struct netgroup_host *)ngdata->data;
+
+err:
+ return NULL;
+}
+
+/**
+ * _ngh_dict_insert - Insert a netgroup host entry into the dict using
+ * the netgroup name as the key.
+ *
+ * @dict : The dict we are inserting into.
+ *
+ * @nge : The data to insert into the dict.
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_ngh_dict_insert (dict_t *dict, struct netgroup_host *ngh)
+{
+ data_t *ngdata = NULL;
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO (GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO (GF_NG, ngh, err);
+
+ ngdata = bin_to_data (ngh, sizeof (*ngh));
+ dict_set (dict, ngh->hostname, ngdata);
+err:
+ return;
+}
+
+/**
+ * _ngh_print - Prints the netgroup host in the
+ * format '(hostname,user,domain)'
+ *
+ * @ngh : The netgroup host to print out
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_ngh_print (const struct netgroup_host *ngh)
+{
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO (GF_NG, ngh, err);
+
+ printf ("(%s,%s,%s)", ngh->hostname, ngh->user ? ngh->user : "",
+ ngh->domain ? ngh->domain : "");
+err:
+ return;
+}
+
+/**
+ * __nge_print_walk - walk the netgroup entry dict and print each entry
+ * associated with the key. This function prints
+ * entries of type 'struct netgroup_entry'.
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__nge_print_walk (dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ if (val)
+ _nge_print ((struct netgroup_entry *)val->data);
+
+ return 0;
+}
+
+/**
+ * __ngh_print_walk - walk the netgroup entry dict and print each entry
+ * associated with the key. This function prints entries
+ * of type 'struct netgroup_host'
+ *
+ * This is passed as a function pointer to dict_foreach (),
+ * which is called from _nge_print ().
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngh_print_walk (dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ if (val)
+ _ngh_print ((struct netgroup_host *)val->data);
+
+ return 0;
+}
+
+/**
+ * _nge_print - Prints the netgroup entry in the
+ * format '<netgroup name> <following entries>'
+ *
+ * @ngh : The netgroup entry to print out
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_nge_print (const struct netgroup_entry *nge)
+{
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO (GF_NG, nge, err);
+
+ printf ("%s ", nge->netgroup_name);
+ if (nge->netgroup_ngs)
+ dict_foreach (nge->netgroup_ngs, __nge_print_walk, NULL);
+
+ if (nge->netgroup_hosts)
+ dict_foreach (nge->netgroup_hosts, __ngh_print_walk, NULL);
+
+err:
+ return;
+}
+
+/**
+ * __ngf_print_walk - walk through each entry in the netgroups file and print it
+ * out. This calls helper functions _nge_print () to print
+ * the netgroup entries.
+ *
+ * This is passed as a function pointer to dict_foreach (),
+ * which is called from ng_file_print ().
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngf_print_walk (dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_entry *snge = NULL;
+
+ if (val) {
+ snge = (struct netgroup_entry *)val->data;
+ _nge_print (snge);
+ printf ("\n");
+ }
+ return 0;
+}
+
+/**
+ * ng_file_print - Prints the netgroup file in the
+ * format '<netgroup name> <following entries>', etc.
+ * The netgroup file is a dict of netgroup entries
+ * which, in turn is a combination of a other 'sub' netgroup
+ * entries and host entries. This function prints
+ * all of that out by calling the corresponding print functions
+ *
+ * @ngfile : The netgroup file to print out
+ *
+ * @return : nothing
+ *
+ * External facing function.
+ *
+ * Can be called on any valid 'struct netgroups_file *' type.
+ */
+void
+ng_file_print (const struct netgroups_file *ngfile)
+{
+ dict_foreach (ngfile->ng_file_dict, __ngf_print_walk, NULL);
+}
+
+/**
+ * ng_file_get_netgroup - Look up a netgroup entry from the netgroups file
+ * based on the netgroup name and return a pointer
+ * to the netgroup entry.
+ *
+ * @ngfile : The netgroup file to lookup from.
+ * @netgroup : The netgroup name used to lookup from the netgroup file.
+ *
+ * @return : nothing
+ *
+ * External facing function.
+ *
+ * Can be called on any valid 'struct netgroups_file *' type with a valid 'char
+ * *' as the lookup key.
+ */
+struct netgroup_entry *
+ng_file_get_netgroup (const struct netgroups_file *ngfile, const char *netgroup)
+{
+ data_t *ndata = NULL;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, ngfile, err);
+ GF_VALIDATE_OR_GOTO (GF_NG, netgroup, err);
+
+ ndata = dict_get (ngfile->ng_file_dict,
+ (char *)netgroup);
+ if (!ndata)
+ goto err;
+
+ return (struct netgroup_entry *)ndata->data;
+
+err:
+ return NULL;
+}
+
+/**
+ * __check_host_entry_str - Check if the host string which should be
+ * in the format '(host,user,domain)' is
+ * valid to be parsed. Currently checks
+ * if the # of commas is correct and there
+ * are no spaces in the string, but more
+ * checks can be added.
+ *
+ * @host_str : String to check
+ * @return : success: TRUE if valid
+ * failure: FALSE if not
+ *
+ * Not for external use.
+ */
+static gf_boolean_t
+__check_host_entry_str (const char *host_str)
+{
+ unsigned int comma_count = 0;
+ unsigned int i = 0;
+ gf_boolean_t str_valid = _gf_true;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, host_str, out);
+
+ for (i = 0; i < strlen (host_str); i++) {
+ if (host_str[i] == ',')
+ comma_count++;
+
+ /* Spaces are not allowed in this string. e.g, (a,b,c) is valid
+ * but (a, b,c) is not.
+ */
+ if (host_str[i] == ' ') {
+ str_valid = _gf_false;
+ goto out;
+ }
+ }
+
+ str_valid = (comma_count == 2);
+out:
+ return str_valid;
+}
+
+/**
+ * _parse_ng_host - Parse the netgroup host string into a netgroup host struct.
+ * The netgroup host string is structured as follows:
+ * (host, user, domain)
+ *
+ * @ng_str : String to parse
+ * @return : success: 0 if the parsing succeeded
+ * failure: -EINVAL for bad args, -ENOMEM for allocation errors,
+ * 1 for parsing errors.
+ *
+ * Not for external use.
+ */
+static int
+_parse_ng_host (char *ng_str, struct netgroup_host **ngh)
+{
+ struct netgroup_host *ng_host = NULL;
+ unsigned int parts = 0;
+ char *match = NULL;
+ int ret = -EINVAL;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, ng_str, out);
+ GF_VALIDATE_OR_GOTO (GF_NG, ngh, out);
+
+ if (!__check_host_entry_str (ng_str)) {
+ ret = 1; /* Parse failed */
+ goto out;
+ }
+
+ ret = parser_set_string (ng_host_parser, ng_str);
+ if (ret < 0)
+ goto out;
+
+ ng_host = _netgroup_host_init ();
+ GF_CHECK_ALLOC (ng_host, ret, free_and_out); /* Sets ret to -ENOMEM on
+ * failure.
+ */
+ while ((match = parser_get_next_match (ng_host_parser)) != NULL) {
+ switch (parts) {
+ case 0:
+ ng_host->hostname = match;
+ break;
+ case 1:
+ ng_host->user = match;
+ break;
+ case 2:
+ ng_host->domain = match;
+ break;
+ default:
+ GF_FREE (match);
+ break;
+ };
+
+ /* We only allow three parts in the host string;
+ * The format for the string is (a,b,c)
+ */
+ parts++;
+ if (parts > 2)
+ break;
+ }
+
+ /* Set the parameter */
+ *ngh = ng_host;
+ ret = 0;
+
+free_and_out:
+ parser_unset_string (ng_host_parser);
+out:
+ return ret;
+}
+
+/**
+ * _ng_handle_host_part - Parse the host string that looks like this :
+ * '(dev1763.prn2.facebook.com,,)' into a host
+ * struct and insert it into the parent netgroup's
+ * host dict.
+ * @match : The host string
+ * @ngp : The parent netgroup
+ *
+ * @return: success: 0 if parsing succeeded
+ * failure: -EINVAL for bad args, other errors bubbled up
+ * from _parse_ng_host.
+ *
+ *
+ * Not for external use.
+ */
+static int
+_ng_handle_host_part (char *match, struct netgroup_entry *ngp)
+{
+ struct netgroup_host *ngh = NULL;
+ int ret = -EINVAL;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, match, out);
+ GF_VALIDATE_OR_GOTO (GF_NG, ngp, out);
+
+ if (!ngp->netgroup_name) {
+ gf_log (GF_NG, GF_LOG_WARNING,
+ "Invalid: Line starts with hostname!");
+ goto out;
+ }
+
+ /* Parse the host string and get a struct for it */
+ ret = _parse_ng_host (match, &ngh);
+ if (ret < 0) {
+ gf_log (GF_NG, GF_LOG_CRITICAL,
+ "Critical error : %s", strerror (-ret));
+ goto out;
+ }
+ if (ret != 0) {
+ gf_log (GF_NG, GF_LOG_WARNING,
+ "Parse error for: %s", match);
+ goto out;
+ }
+
+
+ /* Make dict for the parent entry's netgroup hosts */
+ if (!ngp->netgroup_hosts) {
+ ngp->netgroup_hosts = dict_new ();
+ GF_CHECK_ALLOC (ngp->netgroup_hosts, ret,
+ out);
+ }
+
+ /* Insert this entry into the parent netgroup dict */
+ _ngh_dict_insert (ngp->netgroup_hosts, ngh);
+
+out:
+ return ret;
+}
+
+/**
+ * _ng_handle_netgroup_part - Parse the netgroup string that should just be one
+ * string. This may insert the netgroup into the file
+ * struct if it does not already exist. Frees the
+ * parameter match if the netgroup was already found
+ * in the file.
+ *
+ * @match : The netgroup string
+ * @ngp : The netgroup file we may insert the entry into
+ * @ng_entry : Double pointer to the netgroup entry we want to allocate and set.
+ *
+ * @return: success: 0 if parsing succeeded
+ * failure: -EINVAL for bad args, other errors bubbled up
+ * from _parse_ng_host.
+ *
+ *
+ * Not for external use.
+ */
+static int
+_ng_setup_netgroup_entry (char *match, struct netgroups_file *file,
+ struct netgroup_entry **ng_entry)
+{
+ struct netgroup_entry *nge = NULL;
+ int ret = -EINVAL;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, match, out);
+ GF_VALIDATE_OR_GOTO (GF_NG, file, out);
+ GF_VALIDATE_OR_GOTO (GF_NG, ng_entry, out);
+
+ nge = _netgroup_entry_init ();
+ GF_CHECK_ALLOC (nge, ret, out);
+
+ nge->netgroup_name = match;
+
+ /* Insert this new entry into the file dict */
+ _nge_dict_insert (file->ng_file_dict, nge);
+
+ *ng_entry = nge;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * _parse_ng_line - Parse a line in the netgroups file into a netgroup entry
+ * struct. The netgroup line is structured as follows:
+ * 'netgroupx netgroupy (hosta,usera,domaina)...' OR
+ * 'netgroupx netgroupy netgroupz...' OR
+ * 'netgroupx (hosta,usera,domaina) (hostb,userb,domainb)'
+ * This function parses this into a netgroup entry
+ * which will hold either a dict of netgroups and/or
+ * a dict of hosts that make up this netgroup.
+ *
+ * In general terms, the data structure to represent a netgroups file
+ * is a set of nested dictionaries. Each line in the netgroups file
+ * is compiled into a struct netgroup_entry structure that holds a dict
+ * of netgroups and a dict of hostnames. The first string in the netgroups
+ * line is the parent netgroup entry and the rest of the items in the line
+ * are the children of that parent netgroup entry. (Hence variables ngp
+ * and nge).
+ *
+ * A sample netgroup file may look like this:
+ *
+ * async async.ash3 async.ash4
+ * async.ash3 async.04.ash3
+ * async04.ash3 (async001.ash3.facebook.com,,) (async002.ash3.facebook.com,,)
+ *
+ * _parse_ng_line will get called on each line, so on the first call to this
+ * function, our data structure looks like this:
+ *
+ *
+ * dict [
+ * 'async' --> dict [
+ * 'async.ash3'
+ * 'async.ash4'
+ * ]
+ * ]
+ *
+ * On the second call to the function with the second line, our data structure
+ * looks like this:
+ *
+ * dict [
+ * 'async' --> dict [
+ * 'async.ash3' -> dict [ 'async.04.ash3' ]
+ * 'async.ash4' ^
+ * ] |
+ * |
+ * 'async.ash3' ------------------------------
+ * ]
+ *
+ * And so on.
+ *
+ * The obvious answer to storing this file in a data structure may be a tree
+ * but lookups from a tree are expensive and since we may be looking up stuff
+ * in this file in the I/O path, we can't afford expensive lookups.
+ *
+ * @ng_str : String to parse
+ * @file : Netgroup file to put the parsed line into
+ * @ng_entry : Double pointer to struct that we are going to allocate and fill
+ *
+ * The string gets parsed into a structure pointed to by
+ * the parameter 'ng_entry'
+ *
+ * @return : success: 0 if parsing succeeded
+ * failure: NULL if not
+ *
+ * Not for external use.
+ */
+static int
+_parse_ng_line (char *ng_str, struct netgroups_file *file,
+ struct netgroup_entry **ng_entry)
+{
+ struct netgroup_entry *ngp = NULL; /* Parent netgroup entry */
+ struct netgroup_entry *nge = NULL; /* Generic netgroup entry */
+ char *match = NULL;
+ int ret = -EINVAL;
+ unsigned int num_entries = 0;
+
+ /* Validate arguments */
+ GF_VALIDATE_OR_GOTO (GF_NG, ng_str, out);
+ GF_VALIDATE_OR_GOTO (GF_NG, file, out);
+
+ if (*ng_str == ' ' || *ng_str == '\0' || *ng_str == '\n') {
+ ret = 0;
+ goto out;
+ }
+
+ ret = parser_set_string (ng_file_parser, ng_str);
+ if (ret < 0)
+ goto out;
+
+ /* This is the first name in the line, and should be the
+ * parent netgroup entry.
+ */
+ match = parser_get_next_match (ng_file_parser);
+ if (!match) {
+ ret = 1;
+ gf_log (GF_NG, GF_LOG_WARNING, "Unable to find first match.");
+ gf_log (GF_NG, GF_LOG_WARNING, "Error parsing str: %s", ng_str);
+ goto out;
+ }
+
+ /* Lookup to see if the match already exists,
+ * if not, set the parent.
+ */
+ ngp = _nge_dict_get (file->ng_file_dict, match);
+ if (!ngp) {
+ ret = _ng_setup_netgroup_entry (match, file, &ngp);
+ if (ret < 0) {
+ /* Bubble up error to caller. We don't need to free ngp
+ * here because this can only fail if allocating the
+ * struct fails.
+ */
+ goto out;
+ }
+ } else
+ GF_FREE (match);
+
+ if (!ngp->netgroup_ngs) {
+ /* If a netgroup dict has not been allocated
+ * for this parent, allocate it.
+ */
+ ngp->netgroup_ngs = dict_new ();
+ GF_CHECK_ALLOC (ngp->netgroup_ngs, ret, out);
+ /* No need to free anything here since ngp is already
+ * a part of the file. When the file gets
+ * deallocated, we will free ngp.
+ */
+ }
+
+ while ((match = parser_get_next_match (ng_file_parser)) != NULL) {
+ num_entries++;
+ /* This means that we hit a host entry in the line */
+ if (*match == '(') {
+ ret = _ng_handle_host_part (match, ngp);
+ GF_FREE (match);
+ if (ret != 0) {
+ /* If parsing the host fails, bubble the error
+ * code up to the caller.
+ */
+ goto out;
+ }
+ } else {
+ nge = _nge_dict_get (file->ng_file_dict, match);
+ if (!nge) {
+ ret = _ng_setup_netgroup_entry (match, file,
+ &nge);
+ if (ret < 0) {
+ /* Bubble up error to caller. We don't
+ * need to free nge here because this
+ * can only fail if allocating the
+ * struct fails.
+ */
+ goto out;
+ }
+ } else
+ GF_FREE (match);
+
+ /* Insert the netgroup into the parent's dict */
+ _nge_dict_insert (ngp->netgroup_ngs, nge);
+ }
+ }
+
+ /* If there are no entries on the RHS, log an error, but continue */
+ if (!num_entries) {
+ gf_log (GF_NG, GF_LOG_WARNING, "No netgroups were specified "
+ "except for the parent.");
+ }
+
+ *ng_entry = ngp;
+ ret = 0;
+
+out:
+ parser_unset_string (ng_file_parser);
+ return ret;
+}
+
+/**
+ * ng_file_parse - Parse a netgroups file into a the netgroups file struct.
+ * This is the external facing function that must be called
+ * to parse a netgroups file. This function returns a netgroup
+ * file struct that is allocated and must be freed using
+ * ng_file_deinit.
+ *
+ * @filepath : Path to the netgroups file we need to parse
+ *
+ * @return : success: Pointer to a netgroup file struct if parsing succeeded
+ * failure: NULL if not
+ *
+ * Externally facing function
+ */
+struct netgroups_file *
+ng_file_parse (const char *filepath)
+{
+ FILE *fp = NULL;
+ size_t len = 0;
+ size_t read = 0;
+ char *line = NULL;
+ struct netgroups_file *file = NULL;
+ struct netgroup_entry *nge = NULL;
+ int ret = 0;
+
+ GF_VALIDATE_OR_GOTO (GF_NG, filepath, err);
+
+ fp = fopen (filepath, "r");
+ if (!fp)
+ goto err;
+
+ file = _netgroups_file_init ();
+ if (!file)
+ goto err;
+
+ file->ng_file_dict = dict_new ();
+ if (!file->ng_file_dict) {
+ gf_log (GF_NG, GF_LOG_CRITICAL,
+ "Failed to allocate netgroup file dict");
+ goto err;
+ }
+
+ file->filename = gf_strdup (filepath);
+ if (!file->filename) {
+ gf_log (GF_NG, GF_LOG_CRITICAL,
+ "Failed to duplicate filename");
+ goto err;
+ }
+
+ ret = _ng_init_parsers ();
+ if (ret < 0)
+ goto err;
+
+ /* Read the file line-by-line and parse it */
+ while ((read = getline (&line, &len, fp)) != -1) {
+ if (*line == '#') /* Lines starting with # are comments */
+ continue;
+
+ /* Parse the line into a netgroup entry */
+ ret = _parse_ng_line (line, file, &nge);
+ if (ret == -ENOMEM) {
+ gf_log (GF_NG, GF_LOG_CRITICAL, "Allocation error "
+ "while parsing line!");
+ ng_file_deinit (file);
+ GF_FREE (line);
+ goto err;
+ }
+ if (ret != 0) {
+ gf_log (GF_NG, GF_LOG_DEBUG,
+ "Failed to parse line %s", line);
+ continue;
+ }
+ }
+
+ GF_FREE (line);
+
+ if (fp)
+ fclose(fp);
+
+ return file;
+
+err:
+ if (file)
+ ng_file_deinit (file);
+
+ _ng_deinit_parsers ();
+
+ if (fp)
+ fclose (fp);
+ return NULL;
+}
diff --git a/xlators/nfs/server/src/netgroups.h b/xlators/nfs/server/src/netgroups.h
new file mode 100644
index 00000000000..c77a35a41f3
--- /dev/null
+++ b/xlators/nfs/server/src/netgroups.h
@@ -0,0 +1,54 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#ifndef _NETGROUPS_H
+#define _NETGROUPS_H
+
+#include "nfs-mem-types.h"
+#include "dict.h"
+#include "nfs.h"
+
+#define GF_NG GF_NFS"-netgroup"
+
+#define NG_FILE_PARSE_REGEX "([a-zA-Z0-9.(,)]+)"
+#define NG_HOST_PARSE_REGEX "([a-zA-Z0-9.]+)"
+
+struct netgroup_host {
+ char *hostname; /* Hostname of entry */
+ char *user; /* User field in the entry */
+ char *domain; /* Domain field in the entry */
+};
+
+struct netgroup_entry {
+ char *netgroup_name; /* Name of the netgroup */
+ dict_t *netgroup_ngs; /* Dict of netgroups in this netgroup */
+ dict_t *netgroup_hosts; /* Dict of hosts in this netgroup. */
+};
+
+struct netgroups_file {
+ char *filename; /* Filename on disk */
+ dict_t *ng_file_dict; /* Dict of netgroup entries */
+};
+
+struct netgroups_file *
+ng_file_parse (const char *filepath);
+
+struct netgroup_entry *
+ng_file_get_netgroup (const struct netgroups_file *ngfile,
+ const char *netgroup);
+
+void
+ng_file_deinit (struct netgroups_file *ngfile);
+
+#endif /* _NETGROUPS_H */
diff --git a/xlators/nfs/server/src/nfs-mem-types.h b/xlators/nfs/server/src/nfs-mem-types.h
index 450b6f2feab..d9e2c9904c9 100644
--- a/xlators/nfs/server/src/nfs-mem-types.h
+++ b/xlators/nfs/server/src/nfs-mem-types.h
@@ -46,6 +46,7 @@ enum gf_nfs_mem_types_ {
gf_nfs_mt_aux_gids,
gf_nfs_mt_inode_ctx,
gf_nfs_mt_auth_spec,
+ gf_nfs_mt_netgroups,
gf_nfs_mt_arr,
gf_nfs_mt_end
};