/* Copyright 2014-present Facebook. All Rights Reserved This file is part of GlusterFS. Author : Shreyas Siravara 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" #include "nfs-messages.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_common_mt_nfs_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) { if (!ngfile) { return; } __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_common_mt_nfs_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_common_mt_nfs_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 ' ' * * @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 ' ', 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; gf_msg_trace (GF_NG, 0, "parsing host string: %s", ng_str); 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) { gf_msg_trace (GF_NG, 0, "found match: %s (parts=%d)", match, parts); 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_msg (GF_NG, GF_LOG_WARNING, EINVAL, NFS_MSG_INVALID_ENTRY, "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_msg (GF_NG, GF_LOG_CRITICAL, -ret, NFS_MSG_PARSE_FAIL, "Critical error : %s", strerror (-ret)); goto out; } if (ret != 0) { /* Cannot change to gf_msg * gf_msg not giving output to STDOUT * Bug id : BZ1215017 */ 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_msg (GF_NG, GF_LOG_WARNING, 0, NFS_MSG_FIND_FIRST_MATCH_FAIL, "Unable to find " "first match."); gf_msg (GF_NG, GF_LOG_WARNING, 0, NFS_MSG_PARSE_FAIL, "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) { /* Cannot change to gf_msg * gf_msg not giving output to STDOUT * Bug id : BZ1215017 */ 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_msg (GF_NG, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY, "Failed to allocate netgroup file dict"); goto err; } file->filename = gf_strdup (filepath); if (!file->filename) { gf_msg (GF_NG, GF_LOG_CRITICAL, errno, NFS_MSG_FILE_OP_FAILED, "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_msg (GF_NG, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY, "Allocation error " "while parsing line!"); goto err; } if (ret != 0) { gf_msg_debug (GF_NG, 0, "Failed to parse line %s", line); continue; } } /* line got allocated through getline(), don't use GF_FREE() for it */ free (line); if (fp) fclose(fp); _ng_deinit_parsers (); return file; err: if (line) free(line); if (file) ng_file_deinit (file); _ng_deinit_parsers (); if (fp) fclose (fp); return NULL; }