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 }