diff options
author | Niels de Vos <ndevos@redhat.com> | 2015-01-01 13:15:45 +0100 |
---|---|---|
committer | Vijay Bellur <vbellur@redhat.com> | 2015-03-11 09:27:50 -0700 |
commit | 02d42a5e80f3e2624eba2d00acff0aaebb20b12f (patch) | |
tree | 8b70ae4f2e245b6004b6b9dc8caa4c2a7afd9bc8 /xlators/nfs/server/src | |
parent | 381abb5bd2b09a4c40b20ddbe6d385f9a849e384 (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.am | 4 | ||||
-rw-r--r-- | xlators/nfs/server/src/netgroups.c | 1141 | ||||
-rw-r--r-- | xlators/nfs/server/src/netgroups.h | 54 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs-mem-types.h | 1 |
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 }; |