3109 libshare_nfs: findopt() should return index
[unleashed.git] / usr / src / lib / libshare / nfs / libshare_nfs.c
blob0be7bf4690140c98508526c8098f87071c5cfa44
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
30 * NFS specific functions
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <zone.h>
38 #include <errno.h>
39 #include <locale.h>
40 #include <signal.h>
41 #include <strings.h>
42 #include "libshare.h"
43 #include "libshare_impl.h"
44 #include <nfs/export.h>
45 #include <pwd.h>
46 #include <limits.h>
47 #include <libscf.h>
48 #include <syslog.h>
49 #include <rpcsvc/daemon_utils.h>
50 #include "nfslog_config.h"
51 #include "nfslogtab.h"
52 #include "libshare_nfs.h"
53 #include <nfs/nfs.h>
54 #include <nfs/nfssys.h>
55 #include "smfcfg.h"
57 /* should really be in some global place */
58 #define DEF_WIN 30000
59 #define OPT_CHUNK 1024
61 int debug = 0;
63 #define NFS_SERVER_SVC "svc:/network/nfs/server:default"
64 #define NFS_CLIENT_SVC (char *)"svc:/network/nfs/client:default"
66 /* internal functions */
67 static int nfs_init();
68 static void nfs_fini();
69 static int nfs_enable_share(sa_share_t);
70 static int nfs_disable_share(sa_share_t, char *);
71 static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
72 static int nfs_validate_security_mode(char *);
73 static int nfs_is_security_opt(char *);
74 static int nfs_parse_legacy_options(sa_group_t, char *);
75 static char *nfs_format_options(sa_group_t, int);
76 static int nfs_set_proto_prop(sa_property_t);
77 static sa_protocol_properties_t nfs_get_proto_set();
78 static char *nfs_get_status();
79 static char *nfs_space_alias(char *);
80 static uint64_t nfs_features();
83 * ops vector that provides the protocol specific info and operations
84 * for share management.
87 struct sa_plugin_ops sa_plugin_ops = {
88 SA_PLUGIN_VERSION,
89 "nfs",
90 nfs_init,
91 nfs_fini,
92 nfs_enable_share,
93 nfs_disable_share,
94 nfs_validate_property,
95 nfs_validate_security_mode,
96 nfs_is_security_opt,
97 nfs_parse_legacy_options,
98 nfs_format_options,
99 nfs_set_proto_prop,
100 nfs_get_proto_set,
101 nfs_get_status,
102 nfs_space_alias,
103 NULL, /* update_legacy */
104 NULL, /* delete_legacy */
105 NULL, /* change_notify */
106 NULL, /* enable_resource */
107 NULL, /* disable_resource */
108 nfs_features,
109 NULL, /* transient shares */
110 NULL, /* notify resource */
111 NULL, /* rename_resource */
112 NULL, /* run_command */
113 NULL, /* command_help */
114 NULL /* delete_proto_section */
118 * list of support services needed
119 * defines should come from head/rpcsvc/daemon_utils.h
122 static char *service_list_default[] =
123 { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
124 static char *service_list_logging[] =
125 { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED,
126 NULL };
129 * option definitions. Make sure to keep the #define for the option
130 * index just before the entry it is the index for. Changing the order
131 * can cause breakage. E.g OPT_RW is index 1 and must precede the
132 * line that includes the SHOPT_RW and OPT_RW entries.
135 struct option_defs optdefs[] = {
136 #define OPT_RO 0
137 {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
138 #define OPT_RW 1
139 {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
140 #define OPT_ROOT 2
141 {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
142 #define OPT_SECURE 3
143 {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
144 #define OPT_ANON 4
145 {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
146 #define OPT_WINDOW 5
147 {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
148 #define OPT_NOSUID 6
149 {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
150 #define OPT_ACLOK 7
151 {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
152 #define OPT_NOSUB 8
153 {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
154 #define OPT_SEC 9
155 {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
156 #define OPT_PUBLIC 10
157 {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
158 #define OPT_INDEX 11
159 {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
160 #define OPT_LOG 12
161 {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
162 #define OPT_CKSUM 13
163 {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
164 #define OPT_NONE 14
165 {SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST},
166 #define OPT_ROOT_MAPPING 15
167 {SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER},
168 #define OPT_CHARSET_MAP 16
169 {"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST},
170 #define OPT_NOACLFAB 17
171 {SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN},
172 #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
173 #define OPT_VOLFH 18
174 {SHOPT_VOLFH, OPT_VOLFH},
175 #endif /* VOLATILE_FH_TEST */
176 NULL
180 * Codesets that may need to be converted to UTF-8 for file paths.
181 * Add new names here to add new property support. If we ever get a
182 * way to query the kernel for character sets, this should become
183 * dynamically loaded. Make sure changes here are reflected in
184 * cmd/fs.d/nfs/mountd/nfscmd.c
187 static char *legal_conv[] = {
188 "euc-cn",
189 "euc-jp",
190 "euc-jpms",
191 "euc-kr",
192 "euc-tw",
193 "iso8859-1",
194 "iso8859-2",
195 "iso8859-5",
196 "iso8859-6",
197 "iso8859-7",
198 "iso8859-8",
199 "iso8859-9",
200 "iso8859-13",
201 "iso8859-15",
202 "koi8-r",
203 NULL
207 * list of properties that are related to security flavors.
209 static char *seclist[] = {
210 SHOPT_RO,
211 SHOPT_RW,
212 SHOPT_ROOT,
213 SHOPT_WINDOW,
214 SHOPT_NONE,
215 SHOPT_ROOT_MAPPING,
216 NULL
219 /* structure for list of securities */
220 struct securities {
221 sa_security_t security;
222 struct securities *next;
226 * findcharset(charset)
228 * Returns B_TRUE if the charset is a legal conversion otherwise
229 * B_FALSE. This will need to be rewritten to be more efficient when
230 * we have a dynamic list of legal conversions.
233 static boolean_t
234 findcharset(char *charset)
236 int i;
238 for (i = 0; legal_conv[i] != NULL; i++)
239 if (strcmp(charset, legal_conv[i]) == 0)
240 return (B_TRUE);
241 return (B_FALSE);
245 * findopt(name)
247 * Lookup option "name" in the option table and return the table
248 * index.
251 static int
252 findopt(char *name)
254 int i;
255 if (name != NULL) {
256 for (i = 0; optdefs[i].tag != NULL; i++) {
257 if (strcmp(optdefs[i].tag, name) == 0)
258 return (optdefs[i].index);
260 if (findcharset(name))
261 return (OPT_CHARSET_MAP);
263 return (-1);
267 * gettype(name)
269 * Return the type of option "name".
272 static int
273 gettype(char *name)
275 int optdef;
277 optdef = findopt(name);
278 if (optdef != -1)
279 return (optdefs[optdef].type);
280 return (OPT_TYPE_ANY);
284 * nfs_validate_security_mode(mode)
286 * is the specified mode string a valid one for use with NFS?
289 static int
290 nfs_validate_security_mode(char *mode)
292 seconfig_t secinfo;
293 int err;
295 (void) memset(&secinfo, '\0', sizeof (secinfo));
296 err = nfs_getseconfig_byname(mode, &secinfo);
297 if (err == SC_NOERROR)
298 return (1);
299 return (0);
303 * nfs_is_security_opt(tok)
305 * check to see if tok represents an option that is only valid in some
306 * security flavor.
309 static int
310 nfs_is_security_opt(char *tok)
312 int i;
314 for (i = 0; seclist[i] != NULL; i++) {
315 if (strcmp(tok, seclist[i]) == 0)
316 return (1);
318 return (0);
322 * find_security(seclist, sec)
324 * Walk the current list of security flavors and return true if it is
325 * present, else return false.
328 static int
329 find_security(struct securities *seclist, sa_security_t sec)
331 while (seclist != NULL) {
332 if (seclist->security == sec)
333 return (1);
334 seclist = seclist->next;
336 return (0);
340 * make_security_list(group, securitymodes, proto)
341 * go through the list of securitymodes and add them to the
342 * group's list of security optionsets. We also keep a list of
343 * those optionsets so we don't have to find them later. All of
344 * these will get copies of the same properties.
347 static struct securities *
348 make_security_list(sa_group_t group, char *securitymodes, char *proto)
350 char *tok, *next = NULL;
351 struct securities *curp, *headp = NULL, *prev;
352 sa_security_t check;
353 int freetok = 0;
355 for (tok = securitymodes; tok != NULL; tok = next) {
356 next = strchr(tok, ':');
357 if (next != NULL)
358 *next++ = '\0';
359 if (strcmp(tok, "default") == 0) {
360 /* resolve default into the real type */
361 tok = nfs_space_alias(tok);
362 freetok = 1;
364 check = sa_get_security(group, tok, proto);
366 /* add to the security list if it isn't there already */
367 if (check == NULL || !find_security(headp, check)) {
368 curp = (struct securities *)calloc(1,
369 sizeof (struct securities));
370 if (curp != NULL) {
371 if (check == NULL) {
372 curp->security = sa_create_security(
373 group, tok, proto);
374 } else {
375 curp->security = check;
378 * note that the first time through the loop,
379 * headp will be NULL and prev will be
380 * undefined. Since headp is NULL, we set
381 * both it and prev to the curp (first
382 * structure to be allocated).
384 * later passes through the loop will have
385 * headp not being NULL and prev will be used
386 * to allocate at the end of the list.
388 if (headp == NULL) {
389 headp = curp;
390 prev = curp;
391 } else {
392 prev->next = curp;
393 prev = curp;
398 if (freetok) {
399 freetok = 0;
400 sa_free_attr_string(tok);
403 return (headp);
406 static void
407 free_security_list(struct securities *sec)
409 struct securities *next;
410 if (sec != NULL) {
411 for (next = sec->next; sec != NULL; sec = next) {
412 next = sec->next;
413 free(sec);
419 * nfs_alistcat(str1, str2, sep)
421 * concatenate str1 and str2 into a new string using sep as a separate
422 * character. If memory allocation fails, return NULL;
425 static char *
426 nfs_alistcat(char *str1, char *str2, char sep)
428 char *newstr;
429 size_t len;
431 len = strlen(str1) + strlen(str2) + 2;
432 newstr = (char *)malloc(len);
433 if (newstr != NULL)
434 (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
435 return (newstr);
439 * add_security_prop(sec, name, value, persist)
441 * Add the property to the securities structure. This accumulates
442 * properties for as part of parsing legacy options.
445 static int
446 add_security_prop(struct securities *sec, char *name, char *value,
447 int persist, int iszfs)
449 sa_property_t prop;
450 int ret = SA_OK;
452 for (; sec != NULL; sec = sec->next) {
453 if (value == NULL) {
454 if (strcmp(name, SHOPT_RW) == 0 ||
455 strcmp(name, SHOPT_RO) == 0)
456 value = "*";
457 else
458 value = "true";
462 * Get the existing property, if it exists, so we can
463 * determine what to do with it. The ro/rw/root
464 * properties can be merged if multiple instances of
465 * these properies are given. For example, if "rw"
466 * exists with a value "host1" and a later token of
467 * rw="host2" is seen, the values are merged into a
468 * single rw="host1:host2".
470 prop = sa_get_property(sec->security, name);
472 if (prop != NULL) {
473 char *oldvalue;
474 char *newvalue;
477 * The security options of ro/rw/root might appear
478 * multiple times. If they do, the values need to be
479 * merged into an access list. If it was previously
480 * empty, the new value alone is added.
482 oldvalue = sa_get_property_attr(prop, "value");
483 if (oldvalue != NULL) {
485 * The general case is to concatenate the new
486 * value onto the old value for multiple
487 * rw(ro/root) properties. A special case
488 * exists when either the old or new is the
489 * "all" case. In the special case, if both
490 * are "all", then it is "all", else if one is
491 * an access-list, that replaces the "all".
493 if (strcmp(oldvalue, "*") == 0) {
494 /* Replace old value with new value. */
495 newvalue = strdup(value);
496 } else if (strcmp(value, "*") == 0 ||
497 strcmp(oldvalue, value) == 0) {
499 * Keep old value and ignore
500 * the new value.
502 newvalue = NULL;
503 } else {
505 * Make a new list of old plus new
506 * access-list.
508 newvalue = nfs_alistcat(oldvalue,
509 value, ':');
512 if (newvalue != NULL) {
513 (void) sa_remove_property(prop);
514 prop = sa_create_property(name,
515 newvalue);
516 ret = sa_add_property(sec->security,
517 prop);
518 free(newvalue);
520 if (oldvalue != NULL)
521 sa_free_attr_string(oldvalue);
523 } else {
524 prop = sa_create_property(name, value);
525 ret = sa_add_property(sec->security, prop);
527 if (ret == SA_OK && !iszfs) {
528 ret = sa_commit_properties(sec->security, !persist);
531 return (ret);
535 * check to see if group/share is persistent.
537 static int
538 is_persistent(sa_group_t group)
540 char *type;
541 int persist = 1;
543 type = sa_get_group_attr(group, "type");
544 if (type != NULL && strcmp(type, "persist") != 0)
545 persist = 0;
546 if (type != NULL)
547 sa_free_attr_string(type);
548 return (persist);
552 * invalid_security(options)
554 * search option string for any invalid sec= type.
555 * return true (1) if any are not valid else false (0)
557 static int
558 invalid_security(char *options)
560 char *copy, *base, *token, *value;
561 int ret = 0;
563 copy = strdup(options);
564 token = base = copy;
565 while (token != NULL && ret == 0) {
566 token = strtok(base, ",");
567 base = NULL;
568 if (token != NULL) {
569 value = strchr(token, '=');
570 if (value != NULL)
571 *value++ = '\0';
572 if (strcmp(token, "sec") == 0) {
573 /* HAVE security flavors so check them */
574 char *tok, *next;
575 for (next = NULL, tok = value; tok != NULL;
576 tok = next) {
577 next = strchr(tok, ':');
578 if (next != NULL)
579 *next++ = '\0';
580 ret = !nfs_validate_security_mode(tok);
581 if (ret)
582 break;
587 if (copy != NULL)
588 free(copy);
589 return (ret);
593 * nfs_parse_legacy_options(group, options)
595 * Parse the old style options into internal format and store on the
596 * specified group. Group could be a share for full legacy support.
599 static int
600 nfs_parse_legacy_options(sa_group_t group, char *options)
602 char *dup;
603 char *base;
604 char *token;
605 sa_optionset_t optionset;
606 struct securities *security_list = NULL;
607 sa_property_t prop;
608 int ret = SA_OK;
609 int iszfs = 0;
610 sa_group_t parent;
611 int persist = 0;
612 char *lasts;
614 /* do we have an existing optionset? */
615 optionset = sa_get_optionset(group, "nfs");
616 if (optionset == NULL) {
617 /* didn't find existing optionset so create one */
618 optionset = sa_create_optionset(group, "nfs");
619 } else {
621 * Have an existing optionset . Ideally, we would need
622 * to compare options in order to detect errors. For
623 * now, we assume that the first optionset is the
624 * correct one and the others will be the same. An
625 * empty optionset is the same as no optionset so we
626 * don't want to exit in that case. Getting an empty
627 * optionset can occur with ZFS property checking.
629 if (sa_get_property(optionset, NULL) != NULL)
630 return (ret);
633 if (strcmp(options, SHOPT_RW) == 0) {
635 * there is a special case of only the option "rw"
636 * being the default option. We don't have to do
637 * anything.
639 return (ret);
643 * check if security types are present and validate them. If
644 * any are not legal, fail.
647 if (invalid_security(options)) {
648 return (SA_INVALID_SECURITY);
652 * in order to not attempt to change ZFS properties unless
653 * absolutely necessary, we never do it in the legacy parsing.
655 if (sa_is_share(group)) {
656 char *zfs;
657 parent = sa_get_parent_group(group);
658 if (parent != NULL) {
659 zfs = sa_get_group_attr(parent, "zfs");
660 if (zfs != NULL) {
661 sa_free_attr_string(zfs);
662 iszfs++;
665 } else {
666 iszfs = sa_group_is_zfs(group);
669 /* We need a copy of options for the next part. */
670 dup = strdup(options);
671 if (dup == NULL)
672 return (SA_NO_MEMORY);
675 * we need to step through each option in the string and then
676 * add either the option or the security option as needed. If
677 * this is not a persistent share, don't commit to the
678 * repository. If there is an error, we also want to abort the
679 * processing and report it.
681 persist = is_persistent(group);
682 base = dup;
683 token = dup;
684 lasts = NULL;
685 while (token != NULL && ret == SA_OK) {
686 ret = SA_OK;
687 token = strtok_r(base, ",", &lasts);
688 base = NULL;
689 if (token != NULL) {
690 char *value;
692 * if the option has a value, it will have an '=' to
693 * separate the name from the value. The following
694 * code will result in value != NULL and token
695 * pointing to just the name if there is a value.
697 value = strchr(token, '=');
698 if (value != NULL) {
699 *value++ = '\0';
701 if (strcmp(token, "sec") == 0 ||
702 strcmp(token, "secure") == 0) {
704 * Once in security parsing, we only
705 * do security. We do need to move
706 * between the security node and the
707 * toplevel. The security tag goes on
708 * the root while the following ones
709 * go on the security.
711 if (security_list != NULL) {
713 * have an old list so close it and
714 * start the new
716 free_security_list(security_list);
718 if (strcmp(token, "secure") == 0) {
719 value = "dh";
720 } else {
721 if (value == NULL) {
722 ret = SA_SYNTAX_ERR;
723 break;
726 security_list = make_security_list(group,
727 value, "nfs");
728 } else {
730 * Note that the "old" syntax allowed a
731 * default security model This must be
732 * accounted for and internally converted to
733 * "standard" security structure.
735 if (nfs_is_security_opt(token)) {
736 if (security_list == NULL) {
738 * need to have a
739 * security
740 * option. This will
741 * be "closed" when a
742 * defined "sec="
743 * option is
744 * seen. This is
745 * technically an
746 * error but will be
747 * allowed with
748 * warning.
750 security_list =
751 make_security_list(group,
752 "default",
753 "nfs");
755 if (security_list != NULL) {
756 ret = add_security_prop(
757 security_list, token,
758 value, persist, iszfs);
759 } else {
760 ret = SA_NO_MEMORY;
762 } else {
763 /* regular options */
764 if (value == NULL) {
765 if (strcmp(token, SHOPT_RW) ==
766 0 || strcmp(token,
767 SHOPT_RO) == 0) {
768 value = "*";
769 } else {
770 value = "global";
771 if (strcmp(token,
772 SHOPT_LOG) != 0) {
773 value = "true";
778 * In all cases, create the
779 * property specified. If the
780 * value was NULL, the default
781 * value will have been
782 * substituted.
784 prop = sa_create_property(token, value);
785 ret = sa_add_property(optionset, prop);
786 if (ret != SA_OK)
787 break;
789 if (!iszfs) {
790 ret = sa_commit_properties(
791 optionset, !persist);
797 if (security_list != NULL)
798 free_security_list(security_list);
800 free(dup);
801 return (ret);
805 * is_a_number(number)
807 * is the string a number in one of the forms we want to use?
810 static int
811 is_a_number(char *number)
813 int ret = 1;
814 int hex = 0;
816 if (strncmp(number, "0x", 2) == 0) {
817 number += 2;
818 hex = 1;
819 } else if (*number == '-') {
820 number++; /* skip the minus */
822 while (ret == 1 && *number != '\0') {
823 if (hex) {
824 ret = isxdigit(*number++);
825 } else {
826 ret = isdigit(*number++);
829 return (ret);
833 * Look for the specified tag in the configuration file. If it is found,
834 * enable logging and set the logging configuration information for exp.
836 static void
837 configlog(struct exportdata *exp, char *tag)
839 nfsl_config_t *configlist = NULL, *configp;
840 int error = 0;
841 char globaltag[] = DEFAULTTAG;
844 * Sends config errors to stderr
846 nfsl_errs_to_syslog = B_FALSE;
849 * get the list of configuration settings
851 error = nfsl_getconfig_list(&configlist);
852 if (error) {
853 (void) fprintf(stderr,
854 dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"),
855 strerror(error));
858 if (tag == NULL)
859 tag = globaltag;
860 if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
861 nfsl_freeconfig_list(&configlist);
862 (void) fprintf(stderr,
863 dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag);
864 /* bad configuration */
865 error = ENOENT;
866 goto err;
869 if ((exp->ex_tag = strdup(tag)) == NULL) {
870 error = ENOMEM;
871 goto out;
873 if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
874 error = ENOMEM;
875 goto out;
877 exp->ex_flags |= EX_LOG;
878 if (configp->nc_rpclogpath != NULL)
879 exp->ex_flags |= EX_LOG_ALLOPS;
880 out:
881 if (configlist != NULL)
882 nfsl_freeconfig_list(&configlist);
884 err:
885 if (error != 0) {
886 if (exp->ex_flags != NULL)
887 free(exp->ex_tag);
888 if (exp->ex_log_buffer != NULL)
889 free(exp->ex_log_buffer);
890 (void) fprintf(stderr,
891 dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"),
892 strerror(error));
897 * fill_export_from_optionset(export, optionset)
899 * In order to share, we need to set all the possible general options
900 * into the export structure. Share info will be filled in by the
901 * caller. Various property values get turned into structure specific
902 * values.
905 static int
906 fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
908 sa_property_t option;
909 int ret = SA_OK;
911 for (option = sa_get_property(optionset, NULL);
912 option != NULL; option = sa_get_next_property(option)) {
913 char *name;
914 char *value;
915 uint32_t val;
918 * since options may be set/reset multiple times, always do an
919 * explicit set or clear of the option. This allows defaults
920 * to be set and then the protocol specific to override.
923 name = sa_get_property_attr(option, "type");
924 value = sa_get_property_attr(option, "value");
925 switch (findopt(name)) {
926 case OPT_ANON:
927 if (value != NULL && is_a_number(value)) {
928 val = strtoul(value, NULL, 0);
929 } else {
930 struct passwd *pw;
931 pw = getpwnam(value != NULL ? value : "nobody");
932 if (pw != NULL) {
933 val = pw->pw_uid;
934 } else {
935 val = UID_NOBODY;
937 endpwent();
939 export->ex_anon = val;
940 break;
941 case OPT_NOSUID:
942 if (value != NULL && (strcasecmp(value, "true") == 0 ||
943 strcmp(value, "1") == 0))
944 export->ex_flags |= EX_NOSUID;
945 else
946 export->ex_flags &= ~EX_NOSUID;
947 break;
948 case OPT_ACLOK:
949 if (value != NULL && (strcasecmp(value, "true") == 0 ||
950 strcmp(value, "1") == 0))
951 export->ex_flags |= EX_ACLOK;
952 else
953 export->ex_flags &= ~EX_ACLOK;
954 break;
955 case OPT_NOSUB:
956 if (value != NULL && (strcasecmp(value, "true") == 0 ||
957 strcmp(value, "1") == 0))
958 export->ex_flags |= EX_NOSUB;
959 else
960 export->ex_flags &= ~EX_NOSUB;
961 break;
962 case OPT_PUBLIC:
963 if (value != NULL && (strcasecmp(value, "true") == 0 ||
964 strcmp(value, "1") == 0))
965 export->ex_flags |= EX_PUBLIC;
966 else
967 export->ex_flags &= ~EX_PUBLIC;
968 break;
969 case OPT_INDEX:
970 if (value != NULL && (strcmp(value, "..") == 0 ||
971 strchr(value, '/') != NULL)) {
972 /* this is an error */
973 (void) printf(dgettext(TEXT_DOMAIN,
974 "NFS: index=\"%s\" not valid;"
975 "must be a filename.\n"),
976 value);
977 break;
979 if (value != NULL && *value != '\0' &&
980 strcmp(value, ".") != 0) {
981 /* valid index file string */
982 if (export->ex_index != NULL) {
983 /* left over from "default" */
984 free(export->ex_index);
986 /* remember to free */
987 export->ex_index = strdup(value);
988 if (export->ex_index == NULL) {
989 (void) printf(dgettext(TEXT_DOMAIN,
990 "NFS: out of memory setting "
991 "index property\n"));
992 break;
994 export->ex_flags |= EX_INDEX;
996 break;
997 case OPT_LOG:
998 if (value == NULL)
999 value = strdup("global");
1000 if (value != NULL)
1001 configlog(export,
1002 strlen(value) ? value : "global");
1003 break;
1004 case OPT_CHARSET_MAP:
1006 * Set EX_CHARMAP when there is at least one
1007 * charmap conversion property. This will get
1008 * checked by the nfs server when it needs to.
1010 export->ex_flags |= EX_CHARMAP;
1011 break;
1012 case OPT_NOACLFAB:
1013 if (value != NULL && (strcasecmp(value, "true") == 0 ||
1014 strcmp(value, "1") == 0))
1015 export->ex_flags |= EX_NOACLFAB;
1016 else
1017 export->ex_flags &= ~EX_NOACLFAB;
1018 break;
1019 default:
1020 /* have a syntactic error */
1021 (void) printf(dgettext(TEXT_DOMAIN,
1022 "NFS: unrecognized option %s=%s\n"),
1023 name != NULL ? name : "",
1024 value != NULL ? value : "");
1025 break;
1027 if (name != NULL)
1028 sa_free_attr_string(name);
1029 if (value != NULL)
1030 sa_free_attr_string(value);
1032 return (ret);
1036 * cleanup_export(export)
1038 * Cleanup the allocated areas so we don't leak memory
1041 static void
1042 cleanup_export(struct exportdata *export)
1044 int i;
1046 if (export->ex_index != NULL)
1047 free(export->ex_index);
1048 if (export->ex_secinfo != NULL) {
1049 for (i = 0; i < export->ex_seccnt; i++)
1050 if (export->ex_secinfo[i].s_rootnames != NULL)
1051 free(export->ex_secinfo[i].s_rootnames);
1052 free(export->ex_secinfo);
1057 * Given a seconfig entry and a colon-separated
1058 * list of names, allocate an array big enough
1059 * to hold the root list, then convert each name to
1060 * a principal name according to the security
1061 * info and assign it to an array element.
1062 * Return the array and its size.
1064 static caddr_t *
1065 get_rootnames(seconfig_t *sec, char *list, int *count)
1067 caddr_t *a;
1068 int c, i;
1069 char *host, *p;
1072 * Count the number of strings in the list.
1073 * This is the number of colon separators + 1.
1075 c = 1;
1076 for (p = list; *p; p++)
1077 if (*p == ':')
1078 c++;
1079 *count = c;
1081 a = (caddr_t *)malloc(c * sizeof (char *));
1082 if (a == NULL) {
1083 (void) printf(dgettext(TEXT_DOMAIN,
1084 "get_rootnames: no memory\n"));
1085 } else {
1086 for (i = 0; i < c; i++) {
1087 host = strtok(list, ":");
1088 if (!nfs_get_root_principal(sec, host, &a[i])) {
1089 free(a);
1090 a = NULL;
1091 break;
1093 list = NULL;
1097 return (a);
1101 * fill_security_from_secopts(sp, secopts)
1103 * Fill the secinfo structure from the secopts optionset.
1106 static int
1107 fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
1109 sa_property_t prop;
1110 char *type;
1111 int longform;
1112 int err = SC_NOERROR;
1113 uint32_t val;
1115 type = sa_get_security_attr(secopts, "sectype");
1116 if (type != NULL) {
1117 /* named security type needs secinfo to be filled in */
1118 err = nfs_getseconfig_byname(type, &sp->s_secinfo);
1119 sa_free_attr_string(type);
1120 if (err != SC_NOERROR)
1121 return (err);
1122 } else {
1123 /* default case */
1124 err = nfs_getseconfig_default(&sp->s_secinfo);
1125 if (err != SC_NOERROR)
1126 return (err);
1129 err = SA_OK;
1130 for (prop = sa_get_property(secopts, NULL);
1131 prop != NULL && err == SA_OK;
1132 prop = sa_get_next_property(prop)) {
1133 char *name;
1134 char *value;
1136 name = sa_get_property_attr(prop, "type");
1137 value = sa_get_property_attr(prop, "value");
1139 longform = value != NULL && strcmp(value, "*") != 0;
1141 switch (findopt(name)) {
1142 case OPT_RO:
1143 sp->s_flags |= longform ? M_ROL : M_RO;
1144 break;
1145 case OPT_RW:
1146 sp->s_flags |= longform ? M_RWL : M_RW;
1147 break;
1148 case OPT_ROOT:
1149 sp->s_flags |= M_ROOT;
1151 * if we are using AUTH_UNIX, handle like other things
1152 * such as RO/RW
1154 if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
1155 continue;
1156 /* not AUTH_UNIX */
1157 if (value != NULL) {
1158 sp->s_rootnames = get_rootnames(&sp->s_secinfo,
1159 value, &sp->s_rootcnt);
1160 if (sp->s_rootnames == NULL) {
1161 err = SA_BAD_VALUE;
1162 (void) fprintf(stderr,
1163 dgettext(TEXT_DOMAIN,
1164 "Bad root list\n"));
1167 break;
1168 case OPT_NONE:
1169 sp->s_flags |= M_NONE;
1170 break;
1171 case OPT_WINDOW:
1172 if (value != NULL) {
1173 sp->s_window = atoi(value);
1174 /* just in case */
1175 if (sp->s_window < 0)
1176 sp->s_window = DEF_WIN;
1178 break;
1179 case OPT_ROOT_MAPPING:
1180 if (value != NULL && is_a_number(value)) {
1181 val = strtoul(value, NULL, 0);
1182 } else {
1183 struct passwd *pw;
1184 pw = getpwnam(value != NULL ? value : "nobody");
1185 if (pw != NULL) {
1186 val = pw->pw_uid;
1187 } else {
1188 val = UID_NOBODY;
1190 endpwent();
1192 sp->s_rootid = val;
1193 break;
1194 default:
1195 break;
1197 if (name != NULL)
1198 sa_free_attr_string(name);
1199 if (value != NULL)
1200 sa_free_attr_string(value);
1202 /* if rw/ro options not set, use default of RW */
1203 if ((sp->s_flags & NFS_RWMODES) == 0)
1204 sp->s_flags |= M_RW;
1205 return (err);
1209 * This is for testing only
1210 * It displays the export structure that
1211 * goes into the kernel.
1213 static void
1214 printarg(char *path, struct exportdata *ep)
1216 int i, j;
1217 struct secinfo *sp;
1219 if (debug == 0)
1220 return;
1222 (void) printf("%s:\n", path);
1223 (void) printf("\tex_version = %d\n", ep->ex_version);
1224 (void) printf("\tex_path = %s\n", ep->ex_path);
1225 (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen);
1226 (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
1227 if (ep->ex_flags & EX_NOSUID)
1228 (void) printf("NOSUID ");
1229 if (ep->ex_flags & EX_ACLOK)
1230 (void) printf("ACLOK ");
1231 if (ep->ex_flags & EX_PUBLIC)
1232 (void) printf("PUBLIC ");
1233 if (ep->ex_flags & EX_NOSUB)
1234 (void) printf("NOSUB ");
1235 if (ep->ex_flags & EX_LOG)
1236 (void) printf("LOG ");
1237 if (ep->ex_flags & EX_CHARMAP)
1238 (void) printf("CHARMAP ");
1239 if (ep->ex_flags & EX_LOG_ALLOPS)
1240 (void) printf("LOG_ALLOPS ");
1241 if (ep->ex_flags == 0)
1242 (void) printf("(none)");
1243 (void) printf("\n");
1244 if (ep->ex_flags & EX_LOG) {
1245 (void) printf("\tex_log_buffer = %s\n",
1246 (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
1247 (void) printf("\tex_tag = %s\n",
1248 (ep->ex_tag ? ep->ex_tag : "(NULL)"));
1250 (void) printf("\tex_anon = %d\n", ep->ex_anon);
1251 (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
1252 (void) printf("\n");
1253 for (i = 0; i < ep->ex_seccnt; i++) {
1254 sp = &ep->ex_secinfo[i];
1255 (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
1256 (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
1257 if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
1258 if (sp->s_flags & M_RO) (void) printf("M_RO ");
1259 if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
1260 if (sp->s_flags & M_RW) (void) printf("M_RW ");
1261 if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
1262 if (sp->s_flags & M_NONE) (void) printf("M_NONE ");
1263 if (sp->s_flags == 0) (void) printf("(none)");
1264 (void) printf("\n");
1265 (void) printf("\t\ts_window = %d\n", sp->s_window);
1266 (void) printf("\t\ts_rootid = %d\n", sp->s_rootid);
1267 (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
1268 (void) fflush(stdout);
1269 for (j = 0; j < sp->s_rootcnt; j++)
1270 (void) printf("%s ", sp->s_rootnames[j] ?
1271 sp->s_rootnames[j] : "<null>");
1272 (void) printf("\n\n");
1277 * count_security(opts)
1279 * Count the number of security types (flavors). The optionset has
1280 * been populated with the security flavors as a holding mechanism.
1281 * We later use this number to allocate data structures.
1284 static int
1285 count_security(sa_optionset_t opts)
1287 int count = 0;
1288 sa_property_t prop;
1289 if (opts != NULL) {
1290 for (prop = sa_get_property(opts, NULL); prop != NULL;
1291 prop = sa_get_next_property(prop)) {
1292 count++;
1295 return (count);
1299 * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1301 * provides a mechanism to format NFS properties into legacy output
1302 * format. If the buffer would overflow, it is reallocated and grown
1303 * as appropriate. Special cases of converting internal form of values
1304 * to those used by "share" are done. this function does one property
1305 * at a time.
1308 static int
1309 nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1310 sa_property_t prop, int sep)
1312 char *name;
1313 char *value;
1314 int curlen;
1315 char *buff = *rbuff;
1316 size_t buffsize = *rbuffsize;
1317 int printed = B_FALSE;
1319 name = sa_get_property_attr(prop, "type");
1320 value = sa_get_property_attr(prop, "value");
1321 if (buff != NULL)
1322 curlen = strlen(buff);
1323 else
1324 curlen = 0;
1325 if (name != NULL) {
1326 int len;
1327 len = strlen(name) + sep;
1330 * A future RFE would be to replace this with more
1331 * generic code and to possibly handle more types.
1333 switch (gettype(name)) {
1334 case OPT_TYPE_BOOLEAN:
1336 * For NFS, boolean value of FALSE means it
1337 * doesn't show up in the option list at all.
1339 if (value != NULL && strcasecmp(value, "false") == 0)
1340 goto skip;
1341 if (value != NULL) {
1342 sa_free_attr_string(value);
1343 value = NULL;
1345 break;
1346 case OPT_TYPE_ACCLIST:
1347 if (value != NULL && strcmp(value, "*") == 0) {
1348 sa_free_attr_string(value);
1349 value = NULL;
1350 } else {
1351 if (value != NULL)
1352 len += 1 + strlen(value);
1354 break;
1355 case OPT_TYPE_LOGTAG:
1356 if (value != NULL && strlen(value) == 0) {
1357 sa_free_attr_string(value);
1358 value = NULL;
1359 } else {
1360 if (value != NULL)
1361 len += 1 + strlen(value);
1363 break;
1364 default:
1365 if (value != NULL)
1366 len += 1 + strlen(value);
1367 break;
1369 while (buffsize <= (curlen + len)) {
1370 /* need more room */
1371 buffsize += incr;
1372 buff = realloc(buff, buffsize);
1373 if (buff == NULL) {
1374 /* realloc failed so free everything */
1375 if (*rbuff != NULL)
1376 free(*rbuff);
1378 *rbuff = buff;
1379 *rbuffsize = buffsize;
1380 if (buff == NULL)
1381 goto skip;
1385 if (buff == NULL)
1386 goto skip;
1388 if (value == NULL) {
1389 (void) snprintf(buff + curlen, buffsize - curlen,
1390 "%s%s", sep ? "," : "",
1391 name, value != NULL ? value : "");
1392 } else {
1393 (void) snprintf(buff + curlen, buffsize - curlen,
1394 "%s%s=%s", sep ? "," : "",
1395 name, value != NULL ? value : "");
1397 printed = B_TRUE;
1399 skip:
1400 if (name != NULL)
1401 sa_free_attr_string(name);
1402 if (value != NULL)
1403 sa_free_attr_string(value);
1404 return (printed);
1408 * nfs_format_options(group, hier)
1410 * format all the options on the group into an old-style option
1411 * string. If hier is non-zero, walk up the tree to get inherited
1412 * options.
1415 static char *
1416 nfs_format_options(sa_group_t group, int hier)
1418 sa_optionset_t options = NULL;
1419 sa_optionset_t secoptions = NULL;
1420 sa_property_t prop, secprop;
1421 sa_security_t security = NULL;
1422 char *buff;
1423 size_t buffsize;
1424 char *sectype = NULL;
1425 int sep = 0;
1428 buff = malloc(OPT_CHUNK);
1429 if (buff == NULL) {
1430 return (NULL);
1433 buff[0] = '\0';
1434 buffsize = OPT_CHUNK;
1437 * We may have a an optionset relative to this item. format
1438 * these if we find them and then add any security definitions.
1441 options = sa_get_derived_optionset(group, "nfs", hier);
1444 * do the default set first but skip any option that is also
1445 * in the protocol specific optionset.
1447 if (options != NULL) {
1448 for (prop = sa_get_property(options, NULL);
1449 prop != NULL; prop = sa_get_next_property(prop)) {
1451 * use this one since we skipped any
1452 * of these that were also in
1453 * optdefault
1455 if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
1456 prop, sep))
1457 sep = 1;
1458 if (buff == NULL) {
1460 * buff could become NULL if there
1461 * isn't enough memory for
1462 * nfs_sprint_option to realloc()
1463 * as necessary. We can't really
1464 * do anything about it at this
1465 * point so we return NULL. The
1466 * caller should handle the
1467 * failure.
1469 if (options != NULL)
1470 sa_free_derived_optionset(
1471 options);
1472 return (buff);
1476 secoptions = (sa_optionset_t)sa_get_all_security_types(group,
1477 "nfs", hier);
1478 if (secoptions != NULL) {
1479 for (secprop = sa_get_property(secoptions, NULL);
1480 secprop != NULL;
1481 secprop = sa_get_next_property(secprop)) {
1482 sectype = sa_get_property_attr(secprop, "type");
1483 security =
1484 (sa_security_t)sa_get_derived_security(
1485 group, sectype, "nfs", hier);
1486 if (security != NULL) {
1487 if (sectype != NULL) {
1488 prop = sa_create_property(
1489 "sec", sectype);
1490 if (prop == NULL)
1491 goto err;
1492 if (nfs_sprint_option(&buff,
1493 &buffsize, OPT_CHUNK, prop, sep))
1494 sep = 1;
1495 (void) sa_remove_property(prop);
1496 if (buff == NULL)
1497 goto err;
1499 for (prop = sa_get_property(security,
1500 NULL); prop != NULL;
1501 prop = sa_get_next_property(prop)) {
1502 if (nfs_sprint_option(&buff,
1503 &buffsize, OPT_CHUNK, prop, sep))
1504 sep = 1;
1505 if (buff == NULL)
1506 goto err;
1508 sa_free_derived_optionset(security);
1510 if (sectype != NULL)
1511 sa_free_attr_string(sectype);
1513 sa_free_derived_optionset(secoptions);
1516 if (options != NULL)
1517 sa_free_derived_optionset(options);
1518 return (buff);
1520 err:
1522 * If we couldn't allocate memory for option printing, we need
1523 * to break out of the nested loops, cleanup and return NULL.
1525 if (secoptions != NULL)
1526 sa_free_derived_optionset(secoptions);
1527 if (security != NULL)
1528 sa_free_derived_optionset(security);
1529 if (sectype != NULL)
1530 sa_free_attr_string(sectype);
1531 if (options != NULL)
1532 sa_free_derived_optionset(options);
1533 return (buff);
1537 * Append an entry to the nfslogtab file
1539 static int
1540 nfslogtab_add(dir, buffer, tag)
1541 char *dir, *buffer, *tag;
1543 FILE *f;
1544 struct logtab_ent lep;
1545 int error = 0;
1548 * Open the file for update and create it if necessary.
1549 * This may leave the I/O offset at the end of the file,
1550 * so rewind back to the beginning of the file.
1552 f = fopen(NFSLOGTAB, "a+");
1553 if (f == NULL) {
1554 error = errno;
1555 goto out;
1557 rewind(f);
1559 if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1560 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1561 "share complete, however failed to lock %s "
1562 "for update: %s\n"), NFSLOGTAB, strerror(errno));
1563 error = -1;
1564 goto out;
1567 if (logtab_deactivate_after_boot(f) == -1) {
1568 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1569 "share complete, however could not deactivate "
1570 "entries in %s\n"), NFSLOGTAB);
1571 error = -1;
1572 goto out;
1576 * Remove entries matching buffer and sharepoint since we're
1577 * going to replace it with perhaps an entry with a new tag.
1579 if (logtab_rement(f, buffer, dir, NULL, -1)) {
1580 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1581 "share complete, however could not remove matching "
1582 "entries in %s\n"), NFSLOGTAB);
1583 error = -1;
1584 goto out;
1588 * Deactivate all active entries matching this sharepoint
1590 if (logtab_deactivate(f, NULL, dir, NULL)) {
1591 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1592 "share complete, however could not deactivate matching "
1593 "entries in %s\n"), NFSLOGTAB);
1594 error = -1;
1595 goto out;
1598 lep.le_buffer = buffer;
1599 lep.le_path = dir;
1600 lep.le_tag = tag;
1601 lep.le_state = LES_ACTIVE;
1604 * Add new sharepoint / buffer location to nfslogtab
1606 if (logtab_putent(f, &lep) < 0) {
1607 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1608 "share complete, however could not add %s to %s\n"),
1609 dir, NFSLOGTAB);
1610 error = -1;
1613 out:
1614 if (f != NULL)
1615 (void) fclose(f);
1616 return (error);
1620 * Deactivate an entry from the nfslogtab file
1622 static int
1623 nfslogtab_deactivate(path)
1624 char *path;
1626 FILE *f;
1627 int error = 0;
1629 f = fopen(NFSLOGTAB, "r+");
1630 if (f == NULL) {
1631 error = errno;
1632 goto out;
1634 if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1635 error = errno;
1636 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1637 "share complete, however could not lock %s for "
1638 "update: %s\n"), NFSLOGTAB, strerror(error));
1639 goto out;
1641 if (logtab_deactivate(f, NULL, path, NULL) == -1) {
1642 error = -1;
1643 (void) fprintf(stderr,
1644 dgettext(TEXT_DOMAIN,
1645 "share complete, however could not "
1646 "deactivate %s in %s\n"), path, NFSLOGTAB);
1647 goto out;
1650 out: if (f != NULL)
1651 (void) fclose(f);
1653 return (error);
1657 * check_public(group, skipshare)
1659 * Check the group for any shares that have the public property
1660 * enabled. We skip "skipshare" since that is the one we are
1661 * working with. This is a separate function to make handling
1662 * subgroups simpler. Returns true if there is a share with public.
1664 static int
1665 check_public(sa_group_t group, sa_share_t skipshare)
1667 int exists = B_FALSE;
1668 sa_share_t share;
1669 sa_optionset_t opt;
1670 sa_property_t prop;
1671 char *shared;
1673 for (share = sa_get_share(group, NULL); share != NULL;
1674 share = sa_get_next_share(share)) {
1675 if (share == skipshare)
1676 continue;
1678 opt = sa_get_optionset(share, "nfs");
1679 if (opt == NULL)
1680 continue;
1681 prop = sa_get_property(opt, "public");
1682 if (prop == NULL)
1683 continue;
1684 shared = sa_get_share_attr(share, "shared");
1685 if (shared != NULL) {
1686 exists = strcmp(shared, "true") == 0;
1687 sa_free_attr_string(shared);
1688 if (exists == B_TRUE)
1689 break;
1693 return (exists);
1697 * public_exists(handle, share)
1699 * check to see if public option is set on any other share than the
1700 * one specified. Need to check zfs sub-groups as well as the top
1701 * level groups.
1703 static int
1704 public_exists(sa_handle_t handle, sa_share_t skipshare)
1706 sa_group_t group = NULL;
1709 * If we don't have a handle, we can only do syntax check. We
1710 * can't check against other shares so we assume OK and will
1711 * catch the problem only when we actually try to apply it.
1713 if (handle == NULL)
1714 return (SA_OK);
1716 if (skipshare != NULL) {
1717 group = sa_get_parent_group(skipshare);
1718 if (group == NULL)
1719 return (SA_NO_SUCH_GROUP);
1722 for (group = sa_get_group(handle, NULL); group != NULL;
1723 group = sa_get_next_group(group)) {
1724 /* Walk any ZFS subgroups as well as all standard groups */
1725 if (sa_group_is_zfs(group)) {
1726 sa_group_t subgroup;
1727 for (subgroup = sa_get_sub_group(group);
1728 subgroup != NULL;
1729 subgroup = sa_get_next_group(subgroup)) {
1730 if (check_public(subgroup, skipshare))
1731 return (B_TRUE);
1733 } else {
1734 if (check_public(group, skipshare))
1735 return (B_TRUE);
1738 return (B_FALSE);
1742 * sa_enable_share at the protocol level, enable_share must tell the
1743 * implementation that it is to enable the share. This entails
1744 * converting the path and options into the appropriate ioctl
1745 * calls. It is assumed that all error checking of paths, etc. were
1746 * done earlier.
1748 static int
1749 nfs_enable_share(sa_share_t share)
1751 struct exportdata export;
1752 sa_optionset_t secoptlist;
1753 struct secinfo *sp;
1754 int num_secinfo;
1755 sa_optionset_t opt;
1756 sa_security_t sec;
1757 sa_property_t prop;
1758 char *path;
1759 int err = SA_OK;
1760 int i;
1761 int iszfs;
1762 sa_handle_t handle;
1764 /* Don't drop core if the NFS module isn't loaded. */
1765 (void) signal(SIGSYS, SIG_IGN);
1767 /* get the path since it is important in several places */
1768 path = sa_get_share_attr(share, "path");
1769 if (path == NULL)
1770 return (SA_NO_SUCH_PATH);
1772 iszfs = sa_path_is_zfs(path);
1774 * find the optionsets and security sets. There may not be
1775 * any or there could be one or two for each of optionset and
1776 * security may have multiple, one per security type per
1777 * protocol type.
1779 opt = sa_get_derived_optionset(share, "nfs", 1);
1780 secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1);
1781 if (secoptlist != NULL)
1782 num_secinfo = MAX(1, count_security(secoptlist));
1783 else
1784 num_secinfo = 1;
1787 * walk through the options and fill in the structure
1788 * appropriately.
1791 (void) memset(&export, '\0', sizeof (export));
1794 * do non-security options first since there is only one after
1795 * the derived group is constructed.
1797 export.ex_version = EX_CURRENT_VERSION;
1798 export.ex_anon = UID_NOBODY; /* this is our default value */
1799 export.ex_index = NULL;
1800 export.ex_path = path;
1801 export.ex_pathlen = strlen(path) + 1;
1803 if (opt != NULL)
1804 err = fill_export_from_optionset(&export, opt);
1807 * check to see if "public" is set. If it is, then make sure
1808 * no other share has it set. If it is already used, fail.
1811 handle = sa_find_group_handle((sa_group_t)share);
1812 if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) {
1813 (void) printf(dgettext(TEXT_DOMAIN,
1814 "NFS: Cannot share more than one file "
1815 "system with 'public' property\n"));
1816 err = SA_NOT_ALLOWED;
1817 goto out;
1820 sp = calloc(num_secinfo, sizeof (struct secinfo));
1821 if (sp == NULL) {
1822 err = SA_NO_MEMORY;
1823 (void) printf(dgettext(TEXT_DOMAIN,
1824 "NFS: NFS: no memory for security\n"));
1825 goto out;
1827 export.ex_secinfo = sp;
1828 /* get default secinfo */
1829 export.ex_seccnt = num_secinfo;
1831 * since we must have one security option defined, we
1832 * init to the default and then override as we find
1833 * defined security options. This handles the case
1834 * where we have no defined options but we need to set
1835 * up one.
1837 sp[0].s_window = DEF_WIN;
1838 sp[0].s_rootnames = NULL;
1839 /* setup a default in case no properties defined */
1840 if (nfs_getseconfig_default(&sp[0].s_secinfo)) {
1841 (void) printf(dgettext(TEXT_DOMAIN,
1842 "NFS: nfs_getseconfig_default: failed to "
1843 "get default security mode\n"));
1844 err = SA_CONFIG_ERR;
1846 if (secoptlist != NULL) {
1847 for (i = 0, prop = sa_get_property(secoptlist, NULL);
1848 prop != NULL && i < num_secinfo;
1849 prop = sa_get_next_property(prop), i++) {
1850 char *sectype;
1851 sectype = sa_get_property_attr(prop, "type");
1853 * if sectype is NULL, we probably
1854 * have a memory problem and can't get
1855 * the correct values. Rather than
1856 * exporting with incorrect security,
1857 * don't share it.
1859 if (sectype == NULL) {
1860 err = SA_NO_MEMORY;
1861 (void) printf(dgettext(TEXT_DOMAIN,
1862 "NFS: Cannot share %s: "
1863 "no memory\n"), path);
1864 goto out;
1866 sec = (sa_security_t)sa_get_derived_security(
1867 share, sectype, "nfs", 1);
1868 sp[i].s_window = DEF_WIN;
1869 sp[i].s_rootcnt = 0;
1870 sp[i].s_rootnames = NULL;
1871 (void) fill_security_from_secopts(&sp[i], sec);
1872 if (sec != NULL)
1873 sa_free_derived_security(sec);
1874 if (sectype != NULL)
1875 sa_free_attr_string(sectype);
1879 * when we get here, we can do the exportfs system call and
1880 * initiate thinsg. We probably want to enable the nfs.server
1881 * service first if it isn't running within SMF.
1883 /* check nfs.server status and start if needed */
1884 /* now add the share to the internal tables */
1885 printarg(path, &export);
1887 * call the exportfs system call which is implemented
1888 * via the nfssys() call as the EXPORTFS subfunction.
1890 if (iszfs) {
1891 struct exportfs_args ea;
1892 share_t sh;
1893 char *str;
1894 priv_set_t *priv_effective;
1895 int privileged;
1898 * If we aren't a privileged user
1899 * and NFS server service isn't running
1900 * then print out an error message
1901 * and return EPERM
1904 priv_effective = priv_allocset();
1905 (void) getppriv(PRIV_EFFECTIVE, priv_effective);
1907 privileged = (priv_isfullset(priv_effective) == B_TRUE);
1908 priv_freeset(priv_effective);
1910 if (!privileged &&
1911 (str = smf_get_state(NFS_SERVER_SVC)) != NULL) {
1912 err = 0;
1913 if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) {
1914 (void) printf(dgettext(TEXT_DOMAIN,
1915 "NFS: Cannot share remote "
1916 "filesystem: %s\n"), path);
1917 (void) printf(dgettext(TEXT_DOMAIN,
1918 "NFS: Service needs to be enabled "
1919 "by a privileged user\n"));
1920 err = SA_SYSTEM_ERR;
1921 errno = EPERM;
1923 free(str);
1926 if (err == 0) {
1927 ea.dname = path;
1928 ea.uex = &export;
1930 (void) sa_sharetab_fill_zfs(share, &sh, "nfs");
1931 err = sa_share_zfs(share, NULL, path, &sh,
1932 &ea, ZFS_SHARE_NFS);
1933 if (err != SA_OK) {
1934 errno = err;
1935 err = -1;
1937 sa_emptyshare(&sh);
1939 } else {
1940 err = exportfs(path, &export);
1943 if (err < 0) {
1944 err = SA_SYSTEM_ERR;
1945 switch (errno) {
1946 case EREMOTE:
1947 (void) printf(dgettext(TEXT_DOMAIN,
1948 "NFS: Cannot share filesystems "
1949 "in non-global zones: %s\n"), path);
1950 err = SA_NOT_SUPPORTED;
1951 break;
1952 case EPERM:
1953 if (getzoneid() != GLOBAL_ZONEID) {
1954 (void) printf(dgettext(TEXT_DOMAIN,
1955 "NFS: Cannot share file systems "
1956 "in non-global zones: %s\n"), path);
1957 err = SA_NOT_SUPPORTED;
1958 break;
1960 err = SA_NO_PERMISSION;
1961 break;
1962 case EEXIST:
1963 err = SA_SHARE_EXISTS;
1964 break;
1965 default:
1966 break;
1968 } else {
1969 /* update sharetab with an add/modify */
1970 if (!iszfs) {
1971 (void) sa_update_sharetab(share, "nfs");
1975 if (err == SA_OK) {
1977 * enable services as needed. This should probably be
1978 * done elsewhere in order to minimize the calls to
1979 * check services.
1982 * check to see if logging and other services need to
1983 * be triggered, but only if there wasn't an
1984 * error. This is probably where sharetab should be
1985 * updated with the NFS specific entry.
1987 if (export.ex_flags & EX_LOG) {
1988 /* enable logging */
1989 if (nfslogtab_add(path, export.ex_log_buffer,
1990 export.ex_tag) != 0) {
1991 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1992 "Could not enable logging for %s\n"),
1993 path);
1995 _check_services(service_list_logging);
1996 } else {
1998 * don't have logging so remove it from file. It might
1999 * not be thre, but that doesn't matter.
2001 (void) nfslogtab_deactivate(path);
2002 _check_services(service_list_default);
2006 out:
2007 if (path != NULL)
2008 free(path);
2010 cleanup_export(&export);
2011 if (opt != NULL)
2012 sa_free_derived_optionset(opt);
2013 if (secoptlist != NULL)
2014 (void) sa_destroy_optionset(secoptlist);
2015 return (err);
2019 * nfs_disable_share(share, path)
2021 * Unshare the specified share. Note that "path" is the same path as
2022 * what is in the "share" object. It is passed in to avoid an
2023 * additional lookup. A missing "path" value makes this a no-op
2024 * function.
2026 static int
2027 nfs_disable_share(sa_share_t share, char *path)
2029 int err;
2030 int ret = SA_OK;
2031 int iszfs;
2032 sa_group_t parent;
2033 sa_handle_t handle;
2035 if (path == NULL)
2036 return (ret);
2039 * If the share is in a ZFS group we need to handle it
2040 * differently. Just being on a ZFS file system isn't
2041 * enough since we may be in a legacy share case.
2043 parent = sa_get_parent_group(share);
2044 iszfs = sa_group_is_zfs(parent);
2045 if (iszfs) {
2046 struct exportfs_args ea;
2047 share_t sh = { 0 };
2048 ea.dname = path;
2049 ea.uex = NULL;
2050 sh.sh_path = path;
2051 sh.sh_fstype = "nfs";
2053 err = sa_share_zfs(share, NULL, path, &sh,
2054 &ea, ZFS_UNSHARE_NFS);
2055 if (err != SA_OK) {
2056 errno = err;
2057 err = -1;
2059 } else {
2060 err = exportfs(path, NULL);
2062 if (err < 0) {
2064 * TBD: only an error in some
2065 * cases - need better analysis
2067 switch (errno) {
2068 case EPERM:
2069 case EACCES:
2070 ret = SA_NO_PERMISSION;
2071 if (getzoneid() != GLOBAL_ZONEID) {
2072 ret = SA_NOT_SUPPORTED;
2074 break;
2075 case EINVAL:
2076 case ENOENT:
2077 ret = SA_NO_SUCH_PATH;
2078 break;
2079 default:
2080 ret = SA_SYSTEM_ERR;
2081 break;
2084 if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
2085 handle = sa_find_group_handle((sa_group_t)share);
2086 if (!iszfs)
2087 (void) sa_delete_sharetab(handle, path, "nfs");
2088 /* just in case it was logged */
2089 (void) nfslogtab_deactivate(path);
2091 return (ret);
2095 * check_rorwnone(v1, v2, v3)
2097 * check ro vs rw vs none values. Over time this may get beefed up.
2098 * for now it just does simple checks. v1 is never NULL but v2 or v3
2099 * could be.
2102 static int
2103 check_rorwnone(char *v1, char *v2, char *v3)
2105 int ret = SA_OK;
2106 if (v2 != NULL && strcmp(v1, v2) == 0)
2107 ret = SA_VALUE_CONFLICT;
2108 else if (v3 != NULL && strcmp(v1, v3) == 0)
2109 ret = SA_VALUE_CONFLICT;
2111 return (ret);
2115 * nfs_validate_property(handle, property, parent)
2117 * Check that the property has a legitimate value for its type.
2120 static int
2121 nfs_validate_property(sa_handle_t handle, sa_property_t property,
2122 sa_optionset_t parent)
2124 int ret = SA_OK;
2125 char *propname;
2126 char *other1;
2127 char *other2;
2128 int optindex;
2129 nfsl_config_t *configlist;
2130 sa_group_t parent_group;
2131 char *value;
2133 propname = sa_get_property_attr(property, "type");
2135 if ((optindex = findopt(propname)) < 0)
2136 ret = SA_NO_SUCH_PROP;
2138 /* need to validate value range here as well */
2140 if (ret == SA_OK) {
2141 parent_group = sa_get_parent_group((sa_share_t)parent);
2142 if (optdefs[optindex].share && parent_group != NULL &&
2143 !sa_is_share(parent_group))
2144 ret = SA_PROP_SHARE_ONLY;
2146 if (ret == SA_OK) {
2147 if (optdefs[optindex].index == OPT_PUBLIC) {
2149 * Public is special in that only one instance can
2150 * be in the repository at the same time.
2152 if (public_exists(handle, parent_group)) {
2153 sa_free_attr_string(propname);
2154 return (SA_VALUE_CONFLICT);
2157 value = sa_get_property_attr(property, "value");
2158 if (value != NULL) {
2159 /* first basic type checking */
2160 switch (optdefs[optindex].type) {
2161 case OPT_TYPE_NUMBER:
2162 /* check that the value is all digits */
2163 if (!is_a_number(value))
2164 ret = SA_BAD_VALUE;
2165 break;
2166 case OPT_TYPE_BOOLEAN:
2167 if (strlen(value) == 0 ||
2168 strcasecmp(value, "true") == 0 ||
2169 strcmp(value, "1") == 0 ||
2170 strcasecmp(value, "false") == 0 ||
2171 strcmp(value, "0") == 0) {
2172 ret = SA_OK;
2173 } else {
2174 ret = SA_BAD_VALUE;
2176 break;
2177 case OPT_TYPE_USER:
2178 if (!is_a_number(value)) {
2179 struct passwd *pw;
2181 * in this case it would have to be a
2182 * user name
2184 pw = getpwnam(value);
2185 if (pw == NULL)
2186 ret = SA_BAD_VALUE;
2187 endpwent();
2188 } else {
2189 uint64_t intval;
2190 intval = strtoull(value, NULL, 0);
2191 if (intval > UID_MAX && intval != ~0)
2192 ret = SA_BAD_VALUE;
2194 break;
2195 case OPT_TYPE_FILE:
2196 if (strcmp(value, "..") == 0 ||
2197 strchr(value, '/') != NULL) {
2198 ret = SA_BAD_VALUE;
2200 break;
2201 case OPT_TYPE_ACCLIST: {
2202 sa_property_t oprop1;
2203 sa_property_t oprop2;
2204 char *ovalue1 = NULL;
2205 char *ovalue2 = NULL;
2207 if (parent == NULL)
2208 break;
2210 * access list handling. Should eventually
2211 * validate that all the values make sense.
2212 * Also, ro and rw may have cross value
2213 * conflicts.
2215 if (strcmp(propname, SHOPT_RO) == 0) {
2216 other1 = SHOPT_RW;
2217 other2 = SHOPT_NONE;
2218 } else if (strcmp(propname, SHOPT_RW) == 0) {
2219 other1 = SHOPT_RO;
2220 other2 = SHOPT_NONE;
2221 } else if (strcmp(propname, SHOPT_NONE) == 0) {
2222 other1 = SHOPT_RO;
2223 other2 = SHOPT_RW;
2224 } else {
2225 other1 = NULL;
2226 other2 = NULL;
2228 if (other1 == NULL && other2 == NULL)
2229 break;
2231 /* compare rw(ro) with ro(rw) */
2233 oprop1 = sa_get_property(parent, other1);
2234 oprop2 = sa_get_property(parent, other2);
2235 if (oprop1 == NULL && oprop2 == NULL)
2236 break;
2238 * Only potential confusion if other1
2239 * or other2 exists. Check the values
2240 * and run the check if there is a
2241 * value other than the one we are
2242 * explicitly looking at.
2244 ovalue1 = sa_get_property_attr(oprop1, "value");
2245 ovalue2 = sa_get_property_attr(oprop2, "value");
2246 if (ovalue1 != NULL || ovalue2 != NULL)
2247 ret = check_rorwnone(value, ovalue1,
2248 ovalue2);
2250 if (ovalue1 != NULL)
2251 sa_free_attr_string(ovalue1);
2252 if (ovalue2 != NULL)
2253 sa_free_attr_string(ovalue2);
2254 break;
2256 case OPT_TYPE_LOGTAG:
2257 if (nfsl_getconfig_list(&configlist) == 0) {
2258 int error;
2259 if (value == NULL ||
2260 strlen(value) == 0) {
2261 if (value != NULL)
2262 sa_free_attr_string(
2263 value);
2264 value = strdup("global");
2266 if (value != NULL &&
2267 nfsl_findconfig(configlist, value,
2268 &error) == NULL) {
2269 ret = SA_BAD_VALUE;
2271 /* Must always free when done */
2272 nfsl_freeconfig_list(&configlist);
2273 } else {
2274 ret = SA_CONFIG_ERR;
2276 break;
2277 case OPT_TYPE_STRING:
2278 /* whatever is here should be ok */
2279 break;
2280 case OPT_TYPE_SECURITY:
2282 * The "sec" property isn't used in the
2283 * non-legacy parts of sharemgr. We need to
2284 * reject it here. For legacy, it is pulled
2285 * out well before we get here.
2287 ret = SA_NO_SUCH_PROP;
2288 break;
2289 default:
2290 break;
2293 if (value != NULL)
2294 sa_free_attr_string(value);
2296 if (ret == SA_OK && optdefs[optindex].check != NULL) {
2297 /* do the property specific check */
2298 ret = optdefs[optindex].check(handle, property);
2303 if (propname != NULL)
2304 sa_free_attr_string(propname);
2305 return (ret);
2309 * Protocol management functions
2311 * Properties defined in the default files are defined in
2312 * proto_option_defs for parsing and validation. If "other" and
2313 * "compare" are set, then the value for this property should be
2314 * compared against the property specified in "other" using the
2315 * "compare" check (either <= or >=) in order to ensure that the
2316 * values are in the correct range. E.g. setting server_versmin
2317 * higher than server_versmax should not be allowed.
2320 struct proto_option_defs {
2321 char *tag;
2322 char *name; /* display name -- remove protocol identifier */
2323 int index;
2324 int type;
2325 union {
2326 int intval;
2327 char *string;
2328 } defvalue;
2329 uint32_t svcs;
2330 int32_t minval;
2331 int32_t maxval;
2332 char *other;
2333 int compare;
2334 #define OPT_CMP_GE 0
2335 #define OPT_CMP_LE 1
2336 int (*check)(char *);
2337 } proto_options[] = {
2338 #define PROTO_OPT_NFSD_SERVERS 0
2339 {"nfsd_servers",
2340 "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD,
2341 1, INT32_MAX},
2342 #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1
2343 {"lockd_listen_backlog",
2344 "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
2345 OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX},
2346 #define PROTO_OPT_LOCKD_SERVERS 2
2347 {"lockd_servers",
2348 "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20,
2349 SVC_LOCKD, 1, INT32_MAX},
2350 #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3
2351 {"lockd_retransmit_timeout",
2352 "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
2353 OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX},
2354 #define PROTO_OPT_GRACE_PERIOD 4
2355 {"grace_period",
2356 "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90,
2357 SVC_LOCKD, 0, INT32_MAX},
2358 #define PROTO_OPT_NFS_SERVER_VERSMIN 5
2359 {"nfs_server_versmin",
2360 "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER,
2361 (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2362 NFS_VERSMAX, "server_versmax", OPT_CMP_LE},
2363 #define PROTO_OPT_NFS_SERVER_VERSMAX 6
2364 {"nfs_server_versmax",
2365 "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER,
2366 (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2367 NFS_VERSMAX, "server_versmin", OPT_CMP_GE},
2368 #define PROTO_OPT_NFS_CLIENT_VERSMIN 7
2369 {"nfs_client_versmin",
2370 "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER,
2371 (int)NFS_VERSMIN_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2372 "client_versmax", OPT_CMP_LE},
2373 #define PROTO_OPT_NFS_CLIENT_VERSMAX 8
2374 {"nfs_client_versmax",
2375 "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER,
2376 (int)NFS_VERSMAX_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2377 "client_versmin", OPT_CMP_GE},
2378 #define PROTO_OPT_NFS_SERVER_DELEGATION 9
2379 {"nfs_server_delegation",
2380 "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
2381 OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0},
2382 #define PROTO_OPT_NFSMAPID_DOMAIN 10
2383 {"nfsmapid_domain",
2384 "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN,
2385 NULL, SVC_NFSMAPID, 0, 0},
2386 #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11
2387 {"nfsd_max_connections",
2388 "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
2389 OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX},
2390 #define PROTO_OPT_NFSD_PROTOCOL 12
2391 {"nfsd_protocol",
2392 "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0,
2393 SVC_NFSD, 0, 0},
2394 #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13
2395 {"nfsd_listen_backlog",
2396 "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
2397 OPT_TYPE_NUMBER, 0, SVC_NFSD, 0, INT32_MAX},
2398 #define PROTO_OPT_NFSD_DEVICE 14
2399 {"nfsd_device",
2400 "device", PROTO_OPT_NFSD_DEVICE,
2401 OPT_TYPE_STRING, NULL, SVC_NFSD, 0, 0},
2402 {NULL}
2406 * the protoset holds the defined options so we don't have to read
2407 * them multiple times
2409 static sa_protocol_properties_t protoset;
2411 static int
2412 findprotoopt(char *name, int whichname)
2414 int i;
2415 for (i = 0; proto_options[i].tag != NULL; i++) {
2416 if (whichname == 1) {
2417 if (strcasecmp(proto_options[i].name, name) == 0)
2418 return (i);
2419 } else {
2420 if (strcasecmp(proto_options[i].tag, name) == 0)
2421 return (i);
2424 return (-1);
2428 * fixcaselower(str)
2430 * convert a string to lower case (inplace).
2433 static void
2434 fixcaselower(char *str)
2436 while (*str) {
2437 *str = tolower(*str);
2438 str++;
2443 * skipwhitespace(str)
2445 * Skip leading white space. It is assumed that it is called with a
2446 * valid pointer.
2449 static char *
2450 skipwhitespace(char *str)
2452 while (*str && isspace(*str))
2453 str++;
2455 return (str);
2459 * extractprop()
2461 * Extract the property and value out of the line and create the
2462 * property in the optionset.
2464 static int
2465 extractprop(char *name, char *value)
2467 sa_property_t prop;
2468 int index;
2469 int ret = SA_OK;
2471 * Remove any leading
2472 * white space.
2474 name = skipwhitespace(name);
2476 index = findprotoopt(name, 1);
2477 if (index >= 0) {
2478 fixcaselower(name);
2479 prop = sa_create_property(proto_options[index].name, value);
2480 if (prop != NULL)
2481 ret = sa_add_protocol_property(protoset, prop);
2482 else
2483 ret = SA_NO_MEMORY;
2485 return (ret);
2488 scf_type_t
2489 getscftype(int type)
2491 scf_type_t ret;
2493 switch (type) {
2494 case OPT_TYPE_NUMBER:
2495 ret = SCF_TYPE_INTEGER;
2496 break;
2497 case OPT_TYPE_BOOLEAN:
2498 ret = SCF_TYPE_BOOLEAN;
2499 break;
2500 default:
2501 ret = SCF_TYPE_ASTRING;
2503 return (ret);
2506 char *
2507 getsvcname(uint32_t svcs)
2509 char *service;
2510 switch (svcs) {
2511 case SVC_LOCKD:
2512 service = LOCKD;
2513 break;
2514 case SVC_STATD:
2515 service = STATD;
2516 break;
2517 case SVC_NFSD:
2518 service = NFSD;
2519 break;
2520 case SVC_CLIENT:
2521 service = NFS_CLIENT_SVC;
2522 break;
2523 case SVC_NFS4CBD:
2524 service = NFS4CBD;
2525 break;
2526 case SVC_NFSMAPID:
2527 service = NFSMAPID;
2528 break;
2529 case SVC_RQUOTAD:
2530 service = RQUOTAD;
2531 break;
2532 case SVC_NFSLOGD:
2533 service = NFSLOGD;
2534 break;
2535 case SVC_REPARSED:
2536 service = REPARSED;
2537 break;
2538 default:
2539 service = NFSD;
2541 return (service);
2545 * initprotofromsmf()
2547 * Read NFS SMF properties and add the defined values to the
2548 * protoset. Note that default values are known from the built in
2549 * table in case SMF doesn't have a definition. Not having
2550 * SMF properties is OK since we have builtin default
2551 * values.
2553 static int
2554 initprotofromsmf()
2556 char name[PATH_MAX];
2557 char value[PATH_MAX];
2558 int ret = SA_OK, bufsz = 0, i;
2560 protoset = sa_create_protocol_properties("nfs");
2561 if (protoset != NULL) {
2562 for (i = 0; proto_options[i].tag != NULL; i++) {
2563 scf_type_t ptype;
2564 char *svc_name;
2566 bzero(value, PATH_MAX);
2567 (void) strncpy(name, proto_options[i].name, PATH_MAX);
2568 /* Replace NULL with the correct instance */
2569 ptype = getscftype(proto_options[i].type);
2570 svc_name = getsvcname(proto_options[i].svcs);
2571 bufsz = PATH_MAX;
2572 ret = nfs_smf_get_prop(name, value,
2573 (char *)DEFAULT_INSTANCE, ptype,
2574 svc_name, &bufsz);
2575 if (ret == SA_OK) {
2576 ret = extractprop(name, value);
2579 } else {
2580 ret = SA_NO_MEMORY;
2583 return (ret);
2587 * add_defaults()
2589 * Add the default values for any property not defined
2590 * in NFS SMF repository.
2591 * Values are set according to their defined types.
2594 static void
2595 add_defaults()
2597 int i;
2598 char number[MAXDIGITS];
2600 for (i = 0; proto_options[i].tag != NULL; i++) {
2601 sa_property_t prop;
2602 prop = sa_get_protocol_property(protoset,
2603 proto_options[i].name);
2604 if (prop == NULL) {
2605 /* add the default value */
2606 switch (proto_options[i].type) {
2607 case OPT_TYPE_NUMBER:
2608 (void) snprintf(number, sizeof (number), "%d",
2609 proto_options[i].defvalue.intval);
2610 prop = sa_create_property(proto_options[i].name,
2611 number);
2612 break;
2614 case OPT_TYPE_BOOLEAN:
2615 prop = sa_create_property(proto_options[i].name,
2616 proto_options[i].defvalue.intval ?
2617 "true" : "false");
2618 break;
2620 case OPT_TYPE_ONOFF:
2621 prop = sa_create_property(proto_options[i].name,
2622 proto_options[i].defvalue.intval ?
2623 "on" : "off");
2624 break;
2626 default:
2627 /* treat as strings of zero length */
2628 prop = sa_create_property(proto_options[i].name,
2629 "");
2630 break;
2632 if (prop != NULL)
2633 (void) sa_add_protocol_property(protoset, prop);
2638 static void
2639 free_protoprops()
2641 if (protoset != NULL) {
2642 xmlFreeNode(protoset);
2643 protoset = NULL;
2648 * nfs_init()
2650 * Initialize the NFS plugin.
2653 static int
2654 nfs_init()
2656 int ret = SA_OK;
2658 if (sa_plugin_ops.sa_init != nfs_init) {
2659 (void) printf(dgettext(TEXT_DOMAIN,
2660 "NFS plugin not properly initialized\n"));
2661 return (SA_CONFIG_ERR);
2664 ret = initprotofromsmf();
2665 if (ret != SA_OK) {
2666 (void) printf(dgettext(TEXT_DOMAIN,
2667 "NFS plugin problem with SMF repository: %s\n"),
2668 sa_errorstr(ret));
2669 ret = SA_OK;
2671 add_defaults();
2673 return (ret);
2677 * nfs_fini()
2679 * uninitialize the NFS plugin. Want to avoid memory leaks.
2682 static void
2683 nfs_fini()
2685 free_protoprops();
2689 * nfs_get_proto_set()
2691 * Return an optionset with all the protocol specific properties in
2692 * it.
2695 static sa_protocol_properties_t
2696 nfs_get_proto_set()
2698 return (protoset);
2702 * service_in_state(service, chkstate)
2704 * Want to know if the specified service is in the desired state
2705 * (chkstate) or not. Return true (1) if it is and false (0) if it
2706 * isn't.
2708 static int
2709 service_in_state(char *service, const char *chkstate)
2711 char *state;
2712 int ret = B_FALSE;
2714 state = smf_get_state(service);
2715 if (state != NULL) {
2716 /* got the state so get the equality for the return value */
2717 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
2718 free(state);
2720 return (ret);
2724 * restart_service(svcs)
2726 * Walk through the bit mask of services that need to be restarted in
2727 * order to use the new property values. Some properties affect
2728 * multiple daemons. Should only restart a service if it is currently
2729 * enabled (online).
2732 static void
2733 restart_service(uint32_t svcs)
2735 uint32_t mask;
2736 int ret;
2737 char *service;
2739 for (mask = 1; svcs != 0; mask <<= 1) {
2740 switch (svcs & mask) {
2741 case SVC_LOCKD:
2742 service = LOCKD;
2743 break;
2744 case SVC_STATD:
2745 service = STATD;
2746 break;
2747 case SVC_NFSD:
2748 service = NFSD;
2749 break;
2750 case SVC_MOUNTD:
2751 service = MOUNTD;
2752 break;
2753 case SVC_NFS4CBD:
2754 service = NFS4CBD;
2755 break;
2756 case SVC_NFSMAPID:
2757 service = NFSMAPID;
2758 break;
2759 case SVC_RQUOTAD:
2760 service = RQUOTAD;
2761 break;
2762 case SVC_NFSLOGD:
2763 service = NFSLOGD;
2764 break;
2765 case SVC_REPARSED:
2766 service = REPARSED;
2767 break;
2768 case SVC_CLIENT:
2769 service = NFS_CLIENT_SVC;
2770 break;
2771 default:
2772 continue;
2776 * Only attempt to restart the service if it is
2777 * currently running. In the future, it may be
2778 * desirable to use smf_refresh_instance if the NFS
2779 * services ever implement the refresh method.
2781 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
2782 ret = smf_restart_instance(service);
2784 * There are only a few SMF errors at this point, but
2785 * it is also possible that a bad value may have put
2786 * the service into maintenance if there wasn't an
2787 * SMF level error.
2789 if (ret != 0) {
2790 (void) fprintf(stderr,
2791 dgettext(TEXT_DOMAIN,
2792 "%s failed to restart: %s\n"),
2793 scf_strerror(scf_error()));
2794 } else {
2796 * Check whether it has gone to "maintenance"
2797 * mode or not. Maintenance implies something
2798 * went wrong.
2800 if (service_in_state(service,
2801 SCF_STATE_STRING_MAINT)) {
2802 (void) fprintf(stderr,
2803 dgettext(TEXT_DOMAIN,
2804 "%s failed to restart\n"),
2805 service);
2809 svcs &= ~mask;
2814 * nfs_minmax_check(name, value)
2816 * Verify that the value for the property specified by index is valid
2817 * relative to the opposite value in the case of a min/max variable.
2818 * Currently, server_minvers/server_maxvers and
2819 * client_minvers/client_maxvers are the only ones to check.
2822 static int
2823 nfs_minmax_check(int index, int value)
2825 int val;
2826 char *pval;
2827 sa_property_t prop;
2828 sa_optionset_t opts;
2829 int ret = B_TRUE;
2831 if (proto_options[index].other != NULL) {
2832 /* have a property to compare against */
2833 opts = nfs_get_proto_set();
2834 prop = sa_get_property(opts, proto_options[index].other);
2836 * If we don't find the property, assume default
2837 * values which will work since the max will be at the
2838 * max and the min at the min.
2840 if (prop != NULL) {
2841 pval = sa_get_property_attr(prop, "value");
2842 if (pval != NULL) {
2843 val = strtoul(pval, NULL, 0);
2844 if (proto_options[index].compare ==
2845 OPT_CMP_LE) {
2846 ret = value <= val ? B_TRUE : B_FALSE;
2847 } else if (proto_options[index].compare ==
2848 OPT_CMP_GE) {
2849 ret = value >= val ? B_TRUE : B_FALSE;
2851 sa_free_attr_string(pval);
2855 return (ret);
2859 * nfs_validate_proto_prop(index, name, value)
2861 * Verify that the property specified by name can take the new
2862 * value. This is a sanity check to prevent bad values getting into
2863 * the default files. All values need to be checked against what is
2864 * allowed by their defined type. If a type isn't explicitly defined
2865 * here, it is treated as a string.
2867 * Note that OPT_TYPE_NUMBER will additionally check that the value is
2868 * within the range specified and potentially against another property
2869 * value as well as specified in the proto_options members other and
2870 * compare.
2873 static int
2874 nfs_validate_proto_prop(int index, char *name, char *value)
2876 int ret = SA_OK;
2877 char *cp;
2878 #ifdef lint
2879 name = name;
2880 #endif
2881 switch (proto_options[index].type) {
2882 case OPT_TYPE_NUMBER:
2883 if (!is_a_number(value))
2884 ret = SA_BAD_VALUE;
2885 else {
2886 int val;
2887 val = strtoul(value, NULL, 0);
2888 if (val < proto_options[index].minval ||
2889 val > proto_options[index].maxval)
2890 ret = SA_BAD_VALUE;
2892 * For server_versmin/server_versmax and
2893 * client_versmin/client_versmax, the value of the
2894 * min(max) should be checked to be correct relative
2895 * to the current max(min).
2897 if (!nfs_minmax_check(index, val)) {
2898 ret = SA_BAD_VALUE;
2901 break;
2903 case OPT_TYPE_DOMAIN:
2905 * needs to be a qualified domain so will have at
2906 * least one period and other characters on either
2907 * side of it. A zero length string is also allowed
2908 * and is the way to turn off the override.
2910 if (strlen(value) == 0)
2911 break;
2912 cp = strchr(value, '.');
2913 if (cp == NULL || cp == value || strchr(value, '@') != NULL)
2914 ret = SA_BAD_VALUE;
2915 break;
2917 case OPT_TYPE_BOOLEAN:
2918 if (strlen(value) == 0 ||
2919 strcasecmp(value, "true") == 0 ||
2920 strcmp(value, "1") == 0 ||
2921 strcasecmp(value, "false") == 0 ||
2922 strcmp(value, "0") == 0) {
2923 ret = SA_OK;
2924 } else {
2925 ret = SA_BAD_VALUE;
2927 break;
2929 case OPT_TYPE_ONOFF:
2930 if (strcasecmp(value, "on") != 0 &&
2931 strcasecmp(value, "off") != 0) {
2932 ret = SA_BAD_VALUE;
2934 break;
2936 case OPT_TYPE_PROTOCOL:
2937 if (strlen(value) != 0 &&
2938 strcasecmp(value, "all") != 0 &&
2939 strcasecmp(value, "tcp") != 0 &&
2940 strcasecmp(value, "udp") != 0)
2941 ret = SA_BAD_VALUE;
2942 break;
2944 default:
2945 /* treat as a string */
2946 break;
2948 return (ret);
2952 * nfs_set_proto_prop(prop)
2954 * check that prop is valid.
2957 static int
2958 nfs_set_proto_prop(sa_property_t prop)
2960 int ret = SA_OK;
2961 char *name;
2962 char *value;
2964 name = sa_get_property_attr(prop, "type");
2965 value = sa_get_property_attr(prop, "value");
2966 if (name != NULL && value != NULL) {
2967 scf_type_t sctype;
2968 char *svc_name;
2969 char *instance = NULL;
2970 int index = findprotoopt(name, 1);
2972 ret = nfs_validate_proto_prop(index, name, value);
2973 if (ret == SA_OK) {
2974 sctype = getscftype(proto_options[index].type);
2975 svc_name = getsvcname(proto_options[index].svcs);
2976 if (sctype == SCF_TYPE_BOOLEAN) {
2977 if (value != NULL)
2978 sa_free_attr_string(value);
2979 if (string_to_boolean(value) == 0)
2980 value = strdup("0");
2981 else
2982 value = strdup("1");
2984 ret = nfs_smf_set_prop(name, value, instance, sctype,
2985 svc_name);
2986 if (ret == SA_OK) {
2987 restart_service(proto_options[index].svcs);
2988 } else {
2989 (void) printf(dgettext(TEXT_DOMAIN,
2990 "Cannot restart NFS services : %s\n"),
2991 sa_errorstr(ret));
2995 if (name != NULL)
2996 sa_free_attr_string(name);
2997 if (value != NULL)
2998 sa_free_attr_string(value);
2999 return (ret);
3003 * nfs_get_status()
3005 * What is the current status of the nfsd? We use the SMF state here.
3006 * Caller must free the returned value.
3009 static char *
3010 nfs_get_status()
3012 char *state;
3013 state = smf_get_state(NFSD);
3014 return (state != NULL ? state : strdup("-"));
3018 * nfs_space_alias(alias)
3020 * Lookup the space (security) name. If it is default, convert to the
3021 * real name.
3024 static char *
3025 nfs_space_alias(char *space)
3027 char *name = space;
3028 seconfig_t secconf;
3031 * Only the space named "default" is special. If it is used,
3032 * the default needs to be looked up and the real name used.
3033 * This is normally "sys" but could be changed. We always
3034 * change defautl to the real name.
3036 if (strcmp(space, "default") == 0 &&
3037 nfs_getseconfig_default(&secconf) == 0) {
3038 if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
3039 name = secconf.sc_name;
3041 return (strdup(name));
3045 * nfs_features()
3047 * Return a mask of the features required.
3050 static uint64_t
3051 nfs_features()
3053 return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER);