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(CLIENT *);
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"
1256 " security entry\n"));
1257 return (-1);
1258 }
1259 args->flags |= NFSMNT_SECDEFAULT;
1260 }
1261
1262 /*
1263 * Get the network address for the time service on the server.
1264 * If an RPC based time service is not available then try the
1265 * IP time service.
1266 *
1267 * This is for AUTH_DH processing. We will also pass down syncaddr
1268 * and netname for NFS V4 even if AUTH_DH is not requested right now.
1269 * NFS V4 does security negotiation in the kernel via SECINFO.
1270 * These information might be needed later in the kernel.
1271 *
1272 * Eventurally, we want to move this code to nfs_clnt_secdata()
1273 * when autod_nfs.c and mount.c can share the same get_the_addr()
1274 * routine.
1275 */
1276 flags = 0;
1277 syncaddr = NULL;
1278
1279 if (nfs_sec.sc_rpcnum == AUTH_DH || vers == NFS_V4) {
1280 /*
1281 * If using the public fh or nfsv4, we will not contact the
1282 * remote RPCBINDer, since it is possibly behind a firewall.
1283 */
1284 if (use_pubfh == FALSE && vers != NFS_V4) {
1285 syncaddr = get_the_addr(hostname, RPCBPROG, RPCBVERS,
1286 nconf, 0, NULL, NULL, FALSE, NULL, NULL);
1287 }
1288
1289 if (syncaddr != NULL) {
1290 /* for flags in sec_data */
1291 flags |= AUTH_F_RPCTIMESYNC;
1292 } else {
1293 struct nd_hostserv hs;
1294 int error;
1295
1296 hs.h_host = hostname;
1297 hs.h_serv = "timserver";
1298
1299 error = netdir_getbyname(nconf, &hs, &retaddrs);
1300
1301 if (error != ND_OK && (nfs_sec.sc_rpcnum == AUTH_DH)) {
1302 pr_err(gettext("%s: secure: no time service\n"),
1303 hostname);
1304 return (-1);
1305 }
1306
1307 if (error == ND_OK)
1308 syncaddr = retaddrs->n_addrs;
1309
1310 /*
1311 * For NFS_V4 if AUTH_DH is negotiated later in the
1312 * kernel thru SECINFO, it will need syncaddr
1313 * and netname data.
1314 */
1315 if (vers == NFS_V4 && syncaddr &&
1316 host2netname(netname, hostname, NULL)) {
1317 args->syncaddr = malloc(sizeof (struct netbuf));
1318 args->syncaddr->buf = malloc(syncaddr->len);
1319 (void) memcpy(args->syncaddr->buf,
1320 syncaddr->buf, syncaddr->len);
1321 args->syncaddr->len = syncaddr->len;
1322 args->syncaddr->maxlen = syncaddr->maxlen;
1323 args->netname = strdup(netname);
1324 args->flags |= NFSMNT_SECURE;
1325 }
1326 }
1327 }
1328
1329 /*
1330 * For the initial chosen flavor (any flavor defined in nfssec.conf),
1331 * the data will be stored in the sec_data structure via
1332 * nfs_clnt_secdata() and be passed to the kernel via nfs_args_*
1333 * extended data structure.
1334 */
1335 if (!(secdata = nfs_clnt_secdata(&nfs_sec, hostname, args->knconf,
1336 syncaddr, flags))) {
1337 pr_err(gettext("errors constructing security related data\n"));
1338 if (flags & AUTH_F_RPCTIMESYNC) {
1339 free(syncaddr->buf);
1340 free(syncaddr);
1341 } else if (retaddrs)
1342 netdir_free((void *)retaddrs, ND_ADDRLIST);
1343 return (-1);
1344 }
1345
1346 NFS_ARGS_EXTB_secdata(args, secdata);
1347 if (flags & AUTH_F_RPCTIMESYNC) {
1348 free(syncaddr->buf);
1349 free(syncaddr);
1350 } else if (retaddrs)
1351 netdir_free((void *)retaddrs, ND_ADDRLIST);
1352 return (0);
1353 }
1354
1355 /*
1356 * Get the network address on "hostname" for program "prog"
1357 * with version "vers" by using the nconf configuration data
1358 * passed in.
1359 *
1360 * If the address of a netconfig pointer is null then
1361 * information is not sufficient and no netbuf will be returned.
1362 *
1363 * Finally, ping the null procedure of that service.
1364 *
1365 * A similar routine is also defined in ../../autofs/autod_nfs.c.
1366 * This is a potential routine to move to ../lib for common usage.
1367 */
1368 static struct netbuf *
1369 get_the_addr(char *hostname, ulong_t prog, ulong_t vers,
1370 struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
1371 caddr_t *fhp, bool_t get_pubfh, char *fspath, err_ret_t *error)
1372 {
1373 struct netbuf *nb = NULL;
1374 struct t_bind *tbind = NULL;
1375 CLIENT *cl = NULL;
1376 struct timeval tv;
1377 int fd = -1;
1378 AUTH *ah = NULL;
1379 AUTH *new_ah = NULL;
1380 struct snego_t snego;
1381
1382 if (nconf == NULL)
1383 return (NULL);
1384
1385 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
1386 goto done;
1387
1388 /* LINTED pointer alignment */
1389 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
1390 == NULL)
1391 goto done;
1392
1393 /*
1394 * In the case of public filehandle usage or NFSv4 we want to
1395 * avoid use of the rpcbind/portmap protocol
1396 */
1397 if ((get_pubfh == TRUE) || (vers == NFS_V4)) {
1398 struct nd_hostserv hs;
1399 struct nd_addrlist *retaddrs;
1400 int retval;
1401 hs.h_host = hostname;
1402
1403 /* NFS where vers==4 does not support UDP */
1404 if (vers == NFS_V4 &&
1405 strncasecmp(nconf->nc_proto, NC_UDP,
1406 strlen(NC_UDP)) == 0) {
1407 SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
1408 goto done;
1409 }
1410
1411 if (port == 0)
1412 hs.h_serv = "nfs";
1413 else
1414 hs.h_serv = NULL;
1415
1416 if ((retval = netdir_getbyname(nconf, &hs, &retaddrs))
1417 != ND_OK) {
1418 /*
1419 * Carefully set the error value here. Want to signify
1420 * that the error was an unknown host.
1421 */
1422 if (retval == ND_NOHOST) {
1423 SET_ERR_RET(error, ERR_NOHOST, retval);
1424 }
1425
1426 goto done;
1427 }
1428 memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
1429 retaddrs->n_addrs->len);
1430 tbind->addr.len = retaddrs->n_addrs->len;
1431 netdir_free((void *)retaddrs, ND_ADDRLIST);
1432 (void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL);
1433
1434 } else {
1435 if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
1436 hostname) == FALSE) {
1437 goto done;
1438 }
1439 }
1440
1441 if (port) {
1442 /* LINTED pointer alignment */
1443 if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
1444 ((struct sockaddr_in *)tbind->addr.buf)->sin_port
1445 = port;
1446 else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
1447 ((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
1448 = port;
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,
1487 fspath, &snego)) == 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] ==
1497 nfs_sec.sc_nfsnum) {
1498 snego_done = TRUE;
1499 break;
1500 }
1501 }
1502 }
1503
1504 /*
1505 * find a common sec flavor
1506 */
1507 if (!snego_done) {
1508 if (sec_opt) {
1509 pr_err(gettext(
1510 "Server does not support"
1511 " the security"
1512 " flavor specified.\n"));
1513 }
1514
1515 for (jj = 0; jj < snego.cnt; jj++) {
1516 if (!nfs_getseconfig_bynumber(
1517 snego.array[jj],
1518 &nfs_sec)) {
1519 snego_done = TRUE;
1520 if (sec_opt) {
1521 pr_err(gettext(
1522 "Security flavor %d was negotiated and will be used.\n"),
1523 nfs_sec.
1524 sc_nfsnum);
1525 }
1526
1527 break;
1528 }
1529 }
1530
1531 if (!snego_done)
1532 return (NULL);
1533 }
1534
1535 /*
1536 * Now that the flavor has been
1537 * negotiated, get the fh.
1538 *
1539 * First, create an auth handle using
1540 * the negotiated sec flavor in the
1541 * next lookup to fetch the filehandle.
1542 */
1543 new_ah = nfs_create_ah(cl, hostname, &nfs_sec);
1544 if (new_ah == NULL)
1545 goto done;
1546 cl->cl_auth = new_ah;
1547 } else if (sec == SNEGO_ARRAY_TOO_SMALL || sec ==
1548 SNEGO_FAILURE) {
1549 goto done;
1550 }
1551
1552 /*
1553 * Note that if sec == SNEGO_DEF_VALID
1554 * default sec flavor is acceptable.
1555 * Use it to get the filehandle.
1556 */
1557 }
1558
1559 if (vers == NFS_VERSION) {
1560 wnl_diropargs arg;
1561 wnl_diropres *res;
1562
1563 memset((char *)&arg.dir, 0, sizeof (wnl_fh));
1564 arg.name = fspath;
1565 res = wnlproc_lookup_2(&arg, cl);
1566
1567 if (res == NULL || res->status != NFS_OK)
1568 goto done;
1569 *fhp = malloc(sizeof (wnl_fh));
1570
1571 if (*fhp == NULL) {
1572 pr_err(gettext("no memory\n"));
1573 goto done;
1574 }
1575
1576 memcpy((char *)*fhp,
1577 (char *)&res->wnl_diropres_u.wnl_diropres.file,
1578 sizeof (wnl_fh));
1579 } else {
1580 WNL_LOOKUP3args arg;
1581 WNL_LOOKUP3res *res;
1582 nfs_fh3 *fh3p;
1583
1584 memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
1585 arg.what.name = fspath;
1586 res = wnlproc3_lookup_3(&arg, cl);
1587
1588 if (res == NULL || res->status != NFS3_OK)
1589 goto done;
1590
1591 fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
1592
1593 if (fh3p == NULL) {
1594 pr_err(gettext("no memory\n"));
1595 CLNT_FREERES(cl, xdr_WNL_LOOKUP3res,
1596 (char *)res);
1597 goto done;
1598 }
1599
1600 fh3p->fh3_length =
1601 res->WNL_LOOKUP3res_u.res_ok.object.data.data_len;
1602 memcpy(fh3p->fh3_u.data,
1603 res->WNL_LOOKUP3res_u.res_ok.object.data.data_val,
1604 fh3p->fh3_length);
1605
1606 *fhp = (caddr_t)fh3p;
1607
1608 CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
1609 }
1610 } else {
1611 void *res;
1612 struct rpc_err r_err;
1613
1614 if (vers == NFS_VERSION)
1615 res = wnlproc_null_2(NULL, cl);
1616 else if (vers == NFS_V3)
1617 res = wnlproc3_null_3(NULL, cl);
1618 else
1619 res = wnlproc4_null_4(NULL, cl);
1620
1621 if (res == NULL) {
1622 clnt_geterr(cl, &r_err);
1623 if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
1624 switch (r_err.re_status) {
1625 case RPC_TLIERROR:
1626 case RPC_CANTRECV:
1627 case RPC_CANTSEND:
1628 r_err.re_status = RPC_PROGVERSMISMATCH;
1629 }
1630 }
1631 SET_ERR_RET(error, ERR_RPCERROR, r_err.re_status);
1632 goto done;
1633 }
1634 }
1635
1636 /*
1637 * Make a copy of the netbuf to return
1638 */
1639 nb = (struct netbuf *)malloc(sizeof (*nb));
1640 if (nb == NULL) {
1641 pr_err(gettext("no memory\n"));
1642 goto done;
1643 }
1644 *nb = tbind->addr;
1645 nb->buf = (char *)malloc(nb->maxlen);
1646 if (nb->buf == NULL) {
1647 pr_err(gettext("no memory\n"));
1648 free(nb);
1649 nb = NULL;
1650 goto done;
1651 }
1652 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
1653
1654 done:
1655 if (cl) {
1656 if (ah != NULL) {
1657 if (new_ah != NULL)
1658 AUTH_DESTROY(ah);
1659 AUTH_DESTROY(cl->cl_auth);
1660 cl->cl_auth = NULL;
1661 }
1662 clnt_destroy(cl);
1663 cl = NULL;
1664 }
1665 if (tbind) {
1666 t_free((char *)tbind, T_BIND);
1667 tbind = NULL;
1668 }
1669 if (fd >= 0)
1670 (void) t_close(fd);
1671 return (nb);
1672 }
1673
1674 /*
1675 * Get a network address on "hostname" for program "prog"
1676 * with version "vers". If the port number is specified (non zero)
1677 * then try for a TCP/UDP transport and set the port number of the
1678 * resulting IP address.
1679 *
1680 * If the address of a netconfig pointer was passed and
1681 * if it's not null, use it as the netconfig otherwise
1682 * assign the address of the netconfig that was used to
1683 * establish contact with the service.
1684 *
1685 * A similar routine is also defined in ../../autofs/autod_nfs.c.
1686 * This is a potential routine to move to ../lib for common usage.
1687 *
1688 * "error" refers to a more descriptive term when get_addr fails
1689 * and returns NULL: ERR_PROTO_NONE if no error introduced by
1690 * -o proto option, ERR_NETPATH if error found in NETPATH
1691 * environment variable, ERR_PROTO_INVALID if an unrecognized
1692 * protocol is specified by user, and ERR_PROTO_UNSUPP for a
1693 * recognized but invalid protocol (eg. ticlts, ticots, etc.).
1694 * "error" is ignored if get_addr returns non-NULL result.
1695 *
1696 */
1697 static struct netbuf *
1698 get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp,
1699 char *proto, ushort_t port, struct t_info *tinfo, caddr_t *fhp,
1700 bool_t get_pubfh, char *fspath, err_ret_t *error)
1701 {
1702 struct netbuf *nb = NULL;
1703 struct netconfig *nconf = NULL;
1704 NCONF_HANDLE *nc = NULL;
1705 int nthtry = FIRST_TRY;
1706 err_ret_t errsave_nohost, errsave_rpcerr;
1707
1708 SET_ERR_RET(&errsave_nohost, ERR_PROTO_NONE, 0);
1709 SET_ERR_RET(&errsave_rpcerr, ERR_PROTO_NONE, 0);
1710
1711 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1712
1713 if (nconfp && *nconfp)
1714 return (get_the_addr(hostname, prog, vers, *nconfp, port,
1715 tinfo, fhp, get_pubfh, fspath, error));
1716 /*
1717 * No nconf passed in.
1718 *
1719 * Try to get a nconf from /etc/netconfig filtered by
1720 * the NETPATH environment variable.
1721 * First search for COTS, second for CLTS unless proto
1722 * is specified. When we retry, we reset the
1723 * netconfig list so that we would search the whole list
1724 * all over again.
1725 */
1726
1727 if ((nc = setnetpath()) == NULL) {
1728 /* should only return an error if problems with NETPATH */
1729 /* In which case you are hosed */
1730 SET_ERR_RET(error, ERR_NETPATH, 0);
1731 goto done;
1732 }
1733
1734 /*
1735 * If proto is specified, then only search for the match,
1736 * otherwise try COTS first, if failed, try CLTS.
1737 */
1738 if (proto) {
1739 /* no matching proto name */
1740 SET_ERR_RET(error, ERR_PROTO_INVALID, 0);
1741
1742 while (nconf = getnetpath(nc)) {
1743 if (strcmp(nconf->nc_netid, proto))
1744 continue;
1745
1746 /* may be unsupported */
1747 SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
1748
1749 if ((port != 0) &&
1750 ((strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
1751 strcmp(nconf->nc_protofmly, NC_INET6) == 0) &&
1752 (strcmp(nconf->nc_proto, NC_TCP) != 0 &&
1753 strcmp(nconf->nc_proto, NC_UDP) != 0))) {
1754 continue;
1755 } else {
1756 nb = get_the_addr(hostname, prog,
1757 vers, nconf, port, tinfo,
1758 fhp, get_pubfh, fspath, error);
1759
1760 if (nb != NULL)
1761 break;
1762
1763 /* nb is NULL - deal with errors */
1764 if (error) {
1765 if (error->error_type == ERR_NOHOST)
1766 SET_ERR_RET(&errsave_nohost,
1767 error->error_type,
1768 error->error_value);
1769 if (error->error_type == ERR_RPCERROR)
1770 SET_ERR_RET(&errsave_rpcerr,
1771 error->error_type,
1772 error->error_value);
1773 }
1774 /*
1775 * continue with same protocol
1776 * selection
1777 */
1778 continue;
1779 }
1780 } /* end of while */
1781
1782 if (nconf == NULL)
1783 goto done;
1784
1785 if ((nb = get_the_addr(hostname, prog, vers, nconf, port,
1786 tinfo, fhp, get_pubfh, fspath, error)) == NULL)
1787 goto done;
1788 } else {
1789 retry:
1790 SET_ERR_RET(error, ERR_NETPATH, 0);
1791 while (nconf = getnetpath(nc)) {
1792 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1793 if (nconf->nc_flag & NC_VISIBLE) {
1794 if (nthtry == FIRST_TRY) {
1795 if ((nconf->nc_semantics ==
1796 NC_TPI_COTS_ORD) ||
1797 (nconf->nc_semantics ==
1798 NC_TPI_COTS)) {
1799
1800 if (port == 0)
1801 break;
1802
1803 if ((strcmp(nconf->nc_protofmly,
1804 NC_INET) == 0 ||
1805 strcmp(nconf->nc_protofmly,
1806 NC_INET6) == 0) &&
1807 (strcmp(nconf->nc_proto,
1808 NC_TCP) == 0))
1809 break;
1810 }
1811 }
1812 if (nthtry == SECOND_TRY) {
1813 if (nconf->nc_semantics ==
1814 NC_TPI_CLTS) {
1815 if (port == 0)
1816 break;
1817 if ((strcmp(nconf->nc_protofmly,
1818 NC_INET) == 0 ||
1819 strcmp(nconf->nc_protofmly,
1820 NC_INET6) == 0) &&
1821 (strcmp(nconf->nc_proto,
1822 NC_UDP) == 0))
1823 break;
1824 }
1825 }
1826 }
1827 } /* while */
1828
1829 if (nconf == NULL) {
1830 if (++nthtry <= MNT_PREF_LISTLEN) {
1831 endnetpath(nc);
1832 if ((nc = setnetpath()) == NULL)
1833 goto done;
1834 goto retry;
1835 } else
1836 goto done;
1837 } else {
1838 if ((nb = get_the_addr(hostname, prog, vers, nconf,
1839 port, tinfo, fhp, get_pubfh, fspath, error))
1840 == NULL) {
1841
1842 /* nb is NULL - deal with errors */
1843 if (error) {
1844 if (error->error_type == ERR_NOHOST)
1845 SET_ERR_RET(&errsave_nohost,
1846 error->error_type,
1847 error->error_value);
1848 if (error->error_type == ERR_RPCERROR)
1849 SET_ERR_RET(&errsave_rpcerr,
1850 error->error_type,
1851 error->error_value);
1852 }
1853 /*
1854 * Continue the same search path in the
1855 * netconfig db until no more matched
1856 * nconf (nconf == NULL).
1857 */
1858 goto retry;
1859 }
1860 }
1861 }
1862 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1863
1864 /*
1865 * Got nconf and nb. Now dup the netconfig structure (nconf)
1866 * and return it thru nconfp.
1867 */
1868 *nconfp = getnetconfigent(nconf->nc_netid);
1869 if (*nconfp == NULL) {
1870 syslog(LOG_ERR, "no memory\n");
1871 free(nb);
1872 nb = NULL;
1873 }
1874 done:
1875 if (nc)
1876 endnetpath(nc);
1877
1878 if (nb == NULL) {
1879 /*
1880 * Check the saved errors. The RPC error has *
1881 * precedence over the no host error.
1882 */
1883 if (errsave_nohost.error_type != ERR_PROTO_NONE)
1884 SET_ERR_RET(error, errsave_nohost.error_type,
1885 errsave_nohost.error_value);
1886
1887 if (errsave_rpcerr.error_type != ERR_PROTO_NONE)
1888 SET_ERR_RET(error, errsave_rpcerr.error_type,
1889 errsave_rpcerr.error_value);
1890 }
1891
1892 return (nb);
1893 }
1894
1895 /*
1896 * Get a file handle usinging multi-component lookup with the public
1897 * file handle.
1898 */
1899 static int
1900 get_fh_via_pub(struct nfs_args *args, char *fshost, char *fspath, bool_t url,
1901 bool_t loud, int *versp, struct netconfig **nconfp, ushort_t port)
1902 {
1903 uint_t vers_min;
1904 uint_t vers_max;
1905 int r;
1906 char *path;
1907
1908 if (nfsvers != 0) {
1909 vers_max = vers_min = nfsvers;
1910 } else {
1911 vers_max = vers_max_default;
1912 vers_min = vers_min_default;
1913 }
1914
1915 if (url == FALSE) {
1916 path = malloc(strlen(fspath) + 2);
1917 if (path == NULL) {
1918 if (loud == TRUE) {
1919 pr_err(gettext("no memory\n"));
1920 }
1921 return (RET_ERR);
1922 }
1923
1924 path[0] = (char)WNL_NATIVEPATH;
1925 (void) strcpy(&path[1], fspath);
1926
1927 } else {
1928 path = fspath;
1929 }
1930
1931 for (nfsvers_to_use = vers_max; nfsvers_to_use >= vers_min;
1932 nfsvers_to_use--) {
1933 /*
1934 * getaddr_nfs will also fill in the fh for us.
1935 */
1936 r = getaddr_nfs(args, fshost, nconfp,
1937 TRUE, path, port, NULL, FALSE);
1938
1939 if (r == RET_OK) {
1940 /*
1941 * Since we are using the public fh, and NLM is
1942 * not firewall friendly, use local locking.
1943 * Not the case for v4.
1944 */
1945 *versp = nfsvers_to_use;
1946 switch (nfsvers_to_use) {
1947 case NFS_V4:
1948 fstype = MNTTYPE_NFS4;
1949 break;
1950 case NFS_V3:
1951 fstype = MNTTYPE_NFS3;
1952 /* fall through to pick up llock option */
1953 default:
1954 args->flags |= NFSMNT_LLOCK;
1955 break;
1956 }
1957 if (fspath != path)
1958 free(path);
1959
1960 return (r);
1961 }
1962 }
1963
1964 if (fspath != path) {
1965 free(path);
1966 }
1967
1968 if (loud == TRUE) {
1969 pr_err(gettext("Could not use public filehandle in request to"
1970 " server %s\n"), fshost);
1971 }
1972
1973 return (r);
1974 }
1975
1976 /*
1977 * get fhandle of remote path from server's mountd
1978 */
1979 static int
1980 get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp,
1981 bool_t loud_on_mnt_err, struct netconfig **nconfp, ushort_t port)
1982 {
1983 static struct fhstatus fhs;
1984 static struct mountres3 mountres3;
1985 static struct pathcnf p;
1986 nfs_fh3 *fh3p;
1987 struct timeval timeout = { 25, 0};
1988 CLIENT *cl;
1989 enum clnt_stat rpc_stat;
1990 rpcvers_t outvers = 0;
1991 rpcvers_t vers_to_try;
1992 rpcvers_t vers_min;
1993 static int printed = 0;
1994 int count, i, *auths;
1995 char *msg;
1996
1997 switch (nfsvers) {
1998 case 2: /* version 2 specified try that only */
1999 vers_to_try = MOUNTVERS_POSIX;
2000 vers_min = MOUNTVERS;
2001 break;
2002 case 3: /* version 3 specified try that only */
2003 vers_to_try = MOUNTVERS3;
2004 vers_min = MOUNTVERS3;
2005 break;
2006 case 4: /* version 4 specified try that only */
2007 /*
2008 * This assignment is in the wrong version sequence.
2009 * The above are MOUNT program and this is NFS
2010 * program. However, it happens to work out since the
2011 * two don't collide for NFSv4.
2012 */
2013 vers_to_try = NFS_V4;
2014 vers_min = NFS_V4;
2015 break;
2016 default: /* no version specified, start with default */
2017 /*
2018 * If the retry version is set, use that. This will
2019 * be set if the last mount attempt returned any other
2020 * besides an RPC error.
2021 */
2022 if (nfsretry_vers)
2023 vers_to_try = nfsretry_vers;
2024 else {
2025 vers_to_try = vers_max_default;
2026 vers_min = vers_min_default;
2027 }
2028
2029 break;
2030 }
2031
2032 /*
2033 * In the case of version 4, just NULL proc the server since
2034 * there is no MOUNT program. If this fails, then decrease
2035 * vers_to_try and continue on with regular MOUNT program
2036 * processing.
2037 */
2038 if (vers_to_try == NFS_V4) {
2039 int savevers = nfsvers_to_use;
2040 err_ret_t error;
2041 int retval;
2042 SET_ERR_RET(&error, ERR_PROTO_NONE, 0);
2043
2044 /* Let's hope for the best */
2045 nfsvers_to_use = NFS_V4;
2046 retval = getaddr_nfs(args, fshost, nconfp, FALSE,
2047 fspath, port, &error, vers_min == NFS_V4);
2048
2049 if (retval == RET_OK) {
2050 *versp = nfsvers_to_use = NFS_V4;
2051 fstype = MNTTYPE_NFS4;
2052 args->fh = strdup(fspath);
2053 if (args->fh == NULL) {
2054 pr_err(gettext("no memory\n"));
2055 *versp = nfsvers_to_use = savevers;
2056 return (RET_ERR);
2057 }
2058 return (RET_OK);
2059 }
2060 nfsvers_to_use = savevers;
2061
2062 vers_to_try--;
2063 /* If no more versions to try, let the user know. */
2064 if (vers_to_try < vers_min) {
2065 return (retval);
2066 }
2067
2068 /*
2069 * If we are here, there are more versions to try but
2070 * there has been an error of some sort. If it is not
2071 * an RPC error (e.g. host unknown), we just stop and
2072 * return the error since the other versions would see
2073 * the same error as well.
2074 */
2075 if (retval == RET_ERR && error.error_type != ERR_RPCERROR)
2076 return (retval);
2077 }
2078
2079 while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers,
2080 vers_min, vers_to_try, "datagram_v")) == NULL) {
2081 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) {
2082 pr_err(gettext("%s: %s\n"), fshost,
2083 clnt_spcreateerror(""));
2084 return (RET_ERR);
2085 }
2086
2087 /*
2088 * We don't want to downgrade version on lost packets
2089 */
2090 if ((rpc_createerr.cf_stat == RPC_TIMEDOUT) ||
2091 (rpc_createerr.cf_stat == RPC_PMAPFAILURE)) {
2092 pr_err(gettext("%s: %s\n"), fshost,
2093 clnt_spcreateerror(""));
2094 return (RET_RETRY);
2095 }
2096
2097 /*
2098 * back off and try the previous version - patch to the
2099 * problem of version numbers not being contigous and
2100 * clnt_create_vers failing (SunOS4.1 clients & SGI servers)
2101 * The problem happens with most non-Sun servers who
2102 * don't support mountd protocol #2. So, in case the
2103 * call fails, we re-try the call anyway.
2104 */
2105 vers_to_try--;
2106 if (vers_to_try < vers_min) {
2107 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH) {
2108 if (nfsvers == 0) {
2109 pr_err(gettext(
2110 "%s:%s: no applicable versions of NFS supported\n"),
2111 fshost, fspath);
2112 } else {
2113 pr_err(gettext(
2114 "%s:%s: NFS Version %d not supported\n"),
2115 fshost, fspath, nfsvers);
2116 }
2117 return (RET_ERR);
2118 }
2119 if (!printed) {
2120 pr_err(gettext("%s: %s\n"), fshost,
2121 clnt_spcreateerror(""));
2122 printed = 1;
2123 }
2124 return (RET_RETRY);
2125 }
2126 }
2127 if (posix && outvers < MOUNTVERS_POSIX) {
2128 pr_err(gettext("%s: %s: no pathconf info\n"),
2129 fshost, clnt_sperror(cl, ""));
2130 clnt_destroy(cl);
2131 return (RET_ERR);
2132 }
2133
2134 if (__clnt_bindresvport(cl) < 0) {
2135 pr_err(gettext("Couldn't bind to reserved port\n"));
2136 clnt_destroy(cl);
2137 return (RET_RETRY);
2138 }
2139
2140 if ((cl->cl_auth = authsys_create_default()) == NULL) {
2141 pr_err(
2142 gettext("Couldn't create default authentication handle\n"));
2143 clnt_destroy(cl);
2144 return (RET_RETRY);
2145 }
2146
2147 switch (outvers) {
2148 case MOUNTVERS:
2149 case MOUNTVERS_POSIX:
2150 *versp = nfsvers_to_use = NFS_VERSION;
2151 rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
2152 (caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout);
2153 if (rpc_stat != RPC_SUCCESS) {
2154 pr_err(gettext("%s:%s: server not responding %s\n"),
2155 fshost, fspath, clnt_sperror(cl, ""));
2156 clnt_destroy(cl);
2157 return (RET_RETRY);
2158 }
2159
2160 if ((errno = fhs.fhs_status) != MNT_OK) {
2161 if (loud_on_mnt_err) {
2162 if (errno == EACCES) {
2163 pr_err(gettext(
2164 "%s:%s: access denied\n"),
2165 fshost, fspath);
2166 } else {
2167 pr_err(gettext("%s:%s: %s\n"),
2168 fshost, fspath, strerror(errno));
2169 }
2170 }
2171 clnt_destroy(cl);
2172 return (RET_MNTERR);
2173 }
2174 args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle));
2175 if (args->fh == NULL) {
2176 pr_err(gettext("no memory\n"));
2177 return (RET_ERR);
2178 }
2179 memcpy((caddr_t)args->fh, (caddr_t)&fhs.fhstatus_u.fhs_fhandle,
2180 sizeof (fhs.fhstatus_u.fhs_fhandle));
2181 if (!errno && posix) {
2182 rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
2183 xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf,
2184 (caddr_t)&p, timeout);
2185 if (rpc_stat != RPC_SUCCESS) {
2186 pr_err(gettext(
2187 "%s:%s: server not responding %s\n"),
2188 fshost, fspath, clnt_sperror(cl, ""));
2189 free(args->fh);
2190 clnt_destroy(cl);
2191 return (RET_RETRY);
2192 }
2193 if (_PC_ISSET(_PC_ERROR, p.pc_mask)) {
2194 pr_err(gettext(
2195 "%s:%s: no pathconf info\n"),
2196 fshost, fspath);
2197 free(args->fh);
2198 clnt_destroy(cl);
2199 return (RET_ERR);
2200 }
2201 args->flags |= NFSMNT_POSIX;
2202 args->pathconf = malloc(sizeof (p));
2203 if (args->pathconf == NULL) {
2204 pr_err(gettext("no memory\n"));
2205 free(args->fh);
2206 clnt_destroy(cl);
2207 return (RET_ERR);
2208 }
2209 memcpy((caddr_t)args->pathconf, (caddr_t)&p,
2210 sizeof (p));
2211 }
2212 break;
2213
2214 case MOUNTVERS3:
2215 *versp = nfsvers_to_use = NFS_V3;
2216 rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
2217 (caddr_t)&fspath, xdr_mountres3,
2218 (caddr_t)&mountres3, timeout);
2219 if (rpc_stat != RPC_SUCCESS) {
2220 pr_err(gettext("%s:%s: server not responding %s\n"),
2221 fshost, fspath, clnt_sperror(cl, ""));
2222 clnt_destroy(cl);
2223 return (RET_RETRY);
2224 }
2225
2226 /*
2227 * Assume here that most of the MNT3ERR_*
2228 * codes map into E* errors.
2229 */
2230 if ((errno = mountres3.fhs_status) != MNT_OK) {
2231 if (loud_on_mnt_err) {
2232 switch (errno) {
2233 case MNT3ERR_NAMETOOLONG:
2234 msg = "path name is too long";
2235 break;
2236 case MNT3ERR_NOTSUPP:
2237 msg = "operation not supported";
2238 break;
2239 case MNT3ERR_SERVERFAULT:
2240 msg = "server fault";
2241 break;
2242 default:
2243 msg = strerror(errno);
2244 break;
2245 }
2246
2247 pr_err(gettext("%s:%s: %s\n"),
2248 fshost, fspath, msg);
2249 }
2250
2251 clnt_destroy(cl);
2252 return (RET_MNTERR);
2253 }
2254
2255 fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
2256 if (fh3p == NULL) {
2257 pr_err(gettext("no memory\n"));
2258 return (RET_ERR);
2259 }
2260 fh3p->fh3_length =
2261 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
2262 (void) memcpy(fh3p->fh3_u.data,
2263 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val,
2264 fh3p->fh3_length);
2265 args->fh = (caddr_t)fh3p;
2266 fstype = MNTTYPE_NFS3;
2267
2268 /*
2269 * Check the security flavor to be used.
2270 *
2271 * If "secure" or "sec=flavor" is a mount
2272 * option, check if the server supports the "flavor".
2273 * If the server does not support the flavor, return
2274 * error.
2275 *
2276 * If no mount option is given then use the first supported
2277 * security flavor (by the client) in the auth list returned
2278 * from the server.
2279 *
2280 */
2281 auths =
2282 mountres3.mountres3_u.mountinfo.auth_flavors.
2283 auth_flavors_val;
2284 count =
2285 mountres3.mountres3_u.mountinfo.auth_flavors.
2286 auth_flavors_len;
2287
2288 if (sec_opt) {
2289 for (i = 0; i < count; i++) {
2290 if (auths[i] == nfs_sec.sc_nfsnum)
2291 break;
2292 }
2293 if (i >= count)
2294 goto autherr;
2295 } else {
2296 if (count > 0) {
2297 for (i = 0; i < count; i++) {
2298 if (!nfs_getseconfig_bynumber(auths[i],
2299 &nfs_sec)) {
2300 sec_opt++;
2301 break;
2302 }
2303 }
2304
2305 if (i >= count)
2306 goto autherr;
2307 }
2308 }
2309 break;
2310 default:
2311 pr_err(gettext("%s:%s: Unknown MOUNT version %d\n"),
2312 fshost, fspath, outvers);
2313 clnt_destroy(cl);
2314 return (RET_ERR);
2315 }
2316
2317 clnt_destroy(cl);
2318 return (RET_OK);
2319
2320 autherr:
2321 pr_err(gettext(
2322 "security mode does not match the server exporting %s:%s\n"),
2323 fshost, fspath);
2324 clnt_destroy(cl);
2325 return (RET_ERR);
2326 }
2327
2328 /*
2329 * Fill in the address for the server's NFS service and
2330 * fill in a knetconfig structure for the transport that
2331 * the service is available on.
2332 */
2333 static int
2334 getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp,
2335 bool_t get_pubfh, char *fspath, ushort_t port, err_ret_t *error,
2336 bool_t print_rpcerror)
2337 {
2338 struct stat sb;
2339 struct netconfig *nconf;
2340 struct knetconfig *knconfp;
2341 static int printed = 0;
2342 struct t_info tinfo;
2343 err_ret_t addr_error;
2344
2345 SET_ERR_RET(error, ERR_PROTO_NONE, 0);
2346 SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0);
2347
2348 if (nfs_proto) {
2349 /*
2350 * If a proto is specified and its rdma try this. The kernel
2351 * will later do the reachablity test and fail form there
2352 * if rdma transport is not available to kernel rpc
2353 */
2354 if (strcmp(nfs_proto, "rdma") == 0) {
2355 args->addr = get_addr(fshost, NFS_PROGRAM,
2356 nfsvers_to_use, nconfp, NULL, port, &tinfo,
2357 &args->fh, get_pubfh, fspath, &addr_error);
2358
2359 args->flags |= NFSMNT_DORDMA;
2360 } else {
2361 args->addr = get_addr(fshost, NFS_PROGRAM,
2362 nfsvers_to_use, nconfp, nfs_proto, port, &tinfo,
2363 &args->fh, get_pubfh, fspath, &addr_error);
2364 }
2365 } else {
2366 args->addr = get_addr(fshost, NFS_PROGRAM, nfsvers_to_use,
2367 nconfp, nfs_proto, port, &tinfo, &args->fh, get_pubfh,
2368 fspath, &addr_error);
2369 /*
2370 * If no proto is specified set this flag.
2371 * Kernel mount code will try to use RDMA if its on the
2372 * system, otherwise it will keep on using the protocol
2373 * selected here, through the above get_addr call.
2374 */
2375 if (nfs_proto == NULL)
2376 args->flags |= NFSMNT_TRYRDMA;
2377 }
2378
2379 if (args->addr == NULL) {
2380 /*
2381 * We could have failed because the server had no public
2382 * file handle support. So don't print a message and don't
2383 * retry.
2384 */
2385 if (get_pubfh == TRUE)
2386 return (RET_ERR);
2387
2388 if (!printed) {
2389 switch (addr_error.error_type) {
2390 case 0:
2391 printed = 1;
2392 break;
2393 case ERR_RPCERROR:
2394 /* no error print at this time */
2395 if (!print_rpcerror)
2396 break;
2397 pr_err(gettext("%s NFS service not"
2398 " available %s\n"), fshost,
2399 clnt_sperrno(addr_error.error_value));
2400 printed = 1;
2401 break;
2402 case ERR_NETPATH:
2403 pr_err(gettext("%s: Error in NETPATH.\n"),
2404 fshost);
2405 printed = 1;
2406 break;
2407 case ERR_PROTO_INVALID:
2408 pr_err(gettext("%s: NFS service does not"
2409 " recognize protocol: %s.\n"), fshost,
2410 nfs_proto);
2411 printed = 1;
2412 break;
2413 case ERR_PROTO_UNSUPP:
2414 if (nfsvers || nfsvers_to_use == NFS_VERSMIN) {
2415 /*
2416 * Don't set "printed" here. Since we
2417 * have to keep checking here till we
2418 * exhaust transport errors on all vers.
2419 *
2420 * Print this message if:
2421 * 1. After we have tried all versions
2422 * of NFS and none support the asked
2423 * transport.
2424 *
2425 * 2. If a version is specified and it
2426 * does'nt support the asked
2427 * transport.
2428 *
2429 * Otherwise we decrement the version
2430 * and retry below.
2431 */
2432 pr_err(gettext("%s: NFS service does"
2433 " not support protocol: %s.\n"),
2434 fshost, nfs_proto);
2435 }
2436 break;
2437 case ERR_NOHOST:
2438 pr_err("%s: %s\n", fshost, "Unknown host");
2439 printed = 1;
2440 break;
2441 default:
2442 /* case ERR_PROTO_NONE falls through */
2443 pr_err(gettext("%s: NFS service not responding"
2444 "\n"), fshost);
2445 printed = 1;
2446 break;
2447 }
2448 }
2449
2450 SET_ERR_RET(error,
2451 addr_error.error_type, addr_error.error_value);
2452 if (addr_error.error_type == ERR_PROTO_NONE)
2453 return (RET_RETRY);
2454 else if (addr_error.error_type == ERR_RPCERROR &&
2455 ! IS_UNRECOVERABLE_RPC(addr_error.error_value)) {
2456 return (RET_RETRY);
2457 } else if (nfsvers == 0 && addr_error.error_type ==
2458 ERR_PROTO_UNSUPP && nfsvers_to_use != NFS_VERSMIN) {
2459 /*
2460 * If no version is specified, and the error is due
2461 * to an unsupported transport, then decrement the
2462 * version and retry.
2463 */
2464 return (RET_RETRY);
2465 } else
2466 return (RET_ERR);
2467 }
2468 nconf = *nconfp;
2469
2470 if (stat(nconf->nc_device, &sb) < 0) {
2471 pr_err(gettext("getaddr_nfs: couldn't stat: %s: %s\n"),
2472 nconf->nc_device, strerror(errno));
2473 return (RET_ERR);
2474 }
2475
2476 knconfp = (struct knetconfig *)malloc(sizeof (*knconfp));
2477 if (!knconfp) {
2478 pr_err(gettext("no memory\n"));
2479 return (RET_ERR);
2480 }
2481 knconfp->knc_semantics = nconf->nc_semantics;
2482 knconfp->knc_protofmly = nconf->nc_protofmly;
2483 knconfp->knc_proto = nconf->nc_proto;
2484 knconfp->knc_rdev = sb.st_rdev;
2485
2486 /* make sure we don't overload the transport */
2487 if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) {
2488 args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE);
2489 if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR)
2490 args->rsize = tinfo.tsdu - NFS_RPC_HDR;
2491 if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR)
2492 args->wsize = tinfo.tsdu - NFS_RPC_HDR;
2493 }
2494
2495 args->flags |= NFSMNT_KNCONF;
2496 args->knconf = knconfp;
2497 return (RET_OK);
2498 }
2499
2500 static int
2501 retry(struct mnttab *mntp, int ro)
2502 {
2503 int delay = 5;
2504 int count = retries;
2505 int r;
2506
2507 /*
2508 * Please see comments on nfsretry_vers in the beginning of this file
2509 * and in main() routine.
2510 */
2511
2512 if (bg) {
2513 if (fork() > 0)
2514 return (RET_OK);
2515 pr_err(gettext("backgrounding: %s\n"), mntp->mnt_mountp);
2516 backgrounded = 1;
2517 } else {
2518 if (!nfsretry_vers)
2519 pr_err(gettext("retrying: %s\n"), mntp->mnt_mountp);
2520 }
2521
2522 while (count--) {
2523 if ((r = mount_nfs(mntp, ro, NULL)) == RET_OK) {
2524 pr_err(gettext("%s: mounted OK\n"), mntp->mnt_mountp);
2525 return (RET_OK);
2526 }
2527 if (r != RET_RETRY)
2528 break;
2529
2530 if (count > 0) {
2531 (void) sleep(delay);
2532 delay *= 2;
2533 if (delay > 120)
2534 delay = 120;
2535 }
2536 }
2537
2538 if (!nfsretry_vers)
2539 pr_err(gettext("giving up on: %s\n"), mntp->mnt_mountp);
2540
2541 return (RET_ERR);
2542 }
2543
2544 /*
2545 * Read the /etc/default/nfs configuration file to determine if the
2546 * client has been configured for a new min/max for the NFS version to
2547 * use.
2548 */
2549 static void
2550 read_default(void)
2551 {
2552 char *defval;
2553 int errno;
2554 int tmp;
2555
2556 /* Fail silently if error in opening the default nfs config file */
2557 if ((defopen(NFSADMIN)) == 0) {
2558 if ((defval = defread("NFS_CLIENT_VERSMIN=")) != NULL) {
2559 errno = 0;
2560 tmp = strtol(defval, (char **)NULL, 10);
2561 if (errno == 0) {
2562 vers_min_default = tmp;
2563 }
2564 }
2565 if ((defval = defread("NFS_CLIENT_VERSMAX=")) != NULL) {
2566 errno = 0;
2567 tmp = strtol(defval, (char **)NULL, 10);
2568 if (errno == 0) {
2569 vers_max_default = tmp;
2570 }
2571 }
2572 /* close defaults file */
2573 defopen(NULL);
2574 }
2575 }
2576
2577 static void
2578 sigusr1(int s)
2579 {
2580 }