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