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