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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
28 * Copyright 2018 Nexenta Systems, Inc.
29 */
30
31 /*
32 * NFS specific functions
33 */
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <locale.h>
42 #include <signal.h>
43 #include <strings.h>
44 #include "libshare.h"
45 #include "libshare_impl.h"
46 #include <nfs/export.h>
47 #include <pwd.h>
48 #include <grp.h>
49 #include <limits.h>
50 #include <libscf.h>
51 #include <syslog.h>
52 #include <rpcsvc/daemon_utils.h>
53 #include "nfslog_config.h"
54 #include "nfslogtab.h"
55 #include "libshare_nfs.h"
56 #include <nfs/nfs.h>
57 #include <nfs/nfssys.h>
58 #include <netconfig.h>
59 #include "smfcfg.h"
60
61 /* should really be in some global place */
62 #define DEF_WIN 30000
63 #define OPT_CHUNK 1024
64
65 int debug = 0;
66
67 #define NFS_SERVER_SVC "svc:/network/nfs/server:default"
68 #define NFS_CLIENT_SVC (char *)"svc:/network/nfs/client:default"
69
70 /* internal functions */
71 static int nfs_init();
72 static void nfs_fini();
73 static int nfs_enable_share(sa_share_t);
74 static int nfs_disable_share(sa_share_t, char *);
75 static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
76 static int nfs_validate_security_mode(char *);
77 static int nfs_is_security_opt(char *);
78 static int nfs_parse_legacy_options(sa_group_t, char *);
79 static char *nfs_format_options(sa_group_t, int);
80 static int nfs_set_proto_prop(sa_property_t);
81 static sa_protocol_properties_t nfs_get_proto_set();
82 static char *nfs_get_status();
83 static char *nfs_space_alias(char *);
84 static uint64_t nfs_features();
85
86 /*
87 * ops vector that provides the protocol specific info and operations
88 * for share management.
89 */
90
91 struct sa_plugin_ops sa_plugin_ops = {
92 SA_PLUGIN_VERSION,
93 "nfs",
94 nfs_init,
95 nfs_fini,
96 nfs_enable_share,
97 nfs_disable_share,
98 nfs_validate_property,
99 nfs_validate_security_mode,
100 nfs_is_security_opt,
101 nfs_parse_legacy_options,
102 nfs_format_options,
103 nfs_set_proto_prop,
104 nfs_get_proto_set,
105 nfs_get_status,
106 nfs_space_alias,
107 NULL, /* update_legacy */
108 NULL, /* delete_legacy */
109 NULL, /* change_notify */
110 NULL, /* enable_resource */
111 NULL, /* disable_resource */
112 nfs_features,
113 NULL, /* transient shares */
114 NULL, /* notify resource */
115 NULL, /* rename_resource */
116 NULL, /* run_command */
117 NULL, /* command_help */
118 NULL /* delete_proto_section */
119 };
120
121 /*
122 * list of support services needed
123 * defines should come from head/rpcsvc/daemon_utils.h
124 */
125
126 static char *service_list_default[] =
127 { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
128 static char *service_list_logging[] =
129 { NFSLOGD, NULL };
130
131 /*
132 * option definitions. Make sure to keep the #define for the option
133 * index just before the entry it is the index for. Changing the order
134 * can cause breakage. E.g OPT_RW is index 1 and must precede the
135 * line that includes the SHOPT_RW and OPT_RW entries.
136 */
137
138 struct option_defs optdefs[] = {
139 #define OPT_RO 0
140 {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
141 #define OPT_RW 1
142 {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
143 #define OPT_ROOT 2
144 {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
145 #define OPT_SECURE 3
146 {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
147 #define OPT_ANON 4
148 {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
149 #define OPT_WINDOW 5
150 {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
151 #define OPT_NOSUID 6
152 {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
153 #define OPT_ACLOK 7
154 {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
155 #define OPT_NOSUB 8
156 {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
157 #define OPT_SEC 9
158 {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
159 #define OPT_PUBLIC 10
160 {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
161 #define OPT_INDEX 11
162 {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
163 #define OPT_LOG 12
164 {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
165 #define OPT_CKSUM 13
166 {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
167 #define OPT_NONE 14
168 {SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST},
169 #define OPT_ROOT_MAPPING 15
170 {SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER},
171 #define OPT_CHARSET_MAP 16
172 {"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST},
173 #define OPT_NOACLFAB 17
174 {SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN},
175 #define OPT_UIDMAP 18
176 {SHOPT_UIDMAP, OPT_UIDMAP, OPT_TYPE_MAPPING},
177 #define OPT_GIDMAP 19
178 {SHOPT_GIDMAP, OPT_GIDMAP, OPT_TYPE_MAPPING},
179 #define OPT_NOHIDE 20
180 {SHOPT_NOHIDE, OPT_NOHIDE, OPT_TYPE_BOOLEAN},
181 #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
182 #define OPT_VOLFH 21
183 {SHOPT_VOLFH, OPT_VOLFH},
184 #endif /* VOLATILE_FH_TEST */
185 NULL
186 };
187
188 /*
189 * Codesets that may need to be converted to UTF-8 for file paths.
190 * Add new names here to add new property support. If we ever get a
191 * way to query the kernel for character sets, this should become
192 * dynamically loaded. Make sure changes here are reflected in
193 * cmd/fs.d/nfs/mountd/nfs_cmd.c
194 */
195
196 static char *legal_conv[] = {
197 "euc-cn",
198 "euc-jp",
199 "euc-jpms",
200 "euc-kr",
201 "euc-tw",
202 "iso8859-1",
203 "iso8859-2",
204 "iso8859-5",
205 "iso8859-6",
206 "iso8859-7",
207 "iso8859-8",
208 "iso8859-9",
209 "iso8859-13",
210 "iso8859-15",
211 "koi8-r",
212 NULL
213 };
214
215 /*
216 * list of properties that are related to security flavors.
217 */
218 static char *seclist[] = {
219 SHOPT_RO,
220 SHOPT_RW,
221 SHOPT_ROOT,
222 SHOPT_WINDOW,
223 SHOPT_NONE,
224 SHOPT_ROOT_MAPPING,
225 SHOPT_UIDMAP,
226 SHOPT_GIDMAP,
227 NULL
228 };
229
230 /* structure for list of securities */
231 struct securities {
232 sa_security_t security;
233 struct securities *next;
234 };
235
236 /*
237 * findcharset(charset)
238 *
239 * Returns B_TRUE if the charset is a legal conversion otherwise
240 * B_FALSE. This will need to be rewritten to be more efficient when
241 * we have a dynamic list of legal conversions.
242 */
243
244 static boolean_t
245 findcharset(char *charset)
246 {
247 int i;
248
249 for (i = 0; legal_conv[i] != NULL; i++)
250 if (strcmp(charset, legal_conv[i]) == 0)
251 return (B_TRUE);
252 return (B_FALSE);
253 }
254
255 /*
256 * findopt(name)
257 *
258 * Lookup option "name" in the option table and return the table
259 * index.
260 */
261
262 static int
263 findopt(char *name)
264 {
265 int i;
266 if (name != NULL) {
267 for (i = 0; optdefs[i].tag != NULL; i++) {
268 if (strcmp(optdefs[i].tag, name) == 0)
269 return (optdefs[i].index);
270 }
271 if (findcharset(name))
272 return (OPT_CHARSET_MAP);
273 }
274 return (-1);
275 }
276
277 /*
278 * gettype(name)
279 *
280 * Return the type of option "name".
281 */
282
283 static int
284 gettype(char *name)
285 {
286 int optdef;
287
288 optdef = findopt(name);
289 if (optdef != -1)
290 return (optdefs[optdef].type);
291 return (OPT_TYPE_ANY);
292 }
293
294 /*
295 * nfs_validate_security_mode(mode)
296 *
297 * is the specified mode string a valid one for use with NFS?
298 */
299
300 static int
301 nfs_validate_security_mode(char *mode)
302 {
303 seconfig_t secinfo;
304 int err;
305
306 (void) memset(&secinfo, '\0', sizeof (secinfo));
307 err = nfs_getseconfig_byname(mode, &secinfo);
308 if (err == SC_NOERROR)
309 return (1);
310 return (0);
311 }
312
313 /*
314 * nfs_is_security_opt(tok)
315 *
316 * check to see if tok represents an option that is only valid in some
317 * security flavor.
318 */
319
320 static int
321 nfs_is_security_opt(char *tok)
322 {
323 int i;
324
325 for (i = 0; seclist[i] != NULL; i++) {
326 if (strcmp(tok, seclist[i]) == 0)
327 return (1);
328 }
329 return (0);
330 }
331
332 /*
333 * find_security(seclist, sec)
334 *
335 * Walk the current list of security flavors and return true if it is
336 * present, else return false.
337 */
338
339 static int
340 find_security(struct securities *seclist, sa_security_t sec)
341 {
342 while (seclist != NULL) {
343 if (seclist->security == sec)
344 return (1);
345 seclist = seclist->next;
346 }
347 return (0);
348 }
349
350 /*
351 * make_security_list(group, securitymodes, proto)
352 * go through the list of securitymodes and add them to the
353 * group's list of security optionsets. We also keep a list of
354 * those optionsets so we don't have to find them later. All of
355 * these will get copies of the same properties.
356 */
357
358 static struct securities *
359 make_security_list(sa_group_t group, char *securitymodes, char *proto)
360 {
361 char *tok, *next = NULL;
362 struct securities *curp, *headp = NULL, *prev;
363 sa_security_t check;
364 int freetok = 0;
365
366 for (tok = securitymodes; tok != NULL; tok = next) {
367 next = strchr(tok, ':');
368 if (next != NULL)
369 *next++ = '\0';
370 if (strcmp(tok, "default") == 0) {
371 /* resolve default into the real type */
372 tok = nfs_space_alias(tok);
373 freetok = 1;
374 }
375 check = sa_get_security(group, tok, proto);
376
377 /* add to the security list if it isn't there already */
378 if (check == NULL || !find_security(headp, check)) {
379 curp = (struct securities *)calloc(1,
380 sizeof (struct securities));
381 if (curp != NULL) {
382 if (check == NULL) {
383 curp->security = sa_create_security(
384 group, tok, proto);
385 } else {
386 curp->security = check;
387 }
388 /*
389 * note that the first time through the loop,
390 * headp will be NULL and prev will be
391 * undefined. Since headp is NULL, we set
392 * both it and prev to the curp (first
393 * structure to be allocated).
394 *
395 * later passes through the loop will have
396 * headp not being NULL and prev will be used
397 * to allocate at the end of the list.
398 */
399 if (headp == NULL) {
400 headp = curp;
401 prev = curp;
402 } else {
403 prev->next = curp;
404 prev = curp;
405 }
406 }
407 }
408
409 if (freetok) {
410 freetok = 0;
411 sa_free_attr_string(tok);
412 }
413 }
414 return (headp);
415 }
416
417 static void
418 free_security_list(struct securities *sec)
419 {
420 struct securities *next;
421 if (sec != NULL) {
422 for (next = sec->next; sec != NULL; sec = next) {
423 next = sec->next;
424 free(sec);
425 }
426 }
427 }
428
429 /*
430 * nfs_alistcat(str1, str2, sep)
431 *
432 * concatenate str1 and str2 into a new string using sep as a separate
433 * character. If memory allocation fails, return NULL;
434 */
435
436 static char *
437 nfs_alistcat(char *str1, char *str2, char sep)
438 {
439 char *newstr;
440 size_t len;
441
442 len = strlen(str1) + strlen(str2) + 2;
443 newstr = (char *)malloc(len);
444 if (newstr != NULL)
445 (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
446 return (newstr);
447 }
448
449 /*
450 * add_security_prop(sec, name, value, persist, iszfs)
451 *
452 * Add the property to the securities structure. This accumulates
453 * properties for as part of parsing legacy options.
454 */
455
456 static int
457 add_security_prop(struct securities *sec, char *name, char *value,
458 int persist, int iszfs)
459 {
460 sa_property_t prop;
461 int ret = SA_OK;
462
463 for (; sec != NULL; sec = sec->next) {
464 if (value == NULL) {
465 if (strcmp(name, SHOPT_RW) == 0 ||
466 strcmp(name, SHOPT_RO) == 0)
467 value = "*";
468 else
469 value = "true";
470 }
471
472 /*
473 * Get the existing property, if it exists, so we can
474 * determine what to do with it. The ro/rw/root
475 * properties can be merged if multiple instances of
476 * these properies are given. For example, if "rw"
477 * exists with a value "host1" and a later token of
478 * rw="host2" is seen, the values are merged into a
479 * single rw="host1:host2".
480 */
481 prop = sa_get_property(sec->security, name);
482
483 if (prop != NULL) {
484 char *oldvalue;
485 char *newvalue;
486
487 /*
488 * The security options of ro/rw/root/uidmap/gidmap
489 * might appear multiple times. If they do, the values
490 * need to be merged. If it was previously empty, the
491 * new value alone is added.
492 */
493 oldvalue = sa_get_property_attr(prop, "value");
494 if (oldvalue != NULL) {
495 char sep = ':';
496
497 if (strcmp(name, SHOPT_UIDMAP) == 0 ||
498 strcmp(name, SHOPT_GIDMAP) == 0)
499 sep = '~';
500
501 /*
502 * The general case is to concatenate the new
503 * value onto the old value for multiple
504 * rw(ro/root/uidmap/gidmap) properties. For
505 * rw/ro/root a special case exists when either
506 * the old or new is the "all" case. In the
507 * special case, if both are "all", then it is
508 * "all", else if one is an access-list, that
509 * replaces the "all".
510 */
511 if (strcmp(oldvalue, "*") == 0) {
512 /* Replace old value with new value. */
513 newvalue = strdup(value);
514 } else if (strcmp(value, "*") == 0 ||
515 strcmp(oldvalue, value) == 0) {
516 /*
517 * Keep old value and ignore
518 * the new value.
519 */
520 newvalue = NULL;
521 } else {
522 /*
523 * Make a new list of old plus new
524 * access-list.
525 */
526 newvalue = nfs_alistcat(oldvalue,
527 value, sep);
528 }
529
530 if (newvalue != NULL) {
531 (void) sa_remove_property(prop);
532 prop = sa_create_property(name,
533 newvalue);
534 ret = sa_add_property(sec->security,
535 prop);
536 free(newvalue);
537 }
538
539 sa_free_attr_string(oldvalue);
540 }
541 } else {
542 prop = sa_create_property(name, value);
543 ret = sa_add_property(sec->security, prop);
544 }
545 if (ret == SA_OK && !iszfs) {
546 ret = sa_commit_properties(sec->security, !persist);
547 }
548 }
549 return (ret);
550 }
551
552 /*
553 * check to see if group/share is persistent.
554 */
555 static int
556 is_persistent(sa_group_t group)
557 {
558 char *type;
559 int persist = 1;
560
561 type = sa_get_group_attr(group, "type");
562 if (type != NULL && strcmp(type, "persist") != 0)
563 persist = 0;
564 if (type != NULL)
565 sa_free_attr_string(type);
566 return (persist);
567 }
568
569 /*
570 * invalid_security(options)
571 *
572 * search option string for any invalid sec= type.
573 * return true (1) if any are not valid else false (0)
574 */
575 static int
576 invalid_security(char *options)
577 {
578 char *copy, *base, *token, *value;
579 int ret = 0;
580
581 copy = strdup(options);
582 token = base = copy;
583 while (token != NULL && ret == 0) {
584 token = strtok(base, ",");
585 base = NULL;
586 if (token != NULL) {
587 value = strchr(token, '=');
588 if (value != NULL)
589 *value++ = '\0';
590 if (strcmp(token, SHOPT_SEC) == 0) {
591 /* HAVE security flavors so check them */
592 char *tok, *next;
593 for (next = NULL, tok = value; tok != NULL;
594 tok = next) {
595 next = strchr(tok, ':');
596 if (next != NULL)
597 *next++ = '\0';
598 ret = !nfs_validate_security_mode(tok);
599 if (ret)
600 break;
601 }
602 }
603 }
604 }
605 if (copy != NULL)
606 free(copy);
607 return (ret);
608 }
609
610 /*
611 * nfs_parse_legacy_options(group, options)
612 *
613 * Parse the old style options into internal format and store on the
614 * specified group. Group could be a share for full legacy support.
615 */
616
617 static int
618 nfs_parse_legacy_options(sa_group_t group, char *options)
619 {
620 char *dup;
621 char *base;
622 char *token;
623 sa_optionset_t optionset;
624 struct securities *security_list = NULL;
625 sa_property_t prop;
626 int ret = SA_OK;
627 int iszfs = 0;
628 sa_group_t parent;
629 int persist = 0;
630 char *lasts;
631
632 /* do we have an existing optionset? */
633 optionset = sa_get_optionset(group, "nfs");
634 if (optionset == NULL) {
635 /* didn't find existing optionset so create one */
636 optionset = sa_create_optionset(group, "nfs");
637 } else {
638 /*
639 * Have an existing optionset . Ideally, we would need
640 * to compare options in order to detect errors. For
641 * now, we assume that the first optionset is the
642 * correct one and the others will be the same. An
643 * empty optionset is the same as no optionset so we
644 * don't want to exit in that case. Getting an empty
645 * optionset can occur with ZFS property checking.
646 */
647 if (sa_get_property(optionset, NULL) != NULL)
648 return (ret);
649 }
650
651 if (strcmp(options, SHOPT_RW) == 0) {
652 /*
653 * there is a special case of only the option "rw"
654 * being the default option. We don't have to do
655 * anything.
656 */
657 return (ret);
658 }
659
660 /*
661 * check if security types are present and validate them. If
662 * any are not legal, fail.
663 */
664
665 if (invalid_security(options)) {
666 return (SA_INVALID_SECURITY);
667 }
668
669 /*
670 * in order to not attempt to change ZFS properties unless
671 * absolutely necessary, we never do it in the legacy parsing.
672 */
673 if (sa_is_share(group)) {
674 char *zfs;
675 parent = sa_get_parent_group(group);
676 if (parent != NULL) {
677 zfs = sa_get_group_attr(parent, "zfs");
678 if (zfs != NULL) {
679 sa_free_attr_string(zfs);
680 iszfs++;
681 }
682 }
683 } else {
684 iszfs = sa_group_is_zfs(group);
685 }
686
687 /* We need a copy of options for the next part. */
688 dup = strdup(options);
689 if (dup == NULL)
690 return (SA_NO_MEMORY);
691
692 /*
693 * we need to step through each option in the string and then
694 * add either the option or the security option as needed. If
695 * this is not a persistent share, don't commit to the
696 * repository. If there is an error, we also want to abort the
697 * processing and report it.
698 */
699 persist = is_persistent(group);
700 base = dup;
701 token = dup;
702 lasts = NULL;
703 while (token != NULL && ret == SA_OK) {
704 token = strtok_r(base, ",", &lasts);
705 base = NULL;
706 if (token != NULL) {
707 char *value;
708 /*
709 * if the option has a value, it will have an '=' to
710 * separate the name from the value. The following
711 * code will result in value != NULL and token
712 * pointing to just the name if there is a value.
713 */
714 value = strchr(token, '=');
715 if (value != NULL) {
716 *value++ = '\0';
717 }
718 if (strcmp(token, SHOPT_SEC) == 0 ||
719 strcmp(token, SHOPT_SECURE) == 0) {
720 /*
721 * Once in security parsing, we only
722 * do security. We do need to move
723 * between the security node and the
724 * toplevel. The security tag goes on
725 * the root while the following ones
726 * go on the security.
727 */
728 if (security_list != NULL) {
729 /*
730 * have an old list so close it and
731 * start the new
732 */
733 free_security_list(security_list);
734 }
735 if (strcmp(token, SHOPT_SECURE) == 0) {
736 value = "dh";
737 } else {
738 if (value == NULL) {
739 ret = SA_SYNTAX_ERR;
740 break;
741 }
742 }
743 security_list = make_security_list(group,
744 value, "nfs");
745 } else {
746 /*
747 * Note that the "old" syntax allowed a
748 * default security model. This must be
749 * accounted for and internally converted to
750 * "standard" security structure.
751 */
752 if (nfs_is_security_opt(token)) {
753 if (security_list == NULL) {
754 /*
755 * need to have a
756 * security
757 * option. This will
758 * be "closed" when a
759 * defined "sec="
760 * option is
761 * seen. This is
762 * technically an
763 * error but will be
764 * allowed with
765 * warning.
766 */
767 security_list =
768 make_security_list(group,
769 "default",
770 "nfs");
771 }
772 if (security_list != NULL) {
773 ret = add_security_prop(
774 security_list, token,
775 value, persist, iszfs);
776 } else {
777 ret = SA_NO_MEMORY;
778 }
779 } else {
780 /* regular options */
781 if (value == NULL) {
782 if (strcmp(token, SHOPT_RW) ==
783 0 || strcmp(token,
784 SHOPT_RO) == 0) {
785 value = "*";
786 } else {
787 value = "global";
788 if (strcmp(token,
789 SHOPT_LOG) != 0) {
790 value = "true";
791 }
792 }
793 }
794 /*
795 * In all cases, create the
796 * property specified. If the
797 * value was NULL, the default
798 * value will have been
799 * substituted.
800 */
801 prop = sa_create_property(token, value);
802 ret = sa_add_property(optionset, prop);
803 if (ret != SA_OK)
804 break;
805
806 if (!iszfs) {
807 ret = sa_commit_properties(
808 optionset, !persist);
809 }
810 }
811 }
812 }
813 }
814 if (security_list != NULL)
815 free_security_list(security_list);
816
817 free(dup);
818 return (ret);
819 }
820
821 /*
822 * is_a_number(number)
823 *
824 * is the string a number in one of the forms we want to use?
825 */
826
827 static int
828 is_a_number(char *number)
829 {
830 int ret = 1;
831 int hex = 0;
832
833 if (strncmp(number, "0x", 2) == 0) {
834 number += 2;
835 hex = 1;
836 } else if (*number == '-') {
837 number++; /* skip the minus */
838 }
839 while (ret == 1 && *number != '\0') {
840 if (hex) {
841 ret = isxdigit(*number++);
842 } else {
843 ret = isdigit(*number++);
844 }
845 }
846 return (ret);
847 }
848
849 /*
850 * Look for the specified tag in the configuration file. If it is found,
851 * enable logging and set the logging configuration information for exp.
852 */
853 static void
854 configlog(struct exportdata *exp, char *tag)
855 {
856 nfsl_config_t *configlist = NULL, *configp;
857 int error = 0;
858 char globaltag[] = DEFAULTTAG;
859
860 /*
861 * Sends config errors to stderr
862 */
863 nfsl_errs_to_syslog = B_FALSE;
864
865 /*
866 * get the list of configuration settings
867 */
868 error = nfsl_getconfig_list(&configlist);
869 if (error) {
870 (void) fprintf(stderr,
871 dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"),
872 strerror(error));
873 }
874
875 if (tag == NULL)
876 tag = globaltag;
877 if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
878 nfsl_freeconfig_list(&configlist);
879 (void) fprintf(stderr,
880 dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag);
881 /* bad configuration */
882 error = ENOENT;
883 goto err;
884 }
885
886 if ((exp->ex_tag = strdup(tag)) == NULL) {
887 error = ENOMEM;
888 goto out;
889 }
890 if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
891 error = ENOMEM;
892 goto out;
893 }
894 exp->ex_flags |= EX_LOG;
895 if (configp->nc_rpclogpath != NULL)
896 exp->ex_flags |= EX_LOG_ALLOPS;
897 out:
898 if (configlist != NULL)
899 nfsl_freeconfig_list(&configlist);
900
901 err:
902 if (error != 0) {
903 free(exp->ex_tag);
904 free(exp->ex_log_buffer);
905 (void) fprintf(stderr,
906 dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"),
907 strerror(error));
908 }
909 }
910
911 /*
912 * fill_export_from_optionset(export, optionset)
913 *
914 * In order to share, we need to set all the possible general options
915 * into the export structure. Share info will be filled in by the
916 * caller. Various property values get turned into structure specific
917 * values.
918 */
919
920 static int
921 fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
922 {
923 sa_property_t option;
924 int ret = SA_OK;
925
926 for (option = sa_get_property(optionset, NULL);
927 option != NULL; option = sa_get_next_property(option)) {
928 char *name;
929 char *value;
930 uint32_t val;
931
932 /*
933 * since options may be set/reset multiple times, always do an
934 * explicit set or clear of the option. This allows defaults
935 * to be set and then the protocol specific to override.
936 */
937
938 name = sa_get_property_attr(option, "type");
939 value = sa_get_property_attr(option, "value");
940 switch (findopt(name)) {
941 case OPT_ANON:
942 if (value != NULL && is_a_number(value)) {
943 val = strtoul(value, NULL, 0);
944 } else {
945 struct passwd *pw;
946 pw = getpwnam(value != NULL ? value : "nobody");
947 if (pw != NULL) {
948 val = pw->pw_uid;
949 } else {
950 val = UID_NOBODY;
951 }
952 endpwent();
953 }
954 export->ex_anon = val;
955 break;
956 case OPT_NOSUID:
957 if (value != NULL && (strcasecmp(value, "true") == 0 ||
958 strcmp(value, "1") == 0))
959 export->ex_flags |= EX_NOSUID;
960 else
961 export->ex_flags &= ~EX_NOSUID;
962 break;
963 case OPT_ACLOK:
964 if (value != NULL && (strcasecmp(value, "true") == 0 ||
965 strcmp(value, "1") == 0))
966 export->ex_flags |= EX_ACLOK;
967 else
968 export->ex_flags &= ~EX_ACLOK;
969 break;
970 case OPT_NOSUB:
971 if (value != NULL && (strcasecmp(value, "true") == 0 ||
972 strcmp(value, "1") == 0))
973 export->ex_flags |= EX_NOSUB;
974 else
975 export->ex_flags &= ~EX_NOSUB;
976 break;
977 case OPT_PUBLIC:
978 if (value != NULL && (strcasecmp(value, "true") == 0 ||
979 strcmp(value, "1") == 0))
980 export->ex_flags |= EX_PUBLIC;
981 else
982 export->ex_flags &= ~EX_PUBLIC;
983 break;
984 case OPT_INDEX:
985 if (value != NULL && (strcmp(value, "..") == 0 ||
986 strchr(value, '/') != NULL)) {
987 /* this is an error */
988 (void) printf(dgettext(TEXT_DOMAIN,
989 "NFS: index=\"%s\" not valid;"
990 "must be a filename.\n"),
991 value);
992 break;
993 }
994 if (value != NULL && *value != '\0' &&
995 strcmp(value, ".") != 0) {
996 /* valid index file string */
997 if (export->ex_index != NULL) {
998 /* left over from "default" */
999 free(export->ex_index);
1000 }
1001 /* remember to free */
1002 export->ex_index = strdup(value);
1003 if (export->ex_index == NULL) {
1004 (void) printf(dgettext(TEXT_DOMAIN,
1005 "NFS: out of memory setting "
1006 "index property\n"));
1007 break;
1008 }
1009 export->ex_flags |= EX_INDEX;
1010 }
1011 break;
1012 case OPT_LOG:
1013 if (value == NULL)
1014 value = strdup("global");
1015 if (value != NULL)
1016 configlog(export,
1017 strlen(value) ? value : "global");
1018 break;
1019 case OPT_CHARSET_MAP:
1020 /*
1021 * Set EX_CHARMAP when there is at least one
1022 * charmap conversion property. This will get
1023 * checked by the nfs server when it needs to.
1024 */
1025 export->ex_flags |= EX_CHARMAP;
1026 break;
1027 case OPT_NOACLFAB:
1028 if (value != NULL && (strcasecmp(value, "true") == 0 ||
1029 strcmp(value, "1") == 0))
1030 export->ex_flags |= EX_NOACLFAB;
1031 else
1032 export->ex_flags &= ~EX_NOACLFAB;
1033 break;
1034 case OPT_NOHIDE:
1035 if (value != NULL && (strcasecmp(value, "true") == 0 ||
1036 strcmp(value, "1") == 0))
1037 export->ex_flags |= EX_NOHIDE;
1038 else
1039 export->ex_flags &= ~EX_NOHIDE;
1040
1041 break;
1042 default:
1043 /* have a syntactic error */
1044 (void) printf(dgettext(TEXT_DOMAIN,
1045 "NFS: unrecognized option %s=%s\n"),
1046 name != NULL ? name : "",
1047 value != NULL ? value : "");
1048 break;
1049 }
1050 if (name != NULL)
1051 sa_free_attr_string(name);
1052 if (value != NULL)
1053 sa_free_attr_string(value);
1054 }
1055 return (ret);
1056 }
1057
1058 /*
1059 * cleanup_export(export)
1060 *
1061 * Cleanup the allocated areas so we don't leak memory
1062 */
1063
1064 static void
1065 cleanup_export(struct exportdata *export)
1066 {
1067 int i;
1068
1069 free(export->ex_index);
1070
1071 for (i = 0; i < export->ex_seccnt; i++) {
1072 struct secinfo *s = &export->ex_secinfo[i];
1073
1074 while (s->s_rootcnt > 0)
1075 free(s->s_rootnames[--s->s_rootcnt]);
1076
1077 free(s->s_rootnames);
1078 }
1079 free(export->ex_secinfo);
1080 }
1081
1082 /*
1083 * Given a seconfig entry and a colon-separated
1084 * list of names, allocate an array big enough
1085 * to hold the root list, then convert each name to
1086 * a principal name according to the security
1087 * info and assign it to an array element.
1088 * Return the array and its size.
1089 */
1090 static caddr_t *
1091 get_rootnames(seconfig_t *sec, char *list, int *count)
1092 {
1093 caddr_t *a;
1094 int c, i;
1095 char *host, *p;
1096
1097 /*
1098 * Count the number of strings in the list.
1099 * This is the number of colon separators + 1.
1100 */
1101 c = 1;
1102 for (p = list; *p; p++)
1103 if (*p == ':')
1104 c++;
1105 *count = c;
1106
1107 a = (caddr_t *)malloc(c * sizeof (char *));
1108 if (a == NULL) {
1109 (void) printf(dgettext(TEXT_DOMAIN,
1110 "get_rootnames: no memory\n"));
1111 *count = 0;
1112 } else {
1113 for (i = 0; i < c; i++) {
1114 host = strtok(list, ":");
1115 if (!nfs_get_root_principal(sec, host, &a[i])) {
1116 while (i > 0)
1117 free(a[--i]);
1118 free(a);
1119 a = NULL;
1120 *count = 0;
1121 break;
1122 }
1123 list = NULL;
1124 }
1125 }
1126
1127 return (a);
1128 }
1129
1130 /*
1131 * fill_security_from_secopts(sp, secopts)
1132 *
1133 * Fill the secinfo structure from the secopts optionset.
1134 */
1135
1136 static int
1137 fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
1138 {
1139 sa_property_t prop;
1140 char *type;
1141 int longform;
1142 int err = SC_NOERROR;
1143 uint32_t val;
1144
1145 type = sa_get_security_attr(secopts, "sectype");
1146 if (type != NULL) {
1147 /* named security type needs secinfo to be filled in */
1148 err = nfs_getseconfig_byname(type, &sp->s_secinfo);
1149 sa_free_attr_string(type);
1150 if (err != SC_NOERROR)
1151 return (err);
1152 } else {
1153 /* default case */
1154 err = nfs_getseconfig_default(&sp->s_secinfo);
1155 if (err != SC_NOERROR)
1156 return (err);
1157 }
1158
1159 err = SA_OK;
1160 for (prop = sa_get_property(secopts, NULL);
1161 prop != NULL && err == SA_OK;
1162 prop = sa_get_next_property(prop)) {
1163 char *name;
1164 char *value;
1165
1166 name = sa_get_property_attr(prop, "type");
1167 value = sa_get_property_attr(prop, "value");
1168
1169 longform = value != NULL && strcmp(value, "*") != 0;
1170
1171 switch (findopt(name)) {
1172 case OPT_RO:
1173 sp->s_flags |= longform ? M_ROL : M_RO;
1174 break;
1175 case OPT_RW:
1176 sp->s_flags |= longform ? M_RWL : M_RW;
1177 break;
1178 case OPT_ROOT:
1179 sp->s_flags |= M_ROOT;
1180 /*
1181 * if we are using AUTH_UNIX, handle like other things
1182 * such as RO/RW
1183 */
1184 if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
1185 break;
1186 /* not AUTH_UNIX */
1187 if (value != NULL) {
1188 sp->s_rootnames = get_rootnames(&sp->s_secinfo,
1189 value, &sp->s_rootcnt);
1190 if (sp->s_rootnames == NULL) {
1191 err = SA_BAD_VALUE;
1192 (void) fprintf(stderr,
1193 dgettext(TEXT_DOMAIN,
1194 "Bad root list\n"));
1195 }
1196 }
1197 break;
1198 case OPT_NONE:
1199 sp->s_flags |= M_NONE;
1200 break;
1201 case OPT_WINDOW:
1202 if (value != NULL) {
1203 sp->s_window = atoi(value);
1204 /* just in case */
1205 if (sp->s_window < 0)
1206 sp->s_window = DEF_WIN;
1207 }
1208 break;
1209 case OPT_ROOT_MAPPING:
1210 if (value != NULL && is_a_number(value)) {
1211 val = strtoul(value, NULL, 0);
1212 } else {
1213 struct passwd *pw;
1214 pw = getpwnam(value != NULL ? value : "nobody");
1215 if (pw != NULL) {
1216 val = pw->pw_uid;
1217 } else {
1218 val = UID_NOBODY;
1219 }
1220 endpwent();
1221 }
1222 sp->s_rootid = val;
1223 break;
1224 case OPT_UIDMAP:
1225 case OPT_GIDMAP:
1226 sp->s_flags |= M_MAP;
1227 break;
1228 default:
1229 break;
1230 }
1231 if (name != NULL)
1232 sa_free_attr_string(name);
1233 if (value != NULL)
1234 sa_free_attr_string(value);
1235 }
1236 /* if rw/ro options not set, use default of RW */
1237 if ((sp->s_flags & NFS_RWMODES) == 0)
1238 sp->s_flags |= M_RW;
1239 return (err);
1240 }
1241
1242 /*
1243 * This is for testing only
1244 * It displays the export structure that
1245 * goes into the kernel.
1246 */
1247 static void
1248 printarg(char *path, struct exportdata *ep)
1249 {
1250 int i, j;
1251 struct secinfo *sp;
1252
1253 if (debug == 0)
1254 return;
1255
1256 (void) printf("%s:\n", path);
1257 (void) printf("\tex_version = %d\n", ep->ex_version);
1258 (void) printf("\tex_path = %s\n", ep->ex_path);
1259 (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen);
1260 (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
1261 if (ep->ex_flags & EX_NOSUID)
1262 (void) printf("NOSUID ");
1263 if (ep->ex_flags & EX_ACLOK)
1264 (void) printf("ACLOK ");
1265 if (ep->ex_flags & EX_PUBLIC)
1266 (void) printf("PUBLIC ");
1267 if (ep->ex_flags & EX_NOSUB)
1268 (void) printf("NOSUB ");
1269 if (ep->ex_flags & EX_LOG)
1270 (void) printf("LOG ");
1271 if (ep->ex_flags & EX_CHARMAP)
1272 (void) printf("CHARMAP ");
1273 if (ep->ex_flags & EX_LOG_ALLOPS)
1274 (void) printf("LOG_ALLOPS ");
1275 if (ep->ex_flags == 0)
1276 (void) printf("(none)");
1277 (void) printf("\n");
1278 if (ep->ex_flags & EX_LOG) {
1279 (void) printf("\tex_log_buffer = %s\n",
1280 (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
1281 (void) printf("\tex_tag = %s\n",
1282 (ep->ex_tag ? ep->ex_tag : "(NULL)"));
1283 }
1284 (void) printf("\tex_anon = %d\n", ep->ex_anon);
1285 (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
1286 (void) printf("\n");
1287 for (i = 0; i < ep->ex_seccnt; i++) {
1288 sp = &ep->ex_secinfo[i];
1289 (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
1290 (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
1291 if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
1292 if (sp->s_flags & M_RO) (void) printf("M_RO ");
1293 if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
1294 if (sp->s_flags & M_RW) (void) printf("M_RW ");
1295 if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
1296 if (sp->s_flags & M_NONE) (void) printf("M_NONE ");
1297 if (sp->s_flags & M_MAP) (void) printf("M_MAP ");
1298 if (sp->s_flags == 0) (void) printf("(none)");
1299 (void) printf("\n");
1300 (void) printf("\t\ts_window = %d\n", sp->s_window);
1301 (void) printf("\t\ts_rootid = %d\n", sp->s_rootid);
1302 (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
1303 (void) fflush(stdout);
1304 for (j = 0; j < sp->s_rootcnt; j++)
1305 (void) printf("%s ", sp->s_rootnames[j] ?
1306 sp->s_rootnames[j] : "<null>");
1307 (void) printf("\n\n");
1308 }
1309 }
1310
1311 /*
1312 * count_security(opts)
1313 *
1314 * Count the number of security types (flavors). The optionset has
1315 * been populated with the security flavors as a holding mechanism.
1316 * We later use this number to allocate data structures.
1317 */
1318
1319 static int
1320 count_security(sa_optionset_t opts)
1321 {
1322 int count = 0;
1323 sa_property_t prop;
1324 if (opts != NULL) {
1325 for (prop = sa_get_property(opts, NULL); prop != NULL;
1326 prop = sa_get_next_property(prop)) {
1327 count++;
1328 }
1329 }
1330 return (count);
1331 }
1332
1333 /*
1334 * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1335 *
1336 * provides a mechanism to format NFS properties into legacy output
1337 * format. If the buffer would overflow, it is reallocated and grown
1338 * as appropriate. Special cases of converting internal form of values
1339 * to those used by "share" are done. this function does one property
1340 * at a time.
1341 */
1342
1343 static int
1344 nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1345 sa_property_t prop, int sep)
1346 {
1347 char *name;
1348 char *value;
1349 int curlen;
1350 char *buff = *rbuff;
1351 size_t buffsize = *rbuffsize;
1352 int printed = B_FALSE;
1353
1354 name = sa_get_property_attr(prop, "type");
1355 value = sa_get_property_attr(prop, "value");
1356 if (buff != NULL)
1357 curlen = strlen(buff);
1358 else
1359 curlen = 0;
1360 if (name != NULL) {
1361 int len;
1362 len = strlen(name) + sep;
1363
1364 /*
1365 * A future RFE would be to replace this with more
1366 * generic code and to possibly handle more types.
1367 */
1368 switch (gettype(name)) {
1369 case OPT_TYPE_BOOLEAN:
1370 /*
1371 * For NFS, boolean value of FALSE means it
1372 * doesn't show up in the option list at all.
1373 */
1374 if (value != NULL && strcasecmp(value, "false") == 0)
1375 goto skip;
1376 if (value != NULL) {
1377 sa_free_attr_string(value);
1378 value = NULL;
1379 }
1380 break;
1381 case OPT_TYPE_ACCLIST:
1382 if (value != NULL && strcmp(value, "*") == 0) {
1383 sa_free_attr_string(value);
1384 value = NULL;
1385 } else {
1386 if (value != NULL)
1387 len += 1 + strlen(value);
1388 }
1389 break;
1390 case OPT_TYPE_LOGTAG:
1391 if (value != NULL && strlen(value) == 0) {
1392 sa_free_attr_string(value);
1393 value = NULL;
1394 } else {
1395 if (value != NULL)
1396 len += 1 + strlen(value);
1397 }
1398 break;
1399 default:
1400 if (value != NULL)
1401 len += 1 + strlen(value);
1402 break;
1403 }
1404 while (buffsize <= (curlen + len)) {
1405 /* need more room */
1406 buffsize += incr;
1407 buff = realloc(buff, buffsize);
1408 if (buff == NULL) {
1409 /* realloc failed so free everything */
1410 if (*rbuff != NULL)
1411 free(*rbuff);
1412 }
1413 *rbuff = buff;
1414 *rbuffsize = buffsize;
1415 if (buff == NULL)
1416 goto skip;
1417
1418 }
1419
1420 if (buff == NULL)
1421 goto skip;
1422
1423 if (value == NULL) {
1424 (void) snprintf(buff + curlen, buffsize - curlen,
1425 "%s%s", sep ? "," : "", name);
1426 } else {
1427 (void) snprintf(buff + curlen, buffsize - curlen,
1428 "%s%s=%s", sep ? "," : "",
1429 name, value != NULL ? value : "");
1430 }
1431 printed = B_TRUE;
1432 }
1433 skip:
1434 if (name != NULL)
1435 sa_free_attr_string(name);
1436 if (value != NULL)
1437 sa_free_attr_string(value);
1438 return (printed);
1439 }
1440
1441 /*
1442 * nfs_format_options(group, hier)
1443 *
1444 * format all the options on the group into an old-style option
1445 * string. If hier is non-zero, walk up the tree to get inherited
1446 * options.
1447 */
1448
1449 static char *
1450 nfs_format_options(sa_group_t group, int hier)
1451 {
1452 sa_optionset_t options = NULL;
1453 sa_optionset_t secoptions = NULL;
1454 sa_property_t prop, secprop;
1455 sa_security_t security = NULL;
1456 char *buff;
1457 size_t buffsize;
1458 char *sectype = NULL;
1459 int sep = 0;
1460
1461
1462 buff = malloc(OPT_CHUNK);
1463 if (buff == NULL) {
1464 return (NULL);
1465 }
1466
1467 buff[0] = '\0';
1468 buffsize = OPT_CHUNK;
1469
1470 /*
1471 * We may have a an optionset relative to this item. format
1472 * these if we find them and then add any security definitions.
1473 */
1474
1475 options = sa_get_derived_optionset(group, "nfs", hier);
1476
1477 /*
1478 * do the default set first but skip any option that is also
1479 * in the protocol specific optionset.
1480 */
1481 if (options != NULL) {
1482 for (prop = sa_get_property(options, NULL);
1483 prop != NULL; prop = sa_get_next_property(prop)) {
1484 /*
1485 * use this one since we skipped any
1486 * of these that were also in
1487 * optdefault
1488 */
1489 if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
1490 prop, sep))
1491 sep = 1;
1492 if (buff == NULL) {
1493 /*
1494 * buff could become NULL if there
1495 * isn't enough memory for
1496 * nfs_sprint_option to realloc()
1497 * as necessary. We can't really
1498 * do anything about it at this
1499 * point so we return NULL. The
1500 * caller should handle the
1501 * failure.
1502 */
1503 if (options != NULL)
1504 sa_free_derived_optionset(
1505 options);
1506 return (buff);
1507 }
1508 }
1509 }
1510 secoptions = (sa_optionset_t)sa_get_all_security_types(group,
1511 "nfs", hier);
1512 if (secoptions != NULL) {
1513 for (secprop = sa_get_property(secoptions, NULL);
1514 secprop != NULL;
1515 secprop = sa_get_next_property(secprop)) {
1516 sectype = sa_get_property_attr(secprop, "type");
1517 security =
1518 (sa_security_t)sa_get_derived_security(
1519 group, sectype, "nfs", hier);
1520 if (security != NULL) {
1521 if (sectype != NULL) {
1522 prop = sa_create_property(
1523 "sec", sectype);
1524 if (prop == NULL)
1525 goto err;
1526 if (nfs_sprint_option(&buff,
1527 &buffsize, OPT_CHUNK, prop, sep))
1528 sep = 1;
1529 (void) sa_remove_property(prop);
1530 if (buff == NULL)
1531 goto err;
1532 }
1533 for (prop = sa_get_property(security,
1534 NULL); prop != NULL;
1535 prop = sa_get_next_property(prop)) {
1536 if (nfs_sprint_option(&buff,
1537 &buffsize, OPT_CHUNK, prop, sep))
1538 sep = 1;
1539 if (buff == NULL)
1540 goto err;
1541 }
1542 sa_free_derived_optionset(security);
1543 }
1544 if (sectype != NULL)
1545 sa_free_attr_string(sectype);
1546 }
1547 sa_free_derived_optionset(secoptions);
1548 }
1549
1550 if (options != NULL)
1551 sa_free_derived_optionset(options);
1552 return (buff);
1553
1554 err:
1555 /*
1556 * If we couldn't allocate memory for option printing, we need
1557 * to break out of the nested loops, cleanup and return NULL.
1558 */
1559 if (secoptions != NULL)
1560 sa_free_derived_optionset(secoptions);
1561 if (security != NULL)
1562 sa_free_derived_optionset(security);
1563 if (sectype != NULL)
1564 sa_free_attr_string(sectype);
1565 if (options != NULL)
1566 sa_free_derived_optionset(options);
1567 return (buff);
1568 }
1569
1570 /*
1571 * Append an entry to the nfslogtab file
1572 */
1573 static int
1574 nfslogtab_add(char *dir, char *buffer, char *tag)
1575 {
1576 FILE *f;
1577 struct logtab_ent lep;
1578 int error = 0;
1579
1580 /*
1581 * Open the file for update and create it if necessary.
1582 * This may leave the I/O offset at the end of the file,
1583 * so rewind back to the beginning of the file.
1584 */
1585 f = fopen(NFSLOGTAB, "a+");
1586 if (f == NULL) {
1587 error = errno;
1588 goto out;
1589 }
1590 rewind(f);
1591
1592 if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1593 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1594 "share complete, however failed to lock %s "
1595 "for update: %s\n"), NFSLOGTAB, strerror(errno));
1596 error = -1;
1597 goto out;
1598 }
1599
1600 if (logtab_deactivate_after_boot(f) == -1) {
1601 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1602 "share complete, however could not deactivate "
1603 "entries in %s\n"), NFSLOGTAB);
1604 error = -1;
1605 goto out;
1606 }
1607
1608 /*
1609 * Remove entries matching buffer and sharepoint since we're
1610 * going to replace it with perhaps an entry with a new tag.
1611 */
1612 if (logtab_rement(f, buffer, dir, NULL, -1)) {
1613 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1614 "share complete, however could not remove matching "
1615 "entries in %s\n"), NFSLOGTAB);
1616 error = -1;
1617 goto out;
1618 }
1619
1620 /*
1621 * Deactivate all active entries matching this sharepoint
1622 */
1623 if (logtab_deactivate(f, NULL, dir, NULL)) {
1624 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1625 "share complete, however could not deactivate matching "
1626 "entries in %s\n"), NFSLOGTAB);
1627 error = -1;
1628 goto out;
1629 }
1630
1631 lep.le_buffer = buffer;
1632 lep.le_path = dir;
1633 lep.le_tag = tag;
1634 lep.le_state = LES_ACTIVE;
1635
1636 /*
1637 * Add new sharepoint / buffer location to nfslogtab
1638 */
1639 if (logtab_putent(f, &lep) < 0) {
1640 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1641 "share complete, however could not add %s to %s\n"),
1642 dir, NFSLOGTAB);
1643 error = -1;
1644 }
1645
1646 out:
1647 if (f != NULL)
1648 (void) fclose(f);
1649 return (error);
1650 }
1651
1652 /*
1653 * Deactivate an entry from the nfslogtab file
1654 */
1655 static int
1656 nfslogtab_deactivate(char *path)
1657 {
1658 FILE *f;
1659 int error = 0;
1660
1661 f = fopen(NFSLOGTAB, "r+");
1662 if (f == NULL) {
1663 error = errno;
1664 goto out;
1665 }
1666 if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1667 error = errno;
1668 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1669 "share complete, however could not lock %s for "
1670 "update: %s\n"), NFSLOGTAB, strerror(error));
1671 goto out;
1672 }
1673 if (logtab_deactivate(f, NULL, path, NULL) == -1) {
1674 error = -1;
1675 (void) fprintf(stderr,
1676 dgettext(TEXT_DOMAIN,
1677 "share complete, however could not "
1678 "deactivate %s in %s\n"), path, NFSLOGTAB);
1679 goto out;
1680 }
1681
1682 out: if (f != NULL)
1683 (void) fclose(f);
1684
1685 return (error);
1686 }
1687
1688 /*
1689 * check_public(group, skipshare)
1690 *
1691 * Check the group for any shares that have the public property
1692 * enabled. We skip "skipshare" since that is the one we are
1693 * working with. This is a separate function to make handling
1694 * subgroups simpler. Returns true if there is a share with public.
1695 */
1696 static int
1697 check_public(sa_group_t group, sa_share_t skipshare)
1698 {
1699 int exists = B_FALSE;
1700 sa_share_t share;
1701 sa_optionset_t opt;
1702 sa_property_t prop;
1703 char *shared;
1704
1705 for (share = sa_get_share(group, NULL); share != NULL;
1706 share = sa_get_next_share(share)) {
1707 if (share == skipshare)
1708 continue;
1709
1710 opt = sa_get_optionset(share, "nfs");
1711 if (opt == NULL)
1712 continue;
1713 prop = sa_get_property(opt, "public");
1714 if (prop == NULL)
1715 continue;
1716 shared = sa_get_share_attr(share, "shared");
1717 if (shared != NULL) {
1718 exists = strcmp(shared, "true") == 0;
1719 sa_free_attr_string(shared);
1720 if (exists == B_TRUE)
1721 break;
1722 }
1723 }
1724
1725 return (exists);
1726 }
1727
1728 /*
1729 * public_exists(handle, skipshare)
1730 *
1731 * check to see if public option is set on any other share than the
1732 * one specified. Need to check zfs sub-groups as well as the top
1733 * level groups.
1734 */
1735 static int
1736 public_exists(sa_handle_t handle, sa_share_t skipshare)
1737 {
1738 sa_group_t group = NULL;
1739
1740 /*
1741 * If we don't have a handle, we can only do syntax check. We
1742 * can't check against other shares so we assume OK and will
1743 * catch the problem only when we actually try to apply it.
1744 */
1745 if (handle == NULL)
1746 return (SA_OK);
1747
1748 if (skipshare != NULL) {
1749 group = sa_get_parent_group(skipshare);
1750 if (group == NULL)
1751 return (SA_NO_SUCH_GROUP);
1752 }
1753
1754 for (group = sa_get_group(handle, NULL); group != NULL;
1755 group = sa_get_next_group(group)) {
1756 /* Walk any ZFS subgroups as well as all standard groups */
1757 if (sa_group_is_zfs(group)) {
1758 sa_group_t subgroup;
1759 for (subgroup = sa_get_sub_group(group);
1760 subgroup != NULL;
1761 subgroup = sa_get_next_group(subgroup)) {
1762 if (check_public(subgroup, skipshare))
1763 return (B_TRUE);
1764 }
1765 } else {
1766 if (check_public(group, skipshare))
1767 return (B_TRUE);
1768 }
1769 }
1770 return (B_FALSE);
1771 }
1772
1773 /*
1774 * sa_enable_share at the protocol level, enable_share must tell the
1775 * implementation that it is to enable the share. This entails
1776 * converting the path and options into the appropriate ioctl
1777 * calls. It is assumed that all error checking of paths, etc. were
1778 * done earlier.
1779 */
1780 static int
1781 nfs_enable_share(sa_share_t share)
1782 {
1783 struct exportdata export;
1784 sa_optionset_t secoptlist;
1785 struct secinfo *sp;
1786 int num_secinfo;
1787 sa_optionset_t opt;
1788 sa_security_t sec;
1789 sa_property_t prop;
1790 char *path;
1791 int err = SA_OK;
1792 int i;
1793 int iszfs;
1794 sa_handle_t handle;
1795
1796 static int check_services = B_TRUE;
1797
1798 /* Don't drop core if the NFS module isn't loaded. */
1799 (void) signal(SIGSYS, SIG_IGN);
1800
1801 /* get the path since it is important in several places */
1802 path = sa_get_share_attr(share, "path");
1803 if (path == NULL)
1804 return (SA_NO_SUCH_PATH);
1805
1806 iszfs = sa_path_is_zfs(path);
1807 /*
1808 * find the optionsets and security sets. There may not be
1809 * any or there could be one or two for each of optionset and
1810 * security may have multiple, one per security type per
1811 * protocol type.
1812 */
1813 opt = sa_get_derived_optionset(share, "nfs", 1);
1814 secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1);
1815 if (secoptlist != NULL)
1816 num_secinfo = MAX(1, count_security(secoptlist));
1817 else
1818 num_secinfo = 1;
1819
1820 /*
1821 * walk through the options and fill in the structure
1822 * appropriately.
1823 */
1824
1825 (void) memset(&export, '\0', sizeof (export));
1826
1827 /*
1828 * do non-security options first since there is only one after
1829 * the derived group is constructed.
1830 */
1831 export.ex_version = EX_CURRENT_VERSION;
1832 export.ex_anon = UID_NOBODY; /* this is our default value */
1833 export.ex_index = NULL;
1834 export.ex_path = path;
1835 export.ex_pathlen = strlen(path) + 1;
1836
1837 if (opt != NULL)
1838 err = fill_export_from_optionset(&export, opt);
1839
1840 /*
1841 * check to see if "public" is set. If it is, then make sure
1842 * no other share has it set. If it is already used, fail.
1843 */
1844
1845 handle = sa_find_group_handle((sa_group_t)share);
1846 if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) {
1847 (void) printf(dgettext(TEXT_DOMAIN,
1848 "NFS: Cannot share more than one file "
1849 "system with 'public' property\n"));
1850 err = SA_NOT_ALLOWED;
1851 goto out;
1852 }
1853
1854 sp = calloc(num_secinfo, sizeof (struct secinfo));
1855 if (sp == NULL) {
1856 err = SA_NO_MEMORY;
1857 (void) printf(dgettext(TEXT_DOMAIN,
1858 "NFS: NFS: no memory for security\n"));
1859 goto out;
1860 }
1861 export.ex_secinfo = sp;
1862 /* get default secinfo */
1863 export.ex_seccnt = num_secinfo;
1864 /*
1865 * since we must have one security option defined, we
1866 * init to the default and then override as we find
1867 * defined security options. This handles the case
1868 * where we have no defined options but we need to set
1869 * up one.
1870 */
1871 sp[0].s_window = DEF_WIN;
1872 sp[0].s_rootnames = NULL;
1873 /* setup a default in case no properties defined */
1874 if (nfs_getseconfig_default(&sp[0].s_secinfo)) {
1875 (void) printf(dgettext(TEXT_DOMAIN,
1876 "NFS: nfs_getseconfig_default: failed to "
1877 "get default security mode\n"));
1878 err = SA_CONFIG_ERR;
1879 }
1880 if (secoptlist != NULL) {
1881 for (i = 0, prop = sa_get_property(secoptlist, NULL);
1882 prop != NULL && i < num_secinfo;
1883 prop = sa_get_next_property(prop), i++) {
1884 char *sectype;
1885 sectype = sa_get_property_attr(prop, "type");
1886 /*
1887 * if sectype is NULL, we probably
1888 * have a memory problem and can't get
1889 * the correct values. Rather than
1890 * exporting with incorrect security,
1891 * don't share it.
1892 */
1893 if (sectype == NULL) {
1894 err = SA_NO_MEMORY;
1895 (void) printf(dgettext(TEXT_DOMAIN,
1896 "NFS: Cannot share %s: "
1897 "no memory\n"), path);
1898 goto out;
1899 }
1900 sec = (sa_security_t)sa_get_derived_security(
1901 share, sectype, "nfs", 1);
1902 sp[i].s_window = DEF_WIN;
1903 sp[i].s_rootcnt = 0;
1904 sp[i].s_rootnames = NULL;
1905 (void) fill_security_from_secopts(&sp[i], sec);
1906 if (sec != NULL)
1907 sa_free_derived_security(sec);
1908 if (sectype != NULL)
1909 sa_free_attr_string(sectype);
1910 }
1911 }
1912
1913 /* now add the share to the internal tables */
1914 printarg(path, &export);
1915 /*
1916 * call the exportfs system call which is implemented
1917 * via the nfssys() call as the EXPORTFS subfunction.
1918 */
1919 if (iszfs) {
1920 struct exportfs_args ea;
1921 share_t sh;
1922
1923 ea.dname = path;
1924 ea.uex = &export;
1925
1926 (void) sa_sharetab_fill_zfs(share, &sh, "nfs");
1927 err = sa_share_zfs(share, NULL, path, &sh, &ea, ZFS_SHARE_NFS);
1928 if (err != SA_OK) {
1929 errno = err;
1930 err = -1;
1931 }
1932 sa_emptyshare(&sh);
1933 } else {
1934 err = exportfs(path, &export);
1935 }
1936
1937 if (err < 0) {
1938 err = SA_SYSTEM_ERR;
1939 switch (errno) {
1940 case EPERM:
1941 err = SA_NO_PERMISSION;
1942 break;
1943 case EEXIST:
1944 err = SA_SHARE_EXISTS;
1945 break;
1946 default:
1947 break;
1948 }
1949 } else {
1950 /* update sharetab with an add/modify */
1951 if (!iszfs) {
1952 (void) sa_update_sharetab(share, "nfs");
1953 }
1954 }
1955
1956 if (err == SA_OK) {
1957 /*
1958 * Enable services, if required.
1959 * This is only done the first time the function is called,
1960 * per instatiation of the library.
1961 */
1962 if (check_services) {
1963 _check_services(service_list_default);
1964 check_services = B_FALSE;
1965 }
1966
1967 /*
1968 * Enable logging.
1969 */
1970 if (export.ex_flags & EX_LOG) {
1971 if (nfslogtab_add(path, export.ex_log_buffer,
1972 export.ex_tag) != 0) {
1973 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1974 "Could not enable logging for %s\n"),
1975 path);
1976 }
1977 _check_services(service_list_logging);
1978 } else {
1979 /*
1980 * don't have logging so remove it from file. It might
1981 * not be thre, but that doesn't matter.
1982 */
1983 (void) nfslogtab_deactivate(path);
1984 }
1985 }
1986
1987 out:
1988 if (path != NULL)
1989 free(path);
1990
1991 cleanup_export(&export);
1992 if (opt != NULL)
1993 sa_free_derived_optionset(opt);
1994 if (secoptlist != NULL)
1995 (void) sa_destroy_optionset(secoptlist);
1996 return (err);
1997 }
1998
1999 /*
2000 * nfs_disable_share(share, path)
2001 *
2002 * Unshare the specified share. Note that "path" is the same path as
2003 * what is in the "share" object. It is passed in to avoid an
2004 * additional lookup. A missing "path" value makes this a no-op
2005 * function.
2006 */
2007 static int
2008 nfs_disable_share(sa_share_t share, char *path)
2009 {
2010 int err;
2011 int ret = SA_OK;
2012 int iszfs;
2013 sa_group_t parent;
2014 sa_handle_t handle;
2015
2016 if (path == NULL)
2017 return (ret);
2018
2019 /*
2020 * If the share is in a ZFS group we need to handle it
2021 * differently. Just being on a ZFS file system isn't
2022 * enough since we may be in a legacy share case.
2023 */
2024 parent = sa_get_parent_group(share);
2025 iszfs = sa_group_is_zfs(parent);
2026 if (iszfs) {
2027 struct exportfs_args ea;
2028 share_t sh = { 0 };
2029 ea.dname = path;
2030 ea.uex = NULL;
2031 sh.sh_path = path;
2032 sh.sh_fstype = "nfs";
2033
2034 err = sa_share_zfs(share, NULL, path, &sh,
2035 &ea, ZFS_UNSHARE_NFS);
2036 if (err != SA_OK) {
2037 errno = err;
2038 err = -1;
2039 }
2040 } else {
2041 err = exportfs(path, NULL);
2042 }
2043 if (err < 0) {
2044 /*
2045 * TBD: only an error in some
2046 * cases - need better analysis
2047 */
2048 switch (errno) {
2049 case EPERM:
2050 case EACCES:
2051 ret = SA_NO_PERMISSION;
2052 break;
2053 case EINVAL:
2054 case ENOENT:
2055 ret = SA_NO_SUCH_PATH;
2056 break;
2057 default:
2058 ret = SA_SYSTEM_ERR;
2059 break;
2060 }
2061 }
2062 if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
2063 handle = sa_find_group_handle((sa_group_t)share);
2064 if (!iszfs)
2065 (void) sa_delete_sharetab(handle, path, "nfs");
2066 /* just in case it was logged */
2067 (void) nfslogtab_deactivate(path);
2068 }
2069 return (ret);
2070 }
2071
2072 static int
2073 check_user(char *value)
2074 {
2075 int ret = SA_OK;
2076
2077 if (!is_a_number(value)) {
2078 struct passwd *pw;
2079 /*
2080 * in this case it would have to be a
2081 * user name
2082 */
2083 pw = getpwnam(value);
2084 if (pw == NULL)
2085 ret = SA_BAD_VALUE;
2086 endpwent();
2087 } else {
2088 uint64_t intval;
2089 intval = strtoull(value, NULL, 0);
2090 if (intval > UID_MAX && intval != -1)
2091 ret = SA_BAD_VALUE;
2092 }
2093
2094 return (ret);
2095 }
2096
2097 static int
2098 check_group(char *value)
2099 {
2100 int ret = SA_OK;
2101
2102 if (!is_a_number(value)) {
2103 struct group *gr;
2104 /*
2105 * in this case it would have to be a
2106 * group name
2107 */
2108 gr = getgrnam(value);
2109 if (gr == NULL)
2110 ret = SA_BAD_VALUE;
2111 endgrent();
2112 } else {
2113 uint64_t intval;
2114 intval = strtoull(value, NULL, 0);
2115 if (intval > UID_MAX && intval != -1)
2116 ret = SA_BAD_VALUE;
2117 }
2118
2119 return (ret);
2120 }
2121
2122 /*
2123 * check_rorwnone(v1, v2, v3)
2124 *
2125 * check ro vs rw vs none values. Over time this may get beefed up.
2126 * for now it just does simple checks. v1 is never NULL but v2 or v3
2127 * could be.
2128 */
2129
2130 static int
2131 check_rorwnone(char *v1, char *v2, char *v3)
2132 {
2133 int ret = SA_OK;
2134 if (v2 != NULL && strcmp(v1, v2) == 0)
2135 ret = SA_VALUE_CONFLICT;
2136 else if (v3 != NULL && strcmp(v1, v3) == 0)
2137 ret = SA_VALUE_CONFLICT;
2138
2139 return (ret);
2140 }
2141
2142 /*
2143 * nfs_validate_property(handle, property, parent)
2144 *
2145 * Check that the property has a legitimate value for its type.
2146 */
2147
2148 static int
2149 nfs_validate_property(sa_handle_t handle, sa_property_t property,
2150 sa_optionset_t parent)
2151 {
2152 int ret = SA_OK;
2153 char *propname;
2154 char *other1;
2155 char *other2;
2156 int optindex;
2157 nfsl_config_t *configlist;
2158 sa_group_t parent_group;
2159 char *value;
2160
2161 propname = sa_get_property_attr(property, "type");
2162
2163 if ((optindex = findopt(propname)) < 0)
2164 ret = SA_NO_SUCH_PROP;
2165
2166 /* need to validate value range here as well */
2167
2168 if (ret == SA_OK) {
2169 parent_group = sa_get_parent_group((sa_share_t)parent);
2170 if (optdefs[optindex].share && parent_group != NULL &&
2171 !sa_is_share(parent_group))
2172 ret = SA_PROP_SHARE_ONLY;
2173 }
2174 if (ret == SA_OK) {
2175 if (optdefs[optindex].index == OPT_PUBLIC) {
2176 /*
2177 * Public is special in that only one instance can
2178 * be in the repository at the same time.
2179 */
2180 if (public_exists(handle, parent_group)) {
2181 sa_free_attr_string(propname);
2182 return (SA_VALUE_CONFLICT);
2183 }
2184 }
2185 value = sa_get_property_attr(property, "value");
2186 if (value != NULL) {
2187 /* first basic type checking */
2188 switch (optdefs[optindex].type) {
2189
2190 case OPT_TYPE_NUMBER:
2191 /* check that the value is all digits */
2192 if (!is_a_number(value))
2193 ret = SA_BAD_VALUE;
2194 break;
2195
2196 case OPT_TYPE_BOOLEAN:
2197 if (strlen(value) == 0 ||
2198 strcasecmp(value, "true") == 0 ||
2199 strcmp(value, "1") == 0 ||
2200 strcasecmp(value, "false") == 0 ||
2201 strcmp(value, "0") == 0) {
2202 ret = SA_OK;
2203 } else {
2204 ret = SA_BAD_VALUE;
2205 }
2206 break;
2207
2208 case OPT_TYPE_USER:
2209 ret = check_user(value);
2210 break;
2211
2212 case OPT_TYPE_FILE:
2213 if (strcmp(value, "..") == 0 ||
2214 strchr(value, '/') != NULL) {
2215 ret = SA_BAD_VALUE;
2216 }
2217 break;
2218
2219 case OPT_TYPE_ACCLIST: {
2220 sa_property_t oprop1;
2221 sa_property_t oprop2;
2222 char *ovalue1 = NULL;
2223 char *ovalue2 = NULL;
2224
2225 if (parent == NULL)
2226 break;
2227 /*
2228 * access list handling. Should eventually
2229 * validate that all the values make sense.
2230 * Also, ro and rw may have cross value
2231 * conflicts.
2232 */
2233 if (strcmp(propname, SHOPT_RO) == 0) {
2234 other1 = SHOPT_RW;
2235 other2 = SHOPT_NONE;
2236 } else if (strcmp(propname, SHOPT_RW) == 0) {
2237 other1 = SHOPT_RO;
2238 other2 = SHOPT_NONE;
2239 } else if (strcmp(propname, SHOPT_NONE) == 0) {
2240 other1 = SHOPT_RO;
2241 other2 = SHOPT_RW;
2242 } else {
2243 other1 = NULL;
2244 other2 = NULL;
2245 }
2246 if (other1 == NULL && other2 == NULL)
2247 break;
2248
2249 /* compare rw(ro) with ro(rw) */
2250
2251 oprop1 = sa_get_property(parent, other1);
2252 oprop2 = sa_get_property(parent, other2);
2253 if (oprop1 == NULL && oprop2 == NULL)
2254 break;
2255 /*
2256 * Only potential confusion if other1
2257 * or other2 exists. Check the values
2258 * and run the check if there is a
2259 * value other than the one we are
2260 * explicitly looking at.
2261 */
2262 ovalue1 = sa_get_property_attr(oprop1, "value");
2263 ovalue2 = sa_get_property_attr(oprop2, "value");
2264 if (ovalue1 != NULL || ovalue2 != NULL)
2265 ret = check_rorwnone(value, ovalue1,
2266 ovalue2);
2267
2268 if (ovalue1 != NULL)
2269 sa_free_attr_string(ovalue1);
2270 if (ovalue2 != NULL)
2271 sa_free_attr_string(ovalue2);
2272 break;
2273 }
2274
2275 case OPT_TYPE_LOGTAG:
2276 if (nfsl_getconfig_list(&configlist) == 0) {
2277 int error;
2278 if (value == NULL ||
2279 strlen(value) == 0) {
2280 if (value != NULL)
2281 sa_free_attr_string(
2282 value);
2283 value = strdup("global");
2284 }
2285 if (value != NULL &&
2286 nfsl_findconfig(configlist, value,
2287 &error) == NULL) {
2288 ret = SA_BAD_VALUE;
2289 }
2290 /* Must always free when done */
2291 nfsl_freeconfig_list(&configlist);
2292 } else {
2293 ret = SA_CONFIG_ERR;
2294 }
2295 break;
2296
2297 case OPT_TYPE_STRING:
2298 /* whatever is here should be ok */
2299 break;
2300
2301 case OPT_TYPE_SECURITY:
2302 /*
2303 * The "sec" property isn't used in the
2304 * non-legacy parts of sharemgr. We need to
2305 * reject it here. For legacy, it is pulled
2306 * out well before we get here.
2307 */
2308 ret = SA_NO_SUCH_PROP;
2309 break;
2310
2311 case OPT_TYPE_MAPPING: {
2312 char *p;
2313 char *n;
2314 char *c;
2315 int (*f)(char *);
2316
2317 sa_security_t security;
2318
2319 /*
2320 * mapping is only supported for sec=sys
2321 */
2322 ret = SA_CONFIG_ERR;
2323 if (parent_group == NULL)
2324 break;
2325
2326 for (security = sa_get_security(parent_group,
2327 NULL, NULL); security != NULL;
2328 security = sa_get_next_security(security)) {
2329 char *type;
2330 char *sectype;
2331
2332 type = sa_get_security_attr(security,
2333 "type");
2334 if (type == NULL)
2335 continue;
2336
2337 if (strcmp(type, "nfs") != 0) {
2338 sa_free_attr_string(type);
2339 continue;
2340 }
2341 sa_free_attr_string(type);
2342
2343 sectype = sa_get_security_attr(security,
2344 "sectype");
2345 if (sectype == NULL)
2346 continue;
2347
2348 if (strcmp(sectype, "sys") != 0) {
2349 sa_free_attr_string(sectype);
2350 ret = SA_CONFIG_ERR;
2351 break;
2352 }
2353 sa_free_attr_string(sectype);
2354 ret = SA_OK;
2355 }
2356
2357 if (ret != SA_OK)
2358 break;
2359
2360 assert(optindex == OPT_UIDMAP ||
2361 optindex == OPT_GIDMAP);
2362 f = optindex == OPT_UIDMAP ? check_user :
2363 check_group;
2364
2365
2366 p = strdup(value);
2367 if (p == NULL)
2368 ret = SA_BAD_VALUE;
2369
2370 for (c = p; ret == SA_OK && c != NULL; c = n) {
2371 char *s;
2372 char *t;
2373
2374 n = strchr(c, '~');
2375 if (n != NULL)
2376 *n++ = '\0';
2377
2378 s = strchr(c, ':');
2379 if (s != NULL) {
2380 *s++ = '\0';
2381 t = strchr(s, ':');
2382 if (t != NULL)
2383 *t = '\0';
2384 }
2385
2386 if (s == NULL || t == NULL)
2387 ret = SA_BAD_VALUE;
2388
2389 if (ret == SA_OK && *c != '\0' &&
2390 strcmp(c, "*") != 0)
2391 ret = f(c);
2392
2393 if (ret == SA_OK && *s != '\0' &&
2394 strcmp(s, "-1") != 0)
2395 ret = f(s);
2396 }
2397
2398 free(p);
2399
2400 break;
2401 }
2402
2403 default:
2404 break;
2405 }
2406
2407 if (value != NULL)
2408 sa_free_attr_string(value);
2409
2410 if (ret == SA_OK && optdefs[optindex].check != NULL) {
2411 /* do the property specific check */
2412 ret = optdefs[optindex].check(handle, property);
2413 }
2414 }
2415 }
2416
2417 if (propname != NULL)
2418 sa_free_attr_string(propname);
2419 return (ret);
2420 }
2421
2422 /*
2423 * Protocol management functions
2424 *
2425 * Properties defined in the default files are defined in
2426 * proto_option_defs for parsing and validation. If "other" and
2427 * "compare" are set, then the value for this property should be
2428 * compared against the property specified in "other" using the
2429 * "compare" check (either <= or >=) in order to ensure that the
2430 * values are in the correct range. E.g. setting server_versmin
2431 * higher than server_versmax should not be allowed.
2432 */
2433
2434 struct proto_option_defs {
2435 char *tag;
2436 char *name; /* display name -- remove protocol identifier */
2437 int index;
2438 int type;
2439 union {
2440 int intval;
2441 char *string;
2442 } defvalue;
2443 uint32_t svcs;
2444 int32_t minval;
2445 int32_t maxval;
2446 char *other;
2447 int compare;
2448 #define OPT_CMP_GE 0
2449 #define OPT_CMP_LE 1
2450 int (*check)(char *);
2451 } proto_options[] = {
2452 #define PROTO_OPT_NFSD_SERVERS 0
2453 {"nfsd_servers",
2454 "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 1024, SVC_NFSD,
2455 1, INT32_MAX},
2456 #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1
2457 {"lockd_listen_backlog",
2458 "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
2459 OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX},
2460 #define PROTO_OPT_LOCKD_SERVERS 2
2461 {"lockd_servers",
2462 "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 256,
2463 SVC_LOCKD, 1, INT32_MAX},
2464 #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3
2465 {"lockd_retransmit_timeout",
2466 "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
2467 OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX},
2468 #define PROTO_OPT_GRACE_PERIOD 4
2469 {"grace_period",
2470 "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90,
2471 SVC_LOCKD, 0, INT32_MAX},
2472 #define PROTO_OPT_NFS_SERVER_VERSMIN 5
2473 {"nfs_server_versmin",
2474 "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER,
2475 (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2476 NFS_VERSMAX, "server_versmax", OPT_CMP_LE},
2477 #define PROTO_OPT_NFS_SERVER_VERSMAX 6
2478 {"nfs_server_versmax",
2479 "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER,
2480 (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2481 NFS_VERSMAX, "server_versmin", OPT_CMP_GE},
2482 #define PROTO_OPT_NFS_CLIENT_VERSMIN 7
2483 {"nfs_client_versmin",
2484 "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER,
2485 (int)NFS_VERSMIN_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2486 "client_versmax", OPT_CMP_LE},
2487 #define PROTO_OPT_NFS_CLIENT_VERSMAX 8
2488 {"nfs_client_versmax",
2489 "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER,
2490 (int)NFS_VERSMAX_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2491 "client_versmin", OPT_CMP_GE},
2492 #define PROTO_OPT_NFS_SERVER_DELEGATION 9
2493 {"nfs_server_delegation",
2494 "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
2495 OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0},
2496 #define PROTO_OPT_NFSMAPID_DOMAIN 10
2497 {"nfsmapid_domain",
2498 "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN,
2499 NULL, SVC_NFSMAPID, 0, 0},
2500 #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11
2501 {"nfsd_max_connections",
2502 "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
2503 OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX},
2504 #define PROTO_OPT_NFSD_PROTOCOL 12
2505 {"nfsd_protocol",
2506 "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0,
2507 SVC_NFSD, 0, 0},
2508 #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13
2509 {"nfsd_listen_backlog",
2510 "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
2511 OPT_TYPE_NUMBER, 0, SVC_NFSD, 0, INT32_MAX},
2512 #define PROTO_OPT_NFSD_DEVICE 14
2513 {"nfsd_device",
2514 "device", PROTO_OPT_NFSD_DEVICE,
2515 OPT_TYPE_STRING, NULL, SVC_NFSD, 0, 0},
2516 #define PROTO_OPT_MOUNTD_LISTEN_BACKLOG 15
2517 {"mountd_listen_backlog",
2518 "mountd_listen_backlog", PROTO_OPT_MOUNTD_LISTEN_BACKLOG,
2519 OPT_TYPE_NUMBER, 64, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX},
2520 #define PROTO_OPT_MOUNTD_MAX_THREADS 16
2521 {"mountd_max_threads",
2522 "mountd_max_threads", PROTO_OPT_MOUNTD_MAX_THREADS,
2523 OPT_TYPE_NUMBER, 16, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX},
2524 #define PROTO_OPT_MOUNTD_PORT 17
2525 {"mountd_port",
2526 "mountd_port", PROTO_OPT_MOUNTD_PORT,
2527 OPT_TYPE_NUMBER, 0, SVC_MOUNTD, 1, UINT16_MAX},
2528 #define PROTO_OPT_STATD_PORT 18
2529 {"statd_port",
2530 "statd_port", PROTO_OPT_STATD_PORT,
2531 OPT_TYPE_NUMBER, 0, SVC_STATD, 1, UINT16_MAX},
2532 {NULL}
2533 };
2534
2535 /*
2536 * the protoset holds the defined options so we don't have to read
2537 * them multiple times
2538 */
2539 static sa_protocol_properties_t protoset;
2540
2541 static int
2542 findprotoopt(char *name, int whichname)
2543 {
2544 int i;
2545 for (i = 0; proto_options[i].tag != NULL; i++) {
2546 if (whichname == 1) {
2547 if (strcasecmp(proto_options[i].name, name) == 0)
2548 return (i);
2549 } else {
2550 if (strcasecmp(proto_options[i].tag, name) == 0)
2551 return (i);
2552 }
2553 }
2554 return (-1);
2555 }
2556
2557 /*
2558 * fixcaselower(str)
2559 *
2560 * convert a string to lower case (inplace).
2561 */
2562
2563 static void
2564 fixcaselower(char *str)
2565 {
2566 while (*str) {
2567 *str = tolower(*str);
2568 str++;
2569 }
2570 }
2571
2572 /*
2573 * skipwhitespace(str)
2574 *
2575 * Skip leading white space. It is assumed that it is called with a
2576 * valid pointer.
2577 */
2578
2579 static char *
2580 skipwhitespace(char *str)
2581 {
2582 while (*str && isspace(*str))
2583 str++;
2584
2585 return (str);
2586 }
2587
2588 /*
2589 * extractprop()
2590 *
2591 * Extract the property and value out of the line and create the
2592 * property in the optionset.
2593 */
2594 static int
2595 extractprop(char *name, char *value)
2596 {
2597 sa_property_t prop;
2598 int index;
2599 int ret = SA_OK;
2600 /*
2601 * Remove any leading
2602 * white space.
2603 */
2604 name = skipwhitespace(name);
2605
2606 index = findprotoopt(name, 1);
2607 if (index >= 0) {
2608 fixcaselower(name);
2609 prop = sa_create_property(proto_options[index].name, value);
2610 if (prop != NULL)
2611 ret = sa_add_protocol_property(protoset, prop);
2612 else
2613 ret = SA_NO_MEMORY;
2614 }
2615 return (ret);
2616 }
2617
2618 scf_type_t
2619 getscftype(int type)
2620 {
2621 scf_type_t ret;
2622
2623 switch (type) {
2624 case OPT_TYPE_NUMBER:
2625 ret = SCF_TYPE_INTEGER;
2626 break;
2627 case OPT_TYPE_BOOLEAN:
2628 ret = SCF_TYPE_BOOLEAN;
2629 break;
2630 default:
2631 ret = SCF_TYPE_ASTRING;
2632 }
2633 return (ret);
2634 }
2635
2636 char *
2637 getsvcname(uint32_t svcs)
2638 {
2639 char *service;
2640 switch (svcs) {
2641 case SVC_LOCKD:
2642 service = LOCKD;
2643 break;
2644 case SVC_STATD:
2645 service = STATD;
2646 break;
2647 case SVC_NFSD:
2648 service = NFSD;
2649 break;
2650 case SVC_CLIENT:
2651 service = NFS_CLIENT_SVC;
2652 break;
2653 case SVC_NFS4CBD:
2654 service = NFS4CBD;
2655 break;
2656 case SVC_NFSMAPID:
2657 service = NFSMAPID;
2658 break;
2659 case SVC_RQUOTAD:
2660 service = RQUOTAD;
2661 break;
2662 case SVC_NFSLOGD:
2663 service = NFSLOGD;
2664 break;
2665 case SVC_REPARSED:
2666 service = REPARSED;
2667 break;
2668 default:
2669 service = NFSD;
2670 }
2671 return (service);
2672 }
2673
2674 /*
2675 * initprotofromsmf()
2676 *
2677 * Read NFS SMF properties and add the defined values to the
2678 * protoset. Note that default values are known from the built in
2679 * table in case SMF doesn't have a definition. Not having
2680 * SMF properties is OK since we have builtin default
2681 * values.
2682 */
2683 static int
2684 initprotofromsmf()
2685 {
2686 char name[PATH_MAX];
2687 char value[PATH_MAX];
2688 int ret = SA_OK, bufsz = 0, i;
2689
2690 protoset = sa_create_protocol_properties("nfs");
2691 if (protoset != NULL) {
2692 for (i = 0; proto_options[i].tag != NULL; i++) {
2693 scf_type_t ptype;
2694 char *svc_name;
2695
2696 bzero(value, PATH_MAX);
2697 (void) strncpy(name, proto_options[i].name, PATH_MAX);
2698 /* Replace NULL with the correct instance */
2699 ptype = getscftype(proto_options[i].type);
2700 svc_name = getsvcname(proto_options[i].svcs);
2701 bufsz = PATH_MAX;
2702 ret = nfs_smf_get_prop(name, value,
2703 (char *)DEFAULT_INSTANCE, ptype,
2704 svc_name, &bufsz);
2705 if (ret == SA_OK) {
2706 ret = extractprop(name, value);
2707 }
2708 }
2709 } else {
2710 ret = SA_NO_MEMORY;
2711 }
2712
2713 return (ret);
2714 }
2715
2716 /*
2717 * add_defaults()
2718 *
2719 * Add the default values for any property not defined
2720 * in NFS SMF repository.
2721 * Values are set according to their defined types.
2722 */
2723
2724 static void
2725 add_defaults()
2726 {
2727 int i;
2728 char number[MAXDIGITS];
2729
2730 for (i = 0; proto_options[i].tag != NULL; i++) {
2731 sa_property_t prop;
2732 prop = sa_get_protocol_property(protoset,
2733 proto_options[i].name);
2734 if (prop == NULL) {
2735 /* add the default value */
2736 switch (proto_options[i].type) {
2737 case OPT_TYPE_NUMBER:
2738 (void) snprintf(number, sizeof (number), "%d",
2739 proto_options[i].defvalue.intval);
2740 prop = sa_create_property(proto_options[i].name,
2741 number);
2742 break;
2743
2744 case OPT_TYPE_BOOLEAN:
2745 prop = sa_create_property(proto_options[i].name,
2746 proto_options[i].defvalue.intval ?
2747 "true" : "false");
2748 break;
2749
2750 case OPT_TYPE_ONOFF:
2751 prop = sa_create_property(proto_options[i].name,
2752 proto_options[i].defvalue.intval ?
2753 "on" : "off");
2754 break;
2755
2756 default:
2757 /* treat as strings of zero length */
2758 prop = sa_create_property(proto_options[i].name,
2759 "");
2760 break;
2761 }
2762 if (prop != NULL)
2763 (void) sa_add_protocol_property(protoset, prop);
2764 }
2765 }
2766 }
2767
2768 static void
2769 free_protoprops()
2770 {
2771 if (protoset != NULL) {
2772 xmlFreeNode(protoset);
2773 protoset = NULL;
2774 }
2775 }
2776
2777 /*
2778 * nfs_init()
2779 *
2780 * Initialize the NFS plugin.
2781 */
2782
2783 static int
2784 nfs_init()
2785 {
2786 int ret = SA_OK;
2787
2788 if (sa_plugin_ops.sa_init != nfs_init) {
2789 (void) printf(dgettext(TEXT_DOMAIN,
2790 "NFS plugin not properly initialized\n"));
2791 return (SA_CONFIG_ERR);
2792 }
2793
2794 ret = initprotofromsmf();
2795 if (ret != SA_OK) {
2796 (void) printf(dgettext(TEXT_DOMAIN,
2797 "NFS plugin problem with SMF repository: %s\n"),
2798 sa_errorstr(ret));
2799 ret = SA_OK;
2800 }
2801 add_defaults();
2802
2803 return (ret);
2804 }
2805
2806 /*
2807 * nfs_fini()
2808 *
2809 * uninitialize the NFS plugin. Want to avoid memory leaks.
2810 */
2811
2812 static void
2813 nfs_fini()
2814 {
2815 free_protoprops();
2816 }
2817
2818 /*
2819 * nfs_get_proto_set()
2820 *
2821 * Return an optionset with all the protocol specific properties in
2822 * it.
2823 */
2824
2825 static sa_protocol_properties_t
2826 nfs_get_proto_set()
2827 {
2828 return (protoset);
2829 }
2830
2831 /*
2832 * service_in_state(service, chkstate)
2833 *
2834 * Want to know if the specified service is in the desired state
2835 * (chkstate) or not. Return true (1) if it is and false (0) if it
2836 * isn't.
2837 */
2838 static int
2839 service_in_state(char *service, const char *chkstate)
2840 {
2841 char *state;
2842 int ret = B_FALSE;
2843
2844 state = smf_get_state(service);
2845 if (state != NULL) {
2846 /* got the state so get the equality for the return value */
2847 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
2848 free(state);
2849 }
2850 return (ret);
2851 }
2852
2853 /*
2854 * restart_service(svcs)
2855 *
2856 * Walk through the bit mask of services that need to be restarted in
2857 * order to use the new property values. Some properties affect
2858 * multiple daemons. Should only restart a service if it is currently
2859 * enabled (online).
2860 */
2861
2862 static void
2863 restart_service(uint32_t svcs)
2864 {
2865 uint32_t mask;
2866 int ret;
2867 char *service;
2868
2869 for (mask = 1; svcs != 0; mask <<= 1) {
2870 switch (svcs & mask) {
2871 case SVC_LOCKD:
2872 service = LOCKD;
2873 break;
2874 case SVC_STATD:
2875 service = STATD;
2876 break;
2877 case SVC_NFSD:
2878 service = NFSD;
2879 break;
2880 case SVC_MOUNTD:
2881 service = MOUNTD;
2882 break;
2883 case SVC_NFS4CBD:
2884 service = NFS4CBD;
2885 break;
2886 case SVC_NFSMAPID:
2887 service = NFSMAPID;
2888 break;
2889 case SVC_RQUOTAD:
2890 service = RQUOTAD;
2891 break;
2892 case SVC_NFSLOGD:
2893 service = NFSLOGD;
2894 break;
2895 case SVC_REPARSED:
2896 service = REPARSED;
2897 break;
2898 case SVC_CLIENT:
2899 service = NFS_CLIENT_SVC;
2900 break;
2901 default:
2902 continue;
2903 }
2904
2905 /*
2906 * Only attempt to restart the service if it is
2907 * currently running. In the future, it may be
2908 * desirable to use smf_refresh_instance if the NFS
2909 * services ever implement the refresh method.
2910 */
2911 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
2912 ret = smf_restart_instance(service);
2913 /*
2914 * There are only a few SMF errors at this point, but
2915 * it is also possible that a bad value may have put
2916 * the service into maintenance if there wasn't an
2917 * SMF level error.
2918 */
2919 if (ret != 0) {
2920 (void) fprintf(stderr,
2921 dgettext(TEXT_DOMAIN,
2922 "%s failed to restart: %s\n"),
2923 service, scf_strerror(scf_error()));
2924 } else {
2925 /*
2926 * Check whether it has gone to "maintenance"
2927 * mode or not. Maintenance implies something
2928 * went wrong.
2929 */
2930 if (service_in_state(service,
2931 SCF_STATE_STRING_MAINT)) {
2932 (void) fprintf(stderr,
2933 dgettext(TEXT_DOMAIN,
2934 "%s failed to restart\n"),
2935 service);
2936 }
2937 }
2938 }
2939 svcs &= ~mask;
2940 }
2941 }
2942
2943 /*
2944 * nfs_minmax_check(name, value)
2945 *
2946 * Verify that the value for the property specified by index is valid
2947 * relative to the opposite value in the case of a min/max variable.
2948 * Currently, server_minvers/server_maxvers and
2949 * client_minvers/client_maxvers are the only ones to check.
2950 */
2951
2952 static int
2953 nfs_minmax_check(int index, int value)
2954 {
2955 int val;
2956 char *pval;
2957 sa_property_t prop;
2958 sa_optionset_t opts;
2959 int ret = B_TRUE;
2960
2961 if (proto_options[index].other != NULL) {
2962 /* have a property to compare against */
2963 opts = nfs_get_proto_set();
2964 prop = sa_get_property(opts, proto_options[index].other);
2965 /*
2966 * If we don't find the property, assume default
2967 * values which will work since the max will be at the
2968 * max and the min at the min.
2969 */
2970 if (prop != NULL) {
2971 pval = sa_get_property_attr(prop, "value");
2972 if (pval != NULL) {
2973 val = strtoul(pval, NULL, 0);
2974 if (proto_options[index].compare ==
2975 OPT_CMP_LE) {
2976 ret = value <= val ? B_TRUE : B_FALSE;
2977 } else if (proto_options[index].compare ==
2978 OPT_CMP_GE) {
2979 ret = value >= val ? B_TRUE : B_FALSE;
2980 }
2981 sa_free_attr_string(pval);
2982 }
2983 }
2984 }
2985 return (ret);
2986 }
2987
2988 /*
2989 * nfs_validate_proto_prop(index, name, value)
2990 *
2991 * Verify that the property specified by name can take the new
2992 * value. This is a sanity check to prevent bad values getting into
2993 * the default files. All values need to be checked against what is
2994 * allowed by their defined type. If a type isn't explicitly defined
2995 * here, it is treated as a string.
2996 *
2997 * Note that OPT_TYPE_NUMBER will additionally check that the value is
2998 * within the range specified and potentially against another property
2999 * value as well as specified in the proto_options members other and
3000 * compare.
3001 */
3002
3003 static int
3004 nfs_validate_proto_prop(int index, char *name, char *value)
3005 {
3006 int ret = SA_OK;
3007 char *cp;
3008 #ifdef lint
3009 name = name;
3010 #endif
3011 switch (proto_options[index].type) {
3012 case OPT_TYPE_NUMBER:
3013 if (!is_a_number(value))
3014 ret = SA_BAD_VALUE;
3015 else {
3016 int val;
3017 val = strtoul(value, NULL, 0);
3018 if (val < proto_options[index].minval ||
3019 val > proto_options[index].maxval)
3020 ret = SA_BAD_VALUE;
3021 /*
3022 * For server_versmin/server_versmax and
3023 * client_versmin/client_versmax, the value of the
3024 * min(max) should be checked to be correct relative
3025 * to the current max(min).
3026 */
3027 if (!nfs_minmax_check(index, val)) {
3028 ret = SA_BAD_VALUE;
3029 }
3030 }
3031 break;
3032
3033 case OPT_TYPE_DOMAIN:
3034 /*
3035 * needs to be a qualified domain so will have at
3036 * least one period and other characters on either
3037 * side of it. A zero length string is also allowed
3038 * and is the way to turn off the override.
3039 */
3040 if (strlen(value) == 0)
3041 break;
3042 cp = strchr(value, '.');
3043 if (cp == NULL || cp == value || strchr(value, '@') != NULL)
3044 ret = SA_BAD_VALUE;
3045 break;
3046
3047 case OPT_TYPE_BOOLEAN:
3048 if (strlen(value) == 0 ||
3049 strcasecmp(value, "true") == 0 ||
3050 strcmp(value, "1") == 0 ||
3051 strcasecmp(value, "false") == 0 ||
3052 strcmp(value, "0") == 0) {
3053 ret = SA_OK;
3054 } else {
3055 ret = SA_BAD_VALUE;
3056 }
3057 break;
3058
3059 case OPT_TYPE_ONOFF:
3060 if (strcasecmp(value, "on") != 0 &&
3061 strcasecmp(value, "off") != 0) {
3062 ret = SA_BAD_VALUE;
3063 }
3064 break;
3065
3066 case OPT_TYPE_PROTOCOL: {
3067 struct netconfig *nconf;
3068 void *nc;
3069 boolean_t pfound = B_FALSE;
3070
3071 if (strcasecmp(value, "all") == 0)
3072 break;
3073
3074 if ((nc = setnetconfig()) == NULL) {
3075 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
3076 "setnetconfig failed: %s\n"), strerror(errno));
3077 } else {
3078 while ((nconf = getnetconfig(nc)) != NULL) {
3079 if (strcmp(nconf->nc_proto, value) == 0) {
3080 pfound = B_TRUE;
3081 break;
3082 }
3083 }
3084 (void) endnetconfig(nc);
3085 }
3086
3087 if (!pfound)
3088 ret = SA_BAD_VALUE;
3089 break;
3090 }
3091
3092 default:
3093 /* treat as a string */
3094 break;
3095 }
3096 return (ret);
3097 }
3098
3099 /*
3100 * nfs_set_proto_prop(prop)
3101 *
3102 * check that prop is valid.
3103 */
3104
3105 static int
3106 nfs_set_proto_prop(sa_property_t prop)
3107 {
3108 int ret = SA_OK;
3109 char *name;
3110 char *value;
3111
3112 name = sa_get_property_attr(prop, "type");
3113 value = sa_get_property_attr(prop, "value");
3114 if (name != NULL && value != NULL) {
3115 scf_type_t sctype;
3116 char *svc_name;
3117 char *instance = NULL;
3118 int index = findprotoopt(name, 1);
3119
3120 ret = nfs_validate_proto_prop(index, name, value);
3121 if (ret == SA_OK) {
3122 sctype = getscftype(proto_options[index].type);
3123 svc_name = getsvcname(proto_options[index].svcs);
3124 if (sctype == SCF_TYPE_BOOLEAN) {
3125 if (value != NULL)
3126 sa_free_attr_string(value);
3127 if (string_to_boolean(value) == 0)
3128 value = strdup("0");
3129 else
3130 value = strdup("1");
3131 }
3132 ret = nfs_smf_set_prop(name, value, instance, sctype,
3133 svc_name);
3134 if (ret == SA_OK) {
3135 restart_service(proto_options[index].svcs);
3136 } else {
3137 (void) printf(dgettext(TEXT_DOMAIN,
3138 "Cannot restart NFS services : %s\n"),
3139 sa_errorstr(ret));
3140 }
3141 }
3142 }
3143 if (name != NULL)
3144 sa_free_attr_string(name);
3145 if (value != NULL)
3146 sa_free_attr_string(value);
3147 return (ret);
3148 }
3149
3150 /*
3151 * nfs_get_status()
3152 *
3153 * What is the current status of the nfsd? We use the SMF state here.
3154 * Caller must free the returned value.
3155 */
3156
3157 static char *
3158 nfs_get_status()
3159 {
3160 return (smf_get_state(NFSD));
3161 }
3162
3163 /*
3164 * nfs_space_alias(alias)
3165 *
3166 * Lookup the space (security) name. If it is default, convert to the
3167 * real name.
3168 */
3169
3170 static char *
3171 nfs_space_alias(char *space)
3172 {
3173 char *name = space;
3174 seconfig_t secconf;
3175
3176 /*
3177 * Only the space named "default" is special. If it is used,
3178 * the default needs to be looked up and the real name used.
3179 * This is normally "sys" but could be changed. We always
3180 * change default to the real name.
3181 */
3182 if (strcmp(space, "default") == 0 &&
3183 nfs_getseconfig_default(&secconf) == 0) {
3184 if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
3185 name = secconf.sc_name;
3186 }
3187 return (strdup(name));
3188 }
3189
3190 /*
3191 * nfs_features()
3192 *
3193 * Return a mask of the features required.
3194 */
3195
3196 static uint64_t
3197 nfs_features()
3198 {
3199 return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER);
3200 }