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]
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
35 #include <sys/param.h>
36 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
43 #include <sys/signal.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>
51 #include <arpa/inet.h>
53 #include <netconfig.h>
58 #include <nfs/mount.h>
59 #include <rpcsvc/mount.h>
60 #include <rpc/nettype.h>
63 #include <sys/socket.h>
66 #include <nss_dbdefs.h> /* for NSS_BUFLEN_HOSTS */
67 #include <nfs/nfs_sec.h>
68 #include <sys/sockio.h>
71 #include <nfs/nfs_clnt.h>
72 #include <rpcsvc/nfs4_prot.h>
74 #define NO_RDDIR_CACHE
75 #include "automount.h"
79 #include "nfs_resolve.h"
80 #include <sys/sockio.h>
82 #include <rpcsvc/daemon_utils.h>
91 extern void set_nfsv4_ephemeral_mount_to(void);
93 extern char *nfs_get_qop_name();
94 extern AUTH
*nfs_create_ah();
95 extern enum snego_stat
nfs_sec_nego();
106 #define NFS_ARGS_EXTB_secdata(args, secdata) \
107 { (args).nfs_args_ext = NFS_ARGS_EXTB, \
108 (args).nfs_ext_u.nfs_extB.secdata = secdata; }
111 struct cache_entry
*cache_next
;
115 rpcvers_t cache_reqvers
;
116 rpcvers_t cache_outvers
;
126 typedef struct mfs_snego_t mfs_snego_t
;
128 static struct cache_entry
*cache_head
= NULL
;
129 rwlock_t cache_lock
; /* protect the cache chain */
131 static enum nfsstat
nfsmount(struct mapfs
*, char *, char *, int, uid_t
,
133 static int is_nfs_port(char *);
135 static void netbuf_free(struct netbuf
*);
136 static int get_pathconf(CLIENT
*, char *, char *, struct pathcnf
**, int);
137 static struct mapfs
*enum_servers(struct mapent
*, char *);
138 static struct mapfs
*get_mysubnet_servers(struct mapfs
*);
139 static int subnet_test(int af
, struct sioc_addrreq
*);
140 static struct netbuf
*get_addr(char *, rpcprog_t
, rpcvers_t
,
141 struct netconfig
**, char *, ushort_t
, struct t_info
*);
143 static struct netbuf
*get_pubfh(char *, rpcvers_t
, mfs_snego_t
*,
144 struct netconfig
**, char *, ushort_t
, struct t_info
*, caddr_t
*,
147 static int create_homedir(const char *, const char *);
155 static void *get_server_netinfo(enum type_of_stuff
, char *, rpcprog_t
,
156 rpcvers_t
, mfs_snego_t
*, struct netconfig
**, char *, ushort_t
,
157 struct t_info
*, caddr_t
*, bool_t
, char *, enum clnt_stat
*);
158 static void *get_netconfig_info(enum type_of_stuff
, char *, rpcprog_t
,
159 rpcvers_t
, struct netconfig
*, ushort_t
, struct t_info
*,
160 struct t_bind
*, caddr_t
*, bool_t
, char *, enum clnt_stat
*,
162 static void *get_server_addrorping(char *, rpcprog_t
, rpcvers_t
,
163 struct netconfig
*, ushort_t
, struct t_info
*, struct t_bind
*,
164 caddr_t
*, bool_t
, char *, enum clnt_stat
*, int);
165 static void *get_server_fh(char *, rpcprog_t
, rpcvers_t
, mfs_snego_t
*,
166 struct netconfig
*, ushort_t
, struct t_info
*, struct t_bind
*,
167 caddr_t
*, bool_t
, char *, enum clnt_stat
*);
169 struct mapfs
*add_mfs(struct mapfs
*, int, struct mapfs
**, struct mapfs
**);
170 void free_mfs(struct mapfs
*);
171 static void dump_mfs(struct mapfs
*, char *, int);
172 static char *dump_distance(struct mapfs
*);
173 static void cache_free(struct cache_entry
*);
174 static int cache_check(char *, rpcvers_t
*, char *);
175 static void cache_enter(char *, rpcvers_t
, rpcvers_t
, char *, int);
176 void destroy_auth_client_handle(CLIENT
*cl
);
179 static void trace_host_cache(void);
180 static void trace_portmap_cache(void);
181 #endif /* CACHE_DEBUG */
183 static int rpc_timeout
= 20;
187 * host cache counters. These variables do not need to be protected
188 * by mutex's. They have been added to measure the utility of the
189 * goodhost/deadhost cache in the lazy hierarchical mounting scheme.
191 static int host_cache_accesses
= 0;
192 static int host_cache_lookups
= 0;
193 static int deadhost_cache_hits
= 0;
194 static int goodhost_cache_hits
= 0;
197 * portmap cache counters. These variables do not need to be protected
198 * by mutex's. They have been added to measure the utility of the portmap
199 * cache in the lazy hierarchical mounting scheme.
201 static int portmap_cache_accesses
= 0;
202 static int portmap_cache_lookups
= 0;
203 static int portmap_cache_hits
= 0;
204 #endif /* CACHE_DEBUG */
207 * There are the defaults (range) for the client when determining
208 * which NFS version to use when probing the server (see above).
209 * These will only be used when the vers mount option is not used and
210 * these may be reset if /etc/default/nfs is configured to do so.
212 static rpcvers_t vers_max_default
= NFS_VERSMAX_DEFAULT
;
213 static rpcvers_t vers_min_default
= NFS_VERSMIN_DEFAULT
;
216 * list of support services needed
218 static char *service_list
[] = { STATD
, LOCKD
, NULL
};
219 static char *service_list_v4
[] = { STATD
, LOCKD
, NFS4CBD
, NFSMAPID
, NULL
};
221 static void read_default_nfs(void);
222 static int is_v4_mount(char *);
223 static void start_nfs4cbd(void);
226 mount_nfs(struct mapent
*me
, char *mntpnt
, char *prevhost
, int overlay
,
227 uid_t uid
, action_list
**alpp
)
229 struct mapfs
*mfs
, *mp
;
239 mfs
= enum_servers(me
, prevhost
);
244 * Try loopback if we have something on localhost; if nothing
245 * works, we will fall back to NFS
247 if (is_nfs_port(me
->map_mntopts
)) {
248 for (mp
= mfs
; mp
; mp
= mp
->mfs_next
) {
249 if (self_check(mp
->mfs_host
)) {
250 err
= loopbackmount(mp
->mfs_dir
,
251 mntpnt
, me
->map_mntopts
, overlay
);
256 * Free action_list if there
257 * is one as it is not needed.
258 * Make sure to set alpp to null
259 * so caller doesn't try to free it
272 dir
= strdup(mfs
->mfs_dir
);
273 err
= nfsmount(mfs
, mntpnt
, me
->map_mntopts
,
275 if (err
&& trace
> 1) {
276 trace_prt(1, " Couldn't mount %s:%s, err=%d\n",
277 mfs
->mfs_host
? mfs
->mfs_host
: "",
278 mfs
->mfs_dir
? mfs
->mfs_dir
: dir
, err
);
288 * Using the new ioctl SIOCTONLINK to determine if a host is on the same
289 * subnet. Remove the old network, subnet check.
292 static struct mapfs
*
293 get_mysubnet_servers(struct mapfs
*mfs_in
)
296 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
298 struct netconfig
*nconf
;
299 NCONF_HANDLE
*nc
= NULL
;
300 struct nd_hostserv hs
;
301 struct nd_addrlist
*retaddrs
;
303 struct sioc_addrreq areq
;
309 hs
.h_serv
= "rpcbind";
311 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
314 while (nconf
= getnetconfig(nc
)) {
317 * Care about INET family only. proto_done flag
318 * indicates if we have already covered this
319 * protocol family. If so skip it
321 if (((strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0) ||
322 (strcmp(nconf
->nc_protofmly
, NC_INET
) == 0)) &&
323 (nconf
->nc_semantics
== NC_TPI_CLTS
)) {
327 hs
.h_host
= mfs
->mfs_host
;
329 if (netdir_getbyname(nconf
, &hs
, &retaddrs
) != ND_OK
)
333 * For each host address see if it's on our
337 if (strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0)
341 nb
= retaddrs
->n_addrs
;
342 for (i
= 0; i
< retaddrs
->n_cnt
; i
++, nb
++) {
343 memset(&areq
.sa_addr
, 0, sizeof (areq
.sa_addr
));
344 memcpy(&areq
.sa_addr
, nb
->buf
, MIN(nb
->len
,
345 sizeof (areq
.sa_addr
)));
346 if (res
= subnet_test(af
, &areq
)) {
347 p
= add_mfs(mfs
, DIST_MYNET
,
348 &mfs_head
, &mfs_tail
);
350 netdir_free(retaddrs
,
357 } /* end of every host */
359 trace_prt(1, "get_mysubnet_servers: host=%s "
360 "netid=%s res=%s\n", mfs
->mfs_host
,
361 nconf
->nc_netid
, res
== 1?"SUC":"FAIL");
364 netdir_free(retaddrs
, ND_ADDRLIST
);
369 } /* end of every map */
376 subnet_test(int af
, struct sioc_addrreq
*areq
)
380 if ((s
= socket(af
, SOCK_DGRAM
, 0)) < 0) {
386 if (ioctl(s
, SIOCTONLINK
, (caddr_t
)areq
) < 0) {
387 syslog(LOG_ERR
, "subnet_test:SIOCTONLINK failed");
391 if (areq
->sa_res
== 1)
400 * ping a bunch of hosts at once and sort by who responds first
402 static struct mapfs
*
403 sort_servers(struct mapfs
*mfs_in
, int timeout
)
405 struct mapfs
*m1
= NULL
;
406 enum clnt_stat clnt_stat
;
411 clnt_stat
= nfs_cast(mfs_in
, &m1
, timeout
);
414 char buff
[2048] = {'\0'};
416 for (m1
= mfs_in
; m1
; m1
= m1
->mfs_next
) {
417 (void) strcat(buff
, m1
->mfs_host
);
419 (void) strcat(buff
, ",");
422 syslog(LOG_ERR
, "servers %s not responding: %s",
423 buff
, clnt_sperrno(clnt_stat
));
430 * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
431 * provided it is not marked "ignored" and isn't a dupe of ones we've
435 add_mfs(struct mapfs
*mfs
, int distance
, struct mapfs
**mfs_head
,
436 struct mapfs
**mfs_tail
)
438 struct mapfs
*tmp
, *new;
440 for (tmp
= *mfs_head
; tmp
; tmp
= tmp
->mfs_next
)
441 if ((strcmp(tmp
->mfs_host
, mfs
->mfs_host
) == 0 &&
442 strcmp(tmp
->mfs_dir
, mfs
->mfs_dir
) == 0) ||
445 new = (struct mapfs
*)malloc(sizeof (struct mapfs
));
447 syslog(LOG_ERR
, "Memory allocation failed: %m");
450 bcopy(mfs
, new, sizeof (struct mapfs
));
451 new->mfs_next
= NULL
;
453 new->mfs_distance
= distance
;
455 *mfs_tail
= *mfs_head
= new;
457 (*mfs_tail
)->mfs_next
= new;
464 dump_mfs(struct mapfs
*mfs
, char *message
, int level
)
471 trace_prt(1, "%s", message
);
473 trace_prt(0, "mfs is null\n");
476 for (m1
= mfs
; m1
; m1
= m1
->mfs_next
)
477 trace_prt(0, "%s[%s] ", m1
->mfs_host
, dump_distance(m1
));
482 dump_distance(struct mapfs
*mfs
)
484 switch (mfs
->mfs_distance
) {
485 case 0: return ("zero");
486 case DIST_SELF
: return ("self");
487 case DIST_MYSUB
: return ("mysub");
488 case DIST_MYNET
: return ("mynet");
489 case DIST_OTHER
: return ("other");
490 default: return ("other");
495 * Walk linked list "raw", building a new list consisting of members
496 * NOT found in list "filter", returning the result.
498 static struct mapfs
*
499 filter_mfs(struct mapfs
*raw
, struct mapfs
*filter
)
501 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
506 for (mfs
= raw
; mfs
; mfs
= mfs
->mfs_next
) {
507 for (skip
= 0, p
= filter
; p
; p
= p
->mfs_next
) {
508 if (strcmp(p
->mfs_host
, mfs
->mfs_host
) == 0 &&
509 strcmp(p
->mfs_dir
, mfs
->mfs_dir
) == 0) {
516 p
= add_mfs(mfs
, 0, &mfs_head
, &mfs_tail
);
524 * Walk a linked list of mapfs structs, freeing each member.
527 free_mfs(struct mapfs
*mfs
)
539 * New code for NFS client failover: we need to carry and sort
540 * lists of server possibilities rather than return a single
541 * entry. It preserves previous behaviour of sorting first by
542 * locality (loopback-or-preferred/subnet/net/other) and then
543 * by ping times. We'll short-circuit this process when we
544 * have ENOUGH or more entries.
546 static struct mapfs
*
547 enum_servers(struct mapent
*me
, char *preferred
)
549 struct mapfs
*p
, *m1
, *m2
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
552 * Short-circuit for simple cases.
554 if (!me
->map_fs
->mfs_next
) {
555 p
= add_mfs(me
->map_fs
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
561 dump_mfs(me
->map_fs
, " enum_servers: mapent: ", 2);
564 * get addresses & see if any are myself
565 * or were mounted from previously in a
566 * hierarchical mount.
569 trace_prt(1, " enum_servers: looking for pref/self\n");
570 for (m1
= me
->map_fs
; m1
; m1
= m1
->mfs_next
) {
573 if (self_check(m1
->mfs_host
) ||
574 strcmp(m1
->mfs_host
, preferred
) == 0) {
575 p
= add_mfs(m1
, DIST_SELF
, &mfs_head
, &mfs_tail
);
581 trace_prt(1, " enum_servers: pref/self found, %s\n",
585 * look for entries on this subnet
587 dump_mfs(m1
, " enum_servers: input of get_mysubnet_servers: ", 2);
588 m1
= get_mysubnet_servers(me
->map_fs
);
589 dump_mfs(m1
, " enum_servers: output of get_mysubnet_servers: ", 3);
590 if (m1
&& m1
->mfs_next
) {
591 m2
= sort_servers(m1
, rpc_timeout
/ 2);
592 dump_mfs(m2
, " enum_servers: output of sort_servers: ", 3);
597 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
598 p
= add_mfs(m2
, 0, &mfs_head
, &mfs_tail
);
606 * add the rest of the entries at the end
608 m1
= filter_mfs(me
->map_fs
, mfs_head
);
609 dump_mfs(m1
, " enum_servers: etc: output of filter_mfs: ", 3);
610 m2
= sort_servers(m1
, rpc_timeout
/ 2);
611 dump_mfs(m2
, " enum_servers: etc: output of sort_servers: ", 3);
615 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
616 p
= add_mfs(m2
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
624 dump_mfs(mfs_head
, " enum_servers: output: ", 1);
630 struct mapfs
*mfs_in
,
631 char *mntpnt
, char *opts
,
637 char remname
[MAXPATHLEN
], *mnttabtext
= NULL
;
638 char mopts
[MAX_MNTOPT_STR
];
639 char netname
[MAXNETNAMELEN
+1];
640 char *mntopts
= NULL
;
644 struct nfs_args
*argp
= NULL
, *head
= NULL
, *tail
= NULL
,
645 *prevhead
, *prevtail
;
648 struct timeval timeout
;
649 enum clnt_stat rpc_stat
;
652 struct netconfig
*nconf
;
653 rpcvers_t vers
, versmin
; /* used to negotiate nfs version in pingnfs */
654 /* and mount version with mountd */
655 rpcvers_t outvers
; /* final version to be used during mount() */
656 rpcvers_t nfsvers
; /* version in map options, 0 if not there */
657 rpcvers_t mountversmax
; /* tracks the max mountvers during retries */
659 /* used to negotiate nfs version using webnfs */
660 rpcvers_t pubvers
, pubversmin
, pubversmax
;
662 struct nd_addrlist
*retaddrs
;
663 struct mountres3 res3
;
667 char scerror_msg
[MAXMSGLEN
];
671 char *nfs_proto
= NULL
;
673 char *p
, *host
, *rhost
, *dir
;
674 struct mapfs
*mfs
= NULL
;
675 int error
, last_error
= 0;
678 int v2cnt
= 0, v3cnt
= 0, v4cnt
= 0;
679 int v2near
= 0, v3near
= 0, v4near
= 0;
683 int sec_opt
, scerror
;
684 struct sec_data
*secdata
;
686 struct netbuf
*syncaddr
;
690 mfs_snego_t mfssnego_init
, mfssnego
;
692 dump_mfs(mfs_in
, " nfsmount: input: ", 2);
693 replicated
= (mfs_in
->mfs_next
!= NULL
);
694 m
.mnt_mntopts
= opts
;
695 if (replicated
&& hasmntopt(&m
, MNTOPT_SOFT
)) {
698 "mount on %s is soft and will not be replicated.", mntpnt
);
701 if (replicated
&& !hasmntopt(&m
, MNTOPT_RO
)) {
704 "mount on %s is not read-only and will not be replicated.",
709 loglevel
= LOG_WARNING
;
715 trace_prt(1, " nfsmount: replicated mount on %s %s:\n",
718 trace_prt(1, " nfsmount: standard mount on %s %s:\n",
720 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
)
721 trace_prt(1, " %s:%s\n",
722 mfs
->mfs_host
, mfs
->mfs_dir
);
726 * Make sure mountpoint is safe to mount on
728 if (lstat(mntpnt
, &stbuf
) < 0) {
729 syslog(LOG_ERR
, "Couldn't stat %s: %m", mntpnt
);
730 return (NFSERR_NOENT
);
734 * Get protocol specified in options list, if any.
736 if ((str_opt(&m
, "proto", &nfs_proto
)) == -1) {
737 return (NFSERR_NOENT
);
741 * Get port specified in options list, if any.
743 got_val
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
745 nfs_port
= 0; /* "unspecified" */
746 if (nfs_port
> USHRT_MAX
) {
747 syslog(LOG_ERR
, "%s: invalid port number %d", mntpnt
, nfs_port
);
748 return (NFSERR_NOENT
);
752 * Set mount(2) flags here, outside of the loop.
754 flags
= MS_OPTIONSTR
;
755 flags
|= (hasmntopt(&m
, MNTOPT_RO
) == NULL
) ? 0 : MS_RDONLY
;
756 flags
|= (hasmntopt(&m
, MNTOPT_NOSUID
) == NULL
) ? 0 : MS_NOSUID
;
757 flags
|= overlay
? MS_OVERLAY
: 0;
758 if (mntpnt
[strlen(mntpnt
) - 1] != ' ')
759 /* direct mount point without offsets */
762 use_pubfh
= (hasmntopt(&m
, MNTOPT_PUBLIC
) == NULL
) ? FALSE
: TRUE
;
764 (void) memset(&mfssnego_init
, 0, sizeof (mfs_snego_t
));
765 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
766 if (++mfssnego_init
.sec_opt
> 1) {
768 "conflicting security options");
771 if (nfs_getseconfig_byname("dh", &mfssnego_init
.nfs_sec
)) {
773 "error getting dh information from %s",
779 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
780 if ((str_opt(&m
, MNTOPT_SEC
,
781 &mfssnego_init
.nfs_flavor
)) == -1) {
782 syslog(LOG_ERR
, "nfsmount: no memory");
787 if (mfssnego_init
.nfs_flavor
) {
788 if (++mfssnego_init
.sec_opt
> 1) {
790 "conflicting security options");
791 free(mfssnego_init
.nfs_flavor
);
794 if (nfs_getseconfig_byname(mfssnego_init
.nfs_flavor
,
795 &mfssnego_init
.nfs_sec
)) {
797 "error getting %s information from %s",
798 mfssnego_init
.nfs_flavor
, NFSSEC_CONF
);
799 free(mfssnego_init
.nfs_flavor
);
802 free(mfssnego_init
.nfs_flavor
);
808 got_val
= nopt(&m
, MNTOPT_VERS
, (int *)&nfsvers
);
810 nfsvers
= 0; /* "unspecified" */
811 if (set_versrange(nfsvers
, &vers
, &versmin
) != 0) {
812 syslog(LOG_ERR
, "Incorrect NFS version specified for %s",
814 last_error
= NFSERR_NOENT
;
819 pubversmax
= pubversmin
= nfsvers
;
822 pubversmin
= versmin
;
826 * Walk the whole list, pinging and collecting version
827 * info so that we can make sure the mount will be
828 * homogeneous with respect to version.
830 * If we have a version preference, this is easy; we'll
831 * just reject anything that doesn't match.
833 * If not, we want to try to provide the best compromise
834 * that considers proximity, preference for a higher version,
835 * sorted order, and number of replicas. We will count
836 * the number of V2 and V3 replicas and also the number
837 * which are "near", i.e. the localhost or on the same
840 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
847 * If the host is '[a:d:d:r:e:s:s'],
848 * only use 'a:d:d:r:e:s:s' for communication
850 host
= strdup(mfs
->mfs_host
);
852 syslog(LOG_ERR
, "nfsmount: no memory");
853 last_error
= NFSERR_IO
;
858 (void) memcpy(&mfssnego
, &mfssnego_init
, sizeof (mfs_snego_t
));
860 if (use_pubfh
== TRUE
|| mfs
->mfs_flags
& MFS_URL
) {
863 if (nfs_port
!= 0 && mfs
->mfs_port
!= 0 &&
864 nfs_port
!= mfs
->mfs_port
) {
866 syslog(LOG_ERR
, "nfsmount: port (%u) in nfs URL"
867 " not the same as port (%d) in port "
868 "option\n", mfs
->mfs_port
, nfs_port
);
869 last_error
= NFSERR_IO
;
872 } else if (nfs_port
!= 0)
875 thisport
= mfs
->mfs_port
;
879 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
880 path
= malloc(strlen(dir
) + 2);
882 syslog(LOG_ERR
, "nfsmount: no memory");
883 last_error
= NFSERR_IO
;
886 path
[0] = (char)WNL_NATIVEPATH
;
887 (void) strcpy(&path
[1], dir
);
892 argp
= (struct nfs_args
*)
893 malloc(sizeof (struct nfs_args
));
898 syslog(LOG_ERR
, "nfsmount: no memory");
899 last_error
= NFSERR_IO
;
902 (void) memset(argp
, 0, sizeof (*argp
));
906 * By now Mount argument struct has been allocated,
907 * either a pub_fh path will be taken or the regular
908 * one. So here if a protocol was specified and it
909 * was not rdma we let it be, else we set DO_RDMA.
910 * If no proto was there we advise on trying RDMA.
913 if (strcmp(nfs_proto
, "rdma") == 0) {
916 argp
->flags
|= NFSMNT_DORDMA
;
919 argp
->flags
|= NFSMNT_TRYRDMA
;
921 for (pubvers
= pubversmax
; pubvers
>= pubversmin
;
925 argp
->addr
= get_pubfh(host
, pubvers
, &mfssnego
,
926 &nconf
, nfs_proto
, thisport
, NULL
,
927 &argp
->fh
, TRUE
, path
);
929 if (argp
->addr
!= NULL
)
933 freenetconfigent(nconf
);
939 if (argp
->addr
!= NULL
) {
942 * The use of llock option for NFSv4
943 * mounts is not required since file
944 * locking is included within the protocol
946 if (pubvers
!= NFS_V4
)
947 argp
->flags
|= NFSMNT_LLOCK
;
949 argp
->flags
|= NFSMNT_PUBLIC
;
952 mfs
->mfs_args
= argp
;
953 mfs
->mfs_version
= pubvers
;
954 mfs
->mfs_nconf
= nconf
;
955 mfs
->mfs_flags
|= MFS_FH_VIA_WEBNFS
;
961 * If -public was specified, give up
964 if (use_pubfh
== TRUE
) {
966 "%s: no public file handle support",
968 last_error
= NFSERR_NOENT
;
974 * Back off to a conventional mount.
976 * URL's can contain escape characters. Get
979 path
= malloc(strlen(dir
) + 2);
982 syslog(LOG_ERR
, "nfsmount: no memory");
983 last_error
= NFSERR_IO
;
990 mfs
->mfs_flags
|= MFS_ALLOC_DIR
;
991 mfs
->mfs_flags
&= ~MFS_URL
;
995 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
996 i
= pingnfs(host
, get_retry(opts
) + 1, &vers
, versmin
,
997 0, FALSE
, NULL
, nfs_proto
);
998 if (i
!= RPC_SUCCESS
) {
999 if (i
== RPC_PROGVERSMISMATCH
) {
1000 syslog(loglevel
, "server %s: NFS "
1001 "protocol version mismatch",
1004 syslog(loglevel
, "server %s not "
1005 "responding", host
);
1007 mfs
->mfs_ignore
= 1;
1008 last_error
= NFSERR_NOENT
;
1011 if (nfsvers
!= 0 && nfsvers
!= vers
) {
1012 if (nfs_proto
== NULL
)
1015 "not supported by %s",
1021 "not supported by %s",
1022 nfsvers
, nfs_proto
, host
);
1023 mfs
->mfs_ignore
= 1;
1024 last_error
= NFSERR_NOENT
;
1032 case NFS_V4
: v4cnt
++; break;
1033 case NFS_V3
: v3cnt
++; break;
1034 case NFS_VERSION
: v2cnt
++; break;
1039 * It's not clear how useful this stuff is if
1040 * we are using webnfs across the internet, but it
1043 if (mfs
->mfs_distance
&&
1044 mfs
->mfs_distance
<= DIST_MYSUB
) {
1046 case NFS_V4
: v4near
++; break;
1047 case NFS_V3
: v3near
++; break;
1048 case NFS_VERSION
: v2near
++; break;
1054 * If the mount is not replicated, we don't want to
1055 * ping every entry, so we'll stop here. This means
1056 * that we may have to go back to "nextentry" above
1057 * to consider another entry if we can't get
1058 * all the way to mount(2) with this one.
1067 * Choose the NFS version.
1068 * We prefer higher versions, but will choose a one-
1069 * version downgrade in service if we can use a local
1070 * network interface and avoid a router.
1072 if (v4cnt
&& v4cnt
>= v3cnt
&& (v4near
|| !v3near
))
1074 else if (v3cnt
&& v3cnt
>= v2cnt
&& (v3near
|| !v2near
))
1077 nfsvers
= NFS_VERSION
;
1080 " nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1081 v4cnt
, v4near
, v3cnt
, v3near
,
1082 v2cnt
, v2near
, nfsvers
);
1086 * Since we don't support different NFS versions in replicated
1087 * mounts, set fstype now.
1088 * Also take the opportunity to set
1089 * the mount protocol version as appropriate.
1093 fstype
= MNTTYPE_NFS4
;
1096 fstype
= MNTTYPE_NFS3
;
1097 if (use_pubfh
== FALSE
) {
1098 mountversmax
= MOUNTVERS3
;
1099 versmin
= MOUNTVERS3
;
1103 fstype
= MNTTYPE_NFS
;
1104 if (use_pubfh
== FALSE
) {
1105 mountversmax
= MOUNTVERS_POSIX
;
1106 versmin
= MOUNTVERS
;
1112 * Our goal here is to evaluate each of several possible
1113 * replicas and try to come up with a list we can hand
1114 * to mount(2). If we don't have a valid "head" at the
1115 * end of this process, it means we have rejected all
1116 * potential server:/path tuples. We will fail quietly
1117 * in front of mount(2), and will have printed errors
1118 * where we found them.
1119 * XXX - do option work outside loop w careful design
1120 * XXX - use macro for error condition free handling
1122 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
1125 * Initialize retry and delay values on a per-server basis.
1127 retries
= get_retry(opts
);
1130 if (mfs
->mfs_ignore
)
1134 * If we don't have a fh yet, and if this is not a replicated
1135 * mount, we haven't done a pingnfs() on the next entry,
1136 * so we don't know if the next entry is up or if it
1137 * supports an NFS version we like. So if we had a problem
1138 * with an entry, we need to go back and run through some new
1141 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1142 !replicated
&& skipentry
)
1145 vers
= mountversmax
;
1146 host
= mfs
->mfs_host
;
1150 * Remember the possible '[a:d:d:r:e:s:s]' as the address to be
1151 * later passed to mount(2) and used in the mnttab line, but
1152 * only use 'a:d:d:r:e:s:s' for communication
1154 rhost
= strdup(host
);
1155 if (rhost
== NULL
) {
1156 syslog(LOG_ERR
, "nfsmount: no memory");
1157 last_error
= NFSERR_IO
;
1162 (void) sprintf(remname
, "%s:%s", rhost
, dir
);
1163 if (trace
> 4 && replicated
)
1164 trace_prt(1, " nfsmount: examining %s\n", remname
);
1166 if (mfs
->mfs_args
== NULL
) {
1169 * Allocate nfs_args structure
1171 argp
= (struct nfs_args
*)
1172 malloc(sizeof (struct nfs_args
));
1175 syslog(LOG_ERR
, "nfsmount: no memory");
1176 last_error
= NFSERR_IO
;
1180 (void) memset(argp
, 0, sizeof (*argp
));
1184 * By now Mount argument struct has been allocated,
1185 * either a pub_fh path will be taken or the regular
1186 * one. So here if a protocol was specified and it
1187 * was not rdma we let it be, else we set DO_RDMA.
1188 * If no proto was there we advise on trying RDMA.
1191 if (strcmp(nfs_proto
, "rdma") == 0) {
1194 argp
->flags
|= NFSMNT_DORDMA
;
1197 argp
->flags
|= NFSMNT_TRYRDMA
;
1199 argp
= mfs
->mfs_args
;
1200 mfs
->mfs_args
= NULL
;
1203 * Skip entry if we already have file handle but the
1204 * NFS version is wrong.
1206 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) &&
1207 mfs
->mfs_version
!= nfsvers
) {
1211 mfs
->mfs_ignore
= 1;
1221 tail
= tail
->nfs_ext_u
.nfs_extB
.next
= argp
;
1224 * WebNFS and NFSv4 behave similarly in that they
1225 * don't use the mount protocol. Therefore, avoid
1226 * mount protocol like things when version 4 is being
1229 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1230 nfsvers
!= NFS_V4
) {
1231 timeout
.tv_usec
= 0;
1232 timeout
.tv_sec
= rpc_timeout
;
1233 rpc_stat
= RPC_TIMEDOUT
;
1235 /* Create the client handle. */
1239 " nfsmount: Get mount version: request "
1240 "vers=%d min=%d\n", vers
, versmin
);
1243 while ((cl
= clnt_create_vers(host
, MOUNTPROG
, &outvers
,
1244 versmin
, vers
, "udp")) == NULL
) {
1247 " nfsmount: Can't get mount "
1248 "version: rpcerr=%d\n",
1249 rpc_createerr
.cf_stat
);
1251 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
1252 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
1256 * backoff and return lower version to retry the ping.
1257 * XXX we should be more careful and handle
1258 * RPC_PROGVERSMISMATCH here, because that error
1259 * is handled in clnt_create_vers(). It's not done to
1260 * stay in sync with the nfs mount command.
1267 " nfsmount: Try version=%d\n",
1277 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1278 last_error
= NFSERR_NOENT
;
1280 if (rpc_createerr
.cf_stat
!= RPC_UNKNOWNHOST
&&
1281 rpc_createerr
.cf_stat
!=
1282 RPC_PROGVERSMISMATCH
&&
1288 syslog(loglevel
, "%s %s", host
,
1290 "server not responding"));
1292 mfs
->mfs_ignore
= 1;
1297 " nfsmount: mount version=%d\n", outvers
);
1300 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
1301 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1302 __FILE__
, __LINE__
);
1305 if (__clnt_bindresvport(cl
) < 0) {
1310 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1311 last_error
= NFSERR_NOENT
;
1313 if (retries
-- > 0) {
1314 destroy_auth_client_handle(cl
);
1319 syslog(loglevel
, "mount %s: %s", host
,
1320 "Couldn't bind to reserved port");
1321 destroy_auth_client_handle(cl
);
1323 mfs
->mfs_ignore
= 1;
1328 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
1329 __FILE__
, __LINE__
);
1331 AUTH_DESTROY(cl
->cl_auth
);
1332 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
1337 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1338 last_error
= NFSERR_NOENT
;
1340 if (retries
-- > 0) {
1341 destroy_auth_client_handle(cl
);
1346 syslog(loglevel
, "mount %s: %s", host
,
1347 "Failed creating default auth handle");
1348 destroy_auth_client_handle(cl
);
1350 mfs
->mfs_ignore
= 1;
1354 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1355 __FILE__
, __LINE__
);
1361 * set security options
1364 (void) memset(&nfs_sec
, 0, sizeof (nfs_sec
));
1365 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
1366 if (++sec_opt
> 1) {
1368 "conflicting security options for %s",
1374 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1375 last_error
= NFSERR_IO
;
1376 destroy_auth_client_handle(cl
);
1378 mfs
->mfs_ignore
= 1;
1381 if (nfs_getseconfig_byname("dh", &nfs_sec
)) {
1383 "error getting dh information from %s",
1389 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1390 last_error
= NFSERR_IO
;
1391 destroy_auth_client_handle(cl
);
1393 mfs
->mfs_ignore
= 1;
1399 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
1400 if ((str_opt(&m
, MNTOPT_SEC
, &nfs_flavor
)) == -1) {
1401 syslog(LOG_ERR
, "nfsmount: no memory");
1402 last_error
= NFSERR_IO
;
1403 destroy_auth_client_handle(cl
);
1409 if (++sec_opt
> 1) {
1411 "conflicting security options for %s",
1418 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1419 last_error
= NFSERR_IO
;
1420 destroy_auth_client_handle(cl
);
1422 mfs
->mfs_ignore
= 1;
1425 if (nfs_getseconfig_byname(nfs_flavor
, &nfs_sec
)) {
1427 "error getting %s information from %s",
1428 nfs_flavor
, NFSSEC_CONF
);
1434 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1435 last_error
= NFSERR_IO
;
1436 destroy_auth_client_handle(cl
);
1438 mfs
->mfs_ignore
= 1;
1444 posix
= (nfsvers
!= NFS_V4
&&
1445 hasmntopt(&m
, MNTOPT_POSIX
) != NULL
) ? 1 : 0;
1447 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1448 nfsvers
!= NFS_V4
) {
1449 bool_t give_up_on_mnt
;
1450 bool_t got_mnt_error
;
1452 * If we started with a URL, if first byte of path is not "/",
1453 * then the mount will likely fail, so we should try again
1454 * with a prepended "/".
1456 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
&& *dir
!= '/')
1457 give_up_on_mnt
= FALSE
;
1459 give_up_on_mnt
= TRUE
;
1461 got_mnt_error
= FALSE
;
1464 if (got_mnt_error
== TRUE
) {
1467 give_up_on_mnt
= TRUE
;
1471 * Insert a "/" to front of mfs_dir.
1473 for (i
= l
; i
> 0; i
--)
1479 /* Get fhandle of remote path from server's mountd */
1488 tail
->nfs_ext_u
.nfs_extB
.next
=
1490 last_error
= NFSERR_NOENT
;
1492 "can't get posix info for %s",
1494 destroy_auth_client_handle(cl
);
1496 mfs
->mfs_ignore
= 1;
1500 case MOUNTVERS_POSIX
:
1501 if (nfsvers
== NFS_V3
) {
1506 tail
->nfs_ext_u
.nfs_extB
.next
=
1508 last_error
= NFSERR_NOENT
;
1510 "%s doesn't support NFS Version 3",
1512 destroy_auth_client_handle(cl
);
1514 mfs
->mfs_ignore
= 1;
1517 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1518 xdr_dirpath
, (caddr_t
)&dir
,
1519 xdr_fhstatus
, (caddr_t
)&fhs
, timeout
);
1520 if (rpc_stat
!= RPC_SUCCESS
) {
1522 if (give_up_on_mnt
== FALSE
) {
1523 got_mnt_error
= TRUE
;
1528 * Given the way "clnt_sperror" works, the "%s"
1529 * immediately following the "not responding"
1536 tail
->nfs_ext_u
.nfs_extB
.next
=
1538 last_error
= NFSERR_NOENT
;
1540 if (retries
-- > 0) {
1541 destroy_auth_client_handle(cl
);
1548 " nfsmount: mount RPC "
1553 "%s server not responding%s",
1554 host
, clnt_sperror(cl
, ""));
1555 destroy_auth_client_handle(cl
);
1557 mfs
->mfs_ignore
= 1;
1560 if ((errno
= fhs
.fhs_status
) != MNT_OK
) {
1562 if (give_up_on_mnt
== FALSE
) {
1563 got_mnt_error
= TRUE
;
1571 tail
->nfs_ext_u
.nfs_extB
.next
=
1573 if (errno
== EACCES
) {
1574 status
= NFSERR_ACCES
;
1576 syslog(loglevel
, "%s: %m",
1582 " nfsmount: mount RPC gave"
1586 last_error
= status
;
1587 destroy_auth_client_handle(cl
);
1589 mfs
->mfs_ignore
= 1;
1592 argp
->fh
= malloc((sizeof (fhandle
)));
1594 syslog(LOG_ERR
, "nfsmount: no memory");
1595 last_error
= NFSERR_IO
;
1596 destroy_auth_client_handle(cl
);
1599 (void) memcpy(argp
->fh
,
1600 &fhs
.fhstatus_u
.fhs_fhandle
,
1605 (void) memset((char *)&res3
, '\0',
1607 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1608 xdr_dirpath
, (caddr_t
)&dir
,
1609 xdr_mountres3
, (caddr_t
)&res3
, timeout
);
1610 if (rpc_stat
!= RPC_SUCCESS
) {
1612 if (give_up_on_mnt
== FALSE
) {
1613 got_mnt_error
= TRUE
;
1618 * Given the way "clnt_sperror" works, the "%s"
1619 * immediately following the "not responding"
1626 tail
->nfs_ext_u
.nfs_extB
.next
=
1628 last_error
= NFSERR_NOENT
;
1630 if (retries
-- > 0) {
1631 destroy_auth_client_handle(cl
);
1638 " nfsmount: mount RPC "
1643 "%s server not responding%s",
1644 remname
, clnt_sperror(cl
, ""));
1645 destroy_auth_client_handle(cl
);
1647 mfs
->mfs_ignore
= 1;
1650 if ((errno
= res3
.fhs_status
) != MNT_OK
) {
1652 if (give_up_on_mnt
== FALSE
) {
1653 got_mnt_error
= TRUE
;
1661 tail
->nfs_ext_u
.nfs_extB
.next
=
1663 if (errno
== EACCES
) {
1664 status
= NFSERR_ACCES
;
1666 syslog(loglevel
, "%s: %m",
1672 " nfsmount: mount RPC gave"
1676 last_error
= status
;
1677 destroy_auth_client_handle(cl
);
1679 mfs
->mfs_ignore
= 1;
1684 * Negotiate the security flavor for nfs_mount
1686 auths
= res3
.mountres3_u
.mountinfo
.
1687 auth_flavors
.auth_flavors_val
;
1688 count
= res3
.mountres3_u
.mountinfo
.
1689 auth_flavors
.auth_flavors_len
;
1692 for (i
= 0; i
< count
; i
++)
1694 nfs_sec
.sc_nfsnum
) {
1699 "%s: does not support "
1700 "security \"%s\"\n",
1701 remname
, nfs_sec
.sc_name
);
1702 clnt_freeres(cl
, xdr_mountres3
,
1711 last_error
= NFSERR_IO
;
1712 destroy_auth_client_handle(cl
);
1714 mfs
->mfs_ignore
= 1;
1717 } else if (count
> 0) {
1718 for (i
= 0; i
< count
; i
++) {
1720 nfs_getseconfig_bynumber(
1721 auths
[i
], &nfs_sec
))) {
1727 if (nfs_syslog_scerr(scerror
,
1732 "mounted because it"
1734 "security flavor %d"
1740 clnt_freeres(cl
, xdr_mountres3
,
1749 last_error
= NFSERR_IO
;
1750 destroy_auth_client_handle(cl
);
1752 mfs
->mfs_ignore
= 1;
1758 res3
.mountres3_u
.mountinfo
.fhandle
.
1760 (void) memcpy(fh3
.fh3_u
.data
,
1761 res3
.mountres3_u
.mountinfo
.fhandle
.
1764 clnt_freeres(cl
, xdr_mountres3
,
1766 argp
->fh
= malloc(sizeof (nfs_fh3
));
1768 syslog(LOG_ERR
, "nfsmount: no memory");
1769 last_error
= NFSERR_IO
;
1770 destroy_auth_client_handle(cl
);
1773 (void) memcpy(argp
->fh
, &fh3
, sizeof (nfs_fh3
));
1780 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1781 last_error
= NFSERR_NOENT
;
1783 "unknown MOUNT version %ld on %s",
1785 destroy_auth_client_handle(cl
);
1787 mfs
->mfs_ignore
= 1;
1791 if (nfsvers
== NFS_V4
) {
1792 argp
->fh
= strdup(dir
);
1793 if (argp
->fh
== NULL
) {
1794 syslog(LOG_ERR
, "nfsmount: no memory");
1795 last_error
= NFSERR_IO
;
1801 trace_prt(1, " nfsmount: have %s filehandle for %s\n",
1804 argp
->flags
|= NFSMNT_NEWARGS
;
1805 argp
->flags
|= NFSMNT_INT
; /* default is "intr" */
1806 argp
->flags
|= NFSMNT_HOSTNAME
;
1807 argp
->hostname
= strdup(host
);
1808 if (argp
->hostname
== NULL
) {
1809 syslog(LOG_ERR
, "nfsmount: no memory");
1810 last_error
= NFSERR_IO
;
1815 * In this case, we want NFSv4 to behave like
1816 * non-WebNFS so that we get the server address.
1818 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
1822 thisport
= nfs_port
;
1824 thisport
= mfs
->mfs_port
;
1827 * For NFSv4, we want to avoid rpcbind, so call
1828 * get_server_netinfo() directly to tell it that
1829 * we want to go "direct_to_server". Otherwise,
1830 * do what has always been done.
1832 if (nfsvers
== NFS_V4
) {
1833 enum clnt_stat cstat
;
1835 argp
->addr
= get_server_netinfo(SERVER_ADDR
,
1836 host
, NFS_PROGRAM
, nfsvers
, NULL
,
1837 &nconf
, nfs_proto
, thisport
, NULL
,
1838 NULL
, TRUE
, NULL
, &cstat
);
1840 argp
->addr
= get_addr(host
, NFS_PROGRAM
,
1841 nfsvers
, &nconf
, nfs_proto
,
1845 if (argp
->addr
== NULL
) {
1847 free(argp
->hostname
);
1853 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1854 last_error
= NFSERR_NOENT
;
1856 if (retries
-- > 0) {
1857 destroy_auth_client_handle(cl
);
1862 syslog(loglevel
, "%s: no NFS service", host
);
1863 destroy_auth_client_handle(cl
);
1865 mfs
->mfs_ignore
= 1;
1870 "\tnfsmount: have net address for %s\n",
1874 nconf
= mfs
->mfs_nconf
;
1875 mfs
->mfs_nconf
= NULL
;
1878 argp
->flags
|= NFSMNT_KNCONF
;
1879 argp
->knconf
= get_knconf(nconf
);
1880 if (argp
->knconf
== NULL
) {
1881 netbuf_free(argp
->addr
);
1882 freenetconfigent(nconf
);
1884 free(argp
->hostname
);
1890 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1891 last_error
= NFSERR_NOSPC
;
1892 destroy_auth_client_handle(cl
);
1894 mfs
->mfs_ignore
= 1;
1899 "\tnfsmount: have net config for %s\n",
1902 if (hasmntopt(&m
, MNTOPT_SOFT
) != NULL
) {
1903 argp
->flags
|= NFSMNT_SOFT
;
1905 if (hasmntopt(&m
, MNTOPT_NOINTR
) != NULL
) {
1906 argp
->flags
&= ~(NFSMNT_INT
);
1908 if (hasmntopt(&m
, MNTOPT_NOAC
) != NULL
) {
1909 argp
->flags
|= NFSMNT_NOAC
;
1911 if (hasmntopt(&m
, MNTOPT_NOCTO
) != NULL
) {
1912 argp
->flags
|= NFSMNT_NOCTO
;
1914 if (hasmntopt(&m
, MNTOPT_FORCEDIRECTIO
) != NULL
) {
1915 argp
->flags
|= NFSMNT_DIRECTIO
;
1917 if (hasmntopt(&m
, MNTOPT_NOFORCEDIRECTIO
) != NULL
) {
1918 argp
->flags
&= ~(NFSMNT_DIRECTIO
);
1922 * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1924 if (mfssnego
.snego_done
) {
1925 memcpy(&nfs_sec
, &mfssnego
.nfs_sec
,
1926 sizeof (seconfig_t
));
1927 } else if (!sec_opt
) {
1929 * Get default security mode.
1931 if (nfs_getseconfig_default(&nfs_sec
)) {
1933 "error getting default security entry\n");
1934 free_knconf(argp
->knconf
);
1935 netbuf_free(argp
->addr
);
1936 freenetconfigent(nconf
);
1938 free(argp
->hostname
);
1944 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1945 last_error
= NFSERR_NOSPC
;
1946 destroy_auth_client_handle(cl
);
1948 mfs
->mfs_ignore
= 1;
1951 argp
->flags
|= NFSMNT_SECDEFAULT
;
1956 * get the network address for the time service on
1957 * the server. If an RPC based time service is
1958 * not available then try the IP time service.
1960 * Eventurally, we want to move this code to nfs_clnt_secdata()
1961 * when autod_nfs.c and mount.c can share the same
1962 * get_the_addr/get_netconfig_info routine.
1968 if (nfs_sec
.sc_rpcnum
== AUTH_DH
|| nfsvers
== NFS_V4
) {
1970 * If not using the public fh and not NFS_V4, we can try
1971 * talking RPCBIND. Otherwise, assume that firewalls
1972 * prevent us from doing that.
1974 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1975 nfsvers
!= NFS_V4
) {
1976 enum clnt_stat cstat
;
1977 syncaddr
= get_server_netinfo(SERVER_ADDR
,
1978 host
, RPCBPROG
, RPCBVERS
, NULL
, &nconf
,
1979 NULL
, 0, NULL
, NULL
, FALSE
, NULL
, &cstat
);
1982 if (syncaddr
!= NULL
) {
1983 /* for flags in sec_data */
1984 secflags
|= AUTH_F_RPCTIMESYNC
;
1986 struct nd_hostserv hs
;
1990 hs
.h_serv
= "timserver";
1991 error
= netdir_getbyname(nconf
, &hs
, &retaddrs
);
1993 if (error
!= ND_OK
&&
1994 nfs_sec
.sc_rpcnum
== AUTH_DH
) {
1996 "%s: secure: no time service\n",
1998 free_knconf(argp
->knconf
);
1999 netbuf_free(argp
->addr
);
2000 freenetconfigent(nconf
);
2002 free(argp
->hostname
);
2008 tail
->nfs_ext_u
.nfs_extB
.next
=
2010 last_error
= NFSERR_IO
;
2011 destroy_auth_client_handle(cl
);
2013 mfs
->mfs_ignore
= 1;
2018 syncaddr
= retaddrs
->n_addrs
;
2021 * For potential usage by NFS V4 when AUTH_DH
2022 * is negotiated via SECINFO in the kernel.
2024 if (nfsvers
== NFS_V4
&& syncaddr
&&
2025 host2netname(netname
, host
, NULL
)) {
2027 malloc(sizeof (struct netbuf
));
2028 argp
->syncaddr
->buf
=
2029 malloc(syncaddr
->len
);
2030 (void) memcpy(argp
->syncaddr
->buf
,
2031 syncaddr
->buf
, syncaddr
->len
);
2032 argp
->syncaddr
->len
= syncaddr
->len
;
2033 argp
->syncaddr
->maxlen
=
2035 argp
->netname
= strdup(netname
);
2036 argp
->flags
|= NFSMNT_SECURE
;
2041 nfs_sec
.sc_uid
= uid
;
2043 * If AUTH_DH is a chosen flavor now, its data will be stored
2044 * in the sec_data structure via nfs_clnt_secdata().
2046 if (!(secdata
= nfs_clnt_secdata(&nfs_sec
, host
, argp
->knconf
,
2047 syncaddr
, secflags
))) {
2049 "errors constructing security related data\n");
2050 if (secflags
& AUTH_F_RPCTIMESYNC
)
2051 netbuf_free(syncaddr
);
2053 netdir_free(retaddrs
, ND_ADDRLIST
);
2055 netbuf_free(argp
->syncaddr
);
2057 free(argp
->netname
);
2059 free(argp
->hostname
);
2060 free_knconf(argp
->knconf
);
2061 netbuf_free(argp
->addr
);
2062 freenetconfigent(nconf
);
2068 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2069 last_error
= NFSERR_IO
;
2070 destroy_auth_client_handle(cl
);
2072 mfs
->mfs_ignore
= 1;
2075 NFS_ARGS_EXTB_secdata(*argp
, secdata
);
2076 /* end of security stuff */
2080 " nfsmount: have secure info for %s\n", remname
);
2082 if (hasmntopt(&m
, MNTOPT_GRPID
) != NULL
) {
2083 argp
->flags
|= NFSMNT_GRPID
;
2085 if (nopt(&m
, MNTOPT_RSIZE
, &argp
->rsize
)) {
2086 argp
->flags
|= NFSMNT_RSIZE
;
2088 if (nopt(&m
, MNTOPT_WSIZE
, &argp
->wsize
)) {
2089 argp
->flags
|= NFSMNT_WSIZE
;
2091 if (nopt(&m
, MNTOPT_TIMEO
, &argp
->timeo
)) {
2092 argp
->flags
|= NFSMNT_TIMEO
;
2094 if (nopt(&m
, MNTOPT_RETRANS
, &argp
->retrans
)) {
2095 argp
->flags
|= NFSMNT_RETRANS
;
2097 if (nopt(&m
, MNTOPT_ACTIMEO
, &argp
->acregmax
)) {
2098 argp
->flags
|= NFSMNT_ACREGMAX
;
2099 argp
->flags
|= NFSMNT_ACDIRMAX
;
2100 argp
->flags
|= NFSMNT_ACDIRMIN
;
2101 argp
->flags
|= NFSMNT_ACREGMIN
;
2102 argp
->acdirmin
= argp
->acregmin
= argp
->acdirmax
2105 if (nopt(&m
, MNTOPT_ACREGMIN
, &argp
->acregmin
)) {
2106 argp
->flags
|= NFSMNT_ACREGMIN
;
2108 if (nopt(&m
, MNTOPT_ACREGMAX
, &argp
->acregmax
)) {
2109 argp
->flags
|= NFSMNT_ACREGMAX
;
2111 if (nopt(&m
, MNTOPT_ACDIRMIN
, &argp
->acdirmin
)) {
2112 argp
->flags
|= NFSMNT_ACDIRMIN
;
2114 if (nopt(&m
, MNTOPT_ACDIRMAX
, &argp
->acdirmax
)) {
2115 argp
->flags
|= NFSMNT_ACDIRMAX
;
2120 argp
->pathconf
= NULL
;
2121 if (error
= get_pathconf(cl
, dir
, remname
,
2122 &argp
->pathconf
, retries
)) {
2123 if (secflags
& AUTH_F_RPCTIMESYNC
)
2124 netbuf_free(syncaddr
);
2126 netdir_free(retaddrs
, ND_ADDRLIST
);
2127 free_knconf(argp
->knconf
);
2128 netbuf_free(argp
->addr
);
2129 freenetconfigent(nconf
);
2131 argp
->nfs_ext_u
.nfs_extB
.secdata
);
2133 netbuf_free(argp
->syncaddr
);
2135 free(argp
->netname
);
2137 free(argp
->hostname
);
2143 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2144 last_error
= NFSERR_IO
;
2146 if (error
== RET_RETRY
&& retries
-- > 0) {
2147 destroy_auth_client_handle(cl
);
2152 destroy_auth_client_handle(cl
);
2154 mfs
->mfs_ignore
= 1;
2157 argp
->flags
|= NFSMNT_POSIX
;
2160 " nfsmount: have pathconf for %s\n",
2165 * free loop-specific data structures
2167 destroy_auth_client_handle(cl
);
2168 freenetconfigent(nconf
);
2169 if (secflags
& AUTH_F_RPCTIMESYNC
)
2170 netbuf_free(syncaddr
);
2172 netdir_free(retaddrs
, ND_ADDRLIST
);
2175 * Decide whether to use remote host's lockd or local locking.
2176 * If we are using the public fh, we've already turned
2179 if (hasmntopt(&m
, MNTOPT_LLOCK
))
2180 argp
->flags
|= NFSMNT_LLOCK
;
2181 if (!(argp
->flags
& NFSMNT_LLOCK
) && nfsvers
== NFS_VERSION
&&
2182 remote_lock(host
, argp
->fh
)) {
2183 syslog(loglevel
, "No network locking on %s : "
2184 "contact admin to install server change", host
);
2185 argp
->flags
|= NFSMNT_LLOCK
;
2189 * Build a string for /etc/mnttab.
2190 * If possible, coalesce strings with same 'dir' info.
2192 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
2196 p
= strrchr(mnttabtext
, (int)':');
2197 if (!p
|| strcmp(p
+1, dir
) != 0) {
2198 mnttabcnt
+= strlen(remname
) + 2;
2201 mnttabcnt
+= strlen(rhost
) + 2;
2203 if ((tmp
= realloc(mnttabtext
,
2204 mnttabcnt
)) != NULL
) {
2206 strcat(mnttabtext
, ",");
2212 mnttabcnt
= strlen(remname
) + 1;
2213 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2214 mnttabtext
[0] = '\0';
2217 if (mnttabtext
!= NULL
)
2218 strcat(mnttabtext
, remname
);
2225 more_cnt
+= strlen("nfs://");
2226 more_cnt
+= strlen(mfs
->mfs_host
);
2228 if (mfs
->mfs_port
!= 0) {
2229 (void) sprintf(sport
, ":%u", mfs
->mfs_port
);
2233 more_cnt
+= strlen(sport
);
2234 more_cnt
+= 1; /* "/" */
2235 more_cnt
+= strlen(mfs
->mfs_dir
);
2238 more_cnt
+= 1; /* "," */
2239 mnttabcnt
+= more_cnt
;
2241 if ((tmp
= realloc(mnttabtext
,
2242 mnttabcnt
)) != NULL
) {
2244 strcat(mnttabtext
, ",");
2250 mnttabcnt
= more_cnt
+ 1;
2251 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2252 mnttabtext
[0] = '\0';
2255 if (mnttabtext
!= NULL
) {
2256 strcat(mnttabtext
, "nfs://");
2257 strcat(mnttabtext
, mfs
->mfs_host
);
2258 strcat(mnttabtext
, sport
);
2259 strcat(mnttabtext
, "/");
2260 strcat(mnttabtext
, mfs
->mfs_dir
);
2265 syslog(LOG_ERR
, "nfsmount: no memory");
2266 last_error
= NFSERR_IO
;
2271 * At least one entry, can call mount(2).
2276 * If replication was defeated, don't do more work
2284 * Did we get through all possibilities without success?
2289 /* Make "xattr" the default if "noxattr" is not specified. */
2290 strcpy(mopts
, opts
);
2291 if (!hasmntopt(&m
, MNTOPT_NOXATTR
) && !hasmntopt(&m
, MNTOPT_XATTR
)) {
2292 if (strlen(mopts
) > 0)
2294 strcat(mopts
, "xattr");
2298 * enable services as needed.
2303 if (strcmp(fstype
, MNTTYPE_NFS4
) == 0)
2304 sl
= service_list_v4
;
2308 (void) _check_services(sl
);
2312 * Whew; do the mount, at last.
2315 trace_prt(1, " mount %s %s (%s)\n", mnttabtext
, mntpnt
, mopts
);
2319 * About to do a nfs mount, make sure the mount_to is set for
2320 * potential ephemeral mounts with NFSv4.
2322 set_nfsv4_ephemeral_mount_to();
2325 * If no action list pointer then do the mount, otherwise
2326 * build the actions list pointer with the mount information.
2327 * so the mount can be done in the kernel.
2330 if (mount(mnttabtext
, mntpnt
, flags
| MS_DATA
, fstype
,
2331 head
, sizeof (*head
), mopts
, MAX_MNTOPT_STR
) < 0) {
2333 trace_prt(1, " Mount of %s on %s: %d\n",
2334 mnttabtext
, mntpnt
, errno
);
2335 if (errno
!= EBUSY
|| verbose
)
2337 "Mount of %s on %s: %m", mnttabtext
, mntpnt
);
2338 last_error
= NFSERR_IO
;
2342 last_error
= NFS_OK
;
2343 if (stat(mntpnt
, &stbuf
) == 0) {
2345 trace_prt(1, " mount %s dev=%x rdev=%x OK\n",
2346 mnttabtext
, stbuf
.st_dev
, stbuf
.st_rdev
);
2350 trace_prt(1, " mount %s OK\n", mnttabtext
);
2351 trace_prt(1, " stat of %s failed\n", mntpnt
);
2356 alp
->action
.action
= AUTOFS_MOUNT_RQ
;
2357 alp
->action
.action_list_entry_u
.mounta
.spec
=
2359 alp
->action
.action_list_entry_u
.mounta
.dir
= strdup(mntpnt
);
2360 alp
->action
.action_list_entry_u
.mounta
.flags
=
2362 alp
->action
.action_list_entry_u
.mounta
.fstype
=
2364 alp
->action
.action_list_entry_u
.mounta
.dataptr
= (char *)head
;
2365 alp
->action
.action_list_entry_u
.mounta
.datalen
=
2367 mntopts
= malloc(strlen(mopts
) + 1);
2368 strcpy(mntopts
, mopts
);
2369 mntopts
[strlen(mopts
)] = '\0';
2370 alp
->action
.action_list_entry_u
.mounta
.optptr
= mntopts
;
2371 alp
->action
.action_list_entry_u
.mounta
.optlen
=
2372 strlen(mntopts
) + 1;
2373 last_error
= NFS_OK
;
2381 free(argp
->pathconf
);
2382 free_knconf(argp
->knconf
);
2383 netbuf_free(argp
->addr
);
2385 netbuf_free(argp
->syncaddr
);
2386 if (argp
->netname
) {
2387 free(argp
->netname
);
2390 free(argp
->hostname
);
2391 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
2394 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
2403 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
2405 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
) {
2407 mfs
->mfs_dir
= NULL
;
2408 mfs
->mfs_flags
&= ~MFS_ALLOC_DIR
;
2411 if (mfs
->mfs_args
!= NULL
&& alp
== NULL
) {
2412 free(mfs
->mfs_args
);
2413 mfs
->mfs_args
= NULL
;
2416 if (mfs
->mfs_nconf
!= NULL
) {
2417 freenetconfigent(mfs
->mfs_nconf
);
2418 mfs
->mfs_nconf
= NULL
;
2422 return (last_error
);
2426 * get_pathconf(cl, path, fsname, pcnf, cretries)
2427 * ugliness that requires that ppathcnf and pathcnf stay consistent
2428 * cretries is a copy of retries used to determine when to syslog
2429 * on retry situations.
2432 get_pathconf(CLIENT
*cl
, char *path
, char *fsname
, struct pathcnf
**pcnf
,
2435 struct ppathcnf
*p
= NULL
;
2436 enum clnt_stat rpc_stat
;
2437 struct timeval timeout
;
2439 p
= (struct ppathcnf
*)malloc(sizeof (struct ppathcnf
));
2441 syslog(LOG_ERR
, "get_pathconf: Out of memory");
2444 memset((caddr_t
)p
, 0, sizeof (struct ppathcnf
));
2446 timeout
.tv_sec
= 10;
2447 timeout
.tv_usec
= 0;
2448 rpc_stat
= clnt_call(cl
, MOUNTPROC_PATHCONF
,
2449 xdr_dirpath
, (caddr_t
)&path
, xdr_ppathcnf
, (caddr_t
)p
, timeout
);
2450 if (rpc_stat
!= RPC_SUCCESS
) {
2451 if (cretries
-- <= 0) {
2453 "get_pathconf: %s: server not responding: %s",
2454 fsname
, clnt_sperror(cl
, ""));
2459 if (_PC_ISSET(_PC_ERROR
, p
->pc_mask
)) {
2460 syslog(LOG_ERR
, "get_pathconf: no info for %s", fsname
);
2464 *pcnf
= (struct pathcnf
*)p
;
2469 netbuf_free(struct netbuf
*nb
)
2477 #define SMALL_HOSTNAME 20
2478 #define SMALL_PROTONAME 10
2479 #define SMALL_PROTOFMLYNAME 10
2481 struct portmap_cache
{
2485 char cache_small_hosts
[SMALL_HOSTNAME
+ 1];
2486 char *cache_hostname
;
2488 char *cache_protofmly
;
2489 char cache_small_protofmly
[SMALL_PROTOFMLYNAME
+ 1];
2490 char cache_small_proto
[SMALL_PROTONAME
+ 1];
2491 struct netbuf cache_srv_addr
;
2492 struct portmap_cache
*cache_prev
, *cache_next
;
2495 rwlock_t portmap_cache_lock
;
2496 static int portmap_cache_valid_time
= 30;
2497 struct portmap_cache
*portmap_cache_head
, *portmap_cache_tail
;
2501 portmap_cache_flush(void)
2503 struct portmap_cache
*next
= NULL
, *cp
;
2505 (void) rw_wrlock(&portmap_cache_lock
);
2506 for (cp
= portmap_cache_head
; cp
; cp
= cp
->cache_next
) {
2507 if (cp
->cache_hostname
!= NULL
&&
2508 cp
->cache_hostname
!=
2509 cp
->cache_small_hosts
)
2510 free(cp
->cache_hostname
);
2511 if (cp
->cache_proto
!= NULL
&&
2513 cp
->cache_small_proto
)
2514 free(cp
->cache_proto
);
2515 if (cp
->cache_srv_addr
.buf
!= NULL
)
2516 free(cp
->cache_srv_addr
.buf
);
2517 next
= cp
->cache_next
;
2520 portmap_cache_head
= NULL
;
2521 portmap_cache_tail
= NULL
;
2522 (void) rw_unlock(&portmap_cache_lock
);
2527 * Returns 1 if the entry is found in the cache, 0 otherwise.
2530 portmap_cache_lookup(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2531 struct netconfig
*nconf
, struct netbuf
*addrp
)
2533 struct portmap_cache
*cachep
, *prev
, *next
= NULL
, *cp
;
2536 timenow
= time(NULL
);
2538 (void) rw_rdlock(&portmap_cache_lock
);
2541 * Increment the portmap cache counters for # accesses and lookups
2542 * Use a smaller factor (100 vs 1000 for the host cache) since
2543 * initial analysis shows this cache is looked up 10% that of the
2547 portmap_cache_accesses
++;
2548 portmap_cache_lookups
++;
2549 if ((portmap_cache_lookups
%100) == 0)
2550 trace_portmap_cache();
2551 #endif /* CACHE_DEBUG */
2553 for (cachep
= portmap_cache_head
; cachep
;
2554 cachep
= cachep
->cache_next
) {
2555 if (timenow
> cachep
->cache_time
) {
2557 * We stumbled across an entry in the cache which
2558 * has timed out. Free up all the entries that
2559 * were added before it, which will positionally
2560 * be after this entry. And adjust neighboring
2562 * When we drop the lock and re-acquire it, we
2563 * need to start from the beginning.
2565 (void) rw_unlock(&portmap_cache_lock
);
2566 (void) rw_wrlock(&portmap_cache_lock
);
2567 for (cp
= portmap_cache_head
;
2568 cp
&& (cp
->cache_time
>= timenow
);
2569 cp
= cp
->cache_next
)
2574 * Adjust the link of the predecessor.
2575 * Make the tail point to the new last entry.
2577 prev
= cp
->cache_prev
;
2579 portmap_cache_head
= NULL
;
2580 portmap_cache_tail
= NULL
;
2582 prev
->cache_next
= NULL
;
2583 portmap_cache_tail
= prev
;
2585 for (; cp
; cp
= next
) {
2586 if (cp
->cache_hostname
!= NULL
&&
2587 cp
->cache_hostname
!=
2588 cp
->cache_small_hosts
)
2589 free(cp
->cache_hostname
);
2590 if (cp
->cache_proto
!= NULL
&&
2592 cp
->cache_small_proto
)
2593 free(cp
->cache_proto
);
2594 if (cp
->cache_srv_addr
.buf
!= NULL
)
2595 free(cp
->cache_srv_addr
.buf
);
2596 next
= cp
->cache_next
;
2601 if (cachep
->cache_hostname
== NULL
||
2602 prog
!= cachep
->cache_prog
|| vers
!= cachep
->cache_vers
||
2603 strcmp(nconf
->nc_proto
, cachep
->cache_proto
) != 0 ||
2604 strcmp(nconf
->nc_protofmly
, cachep
->cache_protofmly
) != 0 ||
2605 strcmp(hostname
, cachep
->cache_hostname
) != 0)
2611 portmap_cache_hits
++; /* up portmap cache hit counter */
2612 #endif /* CACHE_DEBUG */
2613 addrp
->len
= cachep
->cache_srv_addr
.len
;
2614 memcpy(addrp
->buf
, cachep
->cache_srv_addr
.buf
, addrp
->len
);
2619 (void) rw_unlock(&portmap_cache_lock
);
2624 portmap_cache_enter(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2625 struct netconfig
*nconf
, struct netbuf
*addrp
)
2627 struct portmap_cache
*cachep
;
2629 int protolen
, hostnamelen
;
2631 timenow
= time(NULL
);
2633 cachep
= malloc(sizeof (struct portmap_cache
));
2636 memset((char *)cachep
, 0, sizeof (*cachep
));
2638 hostnamelen
= strlen(hostname
);
2639 if (hostnamelen
<= SMALL_HOSTNAME
)
2640 cachep
->cache_hostname
= cachep
->cache_small_hosts
;
2642 cachep
->cache_hostname
= malloc(hostnamelen
+ 1);
2643 if (cachep
->cache_hostname
== NULL
)
2646 strcpy(cachep
->cache_hostname
, hostname
);
2647 protolen
= strlen(nconf
->nc_proto
);
2648 if (protolen
<= SMALL_PROTONAME
)
2649 cachep
->cache_proto
= cachep
->cache_small_proto
;
2651 cachep
->cache_proto
= malloc(protolen
+ 1);
2652 if (cachep
->cache_proto
== NULL
)
2655 protofmlylen
= strlen(nconf
->nc_protofmly
);
2656 if (protofmlylen
<= SMALL_PROTOFMLYNAME
)
2657 cachep
->cache_protofmly
= cachep
->cache_small_protofmly
;
2659 cachep
->cache_protofmly
= malloc(protofmlylen
+ 1);
2660 if (cachep
->cache_protofmly
== NULL
)
2664 strcpy(cachep
->cache_proto
, nconf
->nc_proto
);
2665 cachep
->cache_prog
= prog
;
2666 cachep
->cache_vers
= vers
;
2667 cachep
->cache_time
= timenow
+ portmap_cache_valid_time
;
2668 cachep
->cache_srv_addr
.len
= addrp
->len
;
2669 cachep
->cache_srv_addr
.buf
= malloc(addrp
->len
);
2670 if (cachep
->cache_srv_addr
.buf
== NULL
)
2672 memcpy(cachep
->cache_srv_addr
.buf
, addrp
->buf
, addrp
->maxlen
);
2673 cachep
->cache_prev
= NULL
;
2674 (void) rw_wrlock(&portmap_cache_lock
);
2676 * There's a window in which we could have multiple threads making
2677 * the same cache entry. This can be avoided by walking the cache
2678 * once again here to check and see if there are duplicate entries
2679 * (after grabbing the write lock). This isn't fatal and I'm not
2680 * going to bother with this.
2683 portmap_cache_accesses
++; /* up portmap cache access counter */
2684 #endif /* CACHE_DEBUG */
2685 cachep
->cache_next
= portmap_cache_head
;
2686 if (portmap_cache_head
!= NULL
)
2687 portmap_cache_head
->cache_prev
= cachep
;
2688 portmap_cache_head
= cachep
;
2689 (void) rw_unlock(&portmap_cache_lock
);
2693 syslog(LOG_ERR
, "portmap_cache_enter: Memory allocation failed");
2694 if (cachep
->cache_srv_addr
.buf
)
2695 free(cachep
->cache_srv_addr
.buf
);
2696 if (cachep
->cache_proto
&& protolen
> SMALL_PROTONAME
)
2697 free(cachep
->cache_proto
);
2698 if (cachep
->cache_hostname
&& hostnamelen
> SMALL_HOSTNAME
)
2699 free(cachep
->cache_hostname
);
2700 if (cachep
->cache_protofmly
&& protofmlylen
> SMALL_PROTOFMLYNAME
)
2701 free(cachep
->cache_protofmly
);
2708 get_cached_srv_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2709 struct netconfig
*nconf
, struct netbuf
*addrp
)
2711 if (portmap_cache_lookup(hostname
, prog
, vers
, nconf
, addrp
))
2713 if (rpcb_getaddr(prog
, vers
, nconf
, addrp
, hostname
) == 0)
2715 portmap_cache_enter(hostname
, prog
, vers
, nconf
, addrp
);
2720 * Get a network address on "hostname" for program "prog"
2721 * with version "vers". If the port number is specified (non zero)
2722 * then try for a TCP/UDP transport and set the port number of the
2723 * resulting IP address.
2725 * If the address of a netconfig pointer was passed and
2726 * if it's not null, use it as the netconfig otherwise
2727 * assign the address of the netconfig that was used to
2728 * establish contact with the service.
2730 * tinfo argument is for matching the get_addr() defined in
2731 * ../nfs/mount/mount.c
2734 static struct netbuf
*
2735 get_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2736 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2737 struct t_info
*tinfo
)
2739 enum clnt_stat cstat
;
2741 return (get_server_netinfo(SERVER_ADDR
, hostname
, prog
, vers
, NULL
,
2742 nconfp
, proto
, port
, tinfo
, NULL
, FALSE
, NULL
, &cstat
));
2745 static struct netbuf
*
2746 get_pubfh(char *hostname
, rpcvers_t vers
, mfs_snego_t
*mfssnego
,
2747 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2748 struct t_info
*tinfo
, caddr_t
*fhp
, bool_t get_pubfh
, char *fspath
)
2750 enum clnt_stat cstat
;
2752 return (get_server_netinfo(SERVER_FH
, hostname
, NFS_PROGRAM
, vers
,
2753 mfssnego
, nconfp
, proto
, port
, tinfo
, fhp
, get_pubfh
, fspath
,
2757 static enum clnt_stat
2758 get_ping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2759 struct netconfig
**nconfp
, ushort_t port
, bool_t direct_to_server
)
2761 enum clnt_stat cstat
;
2763 (void) get_server_netinfo(SERVER_PING
, hostname
, prog
,
2764 vers
, NULL
, nconfp
, NULL
, port
, NULL
, NULL
,
2765 direct_to_server
, NULL
, &cstat
);
2772 enum type_of_stuff type_of_stuff
,
2776 mfs_snego_t
*mfssnego
,
2777 struct netconfig
**nconfp
,
2779 ushort_t port
, /* may be zero */
2780 struct t_info
*tinfo
,
2782 bool_t direct_to_server
,
2784 enum clnt_stat
*cstatp
)
2786 struct netbuf
*nb
= NULL
;
2787 struct netconfig
*nconf
= NULL
;
2788 NCONF_HANDLE
*nc
= NULL
;
2791 struct t_bind
*tbind
= NULL
;
2792 int nthtry
= FIRST_TRY
;
2794 if (nconfp
&& *nconfp
) {
2795 return (get_netconfig_info(type_of_stuff
, hostname
,
2796 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2797 direct_to_server
, fspath
, cstatp
, mfssnego
));
2801 * No nconf passed in.
2803 * Try to get a nconf from /etc/netconfig.
2804 * First choice is COTS, second is CLTS unless proto
2805 * is specified. When we retry, we reset the
2806 * netconfig list, so that we search the whole list
2807 * for the next choice.
2809 if ((nc
= setnetpath()) == NULL
)
2813 * If proto is specified, then only search for the match,
2814 * otherwise try COTS first, if failed, then try CLTS.
2817 while ((nconf
= getnetpath(nc
)) != NULL
) {
2818 if (strcmp(nconf
->nc_proto
, proto
))
2821 * If the port number is specified then TCP/UDP
2822 * is needed. Otherwise any cots/clts will do.
2825 if ((strcmp(nconf
->nc_protofmly
, NC_INET
) &&
2826 strcmp(nconf
->nc_protofmly
, NC_INET6
)) ||
2827 (strcmp(nconf
->nc_proto
, NC_TCP
) &&
2828 strcmp(nconf
->nc_proto
, NC_UDP
)))
2831 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2832 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2833 direct_to_server
, fspath
, cstatp
, mfssnego
);
2834 if (*cstatp
== RPC_SUCCESS
)
2844 while ((nconf
= getnetpath(nc
)) != NULL
) {
2845 if (nconf
->nc_flag
& NC_VISIBLE
) {
2846 if (nthtry
== FIRST_TRY
) {
2847 if ((nconf
->nc_semantics
==
2849 (nconf
->nc_semantics
==
2853 if ((strcmp(nconf
->nc_protofmly
,
2855 strcmp(nconf
->nc_protofmly
,
2857 (strcmp(nconf
->nc_proto
,
2862 if (nthtry
== SECOND_TRY
) {
2863 if (nconf
->nc_semantics
==
2867 if ((strcmp(nconf
->nc_protofmly
,
2869 strcmp(nconf
->nc_protofmly
,
2871 (strcmp(nconf
->nc_proto
,
2879 if (nconf
== NULL
) {
2880 if (++nthtry
<= MNT_PREF_LISTLEN
) {
2882 if ((nc
= setnetpath()) == NULL
)
2888 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2889 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2890 direct_to_server
, fspath
, cstatp
, mfssnego
);
2891 if (*cstatp
!= RPC_SUCCESS
)
2893 * Continue the same search path in the
2894 * netconfig db until no more matched nconf
2902 * Got nconf and nb. Now dup the netconfig structure (nconf)
2903 * and return it thru nconfp.
2905 if (nconf
!= NULL
) {
2906 if ((*nconfp
= getnetconfigent(nconf
->nc_netid
)) == NULL
) {
2907 syslog(LOG_ERR
, "no memory\n");
2921 get_server_fh(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2922 mfs_snego_t
*mfssnego
, struct netconfig
*nconf
, ushort_t port
,
2923 struct t_info
*tinfo
, struct t_bind
*tbind
, caddr_t
*fhp
,
2924 bool_t direct_to_server
, char *fspath
, enum clnt_stat
*cstat
)
2927 AUTH
*new_ah
= NULL
;
2928 struct snego_t snego
;
2929 enum clnt_stat cs
= RPC_TIMEDOUT
;
2931 bool_t file_handle
= 1;
2932 enum snego_stat sec
;
2935 struct netbuf
*nb
= NULL
;
2937 if (direct_to_server
!= TRUE
)
2940 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
2941 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
2944 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0)
2947 /* LINTED pointer alignment */
2948 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
)) == NULL
)
2951 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
, fd
,
2952 direct_to_server
, port
, prog
, vers
, file_handle
) < 0) {
2956 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
, prog
, vers
, 0, 0);
2960 ah
= authsys_create_default();
2963 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
2964 __FILE__
, __LINE__
);
2966 AUTH_DESTROY(cl
->cl_auth
);
2969 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
2970 __FILE__
, __LINE__
);
2974 if (!mfssnego
->snego_done
&& vers
!= NFS_V4
) {
2976 * negotiate sec flavor.
2979 if ((sec
= nfs_sec_nego(vers
, cl
, fspath
, &snego
)) ==
2984 * check if server supports the one
2985 * specified in the sec= option.
2987 if (mfssnego
->sec_opt
) {
2988 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
2989 if (snego
.array
[jj
] ==
2990 mfssnego
->nfs_sec
.sc_nfsnum
) {
2991 mfssnego
->snego_done
= TRUE
;
2998 * find a common sec flavor
3000 if (!mfssnego
->snego_done
) {
3001 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
3002 if (!nfs_getseconfig_bynumber(
3004 &mfssnego
->nfs_sec
)) {
3005 mfssnego
->snego_done
= TRUE
;
3010 if (!mfssnego
->snego_done
)
3013 * Now that the flavor has been
3014 * negotiated, get the fh.
3016 * First, create an auth handle using the negotiated
3017 * sec flavor in the next lookup to
3018 * fetch the filehandle.
3020 new_ah
= nfs_create_ah(cl
, hostname
,
3021 &mfssnego
->nfs_sec
);
3025 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3026 __FILE__
, __LINE__
);
3028 AUTH_DESTROY(cl
->cl_auth
);
3029 cl
->cl_auth
= new_ah
;
3031 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3032 __FILE__
, __LINE__
);
3034 } else if (sec
== SNEGO_ARRAY_TOO_SMALL
||
3035 sec
== SNEGO_FAILURE
) {
3046 memset((char *)&arg
.dir
, 0, sizeof (wnl_fh
));
3047 memset((char *)&res
, 0, sizeof (wnl_diropres
));
3049 if (wnlproc_lookup_2(&arg
, &res
, cl
) !=
3050 RPC_SUCCESS
|| res
.status
!= WNL_OK
)
3052 *fhp
= malloc(sizeof (wnl_fh
));
3055 syslog(LOG_ERR
, "no memory\n");
3059 memcpy((char *)*fhp
,
3060 (char *)&res
.wnl_diropres_u
.wnl_diropres
.file
,
3067 WNL_LOOKUP3args arg
;
3071 memset((char *)&arg
.what
.dir
, 0, sizeof (wnl_fh3
));
3072 memset((char *)&res
, 0, sizeof (WNL_LOOKUP3res
));
3073 arg
.what
.name
= fspath
;
3074 if (wnlproc3_lookup_3(&arg
, &res
, cl
) !=
3075 RPC_SUCCESS
|| res
.status
!= WNL3_OK
)
3078 fh3p
= (nfs_fh3
*)malloc(sizeof (*fh3p
));
3081 syslog(LOG_ERR
, "no memory\n");
3086 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_len
;
3087 memcpy(fh3p
->fh3_u
.data
,
3088 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_val
,
3091 *fhp
= (caddr_t
)fh3p
;
3099 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
3101 if (cs
!= RPC_SUCCESS
)
3104 *fhp
= strdup(fspath
);
3106 cs
= RPC_SYSTEMERROR
;
3111 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
3113 syslog(LOG_ERR
, "no memory\n");
3114 cs
= RPC_SYSTEMERROR
;
3117 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
3118 if (nb
->buf
== NULL
) {
3119 syslog(LOG_ERR
, "no memory\n");
3122 cs
= RPC_SYSTEMERROR
;
3125 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
3126 nb
->len
= tbind
->addr
.len
;
3127 nb
->maxlen
= tbind
->addr
.maxlen
;
3131 destroy_auth_client_handle(cl
);
3132 cleanup_tli_parms(tbind
, fd
);
3137 * Sends a null call to the remote host's (NFS program, versp). versp
3138 * may be "NULL" in which case the default maximum version is used.
3139 * Upon return, versp contains the maximum version supported iff versp!= NULL.
3147 ushort_t port
, /* may be zero */
3153 struct timeval rpc_to_new
= {15, 0};
3154 static struct timeval rpc_rtrans_new
= {-1, -1};
3155 enum clnt_stat clnt_stat
;
3157 rpcvers_t versmax
; /* maximum version to try against server */
3158 rpcvers_t outvers
; /* version supported by host on last call */
3159 rpcvers_t vers_to_try
; /* to try different versions against host */
3161 struct netconfig
*nconf
;
3163 hostname
= strdup(hostpart
);
3164 if (hostname
== NULL
) {
3165 return (RPC_SYSTEMERROR
);
3167 unbracket(&hostname
);
3169 if (path
!= NULL
&& strcmp(hostname
, "nfs") == 0 &&
3170 strncmp(path
, "//", 2) == 0) {
3173 hostname
= strdup(path
+2);
3175 if (hostname
== NULL
)
3176 return (RPC_SYSTEMERROR
);
3178 path
= strchr(hostname
, '/');
3181 * This cannot happen. If it does, give up
3182 * on the ping as this is obviously a corrupt
3187 return (RPC_SUCCESS
);
3191 * Probable end point of host string.
3195 sport
= strchr(hostname
, ':');
3197 if (sport
!= NULL
&& sport
< path
) {
3200 * Actual end point of host string.
3203 port
= htons((ushort_t
)atoi(sport
+1));
3209 /* Pick up the default versions and then set them appropriately */
3212 /* use versmin passed in */
3215 set_versrange(0, &versmax
, &versmin
);
3219 strncasecmp(proto
, NC_UDP
, strlen(NC_UDP
)) == 0 &&
3220 versmax
== NFS_V4
) {
3221 if (versmin
== NFS_V4
) {
3223 *versp
= versmax
- 1;
3224 return (RPC_SUCCESS
);
3226 return (RPC_PROGUNAVAIL
);
3235 switch (cache_check(hostname
, versp
, proto
)) {
3237 if (hostname
!= hostpart
)
3239 return (RPC_SUCCESS
);
3241 if (hostname
!= hostpart
)
3243 return (RPC_TIMEDOUT
);
3250 * XXX The retransmission time rpcbrmttime is a global defined
3251 * in the rpc library (rpcb_clnt.c). We use (and like) the default
3252 * value of 15 sec in the rpc library. The code below is to protect
3253 * us in case it changes. This need not be done under a lock since
3254 * any # of threads entering this function will get the same
3255 * retransmission value.
3257 if (rpc_rtrans_new
.tv_sec
== -1 && rpc_rtrans_new
.tv_usec
== -1) {
3258 __rpc_control(CLCR_GET_RPCB_RMTTIME
, (char *)&rpc_rtrans_new
);
3259 if (rpc_rtrans_new
.tv_sec
!= 15 && rpc_rtrans_new
.tv_sec
!= 0)
3261 trace_prt(1, "RPC library rttimer changed\n");
3265 * XXX Manipulate the total timeout to get the number of
3266 * desired retransmissions. This code is heavily dependant on
3267 * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3269 for (i
= 0, j
= rpc_rtrans_new
.tv_sec
; i
< attempts
-1; i
++) {
3270 if (j
< RPC_MAX_BACKOFF
)
3273 j
= RPC_MAX_BACKOFF
;
3274 rpc_to_new
.tv_sec
+= j
;
3277 vers_to_try
= versmax
;
3280 * check the host's version within the timeout
3283 trace_prt(1, " ping: %s timeout=%ld request vers=%d min=%d\n",
3284 hostname
, rpc_to_new
.tv_sec
, versmax
, versmin
);
3286 if (usepub
== FALSE
) {
3289 * If NFSv4, then we do the same thing as is used
3290 * for public filehandles so that we avoid rpcbind
3292 if (vers_to_try
== NFS_V4
) {
3294 trace_prt(1, " pingnfs: Trying ping via "
3298 cl
= clnt_create_service_timed(hostname
, "nfs",
3299 NFS_PROGRAM
, vers_to_try
,
3300 port
, "circuit_v", &rpc_to_new
);
3302 outvers
= vers_to_try
;
3307 " pingnfs: Can't ping via "
3308 "\"circuit_v\" %s: RPC error=%d\n",
3309 hostname
, rpc_createerr
.cf_stat
);
3313 cl
= clnt_create_vers_timed(hostname
,
3314 NFS_PROGRAM
, &outvers
, versmin
, vers_to_try
,
3315 "datagram_v", &rpc_to_new
);
3320 " pingnfs: Can't ping via "
3321 "\"datagram_v\"%s: RPC error=%d\n",
3322 hostname
, rpc_createerr
.cf_stat
);
3324 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
3325 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
3327 if (rpc_createerr
.cf_stat
==
3328 RPC_PROGNOTREGISTERED
) {
3331 " pingnfs: Trying ping "
3332 "via \"circuit_v\"\n");
3334 cl
= clnt_create_vers_timed(hostname
,
3335 NFS_PROGRAM
, &outvers
,
3336 versmin
, vers_to_try
,
3337 "circuit_v", &rpc_to_new
);
3342 " pingnfs: Can't ping "
3343 "via \"circuit_v\" %s: "
3346 rpc_createerr
.cf_stat
);
3352 * backoff and return lower version to retry the ping.
3353 * XXX we should be more careful and handle
3354 * RPC_PROGVERSMISMATCH here, because that error is handled
3355 * in clnt_create_vers(). It's not done to stay in sync
3356 * with the nfs mount command.
3359 if (vers_to_try
< versmin
)
3361 if (versp
!= NULL
) { /* recheck the cache */
3362 *versp
= vers_to_try
;
3365 " pingnfs: check cache: vers=%d\n",
3368 switch (cache_check(hostname
, versp
, proto
)) {
3370 if (hostname
!= hostpart
)
3372 return (RPC_SUCCESS
);
3374 if (hostname
!= hostpart
)
3376 return (RPC_TIMEDOUT
);
3383 trace_prt(1, " pingnfs: Try version=%d\n",
3386 } while (cl
== NULL
);
3391 syslog(LOG_ERR
, "pingnfs: %s%s",
3392 hostname
, clnt_spcreateerror(""));
3393 clnt_stat
= rpc_createerr
.cf_stat
;
3396 clnt_stat
= RPC_SUCCESS
;
3400 for (vers_to_try
= versmax
; vers_to_try
>= versmin
;
3406 trace_prt(1, " pingnfs: Try version=%d "
3407 "using get_ping()\n", vers_to_try
);
3410 clnt_stat
= get_ping(hostname
, NFS_PROGRAM
,
3411 vers_to_try
, &nconf
, port
, TRUE
);
3414 freenetconfigent(nconf
);
3416 if (clnt_stat
== RPC_SUCCESS
) {
3417 outvers
= vers_to_try
;
3424 clnt_stat
== RPC_SUCCESS
?
3425 trace_prt(1, " pingnfs OK: nfs version=%d\n", outvers
):
3426 trace_prt(1, " pingnfs FAIL: can't get nfs version\n");
3428 if (clnt_stat
== RPC_SUCCESS
) {
3429 cache_enter(hostname
, versmax
, outvers
, proto
, GOODHOST
);
3433 cache_enter(hostname
, versmax
, versmax
, proto
, DEADHOST
);
3435 if (hostpart
!= hostname
)
3441 #define MNTTYPE_LOFS "lofs"
3444 loopbackmount(char *fsname
, char *dir
, char *mntopts
, int overlay
)
3448 char fstype
[] = MNTTYPE_LOFS
;
3451 char optbuf
[MAX_MNTOPT_STR
];
3453 dirlen
= strlen(dir
);
3454 if (dir
[dirlen
-1] == ' ')
3457 if (dirlen
== strlen(fsname
) &&
3458 strncmp(fsname
, dir
, dirlen
) == 0) {
3460 "Mount of %s on %s would result in deadlock, aborted\n",
3464 mnt
.mnt_mntopts
= mntopts
;
3465 if (hasmntopt(&mnt
, MNTOPT_RO
) != NULL
)
3468 (void) strlcpy(optbuf
, mntopts
, sizeof (optbuf
));
3471 flags
|= MS_OVERLAY
;
3474 trace_prt(1, " loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3475 fsname
, dir
, flags
);
3478 if (mount(fsname
, dir
, flags
| MS_DATA
| MS_OPTIONSTR
, fstype
,
3479 NULL
, 0, optbuf
, sizeof (optbuf
)) < 0) {
3480 syslog(LOG_ERR
, "Mount of %s on %s: %m", fsname
, dir
);
3484 if (stat(dir
, &st
) == 0) {
3487 " loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3488 fsname
, dir
, st
.st_dev
, st
.st_rdev
);
3493 " loopbackmount of %s on %s OK\n", fsname
, dir
);
3494 trace_prt(1, " stat of %s failed\n", dir
);
3502 * Look for the value of a numeric option of the form foo=x. If found, set
3503 * *valp to the value and return non-zero. If not found or the option is
3504 * malformed, return zero.
3508 nopt(struct mnttab
*mnt
, char *opt
, int *valp
)
3514 * We should never get a null pointer, but if we do, it's better to
3515 * ignore the option than to dump core.
3519 syslog(LOG_DEBUG
, "null pointer for %s option", opt
);
3523 if (str
= hasmntopt(mnt
, opt
)) {
3524 if (equal
= strchr(str
, '=')) {
3525 *valp
= atoi(&equal
[1]);
3528 syslog(LOG_ERR
, "Bad numeric option '%s'", str
);
3535 nfsunmount(struct mnttab
*mnt
)
3537 struct timeval timeout
;
3539 enum clnt_stat rpc_stat
;
3541 struct replica
*list
;
3543 int isv4mount
= is_v4_mount(mnt
->mnt_mountp
);
3546 trace_prt(1, " nfsunmount: umount %s\n", mnt
->mnt_mountp
);
3548 if (umount(mnt
->mnt_mountp
) < 0) {
3550 trace_prt(1, " nfsunmount: umount %s FAILED\n",
3558 * If this is a NFSv4 mount, the mount protocol was not used
3559 * so we just return.
3563 trace_prt(1, " nfsunmount: umount %s OK\n",
3570 * If mounted with -o public, then no need to contact server
3571 * because mount protocol was not used.
3573 if (hasmntopt(mnt
, MNTOPT_PUBLIC
) != NULL
) {
3578 * The rest of this code is advisory to the server.
3579 * If it fails return success anyway.
3582 list
= parse_replica(mnt
->mnt_special
, &count
);
3585 syslog(LOG_ERR
, "Memory allocation failed: %m");
3589 for (i
= 0; i
< count
; i
++) {
3591 host
= list
[i
].host
;
3592 path
= list
[i
].path
;
3595 * Skip file systems mounted using WebNFS, because mount
3596 * protocol was not used.
3598 if (strcmp(host
, "nfs") == 0 && strncmp(path
, "//", 2) == 0)
3601 cl
= clnt_create(host
, MOUNTPROG
, MOUNTVERS
, "datagram_v");
3605 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
3606 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0, __FILE__
, __LINE__
);
3608 if (__clnt_bindresvport(cl
) < 0) {
3610 syslog(LOG_ERR
, "umount %s:%s: %s", host
, path
,
3611 "Couldn't bind to reserved port");
3613 destroy_auth_client_handle(cl
);
3617 drop_alloc("AUTH_HANDLE", cl
->cl_auth
, __FILE__
, __LINE__
);
3619 AUTH_DESTROY(cl
->cl_auth
);
3620 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
3622 syslog(LOG_ERR
, "umount %s:%s: %s", host
, path
,
3623 "Failed creating default auth handle");
3625 destroy_auth_client_handle(cl
);
3629 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0, __FILE__
, __LINE__
);
3631 timeout
.tv_usec
= 0;
3633 rpc_stat
= clnt_call(cl
, MOUNTPROC_UMNT
, xdr_dirpath
,
3634 (caddr_t
)&path
, xdr_void
, NULL
, timeout
);
3635 if (verbose
&& rpc_stat
!= RPC_SUCCESS
)
3636 syslog(LOG_ERR
, "%s: %s",
3637 host
, clnt_sperror(cl
, "unmount"));
3638 destroy_auth_client_handle(cl
);
3641 free_replica(list
, count
);
3644 trace_prt(1, " nfsunmount: umount %s OK\n", mnt
->mnt_mountp
);
3651 * Put a new entry in the cache chain by prepending it to the front.
3652 * If there isn't enough memory then just give up.
3655 cache_enter(char *host
, rpcvers_t reqvers
, rpcvers_t outvers
, char *proto
,
3658 struct cache_entry
*entry
;
3659 int cache_time
= 30; /* sec */
3661 timenow
= time(NULL
);
3663 entry
= (struct cache_entry
*)malloc(sizeof (struct cache_entry
));
3666 (void) memset((caddr_t
)entry
, 0, sizeof (struct cache_entry
));
3667 entry
->cache_host
= strdup(host
);
3668 if (entry
->cache_host
== NULL
) {
3672 entry
->cache_reqvers
= reqvers
;
3673 entry
->cache_outvers
= outvers
;
3674 entry
->cache_proto
= (proto
== NULL
? NULL
: strdup(proto
));
3675 entry
->cache_state
= state
;
3676 entry
->cache_time
= timenow
+ cache_time
;
3677 (void) rw_wrlock(&cache_lock
);
3679 host_cache_accesses
++; /* up host cache access counter */
3680 #endif /* CACHE DEBUG */
3681 entry
->cache_next
= cache_head
;
3683 (void) rw_unlock(&cache_lock
);
3687 cache_check(char *host
, rpcvers_t
*versp
, char *proto
)
3690 struct cache_entry
*ce
, *prev
;
3692 timenow
= time(NULL
);
3694 (void) rw_rdlock(&cache_lock
);
3697 /* Increment the lookup and access counters for the host cache */
3698 host_cache_accesses
++;
3699 host_cache_lookups
++;
3700 if ((host_cache_lookups
%1000) == 0)
3702 #endif /* CACHE DEBUG */
3704 for (ce
= cache_head
; ce
; ce
= ce
->cache_next
) {
3705 if (timenow
> ce
->cache_time
) {
3706 (void) rw_unlock(&cache_lock
);
3707 (void) rw_wrlock(&cache_lock
);
3708 for (prev
= NULL
, ce
= cache_head
; ce
;
3709 prev
= ce
, ce
= ce
->cache_next
) {
3710 if (timenow
> ce
->cache_time
) {
3713 prev
->cache_next
= NULL
;
3719 (void) rw_unlock(&cache_lock
);
3722 if (strcmp(host
, ce
->cache_host
) != 0)
3724 if ((proto
== NULL
&& ce
->cache_proto
!= NULL
) ||
3725 (proto
!= NULL
&& ce
->cache_proto
== NULL
))
3727 if (proto
!= NULL
&&
3728 strcmp(proto
, ce
->cache_proto
) != 0)
3731 if (versp
== NULL
||
3732 (versp
!= NULL
&& *versp
== ce
->cache_reqvers
) ||
3733 (versp
!= NULL
&& *versp
== ce
->cache_outvers
)) {
3735 *versp
= ce
->cache_outvers
;
3736 state
= ce
->cache_state
;
3738 /* increment the host cache hit counters */
3740 if (state
== GOODHOST
)
3741 goodhost_cache_hits
++;
3742 if (state
== DEADHOST
)
3743 deadhost_cache_hits
++;
3744 #endif /* CACHE_DEBUG */
3745 (void) rw_unlock(&cache_lock
);
3749 (void) rw_unlock(&cache_lock
);
3754 * Free a cache entry and all entries
3755 * further down the chain since they
3756 * will also be expired.
3759 cache_free(struct cache_entry
*entry
)
3761 struct cache_entry
*ce
, *next
= NULL
;
3763 for (ce
= entry
; ce
; ce
= next
) {
3765 free(ce
->cache_host
);
3766 if (ce
->cache_proto
)
3767 free(ce
->cache_proto
);
3768 next
= ce
->cache_next
;
3777 (void) rw_wrlock(&cache_lock
);
3778 cache_free(cache_head
);
3780 (void) rw_unlock(&cache_lock
);
3786 mutex_lock(&cleanup_lock
);
3787 cond_signal(&cleanup_start_cv
);
3788 (void) cond_wait(&cleanup_done_cv
, &cleanup_lock
);
3789 mutex_unlock(&cleanup_lock
);
3791 portmap_cache_flush();
3796 * Returns 1, if port option is NFS_PORT or
3797 * nfsd is running on the port given
3798 * Returns 0, if both port is not NFS_PORT and nfsd is not
3799 * running on the port.
3803 is_nfs_port(char *opts
)
3806 uint_t nfs_port
= 0;
3811 m
.mnt_mntopts
= opts
;
3814 * Get port specified in options list, if any.
3816 got_port
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
3819 * if no port specified or it is same as NFS_PORT return nfs
3820 * To use any other daemon the port number should be different
3822 if (!got_port
|| nfs_port
== NFS_PORT
)
3825 * If daemon is nfsd, return nfs
3827 if (getservbyport_r(nfs_port
, NULL
, &sv
, buf
, 256) == &sv
&&
3828 strcmp(sv
.s_name
, "nfsd") == 0)
3839 * destroy_auth_client_handle(cl)
3840 * destroys the created client handle
3843 destroy_auth_client_handle(CLIENT
*cl
)
3848 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3849 __FILE__
, __LINE__
);
3851 AUTH_DESTROY(cl
->cl_auth
);
3855 drop_alloc("CLNT_HANDLE", cl
,
3856 __FILE__
, __LINE__
);
3864 * Attempt to figure out which version of NFS to use in pingnfs(). If
3865 * the version number was specified (i.e., non-zero), then use it.
3866 * Otherwise, default to the compiled-in default or the default as set
3867 * by the /etc/default/nfs configuration (as read by read_default().
3870 set_versrange(rpcvers_t nfsvers
, rpcvers_t
*vers
, rpcvers_t
*versmin
)
3874 *vers
= vers_max_default
;
3875 *versmin
= vers_min_default
;
3886 *vers
= NFS_VERSION
; /* version 2 */
3887 *versmin
= NFS_VERSMIN
; /* version 2 */
3897 * trace_portmap_cache()
3898 * traces the portmap cache values at desired points
3901 trace_portmap_cache(void)
3903 syslog(LOG_ERR
, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
3904 portmap_cache_accesses
, portmap_cache_lookups
,
3905 portmap_cache_hits
);
3909 * trace_host_cache()
3910 * traces the host cache values at desired points
3913 trace_host_cache(void)
3916 "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
3917 host_cache_accesses
, host_cache_lookups
, deadhost_cache_hits
,
3918 goodhost_cache_hits
);
3920 #endif /* CACHE_DEBUG */
3923 * Read the NFS SMF properties to determine if the
3924 * client has been configured for a new min/max for the NFS version to
3928 #define SVC_NFS_CLIENT "svc:/network/nfs/client"
3931 read_default_nfs(void)
3939 ret
= nfs_smf_get_prop("client_versmin", defval
, DEFAULT_INSTANCE
,
3940 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
3943 tmp
= strtol(defval
, NULL
, 10);
3945 vers_min_default
= tmp
;
3950 ret
= nfs_smf_get_prop("client_versmax", defval
, DEFAULT_INSTANCE
,
3951 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
3954 tmp
= strtol(defval
, NULL
, 10);
3956 vers_max_default
= tmp
;
3961 * Quick sanity check on the values picked up from the
3962 * defaults file. Make sure that a mistake wasn't
3963 * made that will confuse things later on.
3964 * If so, reset to compiled-in defaults
3966 if (vers_min_default
> vers_max_default
||
3967 vers_min_default
< NFS_VERSMIN
||
3968 vers_max_default
> NFS_VERSMAX
) {
3971 " read_default: version minimum/maximum incorrectly configured\n");
3973 " read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
3974 vers_min_default
, vers_max_default
,
3975 NFS_VERSMIN_DEFAULT
,
3976 NFS_VERSMAX_DEFAULT
);
3978 vers_min_default
= NFS_VERSMIN_DEFAULT
;
3979 vers_max_default
= NFS_VERSMAX_DEFAULT
;
3984 * Find the mnttab entry that corresponds to "name".
3985 * We're not sure what the name represents: either
3986 * a mountpoint name, or a special name (server:/path).
3987 * Return the last entry in the file that matches.
3989 static struct extmnttab
*
3990 mnttab_find(char *dirname
)
3993 struct extmnttab mnt
;
3994 struct extmnttab
*res
= NULL
;
3996 fp
= fopen(MNTTAB
, "r");
3999 trace_prt(1, " mnttab_find: unable to open mnttab\n");
4002 while (getextmntent(fp
, &mnt
, sizeof (struct extmnttab
)) == 0) {
4003 if (strcmp(mnt
.mnt_mountp
, dirname
) == 0 ||
4004 strcmp(mnt
.mnt_special
, dirname
) == 0) {
4007 res
= fsdupmnttab(&mnt
);
4015 trace_prt(1, " mnttab_find: unable to find %s\n",
4023 * This function's behavior is taken from nfsstat.
4024 * Trying to determine what NFS version was used for the mount.
4027 is_v4_mount(char *mntpath
)
4029 kstat_ctl_t
*kc
= NULL
; /* libkstat cookie */
4032 struct mntinfo_kstat mik
;
4033 struct extmnttab
*mntp
;
4036 if ((mntp
= mnttab_find(mntpath
)) == NULL
)
4039 /* save the minor number and free the struct so we don't forget */
4040 mnt_minor
= mntp
->mnt_minor
;
4043 if ((kc
= kstat_open()) == NULL
)
4046 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
4047 if (ksp
->ks_type
!= KSTAT_TYPE_RAW
)
4049 if (strcmp(ksp
->ks_module
, "nfs") != 0)
4051 if (strcmp(ksp
->ks_name
, "mntinfo") != 0)
4053 if (mnt_minor
!= ksp
->ks_instance
)
4056 if (kstat_read(kc
, ksp
, &mik
) == -1)
4059 (void) kstat_close(kc
);
4060 if (mik
.mik_vers
== 4)
4065 (void) kstat_close(kc
);
4071 create_homedir(const char *src
, const char *dst
)
4076 struct passwd
*pwd
, pwds
;
4077 char buf_pwd
[NSS_BUFLEN_PASSWD
];
4083 trace_prt(1, "entered create_homedir\n");
4085 if (stat(src
, &stbuf
) == 0) {
4087 trace_prt(1, "src exists\n");
4091 dst_username
= strrchr(dst
, '/');
4093 dst_username
++; /* Skip over slash */
4094 getpwnam_r(dst_username
, &pwds
, buf_pwd
, sizeof (buf_pwd
),
4103 homedir_len
= strlen(pwd
->pw_dir
);
4104 dst_dir_len
= strlen(dst
) - homedir_len
;
4105 src_dir_len
= strlen(src
) - homedir_len
;
4107 /* Check that the paths are in the same zone */
4108 if (src_dir_len
< dst_dir_len
||
4109 (strncmp(dst
, src
, dst_dir_len
) != 0)) {
4111 trace_prt(1, " paths don't match\n");
4114 /* Check that mountpoint is an auto_home entry */
4115 if (dst_dir_len
< 0 ||
4116 (strcmp(pwd
->pw_dir
, dst
+ dst_dir_len
) != 0)) {
4120 /* Check that source is an home directory entry */
4121 if (src_dir_len
< 0 ||
4122 (strcmp(pwd
->pw_dir
, src
+ src_dir_len
) != 0)) {
4124 trace_prt(1, " homedir (2) doesn't match %s\n",
4131 S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
) == -1) {
4133 trace_prt(1, " Couldn't mkdir %s\n", src
);
4138 if (chown(src
, pwd
->pw_uid
, pwd
->pw_gid
) == -1) {
4143 /* Created new home directory for the user */
4148 free_nfs_args(struct nfs_args
*argp
)
4150 struct nfs_args
*oldp
;
4153 free(argp
->pathconf
);
4155 free_knconf(argp
->knconf
);
4157 netbuf_free(argp
->addr
);
4159 netbuf_free(argp
->syncaddr
);
4161 free(argp
->netname
);
4163 free(argp
->hostname
);
4164 if (argp
->nfs_ext_u
.nfs_extB
.secdata
)
4165 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
4168 if (argp
->nfs_ext_u
.nfs_extA
.secdata
) {
4170 sd
= argp
->nfs_ext_u
.nfs_extA
.secdata
;
4173 switch (sd
->rpcflavor
) {
4180 dh_k4_clntdata_t
*dhk4
;
4181 dhk4
= (dh_k4_clntdata_t
*)sd
->data
;
4184 if (dhk4
->syncaddr
.buf
)
4185 free(dhk4
->syncaddr
.buf
);
4186 if (dhk4
->knconf
->knc_protofmly
)
4187 free(dhk4
->knconf
->knc_protofmly
);
4188 if (dhk4
->knconf
->knc_proto
)
4189 free(dhk4
->knconf
->knc_proto
);
4193 free(dhk4
->netname
);
4199 gss_clntdata_t
*gss
;
4200 gss
= (gss_clntdata_t
*)sd
->data
;
4203 if (gss
->mechanism
.elements
)
4204 free(gss
->mechanism
.elements
);
4211 if (argp
->nfs_args_ext
== NFS_ARGS_EXTB
)
4212 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
4220 get_netconfig_info(enum type_of_stuff type_of_stuff
, char *hostname
,
4221 rpcprog_t prog
, rpcvers_t vers
, struct netconfig
*nconf
,
4222 ushort_t port
, struct t_info
*tinfo
, struct t_bind
*tbind
,
4223 caddr_t
*fhp
, bool_t direct_to_server
, char *fspath
,
4224 enum clnt_stat
*cstat
, mfs_snego_t
*mfssnego
)
4226 struct netconfig
*nb
= NULL
;
4227 int ping_server
= 0;
4233 switch (type_of_stuff
) {
4235 nb
= get_server_fh(hostname
, prog
, vers
, mfssnego
,
4236 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4243 nb
= get_server_addrorping(hostname
, prog
, vers
,
4244 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4245 fspath
, cstat
, ping_server
);
4254 * Get the server address or can we ping it or not.
4255 * Check the portmap cache first for server address.
4256 * If no entries there, ping the server with a NULLPROC rpc.
4259 get_server_addrorping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
4260 struct netconfig
*nconf
, ushort_t port
, struct t_info
*tinfo
,
4261 struct t_bind
*tbind
, caddr_t
*fhp
, bool_t direct_to_server
,
4262 char *fspath
, enum clnt_stat
*cstat
, int ping_server
)
4265 enum clnt_stat cs
= RPC_TIMEDOUT
;
4266 struct netbuf
*nb
= NULL
;
4270 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
4271 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
4274 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0) {
4278 /* LINTED pointer alignment */
4279 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
))
4284 if (direct_to_server
!= TRUE
) {
4286 if (get_cached_srv_addr(hostname
, prog
, vers
,
4287 nconf
, &tbind
->addr
) == 0)
4294 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
,
4295 fd
, direct_to_server
, port
, prog
, vers
, 0) < 0)
4298 if (port
|| (direct_to_server
== TRUE
)) {
4301 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
,
4306 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
4308 if (cs
!= RPC_SUCCESS
) {
4309 syslog(LOG_ERR
, "error is %d", cs
);
4314 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
4316 syslog(LOG_ERR
, "no memory\n");
4319 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
4320 if (nb
->buf
== NULL
) {
4321 syslog(LOG_ERR
, "no memory\n");
4326 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
4327 nb
->len
= tbind
->addr
.len
;
4328 nb
->maxlen
= tbind
->addr
.maxlen
;
4332 destroy_auth_client_handle(cl
);
4333 cleanup_tli_parms(tbind
, fd
);