1    	/*
2    	 * Copyright CEA/DAM/DIF  (2008)
3    	 * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
4    	 *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
5    	 *
6    	 *
7    	 * This program is free software; you can redistribute it and/or
8    	 * modify it under the terms of the GNU Lesser General Public License
9    	 * as published by the Free Software Foundation; either version 3 of
10   	 * the License, or (at your option) any later version.
11   	 *
12   	 * This program is distributed in the hope that it will be useful, but
13   	 * WITHOUT ANY WARRANTY; without even the implied warranty of
14   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   	 * Lesser General Public License for more details.
16   	 *
17   	 * You should have received a copy of the GNU Lesser General Public
18   	 * License along with this library; if not, write to the Free Software
19   	 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20   	 * 02110-1301 USA
21   	 *
22   	 * ---------------------------------------
23   	 */
24   	
25   	/**
26   	 * @file  exports.c
27   	 * @brief Export parsing and management
28   	 */
29   	#include "config.h"
30   	#include "cidr.h"
31   	#include "log.h"
32   	#include "fsal.h"
33   	#include "nfs_core.h"
34   	#include "nfs_file_handle.h"
35   	#include "nfs_exports.h"
36   	#include "nfs_ip_stats.h"
37   	#include "nfs_proto_functions.h"
38   	#include "nfs_dupreq.h"
39   	#include "config_parsing.h"
40   	#include "common_utils.h"
41   	#include <stdlib.h>
42   	#include <fnmatch.h>
43   	#include <sys/socket.h>
44   	#include <netinet/in.h>
45   	#include <arpa/inet.h>
46   	#include <string.h>
47   	#include <strings.h>
48   	#include <ctype.h>
49   	#include "export_mgr.h"
50   	#include "fsal_up.h"
51   	#include "sal_functions.h"
52   	#include "pnfs_utils.h"
53   	#include "netgroup_cache.h"
54   	#include "mdcache.h"
55   	
56   	/**
57   	 * @brief Protect EXPORT_DEFAULTS structure for dynamic update.
58   	 *
59   	 * If an export->lock is also held by the code, this lock MUST be
60   	 * taken AFTER the export->lock to avoid ABBA deadlock.
61   	 *
62   	 */
63   	pthread_rwlock_t export_opt_lock = PTHREAD_RWLOCK_INITIALIZER;
64   	
65   	#define GLOBAL_EXPORT_PERMS_INITIALIZER				\
66   		.def.anonymous_uid = ANON_UID,				\
67   		.def.anonymous_gid = ANON_GID,				\
68   		.def.expire_time_attr = 60,				\
69   		/* Note: Access_Type defaults to None on purpose */	\
70   		.def.options = EXPORT_OPTION_ROOT_SQUASH |		\
71   			       EXPORT_OPTION_NO_ACCESS |		\
72   			       EXPORT_OPTION_AUTH_DEFAULTS |		\
73   			       EXPORT_OPTION_PROTO_DEFAULTS |		\
74   			       EXPORT_OPTION_XPORT_DEFAULTS |		\
75   			       EXPORT_OPTION_NO_DELEGATIONS,		\
76   		.def.set = UINT32_MAX
77   	
78   	struct global_export_perms export_opt = {
79   		GLOBAL_EXPORT_PERMS_INITIALIZER
80   	};
81   	
82   	/* A second copy used in configuration, so we can atomically update the
83   	 * primary set.
84   	 */
85   	struct global_export_perms export_opt_cfg = {
86   		GLOBAL_EXPORT_PERMS_INITIALIZER
87   	};
88   	
89   	static void FreeClientList(struct glist_head *clients);
90   	
91   	static int StrExportOptions(struct display_buffer *dspbuf,
92   				    struct export_perms *p_perms)
93   	{
94   		int b_left = display_start(dspbuf);
95   	
96   		if (b_left <= 0)
97   			return b_left;
98   	
99   		b_left = display_printf(dspbuf, "options=%08"PRIx32"/%08"PRIx32" ",
100  					p_perms->options, p_perms->set);
101  	
102  		if (b_left <= 0)
103  			return b_left;
104  	
105  		if ((p_perms->set & EXPORT_OPTION_SQUASH_TYPES) != 0) {
106  			if ((p_perms->options & EXPORT_OPTION_ROOT_SQUASH) != 0)
107  				b_left = display_cat(dspbuf, "root_squash   ");
108  	
109  			if (b_left <= 0)
110  				return b_left;
111  	
112  			if ((p_perms->options & EXPORT_OPTION_ROOT_ID_SQUASH) != 0)
113  				b_left = display_cat(dspbuf, "root_id_squash");
114  	
115  			if (b_left <= 0)
116  				return b_left;
117  	
118  			if ((p_perms->options & EXPORT_OPTION_ALL_ANONYMOUS)  != 0)
119  				b_left = display_cat(dspbuf, "all_squash    ");
120  	
121  			if (b_left <= 0)
122  				return b_left;
123  	
124  			if ((p_perms->options & EXPORT_OPTION_SQUASH_TYPES) == 0)
125  				b_left = display_cat(dspbuf, "no_root_squash");
126  		} else
127  			b_left = display_cat(dspbuf, "              ");
128  	
129  		if (b_left <= 0)
130  			return b_left;
131  	
132  		if ((p_perms->set & EXPORT_OPTION_ACCESS_MASK) != 0) {
133  			if ((p_perms->options & EXPORT_OPTION_READ_ACCESS) != 0)
134  				b_left = display_cat(dspbuf, ", R");
135  			else
136  				b_left = display_cat(dspbuf, ", -");
137  	
138  			if (b_left <= 0)
139  				return b_left;
140  	
141  			if ((p_perms->options & EXPORT_OPTION_WRITE_ACCESS) != 0)
142  				b_left = display_cat(dspbuf, "W");
143  			else
144  				b_left = display_cat(dspbuf, "-");
145  	
146  			if (b_left <= 0)
147  				return b_left;
148  	
149  			if ((p_perms->options & EXPORT_OPTION_MD_READ_ACCESS) != 0)
150  				b_left = display_cat(dspbuf, "r");
151  			else
152  				b_left = display_cat(dspbuf, "-");
153  	
154  			if (b_left <= 0)
155  				return b_left;
156  	
157  			if ((p_perms->options & EXPORT_OPTION_MD_WRITE_ACCESS) != 0)
158  				b_left = display_cat(dspbuf, "w");
159  			else
160  				b_left = display_cat(dspbuf, "-");
161  		} else
162  			b_left = display_cat(dspbuf, ",     ");
163  	
164  		if (b_left <= 0)
165  			return b_left;
166  	
167  		if ((p_perms->set & EXPORT_OPTION_PROTOCOLS) != 0) {
168  			if ((p_perms->options & EXPORT_OPTION_NFSV3) != 0)
169  				b_left = display_cat(dspbuf, ", 3");
170  			else
171  				b_left = display_cat(dspbuf, ", -");
172  	
173  			if (b_left <= 0)
174  				return b_left;
175  	
176  			if ((p_perms->options & EXPORT_OPTION_NFSV4) != 0)
177  				b_left = display_cat(dspbuf, "4");
178  			else
179  				b_left = display_cat(dspbuf, "-");
180  	
181  			if (b_left <= 0)
182  				return b_left;
183  	
184  			if ((p_perms->options & EXPORT_OPTION_9P) != 0)
185  				b_left = display_cat(dspbuf, "9");
186  			else
187  				b_left = display_cat(dspbuf, "-");
188  		} else
189  			b_left = display_cat(dspbuf, ",    ");
190  	
191  		if (b_left <= 0)
192  			return b_left;
193  	
194  		if ((p_perms->set & EXPORT_OPTION_TRANSPORTS) != 0) {
195  			if ((p_perms->options & EXPORT_OPTION_UDP) != 0)
196  				b_left = display_cat(dspbuf, ", UDP");
197  			else
198  				b_left = display_cat(dspbuf, ", ---");
199  	
200  			if (b_left <= 0)
201  				return b_left;
202  	
203  			if ((p_perms->options & EXPORT_OPTION_TCP) != 0)
204  				b_left = display_cat(dspbuf, ", TCP");
205  			else
206  				b_left = display_cat(dspbuf, ", ---");
207  	
208  			if (b_left <= 0)
209  				return b_left;
210  	
211  			if ((p_perms->options & EXPORT_OPTION_RDMA) != 0)
212  				b_left = display_cat(dspbuf, ", RDMA");
213  			else
214  				b_left = display_cat(dspbuf, ", ----");
215  		} else
216  			b_left = display_cat(dspbuf, ",               ");
217  	
218  		if (b_left <= 0)
219  			return b_left;
220  	
221  		if ((p_perms->set & EXPORT_OPTION_MANAGE_GIDS) == 0)
222  			b_left = display_cat(dspbuf, ",               ");
223  		else if ((p_perms->options & EXPORT_OPTION_MANAGE_GIDS) != 0)
224  			b_left = display_cat(dspbuf, ", Manage_Gids   ");
225  		else
226  			b_left = display_cat(dspbuf, ", No Manage_Gids");
227  	
228  		if (b_left <= 0)
229  			return b_left;
230  	
231  		if ((p_perms->set & EXPORT_OPTION_DELEGATIONS) != 0) {
232  			if ((p_perms->options & EXPORT_OPTION_READ_DELEG) != 0)
233  				b_left = display_cat(dspbuf, ", R");
234  			else
235  				b_left = display_cat(dspbuf, ", -");
236  	
237  			if (b_left <= 0)
238  				return b_left;
239  	
240  			if ((p_perms->options & EXPORT_OPTION_WRITE_DELEG) != 0)
241  				b_left = display_cat(dspbuf, "W Deleg");
242  			else
243  				b_left = display_cat(dspbuf, "- Deleg");
244  		} else
245  			b_left = display_cat(dspbuf, ",         ");
246  	
247  		if (b_left <= 0)
248  			return b_left;
249  	
250  		if ((p_perms->set & EXPORT_OPTION_ANON_UID_SET) != 0)
251  			b_left = display_printf(dspbuf, ", anon_uid=%6d",
252  						(int)p_perms->anonymous_uid);
253  		else
254  			b_left = display_cat(dspbuf, ",                ");
255  	
256  		if (b_left <= 0)
257  			return b_left;
258  	
259  		if ((p_perms->set & EXPORT_OPTION_ANON_GID_SET) != 0)
260  			b_left = display_printf(dspbuf, ", anon_gid=%6d",
261  						(int)p_perms->anonymous_gid);
262  		else
263  			b_left = display_cat(dspbuf, ",                ");
264  	
265  		if (b_left <= 0)
266  			return b_left;
267  	
268  		if ((p_perms->set & EXPORT_OPTION_EXPIRE_SET) != 0)
269  			b_left = display_printf(dspbuf, ", expire=%8"PRIi32,
270  						(int)p_perms->expire_time_attr);
271  		else
272  			b_left = display_cat(dspbuf, ",                ");
273  	
274  		if (b_left <= 0)
275  			return b_left;
276  	
277  		if ((p_perms->set & EXPORT_OPTION_AUTH_TYPES) != 0) {
278  			if ((p_perms->options & EXPORT_OPTION_AUTH_NONE) != 0)
279  				b_left = display_cat(dspbuf, ", none");
280  	
281  			if (b_left <= 0)
282  				return b_left;
283  	
284  			if ((p_perms->options & EXPORT_OPTION_AUTH_UNIX) != 0)
285  				b_left = display_cat(dspbuf, ", sys");
286  	
287  			if (b_left <= 0)
288  				return b_left;
289  	
290  			if ((p_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) != 0)
291  				b_left = display_cat(dspbuf, ", krb5");
292  	
293  			if (b_left <= 0)
294  				return b_left;
295  	
296  			if ((p_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) != 0)
297  				b_left = display_cat(dspbuf, ", krb5i");
298  	
299  			if (b_left <= 0)
300  				return b_left;
301  	
302  			if ((p_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) != 0)
303  				b_left = display_cat(dspbuf, ", krb5p");
304  		}
305  	
306  		return b_left;
307  	}
308  	
309  	static char *client_types[] = {
310  		[PROTO_CLIENT] = "PROTO_CLIENT",
311  		[NETWORK_CLIENT] = "NETWORK_CLIENT",
312  		[NETGROUP_CLIENT] = "NETGROUP_CLIENT",
313  		[WILDCARDHOST_CLIENT] = "WILDCARDHOST_CLIENT",
314  		[GSSPRINCIPAL_CLIENT] = "GSSPRINCIPAL_CLIENT",
315  		[MATCH_ANY_CLIENT] = "MATCH_ANY_CLIENT",
316  		[BAD_CLIENT] = "BAD_CLIENT"
317  		 };
318  	
319  	void LogClientListEntry(log_levels_t level,
320  				log_components_t component,
321  				int line,
322  				char *func,
323  				char *tag,
324  				exportlist_client_entry_t *entry)
325  	{
326  		char perms[1024] = "\0";
327  		struct display_buffer dspbuf = {sizeof(perms), perms, perms};
328  		char addr[INET6_ADDRSTRLEN];
329  		char *paddr = addr;
330  		char *client_type;
331  		bool free_paddr = false;
332  	
333  		if (!isLevel(component, level))
334  			return;
335  	
336  		if (entry->type > BAD_CLIENT) {
337  			sprintf(paddr, "0x%08x", entry->type);
338  			client_type = "UNKNOWN_CLIENT_TYPE";
339  		} else {
340  			client_type = client_types[entry->type];
341  		}
342  	
343  		(void) StrExportOptions(&dspbuf, &entry->client_perms);
344  	
345  		switch (entry->type) {
346  		case NETWORK_CLIENT:
347  			paddr = cidr_to_str(entry->client.network.cidr, CIDR_NOFLAGS);
348  			free_paddr = true;
349  			break;
350  	
351  		case NETGROUP_CLIENT:
352  			paddr = entry->client.netgroup.netgroupname;
353  			break;
354  	
355  		case WILDCARDHOST_CLIENT:
356  			paddr = entry->client.wildcard.wildcard;
357  			break;
358  	
359  		case GSSPRINCIPAL_CLIENT:
360  			paddr = entry->client.gssprinc.princname;
361  			break;
362  	
363  		case MATCH_ANY_CLIENT:
364  			paddr = "*";
365  			break;
366  	
367  		case PROTO_CLIENT:
368  		case BAD_CLIENT:
369  			paddr = "<unknown>";
370  			break;
371  		}
372  	
373  		DisplayLogComponentLevel(component, (char *) __FILE__, line, func,
374  					 level, "%s%p %s: %s (%s)",
375  					 tag, entry, client_type, paddr, perms);
376  	
377  		if (free_paddr)
378  			gsh_free(paddr);
379  	}
380  	
381  	static void display_clients(struct gsh_export *export)
382  	{
383  		struct glist_head *glist;
384  	
385  		PTHREAD_RWLOCK_rdlock(&export->lock);
386  	
387  		glist_for_each(glist, &export->clients) {
388  			exportlist_client_entry_t *client;
389  	
390  			client = glist_entry(glist, exportlist_client_entry_t,
391  					     cle_list);
392  			LogClientListEntry(NIV_MID_DEBUG,
393  					   COMPONENT_EXPORT,
394  					   __LINE__,
395  					   (char *) __func__,
396  					   "",
397  					   client);
398  		}
399  	
400  		PTHREAD_RWLOCK_unlock(&export->lock);
401  	}
402  	
403  	/**
404  	 * @brief Expand the client name token into one or more client entries
405  	 *
406  	 * @param client_list[IN] the client list this gets linked to (in tail order)
407  	 * @param client_tok [IN] the name string.  We modify it.
408  	 * @param type_hint  [IN] type hint from parser for client_tok
409  	 * @param perms      [IN] pointer to the permissions to copy into each
410  	 * @param cnode      [IN] opaque pointer needed for config_proc_error()
411  	 * @param err_type   [OUT] error handling ref
412  	 *
413  	 * @returns 0 on success, error count on failure
414  	 */
415  	
416  	static int add_client(struct glist_head *client_list,
417  			      const char *client_tok,
418  			      enum term_type type_hint,
419  			      struct export_perms *perms,
420  			      void *cnode,
421  			      struct config_error_type *err_type)
422  	{
423  		struct exportlist_client_entry__ *cli;
424  		int errcnt = 0;
425  		struct addrinfo *info;
426  		CIDR *cidr;
427  		int rc;
428  	
429  		cli = gsh_calloc(1, sizeof(struct exportlist_client_entry__));
430  	
431  		cli->client.network.cidr = NULL;
432  		glist_init(&cli->cle_list);
433  		switch (type_hint) {
434  		case TERM_V4_ANY:
435  			cli->type = MATCH_ANY_CLIENT;
436  			break;
437  		case TERM_NETGROUP:
438  			if (strlen(client_tok) > MAXHOSTNAMELEN) {
439  				config_proc_error(cnode, err_type,
440  						  "netgroup (%s) name too long",
441  						  client_tok);
442  				err_type->invalid = true;
443  				errcnt++;
444  				goto out;
445  			}
446  			cli->client.netgroup.netgroupname = gsh_strdup(client_tok + 1);
447  			cli->type = NETGROUP_CLIENT;
448  			break;
(1) Event equality_cond: Jumping to case "TERM_V4CIDR".
Also see events: [equality_cond][equality_cond][equality_cond][between][dead_error_condition][dead_error_begin]
449  		case TERM_V4CIDR:
(2) Event equality_cond: Jumping to case "TERM_V6CIDR".
Also see events: [equality_cond][equality_cond][equality_cond][between][dead_error_condition][dead_error_begin]
450  		case TERM_V6CIDR:
(3) Event equality_cond: Jumping to case "TERM_V4ADDR".
Also see events: [equality_cond][equality_cond][equality_cond][between][dead_error_condition][dead_error_begin]
451  		case TERM_V4ADDR:
(4) Event equality_cond: Jumping to case "TERM_V6ADDR".
Also see events: [equality_cond][equality_cond][equality_cond][between][dead_error_condition][dead_error_begin]
452  		case TERM_V6ADDR:
453  			cidr = cidr_from_str(client_tok);
454  			if (cidr == NULL) {
(5) Event between: When switching on "type_hint", the value of "type_hint" must be between 13 and 16.
(6) Event dead_error_condition: The "switch" governing value "type_hint" cannot reach the "default" case.
Also see events: [equality_cond][equality_cond][equality_cond][equality_cond][dead_error_begin]
455  				switch (type_hint) {
456  				case TERM_V4CIDR:
457  					config_proc_error(cnode, err_type,
458  							  "Expected a IPv4 CIDR address, got (%s)",
459  							  client_tok);
460  					break;
461  				case TERM_V6CIDR:
462  					config_proc_error(cnode, err_type,
463  							  "Expected a IPv6 CIDR address, got (%s)",
464  							  client_tok);
465  					break;
466  				case TERM_V4ADDR:
467  					config_proc_error(cnode, err_type,
468  							  "IPv4 addr (%s) not in presentation format",
469  							  client_tok);
470  					break;
471  				case TERM_V6ADDR:
472  					config_proc_error(cnode, err_type,
473  							  "IPv6 addr (%s) not in presentation format",
474  							  client_tok);
475  					break;
(7) Event dead_error_begin: Execution cannot reach this statement: "default:".
Also see events: [equality_cond][equality_cond][equality_cond][equality_cond][between][dead_error_condition]
476  				default:
477  					break;
478  				}
479  				err_type->invalid = true;
480  				errcnt++;
481  				goto out;
482  			}
483  			cli->client.network.cidr = cidr;
484  			cli->type = NETWORK_CLIENT;
485  			break;
486  		case TERM_REGEX:
487  			if (strlen(client_tok) > MAXHOSTNAMELEN) {
488  				config_proc_error(cnode, err_type,
489  						  "Wildcard client (%s) name too long",
490  						  client_tok);
491  				err_type->invalid = true;
492  				errcnt++;
493  				goto out;
494  			}
495  			cli->client.wildcard.wildcard = gsh_strdup(client_tok);
496  			cli->type = WILDCARDHOST_CLIENT;
497  			break;
498  		case TERM_TOKEN: /* only dns names now. */
499  			rc = getaddrinfo(client_tok, NULL, NULL, &info);
500  			if (rc == 0) {
501  				struct addrinfo *ap, *ap_last = NULL;
502  				struct in_addr in_addr_last;
503  				struct in6_addr in6_addr_last;
504  	
505  				for (ap = info; ap != NULL; ap = ap->ai_next) {
506  					LogFullDebug(COMPONENT_EXPORT,
507  						     "flags=%d family=%d socktype=%d protocol=%d addrlen=%d name=%s",
508  						     ap->ai_flags,
509  						     ap->ai_family,
510  						     ap->ai_socktype,
511  						     ap->ai_protocol,
512  						     (int) ap->ai_addrlen,
513  						     ap->ai_canonname);
514  					if (cli == NULL) {
515  						cli = gsh_calloc(1,
516  							sizeof(struct
517  							    exportlist_client_entry__));
518  						glist_init(&cli->cle_list);
519  					}
520  					if (ap->ai_family == AF_INET &&
521  					    (ap->ai_socktype == SOCK_STREAM ||
522  					     ap->ai_socktype == SOCK_DGRAM)) {
523  						struct in_addr infoaddr =
524  							((struct sockaddr_in *)
525  							 ap->ai_addr)->sin_addr;
526  						if (ap_last != NULL &&
527  						    ap_last->ai_family
528  						    == ap->ai_family &&
529  						    memcmp(&infoaddr,
530  							   &in_addr_last,
531  							   sizeof(struct in_addr)) == 0)
532  							continue;
533  						cli->client.network.cidr =
534  							cidr_from_inaddr(&infoaddr);
535  						cli->type = NETWORK_CLIENT;
536  						ap_last = ap;
537  						in_addr_last = infoaddr;
538  	
539  					} else if (ap->ai_family == AF_INET6 &&
540  						   (ap->ai_socktype == SOCK_STREAM ||
541  						    ap->ai_socktype == SOCK_DGRAM)) {
542  						struct in6_addr infoaddr =
543  							((struct sockaddr_in6 *)
544  							 ap->ai_addr)->sin6_addr;
545  	
546  						if (ap_last != NULL &&
547  						    ap_last->ai_family == ap->ai_family
548  						    &&  !memcmp(&infoaddr,
549  							       &in6_addr_last,
550  							       sizeof(struct in6_addr)))
551  							continue;
552  						/* IPv6 address */
553  						cli->client.network.cidr =
554  							cidr_from_in6addr(&infoaddr);
555  						cli->type = NETWORK_CLIENT;
556  						ap_last = ap;
557  						in6_addr_last = infoaddr;
558  					} else
559  						continue;
560  					cli->client_perms = *perms;
561  					LogClientListEntry(NIV_MID_DEBUG,
562  							   COMPONENT_EXPORT,
563  							   __LINE__,
564  							   (char *) __func__,
565  							   "",
566  							   cli);
567  					glist_add_tail(client_list, &cli->cle_list);
568  					cli = NULL; /* let go of it */
569  				}
570  				freeaddrinfo(info);
571  				goto out;
572  			} else {
573  				config_proc_error(cnode, err_type,
574  						  "Client (%s) not found because %s",
575  						  client_tok, gai_strerror(rc));
576  				err_type->bogus = true;
577  				errcnt++;
578  			}
579  			break;
580  		default:
581  			config_proc_error(cnode, err_type,
582  					  "Expected a client, got a %s for (%s)",
583  					  config_term_desc(type_hint),
584  					  client_tok);
585  			err_type->bogus = true;
586  			errcnt++;
587  			goto out;
588  		}
589  		cli->client_perms = *perms;
590  		LogClientListEntry(NIV_MID_DEBUG,
591  				   COMPONENT_EXPORT,
592  				   __LINE__,
593  				   (char *) __func__,
594  				   "",
595  				   cli);
596  		glist_add_tail(client_list, &cli->cle_list);
597  		cli = NULL;
598  	out:
599  		if (cli != NULL)
600  			gsh_free(cli);
601  		return errcnt;
602  	}
603  	
604  	/**
605  	 * @brief Commit and FSAL sub-block init/commit helpers
606  	 */
607  	
608  	/**
609  	 * @brief Init for CLIENT sub-block of an export.
610  	 *
611  	 * Allocate one exportlist_client structure for parameter
612  	 * processing. The client_commit will allocate additional
613  	 * exportlist_client__ storage for each of its enumerated
614  	 * clients and free the initial block.  We only free that
615  	 * resource here on errors.
616  	 */
617  	
618  	static void *client_init(void *link_mem, void *self_struct)
619  	{
620  		struct exportlist_client_entry__ *cli;
621  	
622  		assert(link_mem != NULL || self_struct != NULL);
623  	
624  		if (link_mem == NULL) {
625  			return self_struct;
626  		} else if (self_struct == NULL) {
627  			cli = gsh_calloc(1, sizeof(struct exportlist_client_entry__));
628  	
629  			glist_init(&cli->cle_list);
630  			cli->type = PROTO_CLIENT;
631  			return cli;
632  		} else { /* free resources case */
633  			cli = self_struct;
634  	
635  			if (!glist_empty(&cli->cle_list))
636  				FreeClientList(&cli->cle_list);
637  			assert(glist_empty(&cli->cle_list));
638  			gsh_free(cli);
639  			return NULL;
640  		}
641  	}
642  	
643  	/**
644  	 * @brief Commit this client block
645  	 *
646  	 * Validate "clients" token(s) and perms.  We enter with a client entry
647  	 * allocated by proc_block.  Since we expand the clients token both
648  	 * here and in add_client, we allocate new client entries and free
649  	 * what was passed to us rather than try and link it in.
650  	 *
651  	 * @param node [IN] the config_node **not used**
652  	 * @param link_mem [IN] the exportlist entry. add_client adds to its glist.
653  	 * @param self_struct  [IN] the filled out client entry with a PROTO_CLIENT
654  	 *
655  	 * @return 0 on success, error count for failure.
656  	 */
657  	
658  	static int client_commit(void *node, void *link_mem, void *self_struct,
659  				 struct config_error_type *err_type)
660  	{
661  		struct exportlist_client_entry__ *cli;
662  		struct gsh_export *export;
663  		int errcnt = 0;
664  	
665  		export = container_of(link_mem, struct gsh_export, clients);
666  		cli = self_struct;
667  		assert(cli->type == PROTO_CLIENT);
668  		if (glist_empty(&cli->cle_list)) {
669  			LogCrit(COMPONENT_CONFIG,
670  				"No clients specified");
671  			err_type->invalid = true;
672  			errcnt++;
673  		} else {
674  			glist_splice_tail(&export->clients, &cli->cle_list);
675  		}
676  		if (errcnt == 0)
677  			client_init(link_mem, self_struct);
678  		return errcnt;
679  	}
680  	
681  	/**
682  	 * @brief Clean up EXPORT path strings
683  	 */
684  	void clean_export_paths(struct gsh_export *export)
685  	{
686  		LogFullDebug(COMPONENT_EXPORT,
687  			     "Cleaning paths for %d",
688  			     export->export_id);
689  	
690  		/* Some admins stuff a '/' at  the end for some reason.
691  		 * chomp it so we have a /dir/path/basename to work
692  		 * with. But only if it's a non-root path starting
693  		 * with /.
694  		 */
695  		if (export->fullpath && export->fullpath[0] == '/') {
696  			int pathlen;
697  	
698  			pathlen = strlen(export->fullpath);
699  			while ((export->fullpath[pathlen - 1] == '/') &&
700  			       (pathlen > 1))
701  				pathlen--;
702  			export->fullpath[pathlen] = '\0';
703  		}
704  	
705  		/* Remove trailing slash */
706  		if (export->pseudopath && export->pseudopath[0] == '/') {
707  			int pathlen;
708  	
709  			pathlen = strlen(export->pseudopath);
710  			while ((export->pseudopath[pathlen - 1] == '/') &&
711  			       (pathlen > 1))
712  				pathlen--;
713  			export->pseudopath[pathlen] = '\0';
714  		}
715  	}
716  	
717  	/**
718  	 * @brief Commit a FSAL sub-block
719  	 *
720  	 * Use the Name parameter passed in via the link_mem to lookup the
721  	 * fsal.  If the fsal is not loaded (yet), load it and call its init.
722  	 *
723  	 * Create an export and pass the FSAL sub-block to it so that the
724  	 * fsal method can process the rest of the parameters in the block
725  	 */
726  	
727  	static int fsal_cfg_commit(void *node, void *link_mem, void *self_struct,
728  				   struct config_error_type *err_type)
729  	{
730  		struct fsal_export **exp_hdl = link_mem;
731  		struct gsh_export *export =
732  		    container_of(exp_hdl, struct gsh_export, fsal_export);
733  		struct fsal_args *fp = self_struct;
734  		struct fsal_module *fsal;
735  		struct root_op_context root_op_context;
736  		uint64_t MaxRead, MaxWrite;
737  		fsal_status_t status;
738  		int errcnt;
739  	
740  		/* Initialize req_ctx */
741  		init_root_op_context(&root_op_context, export, NULL, 0, 0,
742  				     UNKNOWN_REQUEST);
743  	
744  		errcnt = fsal_load_init(node, fp->name, &fsal, err_type);
745  		if (errcnt > 0)
746  			goto err;
747  	
748  		clean_export_paths(export);
749  	
750  		/* The handle cache (currently MDCACHE) must be at the top of the stack
751  		 * of FSALs.  To achieve this, call directly into MDCACHE, passing the
752  		 * sub-FSAL's fsal_module.  MDCACHE will stack itself on top of that
753  		 * FSAL, continuing down the chain. */
754  		status = mdcache_fsal_create_export(fsal, node, err_type, &fsal_up_top);
755  	
756  		if (FSAL_IS_ERROR(status)) {
757  			fsal_put(fsal);
758  			LogCrit(COMPONENT_CONFIG,
759  				"Could not create export for (%s) to (%s)",
760  				export->pseudopath,
761  				export->fullpath);
762  			LogFullDebug(COMPONENT_FSAL,
763  				     "FSAL %s refcount %"PRIu32,
764  				     fsal->name,
765  				     atomic_fetch_int32_t(&fsal->refcount));
766  			err_type->export_ = true;
767  			errcnt++;
768  			goto err;
769  		}
770  	
771  		assert(root_op_context.req_ctx.fsal_export != NULL);
772  		export->fsal_export = root_op_context.req_ctx.fsal_export;
773  	
774  		/* We are connected up to the fsal side.  Now
775  		 * validate maxread/write etc with fsal params
776  		 */
777  		MaxRead =
778  		    export->fsal_export->exp_ops.fs_maxread(export->fsal_export);
779  		MaxWrite =
780  		    export->fsal_export->exp_ops.fs_maxwrite(export->fsal_export);
781  	
782  		if (export->MaxRead > MaxRead && MaxRead != 0) {
783  			LogInfo(COMPONENT_CONFIG,
784  				 "Readjusting MaxRead to FSAL, %" PRIu64 " -> %" PRIu64,
785  				 export->MaxRead,
786  				 MaxRead);
787  			export->MaxRead = MaxRead;
788  		}
789  		if (export->MaxWrite > MaxWrite && MaxWrite != 0) {
790  			LogInfo(COMPONENT_CONFIG,
791  				 "Readjusting MaxWrite to FSAL, %"PRIu64" -> %"PRIu64,
792  				 export->MaxWrite,
793  				 MaxWrite);
794  			export->MaxWrite = MaxWrite;
795  		}
796  	
797  	err:
798  		release_root_op_context();
799  		/* Don't leak the FSAL block */
800  		err_type->dispose = true;
801  		return errcnt;
802  	}
803  	
804  	/**
805  	 * @brief Commit a FSAL sub-block for export update
806  	 *
807  	 * Use the Name parameter passed in via the link_mem to lookup the
808  	 * fsal.  If the fsal is not loaded (yet), load it and call its init.
809  	 *
810  	 * Create an export and pass the FSAL sub-block to it so that the
811  	 * fsal method can process the rest of the parameters in the block
812  	 */
813  	
814  	static int fsal_update_cfg_commit(void *node, void *link_mem, void *self_struct,
815  					  struct config_error_type *err_type)
816  	{
817  		struct fsal_export **exp_hdl = link_mem;
818  		struct gsh_export *probe_exp;
819  		struct gsh_export *export =
820  		    container_of(exp_hdl, struct gsh_export, fsal_export);
821  		struct fsal_args *fp = self_struct;
822  		struct root_op_context root_op_context;
823  		uint64_t MaxRead, MaxWrite;
824  		struct fsal_module *fsal;
825  		fsal_status_t status;
826  		int errcnt;
827  	
828  		/* Determine if this is actually an update */
829  		probe_exp = get_gsh_export(export->export_id);
830  	
831  		if (probe_exp == NULL) {
832  			/* Export not found by ID, assume it's a new export. */
833  			return fsal_cfg_commit(node, link_mem, self_struct, err_type);
834  		}
835  	
836  		/* Initialize req_ctx from the probe_exp */
837  		init_root_op_context(&root_op_context, probe_exp,
838  				     probe_exp->fsal_export, 0, 0, UNKNOWN_REQUEST);
839  	
840  		errcnt = fsal_load_init(node, fp->name, &fsal, err_type);
841  	
842  		if (errcnt > 0)
843  			goto err;
844  	
845  		/* We have to clean the export paths so we can properly compare them
846  		 * later.
847  		 */
848  		clean_export_paths(export);
849  	
850  		/* The handle cache (currently MDCACHE) must be at the top of the stack
851  		 * of FSALs.  To achieve this, call directly into MDCACHE, passing the
852  		 * sub-FSAL's fsal_module.  MDCACHE will stack itself on top of that
853  		 * FSAL, continuing down the chain.
854  		 */
855  		status = mdcache_fsal_update_export(fsal, node, err_type,
856  						    probe_exp->fsal_export);
857  	
858  		if (FSAL_IS_ERROR(status)) {
859  			fsal_put(fsal);
860  			LogCrit(COMPONENT_CONFIG,
861  				"Could not update export for (%s) to (%s)",
862  				export->pseudopath,
863  				export->fullpath);
864  			LogFullDebug(COMPONENT_FSAL,
865  				     "FSAL %s refcount %"PRIu32,
866  				     fsal->name,
867  				     atomic_fetch_int32_t(&fsal->refcount));
868  			err_type->export_ = true;
869  			errcnt++;
870  			goto err;
871  		}
872  	
873  		/* We don't assign export->fsal_export because we don't have a new
874  		 * fsal_export to later release...
875  		 */
876  	
877  		/* Now validate maxread/write etc with fsal params based on the
878  		 * original export, which will then allow us to validate the
879  		 * possibly changed values in the new export config.
880  		 */
881  		MaxRead =
882  		    probe_exp->fsal_export->exp_ops.fs_maxread(probe_exp->fsal_export);
883  		MaxWrite =
884  		    probe_exp->fsal_export->exp_ops.fs_maxwrite(probe_exp->fsal_export);
885  	
886  		if (export->MaxRead > MaxRead && MaxRead != 0) {
887  			LogInfo(COMPONENT_CONFIG,
888  				 "Readjusting MaxRead to FSAL, %" PRIu64 " -> %" PRIu64,
889  				 export->MaxRead,
890  				 MaxRead);
891  			export->MaxRead = MaxRead;
892  		}
893  	
894  		if (export->MaxWrite > MaxWrite && MaxWrite != 0) {
895  			LogInfo(COMPONENT_CONFIG,
896  				 "Readjusting MaxWrite to FSAL, %"PRIu64" -> %"PRIu64,
897  				 export->MaxWrite,
898  				 MaxWrite);
899  			export->MaxWrite = MaxWrite;
900  		}
901  	
902  		LogDebug(COMPONENT_EXPORT,
903  			 "Export %d FSAL config update processed",
904  			 export->export_id);
905  	
906  		release_root_op_context();
907  	
908  		put_gsh_export(probe_exp);
909  	
910  		/* Don't leak the FSAL block */
911  		err_type->dispose = true;
912  	
913  		return 0;
914  	
915  	err:
916  	
917  		release_root_op_context();
918  	
919  		/* Don't leak the FSAL block */
920  		err_type->dispose = true;
921  		return errcnt;
922  	}
923  	
924  	/**
925  	 * @brief EXPORT block handlers
926  	 */
927  	
928  	/**
929  	 * @brief Initialize an export block
930  	 *
931  	 * There is no link_mem init required because we are allocating
932  	 * here and doing an insert_gsh_export at the end of export_commit
933  	 * to attach it to the export manager.
934  	 *
935  	 * Use free_exportlist here because in this case, we have not
936  	 * gotten far enough to hand it over to the export manager.
937  	 */
938  	
939  	static void *export_init(void *link_mem, void *self_struct)
940  	{
941  		struct gsh_export *export;
942  	
943  		if (self_struct == NULL) {
944  			export = alloc_export();
945  			return export;
946  		} else { /* free resources case */
947  			export = self_struct;
948  			/* As part of create_export(), FSAL shall take
949  			 * reference to the export if it supports pNFS.
950  			 */
951  			if (export->has_pnfs_ds) {
952  				assert(export->refcnt == 1);
953  				/* export is not yet added to the export
954  				 * manager. Hence there shall not be any
955  				 * other thread racing here. So no need
956  				 * to take lock. */
957  				export->has_pnfs_ds = false;
958  				pnfs_ds_remove(export->export_id, true);
959  			} else {
960  				assert(export->refcnt == 0);
961  				export_cleanup(export);
962  			}
963  	
964  			return NULL;
965  		}
966  	}
967  	
968  	static inline int strcmp_null(const char *s1, const char *s2)
969  	{
970  		if (s1 == s2) {
971  			/* Both strings are NULL or both are same pointer */
972  			return 0;
973  		}
974  	
975  		if (s1 == NULL) {
976  			/* First string is NULL, consider that LESS than */
977  			return -1;
978  		}
979  	
980  		if (s2 == NULL) {
981  			/* Second string is NULL, consider that GREATER than */
982  			return 1;
983  		}
984  	
985  		return strcmp(s1, s2);
986  	}
987  	
988  	static inline void update_atomic_fields(struct gsh_export *export,
989  						struct gsh_export *src)
990  	{
991  		atomic_store_uint64_t(&export->MaxRead, src->MaxRead);
992  		atomic_store_uint64_t(&export->MaxWrite, src->MaxWrite);
993  		atomic_store_uint64_t(&export->PrefRead, src->PrefRead);
994  		atomic_store_uint64_t(&export->PrefWrite, src->PrefWrite);
995  		atomic_store_uint64_t(&export->PrefReaddir, src->PrefReaddir);
996  		atomic_store_uint64_t(&export->MaxOffsetWrite, src->MaxOffsetWrite);
997  		atomic_store_uint64_t(&export->MaxOffsetRead, src->MaxOffsetRead);
998  		atomic_store_uint32_t(&export->options, src->options);
999  		atomic_store_uint32_t(&export->options_set, src->options_set);
1000 	}
1001 	
1002 	/**
1003 	 * @brief Commit an export block
1004 	 *
1005 	 * Validate the export level parameters.  fsal and client
1006 	 * parameters are already done.
1007 	 */
1008 	
1009 	enum export_commit_type {
1010 		initial_export,
1011 		add_export,
1012 		update_export,
1013 	};
1014 	
1015 	static int export_commit_common(void *node, void *link_mem, void *self_struct,
1016 					struct config_error_type *err_type,
1017 					enum export_commit_type commit_type)
1018 	{
1019 		struct gsh_export *export = self_struct, *probe_exp;
1020 		int errcnt = 0;
1021 		char perms[1024] = "\0";
1022 		struct display_buffer dspbuf = {sizeof(perms), perms, perms};
1023 	
1024 		LogFullDebug(COMPONENT_EXPORT, "Processing %p", export);
1025 	
1026 		/* validate the export now */
1027 		if (export->export_perms.options & EXPORT_OPTION_NFSV4) {
1028 			if (export->pseudopath == NULL) {
1029 				LogCrit(COMPONENT_CONFIG,
1030 					"Exporting to NFSv4 but no Pseudo path defined");
1031 				err_type->invalid = true;
1032 				errcnt++;
1033 				return errcnt;
1034 			} else if (export->export_id == 0 &&
1035 				   strcmp(export->pseudopath, "/") != 0) {
1036 				LogCrit(COMPONENT_CONFIG,
1037 					"Export id 0 can only export \"/\" not (%s)",
1038 					export->pseudopath);
1039 				err_type->invalid = true;
1040 				errcnt++;
1041 				return errcnt;
1042 			}
1043 		}
1044 	
1045 		/* If we are using mount_path_pseudo = true we MUST have a Pseudo Path.
1046 		 */
1047 		if (nfs_param.core_param.mount_path_pseudo &&
1048 		    export->pseudopath == NULL) {
1049 			LogCrit(COMPONENT_CONFIG,
1050 				"NFS_CORE_PARAM mount_path_pseudo is TRUE but no Pseudo path defined");
1051 			err_type->invalid = true;
1052 			errcnt++;
1053 			return errcnt;
1054 		}
1055 	
1056 		if (export->pseudopath != NULL &&
1057 		    export->pseudopath[0] != '/') {
1058 			LogCrit(COMPONENT_CONFIG,
1059 				"A Pseudo path must be an absolute path");
1060 			err_type->invalid = true;
1061 			errcnt++;
1062 		}
1063 		if (export->export_id == 0) {
1064 			if (export->pseudopath == NULL) {
1065 				LogCrit(COMPONENT_CONFIG,
1066 					"Pseudo path must be \"/\" for export id 0");
1067 				err_type->invalid = true;
1068 				errcnt++;
1069 			} else if (export->pseudopath[1] != '\0') {
1070 				LogCrit(COMPONENT_CONFIG,
1071 					"Pseudo path must be \"/\" for export id 0");
1072 				err_type->invalid = true;
1073 				errcnt++;
1074 			}
1075 			if ((export->export_perms.options &
1076 			     EXPORT_OPTION_PROTOCOLS) != EXPORT_OPTION_NFSV4) {
1077 				LogCrit(COMPONENT_CONFIG,
1078 					"Export id 0 must include 4 in Protocols");
1079 				err_type->invalid = true;
1080 				errcnt++;
1081 			}
1082 		}
1083 		if (errcnt)
1084 			return errcnt;  /* have basic errors. don't even try more... */
1085 	
1086 		/* Note: need to check export->fsal_export AFTER we have checked for
1087 		 * duplicate export_id. That is because an update export WILL NOT
1088 		 * have fsal_export attached.
1089 		 */
1090 	
1091 		probe_exp = get_gsh_export(export->export_id);
1092 	
1093 		if (commit_type == update_export && probe_exp != NULL) {
1094 			/* We have an actual update case, probe_exp is the target
1095 			 * to update. Check all the options that MUST match.
1096 			 * Note that Path/fullpath will not be NULL, but we compare
1097 			 * the same way as the other string options for code
1098 			 * consistency.
1099 			 */
1100 			LogFullDebug(COMPONENT_EXPORT, "Updating %p", probe_exp);
1101 	
1102 			LogMidDebug(COMPONENT_EXPORT, "Old Client List");
1103 			display_clients(probe_exp);
1104 	
1105 			LogMidDebug(COMPONENT_EXPORT, "New Client List");
1106 			display_clients(export);
1107 	
1108 			if (strcmp_null(export->FS_tag,
1109 					probe_exp->FS_tag) != 0) {
1110 				/* Tag does not match, currently not a candidate for
1111 				 * update.
1112 				 */
1113 				LogCrit(COMPONENT_CONFIG,
1114 					"Tag for export update %d %s doesn't match %s",
1115 					export->export_id,
1116 					export->FS_tag, probe_exp->FS_tag);
1117 				err_type->invalid = true;
1118 				errcnt++;
1119 			}
1120 	
1121 			if (strcmp_null(export->pseudopath,
1122 					probe_exp->pseudopath) != 0) {
1123 				/* Pseudo does not match, currently not a candidate for
1124 				 * update.
1125 				 */
1126 				LogCrit(COMPONENT_CONFIG,
1127 					"Pseudo for export update %d %s doesn't match %s",
1128 					export->export_id,
1129 					export->pseudopath, probe_exp->pseudopath);
1130 				err_type->invalid = true;
1131 				errcnt++;
1132 			}
1133 	
1134 			if (strcmp_null(export->fullpath,
1135 					probe_exp->fullpath) != 0) {
1136 				/* Path does not match, currently not a candidate for
1137 				 * update.
1138 				 */
1139 				LogCrit(COMPONENT_CONFIG,
1140 					"Path for export update %d %s doesn't match %s",
1141 					export->export_id,
1142 					export->fullpath, probe_exp->fullpath);
1143 				err_type->invalid = true;
1144 				errcnt++;
1145 			}
1146 	
1147 			/* At present Filesystem_Id is not updateable, check that
1148 			 * it did not change.
1149 			 */
1150 			if (probe_exp->filesystem_id.major
1151 						!= export->filesystem_id.major ||
1152 			    probe_exp->filesystem_id.minor
1153 						!= export->filesystem_id.minor) {
1154 				LogCrit(COMPONENT_CONFIG,
1155 					"Filesystem_Id for export update %d %"
1156 					PRIu64".%"PRIu64" doesn't match%"
1157 					PRIu64".%"PRIu64,
1158 					export->export_id,
1159 					export->filesystem_id.major,
1160 					export->filesystem_id.minor,
1161 					probe_exp->filesystem_id.major,
1162 					probe_exp->filesystem_id.minor);
1163 				err_type->invalid = true;
1164 				errcnt++;
1165 			}
1166 	
1167 			/* We can't compare the FSAL names because we don't actually
1168 			 * have an fsal_export for "export".
1169 			 */
1170 	
1171 			if (errcnt > 0) {
1172 				put_gsh_export(probe_exp);
1173 				return errcnt;
1174 			}
1175 	
1176 			/* Grab config_generation for this config */
1177 			probe_exp->config_gen = get_parse_root_generation(node);
1178 	
1179 			/* Update atomic fields */
1180 			update_atomic_fields(probe_exp, export);
1181 	
1182 			/* Now take lock and swap out client list and export_perms... */
1183 			PTHREAD_RWLOCK_wrlock(&probe_exp->lock);
1184 	
1185 			/* Copy the export perms into the existing export. */
1186 			probe_exp->export_perms = export->export_perms;
1187 	
1188 			/* Swap the client list from the new export and the existing
1189 			 * export. When we then dispose of the new export, the
1190 			 * old client list will also be disposed of.
1191 			 */
1192 			LogFullDebug(COMPONENT_EXPORT,
1193 				     "Original clients = (%p,%p) New clients = (%p,%p)",
1194 				     probe_exp->clients.next, probe_exp->clients.prev,
1195 				     export->clients.next, export->clients.prev);
1196 	
1197 			glist_swap_lists(&probe_exp->clients, &export->clients);
1198 	
1199 			PTHREAD_RWLOCK_unlock(&probe_exp->lock);
1200 	
1201 			/* We will need to dispose of the config export since we
1202 			 * updated the existing export.
1203 			 */
1204 			err_type->dispose = true;
1205 	
1206 			/* Release the reference to the updated export. */
1207 			put_gsh_export(probe_exp);
1208 			goto success;
1209 		}
1210 	
1211 		if (commit_type == update_export) {
1212 			/* We found a new export during export update, consider it
1213 			 * an add_export for the rest of configuration.
1214 			 */
1215 			commit_type = add_export;
1216 		}
1217 	
1218 		if (probe_exp != NULL) {
1219 			LogDebug(COMPONENT_EXPORT,
1220 				 "Export %d already exists", export->export_id);
1221 			put_gsh_export(probe_exp);
1222 			err_type->exists = true;
1223 			errcnt++;
1224 		}
1225 	
1226 		/* export->fsal_export is valid iff fsal_cfg_commit succeeds.
1227 		 * Config code calls export_commit even if fsal_cfg_commit fails at
1228 		 * the moment, so error out here if fsal_cfg_commit failed.
1229 		 */
1230 		if (export->fsal_export == NULL) {
1231 			err_type->validate = true;
1232 			errcnt++;
1233 			return errcnt;
1234 		}
1235 	
1236 		if (export->FS_tag != NULL) {
1237 			probe_exp = get_gsh_export_by_tag(export->FS_tag);
1238 			if (probe_exp != NULL) {
1239 				put_gsh_export(probe_exp);
1240 				LogCrit(COMPONENT_CONFIG,
1241 					"Tag (%s) is a duplicate",
1242 					export->FS_tag);
1243 				if (!err_type->exists)
1244 					err_type->invalid = true;
1245 				errcnt++;
1246 			}
1247 		}
1248 	
1249 		if (export->pseudopath != NULL) {
1250 			probe_exp = get_gsh_export_by_pseudo(export->pseudopath, true);
1251 			if (probe_exp != NULL) {
1252 				LogCrit(COMPONENT_CONFIG,
1253 					"Pseudo path (%s) is a duplicate",
1254 					export->pseudopath);
1255 				if (!err_type->exists)
1256 					err_type->invalid = true;
1257 				errcnt++;
1258 				put_gsh_export(probe_exp);
1259 			}
1260 		}
1261 	
1262 		probe_exp = get_gsh_export_by_path(export->fullpath, true);
1263 	
1264 		if (probe_exp != NULL) {
1265 			if (export->pseudopath == NULL &&
1266 			    export->FS_tag == NULL) {
1267 				LogCrit(COMPONENT_CONFIG,
1268 					"Duplicate path (%s) without unique tag or Pseudo path",
1269 					export->fullpath);
1270 				err_type->invalid = true;
1271 				errcnt++;
1272 			}
1273 			/* If unique Tag and/or Pseudo, there is no error, but we still
1274 			 * need to release the export reference.
1275 			 */
1276 			put_gsh_export(probe_exp);
1277 		}
1278 	
1279 		if (errcnt) {
1280 			if (err_type->exists && !err_type->invalid)
1281 				LogDebug(COMPONENT_EXPORT,
1282 					 "Duplicate export id = %d",
1283 					 export->export_id);
1284 			else
1285 				LogCrit(COMPONENT_CONFIG,
1286 					 "Duplicate export id = %d",
1287 					 export->export_id);
1288 			return errcnt;  /* have errors. don't init or load a fsal */
1289 		}
1290 	
1291 		if (commit_type != initial_export) {
1292 			/* add_export or update_export with new export_id. */
1293 			int rc = init_export_root(export);
1294 	
1295 			if (rc) {
1296 				switch (rc) {
1297 				case EINVAL:
1298 					err_type->invalid = true;
1299 					break;
1300 	
1301 				case EFAULT:
1302 					err_type->internal = true;
1303 					break;
1304 	
1305 				default:
1306 					err_type->resource = true;
1307 				}
1308 	
1309 				errcnt++;
1310 				return errcnt;
1311 			}
1312 	
1313 			if (!mount_gsh_export(export)) {
1314 				err_type->internal = true;
1315 				errcnt++;
1316 				return errcnt;
1317 			}
1318 		}
1319 	
1320 		if (!insert_gsh_export(export)) {
1321 			LogCrit(COMPONENT_CONFIG,
1322 				"Export id %d already in use.",
1323 				export->export_id);
1324 			err_type->exists = true;
1325 			errcnt++;
1326 			return errcnt;
1327 		}
1328 	
1329 		/* add_export_commit shouldn't add this export to mount work as
1330 		 * add_export_commit deals with creating pseudo mount directly.
1331 		 * So add this export to mount work only if NFSv4 exported and
1332 		 * is not a dynamically added export.
1333 		 */
1334 		if (commit_type == initial_export &&
1335 		    export->export_perms.options & EXPORT_OPTION_NFSV4)
1336 			export_add_to_mount_work(export);
1337 	
1338 		display_clients(export);
1339 	
1340 		/* Copy the generation */
1341 		export->config_gen = get_parse_root_generation(node);
1342 	
1343 	success:
1344 	
1345 		(void) StrExportOptions(&dspbuf, &export->export_perms);
1346 	
1347 		LogInfo(COMPONENT_CONFIG,
1348 			"Export %d %s at pseudo (%s) with path (%s) and tag (%s) perms (%s)",
1349 			export->export_id,
1350 			commit_type == update_export ? "updated" : "created",
1351 			export->pseudopath,
1352 			export->fullpath, export->FS_tag, perms);
1353 	
1354 		LogInfo(COMPONENT_CONFIG,
1355 			"Export %d has %zd defined clients", export->export_id,
1356 			glist_length(&export->clients));
1357 	
1358 		if (commit_type != update_export) {
1359 			/* For initial or add export, insert_gsh_export gave out
1360 			 * two references, a sentinel reference for the export's
1361 			 * presence in the export table, and one reference for our
1362 			 * use here, drop that second reference now.
1363 			 *
1364 			 * In the case of update_export, we already dropped the
1365 			 * reference to the updated export, and this export has
1366 			 * no references and will be freed by the config code.
1367 			 */
1368 			put_gsh_export(export);
1369 		}
1370 	
1371 		return errcnt;
1372 	}
1373 	
1374 	static int export_commit(void *node, void *link_mem, void *self_struct,
1375 				 struct config_error_type *err_type)
1376 	{
1377 		return export_commit_common(node, link_mem, self_struct, err_type,
1378 					    initial_export);
1379 	}
1380 	
1381 	/**
1382 	 * @brief Display an export block
1383 	 *
1384 	 * Validate the export level parameters.  fsal and client
1385 	 * parameters are already done.
1386 	 */
1387 	
1388 	static void export_display(const char *step, void *node,
1389 				   void *link_mem, void *self_struct)
1390 	{
1391 		struct gsh_export *export = self_struct;
1392 		char perms[1024] = "\0";
1393 		struct display_buffer dspbuf = {sizeof(perms), perms, perms};
1394 	
1395 		(void) StrExportOptions(&dspbuf, &export->export_perms);
1396 	
1397 		LogMidDebug(COMPONENT_EXPORT,
1398 			    "%s %p Export %d pseudo (%s) with path (%s) and tag (%s) perms (%s)",
1399 			    step, export, export->export_id, export->pseudopath,
1400 			    export->fullpath, export->FS_tag, perms);
1401 	}
1402 	
1403 	/**
1404 	 * @brief Commit an add export
1405 	 * commit the export
1406 	 * init export root and mount it in pseudo fs
1407 	 */
1408 	
1409 	static int add_export_commit(void *node, void *link_mem, void *self_struct,
1410 				     struct config_error_type *err_type)
1411 	{
1412 		return export_commit_common(node, link_mem, self_struct, err_type,
1413 					    add_export);
1414 	}
1415 	
1416 	/**
1417 	 * @brief Commit an update export
1418 	 * commit the export
1419 	 * init export root and mount it in pseudo fs
1420 	 */
1421 	
1422 	static int update_export_commit(void *node, void *link_mem, void *self_struct,
1423 					struct config_error_type *err_type)
1424 	{
1425 		return export_commit_common(node, link_mem, self_struct, err_type,
1426 					    update_export);
1427 	}
1428 	
1429 	/**
1430 	 * @brief Initialize an EXPORT_DEFAULTS block
1431 	 *
1432 	 */
1433 	
1434 	static void *export_defaults_init(void *link_mem, void *self_struct)
1435 	{
1436 		if (self_struct == NULL)
1437 			return &export_opt_cfg;
1438 		else
1439 			return NULL;
1440 	}
1441 	
1442 	/**
1443 	 * @brief Commit an EXPORT_DEFAULTS block
1444 	 *
1445 	 * Validate the export level parameters.  fsal and client
1446 	 * parameters are already done.
1447 	 */
1448 	
1449 	static int export_defaults_commit(void *node, void *link_mem,
1450 					  void *self_struct,
1451 					  struct config_error_type *err_type)
1452 	{
1453 		char perms[1024] = "\0";
1454 		struct display_buffer dspbuf = {sizeof(perms), perms, perms};
1455 	
1456 		(void) StrExportOptions(&dspbuf, &export_opt_cfg.conf);
1457 	
1458 		LogInfo(COMPONENT_CONFIG, "Export Defaults now (%s)", perms);
1459 	
1460 		/* Update under lock. */
1461 		PTHREAD_RWLOCK_wrlock(&export_opt_lock);
1462 		export_opt = export_opt_cfg;
1463 		PTHREAD_RWLOCK_unlock(&export_opt_lock);
1464 	
1465 		return 0;
1466 	}
1467 	
1468 	/**
1469 	 * @brief Display an EXPORT_DEFAULTS block
1470 	 *
1471 	 * Validate the export level parameters.  fsal and client
1472 	 * parameters are already done.
1473 	 */
1474 	
1475 	static void export_defaults_display(const char *step, void *node,
1476 					    void *link_mem, void *self_struct)
1477 	{
1478 		struct export_perms *defaults = self_struct;
1479 		char perms[1024] = "\0";
1480 		struct display_buffer dspbuf = {sizeof(perms), perms, perms};
1481 	
1482 		(void) StrExportOptions(&dspbuf, defaults);
1483 	
1484 		LogMidDebug(COMPONENT_EXPORT,
1485 			    "%s Export Defaults (%s)",
1486 			    step, perms);
1487 	}
1488 	
1489 	/**
1490 	 * @brief Configuration processing tables for EXPORT blocks
1491 	 */
1492 	
1493 	/**
1494 	 * @brief Access types list for the Access_type parameter
1495 	 */
1496 	
1497 	static struct config_item_list access_types[] = {
1498 		CONFIG_LIST_TOK("NONE", 0),
1499 		CONFIG_LIST_TOK("RW", (EXPORT_OPTION_RW_ACCESS |
1500 				       EXPORT_OPTION_MD_ACCESS)),
1501 		CONFIG_LIST_TOK("RO", (EXPORT_OPTION_READ_ACCESS |
1502 				       EXPORT_OPTION_MD_READ_ACCESS)),
1503 		CONFIG_LIST_TOK("MDONLY", EXPORT_OPTION_MD_ACCESS),
1504 		CONFIG_LIST_TOK("MDONLY_RO", EXPORT_OPTION_MD_READ_ACCESS),
1505 		CONFIG_LIST_EOL
1506 	};
1507 	
1508 	/**
1509 	 * @brief Protocols options list for NFS_Protocols parameter
1510 	 */
1511 	
1512 	static struct config_item_list nfs_protocols[] = {
1513 		CONFIG_LIST_TOK("3", EXPORT_OPTION_NFSV3),
1514 		CONFIG_LIST_TOK("4", EXPORT_OPTION_NFSV4),
1515 		CONFIG_LIST_TOK("NFS3", EXPORT_OPTION_NFSV3),
1516 		CONFIG_LIST_TOK("NFS4", EXPORT_OPTION_NFSV4),
1517 		CONFIG_LIST_TOK("V3", EXPORT_OPTION_NFSV3),
1518 		CONFIG_LIST_TOK("V4", EXPORT_OPTION_NFSV4),
1519 		CONFIG_LIST_TOK("NFSV3", EXPORT_OPTION_NFSV3),
1520 		CONFIG_LIST_TOK("NFSV4", EXPORT_OPTION_NFSV4),
1521 		CONFIG_LIST_TOK("9P", EXPORT_OPTION_9P),
1522 		CONFIG_LIST_EOL
1523 	};
1524 	
1525 	/**
1526 	 * @brief Transport type options list for Transport_Protocols parameter
1527 	 */
1528 	
1529 	static struct config_item_list transports[] = {
1530 		CONFIG_LIST_TOK("UDP", EXPORT_OPTION_UDP),
1531 		CONFIG_LIST_TOK("TCP", EXPORT_OPTION_TCP),
1532 		CONFIG_LIST_EOL
1533 	};
1534 	
1535 	/**
1536 	 * @brief Security options list for SecType parameter
1537 	 */
1538 	
1539 	static struct config_item_list sec_types[] = {
1540 		CONFIG_LIST_TOK("none", EXPORT_OPTION_AUTH_NONE),
1541 		CONFIG_LIST_TOK("sys", EXPORT_OPTION_AUTH_UNIX),
1542 		CONFIG_LIST_TOK("krb5", EXPORT_OPTION_RPCSEC_GSS_NONE),
1543 		CONFIG_LIST_TOK("krb5i", EXPORT_OPTION_RPCSEC_GSS_INTG),
1544 		CONFIG_LIST_TOK("krb5p", EXPORT_OPTION_RPCSEC_GSS_PRIV),
1545 		CONFIG_LIST_EOL
1546 	};
1547 	
1548 	/**
1549 	 * @brief Client UID squash item list for Squash parameter
1550 	 */
1551 	
1552 	static struct config_item_list squash_types[] = {
1553 		CONFIG_LIST_TOK("Root", EXPORT_OPTION_ROOT_SQUASH),
1554 		CONFIG_LIST_TOK("Root_Squash", EXPORT_OPTION_ROOT_SQUASH),
1555 		CONFIG_LIST_TOK("RootSquash", EXPORT_OPTION_ROOT_SQUASH),
1556 		CONFIG_LIST_TOK("All", EXPORT_OPTION_ALL_ANONYMOUS),
1557 		CONFIG_LIST_TOK("All_Squash", EXPORT_OPTION_ALL_ANONYMOUS),
1558 		CONFIG_LIST_TOK("AllSquash", EXPORT_OPTION_ALL_ANONYMOUS),
1559 		CONFIG_LIST_TOK("All_Anonymous", EXPORT_OPTION_ALL_ANONYMOUS),
1560 		CONFIG_LIST_TOK("AllAnonymous", EXPORT_OPTION_ALL_ANONYMOUS),
1561 		CONFIG_LIST_TOK("No_Root_Squash", EXPORT_OPTION_ROOT),
1562 		CONFIG_LIST_TOK("None", EXPORT_OPTION_ROOT),
1563 		CONFIG_LIST_TOK("NoIdSquash", EXPORT_OPTION_ROOT),
1564 		CONFIG_LIST_TOK("RootId", EXPORT_OPTION_ROOT_ID_SQUASH),
1565 		CONFIG_LIST_TOK("Root_Id_Squash", EXPORT_OPTION_ROOT_ID_SQUASH),
1566 		CONFIG_LIST_TOK("RootIdSquash", EXPORT_OPTION_ROOT_ID_SQUASH),
1567 		CONFIG_LIST_EOL
1568 	};
1569 	
1570 	/**
1571 	 * @brief Delegations types list for the Delegations parameter
1572 	 */
1573 	
1574 	static struct config_item_list delegations[] = {
1575 		CONFIG_LIST_TOK("NONE", EXPORT_OPTION_NO_DELEGATIONS),
1576 		CONFIG_LIST_TOK("Read", EXPORT_OPTION_READ_DELEG),
1577 		CONFIG_LIST_TOK("Write", EXPORT_OPTION_WRITE_DELEG),
1578 		CONFIG_LIST_TOK("Readwrite", EXPORT_OPTION_DELEGATIONS),
1579 		CONFIG_LIST_TOK("R", EXPORT_OPTION_READ_DELEG),
1580 		CONFIG_LIST_TOK("W", EXPORT_OPTION_WRITE_DELEG),
1581 		CONFIG_LIST_TOK("RW", EXPORT_OPTION_DELEGATIONS),
1582 		CONFIG_LIST_EOL
1583 	};
1584 	
1585 	struct config_item_list deleg_types[] =  {
1586 		CONFIG_LIST_TOK("NONE", FSAL_OPTION_NO_DELEGATIONS),
1587 		CONFIG_LIST_TOK("Read", FSAL_OPTION_FILE_READ_DELEG),
1588 		CONFIG_LIST_TOK("Write", FSAL_OPTION_FILE_WRITE_DELEG),
1589 		CONFIG_LIST_TOK("Readwrite", FSAL_OPTION_FILE_DELEGATIONS),
1590 		CONFIG_LIST_TOK("R", FSAL_OPTION_FILE_READ_DELEG),
1591 		CONFIG_LIST_TOK("W", FSAL_OPTION_FILE_WRITE_DELEG),
1592 		CONFIG_LIST_TOK("RW", FSAL_OPTION_FILE_DELEGATIONS),
1593 		CONFIG_LIST_EOL
1594 	};
1595 	
1596 	#define CONF_EXPORT_PERMS(_struct_, _perms_)				\
1597 		/* Note: Access_Type defaults to None on purpose */		\
1598 		CONF_ITEM_ENUM_BITS_SET("Access_Type",				\
1599 			EXPORT_OPTION_NO_ACCESS,				\
1600 			EXPORT_OPTION_ACCESS_MASK,				\
1601 			access_types, _struct_, _perms_.options, _perms_.set),	\
1602 		CONF_ITEM_LIST_BITS_SET("Protocols",				\
1603 			EXPORT_OPTION_PROTO_DEFAULTS, EXPORT_OPTION_PROTOCOLS,	\
1604 			nfs_protocols, _struct_, _perms_.options, _perms_.set),	\
1605 		CONF_ITEM_LIST_BITS_SET("Transports",				\
1606 			EXPORT_OPTION_XPORT_DEFAULTS, EXPORT_OPTION_TRANSPORTS,	\
1607 			transports, _struct_, _perms_.options, _perms_.set),	\
1608 		CONF_ITEM_ANON_ID_SET("Anonymous_uid",				\
1609 			ANON_UID, _struct_, _perms_.anonymous_uid,		\
1610 			EXPORT_OPTION_ANON_UID_SET, _perms_.set),		\
1611 		CONF_ITEM_ANON_ID_SET("Anonymous_gid",				\
1612 			ANON_GID, _struct_, _perms_.anonymous_gid,		\
1613 			EXPORT_OPTION_ANON_GID_SET, _perms_.set),		\
1614 		CONF_ITEM_LIST_BITS_SET("SecType",				\
1615 			EXPORT_OPTION_AUTH_DEFAULTS, EXPORT_OPTION_AUTH_TYPES,	\
1616 			sec_types, _struct_, _perms_.options, _perms_.set),	\
1617 		CONF_ITEM_BOOLBIT_SET("PrivilegedPort",				\
1618 			false, EXPORT_OPTION_PRIVILEGED_PORT,			\
1619 			_struct_, _perms_.options, _perms_.set),		\
1620 		CONF_ITEM_BOOLBIT_SET("Manage_Gids",				\
1621 			false, EXPORT_OPTION_MANAGE_GIDS,			\
1622 			_struct_, _perms_.options, _perms_.set),		\
1623 		CONF_ITEM_LIST_BITS_SET("Squash",				\
1624 			EXPORT_OPTION_ROOT_SQUASH, EXPORT_OPTION_SQUASH_TYPES,	\
1625 			squash_types, _struct_, _perms_.options, _perms_.set),	\
1626 		CONF_ITEM_BOOLBIT_SET("NFS_Commit",				\
1627 			false, EXPORT_OPTION_COMMIT,				\
1628 			_struct_, _perms_.options, _perms_.set),		\
1629 		CONF_ITEM_ENUM_BITS_SET("Delegations",				\
1630 			EXPORT_OPTION_NO_DELEGATIONS, EXPORT_OPTION_DELEGATIONS,\
1631 			delegations, _struct_, _perms_.options, _perms_.set)
1632 	
1633 	/**
1634 	 * @brief Process a list of clients for a client block
1635 	 *
1636 	 * CONFIG_PROC handler that gets called for each token in the term list.
1637 	 * Create a exportlist_client_entry__ for each token and link it into
1638 	 * the proto client's cle_list list head.  We will pass that head to the
1639 	 * export in commit.
1640 	 *
1641 	 * NOTES: this is the place to expand a node list with perhaps moving the
1642 	 * call to add_client into the expander rather than build a list there
1643 	 * to be then walked here...
1644 	 *
1645 	 * @param token [IN] pointer to token string from parse tree
1646 	 * @param type_hint [IN] a type hint from what the parser recognized
1647 	 * @param item [IN] pointer to the config item table entry
1648 	 * @param param_addr [IN] pointer to prototype client entry
1649 	 * @param err_type [OUT] error handling
1650 	 * @return error count
1651 	 */
1652 	
1653 	static int client_adder(const char *token,
1654 				enum term_type type_hint,
1655 				struct config_item *item,
1656 				void *param_addr,
1657 				void *cnode,
1658 				struct config_error_type *err_type)
1659 	{
1660 		struct exportlist_client_entry__ *proto_cli;
1661 		int rc;
1662 	
1663 		proto_cli = container_of(param_addr,
1664 					 struct exportlist_client_entry__,
1665 					 cle_list);
1666 		LogMidDebug(COMPONENT_EXPORT, "Adding client %s", token);
1667 		rc = add_client(&proto_cli->cle_list,
1668 				token, type_hint,
1669 				&proto_cli->client_perms, cnode, err_type);
1670 		return rc;
1671 	}
1672 	
1673 	/**
1674 	 * @brief Table of client sub-block parameters
1675 	 *
1676 	 * NOTE: node discovery is ordered by this table!
1677 	 * "Clients" is last because we must have all other params processed
1678 	 * before we walk the list of accessing clients!
1679 	 */
1680 	
1681 	static struct config_item client_params[] = {
1682 		CONF_EXPORT_PERMS(exportlist_client_entry__, client_perms),
1683 		CONF_ITEM_PROC("Clients", noop_conf_init, client_adder,
1684 			       exportlist_client_entry__, cle_list),
1685 		CONFIG_EOL
1686 	};
1687 	
1688 	/**
1689 	 * @brief Table of DEXPORT_DEFAULTS block parameters
1690 	 *
1691 	 * NOTE: node discovery is ordered by this table!
1692 	 */
1693 	
1694 	static struct config_item export_defaults_params[] = {
1695 		CONF_EXPORT_PERMS(global_export_perms, conf),
1696 		CONF_ITEM_I32_SET("Attr_Expiration_Time", -1, INT32_MAX, 60,
1697 			       global_export_perms, conf.expire_time_attr,
1698 			       EXPORT_OPTION_EXPIRE_SET, conf.set),
1699 		CONFIG_EOL
1700 	};
1701 	
1702 	/**
1703 	 * @brief Table of FSAL sub-block parameters
1704 	 *
1705 	 * NOTE: this points to a struct that is private to
1706 	 * fsal_cfg_commit.
1707 	 */
1708 	
1709 	static struct config_item fsal_params[] = {
1710 		CONF_ITEM_STR("Name", 1, 10, NULL,
1711 			      fsal_args, name), /* cheater union */
1712 		CONFIG_EOL
1713 	};
1714 	
1715 	/**
1716 	 * @brief Common EXPORT block parameters
1717 	 */
1718 	#define CONF_EXPORT_PARAMS(_struct_)					\
1719 		CONF_MAND_UI16("Export_id", 0, UINT16_MAX, 1,			\
1720 			       _struct_, export_id),				\
1721 		CONF_MAND_PATH("Path", 1, MAXPATHLEN, NULL,			\
1722 			       _struct_, fullpath), /* must chomp '/' */	\
1723 		CONF_UNIQ_PATH("Pseudo", 1, MAXPATHLEN, NULL,			\
1724 			       _struct_, pseudopath),				\
1725 		CONF_ITEM_UI64_SET("MaxRead", 512, FSAL_MAXIOSIZE,		\
1726 				FSAL_MAXIOSIZE, _struct_, MaxRead,		\
1727 				EXPORT_OPTION_MAXREAD_SET, options_set),	\
1728 		CONF_ITEM_UI64_SET("MaxWrite", 512, FSAL_MAXIOSIZE,		\
1729 				FSAL_MAXIOSIZE, _struct_, MaxWrite,		\
1730 				EXPORT_OPTION_MAXWRITE_SET, options_set),	\
1731 		CONF_ITEM_UI64_SET("PrefRead", 512, FSAL_MAXIOSIZE,		\
1732 				FSAL_MAXIOSIZE, _struct_, PrefRead,		\
1733 				EXPORT_OPTION_PREFREAD_SET, options_set),	\
1734 		CONF_ITEM_UI64_SET("PrefWrite", 512, FSAL_MAXIOSIZE,		\
1735 				FSAL_MAXIOSIZE, _struct_, PrefWrite,		\
1736 				EXPORT_OPTION_PREFWRITE_SET, options_set),	\
1737 		CONF_ITEM_UI64("PrefReaddir", 512, FSAL_MAXIOSIZE, 16384,	\
1738 			       _struct_, PrefReaddir),				\
1739 		CONF_ITEM_FSID_SET("Filesystem_id", 666, 666,			\
1740 			       _struct_, filesystem_id, /* major.minor */	\
1741 			       EXPORT_OPTION_FSID_SET, options_set),		\
1742 		CONF_ITEM_STR("Tag", 1, MAXPATHLEN, NULL,			\
1743 			      _struct_, FS_tag),				\
1744 		CONF_ITEM_UI64("MaxOffsetWrite", 512, UINT64_MAX, INT64_MAX,	\
1745 			       _struct_, MaxOffsetWrite),			\
1746 		CONF_ITEM_UI64("MaxOffsetRead", 512, UINT64_MAX, INT64_MAX,	\
1747 			       _struct_, MaxOffsetRead),			\
1748 		CONF_ITEM_BOOLBIT_SET("UseCookieVerifier",			\
1749 			false, EXPORT_OPTION_USE_COOKIE_VERIFIER,		\
1750 			_struct_, options, options_set),			\
1751 		CONF_ITEM_BOOLBIT_SET("DisableReaddirPlus",			\
1752 			false, EXPORT_OPTION_NO_READDIR_PLUS,			\
1753 			_struct_, options, options_set),			\
1754 		CONF_ITEM_BOOLBIT_SET("Trust_Readdir_Negative_Cache",		\
1755 			false, EXPORT_OPTION_TRUST_READIR_NEGATIVE_CACHE,	\
1756 			_struct_, options, options_set),			\
1757 		CONF_ITEM_BOOLBIT_SET("Disable_ACL",				\
1758 			false, EXPORT_OPTION_DISABLE_ACL,			\
1759 			_struct_, options, options_set),			\
1760 		CONF_ITEM_BOOLBIT_SET("Security_Label",				\
1761 			false, EXPORT_OPTION_SECLABEL_SET,			\
1762 			_struct_, options, options_set)
1763 	
1764 	/**
1765 	 * @brief Table of EXPORT block parameters
1766 	 */
1767 	
1768 	static struct config_item export_params[] = {
1769 		CONF_EXPORT_PARAMS(gsh_export),
1770 		CONF_EXPORT_PERMS(gsh_export, export_perms),
1771 		CONF_ITEM_I32_SET("Attr_Expiration_Time", -1, INT32_MAX, 60,
1772 			       gsh_export, export_perms.expire_time_attr,
1773 			       EXPORT_OPTION_EXPIRE_SET, export_perms.set),
1774 	
1775 		/* NOTE: the Client and FSAL sub-blocks must be the *last*
1776 		 * two entries in the list.  This is so all other
1777 		 * parameters have been processed before these sub-blocks
1778 		 * are processed.
1779 		 */
1780 		CONF_ITEM_BLOCK("Client", client_params,
1781 				client_init, client_commit,
1782 				gsh_export, clients),
1783 		CONF_RELAX_BLOCK("FSAL", fsal_params,
1784 				 fsal_init, fsal_cfg_commit,
1785 				 gsh_export, fsal_export),
1786 		CONFIG_EOL
1787 	};
1788 	
1789 	/**
1790 	 * @brief Table of EXPORT update block parameters
1791 	 */
1792 	
1793 	static struct config_item export_update_params[] = {
1794 		CONF_EXPORT_PARAMS(gsh_export),
1795 		CONF_EXPORT_PERMS(gsh_export, export_perms),
1796 		CONF_ITEM_I32_SET("Attr_Expiration_Time", -1, INT32_MAX, 60,
1797 			       gsh_export, export_perms.expire_time_attr,
1798 			       EXPORT_OPTION_EXPIRE_SET, export_perms.set),
1799 	
1800 		/* NOTE: the Client and FSAL sub-blocks must be the *last*
1801 		 * two entries in the list.  This is so all other
1802 		 * parameters have been processed before these sub-blocks
1803 		 * are processed.
1804 		 */
1805 		CONF_ITEM_BLOCK("Client", client_params,
1806 				client_init, client_commit,
1807 				gsh_export, clients),
1808 		CONF_RELAX_BLOCK("FSAL", fsal_params,
1809 				 fsal_init, fsal_update_cfg_commit,
1810 				 gsh_export, fsal_export),
1811 		CONFIG_EOL
1812 	};
1813 	
1814 	/**
1815 	 * @brief Top level definition for an EXPORT block
1816 	 */
1817 	
1818 	static struct config_block export_param = {
1819 		.dbus_interface_name = "org.ganesha.nfsd.config.%d",
1820 		.blk_desc.name = "EXPORT",
1821 		.blk_desc.type = CONFIG_BLOCK,
1822 		.blk_desc.u.blk.init = export_init,
1823 		.blk_desc.u.blk.params = export_params,
1824 		.blk_desc.u.blk.commit = export_commit,
1825 		.blk_desc.u.blk.display = export_display
1826 	};
1827 	
1828 	/**
1829 	 * @brief Top level definition for an ADD EXPORT block
1830 	 */
1831 	
1832 	struct config_block add_export_param = {
1833 		.dbus_interface_name = "org.ganesha.nfsd.config.%d",
1834 		.blk_desc.name = "EXPORT",
1835 		.blk_desc.type = CONFIG_BLOCK,
1836 		.blk_desc.u.blk.init = export_init,
1837 		.blk_desc.u.blk.params = export_params,
1838 		.blk_desc.u.blk.commit = add_export_commit,
1839 		.blk_desc.u.blk.display = export_display
1840 	};
1841 	
1842 	/**
1843 	 * @brief Top level definition for an UPDATE EXPORT block
1844 	 */
1845 	
1846 	struct config_block update_export_param = {
1847 		.dbus_interface_name = "org.ganesha.nfsd.config.%d",
1848 		.blk_desc.name = "EXPORT",
1849 		.blk_desc.type = CONFIG_BLOCK,
1850 		.blk_desc.u.blk.init = export_init,
1851 		.blk_desc.u.blk.params = export_update_params,
1852 		.blk_desc.u.blk.commit = update_export_commit,
1853 		.blk_desc.u.blk.display = export_display
1854 	};
1855 	
1856 	/**
1857 	 * @brief Top level definition for an EXPORT_DEFAULTS block
1858 	 */
1859 	
1860 	struct config_block export_defaults_param = {
1861 		.dbus_interface_name = "org.ganesha.nfsd.config.defaults",
1862 		.blk_desc.name = "EXPORT_DEFAULTS",
1863 		.blk_desc.type = CONFIG_BLOCK,
1864 		.blk_desc.u.blk.init = export_defaults_init,
1865 		.blk_desc.u.blk.params = export_defaults_params,
1866 		.blk_desc.u.blk.commit = export_defaults_commit,
1867 		.blk_desc.u.blk.display = export_defaults_display
1868 	};
1869 	
1870 	/**
1871 	 * @brief builds an export entry for '/' with default parameters
1872 	 *
1873 	 * If export_id = 0 has not been specified, and not other export
1874 	 * for Pseudo "/" has been specified, build an FSAL_PSEUDO export
1875 	 * for the root of the Pseudo FS.
1876 	 *
1877 	 * @return -1 on error, 0 if we already have one, 1 if created one
1878 	 */
1879 	
1880 	static int build_default_root(struct config_error_type *err_type)
1881 	{
1882 		struct gsh_export *export;
1883 		struct fsal_module *fsal_hdl = NULL;
1884 		struct root_op_context root_op_context;
1885 	
1886 		/* See if export_id = 0 has already been specified */
1887 		export = get_gsh_export(0);
1888 	
1889 		if (export != NULL) {
1890 			/* export_id = 0 has already been specified */
1891 			LogDebug(COMPONENT_EXPORT,
1892 				 "Export 0 already exists");
1893 			put_gsh_export(export);
1894 			return 0;
1895 		}
1896 	
1897 		/* See if another export with Pseudo = "/" has already been specified.
1898 		 */
1899 		export = get_gsh_export_by_pseudo("/", true);
1900 	
1901 		if (export != NULL) {
1902 			/* Pseudo = / has already been specified */
1903 			LogDebug(COMPONENT_EXPORT,
1904 				 "Pseudo root already exists");
1905 			put_gsh_export(export);
1906 			return 0;
1907 		}
1908 	
1909 		/* allocate and initialize the exportlist part with the id */
1910 		LogDebug(COMPONENT_EXPORT,
1911 			 "Allocating Pseudo root export");
1912 		export = alloc_export();
1913 	
1914 		/* Initialize req_ctx */
1915 		init_root_op_context(&root_op_context, export, NULL, 0, 0,
1916 				     UNKNOWN_REQUEST);
1917 	
1918 		export->filesystem_id.major = 152;
1919 		export->filesystem_id.minor = 152;
1920 		export->MaxWrite = FSAL_MAXIOSIZE;
1921 		export->MaxRead = FSAL_MAXIOSIZE;
1922 		export->PrefWrite = FSAL_MAXIOSIZE;
1923 		export->PrefRead = FSAL_MAXIOSIZE;
1924 		export->PrefReaddir = 16384;
1925 	
1926 		/*Don't set anonymous uid and gid, they will actually be ignored */
1927 	
1928 		/* Support only NFS v4 and TCP.
1929 		 * Root is allowed
1930 		 * MD Read Access
1931 		 * Allow use of default auth types
1932 		 *
1933 		 * Allow non-privileged client ports to access pseudo export.
1934 		 */
1935 		export->export_perms.options = EXPORT_OPTION_ROOT |
1936 						EXPORT_OPTION_MD_READ_ACCESS |
1937 						EXPORT_OPTION_NFSV4 |
1938 						EXPORT_OPTION_AUTH_TYPES |
1939 						EXPORT_OPTION_TCP;
1940 	
1941 		export->export_perms.set = EXPORT_OPTION_SQUASH_TYPES |
1942 					    EXPORT_OPTION_ACCESS_MASK |
1943 					    EXPORT_OPTION_PROTOCOLS |
1944 					    EXPORT_OPTION_TRANSPORTS |
1945 					    EXPORT_OPTION_AUTH_TYPES |
1946 					    EXPORT_OPTION_PRIVILEGED_PORT;
1947 	
1948 		export->options = EXPORT_OPTION_USE_COOKIE_VERIFIER;
1949 		export->options_set = EXPORT_OPTION_FSID_SET |
1950 				      EXPORT_OPTION_USE_COOKIE_VERIFIER |
1951 				      EXPORT_OPTION_MAXREAD_SET |
1952 				      EXPORT_OPTION_MAXWRITE_SET |
1953 				      EXPORT_OPTION_PREFREAD_SET |
1954 				      EXPORT_OPTION_PREFWRITE_SET;
1955 	
1956 		/* Set the fullpath to "/" */
1957 		export->fullpath = gsh_strdup("/");
1958 	
1959 		/* Set Pseudo Path to "/" */
1960 		export->pseudopath = gsh_strdup("/");
1961 	
1962 		/* Assign FSAL_PSEUDO */
1963 		fsal_hdl = lookup_fsal("PSEUDO");
1964 	
1965 		if (fsal_hdl == NULL) {
1966 			LogCrit(COMPONENT_CONFIG,
1967 				"FSAL PSEUDO is not loaded!");
1968 			goto err_out;
1969 		} else {
1970 			fsal_status_t rc;
1971 	
1972 			rc = mdcache_fsal_create_export(fsal_hdl, NULL, err_type,
1973 							&fsal_up_top);
1974 	
1975 			if (FSAL_IS_ERROR(rc)) {
1976 				fsal_put(fsal_hdl);
1977 				LogCrit(COMPONENT_CONFIG,
1978 					"Could not create FSAL export for %s",
1979 					export->fullpath);
1980 				LogFullDebug(COMPONENT_FSAL,
1981 					     "FSAL %s refcount %"PRIu32,
1982 					     fsal_hdl->name,
1983 					     atomic_fetch_int32_t(&fsal_hdl->refcount));
1984 				goto err_out;
1985 			}
1986 	
1987 		}
1988 	
1989 		assert(root_op_context.req_ctx.fsal_export != NULL);
1990 		export->fsal_export = root_op_context.req_ctx.fsal_export;
1991 	
1992 		if (!insert_gsh_export(export)) {
1993 			export->fsal_export->exp_ops.release(export->fsal_export);
1994 			fsal_put(fsal_hdl);
1995 			LogCrit(COMPONENT_CONFIG,
1996 				"Failed to insert pseudo root   In use??");
1997 			LogFullDebug(COMPONENT_FSAL,
1998 				     "FSAL %s refcount %"PRIu32,
1999 				     fsal_hdl->name,
2000 				     atomic_fetch_int32_t(&fsal_hdl->refcount));
2001 			goto err_out;
2002 		}
2003 	
2004 		/* This export must be mounted to the PseudoFS */
2005 		export_add_to_mount_work(export);
2006 	
2007 		LogInfo(COMPONENT_CONFIG,
2008 			"Export 0 (/) successfully created");
2009 	
2010 		put_gsh_export(export);	/* all done, let go */
2011 		release_root_op_context();
2012 		return 1;
2013 	
2014 	err_out:
2015 		free_export(export);
2016 		release_root_op_context();
2017 		return -1;
2018 	}
2019 	
2020 	/**
2021 	 * @brief Read the export entries from the parsed configuration file.
2022 	 *
2023 	 * @param[in]  in_config    The file that contains the export list
2024 	 *
2025 	 * @return A negative value on error,
2026 	 *         the number of export entries else.
2027 	 */
2028 	
2029 	int ReadExports(config_file_t in_config,
2030 			struct config_error_type *err_type)
2031 	{
2032 		int rc, num_exp;
2033 	
2034 		rc = load_config_from_parse(in_config,
2035 					    &export_defaults_param,
2036 					    NULL,
2037 					    false,
2038 					    err_type);
2039 		if (rc < 0) {
2040 			LogCrit(COMPONENT_CONFIG, "Export defaults block error");
2041 			return -1;
2042 		}
2043 	
2044 		num_exp = load_config_from_parse(in_config,
2045 					    &export_param,
2046 					    NULL,
2047 					    false,
2048 					    err_type);
2049 		if (num_exp < 0) {
2050 			LogCrit(COMPONENT_CONFIG, "Export block error");
2051 			return -1;
2052 		}
2053 	
2054 		rc = build_default_root(err_type);
2055 		if (rc < 0) {
2056 			LogCrit(COMPONENT_CONFIG, "No pseudo root!");
2057 			return -1;
2058 		}
2059 	
2060 		return num_exp;
2061 	}
2062 	
2063 	/**
2064 	 * @brief Reread the export entries from the parsed configuration file.
2065 	 *
2066 	 * @param[in]  in_config    The file that contains the export list
2067 	 *
2068 	 * @return A negative value on error,
2069 	 *         the number of export entries else.
2070 	 */
2071 	
2072 	int reread_exports(config_file_t in_config,
2073 			   struct config_error_type *err_type)
2074 	{
2075 		int rc, num_exp;
2076 	
2077 		LogInfo(COMPONENT_CONFIG, "Reread exports");
2078 	
2079 		rc = load_config_from_parse(in_config,
2080 					    &export_defaults_param,
2081 					    NULL,
2082 					    false,
2083 					    err_type);
2084 	
2085 		if (rc < 0) {
2086 			LogCrit(COMPONENT_CONFIG, "Export defaults block error");
2087 			return -1;
2088 		}
2089 	
2090 		num_exp = load_config_from_parse(in_config,
2091 						 &update_export_param,
2092 						 NULL,
2093 						 false,
2094 						 err_type);
2095 	
2096 		if (num_exp < 0) {
2097 			LogCrit(COMPONENT_CONFIG, "Export block error");
2098 			return -1;
2099 		}
2100 	
2101 		prune_defunct_exports(get_config_generation(in_config));
2102 		return num_exp;
2103 	}
2104 	
2105 	static void FreeClientList(struct glist_head *clients)
2106 	{
2107 		struct glist_head *glist;
2108 		struct glist_head *glistn;
2109 	
2110 		glist_for_each_safe(glist, glistn, clients) {
2111 			exportlist_client_entry_t *client;
2112 	
2113 			client =
2114 			    glist_entry(glist, exportlist_client_entry_t, cle_list);
2115 			glist_del(&client->cle_list);
2116 			switch (client->type) {
2117 			case NETWORK_CLIENT:
2118 				if (client->client.network.cidr != NULL)
2119 					cidr_free(client->client.network.cidr);
2120 				break;
2121 			case NETGROUP_CLIENT:
2122 				gsh_free(client->client.netgroup.netgroupname);
2123 				break;
2124 			case WILDCARDHOST_CLIENT:
2125 				gsh_free(client->client.wildcard.wildcard);
2126 				break;
2127 			case GSSPRINCIPAL_CLIENT:
2128 				gsh_free(client->client.gssprinc.princname);
2129 				break;
2130 			case PROTO_CLIENT:
2131 			case MATCH_ANY_CLIENT:
2132 			case BAD_CLIENT:
2133 				/* Do nothing for these client types */
2134 				break;
2135 			}
2136 			gsh_free(client);
2137 		}
2138 	}
2139 	
2140 	/**
2141 	 * @brief Free resources attached to an export
2142 	 *
2143 	 * @param export [IN] pointer to export
2144 	 *
2145 	 * @return true if all went well
2146 	 */
2147 	
2148 	void free_export_resources(struct gsh_export *export)
2149 	{
2150 		FreeClientList(&export->clients);
2151 		if (export->fsal_export != NULL) {
2152 			struct fsal_module *fsal = export->fsal_export->fsal;
2153 	
2154 			export->fsal_export->exp_ops.release(export->fsal_export);
2155 			fsal_put(fsal);
2156 			LogFullDebug(COMPONENT_FSAL,
2157 				     "FSAL %s refcount %"PRIu32,
2158 				     fsal->name,
2159 				     atomic_fetch_int32_t(&fsal->refcount));
2160 		}
2161 		export->fsal_export = NULL;
2162 		/* free strings here */
2163 		if (export->fullpath != NULL)
2164 			gsh_free(export->fullpath);
2165 		if (export->pseudopath != NULL)
2166 			gsh_free(export->pseudopath);
2167 		if (export->FS_tag != NULL)
2168 			gsh_free(export->FS_tag);
2169 	}
2170 	
2171 	/**
2172 	 * @brief pkginit callback to initialize exports from nfs_init
2173 	 *
2174 	 * Assumes being called with the export_by_id.lock held.
2175 	 * true on success
2176 	 */
2177 	
2178 	static bool init_export_cb(struct gsh_export *exp, void *state)
2179 	{
2180 		struct glist_head *errlist = state;
2181 	
2182 		if (init_export_root(exp)) {
2183 			glist_del(&exp->exp_list);
2184 			glist_add(errlist, &exp->exp_list);
2185 		}
2186 	
2187 		return true;
2188 	}
2189 	
2190 	/**
2191 	 * @brief Initialize exports over a live cache inode and fsal layer
2192 	 */
2193 	
2194 	void exports_pkginit(void)
2195 	{
2196 		struct glist_head errlist;
2197 		struct glist_head *glist, *glistn;
2198 		struct gsh_export *export;
2199 	
2200 		glist_init(&errlist);
2201 		foreach_gsh_export(init_export_cb, true, &errlist);
2202 	
2203 		glist_for_each_safe(glist, glistn, &errlist) {
2204 			export = glist_entry(glist, struct gsh_export, exp_list);
2205 			export_revert(export);
2206 		}
2207 	}
2208 	
2209 	/**
2210 	 * @brief Return a reference to the root object of the export
2211 	 *
2212 	 * Must be called with the caller holding a reference to the export.
2213 	 *
2214 	 * Returns with an additional reference to the obj held for use by the
2215 	 * caller.
2216 	 *
2217 	 * @param export [IN] the aforementioned export
2218 	 * @param entry  [IN/OUT] call by ref pointer to store obj
2219 	 *
2220 	 * @return FSAL status
2221 	 */
2222 	
2223 	fsal_status_t nfs_export_get_root_entry(struct gsh_export *export,
2224 						struct fsal_obj_handle **obj)
2225 	{
2226 		PTHREAD_RWLOCK_rdlock(&export->lock);
2227 	
2228 		if (export->exp_root_obj)
2229 			export->exp_root_obj->obj_ops->get_ref(export->exp_root_obj);
2230 	
2231 		PTHREAD_RWLOCK_unlock(&export->lock);
2232 	
2233 		*obj = export->exp_root_obj;
2234 	
2235 		if (!(*obj))
2236 			return fsalstat(ERR_FSAL_NOENT, 0);
2237 	
2238 		if ((*obj)->type != DIRECTORY)
2239 			return fsalstat(ERR_FSAL_NOTDIR, 0);
2240 	
2241 		return fsalstat(ERR_FSAL_NO_ERROR, 0);
2242 	}
2243 	
2244 	/**
2245 	 * @brief Set file systems max read write sizes in the export
2246 	 *
2247 	 * @param export [IN] the export
2248 	 * @param maxread [IN] maxread size
2249 	 * @param maxwrite [IN] maxwrite size
2250 	 */
2251 	
2252 	static void set_fs_max_rdwr_size(struct gsh_export *export, uint64_t maxread,
2253 					 uint64_t maxwrite)
2254 	{
2255 		if (maxread != 0) {
2256 			if (!op_ctx_export_has_option_set(EXPORT_OPTION_MAXREAD_SET)) {
2257 				LogInfo(COMPONENT_EXPORT,
2258 					"Readjusting MaxRead to %" PRIu64,
2259 					maxread);
2260 				export->MaxRead = maxread;
2261 			}
2262 	
2263 			if (!op_ctx_export_has_option_set(EXPORT_OPTION_PREFREAD_SET) ||
2264 			    (export->PrefRead > export->MaxRead)) {
2265 				LogInfo(COMPONENT_EXPORT,
2266 					"Readjusting PrefRead to %"PRIu64,
2267 					export->MaxRead);
2268 				export->PrefRead = export->MaxRead;
2269 			}
2270 		}
2271 	
2272 		if (maxwrite != 0) {
2273 			if (!op_ctx_export_has_option_set(EXPORT_OPTION_MAXWRITE_SET)) {
2274 				LogInfo(COMPONENT_EXPORT,
2275 					"Readjusting MaxWrite to %"PRIu64,
2276 					maxwrite);
2277 				export->MaxWrite = maxwrite;
2278 			}
2279 	
2280 			if (!op_ctx_export_has_option_set(EXPORT_OPTION_PREFWRITE_SET)
2281 			    || (export->PrefWrite > export->MaxWrite)) {
2282 				LogInfo(COMPONENT_EXPORT,
2283 					"Readjusting PrefWrite to %"PRIu64,
2284 					export->MaxWrite);
2285 				export->PrefWrite = export->MaxWrite;
2286 			}
2287 		}
2288 	}
2289 	
2290 	/**
2291 	 * @brief Initialize the root cache inode for an export.
2292 	 *
2293 	 * Assumes being called with the export_by_id.lock held.
2294 	 *
2295 	 * @param exp [IN] the export
2296 	 *
2297 	 * @return 0 if successful otherwise err.
2298 	 */
2299 	
2300 	int init_export_root(struct gsh_export *export)
2301 	{
2302 		fsal_status_t fsal_status;
2303 		struct fsal_obj_handle *obj;
2304 		struct root_op_context root_op_context;
2305 		int my_status;
2306 	
2307 		/* Initialize req_ctx */
2308 		init_root_op_context(&root_op_context, export, export->fsal_export,
2309 				     0, 0, UNKNOWN_REQUEST);
2310 	
2311 		/* Lookup for the FSAL Path */
2312 		LogDebug(COMPONENT_EXPORT,
2313 			 "About to lookup_path for ExportId=%u Path=%s",
2314 			 export->export_id, export->fullpath);
2315 	
2316 		/* This takes a reference, which will keep the root object around for
2317 		 * the lifetime of the export. */
2318 		fsal_status =
2319 		    export->fsal_export->exp_ops.lookup_path(export->fsal_export,
2320 							  export->fullpath, &obj, NULL);
2321 	
2322 		if (FSAL_IS_ERROR(fsal_status)) {
2323 			my_status = EINVAL;
2324 	
2325 			LogCrit(COMPONENT_EXPORT,
2326 				"Lookup failed on path, ExportId=%u Path=%s FSAL_ERROR=(%s,%u)",
2327 				export->export_id, export->fullpath,
2328 				msg_fsal_err(fsal_status.major), fsal_status.minor);
2329 			goto out;
2330 		}
2331 	
2332 		if (!op_ctx_export_has_option_set(EXPORT_OPTION_MAXREAD_SET) ||
2333 		    !op_ctx_export_has_option_set(EXPORT_OPTION_MAXWRITE_SET) ||
2334 		    !op_ctx_export_has_option_set(EXPORT_OPTION_PREFREAD_SET) ||
2335 		    !op_ctx_export_has_option_set(EXPORT_OPTION_PREFWRITE_SET)) {
2336 	
2337 			fsal_dynamicfsinfo_t dynamicinfo;
2338 	
2339 			dynamicinfo.maxread = 0;
2340 			dynamicinfo.maxwrite = 0;
2341 			fsal_status =
2342 				export->fsal_export->exp_ops.get_fs_dynamic_info(
2343 					export->fsal_export, obj, &dynamicinfo);
2344 	
2345 			if (!FSAL_IS_ERROR(fsal_status)) {
2346 				set_fs_max_rdwr_size(export,
2347 						     dynamicinfo.maxread,
2348 						     dynamicinfo.maxwrite);
2349 			}
2350 		}
2351 	
2352 		PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock);
2353 		PTHREAD_RWLOCK_wrlock(&export->lock);
2354 	
2355 		/* Pass ref off to export */
2356 		export->exp_root_obj = obj;
2357 		glist_add_tail(&obj->state_hdl->dir.export_roots,
2358 			       &export->exp_root_list);
2359 		/* Protect this entry from removal (unlink) */
2360 		(void) atomic_inc_int32_t(&obj->state_hdl->dir.exp_root_refcount);
2361 	
2362 		PTHREAD_RWLOCK_unlock(&export->lock);
2363 		PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock);
2364 	
2365 		if (isDebug(COMPONENT_EXPORT)) {
2366 			LogDebug(COMPONENT_EXPORT,
2367 				 "Added root obj %p FSAL %s for path %s on export_id=%d",
2368 				 obj, obj->fsal->name, export->fullpath,
2369 				 export->export_id);
2370 		} else {
2371 			LogInfo(COMPONENT_EXPORT,
2372 				"Added root obj for path %s on export_id=%d",
2373 				export->fullpath, export->export_id);
2374 		}
2375 	
2376 		my_status = 0;
2377 	out:
2378 		release_root_op_context();
2379 		return my_status;
2380 	}
2381 	
2382 	/**
2383 	 * @brief Release all the export state, including the root object
2384 	 *
2385 	 * @param exp [IN] the export
2386 	 */
2387 	
2388 	static void release_export(struct gsh_export *export)
2389 	{
2390 		struct fsal_obj_handle *obj = NULL;
2391 		fsal_status_t fsal_status;
2392 	
2393 		/* Get a reference to the root entry */
2394 		fsal_status = nfs_export_get_root_entry(export, &obj);
2395 	
2396 		if (FSAL_IS_ERROR(fsal_status)) {
2397 			/* No more root entry, bail out, this export is
2398 			 * probably about to be destroyed.
2399 			 */
2400 			LogInfo(COMPONENT_CACHE_INODE,
2401 				"Export root for export id %d status %s",
2402 				export->export_id, msg_fsal_err(fsal_status.major));
2403 			return;
2404 		}
2405 	
2406 		/* Make the export unreachable as a root object */
2407 		PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock);
2408 		PTHREAD_RWLOCK_wrlock(&export->lock);
2409 	
2410 		glist_del(&export->exp_root_list);
2411 		export->exp_root_obj->obj_ops->put_ref(export->exp_root_obj);
2412 		export->exp_root_obj = NULL;
2413 	
2414 		(void) atomic_dec_int32_t(&obj->state_hdl->dir.exp_root_refcount);
2415 	
2416 		PTHREAD_RWLOCK_unlock(&export->lock);
2417 		PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock);
2418 	
2419 		LogDebug(COMPONENT_EXPORT,
2420 			 "Released root obj %p for path %s on export_id=%d",
2421 			 obj, export->fullpath, export->export_id);
2422 	
2423 		/* Make export unreachable via pseudo fs.
2424 		 * We keep the export in the export hash table through the following
2425 		 * so that the underlying FSALs have access to the export while
2426 		 * performing the various cleanup operations.
2427 		 */
2428 		pseudo_unmount_export(export);
2429 	
2430 		export->fsal_export->exp_ops.prepare_unexport(export->fsal_export);
2431 	
2432 		/* Release state belonging to this export */
2433 		state_release_export(export);
2434 	
2435 		/* Flush FSAL-specific state */
2436 		export->fsal_export->exp_ops.unexport(export->fsal_export, obj);
2437 	
2438 		/* Remove the mapping to the export now that cleanup is complete. */
2439 		remove_gsh_export(export->export_id);
2440 	
2441 		/* Release ref taken above */
2442 		obj->obj_ops->put_ref(obj);
2443 	}
2444 	
2445 	void unexport(struct gsh_export *export)
2446 	{
2447 		bool op_ctx_set = false;
2448 		struct root_op_context ctx;
2449 	
2450 		/* Make the export unreachable */
2451 		LogDebug(COMPONENT_EXPORT,
2452 			 "Unexport %s, Pseduo %s",
2453 			 export->fullpath, export->pseudopath);
2454 	
2455 		/* Lots of obj_ops may be called during cleanup; make sure that an
2456 		 * op_ctx exists */
2457 		if (!op_ctx) {
2458 			init_root_op_context(&ctx, export, export->fsal_export, 0, 0,
2459 					UNKNOWN_REQUEST);
2460 			op_ctx_set = true;
2461 		}
2462 	
2463 		release_export(export);
2464 	
2465 		if (op_ctx_set)
2466 			release_root_op_context();
2467 	}
2468 	
2469 	/**
2470 	 * @brief Match a specific option in the client export list
2471 	 *
2472 	 * @param[in]  hostaddr      Host to search for
2473 	 * @param[in]  clients       Client list to search
2474 	 * @param[out] client_found Matching entry
2475 	 * @param[in]  export_option Option to search for
2476 	 *
2477 	 * @return true if found, false otherwise.
2478 	 */
2479 	static exportlist_client_entry_t *client_match(sockaddr_t *hostaddr,
2480 						       struct gsh_export *export)
2481 	{
2482 		struct glist_head *glist;
2483 		int rc;
2484 		int ipvalid = -1;	/* -1 need to print, 0 - invalid, 1 - ok */
2485 		char hostname[MAXHOSTNAMELEN + 1];
2486 		char ipstring[SOCK_NAME_MAX + 1];
2487 		CIDR *host_prefix = NULL;
2488 		exportlist_client_entry_t *client;
2489 	
2490 		glist_for_each(glist, &export->clients) {
2491 			client = glist_entry(glist, exportlist_client_entry_t,
2492 					     cle_list);
2493 			LogClientListEntry(NIV_MID_DEBUG,
2494 					   COMPONENT_EXPORT,
2495 					   __LINE__,
2496 					   (char *) __func__,
2497 					   "Match V4: ",
2498 					   client);
2499 	
2500 			switch (client->type) {
2501 			case NETWORK_CLIENT:
2502 				if (host_prefix == NULL) {
2503 					if (hostaddr->ss_family == AF_INET6) {
2504 						host_prefix = cidr_from_in6addr(
2505 							&((struct sockaddr_in6 *)
2506 								hostaddr)->sin6_addr);
2507 					} else {
2508 						host_prefix = cidr_from_inaddr(
2509 							&((struct sockaddr_in *)
2510 								hostaddr)->sin_addr);
2511 					}
2512 				}
2513 	
2514 				if (cidr_contains(client->client.network.cidr,
2515 						  host_prefix) == 0) {
2516 					goto out;
2517 				}
2518 				break;
2519 	
2520 			case NETGROUP_CLIENT:
2521 				/* Try to get the entry from th IP/name cache */
2522 				rc = nfs_ip_name_get(hostaddr, hostname,
2523 						     sizeof(hostname));
2524 	
2525 				if (rc == IP_NAME_NOT_FOUND) {
2526 					/* IPaddr was not cached, add it to the cache */
2527 					rc = nfs_ip_name_add(hostaddr,
2528 							     hostname,
2529 							     sizeof(hostname));
2530 				}
2531 	
2532 				if (rc != IP_NAME_SUCCESS)
2533 					break; /* Fatal failure */
2534 	
2535 				/* At this point 'hostname' should contain the
2536 				 * name that was found
2537 				 */
2538 				if (ng_innetgr(client->client.netgroup.netgroupname,
2539 					    hostname)) {
2540 					goto out;
2541 				}
2542 				break;
2543 	
2544 			case WILDCARDHOST_CLIENT:
2545 				/* Now checking for IP wildcards */
2546 				if (ipvalid < 0)
2547 					ipvalid = sprint_sockip(hostaddr,
2548 								ipstring,
2549 								sizeof(ipstring));
2550 	
2551 				if (ipvalid &&
2552 				    (fnmatch(client->client.wildcard.wildcard,
2553 					     ipstring,
2554 					     FNM_PATHNAME) == 0)) {
2555 					goto out;
2556 				}
2557 	
2558 				/* Try to get the entry from th IP/name cache */
2559 				rc = nfs_ip_name_get(hostaddr, hostname,
2560 						     sizeof(hostname));
2561 	
2562 				if (rc == IP_NAME_NOT_FOUND) {
2563 					/* IPaddr was not cached, add it to the cache */
2564 	
2565 					/** @todo this change from 1.5 is not IPv6
2566 					 * useful.  come back to this and use the
2567 					 * string from client mgr inside req_ctx...
2568 					 */
2569 					rc = nfs_ip_name_add(hostaddr,
2570 							     hostname,
2571 							     sizeof(hostname));
2572 				}
2573 	
2574 				if (rc != IP_NAME_SUCCESS)
2575 					break;
2576 	
2577 				/* At this point 'hostname' should contain the
2578 				 * name that was found
2579 				 */
2580 				if (fnmatch
2581 				    (client->client.wildcard.wildcard, hostname,
2582 				     FNM_PATHNAME) == 0) {
2583 					goto out;
2584 				}
2585 				break;
2586 	
2587 			case GSSPRINCIPAL_CLIENT:
2588 		  /** @todo BUGAZOMEU a completer lors de l'integration de RPCSEC_GSS */
2589 				LogCrit(COMPONENT_EXPORT,
2590 					"Unsupported type GSS_PRINCIPAL_CLIENT");
2591 				break;
2592 	
2593 			case MATCH_ANY_CLIENT:
2594 				goto out;
2595 	
2596 			case BAD_CLIENT:
2597 			default:
2598 				continue;
2599 			}
2600 		}
2601 	
2602 		client = NULL;
2603 	
2604 	out:
2605 	
2606 		if (host_prefix != NULL)
2607 			cidr_free(host_prefix);
2608 	
2609 		/* no export found for this option */
2610 		return client;
2611 	
2612 	}
2613 	
2614 	/**
2615 	 * @brief Checks if request security flavor is suffcient for the requested
2616 	 *        export
2617 	 *
2618 	 * @param[in] req     Related RPC request.
2619 	 *
2620 	 * @return true if the request flavor exists in the matching export
2621 	 * false otherwise
2622 	 */
2623 	bool export_check_security(struct svc_req *req)
2624 	{
2625 		switch (req->rq_msg.cb_cred.oa_flavor) {
2626 		case AUTH_NONE:
2627 			if ((op_ctx->export_perms->options &
2628 			     EXPORT_OPTION_AUTH_NONE) == 0) {
2629 				LogInfo(COMPONENT_EXPORT,
2630 					"Export %s does not support AUTH_NONE",
2631 					op_ctx_export_path(op_ctx->ctx_export));
2632 				return false;
2633 			}
2634 			break;
2635 	
2636 		case AUTH_UNIX:
2637 			if ((op_ctx->export_perms->options &
2638 			     EXPORT_OPTION_AUTH_UNIX) == 0) {
2639 				LogInfo(COMPONENT_EXPORT,
2640 					"Export %s does not support AUTH_UNIX",
2641 					op_ctx_export_path(op_ctx->ctx_export));
2642 				return false;
2643 			}
2644 			break;
2645 	
2646 	#ifdef _HAVE_GSSAPI
2647 		case RPCSEC_GSS:
2648 			if ((op_ctx->export_perms->options &
2649 					(EXPORT_OPTION_RPCSEC_GSS_NONE |
2650 					 EXPORT_OPTION_RPCSEC_GSS_INTG |
2651 					 EXPORT_OPTION_RPCSEC_GSS_PRIV)) == 0) {
2652 				LogInfo(COMPONENT_EXPORT,
2653 					"Export %s does not support RPCSEC_GSS",
2654 					op_ctx_export_path(op_ctx->ctx_export));
2655 				return false;
2656 			} else {
2657 				struct rpc_gss_cred *gc = (struct rpc_gss_cred *)
2658 					req->rq_msg.rq_cred_body;
2659 				rpc_gss_svc_t svc = gc->gc_svc;
2660 	
2661 				LogFullDebug(COMPONENT_EXPORT, "Testing svc %d",
2662 					     (int)svc);
2663 				switch (svc) {
2664 				case RPCSEC_GSS_SVC_NONE:
2665 					if ((op_ctx->export_perms->options &
2666 					     EXPORT_OPTION_RPCSEC_GSS_NONE) == 0) {
2667 						LogInfo(COMPONENT_EXPORT,
2668 							"Export %s does not support RPCSEC_GSS_SVC_NONE",
2669 							op_ctx_export_path(
2670 								op_ctx->ctx_export));
2671 						return false;
2672 					}
2673 					break;
2674 	
2675 				case RPCSEC_GSS_SVC_INTEGRITY:
2676 					if ((op_ctx->export_perms->options &
2677 					     EXPORT_OPTION_RPCSEC_GSS_INTG) == 0) {
2678 						LogInfo(COMPONENT_EXPORT,
2679 							"Export %s does not support RPCSEC_GSS_SVC_INTEGRITY",
2680 							op_ctx_export_path(
2681 								op_ctx->ctx_export));
2682 						return false;
2683 					}
2684 					break;
2685 	
2686 				case RPCSEC_GSS_SVC_PRIVACY:
2687 					if ((op_ctx->export_perms->options &
2688 					     EXPORT_OPTION_RPCSEC_GSS_PRIV) == 0) {
2689 						LogInfo(COMPONENT_EXPORT,
2690 							"Export %s does not support RPCSEC_GSS_SVC_PRIVACY",
2691 							op_ctx_export_path(
2692 								op_ctx->ctx_export));
2693 						return false;
2694 					}
2695 					break;
2696 	
2697 				default:
2698 					LogInfo(COMPONENT_EXPORT,
2699 						"Export %s does not support unknown RPCSEC_GSS_SVC %d",
2700 						op_ctx_export_path(op_ctx->ctx_export),
2701 						(int)svc);
2702 					return false;
2703 				}
2704 			}
2705 			break;
2706 	#endif
2707 		default:
2708 			LogInfo(COMPONENT_EXPORT,
2709 				"Export %s does not support unknown oa_flavor %d",
2710 				op_ctx_export_path(op_ctx->ctx_export),
2711 				(int)req->rq_msg.cb_cred.oa_flavor);
2712 			return false;
2713 		}
2714 	
2715 		return true;
2716 	}
2717 	
2718 	static char ten_bytes_all_0[10];
2719 	
2720 	sockaddr_t *convert_ipv6_to_ipv4(sockaddr_t *ipv6, sockaddr_t *ipv4)
2721 	{
2722 		struct sockaddr_in *paddr = (struct sockaddr_in *)ipv4;
2723 		struct sockaddr_in6 *psockaddr_in6 = (struct sockaddr_in6 *)ipv6;
2724 	
2725 		/* If the client socket is IPv4, then it is wrapped into a
2726 		 * ::ffff:a.b.c.d IPv6 address. We check this here.
2727 		 * This kind of adress is shaped like this:
2728 		 * |---------------------------------------------------------------|
2729 		 * |   80 bits = 10 bytes  | 16 bits = 2 bytes | 32 bits = 4 bytes |
2730 		 * |---------------------------------------------------------------|
2731 		 * |            0          |        FFFF       |    IPv4 address   |
2732 		 * |---------------------------------------------------------------|
2733 		 */
2734 		if ((ipv6->ss_family == AF_INET6)
2735 		    && !memcmp(psockaddr_in6->sin6_addr.s6_addr, ten_bytes_all_0, 10)
2736 		    && (psockaddr_in6->sin6_addr.s6_addr[10] == 0xFF)
2737 		    && (psockaddr_in6->sin6_addr.s6_addr[11] == 0xFF)) {
2738 			void *ab;
2739 	
2740 			memset(ipv4, 0, sizeof(*ipv4));
2741 			ab = &(psockaddr_in6->sin6_addr.s6_addr[12]);
2742 	
2743 			paddr->sin_port = psockaddr_in6->sin6_port;
2744 			paddr->sin_addr.s_addr = *(in_addr_t *) ab;
2745 			ipv4->ss_family = AF_INET;
2746 	
2747 			if (isFullDebug(COMPONENT_EXPORT)) {
2748 				char ipstring4[SOCK_NAME_MAX];
2749 				char ipstring6[SOCK_NAME_MAX];
2750 	
2751 				sprint_sockip(ipv6, ipstring6, sizeof(ipstring6));
2752 				sprint_sockip(ipv4, ipstring4, sizeof(ipstring4));
2753 				LogMidDebug(COMPONENT_EXPORT,
2754 					    "Converting IPv6 encapsulated IPv4 address %s to IPv4 %s",
2755 					    ipstring6, ipstring4);
2756 			}
2757 	
2758 			return ipv4;
2759 		} else {
2760 			return ipv6;
2761 		}
2762 	}
2763 	
2764 	/**
2765 	 * @brief Get the best anonymous uid available.
2766 	 *
2767 	 * This is safe if there is no op_ctx or there is one but there is no
2768 	 * export_perms attached.
2769 	 *
2770 	 */
2771 	
2772 	uid_t get_anonymous_uid(void)
2773 	{
2774 		uid_t anon_uid;
2775 	
2776 		if (op_ctx != NULL &&  op_ctx->export_perms != NULL) {
2777 			/* We have export_perms, use it. */
2778 			return op_ctx->export_perms->anonymous_uid;
2779 		}
2780 	
2781 		PTHREAD_RWLOCK_rdlock(&export_opt_lock);
2782 	
2783 		if ((export_opt.conf.set & EXPORT_OPTION_ANON_UID_SET) != 0) {
2784 			/* Option was set in EXPORT_DEFAULTS */
2785 			anon_uid = export_opt.conf.anonymous_uid;
2786 		} else {
2787 			/* Default to code default. */
2788 			anon_uid = export_opt.def.anonymous_uid;
2789 		}
2790 	
2791 		PTHREAD_RWLOCK_unlock(&export_opt_lock);
2792 	
2793 		return anon_uid;
2794 	}
2795 	
2796 	/**
2797 	 * @brief Get the best anonymous gid available.
2798 	 *
2799 	 * This is safe if there is no op_ctx or there is one but there is no
2800 	 * export_perms attached.
2801 	 *
2802 	 */
2803 	
2804 	gid_t get_anonymous_gid(void)
2805 	{
2806 		/* Default to code default. */
2807 		gid_t anon_gid = export_opt.def.anonymous_gid;
2808 	
2809 		if (op_ctx != NULL &&  op_ctx->export_perms != NULL) {
2810 			/* We have export_perms, use it. */
2811 			return op_ctx->export_perms->anonymous_gid;
2812 		}
2813 	
2814 		PTHREAD_RWLOCK_rdlock(&export_opt_lock);
2815 	
2816 		if ((export_opt.conf.set & EXPORT_OPTION_ANON_GID_SET) != 0) {
2817 			/* Option was set in EXPORT_DEFAULTS */
2818 			anon_gid = export_opt.conf.anonymous_gid;
2819 		} else {
2820 			/* Default to code default. */
2821 			anon_gid = export_opt.def.anonymous_gid;
2822 		}
2823 	
2824 		PTHREAD_RWLOCK_unlock(&export_opt_lock);
2825 	
2826 		return anon_gid;
2827 	}
2828 	
2829 	/**
2830 	 * @brief Checks if a machine is authorized to access an export entry
2831 	 *
2832 	 * Permissions in the op context get updated based on export and client.
2833 	 *
2834 	 * Takes the export->lock in read mode to protect the client list and
2835 	 * export permissions while performing this work.
2836 	 */
2837 	
2838 	void export_check_access(void)
2839 	{
2840 		exportlist_client_entry_t *client = NULL;
2841 		sockaddr_t alt_hostaddr;
2842 		sockaddr_t *hostaddr = NULL;
2843 	
2844 		assert(op_ctx != NULL);
2845 		assert(op_ctx->export_perms != NULL);
2846 	
2847 		/* Initialize permissions to allow nothing, anonymous_uid and
2848 		 * anonymous_gid will get set farther down.
2849 		 */
2850 		memset(op_ctx->export_perms, 0, sizeof(*op_ctx->export_perms));
2851 	
2852 		if (op_ctx->ctx_export != NULL) {
2853 			/* Take lock */
2854 			PTHREAD_RWLOCK_rdlock(&op_ctx->ctx_export->lock);
2855 		} else {
2856 			/* Shortcut if no export */
2857 			goto no_export;
2858 		}
2859 	
2860 		hostaddr = convert_ipv6_to_ipv4(op_ctx->caller_addr, &alt_hostaddr);
2861 	
2862 		if (isMidDebug(COMPONENT_EXPORT)) {
2863 			char ipstring[SOCK_NAME_MAX];
2864 	
2865 			ipstring[0] = '\0';
2866 			(void) sprint_sockip(hostaddr,
2867 					     ipstring, sizeof(ipstring));
2868 			LogMidDebug(COMPONENT_EXPORT,
2869 				    "Check for address %s for export id %u path %s",
2870 				    ipstring, op_ctx->ctx_export->export_id,
2871 				    op_ctx_export_path(op_ctx->ctx_export));
2872 		}
2873 	
2874 		/* Does the client match anyone on the client list? */
2875 		client = client_match(hostaddr, op_ctx->ctx_export);
2876 		if (client != NULL) {
2877 			/* Take client options */
2878 			op_ctx->export_perms->options = client->client_perms.options &
2879 							 client->client_perms.set;
2880 	
2881 			if (client->client_perms.set & EXPORT_OPTION_ANON_UID_SET)
2882 				op_ctx->export_perms->anonymous_uid =
2883 						client->client_perms.anonymous_uid;
2884 	
2885 			if (client->client_perms.set & EXPORT_OPTION_ANON_GID_SET)
2886 				op_ctx->export_perms->anonymous_gid =
2887 						client->client_perms.anonymous_gid;
2888 	
2889 			op_ctx->export_perms->set = client->client_perms.set;
2890 		}
2891 	
2892 		/* Any options not set by the client, take from the export */
2893 		op_ctx->export_perms->options |=
2894 					op_ctx->ctx_export->export_perms.options &
2895 					op_ctx->ctx_export->export_perms.set &
2896 					~op_ctx->export_perms->set;
2897 	
2898 		if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_UID_SET) == 0 &&
2899 		    (op_ctx->ctx_export->export_perms.set &
2900 		     EXPORT_OPTION_ANON_UID_SET) != 0)
2901 			op_ctx->export_perms->anonymous_uid =
2902 				op_ctx->ctx_export->export_perms.anonymous_uid;
2903 	
2904 		if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_GID_SET) == 0 &&
2905 		    (op_ctx->ctx_export->export_perms.set &
2906 		     EXPORT_OPTION_ANON_GID_SET) != 0)
2907 			op_ctx->export_perms->anonymous_gid =
2908 				op_ctx->ctx_export->export_perms.anonymous_gid;
2909 	
2910 		if ((op_ctx->export_perms->set & EXPORT_OPTION_EXPIRE_SET) == 0 &&
2911 		    (op_ctx->ctx_export->export_perms.set &
2912 		     EXPORT_OPTION_EXPIRE_SET) != 0)
2913 			op_ctx->export_perms->expire_time_attr =
2914 				op_ctx->ctx_export->export_perms.expire_time_attr;
2915 	
2916 		op_ctx->export_perms->set |= op_ctx->ctx_export->export_perms.set;
2917 	
2918 	 no_export:
2919 	
2920 		PTHREAD_RWLOCK_rdlock(&export_opt_lock);
2921 	
2922 		/* Any options not set by the client or export, take from the
2923 		 *  EXPORT_DEFAULTS block.
2924 		 */
2925 		op_ctx->export_perms->options |= export_opt.conf.options &
2926 						  export_opt.conf.set &
2927 						  ~op_ctx->export_perms->set;
2928 	
2929 		if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_UID_SET) == 0 &&
2930 		    (export_opt.conf.set & EXPORT_OPTION_ANON_UID_SET) != 0)
2931 			op_ctx->export_perms->anonymous_uid =
2932 						export_opt.conf.anonymous_uid;
2933 	
2934 		if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_GID_SET) == 0 &&
2935 		    (export_opt.conf.set & EXPORT_OPTION_ANON_GID_SET) != 0)
2936 			op_ctx->export_perms->anonymous_gid =
2937 						export_opt.conf.anonymous_gid;
2938 	
2939 		if ((op_ctx->export_perms->set & EXPORT_OPTION_EXPIRE_SET) == 0 &&
2940 		    (export_opt.conf.set & EXPORT_OPTION_EXPIRE_SET) != 0)
2941 			op_ctx->export_perms->expire_time_attr =
2942 				export_opt.conf.expire_time_attr;
2943 	
2944 		op_ctx->export_perms->set |= export_opt.conf.set;
2945 	
2946 		/* And finally take any options not yet set from global defaults */
2947 		op_ctx->export_perms->options |= export_opt.def.options &
2948 						  ~op_ctx->export_perms->set;
2949 	
2950 		if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_UID_SET) == 0)
2951 			op_ctx->export_perms->anonymous_uid =
2952 						export_opt.def.anonymous_uid;
2953 	
2954 		if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_GID_SET) == 0)
2955 			op_ctx->export_perms->anonymous_gid =
2956 						export_opt.def.anonymous_gid;
2957 	
2958 		if ((op_ctx->export_perms->set & EXPORT_OPTION_EXPIRE_SET) == 0)
2959 			op_ctx->export_perms->expire_time_attr =
2960 						export_opt.def.expire_time_attr;
2961 	
2962 		op_ctx->export_perms->set |= export_opt.def.set;
2963 	
2964 		if (isMidDebug(COMPONENT_EXPORT)) {
2965 			char perms[1024] = "\0";
2966 			struct display_buffer dspbuf = {sizeof(perms), perms, perms};
2967 	
2968 			if (client != NULL) {
2969 				(void) StrExportOptions(&dspbuf, &client->client_perms);
2970 				LogMidDebug(COMPONENT_EXPORT,
2971 					    "CLIENT          (%s)",
2972 					    perms);
2973 				display_reset_buffer(&dspbuf);
2974 			}
2975 	
2976 			if (op_ctx->ctx_export != NULL) {
2977 				(void) StrExportOptions(
2978 					&dspbuf, &op_ctx->ctx_export->export_perms);
2979 				LogMidDebug(COMPONENT_EXPORT,
2980 					    "EXPORT          (%s)",
2981 					    perms);
2982 				display_reset_buffer(&dspbuf);
2983 			}
2984 	
2985 			(void) StrExportOptions(&dspbuf, &export_opt.conf);
2986 			LogMidDebug(COMPONENT_EXPORT,
2987 				    "EXPORT_DEFAULTS (%s)",
2988 				    perms);
2989 			display_reset_buffer(&dspbuf);
2990 	
2991 			(void) StrExportOptions(&dspbuf, &export_opt.def);
2992 			LogMidDebug(COMPONENT_EXPORT,
2993 				    "default options (%s)",
2994 				    perms);
2995 			display_reset_buffer(&dspbuf);
2996 	
2997 			(void) StrExportOptions(&dspbuf, op_ctx->export_perms);
2998 			LogMidDebug(COMPONENT_EXPORT,
2999 				    "Final options   (%s)",
3000 				    perms);
3001 		}
3002 	
3003 		PTHREAD_RWLOCK_unlock(&export_opt_lock);
3004 	
3005 		if (op_ctx->ctx_export != NULL) {
3006 			/* Release lock */
3007 			PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock);
3008 		}
3009 	}
3010