xref: /illumos-gate/usr/src/lib/libshare/common/libsharecore.c (revision 1a7b528f36ec61147fc237b7b9a23642fab7ad13)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * core library for common functions across all config store types
31  * and file systems to be exported. This includes legacy dfstab/sharetab
32  * parsing. Need to eliminate XML where possible.
33  */
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <unistd.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <libxml/parser.h>
44 #include <libxml/tree.h>
45 #include "libshare.h"
46 #include "libshare_impl.h"
47 #include <fcntl.h>
48 #include <sys/stat.h>
49 #include <grp.h>
50 #include <limits.h>
51 #include <sys/param.h>
52 #include <signal.h>
53 #include <libintl.h>
54 
55 #include <sharefs/share.h>
56 #include "sharetab.h"
57 
58 #define	DFSTAB_NOTICE_LINES	5
59 static char *notice[DFSTAB_NOTICE_LINES] =	{
60 	"# Do not modify this file directly.\n",
61 	"# Use the sharemgr(1m) command for all share management\n",
62 	"# This file is reconstructed and only maintained for backward\n",
63 	"# compatibility. Configuration lines could be lost.\n",
64 	"#\n"
65 };
66 
67 #define	STRNCAT(x, y, z)	(xmlChar *)strncat((char *)x, (char *)y, z)
68 
69 /* will be much smaller, but this handles bad syntax in the file */
70 #define	MAXARGSFORSHARE	256
71 
72 /* used internally only */
73 typedef
74 struct sharelist {
75     struct sharelist *next;
76     int   persist;
77     char *path;
78     char *resource;
79     char *fstype;
80     char *options;
81     char *description;
82     char *group;
83     char *origline;
84     int lineno;
85 } xfs_sharelist_t;
86 static void parse_dfstab(sa_handle_t, char *, xmlNodePtr);
87 extern char *get_token(char *);
88 static void dfs_free_list(xfs_sharelist_t *);
89 /* prototypes */
90 void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
91 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
92 extern sa_group_t _sa_create_group(sa_handle_impl_t, char *);
93 static void outdfstab(FILE *, xfs_sharelist_t *);
94 extern int _sa_remove_optionset(sa_optionset_t);
95 extern int set_node_share(void *, char *, char *);
96 extern void set_node_attr(void *, char *, char *);
97 
98 /*
99  * sablocksigs(*sigs)
100  *
101  * block important signals for a critical region. Arg is a pointer to
102  * a sigset_t that is used later for the unblock.
103  */
104 void
105 sablocksigs(sigset_t *sigs)
106 {
107 	sigset_t new;
108 
109 	if (sigs != NULL) {
110 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
111 		(void) sigaddset(&new, SIGHUP);
112 		(void) sigaddset(&new, SIGINT);
113 		(void) sigaddset(&new, SIGQUIT);
114 		(void) sigaddset(&new, SIGTSTP);
115 		(void) sigprocmask(SIG_SETMASK, &new, sigs);
116 	}
117 }
118 
119 /*
120  * saunblocksigs(*sigs)
121  *
122  * unblock previously blocked signals from the sigs arg.
123  */
124 void
125 saunblocksigs(sigset_t *sigs)
126 {
127 	if (sigs != NULL)
128 		(void) sigprocmask(SIG_SETMASK, sigs, NULL);
129 }
130 
131 /*
132  * alloc_sharelist()
133  *
134  * allocator function to return an zfs_sharelist_t
135  */
136 
137 static xfs_sharelist_t *
138 alloc_sharelist()
139 {
140 	xfs_sharelist_t *item;
141 
142 	item = (xfs_sharelist_t *)malloc(sizeof (xfs_sharelist_t));
143 	if (item != NULL)
144 		(void) memset(item, '\0', sizeof (xfs_sharelist_t));
145 	return (item);
146 }
147 
148 /*
149  * fix_notice(list)
150  *
151  * Look at the beginning of the current /etc/dfs/dfstab file and add
152  * the do not modify notice if it doesn't exist.
153  */
154 
155 static xfs_sharelist_t *
156 fix_notice(xfs_sharelist_t *list)
157 {
158 	xfs_sharelist_t *item, *prev;
159 	int i;
160 
161 	if (list == NULL) {
162 		/* zero length dfstab */
163 		list = alloc_sharelist();
164 		if (list == NULL)
165 			return (NULL);
166 		list->description = strdup("#\n");
167 	}
168 	if (list->path == NULL && list->description != NULL &&
169 	    strcmp(list->description, notice[0]) != 0) {
170 		for (prev = NULL, i = 0; i < DFSTAB_NOTICE_LINES; i++) {
171 			item = alloc_sharelist();
172 			if (item != NULL) {
173 				item->description = strdup(notice[i]);
174 				if (prev == NULL) {
175 					item->next = list;
176 					prev = item;
177 					list = item;
178 				} else {
179 					item->next = prev->next;
180 					prev->next = item;
181 					prev = item;
182 				}
183 			}
184 		}
185 	}
186 	return (list);
187 }
188 
189 /*
190  * getdfstab(dfs)
191  *
192  * Returns an zfs_sharelist_t list of lines from the dfstab file
193  * pointed to by the FILE pointer dfs. Each entry is parsed and the
194  * original line is also preserved. Used in parsing and updating the
195  * dfstab file.
196  */
197 
198 static xfs_sharelist_t *
199 getdfstab(FILE *dfs)
200 {
201 	char buff[_POSIX_ARG_MAX]; /* reasonable size given syntax of share */
202 	char *bp;
203 	char *token;
204 	char *args[MAXARGSFORSHARE];
205 	int argc;
206 	int c;
207 	static int line = 0;
208 	xfs_sharelist_t *item = NULL, *first = NULL, *last;
209 
210 	if (dfs != NULL) {
211 		first = NULL;
212 		line = 0;
213 		while (fgets(buff, sizeof (buff), dfs) != NULL) {
214 			line++;
215 			bp = buff;
216 			if (buff[0] == '#') {
217 				item = alloc_sharelist();
218 				if (item != NULL) {
219 					/* if no path, then comment */
220 					item->lineno = line;
221 					item->description = strdup(buff);
222 					if (first == NULL) {
223 						first = item;
224 						last = item;
225 					} else {
226 						last->next = item;
227 						last = item;
228 					}
229 				} else {
230 					break;
231 				}
232 				continue;
233 			} else if (buff[0] == '\n') {
234 				continue;
235 			}
236 			optind = 1;
237 			item = alloc_sharelist();
238 			if (item == NULL) {
239 				break;
240 			} else if (first == NULL) {
241 				first = item;
242 				last = item;
243 			} else {
244 				last->next = item;
245 				last = item;
246 			}
247 			item->lineno = line;
248 			item->origline = strdup(buff);
249 			(void) get_token(NULL); /* reset to new pointers */
250 			argc = 0;
251 			while ((token = get_token(bp)) != NULL) {
252 				if (argc < MAXARGSFORSHARE)
253 					args[argc++] = token;
254 			}
255 			while ((c = getopt(argc, args, "F:o:d:pg:")) != -1) {
256 				switch (c) {
257 				case 'p':
258 					item->persist = 1;
259 					break;
260 				case 'F':
261 					item->fstype = strdup(optarg);
262 					break;
263 				case 'o':
264 					item->options = strdup(optarg);
265 					break;
266 				case 'd':
267 					item->description = strdup(optarg);
268 					break;
269 				case 'g':
270 					item->group = strdup(optarg);
271 					break;
272 				default:
273 					break;
274 				}
275 			}
276 			if (optind < argc) {
277 				item->path = strdup(args[optind]);
278 				optind++;
279 				if (optind < argc) {
280 					char *resource;
281 					char *optgroup;
282 					/* resource and/or groupname */
283 					resource = args[optind];
284 					optgroup = strchr(resource, '@');
285 					if (optgroup != NULL)
286 						*optgroup++ = '\0';
287 					if (optgroup != NULL)
288 						item->group = strdup(optgroup);
289 					if (resource != NULL &&
290 					    strlen(resource) > 0)
291 						item->resource =
292 						    strdup(resource);
293 				}
294 			}
295 			/* NFS is the default if none defined */
296 			if (item != NULL && item->fstype == NULL)
297 				item->fstype = strdup("nfs");
298 		}
299 	}
300 	first = fix_notice(first);
301 	return (first);
302 }
303 
304 /*
305  * finddfsentry(list, path)
306  *
307  * Look for path in the zfs_sharelist_t list and return the entry if it
308  * exists.
309  */
310 
311 static xfs_sharelist_t *
312 finddfsentry(xfs_sharelist_t *list, char *path)
313 {
314 	xfs_sharelist_t *item;
315 
316 	for (item = list; item != NULL; item = item->next) {
317 		if (item->path != NULL && strcmp(item->path, path) == 0)
318 		return (item);
319 	}
320 	return (NULL);
321 }
322 
323 /*
324  * remdfsentry(list, path, proto)
325  *
326  * Remove the specified path (with protocol) from the list. This will
327  * remove it from dfstab when the file is rewritten.
328  */
329 
330 static xfs_sharelist_t *
331 remdfsentry(xfs_sharelist_t *list, char *path, char *proto)
332 {
333 	xfs_sharelist_t *item, *prev = NULL;
334 
335 
336 	for (item = prev = list; item != NULL; item = item->next) {
337 	    /* skip comment entry but don't lose it */
338 		if (item->path == NULL) {
339 			prev = item;
340 			continue;
341 		}
342 		/* if proto is NULL, remove all protocols */
343 		if (proto == NULL || (strcmp(item->path, path) == 0 &&
344 		    (item->fstype != NULL && strcmp(item->fstype, proto) == 0)))
345 			break;
346 		if (item->fstype == NULL &&
347 		    (proto == NULL || strcmp(proto, "nfs") == 0))
348 			break;
349 		prev = item;
350 	}
351 	if (item != NULL) {
352 		if (item == prev)
353 			list = item->next; /* this must be the first one */
354 		else
355 			prev->next = item->next;
356 		item->next = NULL;
357 		dfs_free_list(item);
358 	}
359 	return (list);
360 }
361 
362 /*
363  * remdfsline(list, line)
364  *
365  * Remove the line specified from the list.
366  */
367 
368 static xfs_sharelist_t *
369 remdfsline(xfs_sharelist_t *list, char *line)
370 {
371 	xfs_sharelist_t *item, *prev = NULL;
372 
373 	for (item = prev = list; item != NULL; item = item->next) {
374 		/* skip comment entry but don't lose it */
375 		if (item->path == NULL) {
376 		prev = item;
377 		continue;
378 		}
379 		if (strcmp(item->origline, line) == 0)
380 			break;
381 		prev = item;
382 	}
383 	if (item != NULL) {
384 		if (item == prev)
385 			list = item->next; /* this must be the first one */
386 		else
387 			prev->next = item->next;
388 		item->next = NULL;
389 		dfs_free_list(item);
390 	}
391 	return (list);
392 }
393 
394 /*
395  * adddfsentry(list, share, proto)
396  *
397  * Add an entry to the dfstab list for share (relative to proto). This
398  * is used to update dfstab for legacy purposes.
399  */
400 
401 static xfs_sharelist_t *
402 adddfsentry(xfs_sharelist_t *list, sa_share_t share, char *proto)
403 {
404 	xfs_sharelist_t *item, *tmp;
405 	sa_group_t parent;
406 	char *groupname;
407 
408 	item = alloc_sharelist();
409 	if (item != NULL) {
410 		parent = sa_get_parent_group(share);
411 		groupname = sa_get_group_attr(parent, "name");
412 		if (strcmp(groupname, "default") == 0) {
413 			sa_free_attr_string(groupname);
414 			groupname = NULL;
415 		}
416 		item->path = sa_get_share_attr(share, "path");
417 		item->resource = sa_get_share_attr(share, "resource");
418 		item->group = groupname;
419 		item->fstype = strdup(proto);
420 		item->options = sa_proto_legacy_format(proto, share, 1);
421 		if (item->options != NULL && strlen(item->options) == 0) {
422 			free(item->options);
423 			item->options = NULL;
424 		}
425 		item->description = sa_get_share_description(share);
426 		if (item->description != NULL &&
427 		    strlen(item->description) == 0) {
428 			sa_free_share_description(item->description);
429 			item->description = NULL;
430 		}
431 		if (list == NULL) {
432 			list = item;
433 		} else {
434 			for (tmp = list; tmp->next != NULL; tmp = tmp->next)
435 				/* do nothing */;
436 				tmp->next = item;
437 		}
438 	}
439 	return (list);
440 }
441 
442 /*
443  * outdfstab(dfstab, list)
444  *
445  * Output the list to dfstab making sure the file is truncated.
446  * Comments and errors are preserved.
447  */
448 
449 static void
450 outdfstab(FILE *dfstab, xfs_sharelist_t *list)
451 {
452 	xfs_sharelist_t *item;
453 
454 	(void) ftruncate(fileno(dfstab), 0);
455 
456 	for (item = list; item != NULL; item = item->next) {
457 		if (item->path != NULL) {
458 			if (*item->path == '/') {
459 				(void) fprintf(dfstab,
460 				    "share %s%s%s%s%s%s%s %s%s%s%s%s\n",
461 				    (item->fstype != NULL) ? "-F " : "",
462 				    (item->fstype != NULL) ? item->fstype : "",
463 				    (item->options != NULL) ? " -o " : "",
464 				    (item->options != NULL) ?
465 				    item->options : "",
466 				    (item->description != NULL) ?
467 				    " -d \"" : "",
468 				    (item->description != NULL) ?
469 				    item->description : "",
470 				    (item->description != NULL) ? "\"" : "",
471 				    item->path,
472 				    ((item->resource != NULL) ||
473 				    (item->group != NULL)) ? " " : "",
474 				    (item->resource != NULL) ?
475 				    item->resource : "",
476 				    item->group != NULL ? "@" : "",
477 				    item->group != NULL ? item->group : "");
478 			} else {
479 				(void) fprintf(dfstab, "%s", item->origline);
480 			}
481 		} else {
482 			if (item->description != NULL)
483 				(void) fprintf(dfstab, "%s", item->description);
484 			else
485 				(void) fprintf(dfstab, "%s", item->origline);
486 		}
487 	}
488 }
489 
490 /*
491  * open_dfstab(file)
492  *
493  * Open the specified dfstab file. If the owner/group/perms are wrong,
494  * fix them.
495  */
496 
497 static FILE *
498 open_dfstab(char *file)
499 {
500 	struct group *grp;
501 	struct group group;
502 	char *buff;
503 	int grsize;
504 	FILE *dfstab;
505 
506 	dfstab = fopen(file, "r+");
507 	if (dfstab == NULL) {
508 		dfstab = fopen(file, "w+");
509 	}
510 	if (dfstab != NULL) {
511 		grsize = sysconf(_SC_GETGR_R_SIZE_MAX);
512 		buff = malloc(grsize);
513 		if (buff != NULL)
514 			grp = getgrnam_r(SA_DEFAULT_FILE_GRP, &group, buff,
515 			    grsize);
516 		else
517 			grp = getgrnam(SA_DEFAULT_FILE_GRP);
518 		(void) fchmod(fileno(dfstab), 0644);
519 		(void) fchown(fileno(dfstab), 0,
520 		    grp != NULL ? grp->gr_gid : 3);
521 		if (buff != NULL)
522 			free(buff);
523 		rewind(dfstab);
524 	}
525 	return (dfstab);
526 }
527 
528 /*
529  * sa_comment_line(line, err)
530  *
531  * Add a comment to the dfstab file with err as a prefix to the
532  * original line.
533  */
534 
535 static void
536 sa_comment_line(char *line, char *err)
537 {
538 	FILE *dfstab;
539 	xfs_sharelist_t *list;
540 	sigset_t old;
541 
542 	dfstab = open_dfstab(SA_LEGACY_DFSTAB);
543 	if (dfstab != NULL) {
544 		(void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
545 		sablocksigs(&old);
546 		(void) lockf(fileno(dfstab), F_LOCK, 0);
547 		list = getdfstab(dfstab);
548 		rewind(dfstab);
549 		/*
550 		 * don't ignore the return since the list could have
551 		 * gone to NULL if the file only had one line in it.
552 		 */
553 		list = remdfsline(list, line);
554 		outdfstab(dfstab, list);
555 		(void) fprintf(dfstab, "# Error: %s: %s", err, line);
556 		(void) fsync(fileno(dfstab));
557 		(void) lockf(fileno(dfstab), F_ULOCK, 0);
558 		(void) fclose(dfstab);
559 		saunblocksigs(&old);
560 		if (list != NULL)
561 			dfs_free_list(list);
562 	}
563 }
564 
565 /*
566  * sa_delete_legacy(share)
567  *
568  * Delete the specified share from the legacy config file.
569  */
570 
571 int
572 sa_delete_legacy(sa_share_t share)
573 {
574 	FILE *dfstab;
575 	int err;
576 	int ret = SA_OK;
577 	xfs_sharelist_t *list;
578 	char *path;
579 	sa_optionset_t optionset;
580 	sa_group_t parent;
581 	sigset_t old;
582 
583 	dfstab = open_dfstab(SA_LEGACY_DFSTAB);
584 	if (dfstab != NULL) {
585 		(void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
586 		sablocksigs(&old);
587 		path = sa_get_share_attr(share, "path");
588 		parent = sa_get_parent_group(share);
589 		if (parent != NULL) {
590 			(void) lockf(fileno(dfstab), F_LOCK, 0);
591 			list = getdfstab(dfstab);
592 			rewind(dfstab);
593 			for (optionset = sa_get_optionset(parent, NULL);
594 			    optionset != NULL;
595 			    optionset = sa_get_next_optionset(optionset)) {
596 				char *proto = sa_get_optionset_attr(optionset,
597 				    "type");
598 				if (list != NULL && proto != NULL)
599 					list = remdfsentry(list, path,
600 					    proto);
601 				if (proto == NULL)
602 					ret = SA_NO_MEMORY;
603 				/*
604 				 * May want to only do the dfstab if
605 				 * this call returns NOT IMPLEMENTED
606 				 * but it shouldn't hurt.
607 				 */
608 				if (ret == SA_OK) {
609 					err = sa_proto_delete_legacy(proto,
610 					    share);
611 					if (err != SA_NOT_IMPLEMENTED)
612 						ret = err;
613 				}
614 				if (proto != NULL)
615 					sa_free_attr_string(proto);
616 			}
617 			outdfstab(dfstab, list);
618 			if (list != NULL)
619 				dfs_free_list(list);
620 			(void) fflush(dfstab);
621 			(void) lockf(fileno(dfstab), F_ULOCK, 0);
622 		}
623 		(void) fsync(fileno(dfstab));
624 		saunblocksigs(&old);
625 		(void) fclose(dfstab);
626 		sa_free_attr_string(path);
627 	} else {
628 		if (errno == EACCES || errno == EPERM)
629 			ret = SA_NO_PERMISSION;
630 		else
631 			ret = SA_CONFIG_ERR;
632 	}
633 	return (ret);
634 }
635 
636 /*
637  * sa_update_legacy(share, proto)
638  *
639  * There is an assumption that dfstab will be the most common form of
640  * legacy configuration file for shares, but not the only one. Because
641  * of that, dfstab handling is done in the main code with calls to
642  * this function and protocol specific calls to deal with formating
643  * options into dfstab/share compatible syntax. Since not everything
644  * will be dfstab, there is a provision for calling a protocol
645  * specific plugin interface that allows the protocol plugin to do its
646  * own legacy files and skip the dfstab update.
647  */
648 
649 int
650 sa_update_legacy(sa_share_t share, char *proto)
651 {
652 	FILE *dfstab;
653 	int ret = SA_OK;
654 	xfs_sharelist_t *list;
655 	char *path;
656 	sigset_t old;
657 	char *persist;
658 
659 	ret = sa_proto_update_legacy(proto, share);
660 	if (ret != SA_NOT_IMPLEMENTED)
661 		return (ret);
662 	/* do the dfstab format */
663 	persist = sa_get_share_attr(share, "type");
664 	/*
665 	 * only update if the share is not transient -- no share type
666 	 * set or the type is not "transient".
667 	 */
668 	if (persist == NULL || strcmp(persist, "transient") != 0) {
669 		dfstab = open_dfstab(SA_LEGACY_DFSTAB);
670 		if (dfstab != NULL) {
671 			(void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
672 			sablocksigs(&old);
673 			path = sa_get_share_attr(share, "path");
674 			(void) lockf(fileno(dfstab), F_LOCK, 0);
675 			list = getdfstab(dfstab);
676 			rewind(dfstab);
677 			if (list != NULL)
678 				list = remdfsentry(list, path, proto);
679 			list = adddfsentry(list, share, proto);
680 			outdfstab(dfstab, list);
681 			(void) fflush(dfstab);
682 			(void) lockf(fileno(dfstab), F_ULOCK, 0);
683 			(void) fsync(fileno(dfstab));
684 			saunblocksigs(&old);
685 			(void) fclose(dfstab);
686 			sa_free_attr_string(path);
687 			if (list != NULL)
688 				dfs_free_list(list);
689 		} else {
690 			if (errno == EACCES || errno == EPERM)
691 				ret = SA_NO_PERMISSION;
692 			else
693 				ret = SA_CONFIG_ERR;
694 		}
695 	}
696 	if (persist != NULL)
697 		sa_free_attr_string(persist);
698 	return (ret);
699 }
700 
701 /*
702  * sa_is_security(optname, proto)
703  *
704  * Check to see if optname is a security (named optionset) specific
705  * property for the specified protocol.
706  */
707 
708 int
709 sa_is_security(char *optname, char *proto)
710 {
711 	int ret = 0;
712 	if (proto != NULL)
713 		ret = sa_proto_security_prop(proto, optname);
714 	return (ret);
715 }
716 
717 /*
718  * add_syntax_comment(root, line, err, todfstab)
719  *
720  * Add a comment to the document indicating a syntax error. If
721  * todfstab is set, write it back to the dfstab file as well.
722  */
723 
724 static void
725 add_syntax_comment(xmlNodePtr root, char *line, char *err, int todfstab)
726 {
727 	xmlNodePtr node;
728 
729 	node = xmlNewChild(root, NULL, (xmlChar *)"error", (xmlChar *)line);
730 	if (node != NULL)
731 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)err);
732 	if (todfstab)
733 		sa_comment_line(line, err);
734 }
735 
736 /*
737  * sa_is_share(object)
738  *
739  * returns true of the object is of type "share".
740  */
741 
742 int
743 sa_is_share(void *object)
744 {
745 	if (object != NULL) {
746 		if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0)
747 		return (1);
748 	}
749 	return (0);
750 }
751 
752 /*
753  * _sa_remove_property(property)
754  *
755  * remove a property only from the document.
756  */
757 
758 static void
759 _sa_remove_property(sa_property_t property)
760 {
761 	xmlUnlinkNode((xmlNodePtr)property);
762 	xmlFreeNode((xmlNodePtr)property);
763 }
764 
765 /*
766  * _sa_create_dummy_share()
767  *
768  * Create a share entry suitable for parsing but not tied to any real
769  * config tree.  Need to have a parent as well as the node to parse
770  * on.  Free using _sa_free_dummy_share(share);
771  */
772 
773 static sa_group_t
774 _sa_create_dummy_share()
775 {
776 	xmlNodePtr parent_node = NULL;
777 	xmlNodePtr child_node = NULL;
778 
779 	parent_node = xmlNewNode(NULL, (xmlChar *)"group");
780 	if (parent_node != NULL) {
781 		child_node = xmlNewChild(parent_node, NULL, (xmlChar *)"share",
782 		    NULL);
783 		if (child_node != NULL) {
784 			/*
785 			 * Use a "zfs" tag since that will make sure nothing
786 			 * really attempts to put values into the
787 			 * repository. Also ZFS is currently the only user of
788 			 * this interface.
789 			 */
790 			set_node_attr(parent_node, "type", "transient");
791 			set_node_attr(parent_node, "zfs", "true");
792 			set_node_attr(child_node, "type", "transient");
793 			set_node_attr(child_node, "zfs", "true");
794 		} else {
795 			xmlFreeNode(parent_node);
796 		}
797 	}
798 	return (child_node);
799 }
800 
801 /*
802  * _sa_free_dummy_share(share)
803  *
804  * Free the dummy share and its parent.  It is an error to try and
805  * free something that isn't a dummy.
806  */
807 
808 static int
809 _sa_free_dummy_share(sa_share_t share)
810 {
811 	xmlNodePtr node = (xmlNodePtr)share;
812 	xmlNodePtr parent;
813 	int ret = SA_OK;
814 	char *name;
815 
816 	if (node != NULL) {
817 		parent = node->parent;
818 		name = (char *)xmlGetProp(node, (xmlChar *)"path");
819 		if (name != NULL) {
820 			/* Real shares always have a path but a dummy doesn't */
821 			ret = SA_NOT_ALLOWED;
822 			sa_free_attr_string(name);
823 		} else {
824 			/*
825 			 * If there is a parent, do the free on that since
826 			 * xmlFreeNode is a recursive function and free's an
827 			 * child nodes.
828 			 */
829 			if (parent != NULL) {
830 				node = parent;
831 			}
832 			xmlUnlinkNode(node);
833 			xmlFreeNode(node);
834 		}
835 	}
836 	return (ret);
837 }
838 
839 
840 /*
841  * sa_parse_legacy_options(group, options, proto)
842  *
843  * In order to support legacy configurations, we allow the protocol
844  * specific plugin to parse legacy syntax options (like those in
845  * /etc/dfs/dfstab). This adds a new optionset to the group (or
846  * share).
847  *
848  * Once the optionset has been created, we then get the derived
849  * optionset of the parent (options from the optionset of the parent
850  * and any parent it might have) and remove those from the created
851  * optionset. This avoids duplication of options.
852  */
853 
854 int
855 sa_parse_legacy_options(sa_group_t group, char *options, char *proto)
856 {
857 	int ret = SA_INVALID_PROTOCOL;
858 	sa_group_t parent;
859 	int using_dummy = B_FALSE;
860 	char *pvalue;
861 	sa_optionset_t optionset;
862 	sa_property_t popt, prop;
863 	sa_optionset_t localoptions;
864 
865 	/*
866 	 * If "group" is NULL, this is just a parse without saving
867 	 * anything in either SMF or ZFS.  Create a dummy group to
868 	 * handle this case.
869 	 */
870 	if (group == NULL) {
871 		group = (sa_group_t)_sa_create_dummy_share();
872 		using_dummy = B_TRUE;
873 	}
874 
875 	parent = sa_get_parent_group(group);
876 
877 	if (proto != NULL)
878 		ret = sa_proto_legacy_opts(proto, group, options);
879 
880 	if (using_dummy) {
881 		/* Since this is a dummy parse, cleanup and quit here */
882 		(void) _sa_free_dummy_share(parent);
883 		return (ret);
884 	}
885 
886 	if (ret != SA_OK)
887 		return (ret);
888 
889 	/*
890 	 * If in a group, remove the inherited options and security
891 	 */
892 
893 	if (parent == NULL)
894 		return (ret);
895 
896 	/* Find parent options to remove from child */
897 	optionset = sa_get_derived_optionset(parent, proto, 1);
898 	localoptions = sa_get_optionset(group, proto);
899 	if (optionset != NULL) {
900 		for (popt = sa_get_property(optionset, NULL);
901 		    popt != NULL;
902 		    popt = sa_get_next_property(popt)) {
903 			char *tag;
904 			char *value;
905 			tag = sa_get_property_attr(popt, "type");
906 			if (tag == NULL)
907 				continue;
908 			prop = sa_get_property(localoptions, tag);
909 			if (prop != NULL) {
910 				value = sa_get_property_attr(popt,
911 				    "value");
912 				pvalue = sa_get_property_attr(prop,
913 				    "value");
914 				if (value != NULL && pvalue != NULL &&
915 				    strcmp(value, pvalue) == 0) {
916 					/*
917 					 * Remove the property
918 					 * from the
919 					 * child. While we
920 					 * removed it, we
921 					 * don't need to reset
922 					 * as we do below
923 					 * since we always
924 					 * search from the
925 					 * beginning.
926 					 */
927 					(void) _sa_remove_property(
928 					    prop);
929 				}
930 				if (value != NULL)
931 					sa_free_attr_string(value);
932 				if (pvalue != NULL)
933 					sa_free_attr_string(pvalue);
934 			}
935 			sa_free_attr_string(tag);
936 		}
937 		prop = sa_get_property(localoptions, NULL);
938 		if (prop == NULL && sa_is_share(group)) {
939 			/*
940 			 * All properties removed so remove the
941 			 * optionset if it is on a share
942 			 */
943 			(void) _sa_remove_optionset(localoptions);
944 		}
945 		sa_free_derived_optionset(optionset);
946 	}
947 	/*
948 	 * Need to remove security here. If there are no
949 	 * security options on the local group/share, don't
950 	 * bother since those are the only ones that would be
951 	 * affected.
952 	 */
953 	localoptions = sa_get_all_security_types(group, proto, 0);
954 	if (localoptions != NULL) {
955 		for (prop = sa_get_property(localoptions, NULL);
956 		    prop != NULL;
957 		    prop = sa_get_next_property(prop)) {
958 			char *tag;
959 			sa_security_t security;
960 			tag = sa_get_property_attr(prop, "type");
961 			if (tag != NULL) {
962 				sa_property_t nextpopt = NULL;
963 				security = sa_get_security(group, tag, proto);
964 				sa_free_attr_string(tag);
965 				/*
966 				 * prop's value only changes outside this loop
967 				 */
968 				pvalue = sa_get_property_attr(prop, "value");
969 				for (popt = sa_get_property(security, NULL);
970 				    popt != NULL;
971 				    popt = nextpopt) {
972 					char *value;
973 					/*
974 					 * Need to get the next prop
975 					 * now since we could break
976 					 * the list during removal.
977 					 */
978 					nextpopt = sa_get_next_property(popt);
979 					/* remove Duplicates from this level */
980 					value = sa_get_property_attr(popt,
981 					    "value");
982 					if (value != NULL && pvalue != NULL &&
983 					    strcmp(value, pvalue) == 0) {
984 						/*
985 						 * remove the property
986 						 * from the child
987 						 */
988 						(void) _sa_remove_property
989 						    (popt);
990 					}
991 					if (value != NULL)
992 						sa_free_attr_string(value);
993 				}
994 				if (pvalue != NULL)
995 					sa_free_attr_string(pvalue);
996 			}
997 		}
998 		(void) sa_destroy_optionset(localoptions);
999 	}
1000 	return (ret);
1001 }
1002 
1003 /*
1004  * dfs_free_list(list)
1005  *
1006  * Free the data in each list entry of the list as well as freeing the
1007  * entries themselves. We need to avoid memory leaks and don't want to
1008  * dereference any NULL members.
1009  */
1010 
1011 static void
1012 dfs_free_list(xfs_sharelist_t *list)
1013 {
1014 	xfs_sharelist_t *entry;
1015 	for (entry = list; entry != NULL; entry = list) {
1016 		if (entry->path != NULL)
1017 			free(entry->path);
1018 		if (entry->resource != NULL)
1019 			free(entry->resource);
1020 		if (entry->fstype != NULL)
1021 			free(entry->fstype);
1022 		if (entry->options != NULL)
1023 			free(entry->options);
1024 		if (entry->description != NULL)
1025 			free(entry->description);
1026 		if (entry->origline != NULL)
1027 			free(entry->origline);
1028 		if (entry->group != NULL)
1029 			free(entry->group);
1030 		list = list->next;
1031 			free(entry);
1032 	}
1033 }
1034 
1035 /*
1036  * parse_dfstab(dfstab, root)
1037  *
1038  * Open and read the existing dfstab, parsing each line and adding it
1039  * to the internal configuration. Make sure syntax errors, etc are
1040  * preserved as comments.
1041  */
1042 
1043 static void
1044 parse_dfstab(sa_handle_t handle, char *dfstab, xmlNodePtr root)
1045 {
1046 	sa_share_t share;
1047 	sa_group_t group;
1048 	sa_group_t sgroup = NULL;
1049 	sa_group_t defgroup;
1050 	xfs_sharelist_t *head, *list;
1051 	int err;
1052 	int defined_group;
1053 	FILE *dfs;
1054 	char *oldprops;
1055 
1056 	/* read the dfstab format file and fill in the doc tree */
1057 
1058 	dfs = fopen(dfstab, "r");
1059 	if (dfs == NULL)
1060 		return;
1061 
1062 	defgroup = sa_get_group(handle, "default");
1063 
1064 	for (head = list = getdfstab(dfs);
1065 	    list != NULL;
1066 	    list = list->next) {
1067 		share = NULL;
1068 		group = NULL;
1069 		defined_group = 0;
1070 		err = 0;
1071 
1072 		if (list->origline == NULL) {
1073 			/*
1074 			 * Comment line that we will likely skip.
1075 			 * If the line has the syntax:
1076 			 *	# error: string: string
1077 			 * It should be preserved until manually deleted.
1078 			 */
1079 			if (list->description != NULL &&
1080 			    strncmp(list->description, "# Error: ", 9) == 0) {
1081 				char *line;
1082 				char *error;
1083 				char *cmd;
1084 				line = strdup(list->description);
1085 				if (line != NULL) {
1086 					error = line + 9;
1087 					cmd = strchr(error, ':');
1088 					if (cmd != NULL) {
1089 						int len;
1090 						*cmd = '\0';
1091 						cmd += 2;
1092 						len = strlen(cmd);
1093 						cmd[len - 1] = '\0';
1094 						add_syntax_comment(root, cmd,
1095 						    error, 0);
1096 					}
1097 					free(line);
1098 				}
1099 			}
1100 			continue;
1101 		}
1102 		if (list->path != NULL && strlen(list->path) > 0 &&
1103 		    *list->path == '/') {
1104 			share = sa_find_share(handle, list->path);
1105 			if (share != NULL)
1106 				sgroup = sa_get_parent_group(share);
1107 			else
1108 				sgroup = NULL;
1109 		} else {
1110 			(void) printf(dgettext(TEXT_DOMAIN,
1111 			    "No share specified in dfstab: "
1112 			    "line %d: %s\n"),
1113 			    list->lineno, list->origline);
1114 			add_syntax_comment(root, list->origline,
1115 			    dgettext(TEXT_DOMAIN, "No share specified"), 1);
1116 			continue;
1117 		}
1118 		if (list->group != NULL && strlen(list->group) > 0) {
1119 			group = sa_get_group(handle, list->group);
1120 			defined_group = 1;
1121 		} else {
1122 			group = defgroup;
1123 		}
1124 		if (defined_group && group == NULL) {
1125 			(void) printf(dgettext(TEXT_DOMAIN,
1126 			    "Unknown group used in dfstab: line %d: %s\n"),
1127 			    list->lineno, list->origline);
1128 			add_syntax_comment(root, list->origline,
1129 			    dgettext(TEXT_DOMAIN, "Unknown group specified"),
1130 			    1);
1131 			continue;
1132 		}
1133 		if (group == NULL) {
1134 			/* Shouldn't happen unless an SMF error */
1135 			err = SA_CONFIG_ERR;
1136 			continue;
1137 		}
1138 		if (share == NULL) {
1139 			if (defined_group || group != defgroup)
1140 				continue;
1141 			/* This is an OK add for legacy */
1142 			share = sa_add_share(defgroup, list->path,
1143 			    SA_SHARE_PERMANENT | SA_SHARE_PARSER, &err);
1144 			if (share != NULL) {
1145 				if (list->description != NULL &&
1146 				    strlen(list->description) > 0)
1147 					(void) sa_set_share_description(share,
1148 					    list->description);
1149 				if (list->options != NULL &&
1150 				    strlen(list->options) > 0) {
1151 					(void) sa_parse_legacy_options(share,
1152 					    list->options, list->fstype);
1153 				}
1154 				if (list->resource != NULL)
1155 					(void) sa_set_share_attr(share,
1156 					    "resource", list->resource);
1157 			} else {
1158 				(void) printf(dgettext(TEXT_DOMAIN,
1159 				    "Error in dfstab: line %d: %s\n"),
1160 				    list->lineno, list->origline);
1161 				if (err != SA_BAD_PATH)
1162 					add_syntax_comment(root, list->origline,
1163 					    dgettext(TEXT_DOMAIN, "Syntax"), 1);
1164 				else
1165 					add_syntax_comment(root, list->origline,
1166 					    dgettext(TEXT_DOMAIN,
1167 					    "Path"), 1);
1168 				continue;
1169 			}
1170 		} else {
1171 			if (group != sgroup) {
1172 				(void) printf(dgettext(TEXT_DOMAIN,
1173 				    "Attempt to change configuration in "
1174 				    "dfstab: line %d: %s\n"),
1175 				    list->lineno, list->origline);
1176 				add_syntax_comment(root, list->origline,
1177 				    dgettext(TEXT_DOMAIN,
1178 				    "Attempt to change configuration"), 1);
1179 				continue;
1180 			}
1181 			/*
1182 			 * It is the same group but could have changed
1183 			 * options. Make sure we include the group's
1184 			 * properties so we don't end up moving them to
1185 			 * the share inadvertantly. The last arg being
1186 			 * true says to get the inherited properties as well
1187 			 * as the local properties.
1188 			 */
1189 			oldprops = sa_proto_legacy_format(list->fstype, share,
1190 			    B_TRUE);
1191 
1192 			if (oldprops == NULL)
1193 				continue;
1194 
1195 			if (list->options != NULL &&
1196 			    strcmp(oldprops, list->options) != 0) {
1197 				sa_optionset_t opts;
1198 				sa_security_t secs;
1199 
1200 				/* possibly different values */
1201 				opts = sa_get_optionset((sa_group_t)
1202 				    share, list->fstype);
1203 				(void) sa_destroy_optionset(opts);
1204 
1205 				for (secs = sa_get_security(
1206 				    (sa_group_t)share, NULL, list->fstype);
1207 				    secs != NULL;
1208 				    secs = sa_get_security((sa_group_t)share,
1209 				    NULL, list->fstype)) {
1210 					(void) sa_destroy_security(
1211 					    secs);
1212 				}
1213 				(void) sa_parse_legacy_options(share,
1214 				    list->options, list->fstype);
1215 			}
1216 			sa_format_free(oldprops);
1217 		}
1218 	}
1219 	dfs_free_list(head);
1220 }
1221 
1222 /*
1223  * legacy_removes(group, file)
1224  *
1225  * Find any shares that are "missing" from the legacy file. These
1226  * should be removed from the configuration since they are likely from
1227  * a legacy app or the admin modified the dfstab file directly. We
1228  * have to support this even if it is not the recommended way to do
1229  * things.
1230  */
1231 
1232 static void
1233 legacy_removes(sa_group_t group, char *file)
1234 {
1235 	sa_share_t share;
1236 	char *path;
1237 	xfs_sharelist_t *list, *item;
1238 	FILE *dfstab;
1239 
1240 	dfstab = fopen(file, "r");
1241 	if (dfstab != NULL) {
1242 		list = getdfstab(dfstab);
1243 		(void) fclose(dfstab);
1244 retry:
1245 		for (share = sa_get_share(group, NULL);
1246 		    share != NULL;
1247 		    share = sa_get_next_share(share)) {
1248 			/* now see if the share is in the dfstab file */
1249 			path = sa_get_share_attr(share, "path");
1250 			if (path != NULL) {
1251 				item = finddfsentry(list, path);
1252 				sa_free_attr_string(path);
1253 				if (item == NULL) {
1254 					/* The share was removed this way */
1255 					(void) sa_remove_share(share);
1256 
1257 					/*
1258 					 * Start over since the list was broken
1259 					 */
1260 					goto retry;
1261 				}
1262 			}
1263 		}
1264 		if (list != NULL)
1265 			dfs_free_list(list);
1266 	}
1267 }
1268 
1269 /*
1270  * getlegacyconfig(path, root)
1271  *
1272  * Parse dfstab and build the legacy configuration. This only gets
1273  * called when a change was detected.
1274  */
1275 
1276 void
1277 getlegacyconfig(sa_handle_t handle, char *path, xmlNodePtr *root)
1278 {
1279 	sa_group_t defgroup;
1280 
1281 	if (root != NULL) {
1282 		if (*root == NULL)
1283 			*root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
1284 		if (*root != NULL) {
1285 			if (strcmp(path, SA_LEGACY_DFSTAB) == 0) {
1286 				/*
1287 				 * Walk the default shares and find anything
1288 				 * missing.  we do this first to make sure it
1289 				 * is cleaned up since there may be legacy
1290 				 * code add/del via dfstab and we need to
1291 				 * cleanup SMF.
1292 				 */
1293 				defgroup = sa_get_group(handle, "default");
1294 				if (defgroup != NULL)
1295 					legacy_removes(defgroup, path);
1296 				/* Parse the dfstab and add anything new */
1297 				parse_dfstab(handle, path, *root);
1298 			}
1299 		}
1300 	}
1301 }
1302 
1303 /*
1304  * get_share_list(&err)
1305  *
1306  * Get a linked list of all the shares on the system from
1307  * /etc/dfs/sharetab. This is partially copied from libfsmgt which we
1308  * can't use due to package dependencies.
1309  */
1310 static xfs_sharelist_t *
1311 get_share_list(int *errp)
1312 {
1313 	xfs_sharelist_t	*newp;
1314 	xfs_sharelist_t	*headp;
1315 	xfs_sharelist_t	*tailp;
1316 	FILE		*fp;
1317 
1318 	headp = NULL;
1319 	tailp = NULL;
1320 
1321 	if ((fp = fopen(SHARETAB, "r")) != NULL) {
1322 		struct share	*sharetab_entry;
1323 
1324 		while (getshare(fp, &sharetab_entry) > 0) {
1325 			newp = alloc_sharelist();
1326 			if (newp == NULL)
1327 				goto err;
1328 
1329 			/*
1330 			 * Link into the list here so we don't leak
1331 			 * memory on a failure from strdup().
1332 			 */
1333 			if (headp == NULL) {
1334 				headp = newp;
1335 				tailp = newp;
1336 			} else {
1337 				tailp->next = newp;
1338 				tailp = newp;
1339 			}
1340 
1341 			newp->path = strdup(sharetab_entry->sh_path);
1342 			if (newp->path == NULL)
1343 				goto err;
1344 			newp->resource = strdup(sharetab_entry->sh_res);
1345 			if (newp->resource == NULL)
1346 				goto err;
1347 			newp->fstype = strdup(sharetab_entry->sh_fstype);
1348 			if (newp->fstype == NULL)
1349 				goto err;
1350 			newp->options = strdup(sharetab_entry->sh_opts);
1351 			if (newp->options == NULL)
1352 				goto err;
1353 			newp->description = strdup(sharetab_entry->sh_descr);
1354 			if (newp->description == NULL)
1355 				goto err;
1356 		}
1357 		(void) lockf(fileno(fp), F_ULOCK, 0);
1358 		(void) fclose(fp);
1359 	} else {
1360 		*errp = errno;
1361 	}
1362 
1363 	/*
1364 	 * Caller must free the mount list
1365 	 */
1366 	return (headp);
1367 err:
1368 	/*
1369 	 * Out of memory so cleanup and leave.
1370 	 */
1371 	dfs_free_list(headp);
1372 	(void) fclose(fp);
1373 	return (NULL);
1374 }
1375 
1376 /*
1377  * parse_sharetab(handle)
1378  *
1379  * Read the /etc/dfs/sharetab file and see which entries don't exist
1380  * in the repository. These shares are marked transient.  We also need
1381  * to see if they are ZFS shares since ZFS bypasses the SMF
1382  * repository.
1383  */
1384 
1385 int
1386 parse_sharetab(sa_handle_t handle)
1387 {
1388 	xfs_sharelist_t *list, *tmplist;
1389 	int err = 0;
1390 	sa_share_t share;
1391 	sa_group_t group;
1392 	sa_group_t lgroup;
1393 	char *groupname;
1394 	int legacy = 0;
1395 
1396 	list = get_share_list(&err);
1397 	if (list == NULL)
1398 		return (legacy);
1399 
1400 	lgroup = sa_get_group(handle, "default");
1401 
1402 	for (tmplist = list; tmplist != NULL; tmplist = tmplist->next) {
1403 		group = NULL;
1404 		share = sa_find_share(handle, tmplist->path);
1405 		if (share != NULL) {
1406 			/*
1407 			 * If this is a legacy share, mark as shared so we
1408 			 * only update sharetab appropriately. We also keep
1409 			 * the sharetab options in order to display for legacy
1410 			 * share with no arguments.
1411 			 */
1412 			set_node_attr(share, "shared", "true");
1413 			set_node_attr(share, "shareopts", tmplist->options);
1414 			continue;
1415 		}
1416 
1417 		/*
1418 		 * This share is transient so needs to be
1419 		 * added. Initially, this will be under
1420 		 * default(legacy) unless it is a ZFS
1421 		 * share. If zfs, we need a zfs group.
1422 		 */
1423 		if (tmplist->resource != NULL &&
1424 		    (groupname = strchr(tmplist->resource, '@')) != NULL) {
1425 			/* There is a defined group */
1426 			*groupname++ = '\0';
1427 			group = sa_get_group(handle, groupname);
1428 			if (group != NULL) {
1429 				share = _sa_add_share(group,
1430 				    tmplist->path,
1431 				    SA_SHARE_TRANSIENT, &err);
1432 			} else {
1433 				/*
1434 				 * While this case shouldn't
1435 				 * occur very often, it does
1436 				 * occur out of a "zfs set
1437 				 * sharenfs=off" when the
1438 				 * dataset is also set to
1439 				 * canmount=off. A warning
1440 				 * will then cause the zfs
1441 				 * command to abort. Since we
1442 				 * add it to the default list,
1443 				 * everything works properly
1444 				 * anyway and the library
1445 				 * doesn't need to give a
1446 				 * warning.
1447 				 */
1448 				share = _sa_add_share(lgroup,
1449 				    tmplist->path, SA_SHARE_TRANSIENT,
1450 				    &err);
1451 			}
1452 		} else {
1453 			if (sa_zfs_is_shared(handle, tmplist->path)) {
1454 				group = sa_get_group(handle, "zfs");
1455 				if (group == NULL) {
1456 					group = sa_create_group(handle,
1457 					    "zfs", &err);
1458 					if (group == NULL &&
1459 					    err == SA_NO_PERMISSION) {
1460 						group = _sa_create_group(
1461 						    (sa_handle_impl_t)
1462 						    handle,
1463 						    "zfs");
1464 					}
1465 					if (group != NULL) {
1466 						(void) sa_create_optionset(
1467 						    group, tmplist->fstype);
1468 						(void) sa_set_group_attr(group,
1469 						    "zfs", "true");
1470 					}
1471 				}
1472 				if (group != NULL) {
1473 					share = _sa_add_share(group,
1474 					    tmplist->path, SA_SHARE_TRANSIENT,
1475 					    &err);
1476 				}
1477 			} else {
1478 				share = _sa_add_share(lgroup, tmplist->path,
1479 				    SA_SHARE_TRANSIENT, &err);
1480 			}
1481 		}
1482 		if (share == NULL)
1483 			(void) printf(dgettext(TEXT_DOMAIN,
1484 			    "Problem with transient: %s\n"), sa_errorstr(err));
1485 		if (share != NULL)
1486 			set_node_attr(share, "shared", "true");
1487 		if (err == SA_OK) {
1488 			if (tmplist->options != NULL &&
1489 			    strlen(tmplist->options) > 0) {
1490 				(void) sa_parse_legacy_options(share,
1491 				    tmplist->options, tmplist->fstype);
1492 			}
1493 			if (tmplist->resource != NULL &&
1494 			    strcmp(tmplist->resource, "-") != 0)
1495 				set_node_attr(share, "resource",
1496 				    tmplist->resource);
1497 			if (tmplist->description != NULL) {
1498 				xmlNodePtr node;
1499 				node = xmlNewChild((xmlNodePtr)share, NULL,
1500 				    (xmlChar *)"description", NULL);
1501 				xmlNodeSetContent(node,
1502 				    (xmlChar *)tmplist->description);
1503 			}
1504 			legacy = 1;
1505 		}
1506 	}
1507 	dfs_free_list(list);
1508 	return (legacy);
1509 }
1510 
1511 /*
1512  * Get the transient shares from the sharetab (or other) file.  since
1513  * these are transient, they only appear in the working file and not
1514  * in a repository.
1515  */
1516 int
1517 gettransients(sa_handle_impl_t ihandle, xmlNodePtr *root)
1518 {
1519 	int legacy = 0;
1520 
1521 	if (root != NULL) {
1522 		if (*root == NULL)
1523 			*root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
1524 		if (*root != NULL)
1525 			legacy = parse_sharetab(ihandle);
1526 	}
1527 	return (legacy);
1528 }
1529 
1530 /*
1531  * sa_has_prop(optionset, prop)
1532  *
1533  * Is the specified property a member of the optionset?
1534  */
1535 
1536 int
1537 sa_has_prop(sa_optionset_t optionset, sa_property_t prop)
1538 {
1539 	char *name;
1540 	sa_property_t otherprop;
1541 	int result = 0;
1542 
1543 	if (optionset != NULL) {
1544 		name = sa_get_property_attr(prop, "type");
1545 		if (name != NULL) {
1546 			otherprop = sa_get_property(optionset, name);
1547 			if (otherprop != NULL)
1548 				result = 1;
1549 			sa_free_attr_string(name);
1550 		}
1551 	}
1552 	return (result);
1553 }
1554 
1555 /*
1556  * Update legacy files
1557  *
1558  * Provides functions to add/remove/modify individual entries
1559  * in dfstab and sharetab
1560  */
1561 
1562 void
1563 update_legacy_config(sa_handle_t handle)
1564 {
1565 	/*
1566 	 * no longer used -- this is a placeholder in case we need to
1567 	 * add it back later.
1568 	 */
1569 #ifdef lint
1570 	handle = handle;
1571 #endif
1572 }
1573 
1574 /*
1575  * sa_valid_property(object, proto, property)
1576  *
1577  * check to see if the specified property is valid relative to the
1578  * specified protocol. The protocol plugin is called to do the work.
1579  */
1580 
1581 int
1582 sa_valid_property(void *object, char *proto, sa_property_t property)
1583 {
1584 	int ret = SA_OK;
1585 
1586 	if (proto != NULL && property != NULL) {
1587 		ret = sa_proto_valid_prop(proto, property, object);
1588 	}
1589 
1590 	return (ret);
1591 }
1592 
1593 /*
1594  * sa_fstype(path)
1595  *
1596  * Given path, return the string representing the path's file system
1597  * type. This is used to discover ZFS shares.
1598  */
1599 
1600 char *
1601 sa_fstype(char *path)
1602 {
1603 	int err;
1604 	struct stat st;
1605 
1606 	err = stat(path, &st);
1607 	if (err < 0)
1608 		err = SA_NO_SUCH_PATH;
1609 	else
1610 		err = SA_OK;
1611 
1612 	/*
1613 	 * If we have a valid path at this point ret, return the fstype.
1614 	 */
1615 	if (err == SA_OK)
1616 		return (strdup(st.st_fstype));
1617 
1618 	return (NULL);
1619 }
1620 
1621 void
1622 sa_free_fstype(char *type)
1623 {
1624 	free(type);
1625 }
1626 
1627 /*
1628  * sa_get_derived_optionset(object, proto, hier)
1629  *
1630  *	Work backward to the top of the share object tree and start
1631  *	copying protocol specific optionsets into a newly created
1632  *	optionset that doesn't have a parent (it will be freed
1633  *	later). This provides for the property inheritence model. That
1634  *	is, properties closer to the share take precedence over group
1635  *	level. This also provides for groups of groups in the future.
1636  */
1637 
1638 sa_optionset_t
1639 sa_get_derived_optionset(void *object, char *proto, int hier)
1640 {
1641 	sa_optionset_t newoptionset;
1642 	sa_optionset_t optionset;
1643 	sa_group_t group;
1644 
1645 	if (hier &&
1646 	    (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
1647 		newoptionset = sa_get_derived_optionset((void *)group, proto,
1648 		    hier);
1649 	} else {
1650 		newoptionset = (sa_optionset_t)xmlNewNode(NULL,
1651 		    (xmlChar *)"optionset");
1652 		if (newoptionset != NULL) {
1653 			sa_set_optionset_attr(newoptionset, "type", proto);
1654 		}
1655 	}
1656 	/* Dont' do anything if memory wasn't allocated */
1657 	if (newoptionset == NULL)
1658 		return (NULL);
1659 
1660 	/* Found the top so working back down the stack */
1661 	optionset = sa_get_optionset((sa_optionset_t)object, proto);
1662 	if (optionset != NULL) {
1663 		sa_property_t prop;
1664 		/* add optionset to the newoptionset */
1665 		for (prop = sa_get_property(optionset, NULL);
1666 		    prop != NULL;
1667 		    prop = sa_get_next_property(prop)) {
1668 			sa_property_t newprop;
1669 			char *name;
1670 			char *value;
1671 			name = sa_get_property_attr(prop, "type");
1672 			value = sa_get_property_attr(prop, "value");
1673 			if (name == NULL)
1674 				continue;
1675 			newprop = sa_get_property(newoptionset, name);
1676 			/* Replace the value with the new value */
1677 			if (newprop != NULL) {
1678 				/*
1679 				 * Only set if value is non NULL, old value ok
1680 				 * if it is NULL.
1681 				 */
1682 				if (value != NULL)
1683 					set_node_attr(newprop, "value", value);
1684 			} else {
1685 				/* an entirely new property */
1686 				if (value != NULL) {
1687 					newprop = sa_create_property(name,
1688 					    value);
1689 					if (newprop != NULL) {
1690 						newprop = (sa_property_t)
1691 						    xmlAddChild(
1692 						    (xmlNodePtr)newoptionset,
1693 						    (xmlNodePtr)newprop);
1694 					}
1695 				}
1696 			}
1697 			sa_free_attr_string(name);
1698 
1699 			if (value != NULL)
1700 				sa_free_attr_string(value);
1701 		}
1702 	}
1703 	return (newoptionset);
1704 }
1705 
1706 void
1707 sa_free_derived_optionset(sa_optionset_t optionset)
1708 {
1709 	/* While it shouldn't be linked, it doesn't hurt */
1710 	if (optionset != NULL) {
1711 		xmlUnlinkNode((xmlNodePtr) optionset);
1712 		xmlFreeNode((xmlNodePtr) optionset);
1713 	}
1714 }
1715 
1716 /*
1717  *  sa_get_all_security_types(object, proto, hier)
1718  *
1719  *	Find all the security types set for this object.  This is
1720  *	preliminary to getting a derived security set. The return value is an
1721  *	optionset containg properties which are the sectype values found by
1722  *	walking up the XML document struture. The returned optionset
1723  *	is a derived optionset.
1724  *
1725  *	If hier is 0, only look at object. If non-zero, walk up the tree.
1726  */
1727 sa_optionset_t
1728 sa_get_all_security_types(void *object, char *proto, int hier)
1729 {
1730 	sa_optionset_t options;
1731 	sa_security_t security;
1732 	sa_group_t group;
1733 	sa_property_t prop;
1734 
1735 	options = NULL;
1736 
1737 	if (hier &&
1738 	    (group = sa_get_parent_group((sa_share_t)object)) != NULL)
1739 		options = sa_get_all_security_types((void *)group, proto, hier);
1740 	else
1741 		options = (sa_optionset_t)xmlNewNode(NULL,
1742 		    (xmlChar *)"optionset");
1743 
1744 	if (options == NULL)
1745 		return (options);
1746 
1747 	/* Hit the top so collect the security types working back. */
1748 	for (security = sa_get_security((sa_group_t)object, NULL, NULL);
1749 	    security != NULL;
1750 	    security = sa_get_next_security(security)) {
1751 		char *type;
1752 		char *sectype;
1753 
1754 		type = sa_get_security_attr(security, "type");
1755 		if (type != NULL) {
1756 			if (strcmp(type, proto) != 0) {
1757 				sa_free_attr_string(type);
1758 				continue;
1759 			}
1760 			sectype = sa_get_security_attr(security, "sectype");
1761 			if (sectype != NULL) {
1762 				/*
1763 				 * Have a security type, check to see if
1764 				 * already present in optionset and add if it
1765 				 * isn't.
1766 				 */
1767 				if (sa_get_property(options, sectype) == NULL) {
1768 					prop = sa_create_property(sectype,
1769 					    "true");
1770 					if (prop != NULL)
1771 						prop = (sa_property_t)
1772 						    xmlAddChild(
1773 						    (xmlNodePtr)options,
1774 						    (xmlNodePtr)prop);
1775 				}
1776 				sa_free_attr_string(sectype);
1777 			}
1778 			sa_free_attr_string(type);
1779 		}
1780 	}
1781 
1782 	return (options);
1783 }
1784 
1785 /*
1786  * sa_get_derived_security(object, sectype, proto, hier)
1787  *
1788  * Get the derived security(named optionset) for the object given the
1789  * sectype and proto. If hier is non-zero, walk up the tree to get all
1790  * properties defined for this object, otherwise just those on the
1791  * object.
1792  */
1793 
1794 sa_security_t
1795 sa_get_derived_security(void *object, char *sectype, char *proto, int hier)
1796 {
1797 	sa_security_t newsecurity;
1798 	sa_security_t security;
1799 	sa_group_t group;
1800 	sa_property_t prop;
1801 
1802 	if (hier &&
1803 	    (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
1804 		newsecurity = sa_get_derived_security((void *)group,
1805 		    sectype, proto, hier);
1806 	} else {
1807 		newsecurity = (sa_security_t)xmlNewNode(NULL,
1808 		    (xmlChar *)"security");
1809 		if (newsecurity != NULL) {
1810 			sa_set_security_attr(newsecurity, "type", proto);
1811 			sa_set_security_attr(newsecurity, "sectype", sectype);
1812 		}
1813 	}
1814 	/* Don't do anything if memory wasn't allocated */
1815 	if (newsecurity == NULL)
1816 		return (newsecurity);
1817 
1818 	/* Found the top so working back down the stack. */
1819 	security = sa_get_security((sa_security_t)object, sectype, proto);
1820 	if (security == NULL)
1821 		return (newsecurity);
1822 
1823 	/* add security to the newsecurity */
1824 	for (prop = sa_get_property(security, NULL);
1825 	    prop != NULL; prop = sa_get_next_property(prop)) {
1826 		sa_property_t newprop;
1827 		char *name;
1828 		char *value;
1829 		name = sa_get_property_attr(prop, "type");
1830 		value = sa_get_property_attr(prop, "value");
1831 		if (name != NULL) {
1832 			newprop = sa_get_property(newsecurity, name);
1833 			/* Replace the value with the new value */
1834 			if (newprop != NULL) {
1835 				/*
1836 				 * Only set if value is non NULL, old value ok
1837 				 * if it is NULL.
1838 				 */
1839 				if (value != NULL)
1840 					set_node_attr(newprop, name, value);
1841 			} else {
1842 				/* An entirely new property */
1843 				if (value != NULL) {
1844 					newprop = sa_create_property(name,
1845 					    value);
1846 					newprop = (sa_property_t)
1847 					    xmlAddChild((xmlNodePtr)newsecurity,
1848 					    (xmlNodePtr)newprop);
1849 				}
1850 			}
1851 			sa_free_attr_string(name);
1852 		}
1853 		if (value != NULL)
1854 			sa_free_attr_string(value);
1855 	}
1856 	return (newsecurity);
1857 }
1858 
1859 void
1860 sa_free_derived_security(sa_security_t security)
1861 {
1862 	/* while it shouldn't be linked, it doesn't hurt */
1863 	if (security != NULL) {
1864 		xmlUnlinkNode((xmlNodePtr)security);
1865 		xmlFreeNode((xmlNodePtr)security);
1866 	}
1867 }
1868 
1869 /*
1870  * sharetab utility functions
1871  *
1872  * Makes use of the original sharetab.c from fs.d/nfs/lib
1873  */
1874 
1875 /*
1876  * sa_fillshare(share, proto, sh)
1877  *
1878  * Fill the struct share with values obtained from the share object.
1879  */
1880 void
1881 sa_fillshare(sa_share_t share, char *proto, struct share *sh)
1882 {
1883 	char *groupname = NULL;
1884 	char *value;
1885 	sa_group_t group;
1886 	char *buff;
1887 	char *zfs;
1888 
1889 	group = sa_get_parent_group(share);
1890 	if (group != NULL) {
1891 		zfs = sa_get_group_attr(group, "zfs");
1892 		groupname = sa_get_group_attr(group, "name");
1893 
1894 		if (groupname != NULL &&
1895 		    (strcmp(groupname, "default") == 0 || zfs != NULL)) {
1896 			/*
1897 			 * since the groupname is either "default" or the
1898 			 * group is a ZFS group, we don't want to keep
1899 			 * groupname. We do want it if it is any other type of
1900 			 * group.
1901 			 */
1902 			sa_free_attr_string(groupname);
1903 			groupname = NULL;
1904 		}
1905 		if (zfs != NULL)
1906 			sa_free_attr_string(zfs);
1907 	}
1908 
1909 	value = sa_get_share_attr(share, "path");
1910 	if (value != NULL) {
1911 		sh->sh_path = strdup(value);
1912 		sa_free_attr_string(value);
1913 	}
1914 
1915 	value = sa_get_share_attr(share, "resource");
1916 	if (value != NULL || groupname != NULL) {
1917 		int len = 0;
1918 
1919 		if (value != NULL)
1920 			len += strlen(value);
1921 		if (groupname != NULL)
1922 			len += strlen(groupname);
1923 		len += 3; /* worst case */
1924 		buff = malloc(len);
1925 		(void) snprintf(buff, len, "%s%s%s",
1926 		    (value != NULL && strlen(value) > 0) ? value : "-",
1927 		    groupname != NULL ? "@" : "",
1928 		    groupname != NULL ? groupname : "");
1929 		sh->sh_res = buff;
1930 		if (value != NULL)
1931 			sa_free_attr_string(value);
1932 		if (groupname != NULL) {
1933 			sa_free_attr_string(groupname);
1934 			groupname = NULL;
1935 		}
1936 	} else {
1937 		sh->sh_res = strdup("-");
1938 	}
1939 
1940 	sh->sh_fstype = strdup(proto);
1941 	value = sa_proto_legacy_format(proto, share, 1);
1942 	if (value != NULL) {
1943 		if (strlen(value) > 0)
1944 			sh->sh_opts = strdup(value);
1945 		else
1946 			sh->sh_opts = strdup("rw");
1947 		free(value);
1948 	} else {
1949 		sh->sh_opts = strdup("rw");
1950 	}
1951 
1952 	value = sa_get_share_description(share);
1953 	if (value != NULL) {
1954 		sh->sh_descr = strdup(value);
1955 		sa_free_share_description(value);
1956 	} else {
1957 		sh->sh_descr = strdup("");
1958 	}
1959 }
1960 
1961 /*
1962  * sa_emptyshare(sh)
1963  *
1964  * Free the strings in the non-NULL members of sh.
1965  */
1966 
1967 void
1968 sa_emptyshare(struct share *sh)
1969 {
1970 	if (sh->sh_path != NULL)
1971 		free(sh->sh_path);
1972 	sh->sh_path = NULL;
1973 	if (sh->sh_res != NULL)
1974 		free(sh->sh_res);
1975 	sh->sh_res = NULL;
1976 	if (sh->sh_fstype != NULL)
1977 		free(sh->sh_fstype);
1978 	sh->sh_fstype = NULL;
1979 	if (sh->sh_opts != NULL)
1980 		free(sh->sh_opts);
1981 	sh->sh_opts = NULL;
1982 	if (sh->sh_descr != NULL)
1983 		free(sh->sh_descr);
1984 	sh->sh_descr = NULL;
1985 }
1986 
1987 /*
1988  * sa_update_sharetab(share, proto)
1989  *
1990  * Update the sharetab file with info from the specified share.
1991  * This could be an update or add.
1992  */
1993 
1994 int
1995 sa_update_sharetab(sa_share_t share, char *proto)
1996 {
1997 	int	ret = SA_OK;
1998 	share_t	sh;
1999 	char	*path;
2000 
2001 	path = sa_get_share_attr(share, "path");
2002 	if (path != NULL) {
2003 		(void) memset(&sh, '\0', sizeof (sh));
2004 
2005 		/*
2006 		 * Fill in share structure and send it to the kernel.
2007 		 */
2008 		(void) sa_fillshare(share, proto, &sh);
2009 		(void) _sharefs(SHAREFS_ADD, &sh);
2010 		sa_emptyshare(&sh);
2011 		sa_free_attr_string(path);
2012 	}
2013 
2014 	return (ret);
2015 }
2016 
2017 /*
2018  * sa_delete_sharetab(path, proto)
2019  *
2020  * remove the specified share from sharetab.
2021  */
2022 
2023 int
2024 sa_delete_sharetab(char *path, char *proto)
2025 {
2026 	int	ret = SA_OK;
2027 
2028 	share_t	sh;
2029 
2030 	/*
2031 	 * Both the path and the proto are
2032 	 * keys into the sharetab.
2033 	 */
2034 	if (path != NULL && proto != NULL) {
2035 		(void) memset(&sh, '\0', sizeof (sh));
2036 		sh.sh_path = path;
2037 		sh.sh_fstype = proto;
2038 
2039 		ret = _sharefs(SHAREFS_REMOVE, &sh);
2040 	}
2041 
2042 	return (ret);
2043 }
2044