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