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