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