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