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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 #pragma ident "%Z%%M% %I% %E% SMI"
40
41 /*
42 * nfs mount
43 */
44
45 #define NFSCLIENT
46 #include <locale.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <memory.h>
50 #include <stdarg.h>
51 #include <unistd.h>
52 #include <ctype.h>
53 #include <stdlib.h>
54 #include <signal.h>
55 #include <sys/param.h>
56 #include <rpc/rpc.h>
57 #include <errno.h>
58 #include <sys/stat.h>
59 #include <netdb.h>
60 #include <sys/mount.h>
61 #include <sys/mntent.h>
62 #include <sys/mnttab.h>
63 #include <nfs/nfs.h>
64 #include <nfs/mount.h>
65 #include <rpcsvc/mount.h>
66 #include <sys/pathconf.h>
67 #include <netdir.h>
68 #include <netconfig.h>
69 #include <sys/sockio.h>
70 #include <net/if.h>
71 #include <syslog.h>
72 #include <fslib.h>
73 #include <deflt.h>
74 #include <sys/wait.h>
75 #include "replica.h"
76 #include <netinet/in.h>
77 #include <nfs/nfs_sec.h>
78 #include <rpcsvc/daemon_utils.h>
79 #include <priv.h>
80 #include <tsol/label.h>
81 #include "nfs_subr.h"
82 #include "webnfs.h"
83 #include <rpcsvc/nfs4_prot.h>
84
85 #ifndef NFS_VERSMAX
86 #define NFS_VERSMAX 4
87 #endif
88 #ifndef NFS_VERSMIN
89 #define NFS_VERSMIN 2
90 #endif
91
92 #define RET_OK 0
93 #define RET_RETRY 32
94 #define RET_ERR 33
95 #define RET_MNTERR 1000
96 #define ERR_PROTO_NONE 0
97 #define ERR_PROTO_INVALID 901
98 #define ERR_PROTO_UNSUPP 902
99 #define ERR_NETPATH 903
100 #define ERR_NOHOST 904
101 #define ERR_RPCERROR 905
102
103 typedef struct err_ret {
104 int error_type;
105 int error_value;
106 } err_ret_t;
107
108 #define SET_ERR_RET(errst, etype, eval) \
109 if (errst) { \
110 (errst)->error_type = etype; \
111 (errst)->error_value = eval; \
112 }
113
114 /* number of transports to try */
115 #define MNT_PREF_LISTLEN 2
116 #define FIRST_TRY 1
117 #define SECOND_TRY 2
118
119 #define BIGRETRY 10000
120
121 /* maximum length of RPC header for NFS messages */
122 #define NFS_RPC_HDR 432
123
124 #define NFS_ARGS_EXTB_secdata(args, secdata) \
125 { (args)->nfs_args_ext = NFS_ARGS_EXTB, \
126 (args)->nfs_ext_u.nfs_extB.secdata = secdata; }
127
128 extern int __clnt_bindresvport();
129 extern char *nfs_get_qop_name();
130 extern AUTH * nfs_create_ah();
131 extern enum snego_stat nfs_sec_nego();
132
133 static void usage(void);
134 static int retry(struct mnttab *, int);
135 static int set_args(int *, struct nfs_args *, char *, struct mnttab *);
136 static int get_fh_via_pub(struct nfs_args *, char *, char *, bool_t, bool_t,
137 int *, struct netconfig **, ushort_t);
138 static int get_fh(struct nfs_args *, char *, char *, int *, bool_t,
139 struct netconfig **, ushort_t);
140 static int make_secure(struct nfs_args *, char *, struct netconfig *,
141 bool_t, rpcvers_t);
142 static int mount_nfs(struct mnttab *, int, err_ret_t *);
143 static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **,
144 bool_t, char *, ushort_t, err_ret_t *, bool_t);
145 static void pr_err(const char *fmt, ...);
146 static void usage(void);
147 static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
148 struct netconfig **, char *, ushort_t, struct t_info *,
149 caddr_t *, bool_t, char *, err_ret_t *);
150
151 static struct netbuf *get_the_addr(char *, rpcprog_t, rpcvers_t,
152 struct netconfig *, ushort_t, struct t_info *, caddr_t *,
153 bool_t, char *, err_ret_t *);
154
155 extern int self_check(char *);
156
157 static void read_default(void);
158
159 static char typename[64];
160
161 static int bg = 0;
162 static int backgrounded = 0;
163 static int posix = 0;
164 static int retries = BIGRETRY;
165 static ushort_t nfs_port = 0;
166 static char *nfs_proto = NULL;
167
168 static int mflg = 0;
169 static int Oflg = 0; /* Overlay mounts */
170 static int qflg = 0; /* quiet - don't print warnings on bad options */
171
172 static char *fstype = MNTTYPE_NFS;
173
174 static seconfig_t nfs_sec;
175 static int sec_opt = 0; /* any security option ? */
176 static bool_t snego_done;
177 static void sigusr1(int);
178
179 /*
180 * list of support services needed
181 */
182 static char *service_list[] = { STATD, LOCKD, NULL };
183 static char *service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
184
185 /*
186 * These two variables control the NFS version number to be used.
187 *
188 * nfsvers defaults to 0 which means to use the highest number that
189 * both the client and the server support. It can also be set to
190 * a particular value, either 2, 3, or 4 to indicate the version
191 * number of choice. If the server (or the client) do not support
192 * the version indicated, then the mount attempt will be failed.
193 *
194 * nfsvers_to_use is the actual version number found to use. It
195 * is determined in get_fh by pinging the various versions of the
196 * NFS service on the server to see which responds positively.
197 *
198 * nfsretry_vers is the version number set when we retry the mount
199 * command with the version decremented from nfsvers_to_use.
200 * nfsretry_vers is set from nfsvers_to_use when we retry the mount
201 * for errors other than RPC errors; it helps un know why we are
202 * retrying. It is an indication that the retry is due to
203 * non-RPC errors.
204 */
205 static rpcvers_t nfsvers = 0;
206 static rpcvers_t nfsvers_to_use = 0;
207 static rpcvers_t nfsretry_vers = 0;
208
209 /*
210 * There are the defaults (range) for the client when determining
211 * which NFS version to use when probing the server (see above).
212 * These will only be used when the vers mount option is not used and
213 * these may be reset if /etc/default/nfs is configured to do so.
214 */
215 static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
216 static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
217
218 /*
219 * This variable controls whether to try the public file handle.
220 */
221 static bool_t public_opt;
222
223 int
224 main(int argc, char *argv[])
225 {
226 struct mnttab mnt;
227 extern char *optarg;
228 extern int optind;
229 char optbuf[MAX_MNTOPT_STR];
230 int ro = 0;
231 int r;
232 int c;
233 char *myname;
234 err_ret_t retry_error;
235
236 (void) setlocale(LC_ALL, "");
237 #if !defined(TEXT_DOMAIN)
238 #define TEXT_DOMAIN "SYS_TEST"
239 #endif
240 (void) textdomain(TEXT_DOMAIN);
241
242 myname = strrchr(argv[0], '/');
243 myname = myname ? myname + 1 : argv[0];
244 (void) snprintf(typename, sizeof (typename), "%s %s",
245 MNTTYPE_NFS, myname);
246 argv[0] = typename;
247
248 mnt.mnt_mntopts = optbuf;
249 (void) strcpy(optbuf, "rw");
250
251 /*
252 * Set options
253 */
254 while ((c = getopt(argc, argv, "ro:mOq")) != EOF) {
255 switch (c) {
256 case 'r':
257 ro++;
258 break;
259 case 'o':
260 if (strlen(optarg) >= MAX_MNTOPT_STR) {
261 pr_err(gettext("option string too long"));
262 return (RET_ERR);
263 }
264 (void) strcpy(mnt.mnt_mntopts, optarg);
265 #ifdef LATER /* XXX */
266 if (strstr(optarg, MNTOPT_REMOUNT)) {
267 /*
268 * If remount is specified, only rw is allowed.
269 */
270 if ((strcmp(optarg, MNTOPT_REMOUNT) != 0) &&
271 (strcmp(optarg, "remount,rw") != 0) &&
272 (strcmp(optarg, "rw,remount") != 0)) {
273 pr_err(gettext("Invalid options\n"));
274 exit(RET_ERR);
275 }
276 }
277 #endif /* LATER */ /* XXX */
278 break;
279 case 'm':
280 mflg++;
281 break;
282 case 'O':
283 Oflg++;
284 break;
285 case 'q':
286 qflg++;
287 break;
288 default:
289 usage();
290 exit(RET_ERR);
291 }
292 }
293 if (argc - optind != 2) {
294 usage();
295 exit(RET_ERR);
296 }
297
298 mnt.mnt_special = argv[optind];
299 mnt.mnt_mountp = argv[optind+1];
300
301 if (!priv_ineffect(PRIV_SYS_MOUNT) ||
302 !priv_ineffect(PRIV_NET_PRIVADDR)) {
303 pr_err(gettext("insufficient privileges\n"));
304 exit(RET_ERR);
305 }
306
307 /*
308 * On a labeled system, allow read-down nfs mounts if privileged
309 * (PRIV_NET_MAC_AWARE) to do so. Otherwise, ignore the error
310 * and "mount equal label only" behavior will result.
311 */
312 if (is_system_labeled())
313 (void) setpflags(NET_MAC_AWARE, 1);
314
315 /*
316 * Read the defaults file to see if the min/max versions have
317 * been set and therefore would override the encoded defaults.
318 * Then check to make sure that if they were set that the
319 * values are reasonable.
320 */
321 read_default();
322 if (vers_min_default > vers_max_default ||
323 vers_min_default < NFS_VERSMIN ||
324 vers_max_default > NFS_VERSMAX) {
325 pr_err("%s %s\n%s %s\n",
326 gettext("Incorrect configuration of client\'s"),
327 NFSADMIN,
328 gettext("NFS_CLIENT_VERSMIN or NFS_CLIENT_VERSMAX"),
329 gettext("is either out of range or overlaps."));
330 }
331
332 SET_ERR_RET(&retry_error, ERR_PROTO_NONE, 0);
333 r = mount_nfs(&mnt, ro, &retry_error);
334 if (r == RET_RETRY && retries) {
335 /*
336 * Check the error code from the last mount attempt if it was
337 * an RPC error, then retry as is. Otherwise we retry with the
338 * nfsretry_vers set. It is set by decrementing nfsvers_to_use.
339 * If we are retrying with nfsretry_vers then we don't print any
340 * retry messages, since we are not retrying due to an RPC
341 * error.
342 */
343 if (retry_error.error_type) {
344 if (retry_error.error_type != ERR_RPCERROR) {
345 nfsretry_vers = nfsvers_to_use =
346 nfsvers_to_use - 1;
347 if (nfsretry_vers < NFS_VERSMIN)
348 return (r);
349 }
350 }
351
352 r = retry(&mnt, ro);
353 }
354 /*
355 * exit(r);
356 */
357 return (r);
358 }
359
360 static void
361 pr_err(const char *fmt, ...)
362 {
363 va_list ap;
364
365 va_start(ap, fmt);
366 if (backgrounded != 0) {
367 (void) vsyslog(LOG_ERR, fmt, ap);
368 } else {
369 (void) fprintf(stderr, "%s: ", typename);
370 (void) vfprintf(stderr, fmt, ap);
371 (void) fflush(stderr);
372 }
373 va_end(ap);
374 }
375
376 static void
377 usage()
378 {
379 (void) fprintf(stderr,
380 gettext("Usage: nfs mount [-r] [-o opts] [server:]path dir\n"));
381 exit(RET_ERR);
382 }
383
384 static int
385 mount_nfs(struct mnttab *mntp, int ro, err_ret_t *retry_error)
386 {
387 struct nfs_args *args = NULL, *argp = NULL, *prev_argp = NULL;
388 struct netconfig *nconf = NULL;
389 struct replica *list = NULL;
390 int mntflags = 0;
391 int i, r, n;
392 int oldvers = 0, vers = 0;
393 int last_error = RET_OK;
394 int replicated = 0;
395 char *p;
396 bool_t url;
397 bool_t use_pubfh;
398 char *special = NULL;
399 char *oldpath = NULL;
400 char *newpath = NULL;
401 char *service;
402 pid_t pi;
403 struct flock f;
404 char *saveopts = NULL;
405 char **sl = NULL;
406
407 mntp->mnt_fstype = MNTTYPE_NFS;
408
409 if (ro) {
410 mntflags |= MS_RDONLY;
411 /* convert "rw"->"ro" */
412 if (p = strstr(mntp->mnt_mntopts, "rw")) {
413 if (*(p+2) == ',' || *(p+2) == '\0')
414 *(p+1) = 'o';
415 }
416 }
417
418 if (Oflg)
419 mntflags |= MS_OVERLAY;
420
421 list = parse_replica(mntp->mnt_special, &n);
422 if (list == NULL) {
423 if (n < 0)
424 pr_err(gettext("nfs file system; use [host:]path\n"));
425 else
426 pr_err(gettext("no memory\n"));
427 return (RET_ERR);
428 }
429
430 replicated = (n > 1);
431
432 /*
433 * There are some free() calls at the bottom of this loop, so be
434 * careful about adding continue statements.
435 */
436 for (i = 0; i < n; i++) {
437 char *path;
438 char *host;
439 ushort_t port;
440
441 argp = (struct nfs_args *)malloc(sizeof (*argp));
442 if (argp == NULL) {
443 pr_err(gettext("no memory\n"));
444 last_error = RET_ERR;
445 goto out;
446 }
447 memset(argp, 0, sizeof (*argp));
448
449 memset(&nfs_sec, 0, sizeof (nfs_sec));
450 sec_opt = 0;
451 use_pubfh = FALSE;
452 url = FALSE;
453 port = 0;
454 snego_done = FALSE;
455
456 /*
457 * Looking for resources of the form
458 * nfs://server_host[:port_number]/path_name
459 */
460 if (strcmp(list[i].host, "nfs") == 0 && strncmp(list[i].path,
461 "//", 2) == 0) {
462 char *sport, *cb;
463 url = TRUE;
464 oldpath = strdup(list[i].path);
465 if (oldpath == NULL) {
466 pr_err(gettext("memory allocation failure\n"));
467 last_error = RET_ERR;
468 goto out;
469 }
470 host = list[i].path+2;
471 path = strchr(host, '/');
472
473 if (path == NULL) {
474 pr_err(gettext(
475 "illegal nfs url syntax\n"));
476 last_error = RET_ERR;
477 goto out;
478 }
479
480 *path = '\0';
481 if (*host == '[') {
482 cb = strchr(host, ']');
483 if (cb == NULL) {
484 pr_err(gettext(
485 "illegal nfs url syntax\n"));
486 last_error = RET_ERR;
487 goto out;
488 } else {
489 *cb = '\0';
490 host++;
491 cb++;
492 if (*cb == ':')
493 port = htons((ushort_t)
494 atoi(cb+1));
495 }
496 } else {
497 sport = strchr(host, ':');
498
499 if (sport != NULL && sport < path) {
500 *sport = '\0';
501 port = htons((ushort_t)atoi(sport+1));
502 }
503 }
504
505 path++;
506 if (*path == '\0')
507 path = ".";
508
509 } else {
510 host = list[i].host;
511 path = list[i].path;
512 }
513
514 if (r = set_args(&mntflags, argp, host, mntp)) {
515 last_error = r;
516 goto out;
517 }
518
519 if (public_opt == TRUE)
520 use_pubfh = TRUE;
521
522 if (port == 0) {
523 port = nfs_port;
524 } else if (nfs_port != 0 && nfs_port != port) {
525 pr_err(gettext(
526 "port (%u) in nfs URL not the same"
527 " as port (%u) in port option\n"),
528 (unsigned int)ntohs(port),
529 (unsigned int)ntohs(nfs_port));
530 last_error = RET_ERR;
531 goto out;
532 }
533
534
535 if (replicated && !(mntflags & MS_RDONLY)) {
536 pr_err(gettext(
537 "replicated mounts must be read-only\n"));
538 last_error = RET_ERR;
539 goto out;
540 }
541
542 if (replicated && (argp->flags & NFSMNT_SOFT)) {
543 pr_err(gettext(
544 "replicated mounts must not be soft\n"));
545 last_error = RET_ERR;
546 goto out;
547 }
548
549 oldvers = vers;
550 nconf = NULL;
551
552 r = RET_ERR;
553
554 /*
555 * If -o public was specified, and/or a URL was specified,
556 * then try the public file handle method.
557 */
558 if ((use_pubfh == TRUE) || (url == TRUE)) {
559 r = get_fh_via_pub(argp, host, path, url, use_pubfh,
560 &vers, &nconf, port);
561
562 if (r != RET_OK) {
563 /*
564 * If -o public was specified, then return the
565 * error now.
566 */
567 if (use_pubfh == TRUE) {
568 last_error = r;
569 goto out;
570 }
571 } else
572 use_pubfh = TRUE;
573 argp->flags |= NFSMNT_PUBLIC;
574 }
575
576 if ((r != RET_OK) || (vers == NFS_V4)) {
577 bool_t loud_on_mnt_err;
578
579 /*
580 * This can happen if -o public is not specified,
581 * special is a URL, and server doesn't support
582 * public file handle.
583 */
584 if (url) {
585 URLparse(path);
586 }
587
588 /*
589 * If the path portion of the URL didn't have
590 * a leading / then there is good possibility
591 * that a mount without a leading slash will
592 * fail.
593 */
594 if (url == TRUE && *path != '/')
595 loud_on_mnt_err = FALSE;
596 else
597 loud_on_mnt_err = TRUE;
598
599 r = get_fh(argp, host, path, &vers,
600 loud_on_mnt_err, &nconf, port);
601
602 if (r != RET_OK) {
603
604 /*
605 * If there was no leading / and the path was
606 * derived from a URL, then try again
607 * with a leading /.
608 */
609 if ((r == RET_MNTERR) &&
610 (loud_on_mnt_err == FALSE)) {
611
612 newpath = malloc(strlen(path)+2);
613
614 if (newpath == NULL) {
615 pr_err(gettext("memory "
616 "allocation failure\n"));
617 last_error = RET_ERR;
618 goto out;
619 }
620
621 strcpy(newpath, "/");
622 strcat(newpath, path);
623
624 r = get_fh(argp, host, newpath, &vers,
625 TRUE, &nconf, port);
626
627 if (r == RET_OK)
628 path = newpath;
629 }
630
631 /*
632 * map exit code back to RET_ERR.
633 */
634 if (r == RET_MNTERR)
635 r = RET_ERR;
636
637 if (r != RET_OK) {
638
639 if (replicated) {
640 if (argp->fh)
641 free(argp->fh);
642 if (argp->pathconf)
643 free(argp->pathconf);
644 free(argp);
645 goto cont;
646 }
647
648 last_error = r;
649 goto out;
650 }
651 }
652 }
653
654 if (oldvers && vers != oldvers) {
655 pr_err(
656 gettext("replicas must have the same version\n"));
657 last_error = RET_ERR;
658 goto out;
659 }
660
661 /*
662 * decide whether to use remote host's
663 * lockd or do local locking
664 */
665 if (!(argp->flags & NFSMNT_LLOCK) && vers == NFS_VERSION &&
666 remote_lock(host, argp->fh)) {
667 (void) fprintf(stderr, gettext(
668 "WARNING: No network locking on %s:%s:"),
669 host, path);
670 (void) fprintf(stderr, gettext(
671 " contact admin to install server change\n"));
672 argp->flags |= NFSMNT_LLOCK;
673 }
674
675 if (self_check(host))
676 argp->flags |= NFSMNT_LOOPBACK;
677
678 if (use_pubfh == FALSE) {
679 /*
680 * Call to get_fh() above may have obtained the
681 * netconfig info and NULL proc'd the server.
682 * This would be the case with v4
683 */
684 if (!(argp->flags & NFSMNT_KNCONF)) {
685 nconf = NULL;
686 if (r = getaddr_nfs(argp, host, &nconf,
687 FALSE, path, port, retry_error,
688 TRUE)) {
689 last_error = r;
690 goto out;
691 }
692 }
693 }
694
695 if (make_secure(argp, host, nconf, use_pubfh, vers) < 0) {
696 last_error = RET_ERR;
697 goto out;
698 }
699
700 if ((url == TRUE) && (use_pubfh == FALSE)) {
701 /*
702 * Convert the special from
703 * nfs://host/path
704 * to
705 * host:path
706 */
707 if (convert_special(&special, host, oldpath, path,
708 mntp->mnt_special) == -1) {
709 (void) fprintf(stderr, gettext(
710 "could not convert URL nfs:%s to %s:%s\n"),
711 oldpath, host, path);
712 last_error = RET_ERR;
713 goto out;
714 } else {
715 mntp->mnt_special = special;
716 }
717 }
718
719 if (prev_argp == NULL)
720 args = argp;
721 else
722 prev_argp->nfs_ext_u.nfs_extB.next = argp;
723 prev_argp = argp;
724
725 cont:
726 if (oldpath != NULL) {
727 free(oldpath);
728 oldpath = NULL;
729 }
730
731 if (newpath != NULL) {
732 free(newpath);
733 newpath = NULL;
734 }
735 }
736
737 argp = NULL;
738
739 if (args == NULL) {
740 last_error = RET_RETRY;
741 goto out;
742 }
743
744 /* Determine which services are appropriate for the NFS version */
745 if (strcmp(fstype, MNTTYPE_NFS4) == 0)
746 sl = service_list_v4;
747 else
748 sl = service_list;
749
750 /*
751 * enable services as needed.
752 */
753 _check_services(sl);
754
755 mntflags |= MS_DATA | MS_OPTIONSTR;
756
757 if (mflg)
758 mntflags |= MS_NOMNTTAB;
759
760 if (!qflg)
761 saveopts = strdup(mntp->mnt_mntopts);
762
763 if (mount(mntp->mnt_special, mntp->mnt_mountp, mntflags, fstype, args,
764 sizeof (*args), mntp->mnt_mntopts, MAX_MNTOPT_STR) < 0) {
765 if (errno != ENOENT) {
766 pr_err(gettext("mount: %s: %s\n"),
767 mntp->mnt_mountp, strerror(errno));
768 } else {
769 struct stat sb;
770 if (stat(mntp->mnt_mountp, &sb) < 0 && errno == ENOENT)
771 pr_err(gettext("mount: %s: %s\n"),
772 mntp->mnt_mountp, strerror(ENOENT));
773 else
774 pr_err("%s: %s\n", mntp->mnt_special,
775 strerror(ENOENT));
776 }
777
778 last_error = RET_ERR;
779 goto out;
780 }
781
782 if (!qflg && saveopts != NULL) {
783 cmp_requested_to_actual_options(saveopts, mntp->mnt_mntopts,
784 mntp->mnt_special, mntp->mnt_mountp);
785 }
786
787 out:
788 if (saveopts != NULL)
789 free(saveopts);
790 if (special != NULL)
791 free(special);
792 if (oldpath != NULL)
793 free(oldpath);
794 if (newpath != NULL)
795 free(newpath);
796
797 free_replica(list, n);
798
799 if (argp != NULL) {
800 /*
801 * If we had a new entry which was not added to the
802 * list yet, then add it now that it can be freed.
803 */
804 if (prev_argp == NULL)
805 args = argp;
806 else
807 prev_argp->nfs_ext_u.nfs_extB.next = argp;
808 }
809 argp = args;
810 while (argp != NULL) {
811 if (argp->fh)
812 free(argp->fh);
813 if (argp->pathconf)
814 free(argp->pathconf);
815 if (argp->knconf)
816 free(argp->knconf);
817 if (argp->addr) {
818 free(argp->addr->buf);
819 free(argp->addr);
820 }
821 nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
822 if (argp->syncaddr) {
823 free(argp->syncaddr->buf);
824 free(argp->syncaddr);
825 }
826 if (argp->netname)
827 free(argp->netname);
828 prev_argp = argp;
829 argp = argp->nfs_ext_u.nfs_extB.next;
830 free(prev_argp);
831 }
832
833 return (last_error);
834 }
835
836 /*
837 * These options are duplicated in uts/common/fs/nfs/nfs_dlinet.c
838 * Changes must be made to both lists.
839 */
840 static char *optlist[] = {
841 #define OPT_RO 0
842 MNTOPT_RO,
843 #define OPT_RW 1
844 MNTOPT_RW,
845 #define OPT_QUOTA 2
846 MNTOPT_QUOTA,
847 #define OPT_NOQUOTA 3
848 MNTOPT_NOQUOTA,
849 #define OPT_SOFT 4
850 MNTOPT_SOFT,
851 #define OPT_HARD 5
852 MNTOPT_HARD,
853 #define OPT_SUID 6
854 MNTOPT_SUID,
855 #define OPT_NOSUID 7
856 MNTOPT_NOSUID,
857 #define OPT_GRPID 8
858 MNTOPT_GRPID,
859 #define OPT_REMOUNT 9
860 MNTOPT_REMOUNT,
861 #define OPT_NOSUB 10
862 MNTOPT_NOSUB,
863 #define OPT_INTR 11
864 MNTOPT_INTR,
865 #define OPT_NOINTR 12
866 MNTOPT_NOINTR,
867 #define OPT_PORT 13
868 MNTOPT_PORT,
869 #define OPT_SECURE 14
870 MNTOPT_SECURE,
871 #define OPT_RSIZE 15
872 MNTOPT_RSIZE,
873 #define OPT_WSIZE 16
874 MNTOPT_WSIZE,
875 #define OPT_TIMEO 17
876 MNTOPT_TIMEO,
877 #define OPT_RETRANS 18
878 MNTOPT_RETRANS,
879 #define OPT_ACTIMEO 19
880 MNTOPT_ACTIMEO,
881 #define OPT_ACREGMIN 20
882 MNTOPT_ACREGMIN,
883 #define OPT_ACREGMAX 21
884 MNTOPT_ACREGMAX,
885 #define OPT_ACDIRMIN 22
886 MNTOPT_ACDIRMIN,
887 #define OPT_ACDIRMAX 23
888 MNTOPT_ACDIRMAX,
889 #define OPT_BG 24
890 MNTOPT_BG,
891 #define OPT_FG 25
892 MNTOPT_FG,
893 #define OPT_RETRY 26
894 MNTOPT_RETRY,
895 #define OPT_NOAC 27
896 MNTOPT_NOAC,
897 #define OPT_NOCTO 28
898 MNTOPT_NOCTO,
899 #define OPT_LLOCK 29
900 MNTOPT_LLOCK,
901 #define OPT_POSIX 30
902 MNTOPT_POSIX,
903 #define OPT_VERS 31
904 MNTOPT_VERS,
905 #define OPT_PROTO 32
906 MNTOPT_PROTO,
907 #define OPT_SEMISOFT 33
908 MNTOPT_SEMISOFT,
909 #define OPT_NOPRINT 34
910 MNTOPT_NOPRINT,
911 #define OPT_SEC 35
912 MNTOPT_SEC,
913 #define OPT_LARGEFILES 36
914 MNTOPT_LARGEFILES,
915 #define OPT_NOLARGEFILES 37
916 MNTOPT_NOLARGEFILES,
917 #define OPT_PUBLIC 38
918 MNTOPT_PUBLIC,
919 #define OPT_DIRECTIO 39
920 MNTOPT_FORCEDIRECTIO,
921 #define OPT_NODIRECTIO 40
922 MNTOPT_NOFORCEDIRECTIO,
923 #define OPT_XATTR 41
924 MNTOPT_XATTR,
925 #define OPT_NOXATTR 42
926 MNTOPT_NOXATTR,
927 #define OPT_DEVICES 43
928 MNTOPT_DEVICES,
929 #define OPT_NODEVICES 44
930 MNTOPT_NODEVICES,
931 #define OPT_SETUID 45
932 MNTOPT_SETUID,
933 #define OPT_NOSETUID 46
934 MNTOPT_NOSETUID,
935 #define OPT_EXEC 47
936 MNTOPT_EXEC,
937 #define OPT_NOEXEC 48
938 MNTOPT_NOEXEC,
939 NULL
940 };
941
942 #define bad(val) (val == NULL || !isdigit(*val))
943
944 static int
945 set_args(int *mntflags, struct nfs_args *args, char *fshost, struct mnttab *mnt)
946 {
947 char *saveopt, *optstr, *opts, *newopts, *val;
948 int largefiles = 0;
949 int invalid = 0;
950 int attrpref = 0;
951 int optlen;
952
953 args->flags = NFSMNT_INT; /* default is "intr" */
954 args->flags |= NFSMNT_HOSTNAME;
955 args->flags |= NFSMNT_NEWARGS; /* using extented nfs_args structure */
956 args->hostname = fshost;
957
958 optstr = opts = strdup(mnt->mnt_mntopts);
959 /* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */
960 optlen = strlen(mnt->mnt_mntopts) + sizeof (MNTOPT_XATTR) + 1;
961 if (optlen > MAX_MNTOPT_STR) {
962 pr_err(gettext("option string too long"));
963 return (RET_ERR);
964 }
965 newopts = malloc(optlen);
966 if (opts == NULL || newopts == NULL) {
967 pr_err(gettext("no memory"));
968 if (opts)
969 free(opts);
970 if (newopts)
971 free(newopts);
972 return (RET_ERR);
973 }
974 newopts[0] = '\0';
975
976 while (*opts) {
977 invalid = 0;
978 saveopt = opts;
979 switch (getsubopt(&opts, optlist, &val)) {
980 case OPT_RO:
981 *mntflags |= MS_RDONLY;
982 break;
983 case OPT_RW:
984 *mntflags &= ~(MS_RDONLY);
985 break;
986 case OPT_QUOTA:
987 case OPT_NOQUOTA:
988 break;
989 case OPT_SOFT:
990 args->flags |= NFSMNT_SOFT;
991 args->flags &= ~(NFSMNT_SEMISOFT);
992 break;
993 case OPT_SEMISOFT:
994 args->flags |= NFSMNT_SOFT;
995 args->flags |= NFSMNT_SEMISOFT;
996 break;
997 case OPT_HARD:
998 args->flags &= ~(NFSMNT_SOFT);
999 args->flags &= ~(NFSMNT_SEMISOFT);
1000 break;
1001 case OPT_SUID:
1002 *mntflags &= ~(MS_NOSUID);
1003 break;
1004 case OPT_NOSUID:
1005 *mntflags |= MS_NOSUID;
1006 break;
1007 case OPT_GRPID:
1008 args->flags |= NFSMNT_GRPID;
1009 break;
1010 case OPT_REMOUNT:
1011 *mntflags |= MS_REMOUNT;
1012 break;
1013 case OPT_INTR:
1014 args->flags |= NFSMNT_INT;
1015 break;
1016 case OPT_NOINTR:
1017 args->flags &= ~(NFSMNT_INT);
1018 break;
1019 case OPT_NOAC:
1020 args->flags |= NFSMNT_NOAC;
1021 break;
1022 case OPT_PORT:
1023 if (bad(val))
1024 goto badopt;
1025 nfs_port = htons((ushort_t)atoi(val));
1026 break;
1027
1028 case OPT_SECURE:
1029 if (nfs_getseconfig_byname("dh", &nfs_sec)) {
1030 pr_err(gettext("can not get \"dh\" from %s\n"),
1031 NFSSEC_CONF);
1032 goto badopt;
1033 }
1034 sec_opt++;
1035 break;
1036
1037 case OPT_NOCTO:
1038 args->flags |= NFSMNT_NOCTO;
1039 break;
1040
1041 case OPT_RSIZE:
1042 args->flags |= NFSMNT_RSIZE;
1043 if (bad(val))
1044 goto badopt;
1045 args->rsize = atoi(val);
1046 break;
1047 case OPT_WSIZE:
1048 args->flags |= NFSMNT_WSIZE;
1049 if (bad(val))
1050 goto badopt;
1051 args->wsize = atoi(val);
1052 break;
1053 case OPT_TIMEO:
1054 args->flags |= NFSMNT_TIMEO;
1055 if (bad(val))
1056 goto badopt;
1057 args->timeo = atoi(val);
1058 break;
1059 case OPT_RETRANS:
1060 args->flags |= NFSMNT_RETRANS;
1061 if (bad(val))
1062 goto badopt;
1063 args->retrans = atoi(val);
1064 break;
1065 case OPT_ACTIMEO:
1066 args->flags |= NFSMNT_ACDIRMAX;
1067 args->flags |= NFSMNT_ACREGMAX;
1068 args->flags |= NFSMNT_ACDIRMIN;
1069 args->flags |= NFSMNT_ACREGMIN;
1070 if (bad(val))
1071 goto badopt;
1072 args->acdirmin = args->acregmin = args->acdirmax
1073 = args->acregmax = atoi(val);
1074 break;
1075 case OPT_ACREGMIN:
1076 args->flags |= NFSMNT_ACREGMIN;
1077 if (bad(val))
1078 goto badopt;
1079 args->acregmin = atoi(val);
1080 break;
1081 case OPT_ACREGMAX:
1082 args->flags |= NFSMNT_ACREGMAX;
1083 if (bad(val))
1084 goto badopt;
1085 args->acregmax = atoi(val);
1086 break;
1087 case OPT_ACDIRMIN:
1088 args->flags |= NFSMNT_ACDIRMIN;
1089 if (bad(val))
1090 goto badopt;
1091 args->acdirmin = atoi(val);
1092 break;
1093 case OPT_ACDIRMAX:
1094 args->flags |= NFSMNT_ACDIRMAX;
1095 if (bad(val))
1096 goto badopt;
1097 args->acdirmax = atoi(val);
1098 break;
1099 case OPT_BG:
1100 bg++;
1101 break;
1102 case OPT_FG:
1103 bg = 0;
1104 break;
1105 case OPT_RETRY:
1106 if (bad(val))
1107 goto badopt;
1108 retries = atoi(val);
1109 break;
1110 case OPT_LLOCK:
1111 args->flags |= NFSMNT_LLOCK;
1112 break;
1113 case OPT_POSIX:
1114 posix = 1;
1115 break;
1116 case OPT_VERS:
1117 if (bad(val))
1118 goto badopt;
1119 nfsvers = (rpcvers_t)atoi(val);
1120 break;
1121 case OPT_PROTO:
1122 if (val == NULL)
1123 goto badopt;
1124
1125 nfs_proto = (char *)malloc(strlen(val)+1);
1126 if (!nfs_proto) {
1127 pr_err(gettext("no memory"));
1128 return (RET_ERR);
1129 }
1130
1131 (void) strncpy(nfs_proto, val, strlen(val)+1);
1132 break;
1133 case OPT_NOPRINT:
1134 args->flags |= NFSMNT_NOPRINT;
1135 break;
1136 case OPT_LARGEFILES:
1137 largefiles = 1;
1138 break;
1139 case OPT_NOLARGEFILES:
1140 pr_err(gettext("NFS can't support \"nolargefiles\"\n"));
1141 free(optstr);
1142 return (RET_ERR);
1143
1144 case OPT_SEC:
1145 if (nfs_getseconfig_byname(val, &nfs_sec)) {
1146 pr_err(gettext("can not get \"%s\" from %s\n"),
1147 val, NFSSEC_CONF);
1148 return (RET_ERR);
1149 }
1150 sec_opt++;
1151 break;
1152
1153 case OPT_PUBLIC:
1154 public_opt = TRUE;
1155 break;
1156
1157 case OPT_DIRECTIO:
1158 args->flags |= NFSMNT_DIRECTIO;
1159 break;
1160
1161 case OPT_NODIRECTIO:
1162 args->flags &= ~(NFSMNT_DIRECTIO);
1163 break;
1164
1165 case OPT_XATTR:
1166 case OPT_NOXATTR:
1167 /*
1168 * VFS options; just need to get them into the
1169 * new mount option string and note we've seen them
1170 */
1171 attrpref = 1;
1172 break;
1173 default:
1174 /*
1175 * Note that this could be a valid OPT_* option so
1176 * we can't use "val" but need to use "saveopt".
1177 */
1178 if (fsisstdopt(saveopt))
1179 break;
1180 invalid = 1;
1181 if (!qflg)
1182 (void) fprintf(stderr, gettext(
1183 "mount: %s on %s - WARNING unknown option"
1184 " \"%s\"\n"), mnt->mnt_special,
1185 mnt->mnt_mountp, saveopt);
1186 break;
1187 }
1188 if (!invalid) {
1189 if (newopts[0])
1190 strcat(newopts, ",");
1191 strcat(newopts, saveopt);
1192 }
1193 }
1194 /* Default is to turn extended attrs on */
1195 if (!attrpref) {
1196 if (newopts[0])
1197 strcat(newopts, ",");
1198 strcat(newopts, MNTOPT_XATTR);
1199 }
1200 strcpy(mnt->mnt_mntopts, newopts);
1201 free(newopts);
1202 free(optstr);
1203
1204 /* ensure that only one secure mode is requested */
1205 if (sec_opt > 1) {
1206 pr_err(gettext("Security options conflict\n"));
1207 return (RET_ERR);
1208 }
1209
1210 /* ensure that the user isn't trying to get large files over V2 */
1211 if (nfsvers == NFS_VERSION && largefiles) {
1212 pr_err(gettext("NFS V2 can't support \"largefiles\"\n"));
1213 return (RET_ERR);
1214 }
1215
1216 if (nfsvers == NFS_V4 &&
1217 nfs_proto != NULL &&
1218 strncasecmp(nfs_proto, NC_UDP, strlen(NC_UDP)) == 0) {
1219 pr_err(gettext("NFS V4 does not support %s\n"), nfs_proto);
1220 return (RET_ERR);
1221 }
1222
1223 return (RET_OK);
1224
1225 badopt:
1226 pr_err(gettext("invalid option: \"%s\"\n"), saveopt);
1227 free(optstr);
1228 return (RET_ERR);
1229 }
1230
1231 static int
1232 make_secure(struct nfs_args *args, char *hostname, struct netconfig *nconf,
1233 bool_t use_pubfh, rpcvers_t vers)
1234 {
1235 sec_data_t *secdata;
1236 int flags;
1237 struct netbuf *syncaddr = NULL;
1238 struct nd_addrlist *retaddrs = NULL;
1239 char netname[MAXNETNAMELEN+1];
1240
1241 /*
1242 * check to see if any secure mode is requested.
1243 * if not, use default security mode.
1244 */
1245 if (!snego_done && !sec_opt) {
1246 /*
1247 * Get default security mode.
1248 * AUTH_UNIX has been the default choice for a long time.
1249 * The better NFS security service becomes, the better chance
1250 * we will set stronger security service as the default NFS
1251 * security mode.
1252 *
1253 */
1254 if (nfs_getseconfig_default(&nfs_sec)) {
1255 pr_err(gettext("error getting default security entry\n"));
1256 return (-1);
1257 }
1258 args->flags |= NFSMNT_SECDEFAULT;
1259 }
1260
1261 /*
1262 * Get the network address for the time service on the server.
1263 * If an RPC based time service is not available then try the
1264 * IP time service.
1265 *
1266 * This is for AUTH_DH processing. We will also pass down syncaddr
1267 * and netname for NFS V4 even if AUTH_DH is not requested right now.
1268 * NFS V4 does security negotiation in the kernel via SECINFO.
1269 * These information might be needed later in the kernel.
1270 *
1271 * Eventurally, we want to move this code to nfs_clnt_secdata()
1272 * when autod_nfs.c and mount.c can share the same get_the_addr()
1273 * routine.
1274 */
1275 flags = 0;
1276 syncaddr = NULL;
1277
1278 if (nfs_sec.sc_rpcnum == AUTH_DH || vers == NFS_V4) {
1279 /*
1280 * If using the public fh or nfsv4, we will not contact the
1281 * remote RPCBINDer, since it is possibly behind a firewall.
1282 */
1283 if (use_pubfh == FALSE && vers != NFS_V4) {
1284 syncaddr = get_the_addr(hostname, RPCBPROG, RPCBVERS,
1285 nconf, 0, NULL, NULL, FALSE, NULL, NULL);
1286 }
1287
1288 if (syncaddr != NULL) {
1289 /* for flags in sec_data */
1290 flags |= AUTH_F_RPCTIMESYNC;
1291 } else {
1292 struct nd_hostserv hs;
1293 int error;
1294
1295 hs.h_host = hostname;
1296 hs.h_serv = "timserver";
1297
1298 error = netdir_getbyname(nconf, &hs, &retaddrs);
1299
1300 if (error != ND_OK && (nfs_sec.sc_rpcnum == AUTH_DH)) {
1301 pr_err(gettext("%s: secure: no time service\n"),
1302 hostname);
1303 return (-1);
1304 }
1305
1306 if (error == ND_OK)
1307 syncaddr = retaddrs->n_addrs;
1308
1309 /*
1310 * For NFS_V4 if AUTH_DH is negotiated later in the
1311 * kernel thru SECINFO, it will need syncaddr
1312 * and netname data.
1313 */
1314 if (vers == NFS_V4 && syncaddr &&
1315 host2netname(netname, hostname, NULL)) {
1316 args->syncaddr = malloc(sizeof (struct netbuf));
1317 args->syncaddr->buf = malloc(syncaddr->len);
1318 (void) memcpy(args->syncaddr->buf, syncaddr->buf,
1319 syncaddr->len);
1320 args->syncaddr->len = syncaddr->len;
1321 args->syncaddr->maxlen = syncaddr->maxlen;
1322 args->netname = strdup(netname);
1323 args->flags |= NFSMNT_SECURE;
1324 }
1325 }
1326 }
1327
1328 /*
1329 * For the initial chosen flavor (any flavor defined in nfssec.conf),
1330 * the data will be stored in the sec_data structure via
1331 * nfs_clnt_secdata() and be passed to the kernel via nfs_args_*
1332 * extended data structure.
1333 */
1334 if (!(secdata = nfs_clnt_secdata(&nfs_sec, hostname, args->knconf,
1335 syncaddr, flags))) {
1336 pr_err(gettext("errors constructing security related data\n"));
1337 if (flags & AUTH_F_RPCTIMESYNC) {
1338 free(syncaddr->buf);
1339 free(syncaddr);
1340 } else if (retaddrs)
1341 netdir_free((void *)retaddrs, ND_ADDRLIST);
1342 return (-1);
1343 }
1344
1345 NFS_ARGS_EXTB_secdata(args, secdata);
1346 if (flags & AUTH_F_RPCTIMESYNC) {
1347 free(syncaddr->buf);
1348 free(syncaddr);
1349 } else if (retaddrs)
1350 netdir_free((void *)retaddrs, ND_ADDRLIST);
1351 return (0);
1352 }
1353
1354 /*
1355 * Get the network address on "hostname" for program "prog"
1356 * with version "vers" by using the nconf configuration data
1357 * passed in.
1358 *
1359 * If the address of a netconfig pointer is null then
1360 * information is not sufficient and no netbuf will be returned.
1361 *
1362 * Finally, ping the null procedure of that service.
1363 *
1364 * A similar routine is also defined in ../../autofs/autod_nfs.c.
1365 * This is a potential routine to move to ../lib for common usage.
1366 */
1367 static struct netbuf *
1368 get_the_addr(char *hostname, ulong_t prog, ulong_t vers,
1369 struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
1370 caddr_t *fhp, bool_t get_pubfh, char *fspath, err_ret_t *error)
1371 {
1372 struct netbuf *nb = NULL;
1373 struct t_bind *tbind = NULL;
1374 CLIENT *cl = NULL;
1375 struct timeval tv;
1376 int fd = -1;
1377 AUTH *ah = NULL;
1378 AUTH *new_ah = NULL;
1379 struct snego_t snego;
1380
1381 if (nconf == NULL)
1382 return (NULL);
1383
1384 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
1385 goto done;
1386
1387 /* LINTED pointer alignment */
1388 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
1389 == NULL)
1390 goto done;
1391
1392 /*
1393 * In the case of public filehandle usage or NFSv4 we want to
1394 * avoid use of the rpcbind/portmap protocol
1395 */
1396 if ((get_pubfh == TRUE) || (vers == NFS_V4)) {
1397 struct nd_hostserv hs;
1398 struct nd_addrlist *retaddrs;
1399 int retval;
1400 hs.h_host = hostname;
1401
1402 /* NFS where vers==4 does not support UDP */
1403 if (vers == NFS_V4 &&
1404 strncasecmp(nconf->nc_proto, NC_UDP,
1405 strlen(NC_UDP)) == 0) {
1406 SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
1407 goto done;
1408 }
1409
1410 if (port == 0)
1411 hs.h_serv = "nfs";
1412 else
1413 hs.h_serv = NULL;
1414
1415 if ((retval = netdir_getbyname(nconf, &hs, &retaddrs))
1416 != ND_OK) {
1417 /*
1418 * Carefully set the error value here. Want to signify
1419 * that the error was an unknown host.
1420 */
1421 if (retval == ND_NOHOST) {
1422 SET_ERR_RET(error, ERR_NOHOST, retval);
1423 }
1424
1425 goto done;
1426 }
1427 memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
1428 retaddrs->n_addrs->len);
1429 tbind->addr.len = retaddrs->n_addrs->len;
1430 netdir_free((void *)retaddrs, ND_ADDRLIST);
1431 (void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL);
1432
1433 } else {
1434 if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
1435 hostname) == FALSE) {
1436 goto done;
1437 }
1438 }
1439
1440 if (port) {
1441 /* LINTED pointer alignment */
1442 if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
1443 ((struct sockaddr_in *)tbind->addr.buf)->sin_port
1444 = port;
1445 else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
1446 ((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
1447 = port;
1448
1449 }
1450
1451 cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
1452 if (cl == NULL) {
1453 /*
1454 * clnt_tli_create() returns either RPC_SYSTEMERROR,
1455 * RPC_UNKNOWNPROTO or RPC_TLIERROR. The RPC_TLIERROR translates
1456 * to "Misc. TLI error". This is not too helpful. Most likely
1457 * the connection to the remote server timed out, so this
1458 * error is at least less perplexing.
1459 * See: usr/src/cmd/rpcinfo/rpcinfo.c
1460 */
1461 if (rpc_createerr.cf_stat == RPC_TLIERROR) {
1462 SET_ERR_RET(error, ERR_RPCERROR, RPC_PMAPFAILURE);
1463 } else {
1464 SET_ERR_RET(error, ERR_RPCERROR, rpc_createerr.cf_stat);
1465 }
1466 goto done;
1467 }
1468
1469 ah = authsys_create_default();
1470 if (ah != NULL)
1471 cl->cl_auth = ah;
1472
1473 tv.tv_sec = 5;
1474 tv.tv_usec = 0;
1475
1476 (void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
1477
1478 if ((get_pubfh == TRUE) && (vers != NFS_V4)) {
1479 enum snego_stat sec;
1480
1481 if (!snego_done) {
1482 /*
1483 * negotiate sec flavor.
1484 */
1485 snego.cnt = 0;
1486 if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
1487 SNEGO_SUCCESS) {
1488 int jj;
1489
1490 /*
1491 * check if server supports the one
1492 * specified in the sec= option.
1493 */
1494 if (sec_opt) {
1495 for (jj = 0; jj < snego.cnt; jj++) {
1496 if (snego.array[jj] == nfs_sec.sc_nfsnum) {
1497 snego_done = TRUE;
1498 break;
1499 }
1500 }
1501 }
1502
1503 /*
1504 * find a common sec flavor
1505 */
1506 if (!snego_done) {
1507 if (sec_opt) {
1508 pr_err(gettext(
1509 "Server does not support the security"
1510 " flavor specified.\n"));
1511 }
1512 for (jj = 0; jj < snego.cnt; jj++) {
1513 if (!nfs_getseconfig_bynumber(snego.array[jj],
1514 &nfs_sec)) {
1515 snego_done = TRUE;
1516 if (sec_opt) {
1517 pr_err(gettext(
1518 "Security flavor %d was negotiated and"
1519 " will be used.\n"),
1520 nfs_sec.sc_nfsnum);
1521 }
1522 break;
1523 }
1524 }
1525 }
1526 if (!snego_done)
1527 return (NULL);
1528
1529 /*
1530 * Now that the flavor has been
1531 * negotiated, get the fh.
1532 *
1533 * First, create an auth handle using the negotiated
1534 * sec flavor in the next lookup to
1535 * fetch the filehandle.
1536 */
1537 new_ah = nfs_create_ah(cl, hostname, &nfs_sec);
1538 if (new_ah == NULL)
1539 goto done;
1540 cl->cl_auth = new_ah;
1541 } else if (sec == SNEGO_ARRAY_TOO_SMALL || sec ==
1542 SNEGO_FAILURE) {
1543 goto done;
1544 }
1545 /*
1546 * Note that if sec == SNEGO_DEF_VALID
1547 * default sec flavor is acceptable.
1548 * Use it to get the filehandle.
1549 */
1550 }
1551
1552 if (vers == NFS_VERSION) {
1553 wnl_diropargs arg;
1554 wnl_diropres *res;
1555
1556 memset((char *)&arg.dir, 0, sizeof (wnl_fh));
1557 arg.name = fspath;
1558 res = wnlproc_lookup_2(&arg, cl);
1559
1560 if (res == NULL || res->status != NFS_OK)
1561 goto done;
1562 *fhp = malloc(sizeof (wnl_fh));
1563
1564 if (*fhp == NULL) {
1565 pr_err(gettext("no memory\n"));
1566 goto done;
1567 }
1568
1569 memcpy((char *)*fhp,
1570 (char *)&res->wnl_diropres_u.wnl_diropres.file,
1571 sizeof (wnl_fh));
1572 } else {
1573 WNL_LOOKUP3args arg;
1574 WNL_LOOKUP3res *res;
1575 nfs_fh3 *fh3p;
1576
1577 memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
1578 arg.what.name = fspath;
1579 res = wnlproc3_lookup_3(&arg, cl);
1580
1581 if (res == NULL || res->status != NFS3_OK)
1582 goto done;
1583
1584 fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
1585
1586 if (fh3p == NULL) {
1587 pr_err(gettext("no memory\n"));
1588 CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
1589 goto done;
1590 }
1591
1592 fh3p->fh3_length =
1593 res->WNL_LOOKUP3res_u.res_ok.object.data.data_len;
1594 memcpy(fh3p->fh3_u.data,
1595 res->WNL_LOOKUP3res_u.res_ok.object.data.data_val,
1596 fh3p->fh3_length);
1597
1598 *fhp = (caddr_t)fh3p;
1599
1600 CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
1601 }
1602 } else {
1603 void *res;
1604 struct rpc_err r_err;
1605
1606 if (vers == NFS_VERSION)
1607 res = wnlproc_null_2(NULL, cl);
1608 else if (vers == NFS_V3)
1609 res = wnlproc3_null_3(NULL, cl);
1610 else
1611 res = wnlproc4_null_4(NULL, cl);
1612
1613 if (res == NULL) {
1614 clnt_geterr(cl, &r_err);
1615 if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
1616 switch (r_err.re_status) {
1617 case RPC_TLIERROR:
1618 case RPC_CANTRECV:
1619 case RPC_CANTSEND:
1620 r_err.re_status = RPC_PROGVERSMISMATCH;
1621 }
1622 }
1623 SET_ERR_RET(error, ERR_RPCERROR, r_err.re_status);
1624 goto done;
1625 }
1626 }
1627
1628 /*
1629 * Make a copy of the netbuf to return
1630 */
1631 nb = (struct netbuf *)malloc(sizeof (*nb));
1632 if (nb == NULL) {
1633 pr_err(gettext("no memory\n"));
1634 goto done;
1635 }
1636 *nb = tbind->addr;
1637 nb->buf = (char *)malloc(nb->maxlen);
1638 if (nb->buf == NULL) {
1639 pr_err(gettext("no memory\n"));
1640 free(nb);
1641 nb = NULL;
1642 goto done;
1643 }
1644 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
1645
1646 done:
1647 if (cl) {
1648 if (ah != NULL) {
1649 if (new_ah != NULL)
1650 AUTH_DESTROY(ah);
1651 AUTH_DESTROY(cl->cl_auth);
1652 cl->cl_auth = NULL;
1653 }
1654 clnt_destroy(cl);
1655 cl = NULL;
1656 }
1657 if (tbind) {
1658 t_free((char *)tbind, T_BIND);
1659 tbind = NULL;
1660 }
1661 if (fd >= 0)
1662 (void) t_close(fd);
1663 return (nb);
1664 }
1665
1666 /*
1667 * Get a network address on "hostname" for program "prog"
1668 * with version "vers". If the port number is specified (non zero)
1669 * then try for a TCP/UDP transport and set the port number of the
1670 * resulting IP address.
1671 *
1672 * If the address of a netconfig pointer was passed and
1673 * if it's not null, use it as the netconfig otherwise
1674 * assign the address of the netconfig that was used to
1675 * establish contact with the service.
1676 *
1677 * A similar routine is also defined in ../../autofs/autod_nfs.c.
1678 * This is a potential routine to move to ../lib for common usage.
1679 *
1680 * "error" refers to a more descriptive term when get_addr fails
1681 * and returns NULL: ERR_PROTO_NONE if no error introduced by
1682 * -o proto option, ERR_NETPATH if error found in NETPATH
1683 * environment variable, ERR_PROTO_INVALID if an unrecognized
1684 * protocol is specified by user, and ERR_PROTO_UNSUPP for a
1685 * recognized but invalid protocol (eg. ticlts, ticots, etc.).
1686 * "error" is ignored if get_addr returns non-NULL result.
1687 *
1688 */
1689 static struct netbuf *
1690 get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp,
1691 char *proto, ushort_t port, struct t_info *tinfo, caddr_t *fhp,
1692 bool_t get_pubfh, char *fspath, err_ret_t *error)
1693 {
1694 struct netbuf *nb = NULL;
1695 struct netconfig *nconf = NULL;
1696 NCONF_HANDLE *nc = NULL;
1697 int nthtry = FIRST_TRY;
1698 err_ret_t errsave_nohost, errsave_rpcerr;
1699
1700 SET_ERR_RET(&errsave_nohost, ERR_PROTO_NONE, 0);
1701 SET_ERR_RET(&errsave_rpcerr, ERR_PROTO_NONE, 0);
1702
1703 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1704
1705 if (nconfp && *nconfp)
1706 return (get_the_addr(hostname, prog, vers, *nconfp, port,
1707 tinfo, fhp, get_pubfh, fspath, error));
1708 /*
1709 * No nconf passed in.
1710 *
1711 * Try to get a nconf from /etc/netconfig filtered by
1712 * the NETPATH environment variable.
1713 * First search for COTS, second for CLTS unless proto
1714 * is specified. When we retry, we reset the
1715 * netconfig list so that we would search the whole list
1716 * all over again.
1717 */
1718
1719 if ((nc = setnetpath()) == NULL) {
1720 /* should only return an error if problems with NETPATH */
1721 /* In which case you are hosed */
1722 SET_ERR_RET(error, ERR_NETPATH, 0);
1723 goto done;
1724 }
1725
1726 /*
1727 * If proto is specified, then only search for the match,
1728 * otherwise try COTS first, if failed, try CLTS.
1729 */
1730 if (proto) {
1731 /* no matching proto name */
1732 SET_ERR_RET(error, ERR_PROTO_INVALID, 0);
1733
1734 while (nconf = getnetpath(nc)) {
1735 if (strcmp(nconf->nc_netid, proto))
1736 continue;
1737
1738 /* may be unsupported */
1739 SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
1740
1741 if ((port != 0) &&
1742 ((strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
1743 strcmp(nconf->nc_protofmly, NC_INET6) == 0) &&
1744 (strcmp(nconf->nc_proto, NC_TCP) != 0 &&
1745 strcmp(nconf->nc_proto, NC_UDP) != 0)))
1746
1747 continue;
1748
1749 else {
1750 nb = get_the_addr(hostname, prog,
1751 vers, nconf, port, tinfo,
1752 fhp, get_pubfh, fspath, error);
1753
1754 if (nb != NULL)
1755 break;
1756
1757 /* nb is NULL - deal with errors */
1758 if (error) {
1759 if (error->error_type == ERR_NOHOST)
1760 SET_ERR_RET(&errsave_nohost,
1761 error->error_type,
1762 error->error_value);
1763 if (error->error_type == ERR_RPCERROR)
1764 SET_ERR_RET(&errsave_rpcerr,
1765 error->error_type,
1766 error->error_value);
1767 }
1768 /*
1769 * continue with same protocol
1770 * selection
1771 */
1772 continue;
1773 }
1774 } /* end of while */
1775
1776 if (nconf == NULL)
1777 goto done;
1778
1779 if ((nb = get_the_addr(hostname, prog, vers, nconf, port,
1780 tinfo, fhp, get_pubfh, fspath, error)) == NULL)
1781 goto done;
1782
1783
1784 } else {
1785 retry:
1786 SET_ERR_RET(error, ERR_NETPATH, 0);
1787 while (nconf = getnetpath(nc)) {
1788 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1789 if (nconf->nc_flag & NC_VISIBLE) {
1790 if (nthtry == FIRST_TRY) {
1791 if ((nconf->nc_semantics ==
1792 NC_TPI_COTS_ORD) ||
1793 (nconf->nc_semantics ==
1794 NC_TPI_COTS)) {
1795
1796 if (port == 0)
1797 break;
1798
1799 if ((strcmp(nconf->nc_protofmly,
1800 NC_INET) == 0 ||
1801 strcmp(nconf->
1802 nc_protofmly,
1803 NC_INET6) == 0) &&
1804 (strcmp(nconf->nc_proto,
1805 NC_TCP) == 0))
1806
1807 break;
1808 }
1809 }
1810 if (nthtry == SECOND_TRY) {
1811 if (nconf->nc_semantics ==
1812 NC_TPI_CLTS) {
1813 if (port == 0)
1814 break;
1815 if ((strcmp(nconf->nc_protofmly,
1816 NC_INET) == 0 ||
1817 strcmp(nconf->
1818 nc_protofmly, NC_INET6)
1819 == 0) &&
1820 (strcmp(
1821 nconf->nc_proto,
1822 NC_UDP) == 0))
1823 break;
1824 }
1825 }
1826 }
1827 } /* while */
1828 if (nconf == NULL) {
1829 if (++nthtry <= MNT_PREF_LISTLEN) {
1830 endnetpath(nc);
1831 if ((nc = setnetpath()) == NULL)
1832 goto done;
1833 goto retry;
1834 } else
1835 goto done;
1836 } else {
1837 if ((nb = get_the_addr(hostname, prog, vers, nconf,
1838 port, tinfo, fhp, get_pubfh, fspath, error))
1839 == NULL) {
1840 /* nb is NULL - deal with errors */
1841 if (error) {
1842 if (error->error_type == ERR_NOHOST)
1843 SET_ERR_RET(&errsave_nohost,
1844 error->error_type,
1845 error->error_value);
1846 if (error->error_type == ERR_RPCERROR)
1847 SET_ERR_RET(&errsave_rpcerr,
1848 error->error_type,
1849 error->error_value);
1850 }
1851 /*
1852 * Continue the same search path in the
1853 * netconfig db until no more matched
1854 * nconf (nconf == NULL).
1855 */
1856 goto retry;
1857 }
1858 }
1859 }
1860 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1861
1862 /*
1863 * Got nconf and nb. Now dup the netconfig structure (nconf)
1864 * and return it thru nconfp.
1865 */
1866 *nconfp = getnetconfigent(nconf->nc_netid);
1867 if (*nconfp == NULL) {
1868 syslog(LOG_ERR, "no memory\n");
1869 free(nb);
1870 nb = NULL;
1871 }
1872 done:
1873 if (nc)
1874 endnetpath(nc);
1875
1876 if (nb == NULL) {
1877 /*
1878 * Check the saved errors. The RPC error has *
1879 * precedence over the no host error.
1880 */
1881 if (errsave_nohost.error_type != ERR_PROTO_NONE)
1882 SET_ERR_RET(error, errsave_nohost.error_type,
1883 errsave_nohost.error_value);
1884
1885 if (errsave_rpcerr.error_type != ERR_PROTO_NONE)
1886 SET_ERR_RET(error, errsave_rpcerr.error_type,
1887 errsave_rpcerr.error_value);
1888 }
1889
1890 return (nb);
1891 }
1892
1893 /*
1894 * Get a file handle usinging multi-component lookup with the public
1895 * file handle.
1896 */
1897 static int
1898 get_fh_via_pub(struct nfs_args *args, char *fshost, char *fspath, bool_t url,
1899 bool_t loud, int *versp, struct netconfig **nconfp, ushort_t port)
1900 {
1901 uint_t vers_min;
1902 uint_t vers_max;
1903 int r;
1904 char *path;
1905
1906 if (nfsvers != 0) {
1907 vers_max = vers_min = nfsvers;
1908 } else {
1909 vers_max = vers_max_default;
1910 vers_min = vers_min_default;
1911 }
1912
1913 if (url == FALSE) {
1914 path = malloc(strlen(fspath) + 2);
1915 if (path == NULL) {
1916 if (loud == TRUE) {
1917 pr_err(gettext("no memory\n"));
1918 }
1919 return (RET_ERR);
1920 }
1921
1922 path[0] = (char)WNL_NATIVEPATH;
1923 (void) strcpy(&path[1], fspath);
1924
1925 } else {
1926 path = fspath;
1927 }
1928
1929 for (nfsvers_to_use = vers_max; nfsvers_to_use >= vers_min;
1930 nfsvers_to_use--) {
1931 /*
1932 * getaddr_nfs will also fill in the fh for us.
1933 */
1934 r = getaddr_nfs(args, fshost, nconfp,
1935 TRUE, path, port, NULL, FALSE);
1936
1937 if (r == RET_OK) {
1938 /*
1939 * Since we are using the public fh, and NLM is
1940 * not firewall friendly, use local locking.
1941 * Not the case for v4.
1942 */
1943 *versp = nfsvers_to_use;
1944 switch (nfsvers_to_use) {
1945 case NFS_V4:
1946 fstype = MNTTYPE_NFS4;
1947 break;
1948 case NFS_V3:
1949 fstype = MNTTYPE_NFS3;
1950 /* fall through to pick up llock option */
1951 default:
1952 args->flags |= NFSMNT_LLOCK;
1953 break;
1954 }
1955 if (fspath != path)
1956 free(path);
1957
1958 return (r);
1959 }
1960 }
1961
1962 if (fspath != path) {
1963 free(path);
1964 }
1965
1966 if (loud == TRUE) {
1967 pr_err(gettext("Could not use public filehandle in request to"
1968 " server %s\n"), fshost);
1969 }
1970
1971 return (r);
1972 }
1973
1974 /*
1975 * get fhandle of remote path from server's mountd
1976 */
1977 static int
1978 get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp,
1979 bool_t loud_on_mnt_err, struct netconfig **nconfp, ushort_t port)
1980 {
1981 static struct fhstatus fhs;
1982 static struct mountres3 mountres3;
1983 static struct pathcnf p;
1984 nfs_fh3 *fh3p;
1985 struct timeval timeout = { 25, 0};
1986 CLIENT *cl;
1987 enum clnt_stat rpc_stat;
1988 rpcvers_t outvers = 0;
1989 rpcvers_t vers_to_try;
1990 rpcvers_t vers_min;
1991 static int printed = 0;
1992 int count, i, *auths;
1993 char *msg;
1994
1995 switch (nfsvers) {
1996 case 2: /* version 2 specified try that only */
1997 vers_to_try = MOUNTVERS_POSIX;
1998 vers_min = MOUNTVERS;
1999 break;
2000 case 3: /* version 3 specified try that only */
2001 vers_to_try = MOUNTVERS3;
2002 vers_min = MOUNTVERS3;
2003 break;
2004 case 4: /* version 4 specified try that only */
2005 /*
2006 * This assignment is in the wrong version sequence.
2007 * The above are MOUNT program and this is NFS
2008 * program. However, it happens to work out since the
2009 * two don't collide for NFSv4.
2010 */
2011 vers_to_try = NFS_V4;
2012 vers_min = NFS_V4;
2013 break;
2014 default: /* no version specified, start with default */
2015 /*
2016 * If the retry version is set, use that. This will
2017 * be set if the last mount attempt returned any other
2018 * besides an RPC error.
2019 */
2020 if (nfsretry_vers)
2021 vers_to_try = nfsretry_vers;
2022 else {
2023 vers_to_try = vers_max_default;
2024 vers_min = vers_min_default;
2025 }
2026
2027 break;
2028 }
2029
2030 /*
2031 * In the case of version 4, just NULL proc the server since
2032 * there is no MOUNT program. If this fails, then decrease
2033 * vers_to_try and continue on with regular MOUNT program
2034 * processing.
2035 */
2036 if (vers_to_try == NFS_V4) {
2037 int savevers = nfsvers_to_use;
2038 err_ret_t error;
2039 int retval;
2040 SET_ERR_RET(&error, ERR_PROTO_NONE, 0);
2041
2042 /* Let's hope for the best */
2043 nfsvers_to_use = NFS_V4;
2044 retval =
2045 getaddr_nfs(args, fshost, nconfp, FALSE,
2046 fspath, port, &error, vers_min == NFS_V4);
2047
2048 if (retval == RET_OK) {
2049 *versp = nfsvers_to_use = NFS_V4;
2050 fstype = MNTTYPE_NFS4;
2051 args->fh = strdup(fspath);
2052 if (args->fh == NULL) {
2053 pr_err(gettext("no memory\n"));
2054 *versp = nfsvers_to_use = savevers;
2055 return (RET_ERR);
2056 }
2057 return (RET_OK);
2058 }
2059 nfsvers_to_use = savevers;
2060
2061 vers_to_try--;
2062 /* If no more versions to try, let the user know. */
2063 if (vers_to_try < vers_min) {
2064 return (retval);
2065 }
2066
2067 /*
2068 * If we are here, there are more versions to try but
2069 * there has been an error of some sort. If it is not
2070 * an RPC error (e.g. host unknown), we just stop and
2071 * return the error since the other versions would see
2072 * the same error as well.
2073 */
2074 if (retval == RET_ERR && error.error_type != ERR_RPCERROR)
2075 return (retval);
2076 }
2077
2078 while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers,
2079 vers_min, vers_to_try, "datagram_v")) == NULL) {
2080 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) {
2081 pr_err(gettext("%s: %s\n"), fshost,
2082 clnt_spcreateerror(""));
2083 return (RET_ERR);
2084 }
2085
2086 /*
2087 * We don't want to downgrade version on lost packets
2088 */
2089 if ((rpc_createerr.cf_stat == RPC_TIMEDOUT) ||
2090 (rpc_createerr.cf_stat == RPC_PMAPFAILURE)) {
2091 pr_err(gettext("%s: %s\n"), fshost,
2092 clnt_spcreateerror(""));
2093 return (RET_RETRY);
2094 }
2095
2096 /*
2097 * back off and try the previous version - patch to the
2098 * problem of version numbers not being contigous and
2099 * clnt_create_vers failing (SunOS4.1 clients & SGI servers)
2100 * The problem happens with most non-Sun servers who
2101 * don't support mountd protocol #2. So, in case the
2102 * call fails, we re-try the call anyway.
2103 */
2104 vers_to_try--;
2105 if (vers_to_try < vers_min) {
2106 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH) {
2107 if (nfsvers == 0) {
2108 pr_err(gettext(
2109 "%s:%s: no applicable versions of NFS supported\n"),
2110 fshost, fspath);
2111 } else {
2112 pr_err(gettext(
2113 "%s:%s: NFS Version %d not supported\n"),
2114 fshost, fspath, nfsvers);
2115 }
2116 return (RET_ERR);
2117 }
2118 if (!printed) {
2119 pr_err(gettext("%s: %s\n"), fshost,
2120 clnt_spcreateerror(""));
2121 printed = 1;
2122 }
2123 return (RET_RETRY);
2124 }
2125 }
2126 if (posix && outvers < MOUNTVERS_POSIX) {
2127 pr_err(gettext("%s: %s: no pathconf info\n"),
2128 fshost, clnt_sperror(cl, ""));
2129 clnt_destroy(cl);
2130 return (RET_ERR);
2131 }
2132
2133 if (__clnt_bindresvport(cl) < 0) {
2134 pr_err(gettext("Couldn't bind to reserved port\n"));
2135 clnt_destroy(cl);
2136 return (RET_RETRY);
2137 }
2138
2139 if ((cl->cl_auth = authsys_create_default()) == NULL) {
2140 pr_err(
2141 gettext("Couldn't create default authentication handle\n"));
2142 clnt_destroy(cl);
2143 return (RET_RETRY);
2144 }
2145
2146 switch (outvers) {
2147 case MOUNTVERS:
2148 case MOUNTVERS_POSIX:
2149 *versp = nfsvers_to_use = NFS_VERSION;
2150 rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
2151 (caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout);
2152 if (rpc_stat != RPC_SUCCESS) {
2153 pr_err(gettext("%s:%s: server not responding %s\n"),
2154 fshost, fspath, clnt_sperror(cl, ""));
2155 clnt_destroy(cl);
2156 return (RET_RETRY);
2157 }
2158
2159 if ((errno = fhs.fhs_status) != MNT_OK) {
2160 if (loud_on_mnt_err) {
2161 if (errno == EACCES) {
2162 pr_err(gettext("%s:%s: access denied\n"),
2163 fshost, fspath);
2164 } else {
2165 pr_err(gettext("%s:%s: %s\n"), fshost, fspath,
2166 strerror(errno));
2167 }
2168 }
2169 clnt_destroy(cl);
2170 return (RET_MNTERR);
2171 }
2172 args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle));
2173 if (args->fh == NULL) {
2174 pr_err(gettext("no memory\n"));
2175 return (RET_ERR);
2176 }
2177 memcpy((caddr_t)args->fh, (caddr_t)&fhs.fhstatus_u.fhs_fhandle,
2178 sizeof (fhs.fhstatus_u.fhs_fhandle));
2179 if (!errno && posix) {
2180 rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
2181 xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf,
2182 (caddr_t)&p, timeout);
2183 if (rpc_stat != RPC_SUCCESS) {
2184 pr_err(gettext(
2185 "%s:%s: server not responding %s\n"),
2186 fshost, fspath, clnt_sperror(cl, ""));
2187 free(args->fh);
2188 clnt_destroy(cl);
2189 return (RET_RETRY);
2190 }
2191 if (_PC_ISSET(_PC_ERROR, p.pc_mask)) {
2192 pr_err(gettext(
2193 "%s:%s: no pathconf info\n"),
2194 fshost, fspath);
2195 free(args->fh);
2196 clnt_destroy(cl);
2197 return (RET_ERR);
2198 }
2199 args->flags |= NFSMNT_POSIX;
2200 args->pathconf = malloc(sizeof (p));
2201 if (args->pathconf == NULL) {
2202 pr_err(gettext("no memory\n"));
2203 free(args->fh);
2204 clnt_destroy(cl);
2205 return (RET_ERR);
2206 }
2207 memcpy((caddr_t)args->pathconf, (caddr_t)&p,
2208 sizeof (p));
2209 }
2210 break;
2211
2212 case MOUNTVERS3:
2213 *versp = nfsvers_to_use = NFS_V3;
2214 rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
2215 (caddr_t)&fspath,
2216 xdr_mountres3, (caddr_t)&mountres3, timeout);
2217 if (rpc_stat != RPC_SUCCESS) {
2218 pr_err(gettext("%s:%s: server not responding %s\n"),
2219 fshost, fspath, clnt_sperror(cl, ""));
2220 clnt_destroy(cl);
2221 return (RET_RETRY);
2222 }
2223
2224 /*
2225 * Assume here that most of the MNT3ERR_*
2226 * codes map into E* errors.
2227 */
2228 if ((errno = mountres3.fhs_status) != MNT_OK) {
2229 if (loud_on_mnt_err) {
2230 switch (errno) {
2231 case MNT3ERR_NAMETOOLONG:
2232 msg = "path name is too long";
2233 break;
2234 case MNT3ERR_NOTSUPP:
2235 msg = "operation not supported";
2236 break;
2237 case MNT3ERR_SERVERFAULT:
2238 msg = "server fault";
2239 break;
2240 default:
2241 msg = strerror(errno);
2242 break;
2243 }
2244 pr_err(gettext("%s:%s: %s\n"), fshost, fspath, msg);
2245 }
2246 clnt_destroy(cl);
2247 return (RET_MNTERR);
2248 }
2249
2250 fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
2251 if (fh3p == NULL) {
2252 pr_err(gettext("no memory\n"));
2253 return (RET_ERR);
2254 }
2255 fh3p->fh3_length =
2256 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
2257 (void) memcpy(fh3p->fh3_u.data,
2258 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val,
2259 fh3p->fh3_length);
2260 args->fh = (caddr_t)fh3p;
2261 fstype = MNTTYPE_NFS3;
2262
2263 /*
2264 * Check the security flavor to be used.
2265 *
2266 * If "secure" or "sec=flavor" is a mount
2267 * option, check if the server supports the "flavor".
2268 * If the server does not support the flavor, return
2269 * error.
2270 *
2271 * If no mount option is given then use the first supported
2272 * security flavor (by the client) in the auth list returned
2273 * from the server.
2274 *
2275 */
2276 auths =
2277 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val;
2278 count =
2279 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len;
2280
2281 if (sec_opt) {
2282 for (i = 0; i < count; i++) {
2283 if (auths[i] == nfs_sec.sc_nfsnum)
2284 break;
2285 }
2286 if (i >= count) {
2287 goto autherr;
2288 }
2289 } else {
2290 if (count > 0) {
2291 for (i = 0; i < count; i++) {
2292 if (!nfs_getseconfig_bynumber(auths[i], &nfs_sec)) {
2293 sec_opt++;
2294 break;
2295 }
2296 }
2297 if (i >= count) {
2298 goto autherr;
2299 }
2300 }
2301 }
2302 break;
2303 default:
2304 pr_err(gettext("%s:%s: Unknown MOUNT version %d\n"),
2305 fshost, fspath, outvers);
2306 clnt_destroy(cl);
2307 return (RET_ERR);
2308 }
2309
2310 clnt_destroy(cl);
2311 return (RET_OK);
2312
2313 autherr:
2314 pr_err(gettext(
2315 "security mode does not match the server exporting %s:%s\n"),
2316 fshost, fspath);
2317 clnt_destroy(cl);
2318 return (RET_ERR);
2319 }
2320
2321 /*
2322 * Fill in the address for the server's NFS service and
2323 * fill in a knetconfig structure for the transport that
2324 * the service is available on.
2325 */
2326 static int
2327 getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp,
2328 bool_t get_pubfh, char *fspath, ushort_t port, err_ret_t *error,
2329 bool_t print_rpcerror)
2330 {
2331 struct stat sb;
2332 struct netconfig *nconf;
2333 struct knetconfig *knconfp;
2334 static int printed = 0;
2335 struct t_info tinfo;
2336 err_ret_t addr_error;
2337
2338 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
2339 SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0);
2340
2341 if (nfs_proto) {
2342 /*
2343 * If a proto is specified and its rdma try this. The kernel
2344 * will later do the reachablity test and fail form there
2345 * if rdma transport is not available to kernel rpc
2346 */
2347 if (strcmp(nfs_proto, "rdma") == 0) {
2348 args->addr = get_addr(fshost, NFS_PROGRAM,
2349 nfsvers_to_use, nconfp, NULL, port, &tinfo,
2350 &args->fh, get_pubfh, fspath, &addr_error);
2351
2352 args->flags |= NFSMNT_DORDMA;
2353 } else {
2354 args->addr = get_addr(fshost, NFS_PROGRAM,
2355 nfsvers_to_use, nconfp, nfs_proto, port, &tinfo,
2356 &args->fh, get_pubfh, fspath, &addr_error);
2357 }
2358 } else {
2359 args->addr = get_addr(fshost, NFS_PROGRAM, nfsvers_to_use,
2360 nconfp, nfs_proto, port, &tinfo, &args->fh, get_pubfh,
2361 fspath, &addr_error);
2362 /*
2363 * If no proto is specified set this flag.
2364 * Kernel mount code will try to use RDMA if its on the
2365 * system, otherwise it will keep on using the protocol
2366 * selected here, through the above get_addr call.
2367 */
2368 if (nfs_proto == NULL)
2369 args->flags |= NFSMNT_TRYRDMA;
2370 }
2371
2372 if (args->addr == NULL) {
2373 /*
2374 * We could have failed because the server had no public
2375 * file handle support. So don't print a message and don't
2376 * retry.
2377 */
2378 if (get_pubfh == TRUE)
2379 return (RET_ERR);
2380
2381 if (!printed) {
2382 switch (addr_error.error_type) {
2383 case 0:
2384 printed = 1;
2385 break;
2386 case ERR_RPCERROR:
2387 if (!print_rpcerror)
2388 /* no error print at this time */
2389 break;
2390 pr_err(gettext("%s NFS service not"
2391 " available %s\n"), fshost,
2392 clnt_sperrno(addr_error.error_value));
2393 printed = 1;
2394 break;
2395 case ERR_NETPATH:
2396 pr_err(gettext("%s: Error in NETPATH.\n"),
2397 fshost);
2398 printed = 1;
2399 break;
2400 case ERR_PROTO_INVALID:
2401 pr_err(gettext("%s: NFS service does not"
2402 " recognize protocol: %s.\n"), fshost,
2403 nfs_proto);
2404 printed = 1;
2405 break;
2406 case ERR_PROTO_UNSUPP:
2407 if (nfsvers || nfsvers_to_use == NFS_VERSMIN) {
2408 /*
2409 * Don't set "printed" here. Since we
2410 * have to keep checking here till we
2411 * exhaust transport errors on all vers.
2412 *
2413 * Print this message if:
2414 * 1. After we have tried all versions
2415 * of NFS and none support the asked
2416 * transport.
2417 *
2418 * 2. If a version is specified and it
2419 * does'nt support the asked
2420 * transport.
2421 *
2422 * Otherwise we decrement the version
2423 * and retry below.
2424 */
2425 pr_err(gettext("%s: NFS service does"
2426 " not support protocol: %s.\n"),
2427 fshost, nfs_proto);
2428 }
2429 break;
2430 case ERR_NOHOST:
2431 pr_err("%s: %s\n", fshost, "Unknown host");
2432 printed = 1;
2433 break;
2434 default:
2435 /* case ERR_PROTO_NONE falls through */
2436 pr_err(gettext("%s: NFS service not responding"
2437 "\n"), fshost);
2438 printed = 1;
2439 break;
2440 }
2441 }
2442 SET_ERR_RET(error,
2443 addr_error.error_type, addr_error.error_value);
2444 if (addr_error.error_type == ERR_PROTO_NONE)
2445 return (RET_RETRY);
2446 else if (addr_error.error_type == ERR_RPCERROR &&
2447 ! IS_UNRECOVERABLE_RPC(addr_error.error_value)) {
2448 return (RET_RETRY);
2449 } else if (nfsvers == 0 && addr_error.error_type ==
2450 ERR_PROTO_UNSUPP && nfsvers_to_use != NFS_VERSMIN) {
2451 /*
2452 * If no version is specified, and the error is due
2453 * to an unsupported transport, then decrement the
2454 * version and retry.
2455 */
2456 return (RET_RETRY);
2457 } else
2458 return (RET_ERR);
2459 }
2460 nconf = *nconfp;
2461
2462 if (stat(nconf->nc_device, &sb) < 0) {
2463 pr_err(gettext("getaddr_nfs: couldn't stat: %s: %s\n"),
2464 nconf->nc_device, strerror(errno));
2465 return (RET_ERR);
2466 }
2467
2468 knconfp = (struct knetconfig *)malloc(sizeof (*knconfp));
2469 if (!knconfp) {
2470 pr_err(gettext("no memory\n"));
2471 return (RET_ERR);
2472 }
2473 knconfp->knc_semantics = nconf->nc_semantics;
2474 knconfp->knc_protofmly = nconf->nc_protofmly;
2475 knconfp->knc_proto = nconf->nc_proto;
2476 knconfp->knc_rdev = sb.st_rdev;
2477
2478 /* make sure we don't overload the transport */
2479 if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) {
2480 args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE);
2481 if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR)
2482 args->rsize = tinfo.tsdu - NFS_RPC_HDR;
2483 if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR)
2484 args->wsize = tinfo.tsdu - NFS_RPC_HDR;
2485 }
2486
2487 args->flags |= NFSMNT_KNCONF;
2488 args->knconf = knconfp;
2489 return (RET_OK);
2490 }
2491
2492 static int
2493 retry(struct mnttab *mntp, int ro)
2494 {
2495 int delay = 5;
2496 int count = retries;
2497 int r;
2498
2499 /*
2500 * Please see comments on nfsretry_vers in the beginning of this file
2501 * and in main() routine.
2502 */
2503
2504 if (bg) {
2505 if (fork() > 0)
2506 return (RET_OK);
2507 pr_err(gettext("backgrounding: %s\n"), mntp->mnt_mountp);
2508 backgrounded = 1;
2509 } else {
2510 if (!nfsretry_vers)
2511 pr_err(gettext("retrying: %s\n"), mntp->mnt_mountp);
2512 }
2513
2514 while (count--) {
2515 if ((r = mount_nfs(mntp, ro, NULL)) == RET_OK) {
2516 pr_err(gettext("%s: mounted OK\n"), mntp->mnt_mountp);
2517 return (RET_OK);
2518 }
2519 if (r != RET_RETRY)
2520 break;
2521
2522 if (count > 0) {
2523 (void) sleep(delay);
2524 delay *= 2;
2525 if (delay > 120)
2526 delay = 120;
2527 }
2528 }
2529
2530 if (!nfsretry_vers)
2531 pr_err(gettext("giving up on: %s\n"), mntp->mnt_mountp);
2532
2533 return (RET_ERR);
2534 }
2535
2536 /*
2537 * Read the /etc/default/nfs configuration file to determine if the
2538 * client has been configured for a new min/max for the NFS version to
2539 * use.
2540 */
2541 static void
2542 read_default(void)
2543 {
2544 char *defval;
2545 int errno;
2546 int tmp;
2547
2548 /* Fail silently if error in opening the default nfs config file */
2549 if ((defopen(NFSADMIN)) == 0) {
2550 if ((defval = defread("NFS_CLIENT_VERSMIN=")) != NULL) {
2551 errno = 0;
2552 tmp = strtol(defval, (char **)NULL, 10);
2553 if (errno == 0) {
2554 vers_min_default = tmp;
2555 }
2556 }
2557 if ((defval = defread("NFS_CLIENT_VERSMAX=")) != NULL) {
2558 errno = 0;
2559 tmp = strtol(defval, (char **)NULL, 10);
2560 if (errno == 0) {
2561 vers_max_default = tmp;
2562 }
2563 }
2564 /* close defaults file */
2565 defopen(NULL);
2566 }
2567 }
2568
2569 static void
2570 sigusr1(int s)
2571 {
2572 }