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