1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  25  */
  26 
  27 #include <stdio.h>
  28 #include <unistd.h>
  29 #include <stdlib.h>
  30 #include <ctype.h>
  31 #include <syslog.h>
  32 #include <string.h>
  33 #include <deflt.h>
  34 #include <kstat.h>
  35 #include <sys/param.h>
  36 #include <sys/types.h>
  37 #include <sys/time.h>
  38 #include <sys/stat.h>
  39 #include <sys/wait.h>
  40 #include <sys/socket.h>
  41 #include <netinet/in.h>
  42 #include <signal.h>
  43 #include <sys/signal.h>
  44 #include <rpc/rpc.h>
  45 #include <rpc/pmap_clnt.h>
  46 #include <sys/mount.h>
  47 #include <sys/mntent.h>
  48 #include <sys/mnttab.h>
  49 #include <sys/fstyp.h>
  50 #include <sys/fsid.h>
  51 #include <arpa/inet.h>
  52 #include <netdb.h>
  53 #include <netconfig.h>
  54 #include <netdir.h>
  55 #include <errno.h>
  56 #define NFSCLIENT
  57 #include <nfs/nfs.h>
  58 #include <nfs/mount.h>
  59 #include <rpcsvc/mount.h>
  60 #include <rpc/nettype.h>
  61 #include <locale.h>
  62 #include <setjmp.h>
  63 #include <sys/socket.h>
  64 #include <thread.h>
  65 #include <limits.h>
  66 #include <nss_dbdefs.h>                   /* for NSS_BUFLEN_HOSTS */
  67 #include <nfs/nfs_sec.h>
  68 #include <sys/sockio.h>
  69 #include <net/if.h>
  70 #include <assert.h>
  71 #include <nfs/nfs_clnt.h>
  72 #include <rpcsvc/nfs4_prot.h>
  73 #include <nfs/nfs4.h>
  74 #define NO_RDDIR_CACHE
  75 #include "automount.h"
  76 #include "replica.h"
  77 #include "nfs_subr.h"
  78 #include "webnfs.h"
  79 #include "nfs_resolve.h"
  80 #include <sys/sockio.h>
  81 #include <net/if.h>
  82 #include <rpcsvc/daemon_utils.h>
  83 #include <pwd.h>
  84 #include <strings.h>
  85 #include <tsol/label.h>
  86 #include <zone.h>
  87 #include <limits.h>
  88 #include <libscf.h>
  89 #include <libshare.h>
  90 #include "smfcfg.h"
  91 
  92 extern void set_nfsv4_ephemeral_mount_to(void);
  93 
  94 extern char *nfs_get_qop_name();
  95 extern AUTH *nfs_create_ah();
  96 extern enum snego_stat nfs_sec_nego();
  97 
  98 #define MAXHOSTS        512
  99 
 100 #define MNTTYPE_CACHEFS "cachefs"
 101 
 102 /*
 103  * host cache states
 104  */
 105 #define NOHOST          0
 106 #define GOODHOST        1
 107 #define DEADHOST        2
 108 
 109 #define NFS_ARGS_EXTB_secdata(args, secdata) \
 110         { (args).nfs_args_ext = NFS_ARGS_EXTB, \
 111         (args).nfs_ext_u.nfs_extB.secdata = secdata; }
 112 
 113 struct cache_entry {
 114         struct  cache_entry *cache_next;
 115         char    *cache_host;
 116         time_t  cache_time;
 117         int     cache_state;
 118         rpcvers_t cache_reqvers;
 119         rpcvers_t cache_outvers;
 120         char    *cache_proto;
 121 };
 122 
 123 struct mfs_snego_t {
 124         int sec_opt;
 125         bool_t snego_done;
 126         char *nfs_flavor;
 127         seconfig_t nfs_sec;
 128 };
 129 typedef struct mfs_snego_t mfs_snego_t;
 130 
 131 static struct cache_entry *cache_head = NULL;
 132 rwlock_t cache_lock;    /* protect the cache chain */
 133 
 134 static enum nfsstat nfsmount(struct mapfs *, char *, char *, int, int, uid_t,
 135         action_list *);
 136 static int is_nfs_port(char *);
 137 
 138 static void netbuf_free(struct netbuf *);
 139 static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int);
 140 static struct mapfs *enum_servers(struct mapent *, char *);
 141 static struct mapfs *get_mysubnet_servers(struct mapfs *);
 142 static int subnet_test(int af, struct sioc_addrreq *);
 143 static  struct  netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
 144         struct netconfig **, char *, ushort_t, struct t_info *);
 145 
 146 static  struct  netbuf *get_pubfh(char *, rpcvers_t, mfs_snego_t *,
 147         struct netconfig **, char *, ushort_t, struct t_info *, caddr_t *,
 148         bool_t, char *);
 149 
 150 static int create_homedir(const char *, const char *);
 151 
 152 enum type_of_stuff {
 153         SERVER_ADDR = 0,
 154         SERVER_PING = 1,
 155         SERVER_FH = 2
 156 };
 157 
 158 static void *get_server_netinfo(enum type_of_stuff, char *, rpcprog_t,
 159         rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t,
 160         struct t_info *, caddr_t *, bool_t, char *, enum clnt_stat *);
 161 static void *get_netconfig_info(enum type_of_stuff, char *, rpcprog_t,
 162         rpcvers_t, struct netconfig *, ushort_t, struct t_info *,
 163         struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *,
 164         mfs_snego_t *);
 165 static void *get_server_addrorping(char *, rpcprog_t, rpcvers_t,
 166         struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
 167         caddr_t *, bool_t, char *, enum clnt_stat *, int);
 168 static void *get_server_fh(char *, rpcprog_t, rpcvers_t, mfs_snego_t *,
 169         struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
 170         caddr_t *, bool_t, char *, enum clnt_stat *);
 171 
 172 struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **);
 173 void free_mfs(struct mapfs *);
 174 static void dump_mfs(struct mapfs *, char *, int);
 175 static char *dump_distance(struct mapfs *);
 176 static void cache_free(struct cache_entry *);
 177 static int cache_check(char *, rpcvers_t *, char *);
 178 static void cache_enter(char *, rpcvers_t, rpcvers_t, char *, int);
 179 void destroy_auth_client_handle(CLIENT *cl);
 180 
 181 #ifdef CACHE_DEBUG
 182 static void trace_host_cache();
 183 static void trace_portmap_cache();
 184 #endif /* CACHE_DEBUG */
 185 
 186 static int rpc_timeout = 20;
 187 
 188 #ifdef CACHE_DEBUG
 189 /*
 190  * host cache counters. These variables do not need to be protected
 191  * by mutex's. They have been added to measure the utility of the
 192  * goodhost/deadhost cache in the lazy hierarchical mounting scheme.
 193  */
 194 static int host_cache_accesses = 0;
 195 static int host_cache_lookups = 0;
 196 static int deadhost_cache_hits = 0;
 197 static int goodhost_cache_hits = 0;
 198 
 199 /*
 200  * portmap cache counters. These variables do not need to be protected
 201  * by mutex's. They have been added to measure the utility of the portmap
 202  * cache in the lazy hierarchical mounting scheme.
 203  */
 204 static int portmap_cache_accesses = 0;
 205 static int portmap_cache_lookups = 0;
 206 static int portmap_cache_hits = 0;
 207 #endif /* CACHE_DEBUG */
 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  * list of support services needed
 220  */
 221 static char     *service_list[] = { STATD, LOCKD, NULL };
 222 static char     *service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
 223 
 224 static void read_default_nfs(void);
 225 static int is_v4_mount(char *);
 226 static void start_nfs4cbd(void);
 227 
 228 int
 229 mount_nfs(
 230         struct mapent *me,
 231         char *mntpnt,
 232         char *prevhost,
 233         int overlay,
 234         uid_t uid,
 235         action_list **alpp)
 236 {
 237         struct mapfs *mfs, *mp;
 238         int err = -1;
 239         int cached;
 240         action_list *alp;
 241         char *dir;
 242 
 243 
 244         alp = *alpp;
 245 
 246         read_default_nfs();
 247 
 248         mfs = enum_servers(me, prevhost);
 249         if (mfs == NULL)
 250                 return (ENOENT);
 251 
 252         /*
 253          * Try loopback if we have something on localhost; if nothing
 254          * works, we will fall back to NFS
 255          */
 256         if (is_nfs_port(me->map_mntopts)) {
 257                 for (mp = mfs; mp; mp = mp->mfs_next) {
 258                         if (self_check(mp->mfs_host)) {
 259                                 err = loopbackmount(mp->mfs_dir,
 260                                     mntpnt, me->map_mntopts, overlay);
 261                                 if (err) {
 262                                         mp->mfs_ignore = 1;
 263                                 } else {
 264                                         /*
 265                                          * Free action_list if there
 266                                          * is one as it is not needed.
 267                                          * Make sure to set alpp to null
 268                                          * so caller doesn't try to free it
 269                                          * again.
 270                                          */
 271                                         if (*alpp) {
 272                                                 free(*alpp);
 273                                                 *alpp = NULL;
 274                                         }
 275                                         break;
 276                                 }
 277                         }
 278                 }
 279         }
 280         if (err) {
 281                 cached = strcmp(me->map_mounter, MNTTYPE_CACHEFS) == 0;
 282                 dir = strdup(mfs->mfs_dir);
 283                 err = nfsmount(mfs, mntpnt, me->map_mntopts,
 284                     cached, overlay, uid, alp);
 285                 if (err && trace > 1) {
 286                         trace_prt(1, "  Couldn't mount %s:%s, err=%d\n",
 287                             mfs->mfs_host ? mfs->mfs_host : "",
 288                             mfs->mfs_dir ? mfs->mfs_dir : dir, err);
 289                 }
 290                 free(dir);
 291         }
 292         free_mfs(mfs);
 293         return (err);
 294 }
 295 
 296 
 297 /*
 298  * Using the new ioctl SIOCTONLINK to determine if a host is on the same
 299  * subnet. Remove the old network, subnet check.
 300  */
 301 
 302 static struct mapfs *
 303 get_mysubnet_servers(struct mapfs *mfs_in)
 304 {
 305         int s;
 306         struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
 307 
 308         struct netconfig *nconf;
 309         NCONF_HANDLE *nc = NULL;
 310         struct nd_hostserv hs;
 311         struct nd_addrlist *retaddrs;
 312         struct netbuf *nb;
 313         struct sioc_addrreq areq;
 314         int res;
 315         int af;
 316         int i;
 317         int sa_size;
 318 
 319         hs.h_serv = "rpcbind";
 320 
 321         for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
 322                 nc = setnetconfig();
 323 
 324                 while (nconf = getnetconfig(nc)) {
 325 
 326                         /*
 327                          * Care about INET family only. proto_done flag
 328                          * indicates if we have already covered this
 329                          * protocol family. If so skip it
 330                          */
 331                         if (((strcmp(nconf->nc_protofmly, NC_INET6) == 0) ||
 332                             (strcmp(nconf->nc_protofmly, NC_INET) == 0)) &&
 333                             (nconf->nc_semantics == NC_TPI_CLTS)) {
 334                         } else
 335                                 continue;
 336 
 337                         hs.h_host = mfs->mfs_host;
 338 
 339                         if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK)
 340                                 continue;
 341 
 342                         /*
 343                          * For each host address see if it's on our
 344                          * local subnet.
 345                          */
 346 
 347                         if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
 348                                 af = AF_INET6;
 349                         else
 350                                 af = AF_INET;
 351                         nb = retaddrs->n_addrs;
 352                         for (i = 0; i < retaddrs->n_cnt; i++, nb++) {
 353                                 memset(&areq.sa_addr, 0, sizeof (areq.sa_addr));
 354                                 memcpy(&areq.sa_addr, nb->buf, MIN(nb->len,
 355                                     sizeof (areq.sa_addr)));
 356                                 if (res = subnet_test(af, &areq)) {
 357                                         p = add_mfs(mfs, DIST_MYNET,
 358                                             &mfs_head, &mfs_tail);
 359                                         if (!p) {
 360                                                 netdir_free(retaddrs,
 361                                                     ND_ADDRLIST);
 362                                                 endnetconfig(nc);
 363                                                 return (NULL);
 364                                         }
 365                                         break;
 366                                 }
 367                         }  /* end of every host */
 368                         if (trace > 2) {
 369                                 trace_prt(1, "get_mysubnet_servers: host=%s "
 370                                     "netid=%s res=%s\n", mfs->mfs_host,
 371                                     nconf->nc_netid, res == 1?"SUC":"FAIL");
 372                         }
 373 
 374                         netdir_free(retaddrs, ND_ADDRLIST);
 375                 } /* end of while */
 376 
 377                 endnetconfig(nc);
 378 
 379         } /* end of every map */
 380 
 381         return (mfs_head);
 382 
 383 }
 384 
 385 int
 386 subnet_test(int af, struct sioc_addrreq *areq)
 387 {
 388         int s;
 389 
 390         if ((s = socket(af, SOCK_DGRAM, 0)) < 0) {
 391                 return (0);
 392         }
 393 
 394         areq->sa_res = -1;
 395 
 396         if (ioctl(s, SIOCTONLINK, (caddr_t)areq) < 0) {
 397                 syslog(LOG_ERR, "subnet_test:SIOCTONLINK failed");
 398                 return (0);
 399         }
 400         close(s);
 401         if (areq->sa_res == 1)
 402                 return (1);
 403         else
 404                 return (0);
 405 
 406 
 407 }
 408 
 409 /*
 410  * ping a bunch of hosts at once and sort by who responds first
 411  */
 412 static struct mapfs *
 413 sort_servers(struct mapfs *mfs_in, int timeout)
 414 {
 415         struct mapfs *m1 = NULL;
 416         enum clnt_stat clnt_stat;
 417 
 418         if (!mfs_in)
 419                 return (NULL);
 420 
 421         clnt_stat = nfs_cast(mfs_in, &m1, timeout);
 422 
 423         if (!m1) {
 424                 char buff[2048] = {'\0'};
 425 
 426                 for (m1 = mfs_in; m1; m1 = m1->mfs_next) {
 427                         (void) strcat(buff, m1->mfs_host);
 428                         if (m1->mfs_next)
 429                                 (void) strcat(buff, ",");
 430                 }
 431 
 432                 syslog(LOG_ERR, "servers %s not responding: %s",
 433                     buff, clnt_sperrno(clnt_stat));
 434         }
 435 
 436         return (m1);
 437 }
 438 
 439 /*
 440  * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
 441  * provided it is not marked "ignored" and isn't a dupe of ones we've
 442  * already seen.
 443  */
 444 struct mapfs *
 445 add_mfs(struct mapfs *mfs, int distance, struct mapfs **mfs_head,
 446         struct mapfs **mfs_tail)
 447 {
 448         struct mapfs *tmp, *new;
 449 
 450         for (tmp = *mfs_head; tmp; tmp = tmp->mfs_next)
 451                 if ((strcmp(tmp->mfs_host, mfs->mfs_host) == 0 &&
 452                     strcmp(tmp->mfs_dir, mfs->mfs_dir) == 0) ||
 453                     mfs->mfs_ignore)
 454                         return (*mfs_head);
 455         new = (struct mapfs *)malloc(sizeof (struct mapfs));
 456         if (!new) {
 457                 syslog(LOG_ERR, "Memory allocation failed: %m");
 458                 return (NULL);
 459         }
 460         bcopy(mfs, new, sizeof (struct mapfs));
 461         new->mfs_next = NULL;
 462         if (distance)
 463                 new->mfs_distance = distance;
 464         if (!*mfs_head)
 465                 *mfs_tail = *mfs_head = new;
 466         else {
 467                 (*mfs_tail)->mfs_next = new;
 468                 *mfs_tail = new;
 469         }
 470         return (*mfs_head);
 471 }
 472 
 473 static void
 474 dump_mfs(struct mapfs *mfs, char *message, int level)
 475 {
 476         struct mapfs *m1;
 477 
 478         if (trace <= level)
 479                 return;
 480 
 481         trace_prt(1, "%s", message);
 482         if (!mfs) {
 483                 trace_prt(0, "mfs is null\n");
 484                 return;
 485         }
 486         for (m1 = mfs; m1; m1 = m1->mfs_next)
 487                 trace_prt(0, "%s[%s] ", m1->mfs_host, dump_distance(m1));
 488         trace_prt(0, "\n");
 489 }
 490 
 491 static char *
 492 dump_distance(struct mapfs *mfs)
 493 {
 494         switch (mfs->mfs_distance) {
 495         case 0:                 return ("zero");
 496         case DIST_SELF:         return ("self");
 497         case DIST_MYSUB:        return ("mysub");
 498         case DIST_MYNET:        return ("mynet");
 499         case DIST_OTHER:        return ("other");
 500         default:                return ("other");
 501         }
 502 }
 503 
 504 /*
 505  * Walk linked list "raw", building a new list consisting of members
 506  * NOT found in list "filter", returning the result.
 507  */
 508 static struct mapfs *
 509 filter_mfs(struct mapfs *raw, struct mapfs *filter)
 510 {
 511         struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
 512         int skip;
 513 
 514         if (!raw)
 515                 return (NULL);
 516         for (mfs = raw; mfs; mfs = mfs->mfs_next) {
 517                 for (skip = 0, p = filter; p; p = p->mfs_next) {
 518                         if (strcmp(p->mfs_host, mfs->mfs_host) == 0 &&
 519                             strcmp(p->mfs_dir, mfs->mfs_dir) == 0) {
 520                                 skip = 1;
 521                                 break;
 522                         }
 523                 }
 524                 if (skip)
 525                         continue;
 526                 p = add_mfs(mfs, 0, &mfs_head, &mfs_tail);
 527                 if (!p)
 528                         return (NULL);
 529         }
 530         return (mfs_head);
 531 }
 532 
 533 /*
 534  * Walk a linked list of mapfs structs, freeing each member.
 535  */
 536 void
 537 free_mfs(struct mapfs *mfs)
 538 {
 539         struct mapfs *tmp;
 540 
 541         while (mfs) {
 542                 tmp = mfs->mfs_next;
 543                 free(mfs);
 544                 mfs = tmp;
 545         }
 546 }
 547 
 548 /*
 549  * New code for NFS client failover: we need to carry and sort
 550  * lists of server possibilities rather than return a single
 551  * entry.  It preserves previous behaviour of sorting first by
 552  * locality (loopback-or-preferred/subnet/net/other) and then
 553  * by ping times.  We'll short-circuit this process when we
 554  * have ENOUGH or more entries.
 555  */
 556 static struct mapfs *
 557 enum_servers(struct mapent *me, char *preferred)
 558 {
 559         struct mapfs *p, *m1, *m2, *mfs_head = NULL, *mfs_tail = NULL;
 560 
 561         /*
 562          * Short-circuit for simple cases.
 563          */
 564         if (!me->map_fs->mfs_next) {
 565                 p = add_mfs(me->map_fs, DIST_OTHER, &mfs_head, &mfs_tail);
 566                 if (!p)
 567                         return (NULL);
 568                 return (mfs_head);
 569         }
 570 
 571         dump_mfs(me->map_fs, "       enum_servers: mapent: ", 2);
 572 
 573         /*
 574          * get addresses & see if any are myself
 575          * or were mounted from previously in a
 576          * hierarchical mount.
 577          */
 578         if (trace > 2)
 579                 trace_prt(1, "  enum_servers: looking for pref/self\n");
 580         for (m1 = me->map_fs; m1; m1 = m1->mfs_next) {
 581                 if (m1->mfs_ignore)
 582                         continue;
 583                 if (self_check(m1->mfs_host) ||
 584                     strcmp(m1->mfs_host, preferred) == 0) {
 585                         p = add_mfs(m1, DIST_SELF, &mfs_head, &mfs_tail);
 586                         if (!p)
 587                                 return (NULL);
 588                 }
 589         }
 590         if (trace > 2 && m1)
 591                 trace_prt(1, "  enum_servers: pref/self found, %s\n",
 592                     m1->mfs_host);
 593 
 594         /*
 595          * look for entries on this subnet
 596          */
 597         dump_mfs(m1, "  enum_servers: input of get_mysubnet_servers: ", 2);
 598         m1 = get_mysubnet_servers(me->map_fs);
 599         dump_mfs(m1, "  enum_servers: output of get_mysubnet_servers: ", 3);
 600         if (m1 && m1->mfs_next) {
 601                 m2 = sort_servers(m1, rpc_timeout / 2);
 602                 dump_mfs(m2, "  enum_servers: output of sort_servers: ", 3);
 603                 free_mfs(m1);
 604                 m1 = m2;
 605         }
 606 
 607         for (m2 = m1; m2; m2 = m2->mfs_next) {
 608                 p = add_mfs(m2, 0, &mfs_head, &mfs_tail);
 609                 if (!p)
 610                         return (NULL);
 611         }
 612         if (m1)
 613                 free_mfs(m1);
 614 
 615         /*
 616          * add the rest of the entries at the end
 617          */
 618         m1 = filter_mfs(me->map_fs, mfs_head);
 619         dump_mfs(m1, "  enum_servers: etc: output of filter_mfs: ", 3);
 620         m2 = sort_servers(m1, rpc_timeout / 2);
 621         dump_mfs(m2, "  enum_servers: etc: output of sort_servers: ", 3);
 622         if (m1)
 623                 free_mfs(m1);
 624         m1 = m2;
 625         for (m2 = m1; m2; m2 = m2->mfs_next) {
 626                 p = add_mfs(m2, DIST_OTHER, &mfs_head, &mfs_tail);
 627                 if (!p)
 628                         return (NULL);
 629         }
 630         if (m1)
 631                 free_mfs(m1);
 632 
 633 done:
 634         dump_mfs(mfs_head, "  enum_servers: output: ", 1);
 635         return (mfs_head);
 636 }
 637 
 638 static enum nfsstat
 639 nfsmount(
 640         struct mapfs *mfs_in,
 641         char *mntpnt, char *opts,
 642         int cached, int overlay,
 643         uid_t uid,
 644         action_list *alp)
 645 {
 646         CLIENT *cl;
 647         char remname[MAXPATHLEN], *mnttabtext = NULL;
 648         char mopts[MAX_MNTOPT_STR];
 649         char netname[MAXNETNAMELEN+1];
 650         char    *mntopts = NULL;
 651         int mnttabcnt = 0;
 652         int loglevel;
 653         struct mnttab m;
 654         struct nfs_args *argp = NULL, *head = NULL, *tail = NULL,
 655             *prevhead, *prevtail;
 656         int flags;
 657         struct fhstatus fhs;
 658         struct timeval timeout;
 659         enum clnt_stat rpc_stat;
 660         enum nfsstat status;
 661         struct stat stbuf;
 662         struct netconfig *nconf;
 663         rpcvers_t vers, versmin; /* used to negotiate nfs version in pingnfs */
 664                                 /* and mount version with mountd */
 665         rpcvers_t outvers;      /* final version to be used during mount() */
 666         rpcvers_t nfsvers;      /* version in map options, 0 if not there */
 667         rpcvers_t mountversmax; /* tracks the max mountvers during retries */
 668 
 669         /* used to negotiate nfs version using webnfs */
 670         rpcvers_t pubvers, pubversmin, pubversmax;
 671         int posix;
 672         struct nd_addrlist *retaddrs;
 673         struct mountres3 res3;
 674         nfs_fh3 fh3;
 675         char *fstype;
 676         int count, i;
 677         char scerror_msg[MAXMSGLEN];
 678         int *auths;
 679         int delay;
 680         int retries;
 681         char *nfs_proto = NULL;
 682         uint_t nfs_port = 0;
 683         char *p, *host, *rhost, *dir;
 684         struct mapfs *mfs = NULL;
 685         int error, last_error = 0;
 686         int replicated;
 687         int entries = 0;
 688         int v2cnt = 0, v3cnt = 0, v4cnt = 0;
 689         int v2near = 0, v3near = 0, v4near = 0;
 690         int skipentry = 0;
 691         char *nfs_flavor;
 692         seconfig_t nfs_sec;
 693         int sec_opt, scerror;
 694         struct sec_data *secdata;
 695         int secflags;
 696         struct netbuf *syncaddr;
 697         bool_t  use_pubfh;
 698         ushort_t thisport;
 699         int got_val;
 700         mfs_snego_t mfssnego_init, mfssnego;
 701 
 702         dump_mfs(mfs_in, "  nfsmount: input: ", 2);
 703         replicated = (mfs_in->mfs_next != NULL);
 704         m.mnt_mntopts = opts;
 705         if (replicated && hasmntopt(&m, MNTOPT_SOFT)) {
 706                 if (verbose)
 707                         syslog(LOG_WARNING,
 708                     "mount on %s is soft and will not be replicated.", mntpnt);
 709                 replicated = 0;
 710         }
 711         if (replicated && !hasmntopt(&m, MNTOPT_RO)) {
 712                 if (verbose)
 713                         syslog(LOG_WARNING,
 714                     "mount on %s is not read-only and will not be replicated.",
 715                             mntpnt);
 716                 replicated = 0;
 717         }
 718         if (replicated && cached) {
 719                 if (verbose)
 720                         syslog(LOG_WARNING,
 721                     "mount on %s is cached and will not be replicated.",
 722                             mntpnt);
 723                 replicated = 0;
 724         }
 725         if (replicated)
 726                 loglevel = LOG_WARNING;
 727         else
 728                 loglevel = LOG_ERR;
 729 
 730         if (trace > 1) {
 731                 if (replicated)
 732                         trace_prt(1, "  nfsmount: replicated mount on %s %s:\n",
 733                             mntpnt, opts);
 734                 else
 735                         trace_prt(1, "  nfsmount: standard mount on %s %s:\n",
 736                             mntpnt, opts);
 737                 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next)
 738                         trace_prt(1, "    %s:%s\n",
 739                             mfs->mfs_host, mfs->mfs_dir);
 740         }
 741 
 742         /*
 743          * Make sure mountpoint is safe to mount on
 744          */
 745         if (lstat(mntpnt, &stbuf) < 0) {
 746                 syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt);
 747                 return (NFSERR_NOENT);
 748         }
 749 
 750         /*
 751          * Get protocol specified in options list, if any.
 752          */
 753         if ((str_opt(&m, "proto", &nfs_proto)) == -1) {
 754                 return (NFSERR_NOENT);
 755         }
 756 
 757         /*
 758          * Get port specified in options list, if any.
 759          */
 760         got_val = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
 761         if (!got_val)
 762                 nfs_port = 0;   /* "unspecified" */
 763         if (nfs_port > USHRT_MAX) {
 764                 syslog(LOG_ERR, "%s: invalid port number %d", mntpnt, nfs_port);
 765                 return (NFSERR_NOENT);
 766         }
 767 
 768         /*
 769          * Set mount(2) flags here, outside of the loop.
 770          */
 771         flags = MS_OPTIONSTR;
 772         flags |= (hasmntopt(&m, MNTOPT_RO) == NULL) ? 0 : MS_RDONLY;
 773         flags |= (hasmntopt(&m, MNTOPT_NOSUID) == NULL) ? 0 : MS_NOSUID;
 774         flags |= overlay ? MS_OVERLAY : 0;
 775         if (mntpnt[strlen(mntpnt) - 1] != ' ')
 776                 /* direct mount point without offsets */
 777                 flags |= MS_OVERLAY;
 778 
 779         use_pubfh = (hasmntopt(&m, MNTOPT_PUBLIC) == NULL) ? FALSE : TRUE;
 780 
 781         (void) memset(&mfssnego_init, 0, sizeof (mfs_snego_t));
 782         if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
 783                 if (++mfssnego_init.sec_opt > 1) {
 784                         syslog(loglevel,
 785                             "conflicting security options");
 786                         return (NFSERR_IO);
 787                 }
 788                 if (nfs_getseconfig_byname("dh", &mfssnego_init.nfs_sec)) {
 789                         syslog(loglevel,
 790                             "error getting dh information from %s",
 791                             NFSSEC_CONF);
 792                         return (NFSERR_IO);
 793                 }
 794         }
 795 
 796         if (hasmntopt(&m, MNTOPT_SEC) != NULL) {
 797                 if ((str_opt(&m, MNTOPT_SEC,
 798                     &mfssnego_init.nfs_flavor)) == -1) {
 799                         syslog(LOG_ERR, "nfsmount: no memory");
 800                         return (NFSERR_IO);
 801                 }
 802         }
 803 
 804         if (mfssnego_init.nfs_flavor) {
 805                 if (++mfssnego_init.sec_opt > 1) {
 806                         syslog(loglevel,
 807                             "conflicting security options");
 808                         free(mfssnego_init.nfs_flavor);
 809                         return (NFSERR_IO);
 810                 }
 811                 if (nfs_getseconfig_byname(mfssnego_init.nfs_flavor,
 812                     &mfssnego_init.nfs_sec)) {
 813                         syslog(loglevel,
 814                             "error getting %s information from %s",
 815                             mfssnego_init.nfs_flavor, NFSSEC_CONF);
 816                         free(mfssnego_init.nfs_flavor);
 817                         return (NFSERR_IO);
 818                 }
 819                 free(mfssnego_init.nfs_flavor);
 820         }
 821 
 822 nextentry:
 823         skipentry = 0;
 824 
 825         got_val = nopt(&m, MNTOPT_VERS, (int *)&nfsvers);
 826         if (!got_val)
 827                 nfsvers = 0;    /* "unspecified" */
 828         if (set_versrange(nfsvers, &vers, &versmin) != 0) {
 829                 syslog(LOG_ERR, "Incorrect NFS version specified for %s",
 830                     mntpnt);
 831                 last_error = NFSERR_NOENT;
 832                 goto ret;
 833         }
 834 
 835         if (nfsvers != 0) {
 836                 pubversmax = pubversmin = nfsvers;
 837         } else {
 838                 pubversmax = vers;
 839                 pubversmin = versmin;
 840         }
 841 
 842         /*
 843          * Walk the whole list, pinging and collecting version
 844          * info so that we can make sure the mount will be
 845          * homogeneous with respect to version.
 846          *
 847          * If we have a version preference, this is easy; we'll
 848          * just reject anything that doesn't match.
 849          *
 850          * If not, we want to try to provide the best compromise
 851          * that considers proximity, preference for a higher version,
 852          * sorted order, and number of replicas.  We will count
 853          * the number of V2 and V3 replicas and also the number
 854          * which are "near", i.e. the localhost or on the same
 855          * subnet.
 856          */
 857         for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
 858 
 859 
 860                 if (mfs->mfs_ignore)
 861                         continue;
 862 
 863                 /*
 864                  * If the host is '[a:d:d:r:e:s:s'],
 865                  * only use 'a:d:d:r:e:s:s' for communication
 866                  */
 867                 host = strdup(mfs->mfs_host);
 868                 if (host == NULL) {
 869                         syslog(LOG_ERR, "nfsmount: no memory");
 870                         last_error = NFSERR_IO;
 871                         goto out;
 872                 }
 873                 unbracket(&host);
 874 
 875                 (void) memcpy(&mfssnego, &mfssnego_init, sizeof (mfs_snego_t));
 876 
 877                 if (use_pubfh == TRUE || mfs->mfs_flags & MFS_URL) {
 878                         char *path;
 879 
 880                         if (nfs_port != 0 && mfs->mfs_port != 0 &&
 881                             nfs_port != mfs->mfs_port) {
 882 
 883                                 syslog(LOG_ERR, "nfsmount: port (%u) in nfs URL"
 884                                     " not the same as port (%d) in port "
 885                                     "option\n", mfs->mfs_port, nfs_port);
 886                                 last_error = NFSERR_IO;
 887                                 goto out;
 888 
 889                         } else if (nfs_port != 0)
 890                                 thisport = nfs_port;
 891                         else
 892                                 thisport = mfs->mfs_port;
 893 
 894                         dir = mfs->mfs_dir;
 895 
 896                         if ((mfs->mfs_flags & MFS_URL) == 0) {
 897                                 path = malloc(strlen(dir) + 2);
 898                                 if (path == NULL) {
 899                                         syslog(LOG_ERR, "nfsmount: no memory");
 900                                         last_error = NFSERR_IO;
 901                                         goto out;
 902                                 }
 903                                 path[0] = (char)WNL_NATIVEPATH;
 904                                 (void) strcpy(&path[1], dir);
 905                         } else {
 906                                 path = dir;
 907                         }
 908 
 909                         argp = (struct nfs_args *)
 910                             malloc(sizeof (struct nfs_args));
 911 
 912                         if (!argp) {
 913                                 if (path != dir)
 914                                         free(path);
 915                                 syslog(LOG_ERR, "nfsmount: no memory");
 916                                 last_error = NFSERR_IO;
 917                                 goto out;
 918                         }
 919                         (void) memset(argp, 0, sizeof (*argp));
 920 
 921                         /*
 922                          * RDMA support
 923                          * By now Mount argument struct has been allocated,
 924                          * either a pub_fh path will be taken or the regular
 925                          * one. So here if a protocol was specified and it
 926                          * was not rdma we let it be, else we set DO_RDMA.
 927                          * If no proto was there we advise on trying RDMA.
 928                          */
 929                         if (nfs_proto) {
 930                                 if (strcmp(nfs_proto, "rdma") == 0) {
 931                                         free(nfs_proto);
 932                                         nfs_proto = NULL;
 933                                         argp->flags |= NFSMNT_DORDMA;
 934                                 }
 935                         } else
 936                                 argp->flags |= NFSMNT_TRYRDMA;
 937 
 938                         for (pubvers = pubversmax; pubvers >= pubversmin;
 939                             pubvers--) {
 940 
 941                                 nconf = NULL;
 942                                 argp->addr = get_pubfh(host, pubvers, &mfssnego,
 943                                     &nconf, nfs_proto, thisport, NULL,
 944                                     &argp->fh, TRUE, path);
 945 
 946                                 if (argp->addr != NULL)
 947                                         break;
 948 
 949                                 if (nconf != NULL)
 950                                         freenetconfigent(nconf);
 951                         }
 952 
 953                         if (path != dir)
 954                                 free(path);
 955 
 956                         if (argp->addr != NULL) {
 957 
 958                                 /*
 959                                  * The use of llock option for NFSv4
 960                                  * mounts is not required since file
 961                                  * locking is included within the protocol
 962                                  */
 963                                 if (pubvers != NFS_V4)
 964                                         argp->flags |= NFSMNT_LLOCK;
 965 
 966                                 argp->flags |= NFSMNT_PUBLIC;
 967 
 968                                 vers = pubvers;
 969                                 mfs->mfs_args = argp;
 970                                 mfs->mfs_version = pubvers;
 971                                 mfs->mfs_nconf = nconf;
 972                                 mfs->mfs_flags |= MFS_FH_VIA_WEBNFS;
 973 
 974                         } else {
 975                                 free(argp);
 976 
 977                                 /*
 978                                  * If -public was specified, give up
 979                                  * on this entry now.
 980                                  */
 981                                 if (use_pubfh == TRUE) {
 982                                         syslog(loglevel,
 983                                             "%s: no public file handle support",
 984                                             host);
 985                                         last_error = NFSERR_NOENT;
 986                                         mfs->mfs_ignore = 1;
 987                                         continue;
 988                                 }
 989 
 990                                 /*
 991                                  * Back off to a conventional mount.
 992                                  *
 993                                  * URL's can contain escape characters. Get
 994                                  * rid of them.
 995                                  */
 996                                 path = malloc(strlen(dir) + 2);
 997 
 998                                 if (path == NULL) {
 999                                         syslog(LOG_ERR, "nfsmount: no memory");
1000                                         last_error = NFSERR_IO;
1001                                         goto out;
1002                                 }
1003 
1004                                 strcpy(path, dir);
1005                                 URLparse(path);
1006                                 mfs->mfs_dir = path;
1007                                 mfs->mfs_flags |= MFS_ALLOC_DIR;
1008                                 mfs->mfs_flags &= ~MFS_URL;
1009                         }
1010                 }
1011 
1012                 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) ==  0) {
1013                         i = pingnfs(host, get_retry(opts) + 1, &vers, versmin,
1014                             0, FALSE, NULL, nfs_proto);
1015                         if (i != RPC_SUCCESS) {
1016                                 if (i == RPC_PROGVERSMISMATCH) {
1017                                         syslog(loglevel, "server %s: NFS "
1018                                             "protocol version mismatch",
1019                                             host);
1020                                 } else {
1021                                         syslog(loglevel, "server %s not "
1022                                             "responding", host);
1023                                 }
1024                                 mfs->mfs_ignore = 1;
1025                                 last_error = NFSERR_NOENT;
1026                                 continue;
1027                         }
1028                         if (nfsvers != 0 && nfsvers != vers) {
1029                                 if (nfs_proto == NULL)
1030                                         syslog(loglevel,
1031                                             "NFS version %d "
1032                                             "not supported by %s",
1033                                             nfsvers, host);
1034                                 else
1035                                         syslog(loglevel,
1036                                             "NFS version %d "
1037                                             "with proto %s "
1038                                             "not supported by %s",
1039                                             nfsvers, nfs_proto, host);
1040                                 mfs->mfs_ignore = 1;
1041                                 last_error = NFSERR_NOENT;
1042                                 continue;
1043                         }
1044                 }
1045 
1046                 free(host);
1047 
1048                 switch (vers) {
1049                 case NFS_V4: v4cnt++; break;
1050                 case NFS_V3: v3cnt++; break;
1051                 case NFS_VERSION: v2cnt++; break;
1052                 default: break;
1053                 }
1054 
1055                 /*
1056                  * It's not clear how useful this stuff is if
1057                  * we are using webnfs across the internet, but it
1058                  * can't hurt.
1059                  */
1060                 if (mfs->mfs_distance &&
1061                     mfs->mfs_distance <= DIST_MYSUB) {
1062                         switch (vers) {
1063                         case NFS_V4: v4near++; break;
1064                         case NFS_V3: v3near++; break;
1065                         case NFS_VERSION: v2near++; break;
1066                         default: break;
1067                         }
1068                 }
1069 
1070                 /*
1071                  * If the mount is not replicated, we don't want to
1072                  * ping every entry, so we'll stop here.  This means
1073                  * that we may have to go back to "nextentry" above
1074                  * to consider another entry if we can't get
1075                  * all the way to mount(2) with this one.
1076                  */
1077                 if (!replicated)
1078                         break;
1079 
1080         }
1081 
1082         if (nfsvers == 0) {
1083                 /*
1084                  * Choose the NFS version.
1085                  * We prefer higher versions, but will choose a one-
1086                  * version downgrade in service if we can use a local
1087                  * network interface and avoid a router.
1088                  */
1089                 if (v4cnt && v4cnt >= v3cnt && (v4near || !v3near))
1090                         nfsvers = NFS_V4;
1091                 else if (v3cnt && v3cnt >= v2cnt && (v3near || !v2near))
1092                         nfsvers = NFS_V3;
1093                 else
1094                         nfsvers = NFS_VERSION;
1095                 if (trace > 2)
1096                         trace_prt(1,
1097                     "  nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1098                             v4cnt, v4near, v3cnt, v3near,
1099                             v2cnt, v2near, nfsvers);
1100         }
1101 
1102         /*
1103          * Since we don't support different NFS versions in replicated
1104          * mounts, set fstype now.
1105          * Also take the opportunity to set
1106          * the mount protocol version as appropriate.
1107          */
1108         switch (nfsvers) {
1109         case NFS_V4:
1110                 fstype = MNTTYPE_NFS4;
1111                 break;
1112         case NFS_V3:
1113                 fstype = MNTTYPE_NFS3;
1114                 if (use_pubfh == FALSE) {
1115                         mountversmax = MOUNTVERS3;
1116                         versmin = MOUNTVERS3;
1117                 }
1118                 break;
1119         case NFS_VERSION:
1120                 fstype = MNTTYPE_NFS;
1121                 if (use_pubfh == FALSE) {
1122                         mountversmax = MOUNTVERS_POSIX;
1123                         versmin = MOUNTVERS;
1124                 }
1125                 break;
1126         }
1127 
1128         /*
1129          * Our goal here is to evaluate each of several possible
1130          * replicas and try to come up with a list we can hand
1131          * to mount(2).  If we don't have a valid "head" at the
1132          * end of this process, it means we have rejected all
1133          * potential server:/path tuples.  We will fail quietly
1134          * in front of mount(2), and will have printed errors
1135          * where we found them.
1136          * XXX - do option work outside loop w careful design
1137          * XXX - use macro for error condition free handling
1138          */
1139         for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
1140 
1141                 /*
1142                  * Initialize retry and delay values on a per-server basis.
1143                  */
1144                 retries = get_retry(opts);
1145                 delay = INITDELAY;
1146 retry:
1147                 if (mfs->mfs_ignore)
1148                         continue;
1149 
1150                 /*
1151                  * If we don't have a fh yet, and if this is not a replicated
1152                  * mount, we haven't done a pingnfs() on the next entry,
1153                  * so we don't know if the next entry is up or if it
1154                  * supports an NFS version we like.  So if we had a problem
1155                  * with an entry, we need to go back and run through some new
1156                  * code.
1157                  */
1158                 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1159                     !replicated && skipentry)
1160                         goto nextentry;
1161 
1162                 vers = mountversmax;
1163                 host = mfs->mfs_host;
1164                 dir = mfs->mfs_dir;
1165 
1166                 /*
1167                  * Remember the possible '[a:d:d:r:e:s:s]' as the address to be
1168                  * later passed to mount(2) and used in the mnttab line, but
1169                  * only use 'a:d:d:r:e:s:s' for communication
1170                  */
1171                 rhost = strdup(host);
1172                 if (rhost == NULL) {
1173                         syslog(LOG_ERR, "nfsmount: no memory");
1174                         last_error = NFSERR_IO;
1175                         goto out;
1176                 }
1177                 unbracket(&host);
1178 
1179                 (void) sprintf(remname, "%s:%s", rhost, dir);
1180                 if (trace > 4 && replicated)
1181                         trace_prt(1, "  nfsmount: examining %s\n", remname);
1182 
1183                 /*
1184                  * If it's cached we need to get cachefs to mount it.
1185                  */
1186                 if (cached) {
1187                         char *copts = opts;
1188 
1189                         /*
1190                          * If we started with a URL we need to turn on
1191                          * -o public if not on already
1192                          */
1193                         if (use_pubfh == FALSE &&
1194                             (mfs->mfs_flags & MFS_FH_VIA_WEBNFS)) {
1195 
1196                                 copts = malloc(strlen(opts) +
1197                                     strlen(",public")+1);
1198 
1199                                 if (copts == NULL) {
1200                                         syslog(LOG_ERR, "nfsmount: no memory");
1201                                         last_error = NFSERR_IO;
1202                                         goto out;
1203                                 }
1204 
1205                                 strcpy(copts, opts);
1206 
1207                                 if (strlen(copts) != 0)
1208                                         strcat(copts, ",");
1209 
1210                                 strcat(copts, "public");
1211                         }
1212 
1213                         last_error = mount_generic(remname, MNTTYPE_CACHEFS,
1214                             copts, mntpnt, overlay);
1215 
1216                         if (copts != opts)
1217                                 free(copts);
1218 
1219                         if (last_error) {
1220                                 skipentry = 1;
1221                                 mfs->mfs_ignore = 1;
1222                                 continue;
1223                         }
1224                         goto out;
1225                 }
1226 
1227                 if (mfs->mfs_args == NULL) {
1228 
1229                         /*
1230                          * Allocate nfs_args structure
1231                          */
1232                         argp = (struct nfs_args *)
1233                             malloc(sizeof (struct nfs_args));
1234 
1235                         if (!argp) {
1236                                 syslog(LOG_ERR, "nfsmount: no memory");
1237                                 last_error = NFSERR_IO;
1238                                 goto out;
1239                         }
1240 
1241                         (void) memset(argp, 0, sizeof (*argp));
1242 
1243                         /*
1244                          * RDMA support
1245                          * By now Mount argument struct has been allocated,
1246                          * either a pub_fh path will be taken or the regular
1247                          * one. So here if a protocol was specified and it
1248                          * was not rdma we let it be, else we set DO_RDMA.
1249                          * If no proto was there we advise on trying RDMA.
1250                          */
1251                         if (nfs_proto) {
1252                                 if (strcmp(nfs_proto, "rdma") == 0) {
1253                                         free(nfs_proto);
1254                                         nfs_proto = NULL;
1255                                         argp->flags |= NFSMNT_DORDMA;
1256                                 }
1257                         } else
1258                                 argp->flags |= NFSMNT_TRYRDMA;
1259                 } else {
1260                         argp = mfs->mfs_args;
1261                         mfs->mfs_args = NULL;
1262 
1263                         /*
1264                          * Skip entry if we already have file handle but the
1265                          * NFS version is wrong.
1266                          */
1267                         if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) &&
1268                             mfs->mfs_version != nfsvers) {
1269 
1270                                 free(argp);
1271                                 skipentry = 1;
1272                                 mfs->mfs_ignore = 1;
1273                                 continue;
1274                         }
1275                 }
1276 
1277                 prevhead = head;
1278                 prevtail = tail;
1279                 if (!head)
1280                         head = tail = argp;
1281                 else
1282                         tail = tail->nfs_ext_u.nfs_extB.next = argp;
1283 
1284                 /*
1285                  * WebNFS and NFSv4 behave similarly in that they
1286                  * don't use the mount protocol.  Therefore, avoid
1287                  * mount protocol like things when version 4 is being
1288                  * used.
1289                  */
1290                 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1291                     nfsvers != NFS_V4) {
1292                         timeout.tv_usec = 0;
1293                         timeout.tv_sec = rpc_timeout;
1294                         rpc_stat = RPC_TIMEDOUT;
1295 
1296                         /* Create the client handle. */
1297 
1298                         if (trace > 1) {
1299                                 trace_prt(1,
1300                                     "  nfsmount: Get mount version: request "
1301                                     "vers=%d min=%d\n", vers, versmin);
1302                         }
1303 
1304                         while ((cl = clnt_create_vers(host, MOUNTPROG, &outvers,
1305                             versmin, vers, "udp")) == NULL) {
1306                                 if (trace > 4) {
1307                                         trace_prt(1,
1308                                             "  nfsmount: Can't get mount "
1309                                             "version: rpcerr=%d\n",
1310                                             rpc_createerr.cf_stat);
1311                                 }
1312                                 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
1313                                     rpc_createerr.cf_stat == RPC_TIMEDOUT)
1314                                         break;
1315 
1316                         /*
1317                          * backoff and return lower version to retry the ping.
1318                          * XXX we should be more careful and handle
1319                          * RPC_PROGVERSMISMATCH here, because that error
1320                          * is handled in clnt_create_vers(). It's not done to
1321                          * stay in sync with the nfs mount command.
1322                          */
1323                                 vers--;
1324                                 if (vers < versmin)
1325                                         break;
1326                                 if (trace > 4) {
1327                                         trace_prt(1,
1328                                             "  nfsmount: Try version=%d\n",
1329                                             vers);
1330                                 }
1331                         }
1332 
1333                         if (cl == NULL) {
1334                                 free(argp);
1335                                 head = prevhead;
1336                                 tail = prevtail;
1337                                 if (tail)
1338                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1339                                 last_error = NFSERR_NOENT;
1340 
1341                                 if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST &&
1342                                     rpc_createerr.cf_stat !=
1343                                     RPC_PROGVERSMISMATCH &&
1344                                     retries-- > 0) {
1345                                         DELAY(delay);
1346                                         goto retry;
1347                                 }
1348 
1349                                 syslog(loglevel, "%s %s", host,
1350                                     clnt_spcreateerror(
1351                                     "server not responding"));
1352                                 skipentry = 1;
1353                                 mfs->mfs_ignore = 1;
1354                                 continue;
1355                         }
1356                         if (trace > 1) {
1357                                 trace_prt(1,
1358                                     "   nfsmount: mount version=%d\n", outvers);
1359                         }
1360 #ifdef MALLOC_DEBUG
1361                         add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
1362                         add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1363                             __FILE__, __LINE__);
1364 #endif
1365 
1366                         if (__clnt_bindresvport(cl) < 0) {
1367                                 free(argp);
1368                                 head = prevhead;
1369                                 tail = prevtail;
1370                                 if (tail)
1371                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1372                                 last_error = NFSERR_NOENT;
1373 
1374                                 if (retries-- > 0) {
1375                                         destroy_auth_client_handle(cl);
1376                                         DELAY(delay);
1377                                         goto retry;
1378                                 }
1379 
1380                                 syslog(loglevel, "mount %s: %s", host,
1381                                     "Couldn't bind to reserved port");
1382                                 destroy_auth_client_handle(cl);
1383                                 skipentry = 1;
1384                                 mfs->mfs_ignore = 1;
1385                                 continue;
1386                         }
1387 
1388 #ifdef MALLOC_DEBUG
1389                         drop_alloc("AUTH_HANDLE", cl->cl_auth,
1390                             __FILE__, __LINE__);
1391 #endif
1392                         AUTH_DESTROY(cl->cl_auth);
1393                         if ((cl->cl_auth = authsys_create_default()) == NULL) {
1394                                 free(argp);
1395                                 head = prevhead;
1396                                 tail = prevtail;
1397                                 if (tail)
1398                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1399                                 last_error = NFSERR_NOENT;
1400 
1401                                 if (retries-- > 0) {
1402                                         destroy_auth_client_handle(cl);
1403                                         DELAY(delay);
1404                                         goto retry;
1405                                 }
1406 
1407                                 syslog(loglevel, "mount %s: %s", host,
1408                                     "Failed creating default auth handle");
1409                                 destroy_auth_client_handle(cl);
1410                                 skipentry = 1;
1411                                 mfs->mfs_ignore = 1;
1412                                 continue;
1413                         }
1414 #ifdef MALLOC_DEBUG
1415                         add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1416                             __FILE__, __LINE__);
1417 #endif
1418                 } else
1419                         cl = NULL;
1420 
1421                 /*
1422                  * set security options
1423                  */
1424                 sec_opt = 0;
1425                 (void) memset(&nfs_sec, 0, sizeof (nfs_sec));
1426                 if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
1427                         if (++sec_opt > 1) {
1428                                 syslog(loglevel,
1429                                     "conflicting security options for %s",
1430                                     remname);
1431                                 free(argp);
1432                                 head = prevhead;
1433                                 tail = prevtail;
1434                                 if (tail)
1435                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1436                                 last_error = NFSERR_IO;
1437                                 destroy_auth_client_handle(cl);
1438                                 skipentry = 1;
1439                                 mfs->mfs_ignore = 1;
1440                                 continue;
1441                         }
1442                         if (nfs_getseconfig_byname("dh", &nfs_sec)) {
1443                                 syslog(loglevel,
1444                                     "error getting dh information from %s",
1445                                     NFSSEC_CONF);
1446                                 free(argp);
1447                                 head = prevhead;
1448                                 tail = prevtail;
1449                                 if (tail)
1450                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1451                                 last_error = NFSERR_IO;
1452                                 destroy_auth_client_handle(cl);
1453                                 skipentry = 1;
1454                                 mfs->mfs_ignore = 1;
1455                                 continue;
1456                         }
1457                 }
1458 
1459                 nfs_flavor = NULL;
1460                 if (hasmntopt(&m, MNTOPT_SEC) != NULL) {
1461                         if ((str_opt(&m, MNTOPT_SEC, &nfs_flavor)) == -1) {
1462                                 syslog(LOG_ERR, "nfsmount: no memory");
1463                                 last_error = NFSERR_IO;
1464                                 destroy_auth_client_handle(cl);
1465                                 goto out;
1466                         }
1467                 }
1468 
1469                 if (nfs_flavor) {
1470                         if (++sec_opt > 1) {
1471                                 syslog(loglevel,
1472                                     "conflicting security options for %s",
1473                                     remname);
1474                                 free(nfs_flavor);
1475                                 free(argp);
1476                                 head = prevhead;
1477                                 tail = prevtail;
1478                                 if (tail)
1479                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1480                                 last_error = NFSERR_IO;
1481                                 destroy_auth_client_handle(cl);
1482                                 skipentry = 1;
1483                                 mfs->mfs_ignore = 1;
1484                                 continue;
1485                         }
1486                         if (nfs_getseconfig_byname(nfs_flavor, &nfs_sec)) {
1487                                 syslog(loglevel,
1488                                     "error getting %s information from %s",
1489                                     nfs_flavor, NFSSEC_CONF);
1490                                 free(nfs_flavor);
1491                                 free(argp);
1492                                 head = prevhead;
1493                                 tail = prevtail;
1494                                 if (tail)
1495                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1496                                 last_error = NFSERR_IO;
1497                                 destroy_auth_client_handle(cl);
1498                                 skipentry = 1;
1499                                 mfs->mfs_ignore = 1;
1500                                 continue;
1501                         }
1502                         free(nfs_flavor);
1503                 }
1504 
1505                 posix = (nfsvers != NFS_V4 &&
1506                     hasmntopt(&m, MNTOPT_POSIX) != NULL) ? 1 : 0;
1507 
1508                 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1509                     nfsvers != NFS_V4) {
1510                         bool_t give_up_on_mnt;
1511                         bool_t got_mnt_error;
1512                 /*
1513                  * If we started with a URL, if first byte of path is not "/",
1514                  * then the mount will likely fail, so we should try again
1515                  * with a prepended "/".
1516                  */
1517                         if (mfs->mfs_flags & MFS_ALLOC_DIR && *dir != '/')
1518                                 give_up_on_mnt = FALSE;
1519                         else
1520                                 give_up_on_mnt = TRUE;
1521 
1522                         got_mnt_error = FALSE;
1523 
1524 try_mnt_slash:
1525                         if (got_mnt_error == TRUE) {
1526                                 int i, l;
1527 
1528                                 give_up_on_mnt = TRUE;
1529                                 l = strlen(dir);
1530 
1531                                 /*
1532                                  * Insert a "/" to front of mfs_dir.
1533                                  */
1534                                 for (i = l; i > 0; i--)
1535                                         dir[i] = dir[i-1];
1536 
1537                                 dir[0] = '/';
1538                         }
1539 
1540                         /* Get fhandle of remote path from server's mountd */
1541 
1542                         switch (outvers) {
1543                         case MOUNTVERS:
1544                                 if (posix) {
1545                                         free(argp);
1546                                         head = prevhead;
1547                                         tail = prevtail;
1548                                         if (tail)
1549                                                 tail->nfs_ext_u.nfs_extB.next =
1550                                                     NULL;
1551                                         last_error = NFSERR_NOENT;
1552                                         syslog(loglevel,
1553                                             "can't get posix info for %s",
1554                                             host);
1555                                         destroy_auth_client_handle(cl);
1556                                         skipentry = 1;
1557                                         mfs->mfs_ignore = 1;
1558                                         continue;
1559                                 }
1560                     /* FALLTHRU */
1561                         case MOUNTVERS_POSIX:
1562                                 if (nfsvers == NFS_V3) {
1563                                         free(argp);
1564                                         head = prevhead;
1565                                         tail = prevtail;
1566                                         if (tail)
1567                                                 tail->nfs_ext_u.nfs_extB.next =
1568                                                     NULL;
1569                                         last_error = NFSERR_NOENT;
1570                                         syslog(loglevel,
1571                                             "%s doesn't support NFS Version 3",
1572                                             host);
1573                                         destroy_auth_client_handle(cl);
1574                                         skipentry = 1;
1575                                         mfs->mfs_ignore = 1;
1576                                         continue;
1577                                 }
1578                                 rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1579                                     xdr_dirpath, (caddr_t)&dir,
1580                                     xdr_fhstatus, (caddr_t)&fhs, timeout);
1581                                 if (rpc_stat != RPC_SUCCESS) {
1582 
1583                                         if (give_up_on_mnt == FALSE) {
1584                                                 got_mnt_error = TRUE;
1585                                                 goto try_mnt_slash;
1586                                         }
1587 
1588                                 /*
1589                                  * Given the way "clnt_sperror" works, the "%s"
1590                                  * immediately following the "not responding"
1591                                  * is correct.
1592                                  */
1593                                         free(argp);
1594                                         head = prevhead;
1595                                         tail = prevtail;
1596                                         if (tail)
1597                                                 tail->nfs_ext_u.nfs_extB.next =
1598                                                     NULL;
1599                                         last_error = NFSERR_NOENT;
1600 
1601                                         if (retries-- > 0) {
1602                                                 destroy_auth_client_handle(cl);
1603                                                 DELAY(delay);
1604                                                 goto retry;
1605                                         }
1606 
1607                                         if (trace > 3) {
1608                                                 trace_prt(1,
1609                                                     "  nfsmount: mount RPC "
1610                                                     "failed for %s\n",
1611                                                     host);
1612                                         }
1613                                         syslog(loglevel,
1614                                             "%s server not responding%s",
1615                                             host, clnt_sperror(cl, ""));
1616                                         destroy_auth_client_handle(cl);
1617                                         skipentry = 1;
1618                                         mfs->mfs_ignore = 1;
1619                                         continue;
1620                                 }
1621                                 if ((errno = fhs.fhs_status) != MNT_OK)  {
1622 
1623                                         if (give_up_on_mnt == FALSE) {
1624                                                 got_mnt_error = TRUE;
1625                                                 goto try_mnt_slash;
1626                                         }
1627 
1628                                         free(argp);
1629                                         head = prevhead;
1630                                         tail = prevtail;
1631                                         if (tail)
1632                                                 tail->nfs_ext_u.nfs_extB.next =
1633                                                     NULL;
1634                                         if (errno == EACCES) {
1635                                                 status = NFSERR_ACCES;
1636                                         } else {
1637                                                 syslog(loglevel, "%s: %m",
1638                                                     host);
1639                                                 status = NFSERR_IO;
1640                                         }
1641                                         if (trace > 3) {
1642                                                 trace_prt(1,
1643                                                     "  nfsmount: mount RPC gave"
1644                                                     " %d for %s:%s\n",
1645                                                     errno, host, dir);
1646                                         }
1647                                         last_error = status;
1648                                         destroy_auth_client_handle(cl);
1649                                         skipentry = 1;
1650                                         mfs->mfs_ignore = 1;
1651                                         continue;
1652                                 }
1653                                 argp->fh = malloc((sizeof (fhandle)));
1654                                 if (!argp->fh) {
1655                                         syslog(LOG_ERR, "nfsmount: no memory");
1656                                         last_error = NFSERR_IO;
1657                                         destroy_auth_client_handle(cl);
1658                                         goto out;
1659                                 }
1660                                 (void) memcpy(argp->fh,
1661                                     &fhs.fhstatus_u.fhs_fhandle,
1662                                     sizeof (fhandle));
1663                                 break;
1664                         case MOUNTVERS3:
1665                                 posix = 0;
1666                                 (void) memset((char *)&res3, '\0',
1667                                     sizeof (res3));
1668                                 rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1669                                     xdr_dirpath, (caddr_t)&dir,
1670                                     xdr_mountres3, (caddr_t)&res3, timeout);
1671                                 if (rpc_stat != RPC_SUCCESS) {
1672 
1673                                         if (give_up_on_mnt == FALSE) {
1674                                                 got_mnt_error = TRUE;
1675                                                 goto try_mnt_slash;
1676                                         }
1677 
1678                                 /*
1679                                  * Given the way "clnt_sperror" works, the "%s"
1680                                  * immediately following the "not responding"
1681                                  * is correct.
1682                                  */
1683                                         free(argp);
1684                                         head = prevhead;
1685                                         tail = prevtail;
1686                                         if (tail)
1687                                                 tail->nfs_ext_u.nfs_extB.next =
1688                                                     NULL;
1689                                         last_error = NFSERR_NOENT;
1690 
1691                                         if (retries-- > 0) {
1692                                                 destroy_auth_client_handle(cl);
1693                                                 DELAY(delay);
1694                                                 goto retry;
1695                                         }
1696 
1697                                         if (trace > 3) {
1698                                                 trace_prt(1,
1699                                                     "  nfsmount: mount RPC "
1700                                                     "failed for %s\n",
1701                                                     host);
1702                                         }
1703                                         syslog(loglevel,
1704                                             "%s server not responding%s",
1705                                             remname, clnt_sperror(cl, ""));
1706                                         destroy_auth_client_handle(cl);
1707                                         skipentry = 1;
1708                                         mfs->mfs_ignore = 1;
1709                                         continue;
1710                                 }
1711                                 if ((errno = res3.fhs_status) != MNT_OK)  {
1712 
1713                                         if (give_up_on_mnt == FALSE) {
1714                                                 got_mnt_error = TRUE;
1715                                                 goto try_mnt_slash;
1716                                         }
1717 
1718                                         free(argp);
1719                                         head = prevhead;
1720                                         tail = prevtail;
1721                                         if (tail)
1722                                                 tail->nfs_ext_u.nfs_extB.next =
1723                                                     NULL;
1724                                         if (errno == EACCES) {
1725                                                 status = NFSERR_ACCES;
1726                                         } else {
1727                                                 syslog(loglevel, "%s: %m",
1728                                                     remname);
1729                                                 status = NFSERR_IO;
1730                                         }
1731                                         if (trace > 3) {
1732                                                 trace_prt(1,
1733                                                     "  nfsmount: mount RPC gave"
1734                                                     " %d for %s:%s\n",
1735                                                     errno, host, dir);
1736                                         }
1737                                         last_error = status;
1738                                         destroy_auth_client_handle(cl);
1739                                         skipentry = 1;
1740                                         mfs->mfs_ignore = 1;
1741                                         continue;
1742                                 }
1743 
1744                         /*
1745                          *  Negotiate the security flavor for nfs_mount
1746                          */
1747                                 auths = res3.mountres3_u.mountinfo.
1748                                     auth_flavors.auth_flavors_val;
1749                                 count = res3.mountres3_u.mountinfo.
1750                                     auth_flavors.auth_flavors_len;
1751 
1752                                 if (sec_opt) {
1753                                         for (i = 0; i < count; i++)
1754                                                 if (auths[i] ==
1755                                                     nfs_sec.sc_nfsnum) {
1756                                                         break;
1757                                                 }
1758                                         if (i >= count) {
1759                                                 syslog(LOG_ERR,
1760                                                     "%s: does not support "
1761                                                     "security \"%s\"\n",
1762                                                     remname, nfs_sec.sc_name);
1763                                                 clnt_freeres(cl, xdr_mountres3,
1764                                                     (caddr_t)&res3);
1765                                                 free(argp);
1766                                                 head = prevhead;
1767                                                 tail = prevtail;
1768                                                 if (tail)
1769                                                         tail->nfs_ext_u.
1770                                                             nfs_extB.next =
1771                                                             NULL;
1772                                                 last_error = NFSERR_IO;
1773                                                 destroy_auth_client_handle(cl);
1774                                                 skipentry = 1;
1775                                                 mfs->mfs_ignore = 1;
1776                                                 continue;
1777                                         }
1778                                 } else if (count > 0) {
1779                                         for (i = 0; i < count; i++) {
1780                                                 if (!(scerror =
1781                                                     nfs_getseconfig_bynumber(
1782                                                     auths[i], &nfs_sec))) {
1783                                                         sec_opt++;
1784                                                         break;
1785                                                 }
1786                                         }
1787                                         if (i >= count) {
1788                                                 if (nfs_syslog_scerr(scerror,
1789                                                     scerror_msg)
1790                                                     != -1) {
1791                                                         syslog(LOG_ERR,
1792                                                             "%s cannot be "
1793                                                             "mounted because it"
1794                                                             " is shared with "
1795                                                             "security flavor %d"
1796                                                             " which %s",
1797                                                             remname,
1798                                                             auths[i-1],
1799                                                             scerror_msg);
1800                                                 }
1801                                                 clnt_freeres(cl, xdr_mountres3,
1802                                                     (caddr_t)&res3);
1803                                                 free(argp);
1804                                                 head = prevhead;
1805                                                 tail = prevtail;
1806                                                 if (tail)
1807                                                         tail->nfs_ext_u.
1808                                                             nfs_extB.next =
1809                                                             NULL;
1810                                                 last_error = NFSERR_IO;
1811                                                 destroy_auth_client_handle(cl);
1812                                                 skipentry = 1;
1813                                                 mfs->mfs_ignore = 1;
1814                                                 continue;
1815                                                 }
1816                                 }
1817 
1818                                 fh3.fh3_length =
1819                                     res3.mountres3_u.mountinfo.fhandle.
1820                                     fhandle3_len;
1821                                 (void) memcpy(fh3.fh3_u.data,
1822                                     res3.mountres3_u.mountinfo.fhandle.
1823                                     fhandle3_val,
1824                                     fh3.fh3_length);
1825                                 clnt_freeres(cl, xdr_mountres3,
1826                                     (caddr_t)&res3);
1827                                 argp->fh = malloc(sizeof (nfs_fh3));
1828                                 if (!argp->fh) {
1829                                         syslog(LOG_ERR, "nfsmount: no memory");
1830                                         last_error = NFSERR_IO;
1831                                         destroy_auth_client_handle(cl);
1832                                         goto out;
1833                                 }
1834                                 (void) memcpy(argp->fh, &fh3, sizeof (nfs_fh3));
1835                                 break;
1836                         default:
1837                                 free(argp);
1838                                 head = prevhead;
1839                                 tail = prevtail;
1840                                 if (tail)
1841                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1842                                 last_error = NFSERR_NOENT;
1843                                 syslog(loglevel,
1844                                     "unknown MOUNT version %ld on %s",
1845                                     vers, remname);
1846                                 destroy_auth_client_handle(cl);
1847                                 skipentry = 1;
1848                                 mfs->mfs_ignore = 1;
1849                                 continue;
1850                         } /* switch */
1851                 }
1852                 if (nfsvers == NFS_V4) {
1853                         argp->fh = strdup(dir);
1854                         if (argp->fh == NULL) {
1855                                 syslog(LOG_ERR, "nfsmount: no memory");
1856                                 last_error = NFSERR_IO;
1857                                 goto out;
1858                         }
1859                 }
1860 
1861                 if (trace > 4)
1862                         trace_prt(1, "  nfsmount: have %s filehandle for %s\n",
1863                             fstype, remname);
1864 
1865                 argp->flags |= NFSMNT_NEWARGS;
1866                 argp->flags |= NFSMNT_INT;   /* default is "intr" */
1867                 argp->flags |= NFSMNT_HOSTNAME;
1868                 argp->hostname = strdup(host);
1869                 if (argp->hostname == NULL) {
1870                         syslog(LOG_ERR, "nfsmount: no memory");
1871                         last_error = NFSERR_IO;
1872                         goto out;
1873                 }
1874 
1875                 /*
1876                  * In this case, we want NFSv4 to behave like
1877                  * non-WebNFS so that we get the server address.
1878                  */
1879                 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) {
1880                         nconf = NULL;
1881 
1882                         if (nfs_port != 0)
1883                                 thisport = nfs_port;
1884                         else
1885                                 thisport = mfs->mfs_port;
1886 
1887                         /*
1888                          * For NFSv4, we want to avoid rpcbind, so call
1889                          * get_server_netinfo() directly to tell it that
1890                          * we want to go "direct_to_server".  Otherwise,
1891                          * do what has always been done.
1892                          */
1893                         if (nfsvers == NFS_V4) {
1894                                 enum clnt_stat cstat;
1895 
1896                                 argp->addr = get_server_netinfo(SERVER_ADDR,
1897                                     host, NFS_PROGRAM, nfsvers, NULL,
1898                                     &nconf, nfs_proto, thisport, NULL,
1899                                     NULL, TRUE, NULL, &cstat);
1900                         } else {
1901                                 argp->addr = get_addr(host, NFS_PROGRAM,
1902                                     nfsvers, &nconf, nfs_proto,
1903                                     thisport, NULL);
1904                         }
1905 
1906                         if (argp->addr == NULL) {
1907                                 if (argp->hostname)
1908                                         free(argp->hostname);
1909                                 free(argp->fh);
1910                                 free(argp);
1911                                 head = prevhead;
1912                                 tail = prevtail;
1913                                 if (tail)
1914                                         tail->nfs_ext_u.nfs_extB.next = NULL;
1915                                 last_error = NFSERR_NOENT;
1916 
1917                                 if (retries-- > 0) {
1918                                         destroy_auth_client_handle(cl);
1919                                         DELAY(delay);
1920                                         goto retry;
1921                                 }
1922 
1923                                 syslog(loglevel, "%s: no NFS service", host);
1924                                 destroy_auth_client_handle(cl);
1925                                 skipentry = 1;
1926                                 mfs->mfs_ignore = 1;
1927                                 continue;
1928                         }
1929                         if (trace > 4)
1930                                 trace_prt(1,
1931                                     "\tnfsmount: have net address for %s\n",
1932                                     remname);
1933 
1934                 } else {
1935                         nconf = mfs->mfs_nconf;
1936                         mfs->mfs_nconf = NULL;
1937                 }
1938 
1939                 argp->flags |= NFSMNT_KNCONF;
1940                 argp->knconf = get_knconf(nconf);
1941                 if (argp->knconf == NULL) {
1942                         netbuf_free(argp->addr);
1943                         freenetconfigent(nconf);
1944                         if (argp->hostname)
1945                                 free(argp->hostname);
1946                         free(argp->fh);
1947                         free(argp);
1948                         head = prevhead;
1949                         tail = prevtail;
1950                         if (tail)
1951                                 tail->nfs_ext_u.nfs_extB.next = NULL;
1952                         last_error = NFSERR_NOSPC;
1953                         destroy_auth_client_handle(cl);
1954                         skipentry = 1;
1955                         mfs->mfs_ignore = 1;
1956                         continue;
1957                 }
1958                 if (trace > 4)
1959                         trace_prt(1,
1960                             "\tnfsmount: have net config for %s\n",
1961                             remname);
1962 
1963                 if (hasmntopt(&m, MNTOPT_SOFT) != NULL) {
1964                         argp->flags |= NFSMNT_SOFT;
1965                 }
1966                 if (hasmntopt(&m, MNTOPT_NOINTR) != NULL) {
1967                         argp->flags &= ~(NFSMNT_INT);
1968                 }
1969                 if (hasmntopt(&m, MNTOPT_NOAC) != NULL) {
1970                         argp->flags |= NFSMNT_NOAC;
1971                 }
1972                 if (hasmntopt(&m, MNTOPT_NOCTO) != NULL) {
1973                         argp->flags |= NFSMNT_NOCTO;
1974                 }
1975                 if (hasmntopt(&m, MNTOPT_FORCEDIRECTIO) != NULL) {
1976                         argp->flags |= NFSMNT_DIRECTIO;
1977                 }
1978                 if (hasmntopt(&m, MNTOPT_NOFORCEDIRECTIO) != NULL) {
1979                         argp->flags &= ~(NFSMNT_DIRECTIO);
1980                 }
1981 
1982                 /*
1983                  * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1984                  */
1985                 if (mfssnego.snego_done) {
1986                         memcpy(&nfs_sec, &mfssnego.nfs_sec,
1987                             sizeof (seconfig_t));
1988                 } else if (!sec_opt) {
1989                         /*
1990                          * Get default security mode.
1991                          */
1992                         if (nfs_getseconfig_default(&nfs_sec)) {
1993                                 syslog(loglevel,
1994                                     "error getting default security entry\n");
1995                                 free_knconf(argp->knconf);
1996                                 netbuf_free(argp->addr);
1997                                 freenetconfigent(nconf);
1998                                 if (argp->hostname)
1999                                         free(argp->hostname);
2000                                 free(argp->fh);
2001                                 free(argp);
2002                                 head = prevhead;
2003                                 tail = prevtail;
2004                                 if (tail)
2005                                         tail->nfs_ext_u.nfs_extB.next = NULL;
2006                                 last_error = NFSERR_NOSPC;
2007                                 destroy_auth_client_handle(cl);
2008                                 skipentry = 1;
2009                                 mfs->mfs_ignore = 1;
2010                                 continue;
2011                         }
2012                         argp->flags |= NFSMNT_SECDEFAULT;
2013                 }
2014 
2015                 /*
2016                  * For AUTH_DH
2017                  * get the network address for the time service on
2018                  * the server.  If an RPC based time service is
2019                  * not available then try the IP time service.
2020                  *
2021                  * Eventurally, we want to move this code to nfs_clnt_secdata()
2022                  * when autod_nfs.c and mount.c can share the same
2023                  * get_the_addr/get_netconfig_info routine.
2024                  */
2025                 secflags = 0;
2026                 syncaddr = NULL;
2027                 retaddrs = NULL;
2028 
2029                 if (nfs_sec.sc_rpcnum == AUTH_DH || nfsvers == NFS_V4) {
2030                 /*
2031                  * If not using the public fh and not NFS_V4, we can try
2032                  * talking RPCBIND. Otherwise, assume that firewalls
2033                  * prevent us from doing that.
2034                  */
2035                         if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
2036                             nfsvers != NFS_V4) {
2037                                 enum clnt_stat cstat;
2038                                 syncaddr = get_server_netinfo(SERVER_ADDR,
2039                                     host, RPCBPROG, RPCBVERS, NULL, &nconf,
2040                                     NULL, 0, NULL, NULL, FALSE, NULL, &cstat);
2041                         }
2042 
2043                         if (syncaddr != NULL) {
2044                                 /* for flags in sec_data */
2045                                 secflags |= AUTH_F_RPCTIMESYNC;
2046                         } else {
2047                                 struct nd_hostserv hs;
2048                                 int error;
2049 
2050                                 hs.h_host = host;
2051                                 hs.h_serv = "timserver";
2052                                 error = netdir_getbyname(nconf, &hs, &retaddrs);
2053 
2054                                 if (error != ND_OK &&
2055                                     nfs_sec.sc_rpcnum == AUTH_DH) {
2056                                         syslog(loglevel,
2057                                             "%s: secure: no time service\n",
2058                                             host);
2059                                         free_knconf(argp->knconf);
2060                                         netbuf_free(argp->addr);
2061                                         freenetconfigent(nconf);
2062                                         if (argp->hostname)
2063                                                 free(argp->hostname);
2064                                         free(argp->fh);
2065                                         free(argp);
2066                                         head = prevhead;
2067                                         tail = prevtail;
2068                                         if (tail)
2069                                                 tail->nfs_ext_u.nfs_extB.next =
2070                                                     NULL;
2071                                         last_error = NFSERR_IO;
2072                                         destroy_auth_client_handle(cl);
2073                                         skipentry = 1;
2074                                         mfs->mfs_ignore = 1;
2075                                         continue;
2076                                 }
2077 
2078                                 if (error == ND_OK)
2079                                         syncaddr = retaddrs->n_addrs;
2080 
2081                         /*
2082                          * For potential usage by NFS V4 when AUTH_DH
2083                          * is negotiated via SECINFO in the kernel.
2084                          */
2085                                 if (nfsvers == NFS_V4 && syncaddr &&
2086                                     host2netname(netname, host, NULL)) {
2087                                         argp->syncaddr =
2088                                             malloc(sizeof (struct netbuf));
2089                                         argp->syncaddr->buf =
2090                                             malloc(syncaddr->len);
2091                                         (void) memcpy(argp->syncaddr->buf,
2092                                             syncaddr->buf, syncaddr->len);
2093                                         argp->syncaddr->len = syncaddr->len;
2094                                         argp->syncaddr->maxlen =
2095                                             syncaddr->maxlen;
2096                                         argp->netname = strdup(netname);
2097                                         argp->flags |= NFSMNT_SECURE;
2098                                 }
2099                         } /* syncaddr */
2100                 } /* AUTH_DH */
2101 
2102                 /*
2103                  * TSOL notes: automountd in tsol extension
2104                  * has "read down" capability, i.e. we allow
2105                  * a user to trigger an nfs mount into a lower
2106                  * labeled zone. We achieve this by always having
2107                  * root issue the mount request so that the
2108                  * lookup ops can go past /zone/<zone_name>
2109                  * on the server side.
2110                  */
2111                 if (is_system_labeled())
2112                         nfs_sec.sc_uid = (uid_t)0;
2113                 else
2114                         nfs_sec.sc_uid = uid;
2115                 /*
2116                  * If AUTH_DH is a chosen flavor now, its data will be stored
2117                  * in the sec_data structure via nfs_clnt_secdata().
2118                  */
2119                 if (!(secdata = nfs_clnt_secdata(&nfs_sec, host, argp->knconf,
2120                     syncaddr, secflags))) {
2121                         syslog(LOG_ERR,
2122                             "errors constructing security related data\n");
2123                         if (secflags & AUTH_F_RPCTIMESYNC)
2124                                 netbuf_free(syncaddr);
2125                         else if (retaddrs)
2126                                 netdir_free(retaddrs, ND_ADDRLIST);
2127                         if (argp->syncaddr)
2128                                 netbuf_free(argp->syncaddr);
2129                         if (argp->netname)
2130                                 free(argp->netname);
2131                         if (argp->hostname)
2132                                 free(argp->hostname);
2133                         free_knconf(argp->knconf);
2134                         netbuf_free(argp->addr);
2135                         freenetconfigent(nconf);
2136                         free(argp->fh);
2137                         free(argp);
2138                         head = prevhead;
2139                         tail = prevtail;
2140                         if (tail)
2141                                 tail->nfs_ext_u.nfs_extB.next = NULL;
2142                         last_error = NFSERR_IO;
2143                         destroy_auth_client_handle(cl);
2144                         skipentry = 1;
2145                         mfs->mfs_ignore = 1;
2146                         continue;
2147                 }
2148                 NFS_ARGS_EXTB_secdata(*argp, secdata);
2149                 /* end of security stuff */
2150 
2151                 if (trace > 4)
2152                         trace_prt(1,
2153                             "  nfsmount: have secure info for %s\n", remname);
2154 
2155                 if (hasmntopt(&m, MNTOPT_GRPID) != NULL) {
2156                         argp->flags |= NFSMNT_GRPID;
2157                 }
2158                 if (nopt(&m, MNTOPT_RSIZE, &argp->rsize)) {
2159                         argp->flags |= NFSMNT_RSIZE;
2160                 }
2161                 if (nopt(&m, MNTOPT_WSIZE, &argp->wsize)) {
2162                         argp->flags |= NFSMNT_WSIZE;
2163                 }
2164                 if (nopt(&m, MNTOPT_TIMEO, &argp->timeo)) {
2165                         argp->flags |= NFSMNT_TIMEO;
2166                 }
2167                 if (nopt(&m, MNTOPT_RETRANS, &argp->retrans)) {
2168                         argp->flags |= NFSMNT_RETRANS;
2169                 }
2170                 if (nopt(&m, MNTOPT_ACTIMEO, &argp->acregmax)) {
2171                         argp->flags |= NFSMNT_ACREGMAX;
2172                         argp->flags |= NFSMNT_ACDIRMAX;
2173                         argp->flags |= NFSMNT_ACDIRMIN;
2174                         argp->flags |= NFSMNT_ACREGMIN;
2175                         argp->acdirmin = argp->acregmin = argp->acdirmax
2176                             = argp->acregmax;
2177                 } else {
2178                         if (nopt(&m, MNTOPT_ACREGMIN, &argp->acregmin)) {
2179                                 argp->flags |= NFSMNT_ACREGMIN;
2180                         }
2181                         if (nopt(&m, MNTOPT_ACREGMAX, &argp->acregmax)) {
2182                                 argp->flags |= NFSMNT_ACREGMAX;
2183                         }
2184                         if (nopt(&m, MNTOPT_ACDIRMIN, &argp->acdirmin)) {
2185                                 argp->flags |= NFSMNT_ACDIRMIN;
2186                         }
2187                         if (nopt(&m, MNTOPT_ACDIRMAX, &argp->acdirmax)) {
2188                                 argp->flags |= NFSMNT_ACDIRMAX;
2189                         }
2190                 }
2191 
2192                 if (posix) {
2193                         argp->pathconf = NULL;
2194                         if (error = get_pathconf(cl, dir, remname,
2195                             &argp->pathconf, retries)) {
2196                                 if (secflags & AUTH_F_RPCTIMESYNC)
2197                                         netbuf_free(syncaddr);
2198                                 else if (retaddrs)
2199                                         netdir_free(retaddrs, ND_ADDRLIST);
2200                                 free_knconf(argp->knconf);
2201                                 netbuf_free(argp->addr);
2202                                 freenetconfigent(nconf);
2203                                 nfs_free_secdata(
2204                                     argp->nfs_ext_u.nfs_extB.secdata);
2205                                 if (argp->syncaddr)
2206                                         netbuf_free(argp->syncaddr);
2207                                 if (argp->netname)
2208                                         free(argp->netname);
2209                                 if (argp->hostname)
2210                                         free(argp->hostname);
2211                                 free(argp->fh);
2212                                 free(argp);
2213                                 head = prevhead;
2214                                 tail = prevtail;
2215                                 if (tail)
2216                                         tail->nfs_ext_u.nfs_extB.next = NULL;
2217                                 last_error = NFSERR_IO;
2218 
2219                                 if (error == RET_RETRY && retries-- > 0) {
2220                                         destroy_auth_client_handle(cl);
2221                                         DELAY(delay);
2222                                         goto retry;
2223                                 }
2224 
2225                                 destroy_auth_client_handle(cl);
2226                                 skipentry = 1;
2227                                 mfs->mfs_ignore = 1;
2228                                 continue;
2229                         }
2230                         argp->flags |= NFSMNT_POSIX;
2231                         if (trace > 4)
2232                                 trace_prt(1,
2233                                     "  nfsmount: have pathconf for %s\n",
2234                                     remname);
2235                 }
2236 
2237                 /*
2238                  * free loop-specific data structures
2239                  */
2240                 destroy_auth_client_handle(cl);
2241                 freenetconfigent(nconf);
2242                 if (secflags & AUTH_F_RPCTIMESYNC)
2243                         netbuf_free(syncaddr);
2244                 else if (retaddrs)
2245                         netdir_free(retaddrs, ND_ADDRLIST);
2246 
2247                 /*
2248                  * Decide whether to use remote host's lockd or local locking.
2249                  * If we are using the public fh, we've already turned
2250                  * LLOCK on.
2251                  */
2252                 if (hasmntopt(&m, MNTOPT_LLOCK))
2253                         argp->flags |= NFSMNT_LLOCK;
2254                 if (!(argp->flags & NFSMNT_LLOCK) && nfsvers == NFS_VERSION &&
2255                     remote_lock(host, argp->fh)) {
2256                         syslog(loglevel, "No network locking on %s : "
2257                         "contact admin to install server change", host);
2258                         argp->flags |= NFSMNT_LLOCK;
2259                 }
2260 
2261                 /*
2262                  * Build a string for /etc/mnttab.
2263                  * If possible, coalesce strings with same 'dir' info.
2264                  */
2265                 if ((mfs->mfs_flags & MFS_URL) == 0) {
2266                         char *tmp;
2267 
2268                         if (mnttabcnt) {
2269                                 p = strrchr(mnttabtext, (int)':');
2270                                 if (!p || strcmp(p+1, dir) != 0) {
2271                                         mnttabcnt += strlen(remname) + 2;
2272                                 } else {
2273                                         *p = '\0';
2274                                         mnttabcnt += strlen(rhost) + 2;
2275                                 }
2276                                 if ((tmp = realloc(mnttabtext,
2277                                     mnttabcnt)) != NULL) {
2278                                         mnttabtext = tmp;
2279                                         strcat(mnttabtext, ",");
2280                                 } else {
2281                                         free(mnttabtext);
2282                                         mnttabtext = NULL;
2283                                 }
2284                         } else {
2285                                 mnttabcnt = strlen(remname) + 1;
2286                                 if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2287                                         mnttabtext[0] = '\0';
2288                         }
2289 
2290                         if (mnttabtext != NULL)
2291                                 strcat(mnttabtext, remname);
2292 
2293                 } else {
2294                         char *tmp;
2295                         int more_cnt = 0;
2296                         char sport[16];
2297 
2298                         more_cnt += strlen("nfs://");
2299                         more_cnt += strlen(mfs->mfs_host);
2300 
2301                         if (mfs->mfs_port != 0) {
2302                                 (void) sprintf(sport, ":%u", mfs->mfs_port);
2303                         } else
2304                                 sport[0] = '\0';
2305 
2306                         more_cnt += strlen(sport);
2307                         more_cnt += 1; /* "/" */
2308                         more_cnt += strlen(mfs->mfs_dir);
2309 
2310                         if (mnttabcnt) {
2311                                 more_cnt += 1; /* "," */
2312                                 mnttabcnt += more_cnt;
2313 
2314                                 if ((tmp = realloc(mnttabtext,
2315                                     mnttabcnt)) != NULL) {
2316                                         mnttabtext = tmp;
2317                                         strcat(mnttabtext, ",");
2318                                 } else {
2319                                         free(mnttabtext);
2320                                         mnttabtext = NULL;
2321                                 }
2322                         } else {
2323                                 mnttabcnt = more_cnt + 1;
2324                                 if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2325                                         mnttabtext[0] = '\0';
2326                         }
2327 
2328                         if (mnttabtext != NULL) {
2329                                 strcat(mnttabtext, "nfs://");
2330                                 strcat(mnttabtext, mfs->mfs_host);
2331                                 strcat(mnttabtext, sport);
2332                                 strcat(mnttabtext, "/");
2333                                 strcat(mnttabtext, mfs->mfs_dir);
2334                         }
2335                 }
2336 
2337                 if (!mnttabtext) {
2338                         syslog(LOG_ERR, "nfsmount: no memory");
2339                         last_error = NFSERR_IO;
2340                         goto out;
2341                 }
2342 
2343                 /*
2344                  * At least one entry, can call mount(2).
2345                  */
2346                 entries++;
2347 
2348                 /*
2349                  * If replication was defeated, don't do more work
2350                  */
2351                 if (!replicated)
2352                         break;
2353         }
2354 
2355 
2356         /*
2357          * Did we get through all possibilities without success?
2358          */
2359         if (!entries)
2360                 goto out;
2361 
2362         /* Make "xattr" the default if "noxattr" is not specified. */
2363         strcpy(mopts, opts);
2364         if (!hasmntopt(&m, MNTOPT_NOXATTR) && !hasmntopt(&m, MNTOPT_XATTR)) {
2365                 if (strlen(mopts) > 0)
2366                         strcat(mopts, ",");
2367                 strcat(mopts, "xattr");
2368         }
2369 
2370         /*
2371          * enable services as needed.
2372          */
2373         {
2374                 char **sl;
2375 
2376                 if (strcmp(fstype, MNTTYPE_NFS4) == 0)
2377                         sl = service_list_v4;
2378                 else
2379                         sl = service_list;
2380 
2381                 (void) _check_services(sl);
2382         }
2383 
2384         /*
2385          * Whew; do the mount, at last.
2386          */
2387         if (trace > 1) {
2388                 trace_prt(1, "  mount %s %s (%s)\n", mnttabtext, mntpnt, mopts);
2389         }
2390 
2391         /*
2392          * About to do a nfs mount, make sure the mount_to is set for
2393          * potential ephemeral mounts with NFSv4.
2394          */
2395         set_nfsv4_ephemeral_mount_to();
2396 
2397         /*
2398          * If no action list pointer then do the mount, otherwise
2399          * build the actions list pointer with the mount information.
2400          * so the mount can be done in the kernel.
2401          */
2402         if (alp == NULL) {
2403                 if (mount(mnttabtext, mntpnt, flags | MS_DATA, fstype,
2404                     head, sizeof (*head), mopts, MAX_MNTOPT_STR) < 0) {
2405                         if (trace > 1)
2406                                 trace_prt(1, "  Mount of %s on %s: %d\n",
2407                                     mnttabtext, mntpnt, errno);
2408                         if (errno != EBUSY || verbose)
2409                                 syslog(LOG_ERR,
2410                                 "Mount of %s on %s: %m", mnttabtext, mntpnt);
2411                         last_error = NFSERR_IO;
2412                         goto out;
2413                 }
2414 
2415                 last_error = NFS_OK;
2416                 if (stat(mntpnt, &stbuf) == 0) {
2417                         if (trace > 1) {
2418                                 trace_prt(1, "  mount %s dev=%x rdev=%x OK\n",
2419                                     mnttabtext, stbuf.st_dev, stbuf.st_rdev);
2420                         }
2421                 } else {
2422                         if (trace > 1) {
2423                                 trace_prt(1, "  mount %s OK\n", mnttabtext);
2424                                 trace_prt(1, "  stat of %s failed\n", mntpnt);
2425                         }
2426 
2427                 }
2428         } else {
2429                 alp->action.action = AUTOFS_MOUNT_RQ;
2430                 alp->action.action_list_entry_u.mounta.spec =
2431                     strdup(mnttabtext);
2432                 alp->action.action_list_entry_u.mounta.dir = strdup(mntpnt);
2433                 alp->action.action_list_entry_u.mounta.flags =
2434                     flags | MS_DATA;
2435                 alp->action.action_list_entry_u.mounta.fstype =
2436                     strdup(fstype);
2437                 alp->action.action_list_entry_u.mounta.dataptr = (char *)head;
2438                 alp->action.action_list_entry_u.mounta.datalen =
2439                     sizeof (*head);
2440                 mntopts = malloc(strlen(mopts) + 1);
2441                 strcpy(mntopts, mopts);
2442                 mntopts[strlen(mopts)] = '\0';
2443                 alp->action.action_list_entry_u.mounta.optptr = mntopts;
2444                 alp->action.action_list_entry_u.mounta.optlen =
2445                     strlen(mntopts) + 1;
2446                 last_error = NFS_OK;
2447                 goto ret;
2448         }
2449 
2450 out:
2451         argp = head;
2452         while (argp) {
2453                 if (argp->pathconf)
2454                         free(argp->pathconf);
2455                 free_knconf(argp->knconf);
2456                 netbuf_free(argp->addr);
2457                 if (argp->syncaddr)
2458                         netbuf_free(argp->syncaddr);
2459                 if (argp->netname) {
2460                         free(argp->netname);
2461                 }
2462                 if (argp->hostname)
2463                         free(argp->hostname);
2464                 nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
2465                 free(argp->fh);
2466                 head = argp;
2467                 argp = argp->nfs_ext_u.nfs_extB.next;
2468                 free(head);
2469         }
2470 ret:
2471         if (nfs_proto)
2472                 free(nfs_proto);
2473         if (mnttabtext)
2474                 free(mnttabtext);
2475 
2476         for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
2477 
2478                 if (mfs->mfs_flags & MFS_ALLOC_DIR) {
2479                         free(mfs->mfs_dir);
2480                         mfs->mfs_dir = NULL;
2481                         mfs->mfs_flags &= ~MFS_ALLOC_DIR;
2482                 }
2483 
2484                 if (mfs->mfs_args != NULL && alp == NULL) {
2485                         free(mfs->mfs_args);
2486                         mfs->mfs_args = NULL;
2487                 }
2488 
2489                 if (mfs->mfs_nconf != NULL) {
2490                         freenetconfigent(mfs->mfs_nconf);
2491                         mfs->mfs_nconf = NULL;
2492                 }
2493         }
2494 
2495         return (last_error);
2496 }
2497 
2498 /*
2499  * get_pathconf(cl, path, fsname, pcnf, cretries)
2500  * ugliness that requires that ppathcnf and pathcnf stay consistent
2501  * cretries is a copy of retries used to determine when to syslog
2502  * on retry situations.
2503  */
2504 static int
2505 get_pathconf(CLIENT *cl, char *path, char *fsname, struct pathcnf **pcnf,
2506         int cretries)
2507 {
2508         struct ppathcnf *p = NULL;
2509         enum clnt_stat rpc_stat;
2510         struct timeval timeout;
2511 
2512         p = (struct ppathcnf *)malloc(sizeof (struct ppathcnf));
2513         if (p == NULL) {
2514                 syslog(LOG_ERR, "get_pathconf: Out of memory");
2515                 return (RET_ERR);
2516         }
2517         memset((caddr_t)p, 0, sizeof (struct ppathcnf));
2518 
2519         timeout.tv_sec = 10;
2520         timeout.tv_usec = 0;
2521         rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
2522             xdr_dirpath, (caddr_t)&path, xdr_ppathcnf, (caddr_t)p, timeout);
2523         if (rpc_stat != RPC_SUCCESS) {
2524                 if (cretries-- <= 0) {
2525                         syslog(LOG_ERR,
2526                             "get_pathconf: %s: server not responding: %s",
2527                             fsname, clnt_sperror(cl, ""));
2528                 }
2529                 free(p);
2530                 return (RET_RETRY);
2531         }
2532         if (_PC_ISSET(_PC_ERROR, p->pc_mask)) {
2533                 syslog(LOG_ERR, "get_pathconf: no info for %s", fsname);
2534                 free(p);
2535                 return (RET_ERR);
2536         }
2537         *pcnf = (struct pathcnf *)p;
2538         return (RET_OK);
2539 }
2540 
2541 void
2542 netbuf_free(nb)
2543         struct netbuf *nb;
2544 {
2545         if (nb == NULL)
2546                 return;
2547         if (nb->buf)
2548                 free(nb->buf);
2549         free(nb);
2550 }
2551 
2552 #define SMALL_HOSTNAME          20
2553 #define SMALL_PROTONAME         10
2554 #define SMALL_PROTOFMLYNAME             10
2555 
2556 struct portmap_cache {
2557         int cache_prog;
2558         int cache_vers;
2559         time_t cache_time;
2560         char cache_small_hosts[SMALL_HOSTNAME + 1];
2561         char *cache_hostname;
2562         char *cache_proto;
2563         char *cache_protofmly;
2564         char cache_small_protofmly[SMALL_PROTOFMLYNAME + 1];
2565         char cache_small_proto[SMALL_PROTONAME + 1];
2566         struct netbuf cache_srv_addr;
2567         struct portmap_cache *cache_prev, *cache_next;
2568 };
2569 
2570 rwlock_t portmap_cache_lock;
2571 static int portmap_cache_valid_time = 30;
2572 struct portmap_cache *portmap_cache_head, *portmap_cache_tail;
2573 
2574 #ifdef MALLOC_DEBUG
2575 void
2576 portmap_cache_flush()
2577 {
2578         struct  portmap_cache *next = NULL, *cp;
2579 
2580         (void) rw_wrlock(&portmap_cache_lock);
2581         for (cp = portmap_cache_head; cp; cp = cp->cache_next) {
2582                 if (cp->cache_hostname != NULL &&
2583                     cp->cache_hostname !=
2584                     cp->cache_small_hosts)
2585                         free(cp->cache_hostname);
2586                 if (cp->cache_proto != NULL &&
2587                     cp->cache_proto !=
2588                     cp->cache_small_proto)
2589                         free(cp->cache_proto);
2590                 if (cp->cache_srv_addr.buf != NULL)
2591                         free(cp->cache_srv_addr.buf);
2592                 next = cp->cache_next;
2593                 free(cp);
2594         }
2595         portmap_cache_head = NULL;
2596         portmap_cache_tail = NULL;
2597         (void) rw_unlock(&portmap_cache_lock);
2598 }
2599 #endif
2600 
2601 /*
2602  * Returns 1 if the entry is found in the cache, 0 otherwise.
2603  */
2604 static int
2605 portmap_cache_lookup(hostname, prog, vers, nconf, addrp)
2606         char *hostname;
2607         rpcprog_t prog;
2608         rpcvers_t vers;
2609         struct netconfig *nconf;
2610         struct netbuf *addrp;
2611 {
2612         struct  portmap_cache *cachep, *prev, *next = NULL, *cp;
2613         int     retval = 0;
2614 
2615         timenow = time(NULL);
2616 
2617         (void) rw_rdlock(&portmap_cache_lock);
2618 
2619         /*
2620          * Increment the portmap cache counters for # accesses and lookups
2621          * Use a smaller factor (100 vs 1000 for the host cache) since
2622          * initial analysis shows this cache is looked up 10% that of the
2623          * host cache.
2624          */
2625 #ifdef CACHE_DEBUG
2626         portmap_cache_accesses++;
2627         portmap_cache_lookups++;
2628         if ((portmap_cache_lookups%100) == 0)
2629                 trace_portmap_cache();
2630 #endif /* CACHE_DEBUG */
2631 
2632         for (cachep = portmap_cache_head; cachep;
2633                 cachep = cachep->cache_next) {
2634                 if (timenow > cachep->cache_time) {
2635                         /*
2636                          * We stumbled across an entry in the cache which
2637                          * has timed out. Free up all the entries that
2638                          * were added before it, which will positionally
2639                          * be after this entry. And adjust neighboring
2640                          * pointers.
2641                          * When we drop the lock and re-acquire it, we
2642                          * need to start from the beginning.
2643                          */
2644                         (void) rw_unlock(&portmap_cache_lock);
2645                         (void) rw_wrlock(&portmap_cache_lock);
2646                         for (cp = portmap_cache_head;
2647                                 cp && (cp->cache_time >= timenow);
2648                                 cp = cp->cache_next)
2649                                 ;
2650                         if (cp == NULL)
2651                                 goto done;
2652                         /*
2653                          * Adjust the link of the predecessor.
2654                          * Make the tail point to the new last entry.
2655                          */
2656                         prev = cp->cache_prev;
2657                         if (prev == NULL) {
2658                                 portmap_cache_head = NULL;
2659                                 portmap_cache_tail = NULL;
2660                         } else {
2661                                 prev->cache_next = NULL;
2662                                 portmap_cache_tail = prev;
2663                         }
2664                         for (; cp; cp = next) {
2665                                 if (cp->cache_hostname != NULL &&
2666                                     cp->cache_hostname !=
2667                                     cp->cache_small_hosts)
2668                                         free(cp->cache_hostname);
2669                                 if (cp->cache_proto != NULL &&
2670                                     cp->cache_proto !=
2671                                     cp->cache_small_proto)
2672                                         free(cp->cache_proto);
2673                                 if (cp->cache_srv_addr.buf != NULL)
2674                                         free(cp->cache_srv_addr.buf);
2675                                 next = cp->cache_next;
2676                                 free(cp);
2677                         }
2678                         goto done;
2679                 }
2680                 if (cachep->cache_hostname == NULL ||
2681                     prog != cachep->cache_prog || vers != cachep->cache_vers ||
2682                     strcmp(nconf->nc_proto, cachep->cache_proto) != 0 ||
2683                     strcmp(nconf->nc_protofmly, cachep->cache_protofmly) != 0 ||
2684                     strcmp(hostname, cachep->cache_hostname) != 0)
2685                         continue;
2686                 /*
2687                  * Cache Hit.
2688                  */
2689 #ifdef CACHE_DEBUG
2690                 portmap_cache_hits++;   /* up portmap cache hit counter */
2691 #endif /* CACHE_DEBUG */
2692                 addrp->len = cachep->cache_srv_addr.len;
2693                 memcpy(addrp->buf, cachep->cache_srv_addr.buf, addrp->len);
2694                 retval = 1;
2695                 break;
2696         }
2697 done:
2698         (void) rw_unlock(&portmap_cache_lock);
2699         return (retval);
2700 }
2701 
2702 static void
2703 portmap_cache_enter(hostname, prog, vers, nconf, addrp)
2704         char *hostname;
2705         rpcprog_t prog;
2706         rpcvers_t vers;
2707         struct netconfig *nconf;
2708         struct netbuf *addrp;
2709 {
2710         struct portmap_cache *cachep;
2711         int protofmlylen;
2712         int protolen, hostnamelen;
2713 
2714         timenow = time(NULL);
2715 
2716         cachep = malloc(sizeof (struct portmap_cache));
2717         if (cachep == NULL)
2718                 return;
2719         memset((char *)cachep, 0, sizeof (*cachep));
2720 
2721         hostnamelen = strlen(hostname);
2722         if (hostnamelen <= SMALL_HOSTNAME)
2723                 cachep->cache_hostname = cachep->cache_small_hosts;
2724         else {
2725                 cachep->cache_hostname = malloc(hostnamelen + 1);
2726                 if (cachep->cache_hostname == NULL)
2727                         goto nomem;
2728         }
2729         strcpy(cachep->cache_hostname, hostname);
2730         protolen = strlen(nconf->nc_proto);
2731         if (protolen <= SMALL_PROTONAME)
2732                 cachep->cache_proto = cachep->cache_small_proto;
2733         else {
2734                 cachep->cache_proto = malloc(protolen + 1);
2735                 if (cachep->cache_proto == NULL)
2736                         goto nomem;
2737         }
2738         protofmlylen = strlen(nconf->nc_protofmly);
2739         if (protofmlylen <= SMALL_PROTOFMLYNAME)
2740                 cachep->cache_protofmly = cachep->cache_small_protofmly;
2741         else {
2742                 cachep->cache_protofmly = malloc(protofmlylen + 1);
2743                 if (cachep->cache_protofmly == NULL)
2744                         goto nomem;
2745         }
2746 
2747         strcpy(cachep->cache_proto, nconf->nc_proto);
2748         cachep->cache_prog = prog;
2749         cachep->cache_vers = vers;
2750         cachep->cache_time = timenow + portmap_cache_valid_time;
2751         cachep->cache_srv_addr.len = addrp->len;
2752         cachep->cache_srv_addr.buf = malloc(addrp->len);
2753         if (cachep->cache_srv_addr.buf == NULL)
2754                 goto nomem;
2755         memcpy(cachep->cache_srv_addr.buf, addrp->buf, addrp->maxlen);
2756         cachep->cache_prev = NULL;
2757         (void) rw_wrlock(&portmap_cache_lock);
2758         /*
2759          * There's a window in which we could have multiple threads making
2760          * the same cache entry. This can be avoided by walking the cache
2761          * once again here to check and see if there are duplicate entries
2762          * (after grabbing the write lock). This isn't fatal and I'm not
2763          * going to bother with this.
2764          */
2765 #ifdef CACHE_DEBUG
2766         portmap_cache_accesses++;       /* up portmap cache access counter */
2767 #endif /* CACHE_DEBUG */
2768         cachep->cache_next = portmap_cache_head;
2769         if (portmap_cache_head != NULL)
2770                 portmap_cache_head->cache_prev = cachep;
2771         portmap_cache_head = cachep;
2772         (void) rw_unlock(&portmap_cache_lock);
2773         return;
2774 
2775 nomem:
2776         syslog(LOG_ERR, "portmap_cache_enter: Memory allocation failed");
2777         if (cachep->cache_srv_addr.buf)
2778                 free(cachep->cache_srv_addr.buf);
2779         if (cachep->cache_proto && protolen > SMALL_PROTONAME)
2780                 free(cachep->cache_proto);
2781         if (cachep->cache_hostname && hostnamelen > SMALL_HOSTNAME)
2782                 free(cachep->cache_hostname);
2783         if (cachep->cache_protofmly && protofmlylen > SMALL_PROTOFMLYNAME)
2784                 free(cachep->cache_protofmly);
2785         if (cachep)
2786                 free(cachep);
2787         cachep = NULL;
2788 }
2789 
2790 static int
2791 get_cached_srv_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
2792         struct netconfig *nconf, struct netbuf *addrp)
2793 {
2794         if (portmap_cache_lookup(hostname, prog, vers, nconf, addrp))
2795                 return (1);
2796         if (rpcb_getaddr(prog, vers, nconf, addrp, hostname) == 0)
2797                 return (0);
2798         portmap_cache_enter(hostname, prog, vers, nconf, addrp);
2799         return (1);
2800 }
2801 
2802 /*
2803  * Get a network address on "hostname" for program "prog"
2804  * with version "vers".  If the port number is specified (non zero)
2805  * then try for a TCP/UDP transport and set the port number of the
2806  * resulting IP address.
2807  *
2808  * If the address of a netconfig pointer was passed and
2809  * if it's not null, use it as the netconfig otherwise
2810  * assign the address of the netconfig that was used to
2811  * establish contact with the service.
2812  *
2813  * tinfo argument is for matching the get_addr() defined in
2814  * ../nfs/mount/mount.c
2815  */
2816 
2817 static struct netbuf *
2818 get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
2819         struct netconfig **nconfp, char *proto, ushort_t port,
2820         struct t_info *tinfo)
2821 
2822 {
2823         enum clnt_stat cstat;
2824 
2825         return (get_server_netinfo(SERVER_ADDR, hostname, prog, vers, NULL,
2826                 nconfp, proto, port, tinfo, NULL, FALSE, NULL, &cstat));
2827 }
2828 
2829 static struct netbuf *
2830 get_pubfh(char *hostname, rpcvers_t vers, mfs_snego_t *mfssnego,
2831         struct netconfig **nconfp, char *proto, ushort_t port,
2832         struct t_info *tinfo, caddr_t *fhp, bool_t get_pubfh, char *fspath)
2833 {
2834         enum clnt_stat cstat;
2835 
2836         return (get_server_netinfo(SERVER_FH, hostname, NFS_PROGRAM, vers,
2837             mfssnego, nconfp, proto, port, tinfo, fhp, get_pubfh, fspath,
2838             &cstat));
2839 }
2840 
2841 static enum clnt_stat
2842 get_ping(char *hostname, rpcprog_t prog, rpcvers_t vers,
2843         struct netconfig **nconfp, ushort_t port, bool_t direct_to_server)
2844 {
2845         enum clnt_stat cstat;
2846 
2847         (void) get_server_netinfo(SERVER_PING, hostname, prog,
2848             vers, NULL, nconfp, NULL, port, NULL, NULL,
2849             direct_to_server, NULL, &cstat);
2850 
2851         return (cstat);
2852 }
2853 
2854 void *
2855 get_server_netinfo(
2856         enum type_of_stuff type_of_stuff,
2857         char *hostname,
2858         rpcprog_t prog,
2859         rpcvers_t vers,
2860         mfs_snego_t *mfssnego,
2861         struct netconfig **nconfp,
2862         char *proto,
2863         ushort_t port,                  /* may be zero */
2864         struct t_info *tinfo,
2865         caddr_t *fhp,
2866         bool_t direct_to_server,
2867         char *fspath,
2868         enum clnt_stat *cstatp)
2869 {
2870         struct netbuf *nb = NULL;
2871         struct netconfig *nconf = NULL;
2872         NCONF_HANDLE *nc = NULL;
2873         int error = 0;
2874         int fd = 0;
2875         struct t_bind *tbind = NULL;
2876         int nthtry = FIRST_TRY;
2877 
2878         if (nconfp && *nconfp) {
2879                 return (get_netconfig_info(type_of_stuff, hostname,
2880                     prog, vers, nconf, port, tinfo, tbind, fhp,
2881                     direct_to_server, fspath, cstatp, mfssnego));
2882         }
2883 
2884         /*
2885          * No nconf passed in.
2886          *
2887          * Try to get a nconf from /etc/netconfig.
2888          * First choice is COTS, second is CLTS unless proto
2889          * is specified.  When we retry, we reset the
2890          * netconfig list, so that we search the whole list
2891          * for the next choice.
2892          */
2893         if ((nc = setnetpath()) == NULL)
2894                 goto done;
2895 
2896         /*
2897          * If proto is specified, then only search for the match,
2898          * otherwise try COTS first, if failed, then try CLTS.
2899          */
2900         if (proto) {
2901                 while ((nconf = getnetpath(nc)) != NULL) {
2902                         if (strcmp(nconf->nc_proto, proto))
2903                                 continue;
2904                         /*
2905                          * If the port number is specified then TCP/UDP
2906                          * is needed. Otherwise any cots/clts will do.
2907                          */
2908                         if (port)  {
2909                                 if ((strcmp(nconf->nc_protofmly, NC_INET) &&
2910                                     strcmp(nconf->nc_protofmly, NC_INET6)) ||
2911                                     (strcmp(nconf->nc_proto, NC_TCP) &&
2912                                     strcmp(nconf->nc_proto, NC_UDP)))
2913                                         continue;
2914                         }
2915                         nb = get_netconfig_info(type_of_stuff, hostname,
2916                             prog, vers, nconf, port, tinfo, tbind, fhp,
2917                             direct_to_server, fspath, cstatp, mfssnego);
2918                         if (*cstatp == RPC_SUCCESS)
2919                                 break;
2920 
2921                         assert(nb == NULL);
2922 
2923                 }
2924                 if (nconf == NULL)
2925                         goto done;
2926         } else {
2927 retry:
2928                 while ((nconf = getnetpath(nc)) != NULL) {
2929                         if (nconf->nc_flag & NC_VISIBLE) {
2930                                 if (nthtry == FIRST_TRY) {
2931                                         if ((nconf->nc_semantics ==
2932                                             NC_TPI_COTS_ORD) ||
2933                                             (nconf->nc_semantics ==
2934                                             NC_TPI_COTS)) {
2935                                                 if (port == 0)
2936                                                         break;
2937                                                 if ((strcmp(nconf->nc_protofmly,
2938                                                     NC_INET) == 0 ||
2939                                                     strcmp(nconf->nc_protofmly,
2940                                                     NC_INET6) == 0) &&
2941                                                     (strcmp(nconf->nc_proto,
2942                                                     NC_TCP) == 0))
2943                                                         break;
2944                                         }
2945                                 }
2946                                 if (nthtry == SECOND_TRY) {
2947                                         if (nconf->nc_semantics ==
2948                                             NC_TPI_CLTS) {
2949                                                 if (port == 0)
2950                                                         break;
2951                                                 if ((strcmp(nconf->nc_protofmly,
2952                                                     NC_INET) == 0 ||
2953                                                     strcmp(nconf->nc_protofmly,
2954                                                     NC_INET6) == 0) &&
2955                                                     (strcmp(nconf->nc_proto,
2956                                                     NC_UDP) == 0))
2957                                                         break;
2958                                         }
2959                                 }
2960                         }
2961                 }
2962 
2963                 if (nconf == NULL) {
2964                         if (++nthtry <= MNT_PREF_LISTLEN) {
2965                                 endnetpath(nc);
2966                                 if ((nc = setnetpath()) == NULL)
2967                                         goto done;
2968                                 goto retry;
2969                         } else
2970                                 goto done;
2971                 } else {
2972                         nb = get_netconfig_info(type_of_stuff, hostname,
2973                             prog, vers, nconf, port, tinfo, tbind, fhp,
2974                             direct_to_server, fspath, cstatp, mfssnego);
2975                         if (*cstatp != RPC_SUCCESS)
2976                                 /*
2977                                  * Continue the same search path in the
2978                                  * netconfig db until no more matched nconf
2979                                  * (nconf == NULL).
2980                                  */
2981                                 goto retry;
2982                 }
2983         }
2984 
2985         /*
2986          * Got nconf and nb.  Now dup the netconfig structure (nconf)
2987          * and return it thru nconfp.
2988          */
2989         if (nconf != NULL) {
2990                 if ((*nconfp = getnetconfigent(nconf->nc_netid)) == NULL) {
2991                         syslog(LOG_ERR, "no memory\n");
2992                         free(nb);
2993                         nb = NULL;
2994                 }
2995         } else {
2996                 *nconfp = NULL;
2997         }
2998 done:
2999         if (nc)
3000                 endnetpath(nc);
3001         return (nb);
3002 }
3003 
3004 void *
3005 get_server_fh(char *hostname, rpcprog_t prog, rpcvers_t vers,
3006         mfs_snego_t *mfssnego, struct netconfig *nconf, ushort_t port,
3007         struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp,
3008         bool_t direct_to_server, char *fspath, enum clnt_stat *cstat)
3009 {
3010         AUTH *ah = NULL;
3011         AUTH *new_ah = NULL;
3012         struct snego_t  snego;
3013         enum clnt_stat cs = RPC_TIMEDOUT;
3014         struct timeval tv;
3015         bool_t file_handle = 1;
3016         enum snego_stat sec;
3017         CLIENT *cl = NULL;
3018         int fd = -1;
3019         struct netbuf *nb = NULL;
3020 
3021         if (direct_to_server != TRUE)
3022                 return (NULL);
3023 
3024         if (prog == NFS_PROGRAM && vers == NFS_V4)
3025                 if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
3026                         goto done;
3027 
3028         if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0)
3029                 goto done;
3030 
3031         /* LINTED pointer alignment */
3032         if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
3033                 goto done;
3034 
3035         if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd,
3036             direct_to_server, port, prog, vers, file_handle) < 0) {
3037                 goto done;
3038         }
3039 
3040         cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
3041         if (cl == NULL)
3042                 goto done;
3043 
3044         ah = authsys_create_default();
3045         if (ah != NULL) {
3046 #ifdef MALLOC_DEBUG
3047                 drop_alloc("AUTH_HANDLE", cl->cl_auth,
3048                     __FILE__, __LINE__);
3049 #endif
3050                 AUTH_DESTROY(cl->cl_auth);
3051                 cl->cl_auth = ah;
3052 #ifdef MALLOC_DEBUG
3053                 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3054                     __FILE__, __LINE__);
3055 #endif
3056         }
3057 
3058         if (!mfssnego->snego_done && vers != NFS_V4) {
3059                 /*
3060                  * negotiate sec flavor.
3061                  */
3062                 snego.cnt = 0;
3063                 if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
3064                     SNEGO_SUCCESS) {
3065                         int jj;
3066 
3067                         /*
3068                          * check if server supports the one
3069                          * specified in the sec= option.
3070                          */
3071                         if (mfssnego->sec_opt) {
3072                                 for (jj = 0; jj < snego.cnt; jj++) {
3073                                         if (snego.array[jj] ==
3074                                             mfssnego->nfs_sec.sc_nfsnum) {
3075                                                 mfssnego->snego_done = TRUE;
3076                                                 break;
3077                                         }
3078                                 }
3079                         }
3080 
3081                         /*
3082                          * find a common sec flavor
3083                          */
3084                         if (!mfssnego->snego_done) {
3085                                 for (jj = 0; jj < snego.cnt; jj++) {
3086                                         if (!nfs_getseconfig_bynumber(
3087                                             snego.array[jj],
3088                                             &mfssnego->nfs_sec)) {
3089                                                 mfssnego->snego_done = TRUE;
3090                                                 break;
3091                                         }
3092                                 }
3093                         }
3094                         if (!mfssnego->snego_done)
3095                                 goto done;
3096                         /*
3097                          * Now that the flavor has been
3098                          * negotiated, get the fh.
3099                          *
3100                          * First, create an auth handle using the negotiated
3101                          * sec flavor in the next lookup to
3102                          * fetch the filehandle.
3103                          */
3104                         new_ah = nfs_create_ah(cl, hostname,
3105                             &mfssnego->nfs_sec);
3106                         if (new_ah  == NULL)
3107                                 goto done;
3108 #ifdef MALLOC_DEBUG
3109                         drop_alloc("AUTH_HANDLE", cl->cl_auth,
3110                             __FILE__, __LINE__);
3111 #endif
3112                         AUTH_DESTROY(cl->cl_auth);
3113                         cl->cl_auth = new_ah;
3114 #ifdef MALLOC_DEBUG
3115                         add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3116                             __FILE__, __LINE__);
3117 #endif
3118                 } else if (sec == SNEGO_ARRAY_TOO_SMALL ||
3119                     sec == SNEGO_FAILURE) {
3120                         goto done;
3121                 }
3122         }
3123 
3124         switch (vers) {
3125         case NFS_VERSION:
3126                 {
3127                 wnl_diropargs arg;
3128                 wnl_diropres res;
3129 
3130                 memset((char *)&arg.dir, 0, sizeof (wnl_fh));
3131                 memset((char *)&res, 0, sizeof (wnl_diropres));
3132                 arg.name = fspath;
3133                 if (wnlproc_lookup_2(&arg, &res, cl) !=
3134                     RPC_SUCCESS || res.status != WNL_OK)
3135                         goto done;
3136                 *fhp = malloc(sizeof (wnl_fh));
3137 
3138                 if (*fhp == NULL) {
3139                         syslog(LOG_ERR, "no memory\n");
3140                         goto done;
3141                 }
3142 
3143                 memcpy((char *)*fhp,
3144                     (char *)&res.wnl_diropres_u.wnl_diropres.file,
3145                     sizeof (wnl_fh));
3146                 cs = RPC_SUCCESS;
3147                 }
3148                 break;
3149         case NFS_V3:
3150                 {
3151                 WNL_LOOKUP3args arg;
3152                 WNL_LOOKUP3res res;
3153                 nfs_fh3 *fh3p;
3154 
3155                 memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
3156                 memset((char *)&res, 0, sizeof (WNL_LOOKUP3res));
3157                 arg.what.name = fspath;
3158                 if (wnlproc3_lookup_3(&arg, &res, cl) !=
3159                     RPC_SUCCESS || res.status != WNL3_OK)
3160                         goto done;
3161 
3162                 fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
3163 
3164                 if (fh3p == NULL) {
3165                         syslog(LOG_ERR, "no memory\n");
3166                         goto done;
3167                 }
3168 
3169                 fh3p->fh3_length =
3170                     res.WNL_LOOKUP3res_u.res_ok.object.data.data_len;
3171                 memcpy(fh3p->fh3_u.data,
3172                     res.WNL_LOOKUP3res_u.res_ok.object.data.data_val,
3173                     fh3p->fh3_length);
3174 
3175                 *fhp = (caddr_t)fh3p;
3176 
3177                 cs = RPC_SUCCESS;
3178                 }
3179                 break;
3180         case NFS_V4:
3181                 tv.tv_sec = 10;
3182                 tv.tv_usec = 0;
3183                 cs = clnt_call(cl, NULLPROC, xdr_void, 0,
3184                     xdr_void, 0, tv);
3185                 if (cs != RPC_SUCCESS)
3186                         goto done;
3187 
3188                 *fhp = strdup(fspath);
3189                 if (fhp == NULL) {
3190                         cs = RPC_SYSTEMERROR;
3191                         goto done;
3192                 }
3193                 break;
3194         }
3195         nb = (struct netbuf *)malloc(sizeof (struct netbuf));
3196         if (nb == NULL) {
3197                 syslog(LOG_ERR, "no memory\n");
3198                 cs = RPC_SYSTEMERROR;
3199                 goto done;
3200         }
3201         nb->buf = (char *)malloc(tbind->addr.maxlen);
3202         if (nb->buf == NULL) {
3203                 syslog(LOG_ERR, "no memory\n");
3204                 free(nb);
3205                 nb = NULL;
3206                 cs = RPC_SYSTEMERROR;
3207                 goto done;
3208         }
3209         (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
3210         nb->len = tbind->addr.len;
3211         nb->maxlen = tbind->addr.maxlen;
3212 done:
3213         if (cstat != NULL)
3214                 *cstat = cs;
3215         destroy_auth_client_handle(cl);
3216         cleanup_tli_parms(tbind, fd);
3217         return (nb);
3218 }
3219 
3220 /*
3221  * Sends a null call to the remote host's (NFS program, versp). versp
3222  * may be "NULL" in which case the default maximum version is used.
3223  * Upon return, versp contains the maximum version supported iff versp!= NULL.
3224  */
3225 enum clnt_stat
3226 pingnfs(
3227         char *hostpart,
3228         int attempts,
3229         rpcvers_t *versp,
3230         rpcvers_t versmin,
3231         ushort_t port,                  /* may be zero */
3232         bool_t usepub,
3233         char *path,
3234         char *proto)
3235 {
3236         CLIENT *cl = NULL;
3237         struct timeval rpc_to_new = {15, 0};
3238         static struct timeval rpc_rtrans_new = {-1, -1};
3239         enum clnt_stat clnt_stat;
3240         int i, j;
3241         rpcvers_t versmax;      /* maximum version to try against server */
3242         rpcvers_t outvers;      /* version supported by host on last call */
3243         rpcvers_t vers_to_try;  /* to try different versions against host */
3244         char *hostname;
3245         struct netconfig *nconf;
3246 
3247         hostname = strdup(hostpart);
3248         if (hostname == NULL) {
3249                 return (RPC_SYSTEMERROR);
3250         }
3251         unbracket(&hostname);
3252 
3253         if (path != NULL && strcmp(hostname, "nfs") == 0 &&
3254             strncmp(path, "//", 2) == 0) {
3255                 char *sport;
3256 
3257                 hostname = strdup(path+2);
3258 
3259                 if (hostname == NULL)
3260                         return (RPC_SYSTEMERROR);
3261 
3262                 path = strchr(hostname, '/');
3263 
3264                 /*
3265                  * This cannot happen. If it does, give up
3266                  * on the ping as this is obviously a corrupt
3267                  * entry.
3268                  */
3269                 if (path == NULL) {
3270                         free(hostname);
3271                         return (RPC_SUCCESS);
3272                 }
3273 
3274                 /*
3275                  * Probable end point of host string.
3276                  */
3277                 *path = '\0';
3278 
3279                 sport = strchr(hostname, ':');
3280 
3281                 if (sport != NULL && sport < path) {
3282 
3283                         /*
3284                          * Actual end point of host string.
3285                          */
3286                         *sport = '\0';
3287                         port = htons((ushort_t)atoi(sport+1));
3288                 }
3289 
3290                 usepub = TRUE;
3291         }
3292 
3293         /* Pick up the default versions and then set them appropriately */
3294         if (versp) {
3295                 versmax = *versp;
3296                 /* use versmin passed in */
3297         } else {
3298                 read_default_nfs();
3299                 set_versrange(0, &versmax, &versmin);
3300         }
3301 
3302         if (proto &&
3303             strncasecmp(proto, NC_UDP, strlen(NC_UDP)) == 0 &&
3304             versmax == NFS_V4) {
3305                 if (versmin == NFS_V4) {
3306                         if (versp) {
3307                                 *versp = versmax - 1;
3308                                 return (RPC_SUCCESS);
3309                         }
3310                         return (RPC_PROGUNAVAIL);
3311                 } else {
3312                         versmax--;
3313                 }
3314         }
3315 
3316         if (versp)
3317                 *versp = versmax;
3318 
3319         switch (cache_check(hostname, versp, proto)) {
3320         case GOODHOST:
3321                 if (hostname != hostpart)
3322                         free(hostname);
3323                 return (RPC_SUCCESS);
3324         case DEADHOST:
3325                 if (hostname != hostpart)
3326                         free(hostname);
3327                 return (RPC_TIMEDOUT);
3328         case NOHOST:
3329         default:
3330                 break;
3331         }
3332 
3333         /*
3334          * XXX The retransmission time rpcbrmttime is a global defined
3335          * in the rpc library (rpcb_clnt.c). We use (and like) the default
3336          * value of 15 sec in the rpc library. The code below is to protect
3337          * us in case it changes. This need not be done under a lock since
3338          * any # of threads entering this function will get the same
3339          * retransmission value.
3340          */
3341         if (rpc_rtrans_new.tv_sec == -1 && rpc_rtrans_new.tv_usec == -1) {
3342                 __rpc_control(CLCR_GET_RPCB_RMTTIME, (char *)&rpc_rtrans_new);
3343                 if (rpc_rtrans_new.tv_sec != 15 && rpc_rtrans_new.tv_sec != 0)
3344                         if (trace > 1)
3345                                 trace_prt(1, "RPC library rttimer changed\n");
3346         }
3347 
3348         /*
3349          * XXX Manipulate the total timeout to get the number of
3350          * desired retransmissions. This code is heavily dependant on
3351          * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3352          */
3353         for (i = 0, j = rpc_rtrans_new.tv_sec; i < attempts-1; i++) {
3354                 if (j < RPC_MAX_BACKOFF)
3355                         j *= 2;
3356                 else
3357                         j = RPC_MAX_BACKOFF;
3358                 rpc_to_new.tv_sec += j;
3359         }
3360 
3361         vers_to_try = versmax;
3362 
3363         /*
3364          * check the host's version within the timeout
3365          */
3366         if (trace > 1)
3367                 trace_prt(1, "  ping: %s timeout=%ld request vers=%d min=%d\n",
3368                     hostname, rpc_to_new.tv_sec, versmax, versmin);
3369 
3370         if (usepub == FALSE) {
3371                 do {
3372                         /*
3373                          * If NFSv4, then we do the same thing as is used
3374                          * for public filehandles so that we avoid rpcbind
3375                          */
3376                         if (vers_to_try == NFS_V4) {
3377                                 if (trace > 4) {
3378                                 trace_prt(1, "  pingnfs: Trying ping via "
3379                                     "\"circuit_v\"\n");
3380                                 }
3381 
3382                                 cl = clnt_create_service_timed(hostname, "nfs",
3383                                     NFS_PROGRAM, vers_to_try,
3384                                     port, "circuit_v", &rpc_to_new);
3385                                 if (cl != NULL) {
3386                                         outvers = vers_to_try;
3387                                         break;
3388                                 }
3389                                 if (trace > 4) {
3390                                         trace_prt(1,
3391                                             "  pingnfs: Can't ping via "
3392                                             "\"circuit_v\" %s: RPC error=%d\n",
3393                                             hostname, rpc_createerr.cf_stat);
3394                                 }
3395 
3396                         } else {
3397                                 cl = clnt_create_vers_timed(hostname,
3398                                     NFS_PROGRAM, &outvers, versmin, vers_to_try,
3399                                     "datagram_v", &rpc_to_new);
3400                                 if (cl != NULL)
3401                                         break;
3402                                 if (trace > 4) {
3403                                         trace_prt(1,
3404                                             "  pingnfs: Can't ping via "
3405                                             "\"datagram_v\"%s: RPC error=%d\n",
3406                                             hostname, rpc_createerr.cf_stat);
3407                                 }
3408                                 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
3409                                     rpc_createerr.cf_stat == RPC_TIMEDOUT)
3410                                         break;
3411                                 if (rpc_createerr.cf_stat ==
3412                                     RPC_PROGNOTREGISTERED) {
3413                                         if (trace > 4) {
3414                                                 trace_prt(1,
3415                                                     "  pingnfs: Trying ping "
3416                                                     "via \"circuit_v\"\n");
3417                                         }
3418                                         cl = clnt_create_vers_timed(hostname,
3419                                             NFS_PROGRAM, &outvers,
3420                                             versmin, vers_to_try,
3421                                             "circuit_v", &rpc_to_new);
3422                                         if (cl != NULL)
3423                                                 break;
3424                                         if (trace > 4) {
3425                                                 trace_prt(1,
3426                                                     "  pingnfs: Can't ping "
3427                                                     "via \"circuit_v\" %s: "
3428                                                     "RPC error=%d\n",
3429                                                     hostname,
3430                                                     rpc_createerr.cf_stat);
3431                                         }
3432                                 }
3433                         }
3434 
3435                 /*
3436                  * backoff and return lower version to retry the ping.
3437                  * XXX we should be more careful and handle
3438                  * RPC_PROGVERSMISMATCH here, because that error is handled
3439                  * in clnt_create_vers(). It's not done to stay in sync
3440                  * with the nfs mount command.
3441                  */
3442                         vers_to_try--;
3443                         if (vers_to_try < versmin)
3444                                 break;
3445                         if (versp != NULL) {    /* recheck the cache */
3446                                 *versp = vers_to_try;
3447                                 if (trace > 4) {
3448                                         trace_prt(1,
3449                                             "  pingnfs: check cache: vers=%d\n",
3450                                             *versp);
3451                                 }
3452                                 switch (cache_check(hostname, versp, proto)) {
3453                                 case GOODHOST:
3454                                         if (hostname != hostpart)
3455                                                 free(hostname);
3456                                         return (RPC_SUCCESS);
3457                                 case DEADHOST:
3458                                         if (hostname != hostpart)
3459                                                 free(hostname);
3460                                         return (RPC_TIMEDOUT);
3461                                 case NOHOST:
3462                                 default:
3463                                         break;
3464                                 }
3465                         }
3466                         if (trace > 4) {
3467                                 trace_prt(1, "  pingnfs: Try version=%d\n",
3468                                     vers_to_try);
3469                         }
3470                 } while (cl == NULL);
3471 
3472 
3473                 if (cl == NULL) {
3474                         if (verbose)
3475                                 syslog(LOG_ERR, "pingnfs: %s%s",
3476                                     hostname, clnt_spcreateerror(""));
3477                         clnt_stat = rpc_createerr.cf_stat;
3478                 } else {
3479                         clnt_destroy(cl);
3480                         clnt_stat = RPC_SUCCESS;
3481                 }
3482 
3483         } else {
3484                 for (vers_to_try = versmax; vers_to_try >= versmin;
3485                     vers_to_try--) {
3486 
3487                         nconf = NULL;
3488 
3489                         if (trace > 4) {
3490                                 trace_prt(1, "  pingnfs: Try version=%d "
3491                                     "using get_ping()\n", vers_to_try);
3492                         }
3493 
3494                         clnt_stat = get_ping(hostname, NFS_PROGRAM,
3495                             vers_to_try, &nconf, port, TRUE);
3496 
3497                         if (nconf != NULL)
3498                                 freenetconfigent(nconf);
3499 
3500                         if (clnt_stat == RPC_SUCCESS) {
3501                                 outvers = vers_to_try;
3502                                 break;
3503                         }
3504                 }
3505         }
3506 
3507         if (trace > 1)
3508                 clnt_stat == RPC_SUCCESS ?
3509                     trace_prt(1, "      pingnfs OK: nfs version=%d\n", outvers):
3510                     trace_prt(1, "      pingnfs FAIL: can't get nfs version\n");
3511 
3512         if (clnt_stat == RPC_SUCCESS) {
3513                 cache_enter(hostname, versmax, outvers, proto, GOODHOST);
3514                 if (versp != NULL)
3515                         *versp = outvers;
3516         } else
3517                 cache_enter(hostname, versmax, versmax, proto, DEADHOST);
3518 
3519         if (hostpart != hostname)
3520                 free(hostname);
3521 
3522         return (clnt_stat);
3523 }
3524 
3525 #define MNTTYPE_LOFS    "lofs"
3526 
3527 int
3528 loopbackmount(fsname, dir, mntopts, overlay)
3529         char *fsname;           /* Directory being mounted */
3530         char *dir;              /* Directory being mounted on */
3531         char *mntopts;
3532         int overlay;
3533 {
3534         struct mnttab mnt;
3535         int flags = 0;
3536         char fstype[] = MNTTYPE_LOFS;
3537         int dirlen;
3538         struct stat st;
3539         char optbuf[MAX_MNTOPT_STR];
3540 
3541         dirlen = strlen(dir);
3542         if (dir[dirlen-1] == ' ')
3543                 dirlen--;
3544 
3545         if (dirlen == strlen(fsname) &&
3546                 strncmp(fsname, dir, dirlen) == 0) {
3547                 syslog(LOG_ERR,
3548                         "Mount of %s on %s would result in deadlock, aborted\n",
3549                         fsname, dir);
3550                 return (RET_ERR);
3551         }
3552         mnt.mnt_mntopts = mntopts;
3553         if (hasmntopt(&mnt, MNTOPT_RO) != NULL)
3554                 flags |= MS_RDONLY;
3555 
3556         (void) strlcpy(optbuf, mntopts, sizeof (optbuf));
3557 
3558         if (overlay)
3559                 flags |= MS_OVERLAY;
3560 
3561         if (trace > 1)
3562                 trace_prt(1,
3563                         "  loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3564                         fsname, dir, flags);
3565 
3566         if (is_system_labeled()) {
3567                 if (create_homedir((const char *)fsname,
3568                     (const char *)dir) == 0) {
3569                         return (NFSERR_NOENT);
3570                 }
3571         }
3572 
3573         if (mount(fsname, dir, flags | MS_DATA | MS_OPTIONSTR, fstype,
3574             NULL, 0, optbuf, sizeof (optbuf)) < 0) {
3575                 syslog(LOG_ERR, "Mount of %s on %s: %m", fsname, dir);
3576                 return (RET_ERR);
3577         }
3578 
3579         if (stat(dir, &st) == 0) {
3580                 if (trace > 1) {
3581                         trace_prt(1,
3582                             "  loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3583                             fsname, dir, st.st_dev, st.st_rdev);
3584                 }
3585         } else {
3586                 if (trace > 1) {
3587                         trace_prt(1,
3588                             "  loopbackmount of %s on %s OK\n", fsname, dir);
3589                         trace_prt(1, "  stat of %s failed\n", dir);
3590                 }
3591         }
3592 
3593         return (0);
3594 }
3595 
3596 /*
3597  * Look for the value of a numeric option of the form foo=x.  If found, set
3598  * *valp to the value and return non-zero.  If not found or the option is
3599  * malformed, return zero.
3600  */
3601 
3602 int
3603 nopt(mnt, opt, valp)
3604         struct mnttab *mnt;
3605         char *opt;
3606         int *valp;                      /* OUT */
3607 {
3608         char *equal;
3609         char *str;
3610 
3611         /*
3612          * We should never get a null pointer, but if we do, it's better to
3613          * ignore the option than to dump core.
3614          */
3615 
3616         if (valp == NULL) {
3617                 syslog(LOG_DEBUG, "null pointer for %s option", opt);
3618                 return (0);
3619         }
3620 
3621         if (str = hasmntopt(mnt, opt)) {
3622                 if (equal = strchr(str, '=')) {
3623                         *valp = atoi(&equal[1]);
3624                         return (1);
3625                 } else {
3626                         syslog(LOG_ERR, "Bad numeric option '%s'", str);
3627                 }
3628         }
3629         return (0);
3630 }
3631 
3632 int
3633 nfsunmount(mnt)
3634         struct mnttab *mnt;
3635 {
3636         struct timeval timeout;
3637         CLIENT *cl;
3638         enum clnt_stat rpc_stat;
3639         char *host, *path;
3640         struct replica *list;
3641         int i, count = 0;
3642         int isv4mount = is_v4_mount(mnt->mnt_mountp);
3643 
3644         if (trace > 1)
3645                 trace_prt(1, "  nfsunmount: umount %s\n", mnt->mnt_mountp);
3646 
3647         if (umount(mnt->mnt_mountp) < 0) {
3648                 if (trace > 1)
3649                         trace_prt(1, "  nfsunmount: umount %s FAILED\n",
3650                                 mnt->mnt_mountp);
3651                 if (errno)
3652                         return (errno);
3653         }
3654 
3655         /*
3656          * If this is a NFSv4 mount, the mount protocol was not used
3657          * so we just return.
3658          */
3659         if (isv4mount) {
3660                 if (trace > 1)
3661                         trace_prt(1, "  nfsunmount: umount %s OK\n",
3662                                 mnt->mnt_mountp);
3663                 return (0);
3664         }
3665 
3666         /*
3667          * If mounted with -o public, then no need to contact server
3668          * because mount protocol was not used.
3669          */
3670         if (hasmntopt(mnt, MNTOPT_PUBLIC) != NULL) {
3671                 return (0);
3672         }
3673 
3674         /*
3675          * The rest of this code is advisory to the server.
3676          * If it fails return success anyway.
3677          */
3678 
3679         list = parse_replica(mnt->mnt_special, &count);
3680         if (!list) {
3681                 if (count >= 0)
3682                         syslog(LOG_ERR,
3683                             "Memory allocation failed: %m");
3684                 return (ENOMEM);
3685         }
3686 
3687         for (i = 0; i < count; i++) {
3688 
3689                 host = list[i].host;
3690                 path = list[i].path;
3691 
3692                 /*
3693                  * Skip file systems mounted using WebNFS, because mount
3694                  * protocol was not used.
3695                  */
3696                 if (strcmp(host, "nfs") == 0 && strncmp(path, "//", 2) == 0)
3697                         continue;
3698 
3699                 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v");
3700                 if (cl == NULL)
3701                         break;
3702 #ifdef MALLOC_DEBUG
3703                 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
3704                 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3705                         __FILE__, __LINE__);
3706 #endif
3707                 if (__clnt_bindresvport(cl) < 0) {
3708                         if (verbose)
3709                                 syslog(LOG_ERR, "umount %s:%s: %s",
3710                                         host, path,
3711                                         "Couldn't bind to reserved port");
3712                         destroy_auth_client_handle(cl);
3713                         continue;
3714                 }
3715 #ifdef MALLOC_DEBUG
3716                 drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__);
3717 #endif
3718                 AUTH_DESTROY(cl->cl_auth);
3719                 if ((cl->cl_auth = authsys_create_default()) == NULL) {
3720                         if (verbose)
3721                                 syslog(LOG_ERR, "umount %s:%s: %s",
3722                                         host, path,
3723                                         "Failed creating default auth handle");
3724                         destroy_auth_client_handle(cl);
3725                         continue;
3726                 }
3727 #ifdef MALLOC_DEBUG
3728                 add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__);
3729 #endif
3730                 timeout.tv_usec = 0;
3731                 timeout.tv_sec = 5;
3732                 rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath,
3733                             (caddr_t)&path, xdr_void, (char *)NULL, timeout);
3734                 if (verbose && rpc_stat != RPC_SUCCESS)
3735                         syslog(LOG_ERR, "%s: %s",
3736                                 host, clnt_sperror(cl, "unmount"));
3737                 destroy_auth_client_handle(cl);
3738         }
3739 
3740         free_replica(list, count);
3741 
3742         if (trace > 1)
3743                 trace_prt(1, "  nfsunmount: umount %s OK\n", mnt->mnt_mountp);
3744 
3745 done:
3746         return (0);
3747 }
3748 
3749 /*
3750  * Put a new entry in the cache chain by prepending it to the front.
3751  * If there isn't enough memory then just give up.
3752  */
3753 static void
3754 cache_enter(host, reqvers, outvers, proto, state)
3755         char *host;
3756         rpcvers_t reqvers;
3757         rpcvers_t outvers;
3758         char *proto;
3759         int state;
3760 {
3761         struct cache_entry *entry;
3762         int cache_time = 30;    /* sec */
3763 
3764         timenow = time(NULL);
3765 
3766         entry = (struct cache_entry *)malloc(sizeof (struct cache_entry));
3767         if (entry == NULL)
3768                 return;
3769         (void) memset((caddr_t)entry, 0, sizeof (struct cache_entry));
3770         entry->cache_host = strdup(host);
3771         if (entry->cache_host == NULL) {
3772                 cache_free(entry);
3773                 return;
3774         }
3775         entry->cache_reqvers = reqvers;
3776         entry->cache_outvers = outvers;
3777         entry->cache_proto = (proto == NULL ? NULL : strdup(proto));
3778         entry->cache_state = state;
3779         entry->cache_time = timenow + cache_time;
3780         (void) rw_wrlock(&cache_lock);
3781 #ifdef CACHE_DEBUG
3782         host_cache_accesses++;          /* up host cache access counter */
3783 #endif /* CACHE DEBUG */
3784         entry->cache_next = cache_head;
3785         cache_head = entry;
3786         (void) rw_unlock(&cache_lock);
3787 }
3788 
3789 static int
3790 cache_check(host, versp, proto)
3791         char *host;
3792         rpcvers_t *versp;
3793         char *proto;
3794 {
3795         int state = NOHOST;
3796         struct cache_entry *ce, *prev;
3797 
3798         timenow = time(NULL);
3799 
3800         (void) rw_rdlock(&cache_lock);
3801 
3802 #ifdef CACHE_DEBUG
3803         /* Increment the lookup and access counters for the host cache */
3804         host_cache_accesses++;
3805         host_cache_lookups++;
3806         if ((host_cache_lookups%1000) == 0)
3807                 trace_host_cache();
3808 #endif /* CACHE DEBUG */
3809 
3810         for (ce = cache_head; ce; ce = ce->cache_next) {
3811                 if (timenow > ce->cache_time) {
3812                         (void) rw_unlock(&cache_lock);
3813                         (void) rw_wrlock(&cache_lock);
3814                         for (prev = NULL, ce = cache_head; ce;
3815                                 prev = ce, ce = ce->cache_next) {
3816                                 if (timenow > ce->cache_time) {
3817                                         cache_free(ce);
3818                                         if (prev)
3819                                                 prev->cache_next = NULL;
3820                                         else
3821                                                 cache_head = NULL;
3822                                         break;
3823                                 }
3824                         }
3825                         (void) rw_unlock(&cache_lock);
3826                         return (state);
3827                 }
3828                 if (strcmp(host, ce->cache_host) != 0)
3829                         continue;
3830                 if ((proto == NULL && ce->cache_proto != NULL) ||
3831                     (proto != NULL && ce->cache_proto == NULL))
3832                         continue;
3833                 if (proto != NULL &&
3834                     strcmp(proto, ce->cache_proto) != 0)
3835                         continue;
3836 
3837                 if (versp == NULL ||
3838                         (versp != NULL && *versp == ce->cache_reqvers) ||
3839                         (versp != NULL && *versp == ce->cache_outvers)) {
3840                                 if (versp != NULL)
3841                                         *versp = ce->cache_outvers;
3842                                 state = ce->cache_state;
3843 
3844                                 /* increment the host cache hit counters */
3845 #ifdef CACHE_DEBUG
3846                                 if (state == GOODHOST)
3847                                         goodhost_cache_hits++;
3848                                 if (state == DEADHOST)
3849                                         deadhost_cache_hits++;
3850 #endif /* CACHE_DEBUG */
3851                                 (void) rw_unlock(&cache_lock);
3852                                 return (state);
3853                 }
3854         }
3855         (void) rw_unlock(&cache_lock);
3856         return (state);
3857 }
3858 
3859 /*
3860  * Free a cache entry and all entries
3861  * further down the chain since they
3862  * will also be expired.
3863  */
3864 static void
3865 cache_free(entry)
3866         struct cache_entry *entry;
3867 {
3868         struct cache_entry *ce, *next = NULL;
3869 
3870         for (ce = entry; ce; ce = next) {
3871                 if (ce->cache_host)
3872                         free(ce->cache_host);
3873                 if (ce->cache_proto)
3874                         free(ce->cache_proto);
3875                 next = ce->cache_next;
3876                 free(ce);
3877         }
3878 }
3879 
3880 #ifdef MALLOC_DEBUG
3881 void
3882 cache_flush()
3883 {
3884         (void) rw_wrlock(&cache_lock);
3885         cache_free(cache_head);
3886         cache_head = NULL;
3887         (void) rw_unlock(&cache_lock);
3888 }
3889 
3890 void
3891 flush_caches()
3892 {
3893         mutex_lock(&cleanup_lock);
3894         cond_signal(&cleanup_start_cv);
3895         (void) cond_wait(&cleanup_done_cv, &cleanup_lock);
3896         mutex_unlock(&cleanup_lock);
3897         cache_flush();
3898         portmap_cache_flush();
3899 }
3900 #endif
3901 
3902 /*
3903  * Returns 1, if port option is NFS_PORT or
3904  *      nfsd is running on the port given
3905  * Returns 0, if both port is not NFS_PORT and nfsd is not
3906  *      running on the port.
3907  */
3908 
3909 static int
3910 is_nfs_port(char *opts)
3911 {
3912         struct mnttab m;
3913         uint_t nfs_port = 0;
3914         struct servent sv;
3915         char buf[256];
3916         int got_port;
3917 
3918         m.mnt_mntopts = opts;
3919 
3920         /*
3921          * Get port specified in options list, if any.
3922          */
3923         got_port = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
3924 
3925         /*
3926          * if no port specified or it is same as NFS_PORT return nfs
3927          * To use any other daemon the port number should be different
3928          */
3929         if (!got_port || nfs_port == NFS_PORT)
3930                 return (1);
3931         /*
3932          * If daemon is nfsd, return nfs
3933          */
3934         if (getservbyport_r(nfs_port, NULL, &sv, buf, 256) == &sv &&
3935             strcmp(sv.s_name, "nfsd") == 0)
3936                 return (1);
3937 
3938         /*
3939          * daemon is not nfs
3940          */
3941         return (0);
3942 }
3943 
3944 
3945 /*
3946  * destroy_auth_client_handle(cl)
3947  * destroys the created client handle
3948  */
3949 void
3950 destroy_auth_client_handle(CLIENT *cl)
3951 {
3952         if (cl) {
3953                 if (cl->cl_auth) {
3954 #ifdef MALLOC_DEBUG
3955                         drop_alloc("AUTH_HANDLE", cl->cl_auth,
3956                             __FILE__, __LINE__);
3957 #endif
3958                         AUTH_DESTROY(cl->cl_auth);
3959                         cl->cl_auth = NULL;
3960                 }
3961 #ifdef MALLOC_DEBUG
3962                 drop_alloc("CLNT_HANDLE", cl,
3963                     __FILE__, __LINE__);
3964 #endif
3965                 clnt_destroy(cl);
3966         }
3967 }
3968 
3969 
3970 /*
3971  * Attempt to figure out which version of NFS to use in pingnfs().  If
3972  * the version number was specified (i.e., non-zero), then use it.
3973  * Otherwise, default to the compiled-in default or the default as set
3974  * by the /etc/default/nfs configuration (as read by read_default().
3975  */
3976 int
3977 set_versrange(rpcvers_t nfsvers, rpcvers_t *vers, rpcvers_t *versmin)
3978 {
3979         switch (nfsvers) {
3980         case 0:
3981                 *vers = vers_max_default;
3982                 *versmin = vers_min_default;
3983                 break;
3984         case NFS_V4:
3985                 *vers = NFS_V4;
3986                 *versmin = NFS_V4;
3987                 break;
3988         case NFS_V3:
3989                 *vers = NFS_V3;
3990                 *versmin = NFS_V3;
3991                 break;
3992         case NFS_VERSION:
3993                 *vers = NFS_VERSION;            /* version 2 */
3994                 *versmin = NFS_VERSMIN;         /* version 2 */
3995                 break;
3996         default:
3997                 return (-1);
3998         }
3999         return (0);
4000 }
4001 
4002 #ifdef CACHE_DEBUG
4003 /*
4004  * trace_portmap_cache()
4005  * traces the portmap cache values at desired points
4006  */
4007 static void
4008 trace_portmap_cache()
4009 {
4010         syslog(LOG_ERR, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
4011             portmap_cache_accesses, portmap_cache_lookups,
4012             portmap_cache_hits);
4013 }
4014 
4015 /*
4016  * trace_host_cache()
4017  * traces the host cache values at desired points
4018  */
4019 static void
4020 trace_host_cache()
4021 {
4022         syslog(LOG_ERR,
4023             "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
4024             host_cache_accesses, host_cache_lookups, deadhost_cache_hits,
4025             goodhost_cache_hits);
4026 }
4027 #endif /* CACHE_DEBUG */
4028 
4029 /*
4030  * Read the NFS SMF properties to determine if the
4031  * client has been configured for a new min/max for the NFS version to
4032  * use.
4033  */
4034 
4035 #define SVC_NFS_CLIENT  "svc:/network/nfs/client"
4036 
4037 static void
4038 read_default_nfs(void)
4039 {
4040         static time_t lastread = 0;
4041         struct stat buf;
4042         char defval[4];
4043         int errno, bufsz;
4044         int tmp, ret = 0;
4045 
4046         bufsz = 4;
4047         ret = nfs_smf_get_prop("client_versmin", defval, DEFAULT_INSTANCE,
4048             SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz);
4049         if (ret == SA_OK) {
4050                 errno = 0;
4051                 tmp = strtol(defval, (char **)NULL, 10);
4052                 if (errno == 0) {
4053                         vers_min_default = tmp;
4054                 }
4055         }
4056 
4057         bufsz = 4;
4058         ret = nfs_smf_get_prop("client_versmax", defval, DEFAULT_INSTANCE,
4059             SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz);
4060         if (ret == SA_OK) {
4061                 errno = 0;
4062                 tmp = strtol(defval, (char **)NULL, 10);
4063                 if (errno == 0) {
4064                         vers_max_default = tmp;
4065                 }
4066         }
4067 
4068         lastread = buf.st_mtime;
4069 
4070         /*
4071          * Quick sanity check on the values picked up from the
4072          * defaults file.  Make sure that a mistake wasn't
4073          * made that will confuse things later on.
4074          * If so, reset to compiled-in defaults
4075          */
4076         if (vers_min_default > vers_max_default ||
4077             vers_min_default < NFS_VERSMIN ||
4078             vers_max_default > NFS_VERSMAX) {
4079                 if (trace > 1) {
4080                         trace_prt(1,
4081         "  read_default: version minimum/maximum incorrectly configured\n");
4082                         trace_prt(1,
4083 "  read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
4084                             vers_min_default, vers_max_default,
4085                             NFS_VERSMIN_DEFAULT,
4086                             NFS_VERSMAX_DEFAULT);
4087                 }
4088                 vers_min_default = NFS_VERSMIN_DEFAULT;
4089                 vers_max_default = NFS_VERSMAX_DEFAULT;
4090         }
4091 }
4092 
4093 /*
4094  *  Find the mnttab entry that corresponds to "name".
4095  *  We're not sure what the name represents: either
4096  *  a mountpoint name, or a special name (server:/path).
4097  *  Return the last entry in the file that matches.
4098  */
4099 static struct extmnttab *
4100 mnttab_find(dirname)
4101         char *dirname;
4102 {
4103         FILE *fp;
4104         struct extmnttab mnt;
4105         struct extmnttab *res = NULL;
4106 
4107         fp = fopen(MNTTAB, "r");
4108         if (fp == NULL) {
4109                 if (trace > 1)
4110                         trace_prt(1, "  mnttab_find: unable to open mnttab\n");
4111                 return (NULL);
4112         }
4113         while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) {
4114                 if (strcmp(mnt.mnt_mountp, dirname) == 0 ||
4115                     strcmp(mnt.mnt_special, dirname) == 0) {
4116                         if (res)
4117                                 fsfreemnttab(res);
4118                         res = fsdupmnttab(&mnt);
4119                 }
4120         }
4121 
4122         resetmnttab(fp);
4123         fclose(fp);
4124         if (res == NULL) {
4125                 if (trace > 1)
4126                         trace_prt(1, "  mnttab_find: unable to find %s\n",
4127                                 dirname);
4128         }
4129         return (res);
4130 }
4131 
4132 /*
4133  * This function's behavior is taken from nfsstat.
4134  * Trying to determine what NFS version was used for the mount.
4135  */
4136 static int
4137 is_v4_mount(char *mntpath)
4138 {
4139         kstat_ctl_t *kc = NULL;         /* libkstat cookie */
4140         kstat_t *ksp;
4141         ulong_t fsid;
4142         struct mntinfo_kstat mik;
4143         struct extmnttab *mntp;
4144         uint_t mnt_minor;
4145 
4146         if ((mntp = mnttab_find(mntpath)) == NULL)
4147                 return (FALSE);
4148 
4149         /* save the minor number and free the struct so we don't forget */
4150         mnt_minor = mntp->mnt_minor;
4151         fsfreemnttab(mntp);
4152 
4153         if ((kc = kstat_open()) == NULL)
4154                 return (FALSE);
4155 
4156         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
4157                 if (ksp->ks_type != KSTAT_TYPE_RAW)
4158                         continue;
4159                 if (strcmp(ksp->ks_module, "nfs") != 0)
4160                         continue;
4161                 if (strcmp(ksp->ks_name, "mntinfo") != 0)
4162                         continue;
4163                 if (mnt_minor != ksp->ks_instance)
4164                         continue;
4165 
4166                 if (kstat_read(kc, ksp, &mik) == -1)
4167                         continue;
4168 
4169                 (void) kstat_close(kc);
4170                 if (mik.mik_vers == 4)
4171                         return (TRUE);
4172                 else
4173                         return (FALSE);
4174         }
4175         (void) kstat_close(kc);
4176 
4177         return (FALSE);
4178 }
4179 
4180 static int
4181 create_homedir(const char *src, const char *dst) {
4182 
4183         struct stat stbuf;
4184         char *dst_username;
4185         struct passwd *pwd, pwds;
4186         char buf_pwd[NSS_BUFLEN_PASSWD];
4187         int homedir_len;
4188         int dst_dir_len;
4189         int src_dir_len;
4190 
4191         if (trace > 1)
4192                 trace_prt(1, "entered create_homedir\n");
4193 
4194         if (stat(src, &stbuf) == 0) {
4195                 if (trace > 1)
4196                         trace_prt(1, "src exists\n");
4197                 return (1);
4198         }
4199 
4200         dst_username = strrchr(dst, '/');
4201         if (dst_username) {
4202                 dst_username++; /* Skip over slash */
4203                 pwd = getpwnam_r(dst_username, &pwds, buf_pwd,
4204                     sizeof (buf_pwd));
4205                 if (pwd == NULL) {
4206                         return (0);
4207                 }
4208         } else {
4209                 return (0);
4210         }
4211 
4212         homedir_len = strlen(pwd->pw_dir);
4213         dst_dir_len = strlen(dst) - homedir_len;
4214         src_dir_len = strlen(src) - homedir_len;
4215 
4216         /* Check that the paths are in the same zone */
4217         if (src_dir_len < dst_dir_len ||
4218             (strncmp(dst, src, dst_dir_len) != 0)) {
4219                 if (trace > 1)
4220                         trace_prt(1, "  paths don't match\n");
4221                 return (0);
4222         }
4223         /* Check that mountpoint is an auto_home entry */
4224         if (dst_dir_len < 0 ||
4225             (strcmp(pwd->pw_dir, dst + dst_dir_len) != 0)) {
4226                 return (0);
4227         }
4228 
4229         /* Check that source is an home directory entry */
4230         if (src_dir_len < 0 ||
4231             (strcmp(pwd->pw_dir, src + src_dir_len) != 0)) {
4232                 if (trace > 1)
4233                         trace_prt(1, "  homedir (2) doesn't match %s\n",
4234                 src+src_dir_len);
4235                 return (0);
4236         }
4237 
4238         if (mkdir(src,
4239             S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH) == -1) {
4240                 if (trace > 1) {
4241                         trace_prt(1, "  Couldn't mkdir %s\n", src);
4242                 }
4243                 return (0);
4244         }
4245 
4246         if (chown(src, pwd->pw_uid, pwd->pw_gid) == -1) {
4247                 unlink(src);
4248                 return (0);
4249         }
4250 
4251         /* Created new home directory for the user */
4252         return (1);
4253 }
4254 
4255 void
4256 free_nfs_args(struct nfs_args *argp)
4257 {
4258         struct nfs_args *oldp;
4259         while (argp) {
4260                 if (argp->pathconf)
4261                         free(argp->pathconf);
4262                 if (argp->knconf)
4263                         free_knconf(argp->knconf);
4264                 if (argp->addr)
4265                         netbuf_free(argp->addr);
4266                 if (argp->syncaddr)
4267                         netbuf_free(argp->syncaddr);
4268                 if (argp->netname)
4269                         free(argp->netname);
4270                 if (argp->hostname)
4271                         free(argp->hostname);
4272                 if (argp->nfs_ext_u.nfs_extB.secdata)
4273                         nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
4274                 if (argp->fh)
4275                         free(argp->fh);
4276                 if (argp->nfs_ext_u.nfs_extA.secdata) {
4277                         sec_data_t      *sd;
4278                         sd = argp->nfs_ext_u.nfs_extA.secdata;
4279                         if (sd == NULL)
4280                                 break;
4281                         switch (sd->rpcflavor) {
4282                         case AUTH_NONE:
4283                         case AUTH_UNIX:
4284                         case AUTH_LOOPBACK:
4285                                 break;
4286                         case AUTH_DES:
4287                         {
4288                                 dh_k4_clntdata_t        *dhk4;
4289                                 dhk4 = (dh_k4_clntdata_t *)sd->data;
4290                                 if (dhk4 == NULL)
4291                                         break;
4292                                 if (dhk4->syncaddr.buf)
4293                                         free(dhk4->syncaddr.buf);
4294                                 if (dhk4->knconf->knc_protofmly)
4295                                         free(dhk4->knconf->knc_protofmly);
4296                                 if (dhk4->knconf->knc_proto)
4297                                         free(dhk4->knconf->knc_proto);
4298                                 if (dhk4->knconf)
4299                                         free(dhk4->knconf);
4300                                 if (dhk4->netname)
4301                                         free(dhk4->netname);
4302                                 free(dhk4);
4303                                 break;
4304                         }
4305                         case RPCSEC_GSS:
4306                         {
4307                                 gss_clntdata_t  *gss;
4308                                 gss = (gss_clntdata_t *)sd->data;
4309                                 if (gss == NULL)
4310                                         break;
4311                                 if (gss->mechanism.elements)
4312                                         free(gss->mechanism.elements);
4313                                 free(gss);
4314                                 break;
4315                         }
4316                         }
4317                 }
4318                 oldp = argp;
4319                 if (argp->nfs_args_ext == NFS_ARGS_EXTB)
4320                         argp = argp->nfs_ext_u.nfs_extB.next;
4321                 else
4322                         argp = NULL;
4323                 free(oldp);
4324         }
4325 }
4326 
4327 void *
4328 get_netconfig_info(enum type_of_stuff type_of_stuff, char *hostname,
4329         rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
4330         ushort_t port, struct t_info *tinfo, struct t_bind *tbind,
4331         caddr_t *fhp, bool_t direct_to_server, char *fspath,
4332         enum clnt_stat *cstat, mfs_snego_t *mfssnego)
4333 {
4334         struct netconfig *nb = NULL;
4335         int ping_server = 0;
4336 
4337 
4338         if (nconf == NULL)
4339                 return (NULL);
4340 
4341         switch (type_of_stuff) {
4342         case SERVER_FH:
4343                 nb = get_server_fh(hostname, prog, vers, mfssnego,
4344                     nconf, port, tinfo, tbind, fhp, direct_to_server,
4345                     fspath, cstat);
4346                 break;
4347         case SERVER_PING:
4348                 ping_server = 1;
4349         case SERVER_ADDR:
4350                 nb = get_server_addrorping(hostname, prog, vers,
4351                     nconf, port, tinfo, tbind, fhp, direct_to_server,
4352                     fspath, cstat, ping_server);
4353                 break;
4354         default:
4355                 assert(nb != NULL);
4356         }
4357         return (nb);
4358 }
4359 
4360 /*
4361  * Get the server address or can we ping it or not.
4362  * Check the portmap cache first for server address.
4363  * If no entries there, ping the server with a NULLPROC rpc.
4364  */
4365 void *
4366 get_server_addrorping(char *hostname, rpcprog_t prog, rpcvers_t vers,
4367         struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
4368         struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server,
4369         char *fspath, enum clnt_stat *cstat, int ping_server)
4370 {
4371         struct timeval tv;
4372         enum clnt_stat cs = RPC_TIMEDOUT;
4373         struct netbuf *nb = NULL;
4374         CLIENT *cl = NULL;
4375         int fd = -1;
4376 
4377         if (prog == NFS_PROGRAM && vers == NFS_V4)
4378                 if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
4379                         goto done;
4380 
4381         if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) {
4382                 goto done;
4383         }
4384 
4385         /* LINTED pointer alignment */
4386         if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
4387             == NULL) {
4388                 goto done;
4389         }
4390 
4391         if (direct_to_server != TRUE) {
4392                 if (!ping_server) {
4393                         if (get_cached_srv_addr(hostname, prog, vers,
4394                             nconf, &tbind->addr) == 0)
4395                                 goto done;
4396                 } else {
4397                         if (port == 0)
4398                                 goto done;
4399                 }
4400         }
4401         if (setup_nb_parms(nconf, tbind, tinfo, hostname,
4402             fd, direct_to_server, port, prog, vers, 0) < 0)
4403                 goto done;
4404 
4405         if (port || (direct_to_server == TRUE)) {
4406                 tv.tv_sec = 10;
4407                 tv.tv_usec = 0;
4408                 cl = clnt_tli_create(fd, nconf, &tbind->addr,
4409                     prog, vers, 0, 0);
4410                 if (cl == NULL)
4411                         goto done;
4412 
4413                 cs = clnt_call(cl, NULLPROC, xdr_void, 0,
4414                     xdr_void, 0, tv);
4415                 if (cs != RPC_SUCCESS) {
4416                         syslog(LOG_ERR, "error is %d", cs);
4417                         goto done;
4418                 }
4419         }
4420         if (!ping_server) {
4421                 nb = (struct netbuf *)malloc(sizeof (struct netbuf));
4422                 if (nb == NULL) {
4423                         syslog(LOG_ERR, "no memory\n");
4424                         goto done;
4425                 }
4426                 nb->buf = (char *)malloc(tbind->addr.maxlen);
4427                 if (nb->buf == NULL) {
4428                         syslog(LOG_ERR, "no memory\n");
4429                         free(nb);
4430                         nb = NULL;
4431                         goto done;
4432                 }
4433                 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
4434                 nb->len = tbind->addr.len;
4435                 nb->maxlen = tbind->addr.maxlen;
4436                 cs = RPC_SUCCESS;
4437         }
4438 done:
4439         destroy_auth_client_handle(cl);
4440         cleanup_tli_parms(tbind, fd);
4441         *cstat = cs;
4442         return (nb);
4443 }