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