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