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