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