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