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