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