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