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();
180 static void trace_portmap_cache();
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);
234 struct mapfs
*mfs
, *mp
;
244 mfs
= enum_servers(me
, prevhost
);
249 * Try loopback if we have something on localhost; if nothing
250 * works, we will fall back to NFS
252 if (is_nfs_port(me
->map_mntopts
)) {
253 for (mp
= mfs
; mp
; mp
= mp
->mfs_next
) {
254 if (self_check(mp
->mfs_host
)) {
255 err
= loopbackmount(mp
->mfs_dir
,
256 mntpnt
, me
->map_mntopts
, overlay
);
261 * Free action_list if there
262 * is one as it is not needed.
263 * Make sure to set alpp to null
264 * so caller doesn't try to free it
277 dir
= strdup(mfs
->mfs_dir
);
278 err
= nfsmount(mfs
, mntpnt
, me
->map_mntopts
,
280 if (err
&& trace
> 1) {
281 trace_prt(1, " Couldn't mount %s:%s, err=%d\n",
282 mfs
->mfs_host
? mfs
->mfs_host
: "",
283 mfs
->mfs_dir
? mfs
->mfs_dir
: dir
, err
);
293 * Using the new ioctl SIOCTONLINK to determine if a host is on the same
294 * subnet. Remove the old network, subnet check.
297 static struct mapfs
*
298 get_mysubnet_servers(struct mapfs
*mfs_in
)
301 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
303 struct netconfig
*nconf
;
304 NCONF_HANDLE
*nc
= NULL
;
305 struct nd_hostserv hs
;
306 struct nd_addrlist
*retaddrs
;
308 struct sioc_addrreq areq
;
314 hs
.h_serv
= "rpcbind";
316 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
319 while (nconf
= getnetconfig(nc
)) {
322 * Care about INET family only. proto_done flag
323 * indicates if we have already covered this
324 * protocol family. If so skip it
326 if (((strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0) ||
327 (strcmp(nconf
->nc_protofmly
, NC_INET
) == 0)) &&
328 (nconf
->nc_semantics
== NC_TPI_CLTS
)) {
332 hs
.h_host
= mfs
->mfs_host
;
334 if (netdir_getbyname(nconf
, &hs
, &retaddrs
) != ND_OK
)
338 * For each host address see if it's on our
342 if (strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0)
346 nb
= retaddrs
->n_addrs
;
347 for (i
= 0; i
< retaddrs
->n_cnt
; i
++, nb
++) {
348 memset(&areq
.sa_addr
, 0, sizeof (areq
.sa_addr
));
349 memcpy(&areq
.sa_addr
, nb
->buf
, MIN(nb
->len
,
350 sizeof (areq
.sa_addr
)));
351 if (res
= subnet_test(af
, &areq
)) {
352 p
= add_mfs(mfs
, DIST_MYNET
,
353 &mfs_head
, &mfs_tail
);
355 netdir_free(retaddrs
,
362 } /* end of every host */
364 trace_prt(1, "get_mysubnet_servers: host=%s "
365 "netid=%s res=%s\n", mfs
->mfs_host
,
366 nconf
->nc_netid
, res
== 1?"SUC":"FAIL");
369 netdir_free(retaddrs
, ND_ADDRLIST
);
374 } /* end of every map */
381 subnet_test(int af
, struct sioc_addrreq
*areq
)
385 if ((s
= socket(af
, SOCK_DGRAM
, 0)) < 0) {
391 if (ioctl(s
, SIOCTONLINK
, (caddr_t
)areq
) < 0) {
392 syslog(LOG_ERR
, "subnet_test:SIOCTONLINK failed");
396 if (areq
->sa_res
== 1)
405 * ping a bunch of hosts at once and sort by who responds first
407 static struct mapfs
*
408 sort_servers(struct mapfs
*mfs_in
, int timeout
)
410 struct mapfs
*m1
= NULL
;
411 enum clnt_stat clnt_stat
;
416 clnt_stat
= nfs_cast(mfs_in
, &m1
, timeout
);
419 char buff
[2048] = {'\0'};
421 for (m1
= mfs_in
; m1
; m1
= m1
->mfs_next
) {
422 (void) strcat(buff
, m1
->mfs_host
);
424 (void) strcat(buff
, ",");
427 syslog(LOG_ERR
, "servers %s not responding: %s",
428 buff
, clnt_sperrno(clnt_stat
));
435 * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
436 * provided it is not marked "ignored" and isn't a dupe of ones we've
440 add_mfs(struct mapfs
*mfs
, int distance
, struct mapfs
**mfs_head
,
441 struct mapfs
**mfs_tail
)
443 struct mapfs
*tmp
, *new;
445 for (tmp
= *mfs_head
; tmp
; tmp
= tmp
->mfs_next
)
446 if ((strcmp(tmp
->mfs_host
, mfs
->mfs_host
) == 0 &&
447 strcmp(tmp
->mfs_dir
, mfs
->mfs_dir
) == 0) ||
450 new = (struct mapfs
*)malloc(sizeof (struct mapfs
));
452 syslog(LOG_ERR
, "Memory allocation failed: %m");
455 bcopy(mfs
, new, sizeof (struct mapfs
));
456 new->mfs_next
= NULL
;
458 new->mfs_distance
= distance
;
460 *mfs_tail
= *mfs_head
= new;
462 (*mfs_tail
)->mfs_next
= new;
469 dump_mfs(struct mapfs
*mfs
, char *message
, int level
)
476 trace_prt(1, "%s", message
);
478 trace_prt(0, "mfs is null\n");
481 for (m1
= mfs
; m1
; m1
= m1
->mfs_next
)
482 trace_prt(0, "%s[%s] ", m1
->mfs_host
, dump_distance(m1
));
487 dump_distance(struct mapfs
*mfs
)
489 switch (mfs
->mfs_distance
) {
490 case 0: return ("zero");
491 case DIST_SELF
: return ("self");
492 case DIST_MYSUB
: return ("mysub");
493 case DIST_MYNET
: return ("mynet");
494 case DIST_OTHER
: return ("other");
495 default: return ("other");
500 * Walk linked list "raw", building a new list consisting of members
501 * NOT found in list "filter", returning the result.
503 static struct mapfs
*
504 filter_mfs(struct mapfs
*raw
, struct mapfs
*filter
)
506 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
511 for (mfs
= raw
; mfs
; mfs
= mfs
->mfs_next
) {
512 for (skip
= 0, p
= filter
; p
; p
= p
->mfs_next
) {
513 if (strcmp(p
->mfs_host
, mfs
->mfs_host
) == 0 &&
514 strcmp(p
->mfs_dir
, mfs
->mfs_dir
) == 0) {
521 p
= add_mfs(mfs
, 0, &mfs_head
, &mfs_tail
);
529 * Walk a linked list of mapfs structs, freeing each member.
532 free_mfs(struct mapfs
*mfs
)
544 * New code for NFS client failover: we need to carry and sort
545 * lists of server possibilities rather than return a single
546 * entry. It preserves previous behaviour of sorting first by
547 * locality (loopback-or-preferred/subnet/net/other) and then
548 * by ping times. We'll short-circuit this process when we
549 * have ENOUGH or more entries.
551 static struct mapfs
*
552 enum_servers(struct mapent
*me
, char *preferred
)
554 struct mapfs
*p
, *m1
, *m2
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
557 * Short-circuit for simple cases.
559 if (!me
->map_fs
->mfs_next
) {
560 p
= add_mfs(me
->map_fs
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
566 dump_mfs(me
->map_fs
, " enum_servers: mapent: ", 2);
569 * get addresses & see if any are myself
570 * or were mounted from previously in a
571 * hierarchical mount.
574 trace_prt(1, " enum_servers: looking for pref/self\n");
575 for (m1
= me
->map_fs
; m1
; m1
= m1
->mfs_next
) {
578 if (self_check(m1
->mfs_host
) ||
579 strcmp(m1
->mfs_host
, preferred
) == 0) {
580 p
= add_mfs(m1
, DIST_SELF
, &mfs_head
, &mfs_tail
);
586 trace_prt(1, " enum_servers: pref/self found, %s\n",
590 * look for entries on this subnet
592 dump_mfs(m1
, " enum_servers: input of get_mysubnet_servers: ", 2);
593 m1
= get_mysubnet_servers(me
->map_fs
);
594 dump_mfs(m1
, " enum_servers: output of get_mysubnet_servers: ", 3);
595 if (m1
&& m1
->mfs_next
) {
596 m2
= sort_servers(m1
, rpc_timeout
/ 2);
597 dump_mfs(m2
, " enum_servers: output of sort_servers: ", 3);
602 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
603 p
= add_mfs(m2
, 0, &mfs_head
, &mfs_tail
);
611 * add the rest of the entries at the end
613 m1
= filter_mfs(me
->map_fs
, mfs_head
);
614 dump_mfs(m1
, " enum_servers: etc: output of filter_mfs: ", 3);
615 m2
= sort_servers(m1
, rpc_timeout
/ 2);
616 dump_mfs(m2
, " enum_servers: etc: output of sort_servers: ", 3);
620 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
621 p
= add_mfs(m2
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
629 dump_mfs(mfs_head
, " enum_servers: output: ", 1);
635 struct mapfs
*mfs_in
,
636 char *mntpnt
, char *opts
,
642 char remname
[MAXPATHLEN
], *mnttabtext
= NULL
;
643 char mopts
[MAX_MNTOPT_STR
];
644 char netname
[MAXNETNAMELEN
+1];
645 char *mntopts
= NULL
;
649 struct nfs_args
*argp
= NULL
, *head
= NULL
, *tail
= NULL
,
650 *prevhead
, *prevtail
;
653 struct timeval timeout
;
654 enum clnt_stat rpc_stat
;
657 struct netconfig
*nconf
;
658 rpcvers_t vers
, versmin
; /* used to negotiate nfs version in pingnfs */
659 /* and mount version with mountd */
660 rpcvers_t outvers
; /* final version to be used during mount() */
661 rpcvers_t nfsvers
; /* version in map options, 0 if not there */
662 rpcvers_t mountversmax
; /* tracks the max mountvers during retries */
664 /* used to negotiate nfs version using webnfs */
665 rpcvers_t pubvers
, pubversmin
, pubversmax
;
667 struct nd_addrlist
*retaddrs
;
668 struct mountres3 res3
;
672 char scerror_msg
[MAXMSGLEN
];
676 char *nfs_proto
= NULL
;
678 char *p
, *host
, *rhost
, *dir
;
679 struct mapfs
*mfs
= NULL
;
680 int error
, last_error
= 0;
683 int v2cnt
= 0, v3cnt
= 0, v4cnt
= 0;
684 int v2near
= 0, v3near
= 0, v4near
= 0;
688 int sec_opt
, scerror
;
689 struct sec_data
*secdata
;
691 struct netbuf
*syncaddr
;
695 mfs_snego_t mfssnego_init
, mfssnego
;
697 dump_mfs(mfs_in
, " nfsmount: input: ", 2);
698 replicated
= (mfs_in
->mfs_next
!= NULL
);
699 m
.mnt_mntopts
= opts
;
700 if (replicated
&& hasmntopt(&m
, MNTOPT_SOFT
)) {
703 "mount on %s is soft and will not be replicated.", mntpnt
);
706 if (replicated
&& !hasmntopt(&m
, MNTOPT_RO
)) {
709 "mount on %s is not read-only and will not be replicated.",
714 loglevel
= LOG_WARNING
;
720 trace_prt(1, " nfsmount: replicated mount on %s %s:\n",
723 trace_prt(1, " nfsmount: standard mount on %s %s:\n",
725 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
)
726 trace_prt(1, " %s:%s\n",
727 mfs
->mfs_host
, mfs
->mfs_dir
);
731 * Make sure mountpoint is safe to mount on
733 if (lstat(mntpnt
, &stbuf
) < 0) {
734 syslog(LOG_ERR
, "Couldn't stat %s: %m", mntpnt
);
735 return (NFSERR_NOENT
);
739 * Get protocol specified in options list, if any.
741 if ((str_opt(&m
, "proto", &nfs_proto
)) == -1) {
742 return (NFSERR_NOENT
);
746 * Get port specified in options list, if any.
748 got_val
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
750 nfs_port
= 0; /* "unspecified" */
751 if (nfs_port
> USHRT_MAX
) {
752 syslog(LOG_ERR
, "%s: invalid port number %d", mntpnt
, nfs_port
);
753 return (NFSERR_NOENT
);
757 * Set mount(2) flags here, outside of the loop.
759 flags
= MS_OPTIONSTR
;
760 flags
|= (hasmntopt(&m
, MNTOPT_RO
) == NULL
) ? 0 : MS_RDONLY
;
761 flags
|= (hasmntopt(&m
, MNTOPT_NOSUID
) == NULL
) ? 0 : MS_NOSUID
;
762 flags
|= overlay
? MS_OVERLAY
: 0;
763 if (mntpnt
[strlen(mntpnt
) - 1] != ' ')
764 /* direct mount point without offsets */
767 use_pubfh
= (hasmntopt(&m
, MNTOPT_PUBLIC
) == NULL
) ? FALSE
: TRUE
;
769 (void) memset(&mfssnego_init
, 0, sizeof (mfs_snego_t
));
770 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
771 if (++mfssnego_init
.sec_opt
> 1) {
773 "conflicting security options");
776 if (nfs_getseconfig_byname("dh", &mfssnego_init
.nfs_sec
)) {
778 "error getting dh information from %s",
784 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
785 if ((str_opt(&m
, MNTOPT_SEC
,
786 &mfssnego_init
.nfs_flavor
)) == -1) {
787 syslog(LOG_ERR
, "nfsmount: no memory");
792 if (mfssnego_init
.nfs_flavor
) {
793 if (++mfssnego_init
.sec_opt
> 1) {
795 "conflicting security options");
796 free(mfssnego_init
.nfs_flavor
);
799 if (nfs_getseconfig_byname(mfssnego_init
.nfs_flavor
,
800 &mfssnego_init
.nfs_sec
)) {
802 "error getting %s information from %s",
803 mfssnego_init
.nfs_flavor
, NFSSEC_CONF
);
804 free(mfssnego_init
.nfs_flavor
);
807 free(mfssnego_init
.nfs_flavor
);
813 got_val
= nopt(&m
, MNTOPT_VERS
, (int *)&nfsvers
);
815 nfsvers
= 0; /* "unspecified" */
816 if (set_versrange(nfsvers
, &vers
, &versmin
) != 0) {
817 syslog(LOG_ERR
, "Incorrect NFS version specified for %s",
819 last_error
= NFSERR_NOENT
;
824 pubversmax
= pubversmin
= nfsvers
;
827 pubversmin
= versmin
;
831 * Walk the whole list, pinging and collecting version
832 * info so that we can make sure the mount will be
833 * homogeneous with respect to version.
835 * If we have a version preference, this is easy; we'll
836 * just reject anything that doesn't match.
838 * If not, we want to try to provide the best compromise
839 * that considers proximity, preference for a higher version,
840 * sorted order, and number of replicas. We will count
841 * the number of V2 and V3 replicas and also the number
842 * which are "near", i.e. the localhost or on the same
845 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
852 * If the host is '[a:d:d:r:e:s:s'],
853 * only use 'a:d:d:r:e:s:s' for communication
855 host
= strdup(mfs
->mfs_host
);
857 syslog(LOG_ERR
, "nfsmount: no memory");
858 last_error
= NFSERR_IO
;
863 (void) memcpy(&mfssnego
, &mfssnego_init
, sizeof (mfs_snego_t
));
865 if (use_pubfh
== TRUE
|| mfs
->mfs_flags
& MFS_URL
) {
868 if (nfs_port
!= 0 && mfs
->mfs_port
!= 0 &&
869 nfs_port
!= mfs
->mfs_port
) {
871 syslog(LOG_ERR
, "nfsmount: port (%u) in nfs URL"
872 " not the same as port (%d) in port "
873 "option\n", mfs
->mfs_port
, nfs_port
);
874 last_error
= NFSERR_IO
;
877 } else if (nfs_port
!= 0)
880 thisport
= mfs
->mfs_port
;
884 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
885 path
= malloc(strlen(dir
) + 2);
887 syslog(LOG_ERR
, "nfsmount: no memory");
888 last_error
= NFSERR_IO
;
891 path
[0] = (char)WNL_NATIVEPATH
;
892 (void) strcpy(&path
[1], dir
);
897 argp
= (struct nfs_args
*)
898 malloc(sizeof (struct nfs_args
));
903 syslog(LOG_ERR
, "nfsmount: no memory");
904 last_error
= NFSERR_IO
;
907 (void) memset(argp
, 0, sizeof (*argp
));
911 * By now Mount argument struct has been allocated,
912 * either a pub_fh path will be taken or the regular
913 * one. So here if a protocol was specified and it
914 * was not rdma we let it be, else we set DO_RDMA.
915 * If no proto was there we advise on trying RDMA.
918 if (strcmp(nfs_proto
, "rdma") == 0) {
921 argp
->flags
|= NFSMNT_DORDMA
;
924 argp
->flags
|= NFSMNT_TRYRDMA
;
926 for (pubvers
= pubversmax
; pubvers
>= pubversmin
;
930 argp
->addr
= get_pubfh(host
, pubvers
, &mfssnego
,
931 &nconf
, nfs_proto
, thisport
, NULL
,
932 &argp
->fh
, TRUE
, path
);
934 if (argp
->addr
!= NULL
)
938 freenetconfigent(nconf
);
944 if (argp
->addr
!= NULL
) {
947 * The use of llock option for NFSv4
948 * mounts is not required since file
949 * locking is included within the protocol
951 if (pubvers
!= NFS_V4
)
952 argp
->flags
|= NFSMNT_LLOCK
;
954 argp
->flags
|= NFSMNT_PUBLIC
;
957 mfs
->mfs_args
= argp
;
958 mfs
->mfs_version
= pubvers
;
959 mfs
->mfs_nconf
= nconf
;
960 mfs
->mfs_flags
|= MFS_FH_VIA_WEBNFS
;
966 * If -public was specified, give up
969 if (use_pubfh
== TRUE
) {
971 "%s: no public file handle support",
973 last_error
= NFSERR_NOENT
;
979 * Back off to a conventional mount.
981 * URL's can contain escape characters. Get
984 path
= malloc(strlen(dir
) + 2);
987 syslog(LOG_ERR
, "nfsmount: no memory");
988 last_error
= NFSERR_IO
;
995 mfs
->mfs_flags
|= MFS_ALLOC_DIR
;
996 mfs
->mfs_flags
&= ~MFS_URL
;
1000 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
1001 i
= pingnfs(host
, get_retry(opts
) + 1, &vers
, versmin
,
1002 0, FALSE
, NULL
, nfs_proto
);
1003 if (i
!= RPC_SUCCESS
) {
1004 if (i
== RPC_PROGVERSMISMATCH
) {
1005 syslog(loglevel
, "server %s: NFS "
1006 "protocol version mismatch",
1009 syslog(loglevel
, "server %s not "
1010 "responding", host
);
1012 mfs
->mfs_ignore
= 1;
1013 last_error
= NFSERR_NOENT
;
1016 if (nfsvers
!= 0 && nfsvers
!= vers
) {
1017 if (nfs_proto
== NULL
)
1020 "not supported by %s",
1026 "not supported by %s",
1027 nfsvers
, nfs_proto
, host
);
1028 mfs
->mfs_ignore
= 1;
1029 last_error
= NFSERR_NOENT
;
1037 case NFS_V4
: v4cnt
++; break;
1038 case NFS_V3
: v3cnt
++; break;
1039 case NFS_VERSION
: v2cnt
++; break;
1044 * It's not clear how useful this stuff is if
1045 * we are using webnfs across the internet, but it
1048 if (mfs
->mfs_distance
&&
1049 mfs
->mfs_distance
<= DIST_MYSUB
) {
1051 case NFS_V4
: v4near
++; break;
1052 case NFS_V3
: v3near
++; break;
1053 case NFS_VERSION
: v2near
++; break;
1059 * If the mount is not replicated, we don't want to
1060 * ping every entry, so we'll stop here. This means
1061 * that we may have to go back to "nextentry" above
1062 * to consider another entry if we can't get
1063 * all the way to mount(2) with this one.
1072 * Choose the NFS version.
1073 * We prefer higher versions, but will choose a one-
1074 * version downgrade in service if we can use a local
1075 * network interface and avoid a router.
1077 if (v4cnt
&& v4cnt
>= v3cnt
&& (v4near
|| !v3near
))
1079 else if (v3cnt
&& v3cnt
>= v2cnt
&& (v3near
|| !v2near
))
1082 nfsvers
= NFS_VERSION
;
1085 " nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1086 v4cnt
, v4near
, v3cnt
, v3near
,
1087 v2cnt
, v2near
, nfsvers
);
1091 * Since we don't support different NFS versions in replicated
1092 * mounts, set fstype now.
1093 * Also take the opportunity to set
1094 * the mount protocol version as appropriate.
1098 fstype
= MNTTYPE_NFS4
;
1101 fstype
= MNTTYPE_NFS3
;
1102 if (use_pubfh
== FALSE
) {
1103 mountversmax
= MOUNTVERS3
;
1104 versmin
= MOUNTVERS3
;
1108 fstype
= MNTTYPE_NFS
;
1109 if (use_pubfh
== FALSE
) {
1110 mountversmax
= MOUNTVERS_POSIX
;
1111 versmin
= MOUNTVERS
;
1117 * Our goal here is to evaluate each of several possible
1118 * replicas and try to come up with a list we can hand
1119 * to mount(2). If we don't have a valid "head" at the
1120 * end of this process, it means we have rejected all
1121 * potential server:/path tuples. We will fail quietly
1122 * in front of mount(2), and will have printed errors
1123 * where we found them.
1124 * XXX - do option work outside loop w careful design
1125 * XXX - use macro for error condition free handling
1127 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
1130 * Initialize retry and delay values on a per-server basis.
1132 retries
= get_retry(opts
);
1135 if (mfs
->mfs_ignore
)
1139 * If we don't have a fh yet, and if this is not a replicated
1140 * mount, we haven't done a pingnfs() on the next entry,
1141 * so we don't know if the next entry is up or if it
1142 * supports an NFS version we like. So if we had a problem
1143 * with an entry, we need to go back and run through some new
1146 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1147 !replicated
&& skipentry
)
1150 vers
= mountversmax
;
1151 host
= mfs
->mfs_host
;
1155 * Remember the possible '[a:d:d:r:e:s:s]' as the address to be
1156 * later passed to mount(2) and used in the mnttab line, but
1157 * only use 'a:d:d:r:e:s:s' for communication
1159 rhost
= strdup(host
);
1160 if (rhost
== NULL
) {
1161 syslog(LOG_ERR
, "nfsmount: no memory");
1162 last_error
= NFSERR_IO
;
1167 (void) sprintf(remname
, "%s:%s", rhost
, dir
);
1168 if (trace
> 4 && replicated
)
1169 trace_prt(1, " nfsmount: examining %s\n", remname
);
1171 if (mfs
->mfs_args
== NULL
) {
1174 * Allocate nfs_args structure
1176 argp
= (struct nfs_args
*)
1177 malloc(sizeof (struct nfs_args
));
1180 syslog(LOG_ERR
, "nfsmount: no memory");
1181 last_error
= NFSERR_IO
;
1185 (void) memset(argp
, 0, sizeof (*argp
));
1189 * By now Mount argument struct has been allocated,
1190 * either a pub_fh path will be taken or the regular
1191 * one. So here if a protocol was specified and it
1192 * was not rdma we let it be, else we set DO_RDMA.
1193 * If no proto was there we advise on trying RDMA.
1196 if (strcmp(nfs_proto
, "rdma") == 0) {
1199 argp
->flags
|= NFSMNT_DORDMA
;
1202 argp
->flags
|= NFSMNT_TRYRDMA
;
1204 argp
= mfs
->mfs_args
;
1205 mfs
->mfs_args
= NULL
;
1208 * Skip entry if we already have file handle but the
1209 * NFS version is wrong.
1211 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) &&
1212 mfs
->mfs_version
!= nfsvers
) {
1216 mfs
->mfs_ignore
= 1;
1226 tail
= tail
->nfs_ext_u
.nfs_extB
.next
= argp
;
1229 * WebNFS and NFSv4 behave similarly in that they
1230 * don't use the mount protocol. Therefore, avoid
1231 * mount protocol like things when version 4 is being
1234 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1235 nfsvers
!= NFS_V4
) {
1236 timeout
.tv_usec
= 0;
1237 timeout
.tv_sec
= rpc_timeout
;
1238 rpc_stat
= RPC_TIMEDOUT
;
1240 /* Create the client handle. */
1244 " nfsmount: Get mount version: request "
1245 "vers=%d min=%d\n", vers
, versmin
);
1248 while ((cl
= clnt_create_vers(host
, MOUNTPROG
, &outvers
,
1249 versmin
, vers
, "udp")) == NULL
) {
1252 " nfsmount: Can't get mount "
1253 "version: rpcerr=%d\n",
1254 rpc_createerr
.cf_stat
);
1256 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
1257 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
1261 * backoff and return lower version to retry the ping.
1262 * XXX we should be more careful and handle
1263 * RPC_PROGVERSMISMATCH here, because that error
1264 * is handled in clnt_create_vers(). It's not done to
1265 * stay in sync with the nfs mount command.
1272 " nfsmount: Try version=%d\n",
1282 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1283 last_error
= NFSERR_NOENT
;
1285 if (rpc_createerr
.cf_stat
!= RPC_UNKNOWNHOST
&&
1286 rpc_createerr
.cf_stat
!=
1287 RPC_PROGVERSMISMATCH
&&
1293 syslog(loglevel
, "%s %s", host
,
1295 "server not responding"));
1297 mfs
->mfs_ignore
= 1;
1302 " nfsmount: mount version=%d\n", outvers
);
1305 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
1306 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1307 __FILE__
, __LINE__
);
1310 if (__clnt_bindresvport(cl
) < 0) {
1315 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1316 last_error
= NFSERR_NOENT
;
1318 if (retries
-- > 0) {
1319 destroy_auth_client_handle(cl
);
1324 syslog(loglevel
, "mount %s: %s", host
,
1325 "Couldn't bind to reserved port");
1326 destroy_auth_client_handle(cl
);
1328 mfs
->mfs_ignore
= 1;
1333 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
1334 __FILE__
, __LINE__
);
1336 AUTH_DESTROY(cl
->cl_auth
);
1337 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
1342 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1343 last_error
= NFSERR_NOENT
;
1345 if (retries
-- > 0) {
1346 destroy_auth_client_handle(cl
);
1351 syslog(loglevel
, "mount %s: %s", host
,
1352 "Failed creating default auth handle");
1353 destroy_auth_client_handle(cl
);
1355 mfs
->mfs_ignore
= 1;
1359 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1360 __FILE__
, __LINE__
);
1366 * set security options
1369 (void) memset(&nfs_sec
, 0, sizeof (nfs_sec
));
1370 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
1371 if (++sec_opt
> 1) {
1373 "conflicting security options for %s",
1379 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1380 last_error
= NFSERR_IO
;
1381 destroy_auth_client_handle(cl
);
1383 mfs
->mfs_ignore
= 1;
1386 if (nfs_getseconfig_byname("dh", &nfs_sec
)) {
1388 "error getting dh information from %s",
1394 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1395 last_error
= NFSERR_IO
;
1396 destroy_auth_client_handle(cl
);
1398 mfs
->mfs_ignore
= 1;
1404 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
1405 if ((str_opt(&m
, MNTOPT_SEC
, &nfs_flavor
)) == -1) {
1406 syslog(LOG_ERR
, "nfsmount: no memory");
1407 last_error
= NFSERR_IO
;
1408 destroy_auth_client_handle(cl
);
1414 if (++sec_opt
> 1) {
1416 "conflicting security options for %s",
1423 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1424 last_error
= NFSERR_IO
;
1425 destroy_auth_client_handle(cl
);
1427 mfs
->mfs_ignore
= 1;
1430 if (nfs_getseconfig_byname(nfs_flavor
, &nfs_sec
)) {
1432 "error getting %s information from %s",
1433 nfs_flavor
, NFSSEC_CONF
);
1439 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1440 last_error
= NFSERR_IO
;
1441 destroy_auth_client_handle(cl
);
1443 mfs
->mfs_ignore
= 1;
1449 posix
= (nfsvers
!= NFS_V4
&&
1450 hasmntopt(&m
, MNTOPT_POSIX
) != NULL
) ? 1 : 0;
1452 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1453 nfsvers
!= NFS_V4
) {
1454 bool_t give_up_on_mnt
;
1455 bool_t got_mnt_error
;
1457 * If we started with a URL, if first byte of path is not "/",
1458 * then the mount will likely fail, so we should try again
1459 * with a prepended "/".
1461 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
&& *dir
!= '/')
1462 give_up_on_mnt
= FALSE
;
1464 give_up_on_mnt
= TRUE
;
1466 got_mnt_error
= FALSE
;
1469 if (got_mnt_error
== TRUE
) {
1472 give_up_on_mnt
= TRUE
;
1476 * Insert a "/" to front of mfs_dir.
1478 for (i
= l
; i
> 0; i
--)
1484 /* Get fhandle of remote path from server's mountd */
1493 tail
->nfs_ext_u
.nfs_extB
.next
=
1495 last_error
= NFSERR_NOENT
;
1497 "can't get posix info for %s",
1499 destroy_auth_client_handle(cl
);
1501 mfs
->mfs_ignore
= 1;
1505 case MOUNTVERS_POSIX
:
1506 if (nfsvers
== NFS_V3
) {
1511 tail
->nfs_ext_u
.nfs_extB
.next
=
1513 last_error
= NFSERR_NOENT
;
1515 "%s doesn't support NFS Version 3",
1517 destroy_auth_client_handle(cl
);
1519 mfs
->mfs_ignore
= 1;
1522 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1523 xdr_dirpath
, (caddr_t
)&dir
,
1524 xdr_fhstatus
, (caddr_t
)&fhs
, timeout
);
1525 if (rpc_stat
!= RPC_SUCCESS
) {
1527 if (give_up_on_mnt
== FALSE
) {
1528 got_mnt_error
= TRUE
;
1533 * Given the way "clnt_sperror" works, the "%s"
1534 * immediately following the "not responding"
1541 tail
->nfs_ext_u
.nfs_extB
.next
=
1543 last_error
= NFSERR_NOENT
;
1545 if (retries
-- > 0) {
1546 destroy_auth_client_handle(cl
);
1553 " nfsmount: mount RPC "
1558 "%s server not responding%s",
1559 host
, clnt_sperror(cl
, ""));
1560 destroy_auth_client_handle(cl
);
1562 mfs
->mfs_ignore
= 1;
1565 if ((errno
= fhs
.fhs_status
) != MNT_OK
) {
1567 if (give_up_on_mnt
== FALSE
) {
1568 got_mnt_error
= TRUE
;
1576 tail
->nfs_ext_u
.nfs_extB
.next
=
1578 if (errno
== EACCES
) {
1579 status
= NFSERR_ACCES
;
1581 syslog(loglevel
, "%s: %m",
1587 " nfsmount: mount RPC gave"
1591 last_error
= status
;
1592 destroy_auth_client_handle(cl
);
1594 mfs
->mfs_ignore
= 1;
1597 argp
->fh
= malloc((sizeof (fhandle
)));
1599 syslog(LOG_ERR
, "nfsmount: no memory");
1600 last_error
= NFSERR_IO
;
1601 destroy_auth_client_handle(cl
);
1604 (void) memcpy(argp
->fh
,
1605 &fhs
.fhstatus_u
.fhs_fhandle
,
1610 (void) memset((char *)&res3
, '\0',
1612 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1613 xdr_dirpath
, (caddr_t
)&dir
,
1614 xdr_mountres3
, (caddr_t
)&res3
, timeout
);
1615 if (rpc_stat
!= RPC_SUCCESS
) {
1617 if (give_up_on_mnt
== FALSE
) {
1618 got_mnt_error
= TRUE
;
1623 * Given the way "clnt_sperror" works, the "%s"
1624 * immediately following the "not responding"
1631 tail
->nfs_ext_u
.nfs_extB
.next
=
1633 last_error
= NFSERR_NOENT
;
1635 if (retries
-- > 0) {
1636 destroy_auth_client_handle(cl
);
1643 " nfsmount: mount RPC "
1648 "%s server not responding%s",
1649 remname
, clnt_sperror(cl
, ""));
1650 destroy_auth_client_handle(cl
);
1652 mfs
->mfs_ignore
= 1;
1655 if ((errno
= res3
.fhs_status
) != MNT_OK
) {
1657 if (give_up_on_mnt
== FALSE
) {
1658 got_mnt_error
= TRUE
;
1666 tail
->nfs_ext_u
.nfs_extB
.next
=
1668 if (errno
== EACCES
) {
1669 status
= NFSERR_ACCES
;
1671 syslog(loglevel
, "%s: %m",
1677 " nfsmount: mount RPC gave"
1681 last_error
= status
;
1682 destroy_auth_client_handle(cl
);
1684 mfs
->mfs_ignore
= 1;
1689 * Negotiate the security flavor for nfs_mount
1691 auths
= res3
.mountres3_u
.mountinfo
.
1692 auth_flavors
.auth_flavors_val
;
1693 count
= res3
.mountres3_u
.mountinfo
.
1694 auth_flavors
.auth_flavors_len
;
1697 for (i
= 0; i
< count
; i
++)
1699 nfs_sec
.sc_nfsnum
) {
1704 "%s: does not support "
1705 "security \"%s\"\n",
1706 remname
, nfs_sec
.sc_name
);
1707 clnt_freeres(cl
, xdr_mountres3
,
1716 last_error
= NFSERR_IO
;
1717 destroy_auth_client_handle(cl
);
1719 mfs
->mfs_ignore
= 1;
1722 } else if (count
> 0) {
1723 for (i
= 0; i
< count
; i
++) {
1725 nfs_getseconfig_bynumber(
1726 auths
[i
], &nfs_sec
))) {
1732 if (nfs_syslog_scerr(scerror
,
1737 "mounted because it"
1739 "security flavor %d"
1745 clnt_freeres(cl
, xdr_mountres3
,
1754 last_error
= NFSERR_IO
;
1755 destroy_auth_client_handle(cl
);
1757 mfs
->mfs_ignore
= 1;
1763 res3
.mountres3_u
.mountinfo
.fhandle
.
1765 (void) memcpy(fh3
.fh3_u
.data
,
1766 res3
.mountres3_u
.mountinfo
.fhandle
.
1769 clnt_freeres(cl
, xdr_mountres3
,
1771 argp
->fh
= malloc(sizeof (nfs_fh3
));
1773 syslog(LOG_ERR
, "nfsmount: no memory");
1774 last_error
= NFSERR_IO
;
1775 destroy_auth_client_handle(cl
);
1778 (void) memcpy(argp
->fh
, &fh3
, sizeof (nfs_fh3
));
1785 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1786 last_error
= NFSERR_NOENT
;
1788 "unknown MOUNT version %ld on %s",
1790 destroy_auth_client_handle(cl
);
1792 mfs
->mfs_ignore
= 1;
1796 if (nfsvers
== NFS_V4
) {
1797 argp
->fh
= strdup(dir
);
1798 if (argp
->fh
== NULL
) {
1799 syslog(LOG_ERR
, "nfsmount: no memory");
1800 last_error
= NFSERR_IO
;
1806 trace_prt(1, " nfsmount: have %s filehandle for %s\n",
1809 argp
->flags
|= NFSMNT_NEWARGS
;
1810 argp
->flags
|= NFSMNT_INT
; /* default is "intr" */
1811 argp
->flags
|= NFSMNT_HOSTNAME
;
1812 argp
->hostname
= strdup(host
);
1813 if (argp
->hostname
== NULL
) {
1814 syslog(LOG_ERR
, "nfsmount: no memory");
1815 last_error
= NFSERR_IO
;
1820 * In this case, we want NFSv4 to behave like
1821 * non-WebNFS so that we get the server address.
1823 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
1827 thisport
= nfs_port
;
1829 thisport
= mfs
->mfs_port
;
1832 * For NFSv4, we want to avoid rpcbind, so call
1833 * get_server_netinfo() directly to tell it that
1834 * we want to go "direct_to_server". Otherwise,
1835 * do what has always been done.
1837 if (nfsvers
== NFS_V4
) {
1838 enum clnt_stat cstat
;
1840 argp
->addr
= get_server_netinfo(SERVER_ADDR
,
1841 host
, NFS_PROGRAM
, nfsvers
, NULL
,
1842 &nconf
, nfs_proto
, thisport
, NULL
,
1843 NULL
, TRUE
, NULL
, &cstat
);
1845 argp
->addr
= get_addr(host
, NFS_PROGRAM
,
1846 nfsvers
, &nconf
, nfs_proto
,
1850 if (argp
->addr
== NULL
) {
1852 free(argp
->hostname
);
1858 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1859 last_error
= NFSERR_NOENT
;
1861 if (retries
-- > 0) {
1862 destroy_auth_client_handle(cl
);
1867 syslog(loglevel
, "%s: no NFS service", host
);
1868 destroy_auth_client_handle(cl
);
1870 mfs
->mfs_ignore
= 1;
1875 "\tnfsmount: have net address for %s\n",
1879 nconf
= mfs
->mfs_nconf
;
1880 mfs
->mfs_nconf
= NULL
;
1883 argp
->flags
|= NFSMNT_KNCONF
;
1884 argp
->knconf
= get_knconf(nconf
);
1885 if (argp
->knconf
== NULL
) {
1886 netbuf_free(argp
->addr
);
1887 freenetconfigent(nconf
);
1889 free(argp
->hostname
);
1895 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1896 last_error
= NFSERR_NOSPC
;
1897 destroy_auth_client_handle(cl
);
1899 mfs
->mfs_ignore
= 1;
1904 "\tnfsmount: have net config for %s\n",
1907 if (hasmntopt(&m
, MNTOPT_SOFT
) != NULL
) {
1908 argp
->flags
|= NFSMNT_SOFT
;
1910 if (hasmntopt(&m
, MNTOPT_NOINTR
) != NULL
) {
1911 argp
->flags
&= ~(NFSMNT_INT
);
1913 if (hasmntopt(&m
, MNTOPT_NOAC
) != NULL
) {
1914 argp
->flags
|= NFSMNT_NOAC
;
1916 if (hasmntopt(&m
, MNTOPT_NOCTO
) != NULL
) {
1917 argp
->flags
|= NFSMNT_NOCTO
;
1919 if (hasmntopt(&m
, MNTOPT_FORCEDIRECTIO
) != NULL
) {
1920 argp
->flags
|= NFSMNT_DIRECTIO
;
1922 if (hasmntopt(&m
, MNTOPT_NOFORCEDIRECTIO
) != NULL
) {
1923 argp
->flags
&= ~(NFSMNT_DIRECTIO
);
1927 * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1929 if (mfssnego
.snego_done
) {
1930 memcpy(&nfs_sec
, &mfssnego
.nfs_sec
,
1931 sizeof (seconfig_t
));
1932 } else if (!sec_opt
) {
1934 * Get default security mode.
1936 if (nfs_getseconfig_default(&nfs_sec
)) {
1938 "error getting default security entry\n");
1939 free_knconf(argp
->knconf
);
1940 netbuf_free(argp
->addr
);
1941 freenetconfigent(nconf
);
1943 free(argp
->hostname
);
1949 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1950 last_error
= NFSERR_NOSPC
;
1951 destroy_auth_client_handle(cl
);
1953 mfs
->mfs_ignore
= 1;
1956 argp
->flags
|= NFSMNT_SECDEFAULT
;
1961 * get the network address for the time service on
1962 * the server. If an RPC based time service is
1963 * not available then try the IP time service.
1965 * Eventurally, we want to move this code to nfs_clnt_secdata()
1966 * when autod_nfs.c and mount.c can share the same
1967 * get_the_addr/get_netconfig_info routine.
1973 if (nfs_sec
.sc_rpcnum
== AUTH_DH
|| nfsvers
== NFS_V4
) {
1975 * If not using the public fh and not NFS_V4, we can try
1976 * talking RPCBIND. Otherwise, assume that firewalls
1977 * prevent us from doing that.
1979 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1980 nfsvers
!= NFS_V4
) {
1981 enum clnt_stat cstat
;
1982 syncaddr
= get_server_netinfo(SERVER_ADDR
,
1983 host
, RPCBPROG
, RPCBVERS
, NULL
, &nconf
,
1984 NULL
, 0, NULL
, NULL
, FALSE
, NULL
, &cstat
);
1987 if (syncaddr
!= NULL
) {
1988 /* for flags in sec_data */
1989 secflags
|= AUTH_F_RPCTIMESYNC
;
1991 struct nd_hostserv hs
;
1995 hs
.h_serv
= "timserver";
1996 error
= netdir_getbyname(nconf
, &hs
, &retaddrs
);
1998 if (error
!= ND_OK
&&
1999 nfs_sec
.sc_rpcnum
== AUTH_DH
) {
2001 "%s: secure: no time service\n",
2003 free_knconf(argp
->knconf
);
2004 netbuf_free(argp
->addr
);
2005 freenetconfigent(nconf
);
2007 free(argp
->hostname
);
2013 tail
->nfs_ext_u
.nfs_extB
.next
=
2015 last_error
= NFSERR_IO
;
2016 destroy_auth_client_handle(cl
);
2018 mfs
->mfs_ignore
= 1;
2023 syncaddr
= retaddrs
->n_addrs
;
2026 * For potential usage by NFS V4 when AUTH_DH
2027 * is negotiated via SECINFO in the kernel.
2029 if (nfsvers
== NFS_V4
&& syncaddr
&&
2030 host2netname(netname
, host
, NULL
)) {
2032 malloc(sizeof (struct netbuf
));
2033 argp
->syncaddr
->buf
=
2034 malloc(syncaddr
->len
);
2035 (void) memcpy(argp
->syncaddr
->buf
,
2036 syncaddr
->buf
, syncaddr
->len
);
2037 argp
->syncaddr
->len
= syncaddr
->len
;
2038 argp
->syncaddr
->maxlen
=
2040 argp
->netname
= strdup(netname
);
2041 argp
->flags
|= NFSMNT_SECURE
;
2046 nfs_sec
.sc_uid
= uid
;
2048 * If AUTH_DH is a chosen flavor now, its data will be stored
2049 * in the sec_data structure via nfs_clnt_secdata().
2051 if (!(secdata
= nfs_clnt_secdata(&nfs_sec
, host
, argp
->knconf
,
2052 syncaddr
, secflags
))) {
2054 "errors constructing security related data\n");
2055 if (secflags
& AUTH_F_RPCTIMESYNC
)
2056 netbuf_free(syncaddr
);
2058 netdir_free(retaddrs
, ND_ADDRLIST
);
2060 netbuf_free(argp
->syncaddr
);
2062 free(argp
->netname
);
2064 free(argp
->hostname
);
2065 free_knconf(argp
->knconf
);
2066 netbuf_free(argp
->addr
);
2067 freenetconfigent(nconf
);
2073 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2074 last_error
= NFSERR_IO
;
2075 destroy_auth_client_handle(cl
);
2077 mfs
->mfs_ignore
= 1;
2080 NFS_ARGS_EXTB_secdata(*argp
, secdata
);
2081 /* end of security stuff */
2085 " nfsmount: have secure info for %s\n", remname
);
2087 if (hasmntopt(&m
, MNTOPT_GRPID
) != NULL
) {
2088 argp
->flags
|= NFSMNT_GRPID
;
2090 if (nopt(&m
, MNTOPT_RSIZE
, &argp
->rsize
)) {
2091 argp
->flags
|= NFSMNT_RSIZE
;
2093 if (nopt(&m
, MNTOPT_WSIZE
, &argp
->wsize
)) {
2094 argp
->flags
|= NFSMNT_WSIZE
;
2096 if (nopt(&m
, MNTOPT_TIMEO
, &argp
->timeo
)) {
2097 argp
->flags
|= NFSMNT_TIMEO
;
2099 if (nopt(&m
, MNTOPT_RETRANS
, &argp
->retrans
)) {
2100 argp
->flags
|= NFSMNT_RETRANS
;
2102 if (nopt(&m
, MNTOPT_ACTIMEO
, &argp
->acregmax
)) {
2103 argp
->flags
|= NFSMNT_ACREGMAX
;
2104 argp
->flags
|= NFSMNT_ACDIRMAX
;
2105 argp
->flags
|= NFSMNT_ACDIRMIN
;
2106 argp
->flags
|= NFSMNT_ACREGMIN
;
2107 argp
->acdirmin
= argp
->acregmin
= argp
->acdirmax
2110 if (nopt(&m
, MNTOPT_ACREGMIN
, &argp
->acregmin
)) {
2111 argp
->flags
|= NFSMNT_ACREGMIN
;
2113 if (nopt(&m
, MNTOPT_ACREGMAX
, &argp
->acregmax
)) {
2114 argp
->flags
|= NFSMNT_ACREGMAX
;
2116 if (nopt(&m
, MNTOPT_ACDIRMIN
, &argp
->acdirmin
)) {
2117 argp
->flags
|= NFSMNT_ACDIRMIN
;
2119 if (nopt(&m
, MNTOPT_ACDIRMAX
, &argp
->acdirmax
)) {
2120 argp
->flags
|= NFSMNT_ACDIRMAX
;
2125 argp
->pathconf
= NULL
;
2126 if (error
= get_pathconf(cl
, dir
, remname
,
2127 &argp
->pathconf
, retries
)) {
2128 if (secflags
& AUTH_F_RPCTIMESYNC
)
2129 netbuf_free(syncaddr
);
2131 netdir_free(retaddrs
, ND_ADDRLIST
);
2132 free_knconf(argp
->knconf
);
2133 netbuf_free(argp
->addr
);
2134 freenetconfigent(nconf
);
2136 argp
->nfs_ext_u
.nfs_extB
.secdata
);
2138 netbuf_free(argp
->syncaddr
);
2140 free(argp
->netname
);
2142 free(argp
->hostname
);
2148 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2149 last_error
= NFSERR_IO
;
2151 if (error
== RET_RETRY
&& retries
-- > 0) {
2152 destroy_auth_client_handle(cl
);
2157 destroy_auth_client_handle(cl
);
2159 mfs
->mfs_ignore
= 1;
2162 argp
->flags
|= NFSMNT_POSIX
;
2165 " nfsmount: have pathconf for %s\n",
2170 * free loop-specific data structures
2172 destroy_auth_client_handle(cl
);
2173 freenetconfigent(nconf
);
2174 if (secflags
& AUTH_F_RPCTIMESYNC
)
2175 netbuf_free(syncaddr
);
2177 netdir_free(retaddrs
, ND_ADDRLIST
);
2180 * Decide whether to use remote host's lockd or local locking.
2181 * If we are using the public fh, we've already turned
2184 if (hasmntopt(&m
, MNTOPT_LLOCK
))
2185 argp
->flags
|= NFSMNT_LLOCK
;
2186 if (!(argp
->flags
& NFSMNT_LLOCK
) && nfsvers
== NFS_VERSION
&&
2187 remote_lock(host
, argp
->fh
)) {
2188 syslog(loglevel
, "No network locking on %s : "
2189 "contact admin to install server change", host
);
2190 argp
->flags
|= NFSMNT_LLOCK
;
2194 * Build a string for /etc/mnttab.
2195 * If possible, coalesce strings with same 'dir' info.
2197 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
2201 p
= strrchr(mnttabtext
, (int)':');
2202 if (!p
|| strcmp(p
+1, dir
) != 0) {
2203 mnttabcnt
+= strlen(remname
) + 2;
2206 mnttabcnt
+= strlen(rhost
) + 2;
2208 if ((tmp
= realloc(mnttabtext
,
2209 mnttabcnt
)) != NULL
) {
2211 strcat(mnttabtext
, ",");
2217 mnttabcnt
= strlen(remname
) + 1;
2218 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2219 mnttabtext
[0] = '\0';
2222 if (mnttabtext
!= NULL
)
2223 strcat(mnttabtext
, remname
);
2230 more_cnt
+= strlen("nfs://");
2231 more_cnt
+= strlen(mfs
->mfs_host
);
2233 if (mfs
->mfs_port
!= 0) {
2234 (void) sprintf(sport
, ":%u", mfs
->mfs_port
);
2238 more_cnt
+= strlen(sport
);
2239 more_cnt
+= 1; /* "/" */
2240 more_cnt
+= strlen(mfs
->mfs_dir
);
2243 more_cnt
+= 1; /* "," */
2244 mnttabcnt
+= more_cnt
;
2246 if ((tmp
= realloc(mnttabtext
,
2247 mnttabcnt
)) != NULL
) {
2249 strcat(mnttabtext
, ",");
2255 mnttabcnt
= more_cnt
+ 1;
2256 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2257 mnttabtext
[0] = '\0';
2260 if (mnttabtext
!= NULL
) {
2261 strcat(mnttabtext
, "nfs://");
2262 strcat(mnttabtext
, mfs
->mfs_host
);
2263 strcat(mnttabtext
, sport
);
2264 strcat(mnttabtext
, "/");
2265 strcat(mnttabtext
, mfs
->mfs_dir
);
2270 syslog(LOG_ERR
, "nfsmount: no memory");
2271 last_error
= NFSERR_IO
;
2276 * At least one entry, can call mount(2).
2281 * If replication was defeated, don't do more work
2289 * Did we get through all possibilities without success?
2294 /* Make "xattr" the default if "noxattr" is not specified. */
2295 strcpy(mopts
, opts
);
2296 if (!hasmntopt(&m
, MNTOPT_NOXATTR
) && !hasmntopt(&m
, MNTOPT_XATTR
)) {
2297 if (strlen(mopts
) > 0)
2299 strcat(mopts
, "xattr");
2303 * enable services as needed.
2308 if (strcmp(fstype
, MNTTYPE_NFS4
) == 0)
2309 sl
= service_list_v4
;
2313 (void) _check_services(sl
);
2317 * Whew; do the mount, at last.
2320 trace_prt(1, " mount %s %s (%s)\n", mnttabtext
, mntpnt
, mopts
);
2324 * About to do a nfs mount, make sure the mount_to is set for
2325 * potential ephemeral mounts with NFSv4.
2327 set_nfsv4_ephemeral_mount_to();
2330 * If no action list pointer then do the mount, otherwise
2331 * build the actions list pointer with the mount information.
2332 * so the mount can be done in the kernel.
2335 if (mount(mnttabtext
, mntpnt
, flags
| MS_DATA
, fstype
,
2336 head
, sizeof (*head
), mopts
, MAX_MNTOPT_STR
) < 0) {
2338 trace_prt(1, " Mount of %s on %s: %d\n",
2339 mnttabtext
, mntpnt
, errno
);
2340 if (errno
!= EBUSY
|| verbose
)
2342 "Mount of %s on %s: %m", mnttabtext
, mntpnt
);
2343 last_error
= NFSERR_IO
;
2347 last_error
= NFS_OK
;
2348 if (stat(mntpnt
, &stbuf
) == 0) {
2350 trace_prt(1, " mount %s dev=%x rdev=%x OK\n",
2351 mnttabtext
, stbuf
.st_dev
, stbuf
.st_rdev
);
2355 trace_prt(1, " mount %s OK\n", mnttabtext
);
2356 trace_prt(1, " stat of %s failed\n", mntpnt
);
2361 alp
->action
.action
= AUTOFS_MOUNT_RQ
;
2362 alp
->action
.action_list_entry_u
.mounta
.spec
=
2364 alp
->action
.action_list_entry_u
.mounta
.dir
= strdup(mntpnt
);
2365 alp
->action
.action_list_entry_u
.mounta
.flags
=
2367 alp
->action
.action_list_entry_u
.mounta
.fstype
=
2369 alp
->action
.action_list_entry_u
.mounta
.dataptr
= (char *)head
;
2370 alp
->action
.action_list_entry_u
.mounta
.datalen
=
2372 mntopts
= malloc(strlen(mopts
) + 1);
2373 strcpy(mntopts
, mopts
);
2374 mntopts
[strlen(mopts
)] = '\0';
2375 alp
->action
.action_list_entry_u
.mounta
.optptr
= mntopts
;
2376 alp
->action
.action_list_entry_u
.mounta
.optlen
=
2377 strlen(mntopts
) + 1;
2378 last_error
= NFS_OK
;
2386 free(argp
->pathconf
);
2387 free_knconf(argp
->knconf
);
2388 netbuf_free(argp
->addr
);
2390 netbuf_free(argp
->syncaddr
);
2391 if (argp
->netname
) {
2392 free(argp
->netname
);
2395 free(argp
->hostname
);
2396 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
2399 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
2408 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
2410 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
) {
2412 mfs
->mfs_dir
= NULL
;
2413 mfs
->mfs_flags
&= ~MFS_ALLOC_DIR
;
2416 if (mfs
->mfs_args
!= NULL
&& alp
== NULL
) {
2417 free(mfs
->mfs_args
);
2418 mfs
->mfs_args
= NULL
;
2421 if (mfs
->mfs_nconf
!= NULL
) {
2422 freenetconfigent(mfs
->mfs_nconf
);
2423 mfs
->mfs_nconf
= NULL
;
2427 return (last_error
);
2431 * get_pathconf(cl, path, fsname, pcnf, cretries)
2432 * ugliness that requires that ppathcnf and pathcnf stay consistent
2433 * cretries is a copy of retries used to determine when to syslog
2434 * on retry situations.
2437 get_pathconf(CLIENT
*cl
, char *path
, char *fsname
, struct pathcnf
**pcnf
,
2440 struct ppathcnf
*p
= NULL
;
2441 enum clnt_stat rpc_stat
;
2442 struct timeval timeout
;
2444 p
= (struct ppathcnf
*)malloc(sizeof (struct ppathcnf
));
2446 syslog(LOG_ERR
, "get_pathconf: Out of memory");
2449 memset((caddr_t
)p
, 0, sizeof (struct ppathcnf
));
2451 timeout
.tv_sec
= 10;
2452 timeout
.tv_usec
= 0;
2453 rpc_stat
= clnt_call(cl
, MOUNTPROC_PATHCONF
,
2454 xdr_dirpath
, (caddr_t
)&path
, xdr_ppathcnf
, (caddr_t
)p
, timeout
);
2455 if (rpc_stat
!= RPC_SUCCESS
) {
2456 if (cretries
-- <= 0) {
2458 "get_pathconf: %s: server not responding: %s",
2459 fsname
, clnt_sperror(cl
, ""));
2464 if (_PC_ISSET(_PC_ERROR
, p
->pc_mask
)) {
2465 syslog(LOG_ERR
, "get_pathconf: no info for %s", fsname
);
2469 *pcnf
= (struct pathcnf
*)p
;
2484 #define SMALL_HOSTNAME 20
2485 #define SMALL_PROTONAME 10
2486 #define SMALL_PROTOFMLYNAME 10
2488 struct portmap_cache
{
2492 char cache_small_hosts
[SMALL_HOSTNAME
+ 1];
2493 char *cache_hostname
;
2495 char *cache_protofmly
;
2496 char cache_small_protofmly
[SMALL_PROTOFMLYNAME
+ 1];
2497 char cache_small_proto
[SMALL_PROTONAME
+ 1];
2498 struct netbuf cache_srv_addr
;
2499 struct portmap_cache
*cache_prev
, *cache_next
;
2502 rwlock_t portmap_cache_lock
;
2503 static int portmap_cache_valid_time
= 30;
2504 struct portmap_cache
*portmap_cache_head
, *portmap_cache_tail
;
2508 portmap_cache_flush()
2510 struct portmap_cache
*next
= NULL
, *cp
;
2512 (void) rw_wrlock(&portmap_cache_lock
);
2513 for (cp
= portmap_cache_head
; cp
; cp
= cp
->cache_next
) {
2514 if (cp
->cache_hostname
!= NULL
&&
2515 cp
->cache_hostname
!=
2516 cp
->cache_small_hosts
)
2517 free(cp
->cache_hostname
);
2518 if (cp
->cache_proto
!= NULL
&&
2520 cp
->cache_small_proto
)
2521 free(cp
->cache_proto
);
2522 if (cp
->cache_srv_addr
.buf
!= NULL
)
2523 free(cp
->cache_srv_addr
.buf
);
2524 next
= cp
->cache_next
;
2527 portmap_cache_head
= NULL
;
2528 portmap_cache_tail
= NULL
;
2529 (void) rw_unlock(&portmap_cache_lock
);
2534 * Returns 1 if the entry is found in the cache, 0 otherwise.
2537 portmap_cache_lookup(hostname
, prog
, vers
, nconf
, addrp
)
2541 struct netconfig
*nconf
;
2542 struct netbuf
*addrp
;
2544 struct portmap_cache
*cachep
, *prev
, *next
= NULL
, *cp
;
2547 timenow
= time(NULL
);
2549 (void) rw_rdlock(&portmap_cache_lock
);
2552 * Increment the portmap cache counters for # accesses and lookups
2553 * Use a smaller factor (100 vs 1000 for the host cache) since
2554 * initial analysis shows this cache is looked up 10% that of the
2558 portmap_cache_accesses
++;
2559 portmap_cache_lookups
++;
2560 if ((portmap_cache_lookups
%100) == 0)
2561 trace_portmap_cache();
2562 #endif /* CACHE_DEBUG */
2564 for (cachep
= portmap_cache_head
; cachep
;
2565 cachep
= cachep
->cache_next
) {
2566 if (timenow
> cachep
->cache_time
) {
2568 * We stumbled across an entry in the cache which
2569 * has timed out. Free up all the entries that
2570 * were added before it, which will positionally
2571 * be after this entry. And adjust neighboring
2573 * When we drop the lock and re-acquire it, we
2574 * need to start from the beginning.
2576 (void) rw_unlock(&portmap_cache_lock
);
2577 (void) rw_wrlock(&portmap_cache_lock
);
2578 for (cp
= portmap_cache_head
;
2579 cp
&& (cp
->cache_time
>= timenow
);
2580 cp
= cp
->cache_next
)
2585 * Adjust the link of the predecessor.
2586 * Make the tail point to the new last entry.
2588 prev
= cp
->cache_prev
;
2590 portmap_cache_head
= NULL
;
2591 portmap_cache_tail
= NULL
;
2593 prev
->cache_next
= NULL
;
2594 portmap_cache_tail
= prev
;
2596 for (; cp
; cp
= next
) {
2597 if (cp
->cache_hostname
!= NULL
&&
2598 cp
->cache_hostname
!=
2599 cp
->cache_small_hosts
)
2600 free(cp
->cache_hostname
);
2601 if (cp
->cache_proto
!= NULL
&&
2603 cp
->cache_small_proto
)
2604 free(cp
->cache_proto
);
2605 if (cp
->cache_srv_addr
.buf
!= NULL
)
2606 free(cp
->cache_srv_addr
.buf
);
2607 next
= cp
->cache_next
;
2612 if (cachep
->cache_hostname
== NULL
||
2613 prog
!= cachep
->cache_prog
|| vers
!= cachep
->cache_vers
||
2614 strcmp(nconf
->nc_proto
, cachep
->cache_proto
) != 0 ||
2615 strcmp(nconf
->nc_protofmly
, cachep
->cache_protofmly
) != 0 ||
2616 strcmp(hostname
, cachep
->cache_hostname
) != 0)
2622 portmap_cache_hits
++; /* up portmap cache hit counter */
2623 #endif /* CACHE_DEBUG */
2624 addrp
->len
= cachep
->cache_srv_addr
.len
;
2625 memcpy(addrp
->buf
, cachep
->cache_srv_addr
.buf
, addrp
->len
);
2630 (void) rw_unlock(&portmap_cache_lock
);
2635 portmap_cache_enter(hostname
, prog
, vers
, nconf
, addrp
)
2639 struct netconfig
*nconf
;
2640 struct netbuf
*addrp
;
2642 struct portmap_cache
*cachep
;
2644 int protolen
, hostnamelen
;
2646 timenow
= time(NULL
);
2648 cachep
= malloc(sizeof (struct portmap_cache
));
2651 memset((char *)cachep
, 0, sizeof (*cachep
));
2653 hostnamelen
= strlen(hostname
);
2654 if (hostnamelen
<= SMALL_HOSTNAME
)
2655 cachep
->cache_hostname
= cachep
->cache_small_hosts
;
2657 cachep
->cache_hostname
= malloc(hostnamelen
+ 1);
2658 if (cachep
->cache_hostname
== NULL
)
2661 strcpy(cachep
->cache_hostname
, hostname
);
2662 protolen
= strlen(nconf
->nc_proto
);
2663 if (protolen
<= SMALL_PROTONAME
)
2664 cachep
->cache_proto
= cachep
->cache_small_proto
;
2666 cachep
->cache_proto
= malloc(protolen
+ 1);
2667 if (cachep
->cache_proto
== NULL
)
2670 protofmlylen
= strlen(nconf
->nc_protofmly
);
2671 if (protofmlylen
<= SMALL_PROTOFMLYNAME
)
2672 cachep
->cache_protofmly
= cachep
->cache_small_protofmly
;
2674 cachep
->cache_protofmly
= malloc(protofmlylen
+ 1);
2675 if (cachep
->cache_protofmly
== NULL
)
2679 strcpy(cachep
->cache_proto
, nconf
->nc_proto
);
2680 cachep
->cache_prog
= prog
;
2681 cachep
->cache_vers
= vers
;
2682 cachep
->cache_time
= timenow
+ portmap_cache_valid_time
;
2683 cachep
->cache_srv_addr
.len
= addrp
->len
;
2684 cachep
->cache_srv_addr
.buf
= malloc(addrp
->len
);
2685 if (cachep
->cache_srv_addr
.buf
== NULL
)
2687 memcpy(cachep
->cache_srv_addr
.buf
, addrp
->buf
, addrp
->maxlen
);
2688 cachep
->cache_prev
= NULL
;
2689 (void) rw_wrlock(&portmap_cache_lock
);
2691 * There's a window in which we could have multiple threads making
2692 * the same cache entry. This can be avoided by walking the cache
2693 * once again here to check and see if there are duplicate entries
2694 * (after grabbing the write lock). This isn't fatal and I'm not
2695 * going to bother with this.
2698 portmap_cache_accesses
++; /* up portmap cache access counter */
2699 #endif /* CACHE_DEBUG */
2700 cachep
->cache_next
= portmap_cache_head
;
2701 if (portmap_cache_head
!= NULL
)
2702 portmap_cache_head
->cache_prev
= cachep
;
2703 portmap_cache_head
= cachep
;
2704 (void) rw_unlock(&portmap_cache_lock
);
2708 syslog(LOG_ERR
, "portmap_cache_enter: Memory allocation failed");
2709 if (cachep
->cache_srv_addr
.buf
)
2710 free(cachep
->cache_srv_addr
.buf
);
2711 if (cachep
->cache_proto
&& protolen
> SMALL_PROTONAME
)
2712 free(cachep
->cache_proto
);
2713 if (cachep
->cache_hostname
&& hostnamelen
> SMALL_HOSTNAME
)
2714 free(cachep
->cache_hostname
);
2715 if (cachep
->cache_protofmly
&& protofmlylen
> SMALL_PROTOFMLYNAME
)
2716 free(cachep
->cache_protofmly
);
2723 get_cached_srv_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2724 struct netconfig
*nconf
, struct netbuf
*addrp
)
2726 if (portmap_cache_lookup(hostname
, prog
, vers
, nconf
, addrp
))
2728 if (rpcb_getaddr(prog
, vers
, nconf
, addrp
, hostname
) == 0)
2730 portmap_cache_enter(hostname
, prog
, vers
, nconf
, addrp
);
2735 * Get a network address on "hostname" for program "prog"
2736 * with version "vers". If the port number is specified (non zero)
2737 * then try for a TCP/UDP transport and set the port number of the
2738 * resulting IP address.
2740 * If the address of a netconfig pointer was passed and
2741 * if it's not null, use it as the netconfig otherwise
2742 * assign the address of the netconfig that was used to
2743 * establish contact with the service.
2745 * tinfo argument is for matching the get_addr() defined in
2746 * ../nfs/mount/mount.c
2749 static struct netbuf
*
2750 get_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2751 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2752 struct t_info
*tinfo
)
2755 enum clnt_stat cstat
;
2757 return (get_server_netinfo(SERVER_ADDR
, hostname
, prog
, vers
, NULL
,
2758 nconfp
, proto
, port
, tinfo
, NULL
, FALSE
, NULL
, &cstat
));
2761 static struct netbuf
*
2762 get_pubfh(char *hostname
, rpcvers_t vers
, mfs_snego_t
*mfssnego
,
2763 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2764 struct t_info
*tinfo
, caddr_t
*fhp
, bool_t get_pubfh
, char *fspath
)
2766 enum clnt_stat cstat
;
2768 return (get_server_netinfo(SERVER_FH
, hostname
, NFS_PROGRAM
, vers
,
2769 mfssnego
, nconfp
, proto
, port
, tinfo
, fhp
, get_pubfh
, fspath
,
2773 static enum clnt_stat
2774 get_ping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2775 struct netconfig
**nconfp
, ushort_t port
, bool_t direct_to_server
)
2777 enum clnt_stat cstat
;
2779 (void) get_server_netinfo(SERVER_PING
, hostname
, prog
,
2780 vers
, NULL
, nconfp
, NULL
, port
, NULL
, NULL
,
2781 direct_to_server
, NULL
, &cstat
);
2788 enum type_of_stuff type_of_stuff
,
2792 mfs_snego_t
*mfssnego
,
2793 struct netconfig
**nconfp
,
2795 ushort_t port
, /* may be zero */
2796 struct t_info
*tinfo
,
2798 bool_t direct_to_server
,
2800 enum clnt_stat
*cstatp
)
2802 struct netbuf
*nb
= NULL
;
2803 struct netconfig
*nconf
= NULL
;
2804 NCONF_HANDLE
*nc
= NULL
;
2807 struct t_bind
*tbind
= NULL
;
2808 int nthtry
= FIRST_TRY
;
2810 if (nconfp
&& *nconfp
) {
2811 return (get_netconfig_info(type_of_stuff
, hostname
,
2812 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2813 direct_to_server
, fspath
, cstatp
, mfssnego
));
2817 * No nconf passed in.
2819 * Try to get a nconf from /etc/netconfig.
2820 * First choice is COTS, second is CLTS unless proto
2821 * is specified. When we retry, we reset the
2822 * netconfig list, so that we search the whole list
2823 * for the next choice.
2825 if ((nc
= setnetpath()) == NULL
)
2829 * If proto is specified, then only search for the match,
2830 * otherwise try COTS first, if failed, then try CLTS.
2833 while ((nconf
= getnetpath(nc
)) != NULL
) {
2834 if (strcmp(nconf
->nc_proto
, proto
))
2837 * If the port number is specified then TCP/UDP
2838 * is needed. Otherwise any cots/clts will do.
2841 if ((strcmp(nconf
->nc_protofmly
, NC_INET
) &&
2842 strcmp(nconf
->nc_protofmly
, NC_INET6
)) ||
2843 (strcmp(nconf
->nc_proto
, NC_TCP
) &&
2844 strcmp(nconf
->nc_proto
, NC_UDP
)))
2847 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2848 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2849 direct_to_server
, fspath
, cstatp
, mfssnego
);
2850 if (*cstatp
== RPC_SUCCESS
)
2860 while ((nconf
= getnetpath(nc
)) != NULL
) {
2861 if (nconf
->nc_flag
& NC_VISIBLE
) {
2862 if (nthtry
== FIRST_TRY
) {
2863 if ((nconf
->nc_semantics
==
2865 (nconf
->nc_semantics
==
2869 if ((strcmp(nconf
->nc_protofmly
,
2871 strcmp(nconf
->nc_protofmly
,
2873 (strcmp(nconf
->nc_proto
,
2878 if (nthtry
== SECOND_TRY
) {
2879 if (nconf
->nc_semantics
==
2883 if ((strcmp(nconf
->nc_protofmly
,
2885 strcmp(nconf
->nc_protofmly
,
2887 (strcmp(nconf
->nc_proto
,
2895 if (nconf
== NULL
) {
2896 if (++nthtry
<= MNT_PREF_LISTLEN
) {
2898 if ((nc
= setnetpath()) == NULL
)
2904 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2905 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2906 direct_to_server
, fspath
, cstatp
, mfssnego
);
2907 if (*cstatp
!= RPC_SUCCESS
)
2909 * Continue the same search path in the
2910 * netconfig db until no more matched nconf
2918 * Got nconf and nb. Now dup the netconfig structure (nconf)
2919 * and return it thru nconfp.
2921 if (nconf
!= NULL
) {
2922 if ((*nconfp
= getnetconfigent(nconf
->nc_netid
)) == NULL
) {
2923 syslog(LOG_ERR
, "no memory\n");
2937 get_server_fh(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2938 mfs_snego_t
*mfssnego
, struct netconfig
*nconf
, ushort_t port
,
2939 struct t_info
*tinfo
, struct t_bind
*tbind
, caddr_t
*fhp
,
2940 bool_t direct_to_server
, char *fspath
, enum clnt_stat
*cstat
)
2943 AUTH
*new_ah
= NULL
;
2944 struct snego_t snego
;
2945 enum clnt_stat cs
= RPC_TIMEDOUT
;
2947 bool_t file_handle
= 1;
2948 enum snego_stat sec
;
2951 struct netbuf
*nb
= NULL
;
2953 if (direct_to_server
!= TRUE
)
2956 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
2957 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
2960 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0)
2963 /* LINTED pointer alignment */
2964 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
)) == NULL
)
2967 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
, fd
,
2968 direct_to_server
, port
, prog
, vers
, file_handle
) < 0) {
2972 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
, prog
, vers
, 0, 0);
2976 ah
= authsys_create_default();
2979 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
2980 __FILE__
, __LINE__
);
2982 AUTH_DESTROY(cl
->cl_auth
);
2985 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
2986 __FILE__
, __LINE__
);
2990 if (!mfssnego
->snego_done
&& vers
!= NFS_V4
) {
2992 * negotiate sec flavor.
2995 if ((sec
= nfs_sec_nego(vers
, cl
, fspath
, &snego
)) ==
3000 * check if server supports the one
3001 * specified in the sec= option.
3003 if (mfssnego
->sec_opt
) {
3004 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
3005 if (snego
.array
[jj
] ==
3006 mfssnego
->nfs_sec
.sc_nfsnum
) {
3007 mfssnego
->snego_done
= TRUE
;
3014 * find a common sec flavor
3016 if (!mfssnego
->snego_done
) {
3017 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
3018 if (!nfs_getseconfig_bynumber(
3020 &mfssnego
->nfs_sec
)) {
3021 mfssnego
->snego_done
= TRUE
;
3026 if (!mfssnego
->snego_done
)
3029 * Now that the flavor has been
3030 * negotiated, get the fh.
3032 * First, create an auth handle using the negotiated
3033 * sec flavor in the next lookup to
3034 * fetch the filehandle.
3036 new_ah
= nfs_create_ah(cl
, hostname
,
3037 &mfssnego
->nfs_sec
);
3041 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3042 __FILE__
, __LINE__
);
3044 AUTH_DESTROY(cl
->cl_auth
);
3045 cl
->cl_auth
= new_ah
;
3047 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3048 __FILE__
, __LINE__
);
3050 } else if (sec
== SNEGO_ARRAY_TOO_SMALL
||
3051 sec
== SNEGO_FAILURE
) {
3062 memset((char *)&arg
.dir
, 0, sizeof (wnl_fh
));
3063 memset((char *)&res
, 0, sizeof (wnl_diropres
));
3065 if (wnlproc_lookup_2(&arg
, &res
, cl
) !=
3066 RPC_SUCCESS
|| res
.status
!= WNL_OK
)
3068 *fhp
= malloc(sizeof (wnl_fh
));
3071 syslog(LOG_ERR
, "no memory\n");
3075 memcpy((char *)*fhp
,
3076 (char *)&res
.wnl_diropres_u
.wnl_diropres
.file
,
3083 WNL_LOOKUP3args arg
;
3087 memset((char *)&arg
.what
.dir
, 0, sizeof (wnl_fh3
));
3088 memset((char *)&res
, 0, sizeof (WNL_LOOKUP3res
));
3089 arg
.what
.name
= fspath
;
3090 if (wnlproc3_lookup_3(&arg
, &res
, cl
) !=
3091 RPC_SUCCESS
|| res
.status
!= WNL3_OK
)
3094 fh3p
= (nfs_fh3
*)malloc(sizeof (*fh3p
));
3097 syslog(LOG_ERR
, "no memory\n");
3102 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_len
;
3103 memcpy(fh3p
->fh3_u
.data
,
3104 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_val
,
3107 *fhp
= (caddr_t
)fh3p
;
3115 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
3117 if (cs
!= RPC_SUCCESS
)
3120 *fhp
= strdup(fspath
);
3122 cs
= RPC_SYSTEMERROR
;
3127 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
3129 syslog(LOG_ERR
, "no memory\n");
3130 cs
= RPC_SYSTEMERROR
;
3133 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
3134 if (nb
->buf
== NULL
) {
3135 syslog(LOG_ERR
, "no memory\n");
3138 cs
= RPC_SYSTEMERROR
;
3141 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
3142 nb
->len
= tbind
->addr
.len
;
3143 nb
->maxlen
= tbind
->addr
.maxlen
;
3147 destroy_auth_client_handle(cl
);
3148 cleanup_tli_parms(tbind
, fd
);
3153 * Sends a null call to the remote host's (NFS program, versp). versp
3154 * may be "NULL" in which case the default maximum version is used.
3155 * Upon return, versp contains the maximum version supported iff versp!= NULL.
3163 ushort_t port
, /* may be zero */
3169 struct timeval rpc_to_new
= {15, 0};
3170 static struct timeval rpc_rtrans_new
= {-1, -1};
3171 enum clnt_stat clnt_stat
;
3173 rpcvers_t versmax
; /* maximum version to try against server */
3174 rpcvers_t outvers
; /* version supported by host on last call */
3175 rpcvers_t vers_to_try
; /* to try different versions against host */
3177 struct netconfig
*nconf
;
3179 hostname
= strdup(hostpart
);
3180 if (hostname
== NULL
) {
3181 return (RPC_SYSTEMERROR
);
3183 unbracket(&hostname
);
3185 if (path
!= NULL
&& strcmp(hostname
, "nfs") == 0 &&
3186 strncmp(path
, "//", 2) == 0) {
3189 hostname
= strdup(path
+2);
3191 if (hostname
== NULL
)
3192 return (RPC_SYSTEMERROR
);
3194 path
= strchr(hostname
, '/');
3197 * This cannot happen. If it does, give up
3198 * on the ping as this is obviously a corrupt
3203 return (RPC_SUCCESS
);
3207 * Probable end point of host string.
3211 sport
= strchr(hostname
, ':');
3213 if (sport
!= NULL
&& sport
< path
) {
3216 * Actual end point of host string.
3219 port
= htons((ushort_t
)atoi(sport
+1));
3225 /* Pick up the default versions and then set them appropriately */
3228 /* use versmin passed in */
3231 set_versrange(0, &versmax
, &versmin
);
3235 strncasecmp(proto
, NC_UDP
, strlen(NC_UDP
)) == 0 &&
3236 versmax
== NFS_V4
) {
3237 if (versmin
== NFS_V4
) {
3239 *versp
= versmax
- 1;
3240 return (RPC_SUCCESS
);
3242 return (RPC_PROGUNAVAIL
);
3251 switch (cache_check(hostname
, versp
, proto
)) {
3253 if (hostname
!= hostpart
)
3255 return (RPC_SUCCESS
);
3257 if (hostname
!= hostpart
)
3259 return (RPC_TIMEDOUT
);
3266 * XXX The retransmission time rpcbrmttime is a global defined
3267 * in the rpc library (rpcb_clnt.c). We use (and like) the default
3268 * value of 15 sec in the rpc library. The code below is to protect
3269 * us in case it changes. This need not be done under a lock since
3270 * any # of threads entering this function will get the same
3271 * retransmission value.
3273 if (rpc_rtrans_new
.tv_sec
== -1 && rpc_rtrans_new
.tv_usec
== -1) {
3274 __rpc_control(CLCR_GET_RPCB_RMTTIME
, (char *)&rpc_rtrans_new
);
3275 if (rpc_rtrans_new
.tv_sec
!= 15 && rpc_rtrans_new
.tv_sec
!= 0)
3277 trace_prt(1, "RPC library rttimer changed\n");
3281 * XXX Manipulate the total timeout to get the number of
3282 * desired retransmissions. This code is heavily dependant on
3283 * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3285 for (i
= 0, j
= rpc_rtrans_new
.tv_sec
; i
< attempts
-1; i
++) {
3286 if (j
< RPC_MAX_BACKOFF
)
3289 j
= RPC_MAX_BACKOFF
;
3290 rpc_to_new
.tv_sec
+= j
;
3293 vers_to_try
= versmax
;
3296 * check the host's version within the timeout
3299 trace_prt(1, " ping: %s timeout=%ld request vers=%d min=%d\n",
3300 hostname
, rpc_to_new
.tv_sec
, versmax
, versmin
);
3302 if (usepub
== FALSE
) {
3305 * If NFSv4, then we do the same thing as is used
3306 * for public filehandles so that we avoid rpcbind
3308 if (vers_to_try
== NFS_V4
) {
3310 trace_prt(1, " pingnfs: Trying ping via "
3314 cl
= clnt_create_service_timed(hostname
, "nfs",
3315 NFS_PROGRAM
, vers_to_try
,
3316 port
, "circuit_v", &rpc_to_new
);
3318 outvers
= vers_to_try
;
3323 " pingnfs: Can't ping via "
3324 "\"circuit_v\" %s: RPC error=%d\n",
3325 hostname
, rpc_createerr
.cf_stat
);
3329 cl
= clnt_create_vers_timed(hostname
,
3330 NFS_PROGRAM
, &outvers
, versmin
, vers_to_try
,
3331 "datagram_v", &rpc_to_new
);
3336 " pingnfs: Can't ping via "
3337 "\"datagram_v\"%s: RPC error=%d\n",
3338 hostname
, rpc_createerr
.cf_stat
);
3340 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
3341 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
3343 if (rpc_createerr
.cf_stat
==
3344 RPC_PROGNOTREGISTERED
) {
3347 " pingnfs: Trying ping "
3348 "via \"circuit_v\"\n");
3350 cl
= clnt_create_vers_timed(hostname
,
3351 NFS_PROGRAM
, &outvers
,
3352 versmin
, vers_to_try
,
3353 "circuit_v", &rpc_to_new
);
3358 " pingnfs: Can't ping "
3359 "via \"circuit_v\" %s: "
3362 rpc_createerr
.cf_stat
);
3368 * backoff and return lower version to retry the ping.
3369 * XXX we should be more careful and handle
3370 * RPC_PROGVERSMISMATCH here, because that error is handled
3371 * in clnt_create_vers(). It's not done to stay in sync
3372 * with the nfs mount command.
3375 if (vers_to_try
< versmin
)
3377 if (versp
!= NULL
) { /* recheck the cache */
3378 *versp
= vers_to_try
;
3381 " pingnfs: check cache: vers=%d\n",
3384 switch (cache_check(hostname
, versp
, proto
)) {
3386 if (hostname
!= hostpart
)
3388 return (RPC_SUCCESS
);
3390 if (hostname
!= hostpart
)
3392 return (RPC_TIMEDOUT
);
3399 trace_prt(1, " pingnfs: Try version=%d\n",
3402 } while (cl
== NULL
);
3407 syslog(LOG_ERR
, "pingnfs: %s%s",
3408 hostname
, clnt_spcreateerror(""));
3409 clnt_stat
= rpc_createerr
.cf_stat
;
3412 clnt_stat
= RPC_SUCCESS
;
3416 for (vers_to_try
= versmax
; vers_to_try
>= versmin
;
3422 trace_prt(1, " pingnfs: Try version=%d "
3423 "using get_ping()\n", vers_to_try
);
3426 clnt_stat
= get_ping(hostname
, NFS_PROGRAM
,
3427 vers_to_try
, &nconf
, port
, TRUE
);
3430 freenetconfigent(nconf
);
3432 if (clnt_stat
== RPC_SUCCESS
) {
3433 outvers
= vers_to_try
;
3440 clnt_stat
== RPC_SUCCESS
?
3441 trace_prt(1, " pingnfs OK: nfs version=%d\n", outvers
):
3442 trace_prt(1, " pingnfs FAIL: can't get nfs version\n");
3444 if (clnt_stat
== RPC_SUCCESS
) {
3445 cache_enter(hostname
, versmax
, outvers
, proto
, GOODHOST
);
3449 cache_enter(hostname
, versmax
, versmax
, proto
, DEADHOST
);
3451 if (hostpart
!= hostname
)
3457 #define MNTTYPE_LOFS "lofs"
3460 loopbackmount(fsname
, dir
, mntopts
, overlay
)
3461 char *fsname
; /* Directory being mounted */
3462 char *dir
; /* Directory being mounted on */
3468 char fstype
[] = MNTTYPE_LOFS
;
3471 char optbuf
[MAX_MNTOPT_STR
];
3473 dirlen
= strlen(dir
);
3474 if (dir
[dirlen
-1] == ' ')
3477 if (dirlen
== strlen(fsname
) &&
3478 strncmp(fsname
, dir
, dirlen
) == 0) {
3480 "Mount of %s on %s would result in deadlock, aborted\n",
3484 mnt
.mnt_mntopts
= mntopts
;
3485 if (hasmntopt(&mnt
, MNTOPT_RO
) != NULL
)
3488 (void) strlcpy(optbuf
, mntopts
, sizeof (optbuf
));
3491 flags
|= MS_OVERLAY
;
3495 " loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3496 fsname
, dir
, flags
);
3498 if (mount(fsname
, dir
, flags
| MS_DATA
| MS_OPTIONSTR
, fstype
,
3499 NULL
, 0, optbuf
, sizeof (optbuf
)) < 0) {
3500 syslog(LOG_ERR
, "Mount of %s on %s: %m", fsname
, dir
);
3504 if (stat(dir
, &st
) == 0) {
3507 " loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3508 fsname
, dir
, st
.st_dev
, st
.st_rdev
);
3513 " loopbackmount of %s on %s OK\n", fsname
, dir
);
3514 trace_prt(1, " stat of %s failed\n", dir
);
3522 * Look for the value of a numeric option of the form foo=x. If found, set
3523 * *valp to the value and return non-zero. If not found or the option is
3524 * malformed, return zero.
3528 nopt(mnt
, opt
, valp
)
3531 int *valp
; /* OUT */
3537 * We should never get a null pointer, but if we do, it's better to
3538 * ignore the option than to dump core.
3542 syslog(LOG_DEBUG
, "null pointer for %s option", opt
);
3546 if (str
= hasmntopt(mnt
, opt
)) {
3547 if (equal
= strchr(str
, '=')) {
3548 *valp
= atoi(&equal
[1]);
3551 syslog(LOG_ERR
, "Bad numeric option '%s'", str
);
3561 struct timeval timeout
;
3563 enum clnt_stat rpc_stat
;
3565 struct replica
*list
;
3567 int isv4mount
= is_v4_mount(mnt
->mnt_mountp
);
3570 trace_prt(1, " nfsunmount: umount %s\n", mnt
->mnt_mountp
);
3572 if (umount(mnt
->mnt_mountp
) < 0) {
3574 trace_prt(1, " nfsunmount: umount %s FAILED\n",
3581 * If this is a NFSv4 mount, the mount protocol was not used
3582 * so we just return.
3586 trace_prt(1, " nfsunmount: umount %s OK\n",
3592 * If mounted with -o public, then no need to contact server
3593 * because mount protocol was not used.
3595 if (hasmntopt(mnt
, MNTOPT_PUBLIC
) != NULL
) {
3600 * The rest of this code is advisory to the server.
3601 * If it fails return success anyway.
3604 list
= parse_replica(mnt
->mnt_special
, &count
);
3608 "Memory allocation failed: %m");
3612 for (i
= 0; i
< count
; i
++) {
3614 host
= list
[i
].host
;
3615 path
= list
[i
].path
;
3618 * Skip file systems mounted using WebNFS, because mount
3619 * protocol was not used.
3621 if (strcmp(host
, "nfs") == 0 && strncmp(path
, "//", 2) == 0)
3624 cl
= clnt_create(host
, MOUNTPROG
, MOUNTVERS
, "datagram_v");
3628 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
3629 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3630 __FILE__
, __LINE__
);
3632 if (__clnt_bindresvport(cl
) < 0) {
3634 syslog(LOG_ERR
, "umount %s:%s: %s",
3636 "Couldn't bind to reserved port");
3637 destroy_auth_client_handle(cl
);
3641 drop_alloc("AUTH_HANDLE", cl
->cl_auth
, __FILE__
, __LINE__
);
3643 AUTH_DESTROY(cl
->cl_auth
);
3644 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
3646 syslog(LOG_ERR
, "umount %s:%s: %s",
3648 "Failed creating default auth handle");
3649 destroy_auth_client_handle(cl
);
3653 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0, __FILE__
, __LINE__
);
3655 timeout
.tv_usec
= 0;
3657 rpc_stat
= clnt_call(cl
, MOUNTPROC_UMNT
, xdr_dirpath
,
3658 (caddr_t
)&path
, xdr_void
, NULL
, timeout
);
3659 if (verbose
&& rpc_stat
!= RPC_SUCCESS
)
3660 syslog(LOG_ERR
, "%s: %s",
3661 host
, clnt_sperror(cl
, "unmount"));
3662 destroy_auth_client_handle(cl
);
3665 free_replica(list
, count
);
3668 trace_prt(1, " nfsunmount: umount %s OK\n", mnt
->mnt_mountp
);
3675 * Put a new entry in the cache chain by prepending it to the front.
3676 * If there isn't enough memory then just give up.
3679 cache_enter(host
, reqvers
, outvers
, proto
, state
)
3686 struct cache_entry
*entry
;
3687 int cache_time
= 30; /* sec */
3689 timenow
= time(NULL
);
3691 entry
= (struct cache_entry
*)malloc(sizeof (struct cache_entry
));
3694 (void) memset((caddr_t
)entry
, 0, sizeof (struct cache_entry
));
3695 entry
->cache_host
= strdup(host
);
3696 if (entry
->cache_host
== NULL
) {
3700 entry
->cache_reqvers
= reqvers
;
3701 entry
->cache_outvers
= outvers
;
3702 entry
->cache_proto
= (proto
== NULL
? NULL
: strdup(proto
));
3703 entry
->cache_state
= state
;
3704 entry
->cache_time
= timenow
+ cache_time
;
3705 (void) rw_wrlock(&cache_lock
);
3707 host_cache_accesses
++; /* up host cache access counter */
3708 #endif /* CACHE DEBUG */
3709 entry
->cache_next
= cache_head
;
3711 (void) rw_unlock(&cache_lock
);
3715 cache_check(host
, versp
, proto
)
3721 struct cache_entry
*ce
, *prev
;
3723 timenow
= time(NULL
);
3725 (void) rw_rdlock(&cache_lock
);
3728 /* Increment the lookup and access counters for the host cache */
3729 host_cache_accesses
++;
3730 host_cache_lookups
++;
3731 if ((host_cache_lookups
%1000) == 0)
3733 #endif /* CACHE DEBUG */
3735 for (ce
= cache_head
; ce
; ce
= ce
->cache_next
) {
3736 if (timenow
> ce
->cache_time
) {
3737 (void) rw_unlock(&cache_lock
);
3738 (void) rw_wrlock(&cache_lock
);
3739 for (prev
= NULL
, ce
= cache_head
; ce
;
3740 prev
= ce
, ce
= ce
->cache_next
) {
3741 if (timenow
> ce
->cache_time
) {
3744 prev
->cache_next
= NULL
;
3750 (void) rw_unlock(&cache_lock
);
3753 if (strcmp(host
, ce
->cache_host
) != 0)
3755 if ((proto
== NULL
&& ce
->cache_proto
!= NULL
) ||
3756 (proto
!= NULL
&& ce
->cache_proto
== NULL
))
3758 if (proto
!= NULL
&&
3759 strcmp(proto
, ce
->cache_proto
) != 0)
3762 if (versp
== NULL
||
3763 (versp
!= NULL
&& *versp
== ce
->cache_reqvers
) ||
3764 (versp
!= NULL
&& *versp
== ce
->cache_outvers
)) {
3766 *versp
= ce
->cache_outvers
;
3767 state
= ce
->cache_state
;
3769 /* increment the host cache hit counters */
3771 if (state
== GOODHOST
)
3772 goodhost_cache_hits
++;
3773 if (state
== DEADHOST
)
3774 deadhost_cache_hits
++;
3775 #endif /* CACHE_DEBUG */
3776 (void) rw_unlock(&cache_lock
);
3780 (void) rw_unlock(&cache_lock
);
3785 * Free a cache entry and all entries
3786 * further down the chain since they
3787 * will also be expired.
3791 struct cache_entry
*entry
;
3793 struct cache_entry
*ce
, *next
= NULL
;
3795 for (ce
= entry
; ce
; ce
= next
) {
3797 free(ce
->cache_host
);
3798 if (ce
->cache_proto
)
3799 free(ce
->cache_proto
);
3800 next
= ce
->cache_next
;
3809 (void) rw_wrlock(&cache_lock
);
3810 cache_free(cache_head
);
3812 (void) rw_unlock(&cache_lock
);
3818 mutex_lock(&cleanup_lock
);
3819 cond_signal(&cleanup_start_cv
);
3820 (void) cond_wait(&cleanup_done_cv
, &cleanup_lock
);
3821 mutex_unlock(&cleanup_lock
);
3823 portmap_cache_flush();
3828 * Returns 1, if port option is NFS_PORT or
3829 * nfsd is running on the port given
3830 * Returns 0, if both port is not NFS_PORT and nfsd is not
3831 * running on the port.
3835 is_nfs_port(char *opts
)
3838 uint_t nfs_port
= 0;
3843 m
.mnt_mntopts
= opts
;
3846 * Get port specified in options list, if any.
3848 got_port
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
3851 * if no port specified or it is same as NFS_PORT return nfs
3852 * To use any other daemon the port number should be different
3854 if (!got_port
|| nfs_port
== NFS_PORT
)
3857 * If daemon is nfsd, return nfs
3859 if (getservbyport_r(nfs_port
, NULL
, &sv
, buf
, 256) == &sv
&&
3860 strcmp(sv
.s_name
, "nfsd") == 0)
3871 * destroy_auth_client_handle(cl)
3872 * destroys the created client handle
3875 destroy_auth_client_handle(CLIENT
*cl
)
3880 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3881 __FILE__
, __LINE__
);
3883 AUTH_DESTROY(cl
->cl_auth
);
3887 drop_alloc("CLNT_HANDLE", cl
,
3888 __FILE__
, __LINE__
);
3896 * Attempt to figure out which version of NFS to use in pingnfs(). If
3897 * the version number was specified (i.e., non-zero), then use it.
3898 * Otherwise, default to the compiled-in default or the default as set
3899 * by the /etc/default/nfs configuration (as read by read_default().
3902 set_versrange(rpcvers_t nfsvers
, rpcvers_t
*vers
, rpcvers_t
*versmin
)
3906 *vers
= vers_max_default
;
3907 *versmin
= vers_min_default
;
3918 *vers
= NFS_VERSION
; /* version 2 */
3919 *versmin
= NFS_VERSMIN
; /* version 2 */
3929 * trace_portmap_cache()
3930 * traces the portmap cache values at desired points
3933 trace_portmap_cache()
3935 syslog(LOG_ERR
, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
3936 portmap_cache_accesses
, portmap_cache_lookups
,
3937 portmap_cache_hits
);
3941 * trace_host_cache()
3942 * traces the host cache values at desired points
3948 "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
3949 host_cache_accesses
, host_cache_lookups
, deadhost_cache_hits
,
3950 goodhost_cache_hits
);
3952 #endif /* CACHE_DEBUG */
3955 * Read the NFS SMF properties to determine if the
3956 * client has been configured for a new min/max for the NFS version to
3960 #define SVC_NFS_CLIENT "svc:/network/nfs/client"
3963 read_default_nfs(void)
3965 static time_t lastread
= 0;
3972 ret
= nfs_smf_get_prop("client_versmin", defval
, DEFAULT_INSTANCE
,
3973 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
3976 tmp
= strtol(defval
, (char **)NULL
, 10);
3978 vers_min_default
= tmp
;
3983 ret
= nfs_smf_get_prop("client_versmax", defval
, DEFAULT_INSTANCE
,
3984 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
3987 tmp
= strtol(defval
, (char **)NULL
, 10);
3989 vers_max_default
= tmp
;
3993 lastread
= buf
.st_mtime
;
3996 * Quick sanity check on the values picked up from the
3997 * defaults file. Make sure that a mistake wasn't
3998 * made that will confuse things later on.
3999 * If so, reset to compiled-in defaults
4001 if (vers_min_default
> vers_max_default
||
4002 vers_min_default
< NFS_VERSMIN
||
4003 vers_max_default
> NFS_VERSMAX
) {
4006 " read_default: version minimum/maximum incorrectly configured\n");
4008 " read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
4009 vers_min_default
, vers_max_default
,
4010 NFS_VERSMIN_DEFAULT
,
4011 NFS_VERSMAX_DEFAULT
);
4013 vers_min_default
= NFS_VERSMIN_DEFAULT
;
4014 vers_max_default
= NFS_VERSMAX_DEFAULT
;
4019 * Find the mnttab entry that corresponds to "name".
4020 * We're not sure what the name represents: either
4021 * a mountpoint name, or a special name (server:/path).
4022 * Return the last entry in the file that matches.
4024 static struct extmnttab
*
4025 mnttab_find(dirname
)
4029 struct extmnttab mnt
;
4030 struct extmnttab
*res
= NULL
;
4032 fp
= fopen(MNTTAB
, "r");
4035 trace_prt(1, " mnttab_find: unable to open mnttab\n");
4038 while (getextmntent(fp
, &mnt
, sizeof (struct extmnttab
)) == 0) {
4039 if (strcmp(mnt
.mnt_mountp
, dirname
) == 0 ||
4040 strcmp(mnt
.mnt_special
, dirname
) == 0) {
4043 res
= fsdupmnttab(&mnt
);
4051 trace_prt(1, " mnttab_find: unable to find %s\n",
4058 * This function's behavior is taken from nfsstat.
4059 * Trying to determine what NFS version was used for the mount.
4062 is_v4_mount(char *mntpath
)
4064 kstat_ctl_t
*kc
= NULL
; /* libkstat cookie */
4067 struct mntinfo_kstat mik
;
4068 struct extmnttab
*mntp
;
4071 if ((mntp
= mnttab_find(mntpath
)) == NULL
)
4074 /* save the minor number and free the struct so we don't forget */
4075 mnt_minor
= mntp
->mnt_minor
;
4078 if ((kc
= kstat_open()) == NULL
)
4081 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
4082 if (ksp
->ks_type
!= KSTAT_TYPE_RAW
)
4084 if (strcmp(ksp
->ks_module
, "nfs") != 0)
4086 if (strcmp(ksp
->ks_name
, "mntinfo") != 0)
4088 if (mnt_minor
!= ksp
->ks_instance
)
4091 if (kstat_read(kc
, ksp
, &mik
) == -1)
4094 (void) kstat_close(kc
);
4095 if (mik
.mik_vers
== 4)
4100 (void) kstat_close(kc
);
4106 create_homedir(const char *src
, const char *dst
) {
4110 struct passwd
*pwd
, pwds
;
4111 char buf_pwd
[NSS_BUFLEN_PASSWD
];
4117 trace_prt(1, "entered create_homedir\n");
4119 if (stat(src
, &stbuf
) == 0) {
4121 trace_prt(1, "src exists\n");
4125 dst_username
= strrchr(dst
, '/');
4127 dst_username
++; /* Skip over slash */
4128 getpwnam_r(dst_username
, &pwds
, buf_pwd
, sizeof (buf_pwd
),
4137 homedir_len
= strlen(pwd
->pw_dir
);
4138 dst_dir_len
= strlen(dst
) - homedir_len
;
4139 src_dir_len
= strlen(src
) - homedir_len
;
4141 /* Check that the paths are in the same zone */
4142 if (src_dir_len
< dst_dir_len
||
4143 (strncmp(dst
, src
, dst_dir_len
) != 0)) {
4145 trace_prt(1, " paths don't match\n");
4148 /* Check that mountpoint is an auto_home entry */
4149 if (dst_dir_len
< 0 ||
4150 (strcmp(pwd
->pw_dir
, dst
+ dst_dir_len
) != 0)) {
4154 /* Check that source is an home directory entry */
4155 if (src_dir_len
< 0 ||
4156 (strcmp(pwd
->pw_dir
, src
+ src_dir_len
) != 0)) {
4158 trace_prt(1, " homedir (2) doesn't match %s\n",
4164 S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
) == -1) {
4166 trace_prt(1, " Couldn't mkdir %s\n", src
);
4171 if (chown(src
, pwd
->pw_uid
, pwd
->pw_gid
) == -1) {
4176 /* Created new home directory for the user */
4181 free_nfs_args(struct nfs_args
*argp
)
4183 struct nfs_args
*oldp
;
4186 free(argp
->pathconf
);
4188 free_knconf(argp
->knconf
);
4190 netbuf_free(argp
->addr
);
4192 netbuf_free(argp
->syncaddr
);
4194 free(argp
->netname
);
4196 free(argp
->hostname
);
4197 if (argp
->nfs_ext_u
.nfs_extB
.secdata
)
4198 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
4201 if (argp
->nfs_ext_u
.nfs_extA
.secdata
) {
4203 sd
= argp
->nfs_ext_u
.nfs_extA
.secdata
;
4206 switch (sd
->rpcflavor
) {
4213 dh_k4_clntdata_t
*dhk4
;
4214 dhk4
= (dh_k4_clntdata_t
*)sd
->data
;
4217 if (dhk4
->syncaddr
.buf
)
4218 free(dhk4
->syncaddr
.buf
);
4219 if (dhk4
->knconf
->knc_protofmly
)
4220 free(dhk4
->knconf
->knc_protofmly
);
4221 if (dhk4
->knconf
->knc_proto
)
4222 free(dhk4
->knconf
->knc_proto
);
4226 free(dhk4
->netname
);
4232 gss_clntdata_t
*gss
;
4233 gss
= (gss_clntdata_t
*)sd
->data
;
4236 if (gss
->mechanism
.elements
)
4237 free(gss
->mechanism
.elements
);
4244 if (argp
->nfs_args_ext
== NFS_ARGS_EXTB
)
4245 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
4253 get_netconfig_info(enum type_of_stuff type_of_stuff
, char *hostname
,
4254 rpcprog_t prog
, rpcvers_t vers
, struct netconfig
*nconf
,
4255 ushort_t port
, struct t_info
*tinfo
, struct t_bind
*tbind
,
4256 caddr_t
*fhp
, bool_t direct_to_server
, char *fspath
,
4257 enum clnt_stat
*cstat
, mfs_snego_t
*mfssnego
)
4259 struct netconfig
*nb
= NULL
;
4260 int ping_server
= 0;
4266 switch (type_of_stuff
) {
4268 nb
= get_server_fh(hostname
, prog
, vers
, mfssnego
,
4269 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4275 nb
= get_server_addrorping(hostname
, prog
, vers
,
4276 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4277 fspath
, cstat
, ping_server
);
4286 * Get the server address or can we ping it or not.
4287 * Check the portmap cache first for server address.
4288 * If no entries there, ping the server with a NULLPROC rpc.
4291 get_server_addrorping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
4292 struct netconfig
*nconf
, ushort_t port
, struct t_info
*tinfo
,
4293 struct t_bind
*tbind
, caddr_t
*fhp
, bool_t direct_to_server
,
4294 char *fspath
, enum clnt_stat
*cstat
, int ping_server
)
4297 enum clnt_stat cs
= RPC_TIMEDOUT
;
4298 struct netbuf
*nb
= NULL
;
4302 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
4303 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
4306 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0) {
4310 /* LINTED pointer alignment */
4311 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
))
4316 if (direct_to_server
!= TRUE
) {
4318 if (get_cached_srv_addr(hostname
, prog
, vers
,
4319 nconf
, &tbind
->addr
) == 0)
4326 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
,
4327 fd
, direct_to_server
, port
, prog
, vers
, 0) < 0)
4330 if (port
|| (direct_to_server
== TRUE
)) {
4333 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
,
4338 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
4340 if (cs
!= RPC_SUCCESS
) {
4341 syslog(LOG_ERR
, "error is %d", cs
);
4346 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
4348 syslog(LOG_ERR
, "no memory\n");
4351 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
4352 if (nb
->buf
== NULL
) {
4353 syslog(LOG_ERR
, "no memory\n");
4358 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
4359 nb
->len
= tbind
->addr
.len
;
4360 nb
->maxlen
= tbind
->addr
.maxlen
;
4364 destroy_auth_client_handle(cl
);
4365 cleanup_tli_parms(tbind
, fd
);