Merge commit 'ad3ad82ad2fb99c424a8482bd1908d08b990ccea'
[unleashed.git] / usr / src / cmd / fs.d / autofs / autod_nfs.c
bloba3238894da6120bb890576d83977e6e8d8774e8f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <syslog.h>
32 #include <string.h>
33 #include <deflt.h>
34 #include <kstat.h>
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <signal.h>
43 #include <sys/signal.h>
44 #include <rpc/rpc.h>
45 #include <rpc/pmap_clnt.h>
46 #include <sys/mount.h>
47 #include <sys/mntent.h>
48 #include <sys/mnttab.h>
49 #include <sys/fstyp.h>
50 #include <sys/fsid.h>
51 #include <arpa/inet.h>
52 #include <netdb.h>
53 #include <netconfig.h>
54 #include <netdir.h>
55 #include <errno.h>
56 #define NFSCLIENT
57 #include <nfs/nfs.h>
58 #include <nfs/mount.h>
59 #include <rpcsvc/mount.h>
60 #include <rpc/nettype.h>
61 #include <locale.h>
62 #include <setjmp.h>
63 #include <sys/socket.h>
64 #include <thread.h>
65 #include <limits.h>
66 #include <nss_dbdefs.h> /* for NSS_BUFLEN_HOSTS */
67 #include <nfs/nfs_sec.h>
68 #include <sys/sockio.h>
69 #include <net/if.h>
70 #include <assert.h>
71 #include <nfs/nfs_clnt.h>
72 #include <rpcsvc/nfs4_prot.h>
73 #include <nfs/nfs4.h>
74 #define NO_RDDIR_CACHE
75 #include "automount.h"
76 #include "replica.h"
77 #include "nfs_subr.h"
78 #include "webnfs.h"
79 #include "nfs_resolve.h"
80 #include <sys/sockio.h>
81 #include <net/if.h>
82 #include <rpcsvc/daemon_utils.h>
83 #include <pwd.h>
84 #include <strings.h>
85 #include <zone.h>
86 #include <limits.h>
87 #include <libscf.h>
88 #include <libshare.h>
89 #include "smfcfg.h"
91 extern void set_nfsv4_ephemeral_mount_to(void);
93 extern char *nfs_get_qop_name();
94 extern AUTH *nfs_create_ah();
95 extern enum snego_stat nfs_sec_nego();
97 #define MAXHOSTS 512
100 * host cache states
102 #define NOHOST 0
103 #define GOODHOST 1
104 #define DEADHOST 2
106 #define NFS_ARGS_EXTB_secdata(args, secdata) \
107 { (args).nfs_args_ext = NFS_ARGS_EXTB, \
108 (args).nfs_ext_u.nfs_extB.secdata = secdata; }
110 struct cache_entry {
111 struct cache_entry *cache_next;
112 char *cache_host;
113 time_t cache_time;
114 int cache_state;
115 rpcvers_t cache_reqvers;
116 rpcvers_t cache_outvers;
117 char *cache_proto;
120 struct mfs_snego_t {
121 int sec_opt;
122 bool_t snego_done;
123 char *nfs_flavor;
124 seconfig_t nfs_sec;
126 typedef struct mfs_snego_t mfs_snego_t;
128 static struct cache_entry *cache_head = NULL;
129 rwlock_t cache_lock; /* protect the cache chain */
131 static enum nfsstat nfsmount(struct mapfs *, char *, char *, int, uid_t,
132 action_list *);
133 static int is_nfs_port(char *);
135 static void netbuf_free(struct netbuf *);
136 static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int);
137 static struct mapfs *enum_servers(struct mapent *, char *);
138 static struct mapfs *get_mysubnet_servers(struct mapfs *);
139 static int subnet_test(int af, struct sioc_addrreq *);
140 static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
141 struct netconfig **, char *, ushort_t, struct t_info *);
143 static struct netbuf *get_pubfh(char *, rpcvers_t, mfs_snego_t *,
144 struct netconfig **, char *, ushort_t, struct t_info *, caddr_t *,
145 bool_t, char *);
147 static int create_homedir(const char *, const char *);
149 enum type_of_stuff {
150 SERVER_ADDR = 0,
151 SERVER_PING = 1,
152 SERVER_FH = 2
155 static void *get_server_netinfo(enum type_of_stuff, char *, rpcprog_t,
156 rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t,
157 struct t_info *, caddr_t *, bool_t, char *, enum clnt_stat *);
158 static void *get_netconfig_info(enum type_of_stuff, char *, rpcprog_t,
159 rpcvers_t, struct netconfig *, ushort_t, struct t_info *,
160 struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *,
161 mfs_snego_t *);
162 static void *get_server_addrorping(char *, rpcprog_t, rpcvers_t,
163 struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
164 caddr_t *, bool_t, char *, enum clnt_stat *, int);
165 static void *get_server_fh(char *, rpcprog_t, rpcvers_t, mfs_snego_t *,
166 struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
167 caddr_t *, bool_t, char *, enum clnt_stat *);
169 struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **);
170 void free_mfs(struct mapfs *);
171 static void dump_mfs(struct mapfs *, char *, int);
172 static char *dump_distance(struct mapfs *);
173 static void cache_free(struct cache_entry *);
174 static int cache_check(char *, rpcvers_t *, char *);
175 static void cache_enter(char *, rpcvers_t, rpcvers_t, char *, int);
176 void destroy_auth_client_handle(CLIENT *cl);
178 #ifdef CACHE_DEBUG
179 static void trace_host_cache();
180 static void trace_portmap_cache();
181 #endif /* CACHE_DEBUG */
183 static int rpc_timeout = 20;
185 #ifdef CACHE_DEBUG
187 * host cache counters. These variables do not need to be protected
188 * by mutex's. They have been added to measure the utility of the
189 * goodhost/deadhost cache in the lazy hierarchical mounting scheme.
191 static int host_cache_accesses = 0;
192 static int host_cache_lookups = 0;
193 static int deadhost_cache_hits = 0;
194 static int goodhost_cache_hits = 0;
197 * portmap cache counters. These variables do not need to be protected
198 * by mutex's. They have been added to measure the utility of the portmap
199 * cache in the lazy hierarchical mounting scheme.
201 static int portmap_cache_accesses = 0;
202 static int portmap_cache_lookups = 0;
203 static int portmap_cache_hits = 0;
204 #endif /* CACHE_DEBUG */
207 * There are the defaults (range) for the client when determining
208 * which NFS version to use when probing the server (see above).
209 * These will only be used when the vers mount option is not used and
210 * these may be reset if /etc/default/nfs is configured to do so.
212 static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
213 static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
216 * list of support services needed
218 static char *service_list[] = { STATD, LOCKD, NULL };
219 static char *service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
221 static void read_default_nfs(void);
222 static int is_v4_mount(char *);
223 static void start_nfs4cbd(void);
226 mount_nfs(
227 struct mapent *me,
228 char *mntpnt,
229 char *prevhost,
230 int overlay,
231 uid_t uid,
232 action_list **alpp)
234 struct mapfs *mfs, *mp;
235 int err = -1;
236 action_list *alp;
237 char *dir;
240 alp = *alpp;
242 read_default_nfs();
244 mfs = enum_servers(me, prevhost);
245 if (mfs == NULL)
246 return (ENOENT);
249 * Try loopback if we have something on localhost; if nothing
250 * works, we will fall back to NFS
252 if (is_nfs_port(me->map_mntopts)) {
253 for (mp = mfs; mp; mp = mp->mfs_next) {
254 if (self_check(mp->mfs_host)) {
255 err = loopbackmount(mp->mfs_dir,
256 mntpnt, me->map_mntopts, overlay);
257 if (err) {
258 mp->mfs_ignore = 1;
259 } else {
261 * Free action_list if there
262 * is one as it is not needed.
263 * Make sure to set alpp to null
264 * so caller doesn't try to free it
265 * again.
267 if (*alpp) {
268 free(*alpp);
269 *alpp = NULL;
271 break;
276 if (err) {
277 dir = strdup(mfs->mfs_dir);
278 err = nfsmount(mfs, mntpnt, me->map_mntopts,
279 overlay, uid, alp);
280 if (err && trace > 1) {
281 trace_prt(1, " Couldn't mount %s:%s, err=%d\n",
282 mfs->mfs_host ? mfs->mfs_host : "",
283 mfs->mfs_dir ? mfs->mfs_dir : dir, err);
285 free(dir);
287 free_mfs(mfs);
288 return (err);
293 * Using the new ioctl SIOCTONLINK to determine if a host is on the same
294 * subnet. Remove the old network, subnet check.
297 static struct mapfs *
298 get_mysubnet_servers(struct mapfs *mfs_in)
300 int s;
301 struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
303 struct netconfig *nconf;
304 NCONF_HANDLE *nc = NULL;
305 struct nd_hostserv hs;
306 struct nd_addrlist *retaddrs;
307 struct netbuf *nb;
308 struct sioc_addrreq areq;
309 int res;
310 int af;
311 int i;
312 int sa_size;
314 hs.h_serv = "rpcbind";
316 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
317 nc = setnetconfig();
319 while (nconf = getnetconfig(nc)) {
322 * Care about INET family only. proto_done flag
323 * indicates if we have already covered this
324 * protocol family. If so skip it
326 if (((strcmp(nconf->nc_protofmly, NC_INET6) == 0) ||
327 (strcmp(nconf->nc_protofmly, NC_INET) == 0)) &&
328 (nconf->nc_semantics == NC_TPI_CLTS)) {
329 } else
330 continue;
332 hs.h_host = mfs->mfs_host;
334 if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK)
335 continue;
338 * For each host address see if it's on our
339 * local subnet.
342 if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
343 af = AF_INET6;
344 else
345 af = AF_INET;
346 nb = retaddrs->n_addrs;
347 for (i = 0; i < retaddrs->n_cnt; i++, nb++) {
348 memset(&areq.sa_addr, 0, sizeof (areq.sa_addr));
349 memcpy(&areq.sa_addr, nb->buf, MIN(nb->len,
350 sizeof (areq.sa_addr)));
351 if (res = subnet_test(af, &areq)) {
352 p = add_mfs(mfs, DIST_MYNET,
353 &mfs_head, &mfs_tail);
354 if (!p) {
355 netdir_free(retaddrs,
356 ND_ADDRLIST);
357 endnetconfig(nc);
358 return (NULL);
360 break;
362 } /* end of every host */
363 if (trace > 2) {
364 trace_prt(1, "get_mysubnet_servers: host=%s "
365 "netid=%s res=%s\n", mfs->mfs_host,
366 nconf->nc_netid, res == 1?"SUC":"FAIL");
369 netdir_free(retaddrs, ND_ADDRLIST);
370 } /* end of while */
372 endnetconfig(nc);
374 } /* end of every map */
376 return (mfs_head);
381 subnet_test(int af, struct sioc_addrreq *areq)
383 int s;
385 if ((s = socket(af, SOCK_DGRAM, 0)) < 0) {
386 return (0);
389 areq->sa_res = -1;
391 if (ioctl(s, SIOCTONLINK, (caddr_t)areq) < 0) {
392 syslog(LOG_ERR, "subnet_test:SIOCTONLINK failed");
393 return (0);
395 close(s);
396 if (areq->sa_res == 1)
397 return (1);
398 else
399 return (0);
405 * ping a bunch of hosts at once and sort by who responds first
407 static struct mapfs *
408 sort_servers(struct mapfs *mfs_in, int timeout)
410 struct mapfs *m1 = NULL;
411 enum clnt_stat clnt_stat;
413 if (!mfs_in)
414 return (NULL);
416 clnt_stat = nfs_cast(mfs_in, &m1, timeout);
418 if (!m1) {
419 char buff[2048] = {'\0'};
421 for (m1 = mfs_in; m1; m1 = m1->mfs_next) {
422 (void) strcat(buff, m1->mfs_host);
423 if (m1->mfs_next)
424 (void) strcat(buff, ",");
427 syslog(LOG_ERR, "servers %s not responding: %s",
428 buff, clnt_sperrno(clnt_stat));
431 return (m1);
435 * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
436 * provided it is not marked "ignored" and isn't a dupe of ones we've
437 * already seen.
439 struct mapfs *
440 add_mfs(struct mapfs *mfs, int distance, struct mapfs **mfs_head,
441 struct mapfs **mfs_tail)
443 struct mapfs *tmp, *new;
445 for (tmp = *mfs_head; tmp; tmp = tmp->mfs_next)
446 if ((strcmp(tmp->mfs_host, mfs->mfs_host) == 0 &&
447 strcmp(tmp->mfs_dir, mfs->mfs_dir) == 0) ||
448 mfs->mfs_ignore)
449 return (*mfs_head);
450 new = (struct mapfs *)malloc(sizeof (struct mapfs));
451 if (!new) {
452 syslog(LOG_ERR, "Memory allocation failed: %m");
453 return (NULL);
455 bcopy(mfs, new, sizeof (struct mapfs));
456 new->mfs_next = NULL;
457 if (distance)
458 new->mfs_distance = distance;
459 if (!*mfs_head)
460 *mfs_tail = *mfs_head = new;
461 else {
462 (*mfs_tail)->mfs_next = new;
463 *mfs_tail = new;
465 return (*mfs_head);
468 static void
469 dump_mfs(struct mapfs *mfs, char *message, int level)
471 struct mapfs *m1;
473 if (trace <= level)
474 return;
476 trace_prt(1, "%s", message);
477 if (!mfs) {
478 trace_prt(0, "mfs is null\n");
479 return;
481 for (m1 = mfs; m1; m1 = m1->mfs_next)
482 trace_prt(0, "%s[%s] ", m1->mfs_host, dump_distance(m1));
483 trace_prt(0, "\n");
486 static char *
487 dump_distance(struct mapfs *mfs)
489 switch (mfs->mfs_distance) {
490 case 0: return ("zero");
491 case DIST_SELF: return ("self");
492 case DIST_MYSUB: return ("mysub");
493 case DIST_MYNET: return ("mynet");
494 case DIST_OTHER: return ("other");
495 default: return ("other");
500 * Walk linked list "raw", building a new list consisting of members
501 * NOT found in list "filter", returning the result.
503 static struct mapfs *
504 filter_mfs(struct mapfs *raw, struct mapfs *filter)
506 struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
507 int skip;
509 if (!raw)
510 return (NULL);
511 for (mfs = raw; mfs; mfs = mfs->mfs_next) {
512 for (skip = 0, p = filter; p; p = p->mfs_next) {
513 if (strcmp(p->mfs_host, mfs->mfs_host) == 0 &&
514 strcmp(p->mfs_dir, mfs->mfs_dir) == 0) {
515 skip = 1;
516 break;
519 if (skip)
520 continue;
521 p = add_mfs(mfs, 0, &mfs_head, &mfs_tail);
522 if (!p)
523 return (NULL);
525 return (mfs_head);
529 * Walk a linked list of mapfs structs, freeing each member.
531 void
532 free_mfs(struct mapfs *mfs)
534 struct mapfs *tmp;
536 while (mfs) {
537 tmp = mfs->mfs_next;
538 free(mfs);
539 mfs = tmp;
544 * New code for NFS client failover: we need to carry and sort
545 * lists of server possibilities rather than return a single
546 * entry. It preserves previous behaviour of sorting first by
547 * locality (loopback-or-preferred/subnet/net/other) and then
548 * by ping times. We'll short-circuit this process when we
549 * have ENOUGH or more entries.
551 static struct mapfs *
552 enum_servers(struct mapent *me, char *preferred)
554 struct mapfs *p, *m1, *m2, *mfs_head = NULL, *mfs_tail = NULL;
557 * Short-circuit for simple cases.
559 if (!me->map_fs->mfs_next) {
560 p = add_mfs(me->map_fs, DIST_OTHER, &mfs_head, &mfs_tail);
561 if (!p)
562 return (NULL);
563 return (mfs_head);
566 dump_mfs(me->map_fs, " enum_servers: mapent: ", 2);
569 * get addresses & see if any are myself
570 * or were mounted from previously in a
571 * hierarchical mount.
573 if (trace > 2)
574 trace_prt(1, " enum_servers: looking for pref/self\n");
575 for (m1 = me->map_fs; m1; m1 = m1->mfs_next) {
576 if (m1->mfs_ignore)
577 continue;
578 if (self_check(m1->mfs_host) ||
579 strcmp(m1->mfs_host, preferred) == 0) {
580 p = add_mfs(m1, DIST_SELF, &mfs_head, &mfs_tail);
581 if (!p)
582 return (NULL);
585 if (trace > 2 && m1)
586 trace_prt(1, " enum_servers: pref/self found, %s\n",
587 m1->mfs_host);
590 * look for entries on this subnet
592 dump_mfs(m1, " enum_servers: input of get_mysubnet_servers: ", 2);
593 m1 = get_mysubnet_servers(me->map_fs);
594 dump_mfs(m1, " enum_servers: output of get_mysubnet_servers: ", 3);
595 if (m1 && m1->mfs_next) {
596 m2 = sort_servers(m1, rpc_timeout / 2);
597 dump_mfs(m2, " enum_servers: output of sort_servers: ", 3);
598 free_mfs(m1);
599 m1 = m2;
602 for (m2 = m1; m2; m2 = m2->mfs_next) {
603 p = add_mfs(m2, 0, &mfs_head, &mfs_tail);
604 if (!p)
605 return (NULL);
607 if (m1)
608 free_mfs(m1);
611 * add the rest of the entries at the end
613 m1 = filter_mfs(me->map_fs, mfs_head);
614 dump_mfs(m1, " enum_servers: etc: output of filter_mfs: ", 3);
615 m2 = sort_servers(m1, rpc_timeout / 2);
616 dump_mfs(m2, " enum_servers: etc: output of sort_servers: ", 3);
617 if (m1)
618 free_mfs(m1);
619 m1 = m2;
620 for (m2 = m1; m2; m2 = m2->mfs_next) {
621 p = add_mfs(m2, DIST_OTHER, &mfs_head, &mfs_tail);
622 if (!p)
623 return (NULL);
625 if (m1)
626 free_mfs(m1);
628 done:
629 dump_mfs(mfs_head, " enum_servers: output: ", 1);
630 return (mfs_head);
633 static enum nfsstat
634 nfsmount(
635 struct mapfs *mfs_in,
636 char *mntpnt, char *opts,
637 int overlay,
638 uid_t uid,
639 action_list *alp)
641 CLIENT *cl;
642 char remname[MAXPATHLEN], *mnttabtext = NULL;
643 char mopts[MAX_MNTOPT_STR];
644 char netname[MAXNETNAMELEN+1];
645 char *mntopts = NULL;
646 int mnttabcnt = 0;
647 int loglevel;
648 struct mnttab m;
649 struct nfs_args *argp = NULL, *head = NULL, *tail = NULL,
650 *prevhead, *prevtail;
651 int flags;
652 struct fhstatus fhs;
653 struct timeval timeout;
654 enum clnt_stat rpc_stat;
655 enum nfsstat status;
656 struct stat stbuf;
657 struct netconfig *nconf;
658 rpcvers_t vers, versmin; /* used to negotiate nfs version in pingnfs */
659 /* and mount version with mountd */
660 rpcvers_t outvers; /* final version to be used during mount() */
661 rpcvers_t nfsvers; /* version in map options, 0 if not there */
662 rpcvers_t mountversmax; /* tracks the max mountvers during retries */
664 /* used to negotiate nfs version using webnfs */
665 rpcvers_t pubvers, pubversmin, pubversmax;
666 int posix;
667 struct nd_addrlist *retaddrs;
668 struct mountres3 res3;
669 nfs_fh3 fh3;
670 char *fstype;
671 int count, i;
672 char scerror_msg[MAXMSGLEN];
673 int *auths;
674 int delay;
675 int retries;
676 char *nfs_proto = NULL;
677 uint_t nfs_port = 0;
678 char *p, *host, *rhost, *dir;
679 struct mapfs *mfs = NULL;
680 int error, last_error = 0;
681 int replicated;
682 int entries = 0;
683 int v2cnt = 0, v3cnt = 0, v4cnt = 0;
684 int v2near = 0, v3near = 0, v4near = 0;
685 int skipentry = 0;
686 char *nfs_flavor;
687 seconfig_t nfs_sec;
688 int sec_opt, scerror;
689 struct sec_data *secdata;
690 int secflags;
691 struct netbuf *syncaddr;
692 bool_t use_pubfh;
693 ushort_t thisport;
694 int got_val;
695 mfs_snego_t mfssnego_init, mfssnego;
697 dump_mfs(mfs_in, " nfsmount: input: ", 2);
698 replicated = (mfs_in->mfs_next != NULL);
699 m.mnt_mntopts = opts;
700 if (replicated && hasmntopt(&m, MNTOPT_SOFT)) {
701 if (verbose)
702 syslog(LOG_WARNING,
703 "mount on %s is soft and will not be replicated.", mntpnt);
704 replicated = 0;
706 if (replicated && !hasmntopt(&m, MNTOPT_RO)) {
707 if (verbose)
708 syslog(LOG_WARNING,
709 "mount on %s is not read-only and will not be replicated.",
710 mntpnt);
711 replicated = 0;
713 if (replicated)
714 loglevel = LOG_WARNING;
715 else
716 loglevel = LOG_ERR;
718 if (trace > 1) {
719 if (replicated)
720 trace_prt(1, " nfsmount: replicated mount on %s %s:\n",
721 mntpnt, opts);
722 else
723 trace_prt(1, " nfsmount: standard mount on %s %s:\n",
724 mntpnt, opts);
725 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next)
726 trace_prt(1, " %s:%s\n",
727 mfs->mfs_host, mfs->mfs_dir);
731 * Make sure mountpoint is safe to mount on
733 if (lstat(mntpnt, &stbuf) < 0) {
734 syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt);
735 return (NFSERR_NOENT);
739 * Get protocol specified in options list, if any.
741 if ((str_opt(&m, "proto", &nfs_proto)) == -1) {
742 return (NFSERR_NOENT);
746 * Get port specified in options list, if any.
748 got_val = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
749 if (!got_val)
750 nfs_port = 0; /* "unspecified" */
751 if (nfs_port > USHRT_MAX) {
752 syslog(LOG_ERR, "%s: invalid port number %d", mntpnt, nfs_port);
753 return (NFSERR_NOENT);
757 * Set mount(2) flags here, outside of the loop.
759 flags = MS_OPTIONSTR;
760 flags |= (hasmntopt(&m, MNTOPT_RO) == NULL) ? 0 : MS_RDONLY;
761 flags |= (hasmntopt(&m, MNTOPT_NOSUID) == NULL) ? 0 : MS_NOSUID;
762 flags |= overlay ? MS_OVERLAY : 0;
763 if (mntpnt[strlen(mntpnt) - 1] != ' ')
764 /* direct mount point without offsets */
765 flags |= MS_OVERLAY;
767 use_pubfh = (hasmntopt(&m, MNTOPT_PUBLIC) == NULL) ? FALSE : TRUE;
769 (void) memset(&mfssnego_init, 0, sizeof (mfs_snego_t));
770 if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
771 if (++mfssnego_init.sec_opt > 1) {
772 syslog(loglevel,
773 "conflicting security options");
774 return (NFSERR_IO);
776 if (nfs_getseconfig_byname("dh", &mfssnego_init.nfs_sec)) {
777 syslog(loglevel,
778 "error getting dh information from %s",
779 NFSSEC_CONF);
780 return (NFSERR_IO);
784 if (hasmntopt(&m, MNTOPT_SEC) != NULL) {
785 if ((str_opt(&m, MNTOPT_SEC,
786 &mfssnego_init.nfs_flavor)) == -1) {
787 syslog(LOG_ERR, "nfsmount: no memory");
788 return (NFSERR_IO);
792 if (mfssnego_init.nfs_flavor) {
793 if (++mfssnego_init.sec_opt > 1) {
794 syslog(loglevel,
795 "conflicting security options");
796 free(mfssnego_init.nfs_flavor);
797 return (NFSERR_IO);
799 if (nfs_getseconfig_byname(mfssnego_init.nfs_flavor,
800 &mfssnego_init.nfs_sec)) {
801 syslog(loglevel,
802 "error getting %s information from %s",
803 mfssnego_init.nfs_flavor, NFSSEC_CONF);
804 free(mfssnego_init.nfs_flavor);
805 return (NFSERR_IO);
807 free(mfssnego_init.nfs_flavor);
810 nextentry:
811 skipentry = 0;
813 got_val = nopt(&m, MNTOPT_VERS, (int *)&nfsvers);
814 if (!got_val)
815 nfsvers = 0; /* "unspecified" */
816 if (set_versrange(nfsvers, &vers, &versmin) != 0) {
817 syslog(LOG_ERR, "Incorrect NFS version specified for %s",
818 mntpnt);
819 last_error = NFSERR_NOENT;
820 goto ret;
823 if (nfsvers != 0) {
824 pubversmax = pubversmin = nfsvers;
825 } else {
826 pubversmax = vers;
827 pubversmin = versmin;
831 * Walk the whole list, pinging and collecting version
832 * info so that we can make sure the mount will be
833 * homogeneous with respect to version.
835 * If we have a version preference, this is easy; we'll
836 * just reject anything that doesn't match.
838 * If not, we want to try to provide the best compromise
839 * that considers proximity, preference for a higher version,
840 * sorted order, and number of replicas. We will count
841 * the number of V2 and V3 replicas and also the number
842 * which are "near", i.e. the localhost or on the same
843 * subnet.
845 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
848 if (mfs->mfs_ignore)
849 continue;
852 * If the host is '[a:d:d:r:e:s:s'],
853 * only use 'a:d:d:r:e:s:s' for communication
855 host = strdup(mfs->mfs_host);
856 if (host == NULL) {
857 syslog(LOG_ERR, "nfsmount: no memory");
858 last_error = NFSERR_IO;
859 goto out;
861 unbracket(&host);
863 (void) memcpy(&mfssnego, &mfssnego_init, sizeof (mfs_snego_t));
865 if (use_pubfh == TRUE || mfs->mfs_flags & MFS_URL) {
866 char *path;
868 if (nfs_port != 0 && mfs->mfs_port != 0 &&
869 nfs_port != mfs->mfs_port) {
871 syslog(LOG_ERR, "nfsmount: port (%u) in nfs URL"
872 " not the same as port (%d) in port "
873 "option\n", mfs->mfs_port, nfs_port);
874 last_error = NFSERR_IO;
875 goto out;
877 } else if (nfs_port != 0)
878 thisport = nfs_port;
879 else
880 thisport = mfs->mfs_port;
882 dir = mfs->mfs_dir;
884 if ((mfs->mfs_flags & MFS_URL) == 0) {
885 path = malloc(strlen(dir) + 2);
886 if (path == NULL) {
887 syslog(LOG_ERR, "nfsmount: no memory");
888 last_error = NFSERR_IO;
889 goto out;
891 path[0] = (char)WNL_NATIVEPATH;
892 (void) strcpy(&path[1], dir);
893 } else {
894 path = dir;
897 argp = (struct nfs_args *)
898 malloc(sizeof (struct nfs_args));
900 if (!argp) {
901 if (path != dir)
902 free(path);
903 syslog(LOG_ERR, "nfsmount: no memory");
904 last_error = NFSERR_IO;
905 goto out;
907 (void) memset(argp, 0, sizeof (*argp));
910 * RDMA support
911 * By now Mount argument struct has been allocated,
912 * either a pub_fh path will be taken or the regular
913 * one. So here if a protocol was specified and it
914 * was not rdma we let it be, else we set DO_RDMA.
915 * If no proto was there we advise on trying RDMA.
917 if (nfs_proto) {
918 if (strcmp(nfs_proto, "rdma") == 0) {
919 free(nfs_proto);
920 nfs_proto = NULL;
921 argp->flags |= NFSMNT_DORDMA;
923 } else
924 argp->flags |= NFSMNT_TRYRDMA;
926 for (pubvers = pubversmax; pubvers >= pubversmin;
927 pubvers--) {
929 nconf = NULL;
930 argp->addr = get_pubfh(host, pubvers, &mfssnego,
931 &nconf, nfs_proto, thisport, NULL,
932 &argp->fh, TRUE, path);
934 if (argp->addr != NULL)
935 break;
937 if (nconf != NULL)
938 freenetconfigent(nconf);
941 if (path != dir)
942 free(path);
944 if (argp->addr != NULL) {
947 * The use of llock option for NFSv4
948 * mounts is not required since file
949 * locking is included within the protocol
951 if (pubvers != NFS_V4)
952 argp->flags |= NFSMNT_LLOCK;
954 argp->flags |= NFSMNT_PUBLIC;
956 vers = pubvers;
957 mfs->mfs_args = argp;
958 mfs->mfs_version = pubvers;
959 mfs->mfs_nconf = nconf;
960 mfs->mfs_flags |= MFS_FH_VIA_WEBNFS;
962 } else {
963 free(argp);
966 * If -public was specified, give up
967 * on this entry now.
969 if (use_pubfh == TRUE) {
970 syslog(loglevel,
971 "%s: no public file handle support",
972 host);
973 last_error = NFSERR_NOENT;
974 mfs->mfs_ignore = 1;
975 continue;
979 * Back off to a conventional mount.
981 * URL's can contain escape characters. Get
982 * rid of them.
984 path = malloc(strlen(dir) + 2);
986 if (path == NULL) {
987 syslog(LOG_ERR, "nfsmount: no memory");
988 last_error = NFSERR_IO;
989 goto out;
992 strcpy(path, dir);
993 URLparse(path);
994 mfs->mfs_dir = path;
995 mfs->mfs_flags |= MFS_ALLOC_DIR;
996 mfs->mfs_flags &= ~MFS_URL;
1000 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) {
1001 i = pingnfs(host, get_retry(opts) + 1, &vers, versmin,
1002 0, FALSE, NULL, nfs_proto);
1003 if (i != RPC_SUCCESS) {
1004 if (i == RPC_PROGVERSMISMATCH) {
1005 syslog(loglevel, "server %s: NFS "
1006 "protocol version mismatch",
1007 host);
1008 } else {
1009 syslog(loglevel, "server %s not "
1010 "responding", host);
1012 mfs->mfs_ignore = 1;
1013 last_error = NFSERR_NOENT;
1014 continue;
1016 if (nfsvers != 0 && nfsvers != vers) {
1017 if (nfs_proto == NULL)
1018 syslog(loglevel,
1019 "NFS version %d "
1020 "not supported by %s",
1021 nfsvers, host);
1022 else
1023 syslog(loglevel,
1024 "NFS version %d "
1025 "with proto %s "
1026 "not supported by %s",
1027 nfsvers, nfs_proto, host);
1028 mfs->mfs_ignore = 1;
1029 last_error = NFSERR_NOENT;
1030 continue;
1034 free(host);
1036 switch (vers) {
1037 case NFS_V4: v4cnt++; break;
1038 case NFS_V3: v3cnt++; break;
1039 case NFS_VERSION: v2cnt++; break;
1040 default: break;
1044 * It's not clear how useful this stuff is if
1045 * we are using webnfs across the internet, but it
1046 * can't hurt.
1048 if (mfs->mfs_distance &&
1049 mfs->mfs_distance <= DIST_MYSUB) {
1050 switch (vers) {
1051 case NFS_V4: v4near++; break;
1052 case NFS_V3: v3near++; break;
1053 case NFS_VERSION: v2near++; break;
1054 default: break;
1059 * If the mount is not replicated, we don't want to
1060 * ping every entry, so we'll stop here. This means
1061 * that we may have to go back to "nextentry" above
1062 * to consider another entry if we can't get
1063 * all the way to mount(2) with this one.
1065 if (!replicated)
1066 break;
1070 if (nfsvers == 0) {
1072 * Choose the NFS version.
1073 * We prefer higher versions, but will choose a one-
1074 * version downgrade in service if we can use a local
1075 * network interface and avoid a router.
1077 if (v4cnt && v4cnt >= v3cnt && (v4near || !v3near))
1078 nfsvers = NFS_V4;
1079 else if (v3cnt && v3cnt >= v2cnt && (v3near || !v2near))
1080 nfsvers = NFS_V3;
1081 else
1082 nfsvers = NFS_VERSION;
1083 if (trace > 2)
1084 trace_prt(1,
1085 " nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1086 v4cnt, v4near, v3cnt, v3near,
1087 v2cnt, v2near, nfsvers);
1091 * Since we don't support different NFS versions in replicated
1092 * mounts, set fstype now.
1093 * Also take the opportunity to set
1094 * the mount protocol version as appropriate.
1096 switch (nfsvers) {
1097 case NFS_V4:
1098 fstype = MNTTYPE_NFS4;
1099 break;
1100 case NFS_V3:
1101 fstype = MNTTYPE_NFS3;
1102 if (use_pubfh == FALSE) {
1103 mountversmax = MOUNTVERS3;
1104 versmin = MOUNTVERS3;
1106 break;
1107 case NFS_VERSION:
1108 fstype = MNTTYPE_NFS;
1109 if (use_pubfh == FALSE) {
1110 mountversmax = MOUNTVERS_POSIX;
1111 versmin = MOUNTVERS;
1113 break;
1117 * Our goal here is to evaluate each of several possible
1118 * replicas and try to come up with a list we can hand
1119 * to mount(2). If we don't have a valid "head" at the
1120 * end of this process, it means we have rejected all
1121 * potential server:/path tuples. We will fail quietly
1122 * in front of mount(2), and will have printed errors
1123 * where we found them.
1124 * XXX - do option work outside loop w careful design
1125 * XXX - use macro for error condition free handling
1127 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
1130 * Initialize retry and delay values on a per-server basis.
1132 retries = get_retry(opts);
1133 delay = INITDELAY;
1134 retry:
1135 if (mfs->mfs_ignore)
1136 continue;
1139 * If we don't have a fh yet, and if this is not a replicated
1140 * mount, we haven't done a pingnfs() on the next entry,
1141 * so we don't know if the next entry is up or if it
1142 * supports an NFS version we like. So if we had a problem
1143 * with an entry, we need to go back and run through some new
1144 * code.
1146 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1147 !replicated && skipentry)
1148 goto nextentry;
1150 vers = mountversmax;
1151 host = mfs->mfs_host;
1152 dir = mfs->mfs_dir;
1155 * Remember the possible '[a:d:d:r:e:s:s]' as the address to be
1156 * later passed to mount(2) and used in the mnttab line, but
1157 * only use 'a:d:d:r:e:s:s' for communication
1159 rhost = strdup(host);
1160 if (rhost == NULL) {
1161 syslog(LOG_ERR, "nfsmount: no memory");
1162 last_error = NFSERR_IO;
1163 goto out;
1165 unbracket(&host);
1167 (void) sprintf(remname, "%s:%s", rhost, dir);
1168 if (trace > 4 && replicated)
1169 trace_prt(1, " nfsmount: examining %s\n", remname);
1171 if (mfs->mfs_args == NULL) {
1174 * Allocate nfs_args structure
1176 argp = (struct nfs_args *)
1177 malloc(sizeof (struct nfs_args));
1179 if (!argp) {
1180 syslog(LOG_ERR, "nfsmount: no memory");
1181 last_error = NFSERR_IO;
1182 goto out;
1185 (void) memset(argp, 0, sizeof (*argp));
1188 * RDMA support
1189 * By now Mount argument struct has been allocated,
1190 * either a pub_fh path will be taken or the regular
1191 * one. So here if a protocol was specified and it
1192 * was not rdma we let it be, else we set DO_RDMA.
1193 * If no proto was there we advise on trying RDMA.
1195 if (nfs_proto) {
1196 if (strcmp(nfs_proto, "rdma") == 0) {
1197 free(nfs_proto);
1198 nfs_proto = NULL;
1199 argp->flags |= NFSMNT_DORDMA;
1201 } else
1202 argp->flags |= NFSMNT_TRYRDMA;
1203 } else {
1204 argp = mfs->mfs_args;
1205 mfs->mfs_args = NULL;
1208 * Skip entry if we already have file handle but the
1209 * NFS version is wrong.
1211 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) &&
1212 mfs->mfs_version != nfsvers) {
1214 free(argp);
1215 skipentry = 1;
1216 mfs->mfs_ignore = 1;
1217 continue;
1221 prevhead = head;
1222 prevtail = tail;
1223 if (!head)
1224 head = tail = argp;
1225 else
1226 tail = tail->nfs_ext_u.nfs_extB.next = argp;
1229 * WebNFS and NFSv4 behave similarly in that they
1230 * don't use the mount protocol. Therefore, avoid
1231 * mount protocol like things when version 4 is being
1232 * used.
1234 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1235 nfsvers != NFS_V4) {
1236 timeout.tv_usec = 0;
1237 timeout.tv_sec = rpc_timeout;
1238 rpc_stat = RPC_TIMEDOUT;
1240 /* Create the client handle. */
1242 if (trace > 1) {
1243 trace_prt(1,
1244 " nfsmount: Get mount version: request "
1245 "vers=%d min=%d\n", vers, versmin);
1248 while ((cl = clnt_create_vers(host, MOUNTPROG, &outvers,
1249 versmin, vers, "udp")) == NULL) {
1250 if (trace > 4) {
1251 trace_prt(1,
1252 " nfsmount: Can't get mount "
1253 "version: rpcerr=%d\n",
1254 rpc_createerr.cf_stat);
1256 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
1257 rpc_createerr.cf_stat == RPC_TIMEDOUT)
1258 break;
1261 * backoff and return lower version to retry the ping.
1262 * XXX we should be more careful and handle
1263 * RPC_PROGVERSMISMATCH here, because that error
1264 * is handled in clnt_create_vers(). It's not done to
1265 * stay in sync with the nfs mount command.
1267 vers--;
1268 if (vers < versmin)
1269 break;
1270 if (trace > 4) {
1271 trace_prt(1,
1272 " nfsmount: Try version=%d\n",
1273 vers);
1277 if (cl == NULL) {
1278 free(argp);
1279 head = prevhead;
1280 tail = prevtail;
1281 if (tail)
1282 tail->nfs_ext_u.nfs_extB.next = NULL;
1283 last_error = NFSERR_NOENT;
1285 if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST &&
1286 rpc_createerr.cf_stat !=
1287 RPC_PROGVERSMISMATCH &&
1288 retries-- > 0) {
1289 DELAY(delay);
1290 goto retry;
1293 syslog(loglevel, "%s %s", host,
1294 clnt_spcreateerror(
1295 "server not responding"));
1296 skipentry = 1;
1297 mfs->mfs_ignore = 1;
1298 continue;
1300 if (trace > 1) {
1301 trace_prt(1,
1302 " nfsmount: mount version=%d\n", outvers);
1304 #ifdef MALLOC_DEBUG
1305 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
1306 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1307 __FILE__, __LINE__);
1308 #endif
1310 if (__clnt_bindresvport(cl) < 0) {
1311 free(argp);
1312 head = prevhead;
1313 tail = prevtail;
1314 if (tail)
1315 tail->nfs_ext_u.nfs_extB.next = NULL;
1316 last_error = NFSERR_NOENT;
1318 if (retries-- > 0) {
1319 destroy_auth_client_handle(cl);
1320 DELAY(delay);
1321 goto retry;
1324 syslog(loglevel, "mount %s: %s", host,
1325 "Couldn't bind to reserved port");
1326 destroy_auth_client_handle(cl);
1327 skipentry = 1;
1328 mfs->mfs_ignore = 1;
1329 continue;
1332 #ifdef MALLOC_DEBUG
1333 drop_alloc("AUTH_HANDLE", cl->cl_auth,
1334 __FILE__, __LINE__);
1335 #endif
1336 AUTH_DESTROY(cl->cl_auth);
1337 if ((cl->cl_auth = authsys_create_default()) == NULL) {
1338 free(argp);
1339 head = prevhead;
1340 tail = prevtail;
1341 if (tail)
1342 tail->nfs_ext_u.nfs_extB.next = NULL;
1343 last_error = NFSERR_NOENT;
1345 if (retries-- > 0) {
1346 destroy_auth_client_handle(cl);
1347 DELAY(delay);
1348 goto retry;
1351 syslog(loglevel, "mount %s: %s", host,
1352 "Failed creating default auth handle");
1353 destroy_auth_client_handle(cl);
1354 skipentry = 1;
1355 mfs->mfs_ignore = 1;
1356 continue;
1358 #ifdef MALLOC_DEBUG
1359 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1360 __FILE__, __LINE__);
1361 #endif
1362 } else
1363 cl = NULL;
1366 * set security options
1368 sec_opt = 0;
1369 (void) memset(&nfs_sec, 0, sizeof (nfs_sec));
1370 if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
1371 if (++sec_opt > 1) {
1372 syslog(loglevel,
1373 "conflicting security options for %s",
1374 remname);
1375 free(argp);
1376 head = prevhead;
1377 tail = prevtail;
1378 if (tail)
1379 tail->nfs_ext_u.nfs_extB.next = NULL;
1380 last_error = NFSERR_IO;
1381 destroy_auth_client_handle(cl);
1382 skipentry = 1;
1383 mfs->mfs_ignore = 1;
1384 continue;
1386 if (nfs_getseconfig_byname("dh", &nfs_sec)) {
1387 syslog(loglevel,
1388 "error getting dh information from %s",
1389 NFSSEC_CONF);
1390 free(argp);
1391 head = prevhead;
1392 tail = prevtail;
1393 if (tail)
1394 tail->nfs_ext_u.nfs_extB.next = NULL;
1395 last_error = NFSERR_IO;
1396 destroy_auth_client_handle(cl);
1397 skipentry = 1;
1398 mfs->mfs_ignore = 1;
1399 continue;
1403 nfs_flavor = NULL;
1404 if (hasmntopt(&m, MNTOPT_SEC) != NULL) {
1405 if ((str_opt(&m, MNTOPT_SEC, &nfs_flavor)) == -1) {
1406 syslog(LOG_ERR, "nfsmount: no memory");
1407 last_error = NFSERR_IO;
1408 destroy_auth_client_handle(cl);
1409 goto out;
1413 if (nfs_flavor) {
1414 if (++sec_opt > 1) {
1415 syslog(loglevel,
1416 "conflicting security options for %s",
1417 remname);
1418 free(nfs_flavor);
1419 free(argp);
1420 head = prevhead;
1421 tail = prevtail;
1422 if (tail)
1423 tail->nfs_ext_u.nfs_extB.next = NULL;
1424 last_error = NFSERR_IO;
1425 destroy_auth_client_handle(cl);
1426 skipentry = 1;
1427 mfs->mfs_ignore = 1;
1428 continue;
1430 if (nfs_getseconfig_byname(nfs_flavor, &nfs_sec)) {
1431 syslog(loglevel,
1432 "error getting %s information from %s",
1433 nfs_flavor, NFSSEC_CONF);
1434 free(nfs_flavor);
1435 free(argp);
1436 head = prevhead;
1437 tail = prevtail;
1438 if (tail)
1439 tail->nfs_ext_u.nfs_extB.next = NULL;
1440 last_error = NFSERR_IO;
1441 destroy_auth_client_handle(cl);
1442 skipentry = 1;
1443 mfs->mfs_ignore = 1;
1444 continue;
1446 free(nfs_flavor);
1449 posix = (nfsvers != NFS_V4 &&
1450 hasmntopt(&m, MNTOPT_POSIX) != NULL) ? 1 : 0;
1452 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1453 nfsvers != NFS_V4) {
1454 bool_t give_up_on_mnt;
1455 bool_t got_mnt_error;
1457 * If we started with a URL, if first byte of path is not "/",
1458 * then the mount will likely fail, so we should try again
1459 * with a prepended "/".
1461 if (mfs->mfs_flags & MFS_ALLOC_DIR && *dir != '/')
1462 give_up_on_mnt = FALSE;
1463 else
1464 give_up_on_mnt = TRUE;
1466 got_mnt_error = FALSE;
1468 try_mnt_slash:
1469 if (got_mnt_error == TRUE) {
1470 int i, l;
1472 give_up_on_mnt = TRUE;
1473 l = strlen(dir);
1476 * Insert a "/" to front of mfs_dir.
1478 for (i = l; i > 0; i--)
1479 dir[i] = dir[i-1];
1481 dir[0] = '/';
1484 /* Get fhandle of remote path from server's mountd */
1486 switch (outvers) {
1487 case MOUNTVERS:
1488 if (posix) {
1489 free(argp);
1490 head = prevhead;
1491 tail = prevtail;
1492 if (tail)
1493 tail->nfs_ext_u.nfs_extB.next =
1494 NULL;
1495 last_error = NFSERR_NOENT;
1496 syslog(loglevel,
1497 "can't get posix info for %s",
1498 host);
1499 destroy_auth_client_handle(cl);
1500 skipentry = 1;
1501 mfs->mfs_ignore = 1;
1502 continue;
1504 /* FALLTHRU */
1505 case MOUNTVERS_POSIX:
1506 if (nfsvers == NFS_V3) {
1507 free(argp);
1508 head = prevhead;
1509 tail = prevtail;
1510 if (tail)
1511 tail->nfs_ext_u.nfs_extB.next =
1512 NULL;
1513 last_error = NFSERR_NOENT;
1514 syslog(loglevel,
1515 "%s doesn't support NFS Version 3",
1516 host);
1517 destroy_auth_client_handle(cl);
1518 skipentry = 1;
1519 mfs->mfs_ignore = 1;
1520 continue;
1522 rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1523 xdr_dirpath, (caddr_t)&dir,
1524 xdr_fhstatus, (caddr_t)&fhs, timeout);
1525 if (rpc_stat != RPC_SUCCESS) {
1527 if (give_up_on_mnt == FALSE) {
1528 got_mnt_error = TRUE;
1529 goto try_mnt_slash;
1533 * Given the way "clnt_sperror" works, the "%s"
1534 * immediately following the "not responding"
1535 * is correct.
1537 free(argp);
1538 head = prevhead;
1539 tail = prevtail;
1540 if (tail)
1541 tail->nfs_ext_u.nfs_extB.next =
1542 NULL;
1543 last_error = NFSERR_NOENT;
1545 if (retries-- > 0) {
1546 destroy_auth_client_handle(cl);
1547 DELAY(delay);
1548 goto retry;
1551 if (trace > 3) {
1552 trace_prt(1,
1553 " nfsmount: mount RPC "
1554 "failed for %s\n",
1555 host);
1557 syslog(loglevel,
1558 "%s server not responding%s",
1559 host, clnt_sperror(cl, ""));
1560 destroy_auth_client_handle(cl);
1561 skipentry = 1;
1562 mfs->mfs_ignore = 1;
1563 continue;
1565 if ((errno = fhs.fhs_status) != MNT_OK) {
1567 if (give_up_on_mnt == FALSE) {
1568 got_mnt_error = TRUE;
1569 goto try_mnt_slash;
1572 free(argp);
1573 head = prevhead;
1574 tail = prevtail;
1575 if (tail)
1576 tail->nfs_ext_u.nfs_extB.next =
1577 NULL;
1578 if (errno == EACCES) {
1579 status = NFSERR_ACCES;
1580 } else {
1581 syslog(loglevel, "%s: %m",
1582 host);
1583 status = NFSERR_IO;
1585 if (trace > 3) {
1586 trace_prt(1,
1587 " nfsmount: mount RPC gave"
1588 " %d for %s:%s\n",
1589 errno, host, dir);
1591 last_error = status;
1592 destroy_auth_client_handle(cl);
1593 skipentry = 1;
1594 mfs->mfs_ignore = 1;
1595 continue;
1597 argp->fh = malloc((sizeof (fhandle)));
1598 if (!argp->fh) {
1599 syslog(LOG_ERR, "nfsmount: no memory");
1600 last_error = NFSERR_IO;
1601 destroy_auth_client_handle(cl);
1602 goto out;
1604 (void) memcpy(argp->fh,
1605 &fhs.fhstatus_u.fhs_fhandle,
1606 sizeof (fhandle));
1607 break;
1608 case MOUNTVERS3:
1609 posix = 0;
1610 (void) memset((char *)&res3, '\0',
1611 sizeof (res3));
1612 rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1613 xdr_dirpath, (caddr_t)&dir,
1614 xdr_mountres3, (caddr_t)&res3, timeout);
1615 if (rpc_stat != RPC_SUCCESS) {
1617 if (give_up_on_mnt == FALSE) {
1618 got_mnt_error = TRUE;
1619 goto try_mnt_slash;
1623 * Given the way "clnt_sperror" works, the "%s"
1624 * immediately following the "not responding"
1625 * is correct.
1627 free(argp);
1628 head = prevhead;
1629 tail = prevtail;
1630 if (tail)
1631 tail->nfs_ext_u.nfs_extB.next =
1632 NULL;
1633 last_error = NFSERR_NOENT;
1635 if (retries-- > 0) {
1636 destroy_auth_client_handle(cl);
1637 DELAY(delay);
1638 goto retry;
1641 if (trace > 3) {
1642 trace_prt(1,
1643 " nfsmount: mount RPC "
1644 "failed for %s\n",
1645 host);
1647 syslog(loglevel,
1648 "%s server not responding%s",
1649 remname, clnt_sperror(cl, ""));
1650 destroy_auth_client_handle(cl);
1651 skipentry = 1;
1652 mfs->mfs_ignore = 1;
1653 continue;
1655 if ((errno = res3.fhs_status) != MNT_OK) {
1657 if (give_up_on_mnt == FALSE) {
1658 got_mnt_error = TRUE;
1659 goto try_mnt_slash;
1662 free(argp);
1663 head = prevhead;
1664 tail = prevtail;
1665 if (tail)
1666 tail->nfs_ext_u.nfs_extB.next =
1667 NULL;
1668 if (errno == EACCES) {
1669 status = NFSERR_ACCES;
1670 } else {
1671 syslog(loglevel, "%s: %m",
1672 remname);
1673 status = NFSERR_IO;
1675 if (trace > 3) {
1676 trace_prt(1,
1677 " nfsmount: mount RPC gave"
1678 " %d for %s:%s\n",
1679 errno, host, dir);
1681 last_error = status;
1682 destroy_auth_client_handle(cl);
1683 skipentry = 1;
1684 mfs->mfs_ignore = 1;
1685 continue;
1689 * Negotiate the security flavor for nfs_mount
1691 auths = res3.mountres3_u.mountinfo.
1692 auth_flavors.auth_flavors_val;
1693 count = res3.mountres3_u.mountinfo.
1694 auth_flavors.auth_flavors_len;
1696 if (sec_opt) {
1697 for (i = 0; i < count; i++)
1698 if (auths[i] ==
1699 nfs_sec.sc_nfsnum) {
1700 break;
1702 if (i >= count) {
1703 syslog(LOG_ERR,
1704 "%s: does not support "
1705 "security \"%s\"\n",
1706 remname, nfs_sec.sc_name);
1707 clnt_freeres(cl, xdr_mountres3,
1708 (caddr_t)&res3);
1709 free(argp);
1710 head = prevhead;
1711 tail = prevtail;
1712 if (tail)
1713 tail->nfs_ext_u.
1714 nfs_extB.next =
1715 NULL;
1716 last_error = NFSERR_IO;
1717 destroy_auth_client_handle(cl);
1718 skipentry = 1;
1719 mfs->mfs_ignore = 1;
1720 continue;
1722 } else if (count > 0) {
1723 for (i = 0; i < count; i++) {
1724 if (!(scerror =
1725 nfs_getseconfig_bynumber(
1726 auths[i], &nfs_sec))) {
1727 sec_opt++;
1728 break;
1731 if (i >= count) {
1732 if (nfs_syslog_scerr(scerror,
1733 scerror_msg)
1734 != -1) {
1735 syslog(LOG_ERR,
1736 "%s cannot be "
1737 "mounted because it"
1738 " is shared with "
1739 "security flavor %d"
1740 " which %s",
1741 remname,
1742 auths[i-1],
1743 scerror_msg);
1745 clnt_freeres(cl, xdr_mountres3,
1746 (caddr_t)&res3);
1747 free(argp);
1748 head = prevhead;
1749 tail = prevtail;
1750 if (tail)
1751 tail->nfs_ext_u.
1752 nfs_extB.next =
1753 NULL;
1754 last_error = NFSERR_IO;
1755 destroy_auth_client_handle(cl);
1756 skipentry = 1;
1757 mfs->mfs_ignore = 1;
1758 continue;
1762 fh3.fh3_length =
1763 res3.mountres3_u.mountinfo.fhandle.
1764 fhandle3_len;
1765 (void) memcpy(fh3.fh3_u.data,
1766 res3.mountres3_u.mountinfo.fhandle.
1767 fhandle3_val,
1768 fh3.fh3_length);
1769 clnt_freeres(cl, xdr_mountres3,
1770 (caddr_t)&res3);
1771 argp->fh = malloc(sizeof (nfs_fh3));
1772 if (!argp->fh) {
1773 syslog(LOG_ERR, "nfsmount: no memory");
1774 last_error = NFSERR_IO;
1775 destroy_auth_client_handle(cl);
1776 goto out;
1778 (void) memcpy(argp->fh, &fh3, sizeof (nfs_fh3));
1779 break;
1780 default:
1781 free(argp);
1782 head = prevhead;
1783 tail = prevtail;
1784 if (tail)
1785 tail->nfs_ext_u.nfs_extB.next = NULL;
1786 last_error = NFSERR_NOENT;
1787 syslog(loglevel,
1788 "unknown MOUNT version %ld on %s",
1789 vers, remname);
1790 destroy_auth_client_handle(cl);
1791 skipentry = 1;
1792 mfs->mfs_ignore = 1;
1793 continue;
1794 } /* switch */
1796 if (nfsvers == NFS_V4) {
1797 argp->fh = strdup(dir);
1798 if (argp->fh == NULL) {
1799 syslog(LOG_ERR, "nfsmount: no memory");
1800 last_error = NFSERR_IO;
1801 goto out;
1805 if (trace > 4)
1806 trace_prt(1, " nfsmount: have %s filehandle for %s\n",
1807 fstype, remname);
1809 argp->flags |= NFSMNT_NEWARGS;
1810 argp->flags |= NFSMNT_INT; /* default is "intr" */
1811 argp->flags |= NFSMNT_HOSTNAME;
1812 argp->hostname = strdup(host);
1813 if (argp->hostname == NULL) {
1814 syslog(LOG_ERR, "nfsmount: no memory");
1815 last_error = NFSERR_IO;
1816 goto out;
1820 * In this case, we want NFSv4 to behave like
1821 * non-WebNFS so that we get the server address.
1823 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) {
1824 nconf = NULL;
1826 if (nfs_port != 0)
1827 thisport = nfs_port;
1828 else
1829 thisport = mfs->mfs_port;
1832 * For NFSv4, we want to avoid rpcbind, so call
1833 * get_server_netinfo() directly to tell it that
1834 * we want to go "direct_to_server". Otherwise,
1835 * do what has always been done.
1837 if (nfsvers == NFS_V4) {
1838 enum clnt_stat cstat;
1840 argp->addr = get_server_netinfo(SERVER_ADDR,
1841 host, NFS_PROGRAM, nfsvers, NULL,
1842 &nconf, nfs_proto, thisport, NULL,
1843 NULL, TRUE, NULL, &cstat);
1844 } else {
1845 argp->addr = get_addr(host, NFS_PROGRAM,
1846 nfsvers, &nconf, nfs_proto,
1847 thisport, NULL);
1850 if (argp->addr == NULL) {
1851 if (argp->hostname)
1852 free(argp->hostname);
1853 free(argp->fh);
1854 free(argp);
1855 head = prevhead;
1856 tail = prevtail;
1857 if (tail)
1858 tail->nfs_ext_u.nfs_extB.next = NULL;
1859 last_error = NFSERR_NOENT;
1861 if (retries-- > 0) {
1862 destroy_auth_client_handle(cl);
1863 DELAY(delay);
1864 goto retry;
1867 syslog(loglevel, "%s: no NFS service", host);
1868 destroy_auth_client_handle(cl);
1869 skipentry = 1;
1870 mfs->mfs_ignore = 1;
1871 continue;
1873 if (trace > 4)
1874 trace_prt(1,
1875 "\tnfsmount: have net address for %s\n",
1876 remname);
1878 } else {
1879 nconf = mfs->mfs_nconf;
1880 mfs->mfs_nconf = NULL;
1883 argp->flags |= NFSMNT_KNCONF;
1884 argp->knconf = get_knconf(nconf);
1885 if (argp->knconf == NULL) {
1886 netbuf_free(argp->addr);
1887 freenetconfigent(nconf);
1888 if (argp->hostname)
1889 free(argp->hostname);
1890 free(argp->fh);
1891 free(argp);
1892 head = prevhead;
1893 tail = prevtail;
1894 if (tail)
1895 tail->nfs_ext_u.nfs_extB.next = NULL;
1896 last_error = NFSERR_NOSPC;
1897 destroy_auth_client_handle(cl);
1898 skipentry = 1;
1899 mfs->mfs_ignore = 1;
1900 continue;
1902 if (trace > 4)
1903 trace_prt(1,
1904 "\tnfsmount: have net config for %s\n",
1905 remname);
1907 if (hasmntopt(&m, MNTOPT_SOFT) != NULL) {
1908 argp->flags |= NFSMNT_SOFT;
1910 if (hasmntopt(&m, MNTOPT_NOINTR) != NULL) {
1911 argp->flags &= ~(NFSMNT_INT);
1913 if (hasmntopt(&m, MNTOPT_NOAC) != NULL) {
1914 argp->flags |= NFSMNT_NOAC;
1916 if (hasmntopt(&m, MNTOPT_NOCTO) != NULL) {
1917 argp->flags |= NFSMNT_NOCTO;
1919 if (hasmntopt(&m, MNTOPT_FORCEDIRECTIO) != NULL) {
1920 argp->flags |= NFSMNT_DIRECTIO;
1922 if (hasmntopt(&m, MNTOPT_NOFORCEDIRECTIO) != NULL) {
1923 argp->flags &= ~(NFSMNT_DIRECTIO);
1927 * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1929 if (mfssnego.snego_done) {
1930 memcpy(&nfs_sec, &mfssnego.nfs_sec,
1931 sizeof (seconfig_t));
1932 } else if (!sec_opt) {
1934 * Get default security mode.
1936 if (nfs_getseconfig_default(&nfs_sec)) {
1937 syslog(loglevel,
1938 "error getting default security entry\n");
1939 free_knconf(argp->knconf);
1940 netbuf_free(argp->addr);
1941 freenetconfigent(nconf);
1942 if (argp->hostname)
1943 free(argp->hostname);
1944 free(argp->fh);
1945 free(argp);
1946 head = prevhead;
1947 tail = prevtail;
1948 if (tail)
1949 tail->nfs_ext_u.nfs_extB.next = NULL;
1950 last_error = NFSERR_NOSPC;
1951 destroy_auth_client_handle(cl);
1952 skipentry = 1;
1953 mfs->mfs_ignore = 1;
1954 continue;
1956 argp->flags |= NFSMNT_SECDEFAULT;
1960 * For AUTH_DH
1961 * get the network address for the time service on
1962 * the server. If an RPC based time service is
1963 * not available then try the IP time service.
1965 * Eventurally, we want to move this code to nfs_clnt_secdata()
1966 * when autod_nfs.c and mount.c can share the same
1967 * get_the_addr/get_netconfig_info routine.
1969 secflags = 0;
1970 syncaddr = NULL;
1971 retaddrs = NULL;
1973 if (nfs_sec.sc_rpcnum == AUTH_DH || nfsvers == NFS_V4) {
1975 * If not using the public fh and not NFS_V4, we can try
1976 * talking RPCBIND. Otherwise, assume that firewalls
1977 * prevent us from doing that.
1979 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1980 nfsvers != NFS_V4) {
1981 enum clnt_stat cstat;
1982 syncaddr = get_server_netinfo(SERVER_ADDR,
1983 host, RPCBPROG, RPCBVERS, NULL, &nconf,
1984 NULL, 0, NULL, NULL, FALSE, NULL, &cstat);
1987 if (syncaddr != NULL) {
1988 /* for flags in sec_data */
1989 secflags |= AUTH_F_RPCTIMESYNC;
1990 } else {
1991 struct nd_hostserv hs;
1992 int error;
1994 hs.h_host = host;
1995 hs.h_serv = "timserver";
1996 error = netdir_getbyname(nconf, &hs, &retaddrs);
1998 if (error != ND_OK &&
1999 nfs_sec.sc_rpcnum == AUTH_DH) {
2000 syslog(loglevel,
2001 "%s: secure: no time service\n",
2002 host);
2003 free_knconf(argp->knconf);
2004 netbuf_free(argp->addr);
2005 freenetconfigent(nconf);
2006 if (argp->hostname)
2007 free(argp->hostname);
2008 free(argp->fh);
2009 free(argp);
2010 head = prevhead;
2011 tail = prevtail;
2012 if (tail)
2013 tail->nfs_ext_u.nfs_extB.next =
2014 NULL;
2015 last_error = NFSERR_IO;
2016 destroy_auth_client_handle(cl);
2017 skipentry = 1;
2018 mfs->mfs_ignore = 1;
2019 continue;
2022 if (error == ND_OK)
2023 syncaddr = retaddrs->n_addrs;
2026 * For potential usage by NFS V4 when AUTH_DH
2027 * is negotiated via SECINFO in the kernel.
2029 if (nfsvers == NFS_V4 && syncaddr &&
2030 host2netname(netname, host, NULL)) {
2031 argp->syncaddr =
2032 malloc(sizeof (struct netbuf));
2033 argp->syncaddr->buf =
2034 malloc(syncaddr->len);
2035 (void) memcpy(argp->syncaddr->buf,
2036 syncaddr->buf, syncaddr->len);
2037 argp->syncaddr->len = syncaddr->len;
2038 argp->syncaddr->maxlen =
2039 syncaddr->maxlen;
2040 argp->netname = strdup(netname);
2041 argp->flags |= NFSMNT_SECURE;
2043 } /* syncaddr */
2044 } /* AUTH_DH */
2046 nfs_sec.sc_uid = uid;
2048 * If AUTH_DH is a chosen flavor now, its data will be stored
2049 * in the sec_data structure via nfs_clnt_secdata().
2051 if (!(secdata = nfs_clnt_secdata(&nfs_sec, host, argp->knconf,
2052 syncaddr, secflags))) {
2053 syslog(LOG_ERR,
2054 "errors constructing security related data\n");
2055 if (secflags & AUTH_F_RPCTIMESYNC)
2056 netbuf_free(syncaddr);
2057 else if (retaddrs)
2058 netdir_free(retaddrs, ND_ADDRLIST);
2059 if (argp->syncaddr)
2060 netbuf_free(argp->syncaddr);
2061 if (argp->netname)
2062 free(argp->netname);
2063 if (argp->hostname)
2064 free(argp->hostname);
2065 free_knconf(argp->knconf);
2066 netbuf_free(argp->addr);
2067 freenetconfigent(nconf);
2068 free(argp->fh);
2069 free(argp);
2070 head = prevhead;
2071 tail = prevtail;
2072 if (tail)
2073 tail->nfs_ext_u.nfs_extB.next = NULL;
2074 last_error = NFSERR_IO;
2075 destroy_auth_client_handle(cl);
2076 skipentry = 1;
2077 mfs->mfs_ignore = 1;
2078 continue;
2080 NFS_ARGS_EXTB_secdata(*argp, secdata);
2081 /* end of security stuff */
2083 if (trace > 4)
2084 trace_prt(1,
2085 " nfsmount: have secure info for %s\n", remname);
2087 if (hasmntopt(&m, MNTOPT_GRPID) != NULL) {
2088 argp->flags |= NFSMNT_GRPID;
2090 if (nopt(&m, MNTOPT_RSIZE, &argp->rsize)) {
2091 argp->flags |= NFSMNT_RSIZE;
2093 if (nopt(&m, MNTOPT_WSIZE, &argp->wsize)) {
2094 argp->flags |= NFSMNT_WSIZE;
2096 if (nopt(&m, MNTOPT_TIMEO, &argp->timeo)) {
2097 argp->flags |= NFSMNT_TIMEO;
2099 if (nopt(&m, MNTOPT_RETRANS, &argp->retrans)) {
2100 argp->flags |= NFSMNT_RETRANS;
2102 if (nopt(&m, MNTOPT_ACTIMEO, &argp->acregmax)) {
2103 argp->flags |= NFSMNT_ACREGMAX;
2104 argp->flags |= NFSMNT_ACDIRMAX;
2105 argp->flags |= NFSMNT_ACDIRMIN;
2106 argp->flags |= NFSMNT_ACREGMIN;
2107 argp->acdirmin = argp->acregmin = argp->acdirmax
2108 = argp->acregmax;
2109 } else {
2110 if (nopt(&m, MNTOPT_ACREGMIN, &argp->acregmin)) {
2111 argp->flags |= NFSMNT_ACREGMIN;
2113 if (nopt(&m, MNTOPT_ACREGMAX, &argp->acregmax)) {
2114 argp->flags |= NFSMNT_ACREGMAX;
2116 if (nopt(&m, MNTOPT_ACDIRMIN, &argp->acdirmin)) {
2117 argp->flags |= NFSMNT_ACDIRMIN;
2119 if (nopt(&m, MNTOPT_ACDIRMAX, &argp->acdirmax)) {
2120 argp->flags |= NFSMNT_ACDIRMAX;
2124 if (posix) {
2125 argp->pathconf = NULL;
2126 if (error = get_pathconf(cl, dir, remname,
2127 &argp->pathconf, retries)) {
2128 if (secflags & AUTH_F_RPCTIMESYNC)
2129 netbuf_free(syncaddr);
2130 else if (retaddrs)
2131 netdir_free(retaddrs, ND_ADDRLIST);
2132 free_knconf(argp->knconf);
2133 netbuf_free(argp->addr);
2134 freenetconfigent(nconf);
2135 nfs_free_secdata(
2136 argp->nfs_ext_u.nfs_extB.secdata);
2137 if (argp->syncaddr)
2138 netbuf_free(argp->syncaddr);
2139 if (argp->netname)
2140 free(argp->netname);
2141 if (argp->hostname)
2142 free(argp->hostname);
2143 free(argp->fh);
2144 free(argp);
2145 head = prevhead;
2146 tail = prevtail;
2147 if (tail)
2148 tail->nfs_ext_u.nfs_extB.next = NULL;
2149 last_error = NFSERR_IO;
2151 if (error == RET_RETRY && retries-- > 0) {
2152 destroy_auth_client_handle(cl);
2153 DELAY(delay);
2154 goto retry;
2157 destroy_auth_client_handle(cl);
2158 skipentry = 1;
2159 mfs->mfs_ignore = 1;
2160 continue;
2162 argp->flags |= NFSMNT_POSIX;
2163 if (trace > 4)
2164 trace_prt(1,
2165 " nfsmount: have pathconf for %s\n",
2166 remname);
2170 * free loop-specific data structures
2172 destroy_auth_client_handle(cl);
2173 freenetconfigent(nconf);
2174 if (secflags & AUTH_F_RPCTIMESYNC)
2175 netbuf_free(syncaddr);
2176 else if (retaddrs)
2177 netdir_free(retaddrs, ND_ADDRLIST);
2180 * Decide whether to use remote host's lockd or local locking.
2181 * If we are using the public fh, we've already turned
2182 * LLOCK on.
2184 if (hasmntopt(&m, MNTOPT_LLOCK))
2185 argp->flags |= NFSMNT_LLOCK;
2186 if (!(argp->flags & NFSMNT_LLOCK) && nfsvers == NFS_VERSION &&
2187 remote_lock(host, argp->fh)) {
2188 syslog(loglevel, "No network locking on %s : "
2189 "contact admin to install server change", host);
2190 argp->flags |= NFSMNT_LLOCK;
2194 * Build a string for /etc/mnttab.
2195 * If possible, coalesce strings with same 'dir' info.
2197 if ((mfs->mfs_flags & MFS_URL) == 0) {
2198 char *tmp;
2200 if (mnttabcnt) {
2201 p = strrchr(mnttabtext, (int)':');
2202 if (!p || strcmp(p+1, dir) != 0) {
2203 mnttabcnt += strlen(remname) + 2;
2204 } else {
2205 *p = '\0';
2206 mnttabcnt += strlen(rhost) + 2;
2208 if ((tmp = realloc(mnttabtext,
2209 mnttabcnt)) != NULL) {
2210 mnttabtext = tmp;
2211 strcat(mnttabtext, ",");
2212 } else {
2213 free(mnttabtext);
2214 mnttabtext = NULL;
2216 } else {
2217 mnttabcnt = strlen(remname) + 1;
2218 if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2219 mnttabtext[0] = '\0';
2222 if (mnttabtext != NULL)
2223 strcat(mnttabtext, remname);
2225 } else {
2226 char *tmp;
2227 int more_cnt = 0;
2228 char sport[16];
2230 more_cnt += strlen("nfs://");
2231 more_cnt += strlen(mfs->mfs_host);
2233 if (mfs->mfs_port != 0) {
2234 (void) sprintf(sport, ":%u", mfs->mfs_port);
2235 } else
2236 sport[0] = '\0';
2238 more_cnt += strlen(sport);
2239 more_cnt += 1; /* "/" */
2240 more_cnt += strlen(mfs->mfs_dir);
2242 if (mnttabcnt) {
2243 more_cnt += 1; /* "," */
2244 mnttabcnt += more_cnt;
2246 if ((tmp = realloc(mnttabtext,
2247 mnttabcnt)) != NULL) {
2248 mnttabtext = tmp;
2249 strcat(mnttabtext, ",");
2250 } else {
2251 free(mnttabtext);
2252 mnttabtext = NULL;
2254 } else {
2255 mnttabcnt = more_cnt + 1;
2256 if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2257 mnttabtext[0] = '\0';
2260 if (mnttabtext != NULL) {
2261 strcat(mnttabtext, "nfs://");
2262 strcat(mnttabtext, mfs->mfs_host);
2263 strcat(mnttabtext, sport);
2264 strcat(mnttabtext, "/");
2265 strcat(mnttabtext, mfs->mfs_dir);
2269 if (!mnttabtext) {
2270 syslog(LOG_ERR, "nfsmount: no memory");
2271 last_error = NFSERR_IO;
2272 goto out;
2276 * At least one entry, can call mount(2).
2278 entries++;
2281 * If replication was defeated, don't do more work
2283 if (!replicated)
2284 break;
2289 * Did we get through all possibilities without success?
2291 if (!entries)
2292 goto out;
2294 /* Make "xattr" the default if "noxattr" is not specified. */
2295 strcpy(mopts, opts);
2296 if (!hasmntopt(&m, MNTOPT_NOXATTR) && !hasmntopt(&m, MNTOPT_XATTR)) {
2297 if (strlen(mopts) > 0)
2298 strcat(mopts, ",");
2299 strcat(mopts, "xattr");
2303 * enable services as needed.
2306 char **sl;
2308 if (strcmp(fstype, MNTTYPE_NFS4) == 0)
2309 sl = service_list_v4;
2310 else
2311 sl = service_list;
2313 (void) _check_services(sl);
2317 * Whew; do the mount, at last.
2319 if (trace > 1) {
2320 trace_prt(1, " mount %s %s (%s)\n", mnttabtext, mntpnt, mopts);
2324 * About to do a nfs mount, make sure the mount_to is set for
2325 * potential ephemeral mounts with NFSv4.
2327 set_nfsv4_ephemeral_mount_to();
2330 * If no action list pointer then do the mount, otherwise
2331 * build the actions list pointer with the mount information.
2332 * so the mount can be done in the kernel.
2334 if (alp == NULL) {
2335 if (mount(mnttabtext, mntpnt, flags | MS_DATA, fstype,
2336 head, sizeof (*head), mopts, MAX_MNTOPT_STR) < 0) {
2337 if (trace > 1)
2338 trace_prt(1, " Mount of %s on %s: %d\n",
2339 mnttabtext, mntpnt, errno);
2340 if (errno != EBUSY || verbose)
2341 syslog(LOG_ERR,
2342 "Mount of %s on %s: %m", mnttabtext, mntpnt);
2343 last_error = NFSERR_IO;
2344 goto out;
2347 last_error = NFS_OK;
2348 if (stat(mntpnt, &stbuf) == 0) {
2349 if (trace > 1) {
2350 trace_prt(1, " mount %s dev=%x rdev=%x OK\n",
2351 mnttabtext, stbuf.st_dev, stbuf.st_rdev);
2353 } else {
2354 if (trace > 1) {
2355 trace_prt(1, " mount %s OK\n", mnttabtext);
2356 trace_prt(1, " stat of %s failed\n", mntpnt);
2360 } else {
2361 alp->action.action = AUTOFS_MOUNT_RQ;
2362 alp->action.action_list_entry_u.mounta.spec =
2363 strdup(mnttabtext);
2364 alp->action.action_list_entry_u.mounta.dir = strdup(mntpnt);
2365 alp->action.action_list_entry_u.mounta.flags =
2366 flags | MS_DATA;
2367 alp->action.action_list_entry_u.mounta.fstype =
2368 strdup(fstype);
2369 alp->action.action_list_entry_u.mounta.dataptr = (char *)head;
2370 alp->action.action_list_entry_u.mounta.datalen =
2371 sizeof (*head);
2372 mntopts = malloc(strlen(mopts) + 1);
2373 strcpy(mntopts, mopts);
2374 mntopts[strlen(mopts)] = '\0';
2375 alp->action.action_list_entry_u.mounta.optptr = mntopts;
2376 alp->action.action_list_entry_u.mounta.optlen =
2377 strlen(mntopts) + 1;
2378 last_error = NFS_OK;
2379 goto ret;
2382 out:
2383 argp = head;
2384 while (argp) {
2385 if (argp->pathconf)
2386 free(argp->pathconf);
2387 free_knconf(argp->knconf);
2388 netbuf_free(argp->addr);
2389 if (argp->syncaddr)
2390 netbuf_free(argp->syncaddr);
2391 if (argp->netname) {
2392 free(argp->netname);
2394 if (argp->hostname)
2395 free(argp->hostname);
2396 nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
2397 free(argp->fh);
2398 head = argp;
2399 argp = argp->nfs_ext_u.nfs_extB.next;
2400 free(head);
2402 ret:
2403 if (nfs_proto)
2404 free(nfs_proto);
2405 if (mnttabtext)
2406 free(mnttabtext);
2408 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
2410 if (mfs->mfs_flags & MFS_ALLOC_DIR) {
2411 free(mfs->mfs_dir);
2412 mfs->mfs_dir = NULL;
2413 mfs->mfs_flags &= ~MFS_ALLOC_DIR;
2416 if (mfs->mfs_args != NULL && alp == NULL) {
2417 free(mfs->mfs_args);
2418 mfs->mfs_args = NULL;
2421 if (mfs->mfs_nconf != NULL) {
2422 freenetconfigent(mfs->mfs_nconf);
2423 mfs->mfs_nconf = NULL;
2427 return (last_error);
2431 * get_pathconf(cl, path, fsname, pcnf, cretries)
2432 * ugliness that requires that ppathcnf and pathcnf stay consistent
2433 * cretries is a copy of retries used to determine when to syslog
2434 * on retry situations.
2436 static int
2437 get_pathconf(CLIENT *cl, char *path, char *fsname, struct pathcnf **pcnf,
2438 int cretries)
2440 struct ppathcnf *p = NULL;
2441 enum clnt_stat rpc_stat;
2442 struct timeval timeout;
2444 p = (struct ppathcnf *)malloc(sizeof (struct ppathcnf));
2445 if (p == NULL) {
2446 syslog(LOG_ERR, "get_pathconf: Out of memory");
2447 return (RET_ERR);
2449 memset((caddr_t)p, 0, sizeof (struct ppathcnf));
2451 timeout.tv_sec = 10;
2452 timeout.tv_usec = 0;
2453 rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
2454 xdr_dirpath, (caddr_t)&path, xdr_ppathcnf, (caddr_t)p, timeout);
2455 if (rpc_stat != RPC_SUCCESS) {
2456 if (cretries-- <= 0) {
2457 syslog(LOG_ERR,
2458 "get_pathconf: %s: server not responding: %s",
2459 fsname, clnt_sperror(cl, ""));
2461 free(p);
2462 return (RET_RETRY);
2464 if (_PC_ISSET(_PC_ERROR, p->pc_mask)) {
2465 syslog(LOG_ERR, "get_pathconf: no info for %s", fsname);
2466 free(p);
2467 return (RET_ERR);
2469 *pcnf = (struct pathcnf *)p;
2470 return (RET_OK);
2473 void
2474 netbuf_free(nb)
2475 struct netbuf *nb;
2477 if (nb == NULL)
2478 return;
2479 if (nb->buf)
2480 free(nb->buf);
2481 free(nb);
2484 #define SMALL_HOSTNAME 20
2485 #define SMALL_PROTONAME 10
2486 #define SMALL_PROTOFMLYNAME 10
2488 struct portmap_cache {
2489 int cache_prog;
2490 int cache_vers;
2491 time_t cache_time;
2492 char cache_small_hosts[SMALL_HOSTNAME + 1];
2493 char *cache_hostname;
2494 char *cache_proto;
2495 char *cache_protofmly;
2496 char cache_small_protofmly[SMALL_PROTOFMLYNAME + 1];
2497 char cache_small_proto[SMALL_PROTONAME + 1];
2498 struct netbuf cache_srv_addr;
2499 struct portmap_cache *cache_prev, *cache_next;
2502 rwlock_t portmap_cache_lock;
2503 static int portmap_cache_valid_time = 30;
2504 struct portmap_cache *portmap_cache_head, *portmap_cache_tail;
2506 #ifdef MALLOC_DEBUG
2507 void
2508 portmap_cache_flush()
2510 struct portmap_cache *next = NULL, *cp;
2512 (void) rw_wrlock(&portmap_cache_lock);
2513 for (cp = portmap_cache_head; cp; cp = cp->cache_next) {
2514 if (cp->cache_hostname != NULL &&
2515 cp->cache_hostname !=
2516 cp->cache_small_hosts)
2517 free(cp->cache_hostname);
2518 if (cp->cache_proto != NULL &&
2519 cp->cache_proto !=
2520 cp->cache_small_proto)
2521 free(cp->cache_proto);
2522 if (cp->cache_srv_addr.buf != NULL)
2523 free(cp->cache_srv_addr.buf);
2524 next = cp->cache_next;
2525 free(cp);
2527 portmap_cache_head = NULL;
2528 portmap_cache_tail = NULL;
2529 (void) rw_unlock(&portmap_cache_lock);
2531 #endif
2534 * Returns 1 if the entry is found in the cache, 0 otherwise.
2536 static int
2537 portmap_cache_lookup(hostname, prog, vers, nconf, addrp)
2538 char *hostname;
2539 rpcprog_t prog;
2540 rpcvers_t vers;
2541 struct netconfig *nconf;
2542 struct netbuf *addrp;
2544 struct portmap_cache *cachep, *prev, *next = NULL, *cp;
2545 int retval = 0;
2547 timenow = time(NULL);
2549 (void) rw_rdlock(&portmap_cache_lock);
2552 * Increment the portmap cache counters for # accesses and lookups
2553 * Use a smaller factor (100 vs 1000 for the host cache) since
2554 * initial analysis shows this cache is looked up 10% that of the
2555 * host cache.
2557 #ifdef CACHE_DEBUG
2558 portmap_cache_accesses++;
2559 portmap_cache_lookups++;
2560 if ((portmap_cache_lookups%100) == 0)
2561 trace_portmap_cache();
2562 #endif /* CACHE_DEBUG */
2564 for (cachep = portmap_cache_head; cachep;
2565 cachep = cachep->cache_next) {
2566 if (timenow > cachep->cache_time) {
2568 * We stumbled across an entry in the cache which
2569 * has timed out. Free up all the entries that
2570 * were added before it, which will positionally
2571 * be after this entry. And adjust neighboring
2572 * pointers.
2573 * When we drop the lock and re-acquire it, we
2574 * need to start from the beginning.
2576 (void) rw_unlock(&portmap_cache_lock);
2577 (void) rw_wrlock(&portmap_cache_lock);
2578 for (cp = portmap_cache_head;
2579 cp && (cp->cache_time >= timenow);
2580 cp = cp->cache_next)
2582 if (cp == NULL)
2583 goto done;
2585 * Adjust the link of the predecessor.
2586 * Make the tail point to the new last entry.
2588 prev = cp->cache_prev;
2589 if (prev == NULL) {
2590 portmap_cache_head = NULL;
2591 portmap_cache_tail = NULL;
2592 } else {
2593 prev->cache_next = NULL;
2594 portmap_cache_tail = prev;
2596 for (; cp; cp = next) {
2597 if (cp->cache_hostname != NULL &&
2598 cp->cache_hostname !=
2599 cp->cache_small_hosts)
2600 free(cp->cache_hostname);
2601 if (cp->cache_proto != NULL &&
2602 cp->cache_proto !=
2603 cp->cache_small_proto)
2604 free(cp->cache_proto);
2605 if (cp->cache_srv_addr.buf != NULL)
2606 free(cp->cache_srv_addr.buf);
2607 next = cp->cache_next;
2608 free(cp);
2610 goto done;
2612 if (cachep->cache_hostname == NULL ||
2613 prog != cachep->cache_prog || vers != cachep->cache_vers ||
2614 strcmp(nconf->nc_proto, cachep->cache_proto) != 0 ||
2615 strcmp(nconf->nc_protofmly, cachep->cache_protofmly) != 0 ||
2616 strcmp(hostname, cachep->cache_hostname) != 0)
2617 continue;
2619 * Cache Hit.
2621 #ifdef CACHE_DEBUG
2622 portmap_cache_hits++; /* up portmap cache hit counter */
2623 #endif /* CACHE_DEBUG */
2624 addrp->len = cachep->cache_srv_addr.len;
2625 memcpy(addrp->buf, cachep->cache_srv_addr.buf, addrp->len);
2626 retval = 1;
2627 break;
2629 done:
2630 (void) rw_unlock(&portmap_cache_lock);
2631 return (retval);
2634 static void
2635 portmap_cache_enter(hostname, prog, vers, nconf, addrp)
2636 char *hostname;
2637 rpcprog_t prog;
2638 rpcvers_t vers;
2639 struct netconfig *nconf;
2640 struct netbuf *addrp;
2642 struct portmap_cache *cachep;
2643 int protofmlylen;
2644 int protolen, hostnamelen;
2646 timenow = time(NULL);
2648 cachep = malloc(sizeof (struct portmap_cache));
2649 if (cachep == NULL)
2650 return;
2651 memset((char *)cachep, 0, sizeof (*cachep));
2653 hostnamelen = strlen(hostname);
2654 if (hostnamelen <= SMALL_HOSTNAME)
2655 cachep->cache_hostname = cachep->cache_small_hosts;
2656 else {
2657 cachep->cache_hostname = malloc(hostnamelen + 1);
2658 if (cachep->cache_hostname == NULL)
2659 goto nomem;
2661 strcpy(cachep->cache_hostname, hostname);
2662 protolen = strlen(nconf->nc_proto);
2663 if (protolen <= SMALL_PROTONAME)
2664 cachep->cache_proto = cachep->cache_small_proto;
2665 else {
2666 cachep->cache_proto = malloc(protolen + 1);
2667 if (cachep->cache_proto == NULL)
2668 goto nomem;
2670 protofmlylen = strlen(nconf->nc_protofmly);
2671 if (protofmlylen <= SMALL_PROTOFMLYNAME)
2672 cachep->cache_protofmly = cachep->cache_small_protofmly;
2673 else {
2674 cachep->cache_protofmly = malloc(protofmlylen + 1);
2675 if (cachep->cache_protofmly == NULL)
2676 goto nomem;
2679 strcpy(cachep->cache_proto, nconf->nc_proto);
2680 cachep->cache_prog = prog;
2681 cachep->cache_vers = vers;
2682 cachep->cache_time = timenow + portmap_cache_valid_time;
2683 cachep->cache_srv_addr.len = addrp->len;
2684 cachep->cache_srv_addr.buf = malloc(addrp->len);
2685 if (cachep->cache_srv_addr.buf == NULL)
2686 goto nomem;
2687 memcpy(cachep->cache_srv_addr.buf, addrp->buf, addrp->maxlen);
2688 cachep->cache_prev = NULL;
2689 (void) rw_wrlock(&portmap_cache_lock);
2691 * There's a window in which we could have multiple threads making
2692 * the same cache entry. This can be avoided by walking the cache
2693 * once again here to check and see if there are duplicate entries
2694 * (after grabbing the write lock). This isn't fatal and I'm not
2695 * going to bother with this.
2697 #ifdef CACHE_DEBUG
2698 portmap_cache_accesses++; /* up portmap cache access counter */
2699 #endif /* CACHE_DEBUG */
2700 cachep->cache_next = portmap_cache_head;
2701 if (portmap_cache_head != NULL)
2702 portmap_cache_head->cache_prev = cachep;
2703 portmap_cache_head = cachep;
2704 (void) rw_unlock(&portmap_cache_lock);
2705 return;
2707 nomem:
2708 syslog(LOG_ERR, "portmap_cache_enter: Memory allocation failed");
2709 if (cachep->cache_srv_addr.buf)
2710 free(cachep->cache_srv_addr.buf);
2711 if (cachep->cache_proto && protolen > SMALL_PROTONAME)
2712 free(cachep->cache_proto);
2713 if (cachep->cache_hostname && hostnamelen > SMALL_HOSTNAME)
2714 free(cachep->cache_hostname);
2715 if (cachep->cache_protofmly && protofmlylen > SMALL_PROTOFMLYNAME)
2716 free(cachep->cache_protofmly);
2717 if (cachep)
2718 free(cachep);
2719 cachep = NULL;
2722 static int
2723 get_cached_srv_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
2724 struct netconfig *nconf, struct netbuf *addrp)
2726 if (portmap_cache_lookup(hostname, prog, vers, nconf, addrp))
2727 return (1);
2728 if (rpcb_getaddr(prog, vers, nconf, addrp, hostname) == 0)
2729 return (0);
2730 portmap_cache_enter(hostname, prog, vers, nconf, addrp);
2731 return (1);
2735 * Get a network address on "hostname" for program "prog"
2736 * with version "vers". If the port number is specified (non zero)
2737 * then try for a TCP/UDP transport and set the port number of the
2738 * resulting IP address.
2740 * If the address of a netconfig pointer was passed and
2741 * if it's not null, use it as the netconfig otherwise
2742 * assign the address of the netconfig that was used to
2743 * establish contact with the service.
2745 * tinfo argument is for matching the get_addr() defined in
2746 * ../nfs/mount/mount.c
2749 static struct netbuf *
2750 get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
2751 struct netconfig **nconfp, char *proto, ushort_t port,
2752 struct t_info *tinfo)
2755 enum clnt_stat cstat;
2757 return (get_server_netinfo(SERVER_ADDR, hostname, prog, vers, NULL,
2758 nconfp, proto, port, tinfo, NULL, FALSE, NULL, &cstat));
2761 static struct netbuf *
2762 get_pubfh(char *hostname, rpcvers_t vers, mfs_snego_t *mfssnego,
2763 struct netconfig **nconfp, char *proto, ushort_t port,
2764 struct t_info *tinfo, caddr_t *fhp, bool_t get_pubfh, char *fspath)
2766 enum clnt_stat cstat;
2768 return (get_server_netinfo(SERVER_FH, hostname, NFS_PROGRAM, vers,
2769 mfssnego, nconfp, proto, port, tinfo, fhp, get_pubfh, fspath,
2770 &cstat));
2773 static enum clnt_stat
2774 get_ping(char *hostname, rpcprog_t prog, rpcvers_t vers,
2775 struct netconfig **nconfp, ushort_t port, bool_t direct_to_server)
2777 enum clnt_stat cstat;
2779 (void) get_server_netinfo(SERVER_PING, hostname, prog,
2780 vers, NULL, nconfp, NULL, port, NULL, NULL,
2781 direct_to_server, NULL, &cstat);
2783 return (cstat);
2786 void *
2787 get_server_netinfo(
2788 enum type_of_stuff type_of_stuff,
2789 char *hostname,
2790 rpcprog_t prog,
2791 rpcvers_t vers,
2792 mfs_snego_t *mfssnego,
2793 struct netconfig **nconfp,
2794 char *proto,
2795 ushort_t port, /* may be zero */
2796 struct t_info *tinfo,
2797 caddr_t *fhp,
2798 bool_t direct_to_server,
2799 char *fspath,
2800 enum clnt_stat *cstatp)
2802 struct netbuf *nb = NULL;
2803 struct netconfig *nconf = NULL;
2804 NCONF_HANDLE *nc = NULL;
2805 int error = 0;
2806 int fd = 0;
2807 struct t_bind *tbind = NULL;
2808 int nthtry = FIRST_TRY;
2810 if (nconfp && *nconfp) {
2811 return (get_netconfig_info(type_of_stuff, hostname,
2812 prog, vers, nconf, port, tinfo, tbind, fhp,
2813 direct_to_server, fspath, cstatp, mfssnego));
2817 * No nconf passed in.
2819 * Try to get a nconf from /etc/netconfig.
2820 * First choice is COTS, second is CLTS unless proto
2821 * is specified. When we retry, we reset the
2822 * netconfig list, so that we search the whole list
2823 * for the next choice.
2825 if ((nc = setnetpath()) == NULL)
2826 goto done;
2829 * If proto is specified, then only search for the match,
2830 * otherwise try COTS first, if failed, then try CLTS.
2832 if (proto) {
2833 while ((nconf = getnetpath(nc)) != NULL) {
2834 if (strcmp(nconf->nc_proto, proto))
2835 continue;
2837 * If the port number is specified then TCP/UDP
2838 * is needed. Otherwise any cots/clts will do.
2840 if (port) {
2841 if ((strcmp(nconf->nc_protofmly, NC_INET) &&
2842 strcmp(nconf->nc_protofmly, NC_INET6)) ||
2843 (strcmp(nconf->nc_proto, NC_TCP) &&
2844 strcmp(nconf->nc_proto, NC_UDP)))
2845 continue;
2847 nb = get_netconfig_info(type_of_stuff, hostname,
2848 prog, vers, nconf, port, tinfo, tbind, fhp,
2849 direct_to_server, fspath, cstatp, mfssnego);
2850 if (*cstatp == RPC_SUCCESS)
2851 break;
2853 assert(nb == NULL);
2856 if (nconf == NULL)
2857 goto done;
2858 } else {
2859 retry:
2860 while ((nconf = getnetpath(nc)) != NULL) {
2861 if (nconf->nc_flag & NC_VISIBLE) {
2862 if (nthtry == FIRST_TRY) {
2863 if ((nconf->nc_semantics ==
2864 NC_TPI_COTS_ORD) ||
2865 (nconf->nc_semantics ==
2866 NC_TPI_COTS)) {
2867 if (port == 0)
2868 break;
2869 if ((strcmp(nconf->nc_protofmly,
2870 NC_INET) == 0 ||
2871 strcmp(nconf->nc_protofmly,
2872 NC_INET6) == 0) &&
2873 (strcmp(nconf->nc_proto,
2874 NC_TCP) == 0))
2875 break;
2878 if (nthtry == SECOND_TRY) {
2879 if (nconf->nc_semantics ==
2880 NC_TPI_CLTS) {
2881 if (port == 0)
2882 break;
2883 if ((strcmp(nconf->nc_protofmly,
2884 NC_INET) == 0 ||
2885 strcmp(nconf->nc_protofmly,
2886 NC_INET6) == 0) &&
2887 (strcmp(nconf->nc_proto,
2888 NC_UDP) == 0))
2889 break;
2895 if (nconf == NULL) {
2896 if (++nthtry <= MNT_PREF_LISTLEN) {
2897 endnetpath(nc);
2898 if ((nc = setnetpath()) == NULL)
2899 goto done;
2900 goto retry;
2901 } else
2902 goto done;
2903 } else {
2904 nb = get_netconfig_info(type_of_stuff, hostname,
2905 prog, vers, nconf, port, tinfo, tbind, fhp,
2906 direct_to_server, fspath, cstatp, mfssnego);
2907 if (*cstatp != RPC_SUCCESS)
2909 * Continue the same search path in the
2910 * netconfig db until no more matched nconf
2911 * (nconf == NULL).
2913 goto retry;
2918 * Got nconf and nb. Now dup the netconfig structure (nconf)
2919 * and return it thru nconfp.
2921 if (nconf != NULL) {
2922 if ((*nconfp = getnetconfigent(nconf->nc_netid)) == NULL) {
2923 syslog(LOG_ERR, "no memory\n");
2924 free(nb);
2925 nb = NULL;
2927 } else {
2928 *nconfp = NULL;
2930 done:
2931 if (nc)
2932 endnetpath(nc);
2933 return (nb);
2936 void *
2937 get_server_fh(char *hostname, rpcprog_t prog, rpcvers_t vers,
2938 mfs_snego_t *mfssnego, struct netconfig *nconf, ushort_t port,
2939 struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp,
2940 bool_t direct_to_server, char *fspath, enum clnt_stat *cstat)
2942 AUTH *ah = NULL;
2943 AUTH *new_ah = NULL;
2944 struct snego_t snego;
2945 enum clnt_stat cs = RPC_TIMEDOUT;
2946 struct timeval tv;
2947 bool_t file_handle = 1;
2948 enum snego_stat sec;
2949 CLIENT *cl = NULL;
2950 int fd = -1;
2951 struct netbuf *nb = NULL;
2953 if (direct_to_server != TRUE)
2954 return (NULL);
2956 if (prog == NFS_PROGRAM && vers == NFS_V4)
2957 if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
2958 goto done;
2960 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0)
2961 goto done;
2963 /* LINTED pointer alignment */
2964 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
2965 goto done;
2967 if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd,
2968 direct_to_server, port, prog, vers, file_handle) < 0) {
2969 goto done;
2972 cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
2973 if (cl == NULL)
2974 goto done;
2976 ah = authsys_create_default();
2977 if (ah != NULL) {
2978 #ifdef MALLOC_DEBUG
2979 drop_alloc("AUTH_HANDLE", cl->cl_auth,
2980 __FILE__, __LINE__);
2981 #endif
2982 AUTH_DESTROY(cl->cl_auth);
2983 cl->cl_auth = ah;
2984 #ifdef MALLOC_DEBUG
2985 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
2986 __FILE__, __LINE__);
2987 #endif
2990 if (!mfssnego->snego_done && vers != NFS_V4) {
2992 * negotiate sec flavor.
2994 snego.cnt = 0;
2995 if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
2996 SNEGO_SUCCESS) {
2997 int jj;
3000 * check if server supports the one
3001 * specified in the sec= option.
3003 if (mfssnego->sec_opt) {
3004 for (jj = 0; jj < snego.cnt; jj++) {
3005 if (snego.array[jj] ==
3006 mfssnego->nfs_sec.sc_nfsnum) {
3007 mfssnego->snego_done = TRUE;
3008 break;
3014 * find a common sec flavor
3016 if (!mfssnego->snego_done) {
3017 for (jj = 0; jj < snego.cnt; jj++) {
3018 if (!nfs_getseconfig_bynumber(
3019 snego.array[jj],
3020 &mfssnego->nfs_sec)) {
3021 mfssnego->snego_done = TRUE;
3022 break;
3026 if (!mfssnego->snego_done)
3027 goto done;
3029 * Now that the flavor has been
3030 * negotiated, get the fh.
3032 * First, create an auth handle using the negotiated
3033 * sec flavor in the next lookup to
3034 * fetch the filehandle.
3036 new_ah = nfs_create_ah(cl, hostname,
3037 &mfssnego->nfs_sec);
3038 if (new_ah == NULL)
3039 goto done;
3040 #ifdef MALLOC_DEBUG
3041 drop_alloc("AUTH_HANDLE", cl->cl_auth,
3042 __FILE__, __LINE__);
3043 #endif
3044 AUTH_DESTROY(cl->cl_auth);
3045 cl->cl_auth = new_ah;
3046 #ifdef MALLOC_DEBUG
3047 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3048 __FILE__, __LINE__);
3049 #endif
3050 } else if (sec == SNEGO_ARRAY_TOO_SMALL ||
3051 sec == SNEGO_FAILURE) {
3052 goto done;
3056 switch (vers) {
3057 case NFS_VERSION:
3059 wnl_diropargs arg;
3060 wnl_diropres res;
3062 memset((char *)&arg.dir, 0, sizeof (wnl_fh));
3063 memset((char *)&res, 0, sizeof (wnl_diropres));
3064 arg.name = fspath;
3065 if (wnlproc_lookup_2(&arg, &res, cl) !=
3066 RPC_SUCCESS || res.status != WNL_OK)
3067 goto done;
3068 *fhp = malloc(sizeof (wnl_fh));
3070 if (*fhp == NULL) {
3071 syslog(LOG_ERR, "no memory\n");
3072 goto done;
3075 memcpy((char *)*fhp,
3076 (char *)&res.wnl_diropres_u.wnl_diropres.file,
3077 sizeof (wnl_fh));
3078 cs = RPC_SUCCESS;
3080 break;
3081 case NFS_V3:
3083 WNL_LOOKUP3args arg;
3084 WNL_LOOKUP3res res;
3085 nfs_fh3 *fh3p;
3087 memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
3088 memset((char *)&res, 0, sizeof (WNL_LOOKUP3res));
3089 arg.what.name = fspath;
3090 if (wnlproc3_lookup_3(&arg, &res, cl) !=
3091 RPC_SUCCESS || res.status != WNL3_OK)
3092 goto done;
3094 fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
3096 if (fh3p == NULL) {
3097 syslog(LOG_ERR, "no memory\n");
3098 goto done;
3101 fh3p->fh3_length =
3102 res.WNL_LOOKUP3res_u.res_ok.object.data.data_len;
3103 memcpy(fh3p->fh3_u.data,
3104 res.WNL_LOOKUP3res_u.res_ok.object.data.data_val,
3105 fh3p->fh3_length);
3107 *fhp = (caddr_t)fh3p;
3109 cs = RPC_SUCCESS;
3111 break;
3112 case NFS_V4:
3113 tv.tv_sec = 10;
3114 tv.tv_usec = 0;
3115 cs = clnt_call(cl, NULLPROC, xdr_void, 0,
3116 xdr_void, 0, tv);
3117 if (cs != RPC_SUCCESS)
3118 goto done;
3120 *fhp = strdup(fspath);
3121 if (fhp == NULL) {
3122 cs = RPC_SYSTEMERROR;
3123 goto done;
3125 break;
3127 nb = (struct netbuf *)malloc(sizeof (struct netbuf));
3128 if (nb == NULL) {
3129 syslog(LOG_ERR, "no memory\n");
3130 cs = RPC_SYSTEMERROR;
3131 goto done;
3133 nb->buf = (char *)malloc(tbind->addr.maxlen);
3134 if (nb->buf == NULL) {
3135 syslog(LOG_ERR, "no memory\n");
3136 free(nb);
3137 nb = NULL;
3138 cs = RPC_SYSTEMERROR;
3139 goto done;
3141 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
3142 nb->len = tbind->addr.len;
3143 nb->maxlen = tbind->addr.maxlen;
3144 done:
3145 if (cstat != NULL)
3146 *cstat = cs;
3147 destroy_auth_client_handle(cl);
3148 cleanup_tli_parms(tbind, fd);
3149 return (nb);
3153 * Sends a null call to the remote host's (NFS program, versp). versp
3154 * may be "NULL" in which case the default maximum version is used.
3155 * Upon return, versp contains the maximum version supported iff versp!= NULL.
3157 enum clnt_stat
3158 pingnfs(
3159 char *hostpart,
3160 int attempts,
3161 rpcvers_t *versp,
3162 rpcvers_t versmin,
3163 ushort_t port, /* may be zero */
3164 bool_t usepub,
3165 char *path,
3166 char *proto)
3168 CLIENT *cl = NULL;
3169 struct timeval rpc_to_new = {15, 0};
3170 static struct timeval rpc_rtrans_new = {-1, -1};
3171 enum clnt_stat clnt_stat;
3172 int i, j;
3173 rpcvers_t versmax; /* maximum version to try against server */
3174 rpcvers_t outvers; /* version supported by host on last call */
3175 rpcvers_t vers_to_try; /* to try different versions against host */
3176 char *hostname;
3177 struct netconfig *nconf;
3179 hostname = strdup(hostpart);
3180 if (hostname == NULL) {
3181 return (RPC_SYSTEMERROR);
3183 unbracket(&hostname);
3185 if (path != NULL && strcmp(hostname, "nfs") == 0 &&
3186 strncmp(path, "//", 2) == 0) {
3187 char *sport;
3189 hostname = strdup(path+2);
3191 if (hostname == NULL)
3192 return (RPC_SYSTEMERROR);
3194 path = strchr(hostname, '/');
3197 * This cannot happen. If it does, give up
3198 * on the ping as this is obviously a corrupt
3199 * entry.
3201 if (path == NULL) {
3202 free(hostname);
3203 return (RPC_SUCCESS);
3207 * Probable end point of host string.
3209 *path = '\0';
3211 sport = strchr(hostname, ':');
3213 if (sport != NULL && sport < path) {
3216 * Actual end point of host string.
3218 *sport = '\0';
3219 port = htons((ushort_t)atoi(sport+1));
3222 usepub = TRUE;
3225 /* Pick up the default versions and then set them appropriately */
3226 if (versp) {
3227 versmax = *versp;
3228 /* use versmin passed in */
3229 } else {
3230 read_default_nfs();
3231 set_versrange(0, &versmax, &versmin);
3234 if (proto &&
3235 strncasecmp(proto, NC_UDP, strlen(NC_UDP)) == 0 &&
3236 versmax == NFS_V4) {
3237 if (versmin == NFS_V4) {
3238 if (versp) {
3239 *versp = versmax - 1;
3240 return (RPC_SUCCESS);
3242 return (RPC_PROGUNAVAIL);
3243 } else {
3244 versmax--;
3248 if (versp)
3249 *versp = versmax;
3251 switch (cache_check(hostname, versp, proto)) {
3252 case GOODHOST:
3253 if (hostname != hostpart)
3254 free(hostname);
3255 return (RPC_SUCCESS);
3256 case DEADHOST:
3257 if (hostname != hostpart)
3258 free(hostname);
3259 return (RPC_TIMEDOUT);
3260 case NOHOST:
3261 default:
3262 break;
3266 * XXX The retransmission time rpcbrmttime is a global defined
3267 * in the rpc library (rpcb_clnt.c). We use (and like) the default
3268 * value of 15 sec in the rpc library. The code below is to protect
3269 * us in case it changes. This need not be done under a lock since
3270 * any # of threads entering this function will get the same
3271 * retransmission value.
3273 if (rpc_rtrans_new.tv_sec == -1 && rpc_rtrans_new.tv_usec == -1) {
3274 __rpc_control(CLCR_GET_RPCB_RMTTIME, (char *)&rpc_rtrans_new);
3275 if (rpc_rtrans_new.tv_sec != 15 && rpc_rtrans_new.tv_sec != 0)
3276 if (trace > 1)
3277 trace_prt(1, "RPC library rttimer changed\n");
3281 * XXX Manipulate the total timeout to get the number of
3282 * desired retransmissions. This code is heavily dependant on
3283 * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3285 for (i = 0, j = rpc_rtrans_new.tv_sec; i < attempts-1; i++) {
3286 if (j < RPC_MAX_BACKOFF)
3287 j *= 2;
3288 else
3289 j = RPC_MAX_BACKOFF;
3290 rpc_to_new.tv_sec += j;
3293 vers_to_try = versmax;
3296 * check the host's version within the timeout
3298 if (trace > 1)
3299 trace_prt(1, " ping: %s timeout=%ld request vers=%d min=%d\n",
3300 hostname, rpc_to_new.tv_sec, versmax, versmin);
3302 if (usepub == FALSE) {
3303 do {
3305 * If NFSv4, then we do the same thing as is used
3306 * for public filehandles so that we avoid rpcbind
3308 if (vers_to_try == NFS_V4) {
3309 if (trace > 4) {
3310 trace_prt(1, " pingnfs: Trying ping via "
3311 "\"circuit_v\"\n");
3314 cl = clnt_create_service_timed(hostname, "nfs",
3315 NFS_PROGRAM, vers_to_try,
3316 port, "circuit_v", &rpc_to_new);
3317 if (cl != NULL) {
3318 outvers = vers_to_try;
3319 break;
3321 if (trace > 4) {
3322 trace_prt(1,
3323 " pingnfs: Can't ping via "
3324 "\"circuit_v\" %s: RPC error=%d\n",
3325 hostname, rpc_createerr.cf_stat);
3328 } else {
3329 cl = clnt_create_vers_timed(hostname,
3330 NFS_PROGRAM, &outvers, versmin, vers_to_try,
3331 "datagram_v", &rpc_to_new);
3332 if (cl != NULL)
3333 break;
3334 if (trace > 4) {
3335 trace_prt(1,
3336 " pingnfs: Can't ping via "
3337 "\"datagram_v\"%s: RPC error=%d\n",
3338 hostname, rpc_createerr.cf_stat);
3340 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
3341 rpc_createerr.cf_stat == RPC_TIMEDOUT)
3342 break;
3343 if (rpc_createerr.cf_stat ==
3344 RPC_PROGNOTREGISTERED) {
3345 if (trace > 4) {
3346 trace_prt(1,
3347 " pingnfs: Trying ping "
3348 "via \"circuit_v\"\n");
3350 cl = clnt_create_vers_timed(hostname,
3351 NFS_PROGRAM, &outvers,
3352 versmin, vers_to_try,
3353 "circuit_v", &rpc_to_new);
3354 if (cl != NULL)
3355 break;
3356 if (trace > 4) {
3357 trace_prt(1,
3358 " pingnfs: Can't ping "
3359 "via \"circuit_v\" %s: "
3360 "RPC error=%d\n",
3361 hostname,
3362 rpc_createerr.cf_stat);
3368 * backoff and return lower version to retry the ping.
3369 * XXX we should be more careful and handle
3370 * RPC_PROGVERSMISMATCH here, because that error is handled
3371 * in clnt_create_vers(). It's not done to stay in sync
3372 * with the nfs mount command.
3374 vers_to_try--;
3375 if (vers_to_try < versmin)
3376 break;
3377 if (versp != NULL) { /* recheck the cache */
3378 *versp = vers_to_try;
3379 if (trace > 4) {
3380 trace_prt(1,
3381 " pingnfs: check cache: vers=%d\n",
3382 *versp);
3384 switch (cache_check(hostname, versp, proto)) {
3385 case GOODHOST:
3386 if (hostname != hostpart)
3387 free(hostname);
3388 return (RPC_SUCCESS);
3389 case DEADHOST:
3390 if (hostname != hostpart)
3391 free(hostname);
3392 return (RPC_TIMEDOUT);
3393 case NOHOST:
3394 default:
3395 break;
3398 if (trace > 4) {
3399 trace_prt(1, " pingnfs: Try version=%d\n",
3400 vers_to_try);
3402 } while (cl == NULL);
3405 if (cl == NULL) {
3406 if (verbose)
3407 syslog(LOG_ERR, "pingnfs: %s%s",
3408 hostname, clnt_spcreateerror(""));
3409 clnt_stat = rpc_createerr.cf_stat;
3410 } else {
3411 clnt_destroy(cl);
3412 clnt_stat = RPC_SUCCESS;
3415 } else {
3416 for (vers_to_try = versmax; vers_to_try >= versmin;
3417 vers_to_try--) {
3419 nconf = NULL;
3421 if (trace > 4) {
3422 trace_prt(1, " pingnfs: Try version=%d "
3423 "using get_ping()\n", vers_to_try);
3426 clnt_stat = get_ping(hostname, NFS_PROGRAM,
3427 vers_to_try, &nconf, port, TRUE);
3429 if (nconf != NULL)
3430 freenetconfigent(nconf);
3432 if (clnt_stat == RPC_SUCCESS) {
3433 outvers = vers_to_try;
3434 break;
3439 if (trace > 1)
3440 clnt_stat == RPC_SUCCESS ?
3441 trace_prt(1, " pingnfs OK: nfs version=%d\n", outvers):
3442 trace_prt(1, " pingnfs FAIL: can't get nfs version\n");
3444 if (clnt_stat == RPC_SUCCESS) {
3445 cache_enter(hostname, versmax, outvers, proto, GOODHOST);
3446 if (versp != NULL)
3447 *versp = outvers;
3448 } else
3449 cache_enter(hostname, versmax, versmax, proto, DEADHOST);
3451 if (hostpart != hostname)
3452 free(hostname);
3454 return (clnt_stat);
3457 #define MNTTYPE_LOFS "lofs"
3460 loopbackmount(fsname, dir, mntopts, overlay)
3461 char *fsname; /* Directory being mounted */
3462 char *dir; /* Directory being mounted on */
3463 char *mntopts;
3464 int overlay;
3466 struct mnttab mnt;
3467 int flags = 0;
3468 char fstype[] = MNTTYPE_LOFS;
3469 int dirlen;
3470 struct stat st;
3471 char optbuf[MAX_MNTOPT_STR];
3473 dirlen = strlen(dir);
3474 if (dir[dirlen-1] == ' ')
3475 dirlen--;
3477 if (dirlen == strlen(fsname) &&
3478 strncmp(fsname, dir, dirlen) == 0) {
3479 syslog(LOG_ERR,
3480 "Mount of %s on %s would result in deadlock, aborted\n",
3481 fsname, dir);
3482 return (RET_ERR);
3484 mnt.mnt_mntopts = mntopts;
3485 if (hasmntopt(&mnt, MNTOPT_RO) != NULL)
3486 flags |= MS_RDONLY;
3488 (void) strlcpy(optbuf, mntopts, sizeof (optbuf));
3490 if (overlay)
3491 flags |= MS_OVERLAY;
3493 if (trace > 1)
3494 trace_prt(1,
3495 " loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3496 fsname, dir, flags);
3498 if (mount(fsname, dir, flags | MS_DATA | MS_OPTIONSTR, fstype,
3499 NULL, 0, optbuf, sizeof (optbuf)) < 0) {
3500 syslog(LOG_ERR, "Mount of %s on %s: %m", fsname, dir);
3501 return (RET_ERR);
3504 if (stat(dir, &st) == 0) {
3505 if (trace > 1) {
3506 trace_prt(1,
3507 " loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3508 fsname, dir, st.st_dev, st.st_rdev);
3510 } else {
3511 if (trace > 1) {
3512 trace_prt(1,
3513 " loopbackmount of %s on %s OK\n", fsname, dir);
3514 trace_prt(1, " stat of %s failed\n", dir);
3518 return (0);
3522 * Look for the value of a numeric option of the form foo=x. If found, set
3523 * *valp to the value and return non-zero. If not found or the option is
3524 * malformed, return zero.
3528 nopt(mnt, opt, valp)
3529 struct mnttab *mnt;
3530 char *opt;
3531 int *valp; /* OUT */
3533 char *equal;
3534 char *str;
3537 * We should never get a null pointer, but if we do, it's better to
3538 * ignore the option than to dump core.
3541 if (valp == NULL) {
3542 syslog(LOG_DEBUG, "null pointer for %s option", opt);
3543 return (0);
3546 if (str = hasmntopt(mnt, opt)) {
3547 if (equal = strchr(str, '=')) {
3548 *valp = atoi(&equal[1]);
3549 return (1);
3550 } else {
3551 syslog(LOG_ERR, "Bad numeric option '%s'", str);
3554 return (0);
3558 nfsunmount(mnt)
3559 struct mnttab *mnt;
3561 struct timeval timeout;
3562 CLIENT *cl;
3563 enum clnt_stat rpc_stat;
3564 char *host, *path;
3565 struct replica *list;
3566 int i, count = 0;
3567 int isv4mount = is_v4_mount(mnt->mnt_mountp);
3569 if (trace > 1)
3570 trace_prt(1, " nfsunmount: umount %s\n", mnt->mnt_mountp);
3572 if (umount(mnt->mnt_mountp) < 0) {
3573 if (trace > 1)
3574 trace_prt(1, " nfsunmount: umount %s FAILED\n",
3575 mnt->mnt_mountp);
3576 if (errno)
3577 return (errno);
3581 * If this is a NFSv4 mount, the mount protocol was not used
3582 * so we just return.
3584 if (isv4mount) {
3585 if (trace > 1)
3586 trace_prt(1, " nfsunmount: umount %s OK\n",
3587 mnt->mnt_mountp);
3588 return (0);
3592 * If mounted with -o public, then no need to contact server
3593 * because mount protocol was not used.
3595 if (hasmntopt(mnt, MNTOPT_PUBLIC) != NULL) {
3596 return (0);
3600 * The rest of this code is advisory to the server.
3601 * If it fails return success anyway.
3604 list = parse_replica(mnt->mnt_special, &count);
3605 if (!list) {
3606 if (count >= 0)
3607 syslog(LOG_ERR,
3608 "Memory allocation failed: %m");
3609 return (ENOMEM);
3612 for (i = 0; i < count; i++) {
3614 host = list[i].host;
3615 path = list[i].path;
3618 * Skip file systems mounted using WebNFS, because mount
3619 * protocol was not used.
3621 if (strcmp(host, "nfs") == 0 && strncmp(path, "//", 2) == 0)
3622 continue;
3624 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v");
3625 if (cl == NULL)
3626 break;
3627 #ifdef MALLOC_DEBUG
3628 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
3629 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3630 __FILE__, __LINE__);
3631 #endif
3632 if (__clnt_bindresvport(cl) < 0) {
3633 if (verbose)
3634 syslog(LOG_ERR, "umount %s:%s: %s",
3635 host, path,
3636 "Couldn't bind to reserved port");
3637 destroy_auth_client_handle(cl);
3638 continue;
3640 #ifdef MALLOC_DEBUG
3641 drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__);
3642 #endif
3643 AUTH_DESTROY(cl->cl_auth);
3644 if ((cl->cl_auth = authsys_create_default()) == NULL) {
3645 if (verbose)
3646 syslog(LOG_ERR, "umount %s:%s: %s",
3647 host, path,
3648 "Failed creating default auth handle");
3649 destroy_auth_client_handle(cl);
3650 continue;
3652 #ifdef MALLOC_DEBUG
3653 add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__);
3654 #endif
3655 timeout.tv_usec = 0;
3656 timeout.tv_sec = 5;
3657 rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath,
3658 (caddr_t)&path, xdr_void, NULL, timeout);
3659 if (verbose && rpc_stat != RPC_SUCCESS)
3660 syslog(LOG_ERR, "%s: %s",
3661 host, clnt_sperror(cl, "unmount"));
3662 destroy_auth_client_handle(cl);
3665 free_replica(list, count);
3667 if (trace > 1)
3668 trace_prt(1, " nfsunmount: umount %s OK\n", mnt->mnt_mountp);
3670 done:
3671 return (0);
3675 * Put a new entry in the cache chain by prepending it to the front.
3676 * If there isn't enough memory then just give up.
3678 static void
3679 cache_enter(host, reqvers, outvers, proto, state)
3680 char *host;
3681 rpcvers_t reqvers;
3682 rpcvers_t outvers;
3683 char *proto;
3684 int state;
3686 struct cache_entry *entry;
3687 int cache_time = 30; /* sec */
3689 timenow = time(NULL);
3691 entry = (struct cache_entry *)malloc(sizeof (struct cache_entry));
3692 if (entry == NULL)
3693 return;
3694 (void) memset((caddr_t)entry, 0, sizeof (struct cache_entry));
3695 entry->cache_host = strdup(host);
3696 if (entry->cache_host == NULL) {
3697 cache_free(entry);
3698 return;
3700 entry->cache_reqvers = reqvers;
3701 entry->cache_outvers = outvers;
3702 entry->cache_proto = (proto == NULL ? NULL : strdup(proto));
3703 entry->cache_state = state;
3704 entry->cache_time = timenow + cache_time;
3705 (void) rw_wrlock(&cache_lock);
3706 #ifdef CACHE_DEBUG
3707 host_cache_accesses++; /* up host cache access counter */
3708 #endif /* CACHE DEBUG */
3709 entry->cache_next = cache_head;
3710 cache_head = entry;
3711 (void) rw_unlock(&cache_lock);
3714 static int
3715 cache_check(host, versp, proto)
3716 char *host;
3717 rpcvers_t *versp;
3718 char *proto;
3720 int state = NOHOST;
3721 struct cache_entry *ce, *prev;
3723 timenow = time(NULL);
3725 (void) rw_rdlock(&cache_lock);
3727 #ifdef CACHE_DEBUG
3728 /* Increment the lookup and access counters for the host cache */
3729 host_cache_accesses++;
3730 host_cache_lookups++;
3731 if ((host_cache_lookups%1000) == 0)
3732 trace_host_cache();
3733 #endif /* CACHE DEBUG */
3735 for (ce = cache_head; ce; ce = ce->cache_next) {
3736 if (timenow > ce->cache_time) {
3737 (void) rw_unlock(&cache_lock);
3738 (void) rw_wrlock(&cache_lock);
3739 for (prev = NULL, ce = cache_head; ce;
3740 prev = ce, ce = ce->cache_next) {
3741 if (timenow > ce->cache_time) {
3742 cache_free(ce);
3743 if (prev)
3744 prev->cache_next = NULL;
3745 else
3746 cache_head = NULL;
3747 break;
3750 (void) rw_unlock(&cache_lock);
3751 return (state);
3753 if (strcmp(host, ce->cache_host) != 0)
3754 continue;
3755 if ((proto == NULL && ce->cache_proto != NULL) ||
3756 (proto != NULL && ce->cache_proto == NULL))
3757 continue;
3758 if (proto != NULL &&
3759 strcmp(proto, ce->cache_proto) != 0)
3760 continue;
3762 if (versp == NULL ||
3763 (versp != NULL && *versp == ce->cache_reqvers) ||
3764 (versp != NULL && *versp == ce->cache_outvers)) {
3765 if (versp != NULL)
3766 *versp = ce->cache_outvers;
3767 state = ce->cache_state;
3769 /* increment the host cache hit counters */
3770 #ifdef CACHE_DEBUG
3771 if (state == GOODHOST)
3772 goodhost_cache_hits++;
3773 if (state == DEADHOST)
3774 deadhost_cache_hits++;
3775 #endif /* CACHE_DEBUG */
3776 (void) rw_unlock(&cache_lock);
3777 return (state);
3780 (void) rw_unlock(&cache_lock);
3781 return (state);
3785 * Free a cache entry and all entries
3786 * further down the chain since they
3787 * will also be expired.
3789 static void
3790 cache_free(entry)
3791 struct cache_entry *entry;
3793 struct cache_entry *ce, *next = NULL;
3795 for (ce = entry; ce; ce = next) {
3796 if (ce->cache_host)
3797 free(ce->cache_host);
3798 if (ce->cache_proto)
3799 free(ce->cache_proto);
3800 next = ce->cache_next;
3801 free(ce);
3805 #ifdef MALLOC_DEBUG
3806 void
3807 cache_flush()
3809 (void) rw_wrlock(&cache_lock);
3810 cache_free(cache_head);
3811 cache_head = NULL;
3812 (void) rw_unlock(&cache_lock);
3815 void
3816 flush_caches()
3818 mutex_lock(&cleanup_lock);
3819 cond_signal(&cleanup_start_cv);
3820 (void) cond_wait(&cleanup_done_cv, &cleanup_lock);
3821 mutex_unlock(&cleanup_lock);
3822 cache_flush();
3823 portmap_cache_flush();
3825 #endif
3828 * Returns 1, if port option is NFS_PORT or
3829 * nfsd is running on the port given
3830 * Returns 0, if both port is not NFS_PORT and nfsd is not
3831 * running on the port.
3834 static int
3835 is_nfs_port(char *opts)
3837 struct mnttab m;
3838 uint_t nfs_port = 0;
3839 struct servent sv;
3840 char buf[256];
3841 int got_port;
3843 m.mnt_mntopts = opts;
3846 * Get port specified in options list, if any.
3848 got_port = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
3851 * if no port specified or it is same as NFS_PORT return nfs
3852 * To use any other daemon the port number should be different
3854 if (!got_port || nfs_port == NFS_PORT)
3855 return (1);
3857 * If daemon is nfsd, return nfs
3859 if (getservbyport_r(nfs_port, NULL, &sv, buf, 256) == &sv &&
3860 strcmp(sv.s_name, "nfsd") == 0)
3861 return (1);
3864 * daemon is not nfs
3866 return (0);
3871 * destroy_auth_client_handle(cl)
3872 * destroys the created client handle
3874 void
3875 destroy_auth_client_handle(CLIENT *cl)
3877 if (cl) {
3878 if (cl->cl_auth) {
3879 #ifdef MALLOC_DEBUG
3880 drop_alloc("AUTH_HANDLE", cl->cl_auth,
3881 __FILE__, __LINE__);
3882 #endif
3883 AUTH_DESTROY(cl->cl_auth);
3884 cl->cl_auth = NULL;
3886 #ifdef MALLOC_DEBUG
3887 drop_alloc("CLNT_HANDLE", cl,
3888 __FILE__, __LINE__);
3889 #endif
3890 clnt_destroy(cl);
3896 * Attempt to figure out which version of NFS to use in pingnfs(). If
3897 * the version number was specified (i.e., non-zero), then use it.
3898 * Otherwise, default to the compiled-in default or the default as set
3899 * by the /etc/default/nfs configuration (as read by read_default().
3902 set_versrange(rpcvers_t nfsvers, rpcvers_t *vers, rpcvers_t *versmin)
3904 switch (nfsvers) {
3905 case 0:
3906 *vers = vers_max_default;
3907 *versmin = vers_min_default;
3908 break;
3909 case NFS_V4:
3910 *vers = NFS_V4;
3911 *versmin = NFS_V4;
3912 break;
3913 case NFS_V3:
3914 *vers = NFS_V3;
3915 *versmin = NFS_V3;
3916 break;
3917 case NFS_VERSION:
3918 *vers = NFS_VERSION; /* version 2 */
3919 *versmin = NFS_VERSMIN; /* version 2 */
3920 break;
3921 default:
3922 return (-1);
3924 return (0);
3927 #ifdef CACHE_DEBUG
3929 * trace_portmap_cache()
3930 * traces the portmap cache values at desired points
3932 static void
3933 trace_portmap_cache()
3935 syslog(LOG_ERR, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
3936 portmap_cache_accesses, portmap_cache_lookups,
3937 portmap_cache_hits);
3941 * trace_host_cache()
3942 * traces the host cache values at desired points
3944 static void
3945 trace_host_cache()
3947 syslog(LOG_ERR,
3948 "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
3949 host_cache_accesses, host_cache_lookups, deadhost_cache_hits,
3950 goodhost_cache_hits);
3952 #endif /* CACHE_DEBUG */
3955 * Read the NFS SMF properties to determine if the
3956 * client has been configured for a new min/max for the NFS version to
3957 * use.
3960 #define SVC_NFS_CLIENT "svc:/network/nfs/client"
3962 static void
3963 read_default_nfs(void)
3965 struct stat buf;
3966 char defval[4];
3967 int errno, bufsz;
3968 int tmp, ret = 0;
3970 bufsz = 4;
3971 ret = nfs_smf_get_prop("client_versmin", defval, DEFAULT_INSTANCE,
3972 SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz);
3973 if (ret == SA_OK) {
3974 errno = 0;
3975 tmp = strtol(defval, (char **)NULL, 10);
3976 if (errno == 0) {
3977 vers_min_default = tmp;
3981 bufsz = 4;
3982 ret = nfs_smf_get_prop("client_versmax", defval, DEFAULT_INSTANCE,
3983 SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz);
3984 if (ret == SA_OK) {
3985 errno = 0;
3986 tmp = strtol(defval, (char **)NULL, 10);
3987 if (errno == 0) {
3988 vers_max_default = tmp;
3993 * Quick sanity check on the values picked up from the
3994 * defaults file. Make sure that a mistake wasn't
3995 * made that will confuse things later on.
3996 * If so, reset to compiled-in defaults
3998 if (vers_min_default > vers_max_default ||
3999 vers_min_default < NFS_VERSMIN ||
4000 vers_max_default > NFS_VERSMAX) {
4001 if (trace > 1) {
4002 trace_prt(1,
4003 " read_default: version minimum/maximum incorrectly configured\n");
4004 trace_prt(1,
4005 " read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
4006 vers_min_default, vers_max_default,
4007 NFS_VERSMIN_DEFAULT,
4008 NFS_VERSMAX_DEFAULT);
4010 vers_min_default = NFS_VERSMIN_DEFAULT;
4011 vers_max_default = NFS_VERSMAX_DEFAULT;
4016 * Find the mnttab entry that corresponds to "name".
4017 * We're not sure what the name represents: either
4018 * a mountpoint name, or a special name (server:/path).
4019 * Return the last entry in the file that matches.
4021 static struct extmnttab *
4022 mnttab_find(dirname)
4023 char *dirname;
4025 FILE *fp;
4026 struct extmnttab mnt;
4027 struct extmnttab *res = NULL;
4029 fp = fopen(MNTTAB, "r");
4030 if (fp == NULL) {
4031 if (trace > 1)
4032 trace_prt(1, " mnttab_find: unable to open mnttab\n");
4033 return (NULL);
4035 while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) {
4036 if (strcmp(mnt.mnt_mountp, dirname) == 0 ||
4037 strcmp(mnt.mnt_special, dirname) == 0) {
4038 if (res)
4039 fsfreemnttab(res);
4040 res = fsdupmnttab(&mnt);
4044 resetmnttab(fp);
4045 fclose(fp);
4046 if (res == NULL) {
4047 if (trace > 1)
4048 trace_prt(1, " mnttab_find: unable to find %s\n",
4049 dirname);
4051 return (res);
4055 * This function's behavior is taken from nfsstat.
4056 * Trying to determine what NFS version was used for the mount.
4058 static int
4059 is_v4_mount(char *mntpath)
4061 kstat_ctl_t *kc = NULL; /* libkstat cookie */
4062 kstat_t *ksp;
4063 ulong_t fsid;
4064 struct mntinfo_kstat mik;
4065 struct extmnttab *mntp;
4066 uint_t mnt_minor;
4068 if ((mntp = mnttab_find(mntpath)) == NULL)
4069 return (FALSE);
4071 /* save the minor number and free the struct so we don't forget */
4072 mnt_minor = mntp->mnt_minor;
4073 fsfreemnttab(mntp);
4075 if ((kc = kstat_open()) == NULL)
4076 return (FALSE);
4078 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
4079 if (ksp->ks_type != KSTAT_TYPE_RAW)
4080 continue;
4081 if (strcmp(ksp->ks_module, "nfs") != 0)
4082 continue;
4083 if (strcmp(ksp->ks_name, "mntinfo") != 0)
4084 continue;
4085 if (mnt_minor != ksp->ks_instance)
4086 continue;
4088 if (kstat_read(kc, ksp, &mik) == -1)
4089 continue;
4091 (void) kstat_close(kc);
4092 if (mik.mik_vers == 4)
4093 return (TRUE);
4094 else
4095 return (FALSE);
4097 (void) kstat_close(kc);
4099 return (FALSE);
4102 static int
4103 create_homedir(const char *src, const char *dst) {
4105 struct stat stbuf;
4106 char *dst_username;
4107 struct passwd *pwd, pwds;
4108 char buf_pwd[NSS_BUFLEN_PASSWD];
4109 int homedir_len;
4110 int dst_dir_len;
4111 int src_dir_len;
4113 if (trace > 1)
4114 trace_prt(1, "entered create_homedir\n");
4116 if (stat(src, &stbuf) == 0) {
4117 if (trace > 1)
4118 trace_prt(1, "src exists\n");
4119 return (1);
4122 dst_username = strrchr(dst, '/');
4123 if (dst_username) {
4124 dst_username++; /* Skip over slash */
4125 getpwnam_r(dst_username, &pwds, buf_pwd, sizeof (buf_pwd),
4126 &pwd);
4127 if (pwd == NULL) {
4128 return (0);
4130 } else {
4131 return (0);
4134 homedir_len = strlen(pwd->pw_dir);
4135 dst_dir_len = strlen(dst) - homedir_len;
4136 src_dir_len = strlen(src) - homedir_len;
4138 /* Check that the paths are in the same zone */
4139 if (src_dir_len < dst_dir_len ||
4140 (strncmp(dst, src, dst_dir_len) != 0)) {
4141 if (trace > 1)
4142 trace_prt(1, " paths don't match\n");
4143 return (0);
4145 /* Check that mountpoint is an auto_home entry */
4146 if (dst_dir_len < 0 ||
4147 (strcmp(pwd->pw_dir, dst + dst_dir_len) != 0)) {
4148 return (0);
4151 /* Check that source is an home directory entry */
4152 if (src_dir_len < 0 ||
4153 (strcmp(pwd->pw_dir, src + src_dir_len) != 0)) {
4154 if (trace > 1)
4155 trace_prt(1, " homedir (2) doesn't match %s\n",
4156 src+src_dir_len);
4157 return (0);
4160 if (mkdir(src,
4161 S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH) == -1) {
4162 if (trace > 1) {
4163 trace_prt(1, " Couldn't mkdir %s\n", src);
4165 return (0);
4168 if (chown(src, pwd->pw_uid, pwd->pw_gid) == -1) {
4169 unlink(src);
4170 return (0);
4173 /* Created new home directory for the user */
4174 return (1);
4177 void
4178 free_nfs_args(struct nfs_args *argp)
4180 struct nfs_args *oldp;
4181 while (argp) {
4182 if (argp->pathconf)
4183 free(argp->pathconf);
4184 if (argp->knconf)
4185 free_knconf(argp->knconf);
4186 if (argp->addr)
4187 netbuf_free(argp->addr);
4188 if (argp->syncaddr)
4189 netbuf_free(argp->syncaddr);
4190 if (argp->netname)
4191 free(argp->netname);
4192 if (argp->hostname)
4193 free(argp->hostname);
4194 if (argp->nfs_ext_u.nfs_extB.secdata)
4195 nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
4196 if (argp->fh)
4197 free(argp->fh);
4198 if (argp->nfs_ext_u.nfs_extA.secdata) {
4199 sec_data_t *sd;
4200 sd = argp->nfs_ext_u.nfs_extA.secdata;
4201 if (sd == NULL)
4202 break;
4203 switch (sd->rpcflavor) {
4204 case AUTH_NONE:
4205 case AUTH_UNIX:
4206 case AUTH_LOOPBACK:
4207 break;
4208 case AUTH_DES:
4210 dh_k4_clntdata_t *dhk4;
4211 dhk4 = (dh_k4_clntdata_t *)sd->data;
4212 if (dhk4 == NULL)
4213 break;
4214 if (dhk4->syncaddr.buf)
4215 free(dhk4->syncaddr.buf);
4216 if (dhk4->knconf->knc_protofmly)
4217 free(dhk4->knconf->knc_protofmly);
4218 if (dhk4->knconf->knc_proto)
4219 free(dhk4->knconf->knc_proto);
4220 if (dhk4->knconf)
4221 free(dhk4->knconf);
4222 if (dhk4->netname)
4223 free(dhk4->netname);
4224 free(dhk4);
4225 break;
4227 case RPCSEC_GSS:
4229 gss_clntdata_t *gss;
4230 gss = (gss_clntdata_t *)sd->data;
4231 if (gss == NULL)
4232 break;
4233 if (gss->mechanism.elements)
4234 free(gss->mechanism.elements);
4235 free(gss);
4236 break;
4240 oldp = argp;
4241 if (argp->nfs_args_ext == NFS_ARGS_EXTB)
4242 argp = argp->nfs_ext_u.nfs_extB.next;
4243 else
4244 argp = NULL;
4245 free(oldp);
4249 void *
4250 get_netconfig_info(enum type_of_stuff type_of_stuff, char *hostname,
4251 rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
4252 ushort_t port, struct t_info *tinfo, struct t_bind *tbind,
4253 caddr_t *fhp, bool_t direct_to_server, char *fspath,
4254 enum clnt_stat *cstat, mfs_snego_t *mfssnego)
4256 struct netconfig *nb = NULL;
4257 int ping_server = 0;
4260 if (nconf == NULL)
4261 return (NULL);
4263 switch (type_of_stuff) {
4264 case SERVER_FH:
4265 nb = get_server_fh(hostname, prog, vers, mfssnego,
4266 nconf, port, tinfo, tbind, fhp, direct_to_server,
4267 fspath, cstat);
4268 break;
4269 case SERVER_PING:
4270 ping_server = 1;
4271 case SERVER_ADDR:
4272 nb = get_server_addrorping(hostname, prog, vers,
4273 nconf, port, tinfo, tbind, fhp, direct_to_server,
4274 fspath, cstat, ping_server);
4275 break;
4276 default:
4277 assert(nb != NULL);
4279 return (nb);
4283 * Get the server address or can we ping it or not.
4284 * Check the portmap cache first for server address.
4285 * If no entries there, ping the server with a NULLPROC rpc.
4287 void *
4288 get_server_addrorping(char *hostname, rpcprog_t prog, rpcvers_t vers,
4289 struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
4290 struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server,
4291 char *fspath, enum clnt_stat *cstat, int ping_server)
4293 struct timeval tv;
4294 enum clnt_stat cs = RPC_TIMEDOUT;
4295 struct netbuf *nb = NULL;
4296 CLIENT *cl = NULL;
4297 int fd = -1;
4299 if (prog == NFS_PROGRAM && vers == NFS_V4)
4300 if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
4301 goto done;
4303 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) {
4304 goto done;
4307 /* LINTED pointer alignment */
4308 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
4309 == NULL) {
4310 goto done;
4313 if (direct_to_server != TRUE) {
4314 if (!ping_server) {
4315 if (get_cached_srv_addr(hostname, prog, vers,
4316 nconf, &tbind->addr) == 0)
4317 goto done;
4318 } else {
4319 if (port == 0)
4320 goto done;
4323 if (setup_nb_parms(nconf, tbind, tinfo, hostname,
4324 fd, direct_to_server, port, prog, vers, 0) < 0)
4325 goto done;
4327 if (port || (direct_to_server == TRUE)) {
4328 tv.tv_sec = 10;
4329 tv.tv_usec = 0;
4330 cl = clnt_tli_create(fd, nconf, &tbind->addr,
4331 prog, vers, 0, 0);
4332 if (cl == NULL)
4333 goto done;
4335 cs = clnt_call(cl, NULLPROC, xdr_void, 0,
4336 xdr_void, 0, tv);
4337 if (cs != RPC_SUCCESS) {
4338 syslog(LOG_ERR, "error is %d", cs);
4339 goto done;
4342 if (!ping_server) {
4343 nb = (struct netbuf *)malloc(sizeof (struct netbuf));
4344 if (nb == NULL) {
4345 syslog(LOG_ERR, "no memory\n");
4346 goto done;
4348 nb->buf = (char *)malloc(tbind->addr.maxlen);
4349 if (nb->buf == NULL) {
4350 syslog(LOG_ERR, "no memory\n");
4351 free(nb);
4352 nb = NULL;
4353 goto done;
4355 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
4356 nb->len = tbind->addr.len;
4357 nb->maxlen = tbind->addr.maxlen;
4358 cs = RPC_SUCCESS;
4360 done:
4361 destroy_auth_client_handle(cl);
4362 cleanup_tli_parms(tbind, fd);
4363 *cstat = cs;
4364 return (nb);