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