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>
85 #include <tsol/label.h>
92 extern void set_nfsv4_ephemeral_mount_to(void);
94 extern char *nfs_get_qop_name();
95 extern AUTH
*nfs_create_ah();
96 extern enum snego_stat
nfs_sec_nego();
107 #define NFS_ARGS_EXTB_secdata(args, secdata) \
108 { (args).nfs_args_ext = NFS_ARGS_EXTB, \
109 (args).nfs_ext_u.nfs_extB.secdata = secdata; }
112 struct cache_entry
*cache_next
;
116 rpcvers_t cache_reqvers
;
117 rpcvers_t cache_outvers
;
127 typedef struct mfs_snego_t mfs_snego_t
;
129 static struct cache_entry
*cache_head
= NULL
;
130 rwlock_t cache_lock
; /* protect the cache chain */
132 static enum nfsstat
nfsmount(struct mapfs
*, char *, char *, int, uid_t
,
134 static int is_nfs_port(char *);
136 static void netbuf_free(struct netbuf
*);
137 static int get_pathconf(CLIENT
*, char *, char *, struct pathcnf
**, int);
138 static struct mapfs
*enum_servers(struct mapent
*, char *);
139 static struct mapfs
*get_mysubnet_servers(struct mapfs
*);
140 static int subnet_test(int af
, struct sioc_addrreq
*);
141 static struct netbuf
*get_addr(char *, rpcprog_t
, rpcvers_t
,
142 struct netconfig
**, char *, ushort_t
, struct t_info
*);
144 static struct netbuf
*get_pubfh(char *, rpcvers_t
, mfs_snego_t
*,
145 struct netconfig
**, char *, ushort_t
, struct t_info
*, caddr_t
*,
148 static int create_homedir(const char *, const char *);
156 static void *get_server_netinfo(enum type_of_stuff
, char *, rpcprog_t
,
157 rpcvers_t
, mfs_snego_t
*, struct netconfig
**, char *, ushort_t
,
158 struct t_info
*, caddr_t
*, bool_t
, char *, enum clnt_stat
*);
159 static void *get_netconfig_info(enum type_of_stuff
, char *, rpcprog_t
,
160 rpcvers_t
, struct netconfig
*, ushort_t
, struct t_info
*,
161 struct t_bind
*, caddr_t
*, bool_t
, char *, enum clnt_stat
*,
163 static void *get_server_addrorping(char *, rpcprog_t
, rpcvers_t
,
164 struct netconfig
*, ushort_t
, struct t_info
*, struct t_bind
*,
165 caddr_t
*, bool_t
, char *, enum clnt_stat
*, int);
166 static void *get_server_fh(char *, rpcprog_t
, rpcvers_t
, mfs_snego_t
*,
167 struct netconfig
*, ushort_t
, struct t_info
*, struct t_bind
*,
168 caddr_t
*, bool_t
, char *, enum clnt_stat
*);
170 struct mapfs
*add_mfs(struct mapfs
*, int, struct mapfs
**, struct mapfs
**);
171 void free_mfs(struct mapfs
*);
172 static void dump_mfs(struct mapfs
*, char *, int);
173 static char *dump_distance(struct mapfs
*);
174 static void cache_free(struct cache_entry
*);
175 static int cache_check(char *, rpcvers_t
*, char *);
176 static void cache_enter(char *, rpcvers_t
, rpcvers_t
, char *, int);
177 void destroy_auth_client_handle(CLIENT
*cl
);
180 static void trace_host_cache();
181 static void trace_portmap_cache();
182 #endif /* CACHE_DEBUG */
184 static int rpc_timeout
= 20;
188 * host cache counters. These variables do not need to be protected
189 * by mutex's. They have been added to measure the utility of the
190 * goodhost/deadhost cache in the lazy hierarchical mounting scheme.
192 static int host_cache_accesses
= 0;
193 static int host_cache_lookups
= 0;
194 static int deadhost_cache_hits
= 0;
195 static int goodhost_cache_hits
= 0;
198 * portmap cache counters. These variables do not need to be protected
199 * by mutex's. They have been added to measure the utility of the portmap
200 * cache in the lazy hierarchical mounting scheme.
202 static int portmap_cache_accesses
= 0;
203 static int portmap_cache_lookups
= 0;
204 static int portmap_cache_hits
= 0;
205 #endif /* CACHE_DEBUG */
208 * There are the defaults (range) for the client when determining
209 * which NFS version to use when probing the server (see above).
210 * These will only be used when the vers mount option is not used and
211 * these may be reset if /etc/default/nfs is configured to do so.
213 static rpcvers_t vers_max_default
= NFS_VERSMAX_DEFAULT
;
214 static rpcvers_t vers_min_default
= NFS_VERSMIN_DEFAULT
;
217 * list of support services needed
219 static char *service_list
[] = { STATD
, LOCKD
, NULL
};
220 static char *service_list_v4
[] = { STATD
, LOCKD
, NFS4CBD
, NFSMAPID
, NULL
};
222 static void read_default_nfs(void);
223 static int is_v4_mount(char *);
224 static void start_nfs4cbd(void);
235 struct mapfs
*mfs
, *mp
;
245 mfs
= enum_servers(me
, prevhost
);
250 * Try loopback if we have something on localhost; if nothing
251 * works, we will fall back to NFS
253 if (is_nfs_port(me
->map_mntopts
)) {
254 for (mp
= mfs
; mp
; mp
= mp
->mfs_next
) {
255 if (self_check(mp
->mfs_host
)) {
256 err
= loopbackmount(mp
->mfs_dir
,
257 mntpnt
, me
->map_mntopts
, overlay
);
262 * Free action_list if there
263 * is one as it is not needed.
264 * Make sure to set alpp to null
265 * so caller doesn't try to free it
278 dir
= strdup(mfs
->mfs_dir
);
279 err
= nfsmount(mfs
, mntpnt
, me
->map_mntopts
,
281 if (err
&& trace
> 1) {
282 trace_prt(1, " Couldn't mount %s:%s, err=%d\n",
283 mfs
->mfs_host
? mfs
->mfs_host
: "",
284 mfs
->mfs_dir
? mfs
->mfs_dir
: dir
, err
);
294 * Using the new ioctl SIOCTONLINK to determine if a host is on the same
295 * subnet. Remove the old network, subnet check.
298 static struct mapfs
*
299 get_mysubnet_servers(struct mapfs
*mfs_in
)
302 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
304 struct netconfig
*nconf
;
305 NCONF_HANDLE
*nc
= NULL
;
306 struct nd_hostserv hs
;
307 struct nd_addrlist
*retaddrs
;
309 struct sioc_addrreq areq
;
315 hs
.h_serv
= "rpcbind";
317 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
320 while (nconf
= getnetconfig(nc
)) {
323 * Care about INET family only. proto_done flag
324 * indicates if we have already covered this
325 * protocol family. If so skip it
327 if (((strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0) ||
328 (strcmp(nconf
->nc_protofmly
, NC_INET
) == 0)) &&
329 (nconf
->nc_semantics
== NC_TPI_CLTS
)) {
333 hs
.h_host
= mfs
->mfs_host
;
335 if (netdir_getbyname(nconf
, &hs
, &retaddrs
) != ND_OK
)
339 * For each host address see if it's on our
343 if (strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0)
347 nb
= retaddrs
->n_addrs
;
348 for (i
= 0; i
< retaddrs
->n_cnt
; i
++, nb
++) {
349 memset(&areq
.sa_addr
, 0, sizeof (areq
.sa_addr
));
350 memcpy(&areq
.sa_addr
, nb
->buf
, MIN(nb
->len
,
351 sizeof (areq
.sa_addr
)));
352 if (res
= subnet_test(af
, &areq
)) {
353 p
= add_mfs(mfs
, DIST_MYNET
,
354 &mfs_head
, &mfs_tail
);
356 netdir_free(retaddrs
,
363 } /* end of every host */
365 trace_prt(1, "get_mysubnet_servers: host=%s "
366 "netid=%s res=%s\n", mfs
->mfs_host
,
367 nconf
->nc_netid
, res
== 1?"SUC":"FAIL");
370 netdir_free(retaddrs
, ND_ADDRLIST
);
375 } /* end of every map */
382 subnet_test(int af
, struct sioc_addrreq
*areq
)
386 if ((s
= socket(af
, SOCK_DGRAM
, 0)) < 0) {
392 if (ioctl(s
, SIOCTONLINK
, (caddr_t
)areq
) < 0) {
393 syslog(LOG_ERR
, "subnet_test:SIOCTONLINK failed");
397 if (areq
->sa_res
== 1)
406 * ping a bunch of hosts at once and sort by who responds first
408 static struct mapfs
*
409 sort_servers(struct mapfs
*mfs_in
, int timeout
)
411 struct mapfs
*m1
= NULL
;
412 enum clnt_stat clnt_stat
;
417 clnt_stat
= nfs_cast(mfs_in
, &m1
, timeout
);
420 char buff
[2048] = {'\0'};
422 for (m1
= mfs_in
; m1
; m1
= m1
->mfs_next
) {
423 (void) strcat(buff
, m1
->mfs_host
);
425 (void) strcat(buff
, ",");
428 syslog(LOG_ERR
, "servers %s not responding: %s",
429 buff
, clnt_sperrno(clnt_stat
));
436 * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
437 * provided it is not marked "ignored" and isn't a dupe of ones we've
441 add_mfs(struct mapfs
*mfs
, int distance
, struct mapfs
**mfs_head
,
442 struct mapfs
**mfs_tail
)
444 struct mapfs
*tmp
, *new;
446 for (tmp
= *mfs_head
; tmp
; tmp
= tmp
->mfs_next
)
447 if ((strcmp(tmp
->mfs_host
, mfs
->mfs_host
) == 0 &&
448 strcmp(tmp
->mfs_dir
, mfs
->mfs_dir
) == 0) ||
451 new = (struct mapfs
*)malloc(sizeof (struct mapfs
));
453 syslog(LOG_ERR
, "Memory allocation failed: %m");
456 bcopy(mfs
, new, sizeof (struct mapfs
));
457 new->mfs_next
= NULL
;
459 new->mfs_distance
= distance
;
461 *mfs_tail
= *mfs_head
= new;
463 (*mfs_tail
)->mfs_next
= new;
470 dump_mfs(struct mapfs
*mfs
, char *message
, int level
)
477 trace_prt(1, "%s", message
);
479 trace_prt(0, "mfs is null\n");
482 for (m1
= mfs
; m1
; m1
= m1
->mfs_next
)
483 trace_prt(0, "%s[%s] ", m1
->mfs_host
, dump_distance(m1
));
488 dump_distance(struct mapfs
*mfs
)
490 switch (mfs
->mfs_distance
) {
491 case 0: return ("zero");
492 case DIST_SELF
: return ("self");
493 case DIST_MYSUB
: return ("mysub");
494 case DIST_MYNET
: return ("mynet");
495 case DIST_OTHER
: return ("other");
496 default: return ("other");
501 * Walk linked list "raw", building a new list consisting of members
502 * NOT found in list "filter", returning the result.
504 static struct mapfs
*
505 filter_mfs(struct mapfs
*raw
, struct mapfs
*filter
)
507 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
512 for (mfs
= raw
; mfs
; mfs
= mfs
->mfs_next
) {
513 for (skip
= 0, p
= filter
; p
; p
= p
->mfs_next
) {
514 if (strcmp(p
->mfs_host
, mfs
->mfs_host
) == 0 &&
515 strcmp(p
->mfs_dir
, mfs
->mfs_dir
) == 0) {
522 p
= add_mfs(mfs
, 0, &mfs_head
, &mfs_tail
);
530 * Walk a linked list of mapfs structs, freeing each member.
533 free_mfs(struct mapfs
*mfs
)
545 * New code for NFS client failover: we need to carry and sort
546 * lists of server possibilities rather than return a single
547 * entry. It preserves previous behaviour of sorting first by
548 * locality (loopback-or-preferred/subnet/net/other) and then
549 * by ping times. We'll short-circuit this process when we
550 * have ENOUGH or more entries.
552 static struct mapfs
*
553 enum_servers(struct mapent
*me
, char *preferred
)
555 struct mapfs
*p
, *m1
, *m2
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
558 * Short-circuit for simple cases.
560 if (!me
->map_fs
->mfs_next
) {
561 p
= add_mfs(me
->map_fs
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
567 dump_mfs(me
->map_fs
, " enum_servers: mapent: ", 2);
570 * get addresses & see if any are myself
571 * or were mounted from previously in a
572 * hierarchical mount.
575 trace_prt(1, " enum_servers: looking for pref/self\n");
576 for (m1
= me
->map_fs
; m1
; m1
= m1
->mfs_next
) {
579 if (self_check(m1
->mfs_host
) ||
580 strcmp(m1
->mfs_host
, preferred
) == 0) {
581 p
= add_mfs(m1
, DIST_SELF
, &mfs_head
, &mfs_tail
);
587 trace_prt(1, " enum_servers: pref/self found, %s\n",
591 * look for entries on this subnet
593 dump_mfs(m1
, " enum_servers: input of get_mysubnet_servers: ", 2);
594 m1
= get_mysubnet_servers(me
->map_fs
);
595 dump_mfs(m1
, " enum_servers: output of get_mysubnet_servers: ", 3);
596 if (m1
&& m1
->mfs_next
) {
597 m2
= sort_servers(m1
, rpc_timeout
/ 2);
598 dump_mfs(m2
, " enum_servers: output of sort_servers: ", 3);
603 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
604 p
= add_mfs(m2
, 0, &mfs_head
, &mfs_tail
);
612 * add the rest of the entries at the end
614 m1
= filter_mfs(me
->map_fs
, mfs_head
);
615 dump_mfs(m1
, " enum_servers: etc: output of filter_mfs: ", 3);
616 m2
= sort_servers(m1
, rpc_timeout
/ 2);
617 dump_mfs(m2
, " enum_servers: etc: output of sort_servers: ", 3);
621 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
622 p
= add_mfs(m2
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
630 dump_mfs(mfs_head
, " enum_servers: output: ", 1);
636 struct mapfs
*mfs_in
,
637 char *mntpnt
, char *opts
,
643 char remname
[MAXPATHLEN
], *mnttabtext
= NULL
;
644 char mopts
[MAX_MNTOPT_STR
];
645 char netname
[MAXNETNAMELEN
+1];
646 char *mntopts
= NULL
;
650 struct nfs_args
*argp
= NULL
, *head
= NULL
, *tail
= NULL
,
651 *prevhead
, *prevtail
;
654 struct timeval timeout
;
655 enum clnt_stat rpc_stat
;
658 struct netconfig
*nconf
;
659 rpcvers_t vers
, versmin
; /* used to negotiate nfs version in pingnfs */
660 /* and mount version with mountd */
661 rpcvers_t outvers
; /* final version to be used during mount() */
662 rpcvers_t nfsvers
; /* version in map options, 0 if not there */
663 rpcvers_t mountversmax
; /* tracks the max mountvers during retries */
665 /* used to negotiate nfs version using webnfs */
666 rpcvers_t pubvers
, pubversmin
, pubversmax
;
668 struct nd_addrlist
*retaddrs
;
669 struct mountres3 res3
;
673 char scerror_msg
[MAXMSGLEN
];
677 char *nfs_proto
= NULL
;
679 char *p
, *host
, *rhost
, *dir
;
680 struct mapfs
*mfs
= NULL
;
681 int error
, last_error
= 0;
684 int v2cnt
= 0, v3cnt
= 0, v4cnt
= 0;
685 int v2near
= 0, v3near
= 0, v4near
= 0;
689 int sec_opt
, scerror
;
690 struct sec_data
*secdata
;
692 struct netbuf
*syncaddr
;
696 mfs_snego_t mfssnego_init
, mfssnego
;
698 dump_mfs(mfs_in
, " nfsmount: input: ", 2);
699 replicated
= (mfs_in
->mfs_next
!= NULL
);
700 m
.mnt_mntopts
= opts
;
701 if (replicated
&& hasmntopt(&m
, MNTOPT_SOFT
)) {
704 "mount on %s is soft and will not be replicated.", mntpnt
);
707 if (replicated
&& !hasmntopt(&m
, MNTOPT_RO
)) {
710 "mount on %s is not read-only and will not be replicated.",
715 loglevel
= LOG_WARNING
;
721 trace_prt(1, " nfsmount: replicated mount on %s %s:\n",
724 trace_prt(1, " nfsmount: standard mount on %s %s:\n",
726 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
)
727 trace_prt(1, " %s:%s\n",
728 mfs
->mfs_host
, mfs
->mfs_dir
);
732 * Make sure mountpoint is safe to mount on
734 if (lstat(mntpnt
, &stbuf
) < 0) {
735 syslog(LOG_ERR
, "Couldn't stat %s: %m", mntpnt
);
736 return (NFSERR_NOENT
);
740 * Get protocol specified in options list, if any.
742 if ((str_opt(&m
, "proto", &nfs_proto
)) == -1) {
743 return (NFSERR_NOENT
);
747 * Get port specified in options list, if any.
749 got_val
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
751 nfs_port
= 0; /* "unspecified" */
752 if (nfs_port
> USHRT_MAX
) {
753 syslog(LOG_ERR
, "%s: invalid port number %d", mntpnt
, nfs_port
);
754 return (NFSERR_NOENT
);
758 * Set mount(2) flags here, outside of the loop.
760 flags
= MS_OPTIONSTR
;
761 flags
|= (hasmntopt(&m
, MNTOPT_RO
) == NULL
) ? 0 : MS_RDONLY
;
762 flags
|= (hasmntopt(&m
, MNTOPT_NOSUID
) == NULL
) ? 0 : MS_NOSUID
;
763 flags
|= overlay
? MS_OVERLAY
: 0;
764 if (mntpnt
[strlen(mntpnt
) - 1] != ' ')
765 /* direct mount point without offsets */
768 use_pubfh
= (hasmntopt(&m
, MNTOPT_PUBLIC
) == NULL
) ? FALSE
: TRUE
;
770 (void) memset(&mfssnego_init
, 0, sizeof (mfs_snego_t
));
771 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
772 if (++mfssnego_init
.sec_opt
> 1) {
774 "conflicting security options");
777 if (nfs_getseconfig_byname("dh", &mfssnego_init
.nfs_sec
)) {
779 "error getting dh information from %s",
785 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
786 if ((str_opt(&m
, MNTOPT_SEC
,
787 &mfssnego_init
.nfs_flavor
)) == -1) {
788 syslog(LOG_ERR
, "nfsmount: no memory");
793 if (mfssnego_init
.nfs_flavor
) {
794 if (++mfssnego_init
.sec_opt
> 1) {
796 "conflicting security options");
797 free(mfssnego_init
.nfs_flavor
);
800 if (nfs_getseconfig_byname(mfssnego_init
.nfs_flavor
,
801 &mfssnego_init
.nfs_sec
)) {
803 "error getting %s information from %s",
804 mfssnego_init
.nfs_flavor
, NFSSEC_CONF
);
805 free(mfssnego_init
.nfs_flavor
);
808 free(mfssnego_init
.nfs_flavor
);
814 got_val
= nopt(&m
, MNTOPT_VERS
, (int *)&nfsvers
);
816 nfsvers
= 0; /* "unspecified" */
817 if (set_versrange(nfsvers
, &vers
, &versmin
) != 0) {
818 syslog(LOG_ERR
, "Incorrect NFS version specified for %s",
820 last_error
= NFSERR_NOENT
;
825 pubversmax
= pubversmin
= nfsvers
;
828 pubversmin
= versmin
;
832 * Walk the whole list, pinging and collecting version
833 * info so that we can make sure the mount will be
834 * homogeneous with respect to version.
836 * If we have a version preference, this is easy; we'll
837 * just reject anything that doesn't match.
839 * If not, we want to try to provide the best compromise
840 * that considers proximity, preference for a higher version,
841 * sorted order, and number of replicas. We will count
842 * the number of V2 and V3 replicas and also the number
843 * which are "near", i.e. the localhost or on the same
846 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
853 * If the host is '[a:d:d:r:e:s:s'],
854 * only use 'a:d:d:r:e:s:s' for communication
856 host
= strdup(mfs
->mfs_host
);
858 syslog(LOG_ERR
, "nfsmount: no memory");
859 last_error
= NFSERR_IO
;
864 (void) memcpy(&mfssnego
, &mfssnego_init
, sizeof (mfs_snego_t
));
866 if (use_pubfh
== TRUE
|| mfs
->mfs_flags
& MFS_URL
) {
869 if (nfs_port
!= 0 && mfs
->mfs_port
!= 0 &&
870 nfs_port
!= mfs
->mfs_port
) {
872 syslog(LOG_ERR
, "nfsmount: port (%u) in nfs URL"
873 " not the same as port (%d) in port "
874 "option\n", mfs
->mfs_port
, nfs_port
);
875 last_error
= NFSERR_IO
;
878 } else if (nfs_port
!= 0)
881 thisport
= mfs
->mfs_port
;
885 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
886 path
= malloc(strlen(dir
) + 2);
888 syslog(LOG_ERR
, "nfsmount: no memory");
889 last_error
= NFSERR_IO
;
892 path
[0] = (char)WNL_NATIVEPATH
;
893 (void) strcpy(&path
[1], dir
);
898 argp
= (struct nfs_args
*)
899 malloc(sizeof (struct nfs_args
));
904 syslog(LOG_ERR
, "nfsmount: no memory");
905 last_error
= NFSERR_IO
;
908 (void) memset(argp
, 0, sizeof (*argp
));
912 * By now Mount argument struct has been allocated,
913 * either a pub_fh path will be taken or the regular
914 * one. So here if a protocol was specified and it
915 * was not rdma we let it be, else we set DO_RDMA.
916 * If no proto was there we advise on trying RDMA.
919 if (strcmp(nfs_proto
, "rdma") == 0) {
922 argp
->flags
|= NFSMNT_DORDMA
;
925 argp
->flags
|= NFSMNT_TRYRDMA
;
927 for (pubvers
= pubversmax
; pubvers
>= pubversmin
;
931 argp
->addr
= get_pubfh(host
, pubvers
, &mfssnego
,
932 &nconf
, nfs_proto
, thisport
, NULL
,
933 &argp
->fh
, TRUE
, path
);
935 if (argp
->addr
!= NULL
)
939 freenetconfigent(nconf
);
945 if (argp
->addr
!= NULL
) {
948 * The use of llock option for NFSv4
949 * mounts is not required since file
950 * locking is included within the protocol
952 if (pubvers
!= NFS_V4
)
953 argp
->flags
|= NFSMNT_LLOCK
;
955 argp
->flags
|= NFSMNT_PUBLIC
;
958 mfs
->mfs_args
= argp
;
959 mfs
->mfs_version
= pubvers
;
960 mfs
->mfs_nconf
= nconf
;
961 mfs
->mfs_flags
|= MFS_FH_VIA_WEBNFS
;
967 * If -public was specified, give up
970 if (use_pubfh
== TRUE
) {
972 "%s: no public file handle support",
974 last_error
= NFSERR_NOENT
;
980 * Back off to a conventional mount.
982 * URL's can contain escape characters. Get
985 path
= malloc(strlen(dir
) + 2);
988 syslog(LOG_ERR
, "nfsmount: no memory");
989 last_error
= NFSERR_IO
;
996 mfs
->mfs_flags
|= MFS_ALLOC_DIR
;
997 mfs
->mfs_flags
&= ~MFS_URL
;
1001 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
1002 i
= pingnfs(host
, get_retry(opts
) + 1, &vers
, versmin
,
1003 0, FALSE
, NULL
, nfs_proto
);
1004 if (i
!= RPC_SUCCESS
) {
1005 if (i
== RPC_PROGVERSMISMATCH
) {
1006 syslog(loglevel
, "server %s: NFS "
1007 "protocol version mismatch",
1010 syslog(loglevel
, "server %s not "
1011 "responding", host
);
1013 mfs
->mfs_ignore
= 1;
1014 last_error
= NFSERR_NOENT
;
1017 if (nfsvers
!= 0 && nfsvers
!= vers
) {
1018 if (nfs_proto
== NULL
)
1021 "not supported by %s",
1027 "not supported by %s",
1028 nfsvers
, nfs_proto
, host
);
1029 mfs
->mfs_ignore
= 1;
1030 last_error
= NFSERR_NOENT
;
1038 case NFS_V4
: v4cnt
++; break;
1039 case NFS_V3
: v3cnt
++; break;
1040 case NFS_VERSION
: v2cnt
++; break;
1045 * It's not clear how useful this stuff is if
1046 * we are using webnfs across the internet, but it
1049 if (mfs
->mfs_distance
&&
1050 mfs
->mfs_distance
<= DIST_MYSUB
) {
1052 case NFS_V4
: v4near
++; break;
1053 case NFS_V3
: v3near
++; break;
1054 case NFS_VERSION
: v2near
++; break;
1060 * If the mount is not replicated, we don't want to
1061 * ping every entry, so we'll stop here. This means
1062 * that we may have to go back to "nextentry" above
1063 * to consider another entry if we can't get
1064 * all the way to mount(2) with this one.
1073 * Choose the NFS version.
1074 * We prefer higher versions, but will choose a one-
1075 * version downgrade in service if we can use a local
1076 * network interface and avoid a router.
1078 if (v4cnt
&& v4cnt
>= v3cnt
&& (v4near
|| !v3near
))
1080 else if (v3cnt
&& v3cnt
>= v2cnt
&& (v3near
|| !v2near
))
1083 nfsvers
= NFS_VERSION
;
1086 " nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1087 v4cnt
, v4near
, v3cnt
, v3near
,
1088 v2cnt
, v2near
, nfsvers
);
1092 * Since we don't support different NFS versions in replicated
1093 * mounts, set fstype now.
1094 * Also take the opportunity to set
1095 * the mount protocol version as appropriate.
1099 fstype
= MNTTYPE_NFS4
;
1102 fstype
= MNTTYPE_NFS3
;
1103 if (use_pubfh
== FALSE
) {
1104 mountversmax
= MOUNTVERS3
;
1105 versmin
= MOUNTVERS3
;
1109 fstype
= MNTTYPE_NFS
;
1110 if (use_pubfh
== FALSE
) {
1111 mountversmax
= MOUNTVERS_POSIX
;
1112 versmin
= MOUNTVERS
;
1118 * Our goal here is to evaluate each of several possible
1119 * replicas and try to come up with a list we can hand
1120 * to mount(2). If we don't have a valid "head" at the
1121 * end of this process, it means we have rejected all
1122 * potential server:/path tuples. We will fail quietly
1123 * in front of mount(2), and will have printed errors
1124 * where we found them.
1125 * XXX - do option work outside loop w careful design
1126 * XXX - use macro for error condition free handling
1128 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
1131 * Initialize retry and delay values on a per-server basis.
1133 retries
= get_retry(opts
);
1136 if (mfs
->mfs_ignore
)
1140 * If we don't have a fh yet, and if this is not a replicated
1141 * mount, we haven't done a pingnfs() on the next entry,
1142 * so we don't know if the next entry is up or if it
1143 * supports an NFS version we like. So if we had a problem
1144 * with an entry, we need to go back and run through some new
1147 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1148 !replicated
&& skipentry
)
1151 vers
= mountversmax
;
1152 host
= mfs
->mfs_host
;
1156 * Remember the possible '[a:d:d:r:e:s:s]' as the address to be
1157 * later passed to mount(2) and used in the mnttab line, but
1158 * only use 'a:d:d:r:e:s:s' for communication
1160 rhost
= strdup(host
);
1161 if (rhost
== NULL
) {
1162 syslog(LOG_ERR
, "nfsmount: no memory");
1163 last_error
= NFSERR_IO
;
1168 (void) sprintf(remname
, "%s:%s", rhost
, dir
);
1169 if (trace
> 4 && replicated
)
1170 trace_prt(1, " nfsmount: examining %s\n", remname
);
1172 if (mfs
->mfs_args
== NULL
) {
1175 * Allocate nfs_args structure
1177 argp
= (struct nfs_args
*)
1178 malloc(sizeof (struct nfs_args
));
1181 syslog(LOG_ERR
, "nfsmount: no memory");
1182 last_error
= NFSERR_IO
;
1186 (void) memset(argp
, 0, sizeof (*argp
));
1190 * By now Mount argument struct has been allocated,
1191 * either a pub_fh path will be taken or the regular
1192 * one. So here if a protocol was specified and it
1193 * was not rdma we let it be, else we set DO_RDMA.
1194 * If no proto was there we advise on trying RDMA.
1197 if (strcmp(nfs_proto
, "rdma") == 0) {
1200 argp
->flags
|= NFSMNT_DORDMA
;
1203 argp
->flags
|= NFSMNT_TRYRDMA
;
1205 argp
= mfs
->mfs_args
;
1206 mfs
->mfs_args
= NULL
;
1209 * Skip entry if we already have file handle but the
1210 * NFS version is wrong.
1212 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) &&
1213 mfs
->mfs_version
!= nfsvers
) {
1217 mfs
->mfs_ignore
= 1;
1227 tail
= tail
->nfs_ext_u
.nfs_extB
.next
= argp
;
1230 * WebNFS and NFSv4 behave similarly in that they
1231 * don't use the mount protocol. Therefore, avoid
1232 * mount protocol like things when version 4 is being
1235 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1236 nfsvers
!= NFS_V4
) {
1237 timeout
.tv_usec
= 0;
1238 timeout
.tv_sec
= rpc_timeout
;
1239 rpc_stat
= RPC_TIMEDOUT
;
1241 /* Create the client handle. */
1245 " nfsmount: Get mount version: request "
1246 "vers=%d min=%d\n", vers
, versmin
);
1249 while ((cl
= clnt_create_vers(host
, MOUNTPROG
, &outvers
,
1250 versmin
, vers
, "udp")) == NULL
) {
1253 " nfsmount: Can't get mount "
1254 "version: rpcerr=%d\n",
1255 rpc_createerr
.cf_stat
);
1257 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
1258 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
1262 * backoff and return lower version to retry the ping.
1263 * XXX we should be more careful and handle
1264 * RPC_PROGVERSMISMATCH here, because that error
1265 * is handled in clnt_create_vers(). It's not done to
1266 * stay in sync with the nfs mount command.
1273 " nfsmount: Try version=%d\n",
1283 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1284 last_error
= NFSERR_NOENT
;
1286 if (rpc_createerr
.cf_stat
!= RPC_UNKNOWNHOST
&&
1287 rpc_createerr
.cf_stat
!=
1288 RPC_PROGVERSMISMATCH
&&
1294 syslog(loglevel
, "%s %s", host
,
1296 "server not responding"));
1298 mfs
->mfs_ignore
= 1;
1303 " nfsmount: mount version=%d\n", outvers
);
1306 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
1307 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1308 __FILE__
, __LINE__
);
1311 if (__clnt_bindresvport(cl
) < 0) {
1316 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1317 last_error
= NFSERR_NOENT
;
1319 if (retries
-- > 0) {
1320 destroy_auth_client_handle(cl
);
1325 syslog(loglevel
, "mount %s: %s", host
,
1326 "Couldn't bind to reserved port");
1327 destroy_auth_client_handle(cl
);
1329 mfs
->mfs_ignore
= 1;
1334 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
1335 __FILE__
, __LINE__
);
1337 AUTH_DESTROY(cl
->cl_auth
);
1338 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
1343 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1344 last_error
= NFSERR_NOENT
;
1346 if (retries
-- > 0) {
1347 destroy_auth_client_handle(cl
);
1352 syslog(loglevel
, "mount %s: %s", host
,
1353 "Failed creating default auth handle");
1354 destroy_auth_client_handle(cl
);
1356 mfs
->mfs_ignore
= 1;
1360 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1361 __FILE__
, __LINE__
);
1367 * set security options
1370 (void) memset(&nfs_sec
, 0, sizeof (nfs_sec
));
1371 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
1372 if (++sec_opt
> 1) {
1374 "conflicting security options for %s",
1380 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1381 last_error
= NFSERR_IO
;
1382 destroy_auth_client_handle(cl
);
1384 mfs
->mfs_ignore
= 1;
1387 if (nfs_getseconfig_byname("dh", &nfs_sec
)) {
1389 "error getting dh information from %s",
1395 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1396 last_error
= NFSERR_IO
;
1397 destroy_auth_client_handle(cl
);
1399 mfs
->mfs_ignore
= 1;
1405 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
1406 if ((str_opt(&m
, MNTOPT_SEC
, &nfs_flavor
)) == -1) {
1407 syslog(LOG_ERR
, "nfsmount: no memory");
1408 last_error
= NFSERR_IO
;
1409 destroy_auth_client_handle(cl
);
1415 if (++sec_opt
> 1) {
1417 "conflicting security options for %s",
1424 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1425 last_error
= NFSERR_IO
;
1426 destroy_auth_client_handle(cl
);
1428 mfs
->mfs_ignore
= 1;
1431 if (nfs_getseconfig_byname(nfs_flavor
, &nfs_sec
)) {
1433 "error getting %s information from %s",
1434 nfs_flavor
, NFSSEC_CONF
);
1440 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1441 last_error
= NFSERR_IO
;
1442 destroy_auth_client_handle(cl
);
1444 mfs
->mfs_ignore
= 1;
1450 posix
= (nfsvers
!= NFS_V4
&&
1451 hasmntopt(&m
, MNTOPT_POSIX
) != NULL
) ? 1 : 0;
1453 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1454 nfsvers
!= NFS_V4
) {
1455 bool_t give_up_on_mnt
;
1456 bool_t got_mnt_error
;
1458 * If we started with a URL, if first byte of path is not "/",
1459 * then the mount will likely fail, so we should try again
1460 * with a prepended "/".
1462 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
&& *dir
!= '/')
1463 give_up_on_mnt
= FALSE
;
1465 give_up_on_mnt
= TRUE
;
1467 got_mnt_error
= FALSE
;
1470 if (got_mnt_error
== TRUE
) {
1473 give_up_on_mnt
= TRUE
;
1477 * Insert a "/" to front of mfs_dir.
1479 for (i
= l
; i
> 0; i
--)
1485 /* Get fhandle of remote path from server's mountd */
1494 tail
->nfs_ext_u
.nfs_extB
.next
=
1496 last_error
= NFSERR_NOENT
;
1498 "can't get posix info for %s",
1500 destroy_auth_client_handle(cl
);
1502 mfs
->mfs_ignore
= 1;
1506 case MOUNTVERS_POSIX
:
1507 if (nfsvers
== NFS_V3
) {
1512 tail
->nfs_ext_u
.nfs_extB
.next
=
1514 last_error
= NFSERR_NOENT
;
1516 "%s doesn't support NFS Version 3",
1518 destroy_auth_client_handle(cl
);
1520 mfs
->mfs_ignore
= 1;
1523 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1524 xdr_dirpath
, (caddr_t
)&dir
,
1525 xdr_fhstatus
, (caddr_t
)&fhs
, timeout
);
1526 if (rpc_stat
!= RPC_SUCCESS
) {
1528 if (give_up_on_mnt
== FALSE
) {
1529 got_mnt_error
= TRUE
;
1534 * Given the way "clnt_sperror" works, the "%s"
1535 * immediately following the "not responding"
1542 tail
->nfs_ext_u
.nfs_extB
.next
=
1544 last_error
= NFSERR_NOENT
;
1546 if (retries
-- > 0) {
1547 destroy_auth_client_handle(cl
);
1554 " nfsmount: mount RPC "
1559 "%s server not responding%s",
1560 host
, clnt_sperror(cl
, ""));
1561 destroy_auth_client_handle(cl
);
1563 mfs
->mfs_ignore
= 1;
1566 if ((errno
= fhs
.fhs_status
) != MNT_OK
) {
1568 if (give_up_on_mnt
== FALSE
) {
1569 got_mnt_error
= TRUE
;
1577 tail
->nfs_ext_u
.nfs_extB
.next
=
1579 if (errno
== EACCES
) {
1580 status
= NFSERR_ACCES
;
1582 syslog(loglevel
, "%s: %m",
1588 " nfsmount: mount RPC gave"
1592 last_error
= status
;
1593 destroy_auth_client_handle(cl
);
1595 mfs
->mfs_ignore
= 1;
1598 argp
->fh
= malloc((sizeof (fhandle
)));
1600 syslog(LOG_ERR
, "nfsmount: no memory");
1601 last_error
= NFSERR_IO
;
1602 destroy_auth_client_handle(cl
);
1605 (void) memcpy(argp
->fh
,
1606 &fhs
.fhstatus_u
.fhs_fhandle
,
1611 (void) memset((char *)&res3
, '\0',
1613 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1614 xdr_dirpath
, (caddr_t
)&dir
,
1615 xdr_mountres3
, (caddr_t
)&res3
, timeout
);
1616 if (rpc_stat
!= RPC_SUCCESS
) {
1618 if (give_up_on_mnt
== FALSE
) {
1619 got_mnt_error
= TRUE
;
1624 * Given the way "clnt_sperror" works, the "%s"
1625 * immediately following the "not responding"
1632 tail
->nfs_ext_u
.nfs_extB
.next
=
1634 last_error
= NFSERR_NOENT
;
1636 if (retries
-- > 0) {
1637 destroy_auth_client_handle(cl
);
1644 " nfsmount: mount RPC "
1649 "%s server not responding%s",
1650 remname
, clnt_sperror(cl
, ""));
1651 destroy_auth_client_handle(cl
);
1653 mfs
->mfs_ignore
= 1;
1656 if ((errno
= res3
.fhs_status
) != MNT_OK
) {
1658 if (give_up_on_mnt
== FALSE
) {
1659 got_mnt_error
= TRUE
;
1667 tail
->nfs_ext_u
.nfs_extB
.next
=
1669 if (errno
== EACCES
) {
1670 status
= NFSERR_ACCES
;
1672 syslog(loglevel
, "%s: %m",
1678 " nfsmount: mount RPC gave"
1682 last_error
= status
;
1683 destroy_auth_client_handle(cl
);
1685 mfs
->mfs_ignore
= 1;
1690 * Negotiate the security flavor for nfs_mount
1692 auths
= res3
.mountres3_u
.mountinfo
.
1693 auth_flavors
.auth_flavors_val
;
1694 count
= res3
.mountres3_u
.mountinfo
.
1695 auth_flavors
.auth_flavors_len
;
1698 for (i
= 0; i
< count
; i
++)
1700 nfs_sec
.sc_nfsnum
) {
1705 "%s: does not support "
1706 "security \"%s\"\n",
1707 remname
, nfs_sec
.sc_name
);
1708 clnt_freeres(cl
, xdr_mountres3
,
1717 last_error
= NFSERR_IO
;
1718 destroy_auth_client_handle(cl
);
1720 mfs
->mfs_ignore
= 1;
1723 } else if (count
> 0) {
1724 for (i
= 0; i
< count
; i
++) {
1726 nfs_getseconfig_bynumber(
1727 auths
[i
], &nfs_sec
))) {
1733 if (nfs_syslog_scerr(scerror
,
1738 "mounted because it"
1740 "security flavor %d"
1746 clnt_freeres(cl
, xdr_mountres3
,
1755 last_error
= NFSERR_IO
;
1756 destroy_auth_client_handle(cl
);
1758 mfs
->mfs_ignore
= 1;
1764 res3
.mountres3_u
.mountinfo
.fhandle
.
1766 (void) memcpy(fh3
.fh3_u
.data
,
1767 res3
.mountres3_u
.mountinfo
.fhandle
.
1770 clnt_freeres(cl
, xdr_mountres3
,
1772 argp
->fh
= malloc(sizeof (nfs_fh3
));
1774 syslog(LOG_ERR
, "nfsmount: no memory");
1775 last_error
= NFSERR_IO
;
1776 destroy_auth_client_handle(cl
);
1779 (void) memcpy(argp
->fh
, &fh3
, sizeof (nfs_fh3
));
1786 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1787 last_error
= NFSERR_NOENT
;
1789 "unknown MOUNT version %ld on %s",
1791 destroy_auth_client_handle(cl
);
1793 mfs
->mfs_ignore
= 1;
1797 if (nfsvers
== NFS_V4
) {
1798 argp
->fh
= strdup(dir
);
1799 if (argp
->fh
== NULL
) {
1800 syslog(LOG_ERR
, "nfsmount: no memory");
1801 last_error
= NFSERR_IO
;
1807 trace_prt(1, " nfsmount: have %s filehandle for %s\n",
1810 argp
->flags
|= NFSMNT_NEWARGS
;
1811 argp
->flags
|= NFSMNT_INT
; /* default is "intr" */
1812 argp
->flags
|= NFSMNT_HOSTNAME
;
1813 argp
->hostname
= strdup(host
);
1814 if (argp
->hostname
== NULL
) {
1815 syslog(LOG_ERR
, "nfsmount: no memory");
1816 last_error
= NFSERR_IO
;
1821 * In this case, we want NFSv4 to behave like
1822 * non-WebNFS so that we get the server address.
1824 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
1828 thisport
= nfs_port
;
1830 thisport
= mfs
->mfs_port
;
1833 * For NFSv4, we want to avoid rpcbind, so call
1834 * get_server_netinfo() directly to tell it that
1835 * we want to go "direct_to_server". Otherwise,
1836 * do what has always been done.
1838 if (nfsvers
== NFS_V4
) {
1839 enum clnt_stat cstat
;
1841 argp
->addr
= get_server_netinfo(SERVER_ADDR
,
1842 host
, NFS_PROGRAM
, nfsvers
, NULL
,
1843 &nconf
, nfs_proto
, thisport
, NULL
,
1844 NULL
, TRUE
, NULL
, &cstat
);
1846 argp
->addr
= get_addr(host
, NFS_PROGRAM
,
1847 nfsvers
, &nconf
, nfs_proto
,
1851 if (argp
->addr
== NULL
) {
1853 free(argp
->hostname
);
1859 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1860 last_error
= NFSERR_NOENT
;
1862 if (retries
-- > 0) {
1863 destroy_auth_client_handle(cl
);
1868 syslog(loglevel
, "%s: no NFS service", host
);
1869 destroy_auth_client_handle(cl
);
1871 mfs
->mfs_ignore
= 1;
1876 "\tnfsmount: have net address for %s\n",
1880 nconf
= mfs
->mfs_nconf
;
1881 mfs
->mfs_nconf
= NULL
;
1884 argp
->flags
|= NFSMNT_KNCONF
;
1885 argp
->knconf
= get_knconf(nconf
);
1886 if (argp
->knconf
== NULL
) {
1887 netbuf_free(argp
->addr
);
1888 freenetconfigent(nconf
);
1890 free(argp
->hostname
);
1896 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1897 last_error
= NFSERR_NOSPC
;
1898 destroy_auth_client_handle(cl
);
1900 mfs
->mfs_ignore
= 1;
1905 "\tnfsmount: have net config for %s\n",
1908 if (hasmntopt(&m
, MNTOPT_SOFT
) != NULL
) {
1909 argp
->flags
|= NFSMNT_SOFT
;
1911 if (hasmntopt(&m
, MNTOPT_NOINTR
) != NULL
) {
1912 argp
->flags
&= ~(NFSMNT_INT
);
1914 if (hasmntopt(&m
, MNTOPT_NOAC
) != NULL
) {
1915 argp
->flags
|= NFSMNT_NOAC
;
1917 if (hasmntopt(&m
, MNTOPT_NOCTO
) != NULL
) {
1918 argp
->flags
|= NFSMNT_NOCTO
;
1920 if (hasmntopt(&m
, MNTOPT_FORCEDIRECTIO
) != NULL
) {
1921 argp
->flags
|= NFSMNT_DIRECTIO
;
1923 if (hasmntopt(&m
, MNTOPT_NOFORCEDIRECTIO
) != NULL
) {
1924 argp
->flags
&= ~(NFSMNT_DIRECTIO
);
1928 * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1930 if (mfssnego
.snego_done
) {
1931 memcpy(&nfs_sec
, &mfssnego
.nfs_sec
,
1932 sizeof (seconfig_t
));
1933 } else if (!sec_opt
) {
1935 * Get default security mode.
1937 if (nfs_getseconfig_default(&nfs_sec
)) {
1939 "error getting default security entry\n");
1940 free_knconf(argp
->knconf
);
1941 netbuf_free(argp
->addr
);
1942 freenetconfigent(nconf
);
1944 free(argp
->hostname
);
1950 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1951 last_error
= NFSERR_NOSPC
;
1952 destroy_auth_client_handle(cl
);
1954 mfs
->mfs_ignore
= 1;
1957 argp
->flags
|= NFSMNT_SECDEFAULT
;
1962 * get the network address for the time service on
1963 * the server. If an RPC based time service is
1964 * not available then try the IP time service.
1966 * Eventurally, we want to move this code to nfs_clnt_secdata()
1967 * when autod_nfs.c and mount.c can share the same
1968 * get_the_addr/get_netconfig_info routine.
1974 if (nfs_sec
.sc_rpcnum
== AUTH_DH
|| nfsvers
== NFS_V4
) {
1976 * If not using the public fh and not NFS_V4, we can try
1977 * talking RPCBIND. Otherwise, assume that firewalls
1978 * prevent us from doing that.
1980 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1981 nfsvers
!= NFS_V4
) {
1982 enum clnt_stat cstat
;
1983 syncaddr
= get_server_netinfo(SERVER_ADDR
,
1984 host
, RPCBPROG
, RPCBVERS
, NULL
, &nconf
,
1985 NULL
, 0, NULL
, NULL
, FALSE
, NULL
, &cstat
);
1988 if (syncaddr
!= NULL
) {
1989 /* for flags in sec_data */
1990 secflags
|= AUTH_F_RPCTIMESYNC
;
1992 struct nd_hostserv hs
;
1996 hs
.h_serv
= "timserver";
1997 error
= netdir_getbyname(nconf
, &hs
, &retaddrs
);
1999 if (error
!= ND_OK
&&
2000 nfs_sec
.sc_rpcnum
== AUTH_DH
) {
2002 "%s: secure: no time service\n",
2004 free_knconf(argp
->knconf
);
2005 netbuf_free(argp
->addr
);
2006 freenetconfigent(nconf
);
2008 free(argp
->hostname
);
2014 tail
->nfs_ext_u
.nfs_extB
.next
=
2016 last_error
= NFSERR_IO
;
2017 destroy_auth_client_handle(cl
);
2019 mfs
->mfs_ignore
= 1;
2024 syncaddr
= retaddrs
->n_addrs
;
2027 * For potential usage by NFS V4 when AUTH_DH
2028 * is negotiated via SECINFO in the kernel.
2030 if (nfsvers
== NFS_V4
&& syncaddr
&&
2031 host2netname(netname
, host
, NULL
)) {
2033 malloc(sizeof (struct netbuf
));
2034 argp
->syncaddr
->buf
=
2035 malloc(syncaddr
->len
);
2036 (void) memcpy(argp
->syncaddr
->buf
,
2037 syncaddr
->buf
, syncaddr
->len
);
2038 argp
->syncaddr
->len
= syncaddr
->len
;
2039 argp
->syncaddr
->maxlen
=
2041 argp
->netname
= strdup(netname
);
2042 argp
->flags
|= NFSMNT_SECURE
;
2048 * TSOL notes: automountd in tsol extension
2049 * has "read down" capability, i.e. we allow
2050 * a user to trigger an nfs mount into a lower
2051 * labeled zone. We achieve this by always having
2052 * root issue the mount request so that the
2053 * lookup ops can go past /zone/<zone_name>
2054 * on the server side.
2056 if (is_system_labeled())
2057 nfs_sec
.sc_uid
= (uid_t
)0;
2059 nfs_sec
.sc_uid
= uid
;
2061 * If AUTH_DH is a chosen flavor now, its data will be stored
2062 * in the sec_data structure via nfs_clnt_secdata().
2064 if (!(secdata
= nfs_clnt_secdata(&nfs_sec
, host
, argp
->knconf
,
2065 syncaddr
, secflags
))) {
2067 "errors constructing security related data\n");
2068 if (secflags
& AUTH_F_RPCTIMESYNC
)
2069 netbuf_free(syncaddr
);
2071 netdir_free(retaddrs
, ND_ADDRLIST
);
2073 netbuf_free(argp
->syncaddr
);
2075 free(argp
->netname
);
2077 free(argp
->hostname
);
2078 free_knconf(argp
->knconf
);
2079 netbuf_free(argp
->addr
);
2080 freenetconfigent(nconf
);
2086 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2087 last_error
= NFSERR_IO
;
2088 destroy_auth_client_handle(cl
);
2090 mfs
->mfs_ignore
= 1;
2093 NFS_ARGS_EXTB_secdata(*argp
, secdata
);
2094 /* end of security stuff */
2098 " nfsmount: have secure info for %s\n", remname
);
2100 if (hasmntopt(&m
, MNTOPT_GRPID
) != NULL
) {
2101 argp
->flags
|= NFSMNT_GRPID
;
2103 if (nopt(&m
, MNTOPT_RSIZE
, &argp
->rsize
)) {
2104 argp
->flags
|= NFSMNT_RSIZE
;
2106 if (nopt(&m
, MNTOPT_WSIZE
, &argp
->wsize
)) {
2107 argp
->flags
|= NFSMNT_WSIZE
;
2109 if (nopt(&m
, MNTOPT_TIMEO
, &argp
->timeo
)) {
2110 argp
->flags
|= NFSMNT_TIMEO
;
2112 if (nopt(&m
, MNTOPT_RETRANS
, &argp
->retrans
)) {
2113 argp
->flags
|= NFSMNT_RETRANS
;
2115 if (nopt(&m
, MNTOPT_ACTIMEO
, &argp
->acregmax
)) {
2116 argp
->flags
|= NFSMNT_ACREGMAX
;
2117 argp
->flags
|= NFSMNT_ACDIRMAX
;
2118 argp
->flags
|= NFSMNT_ACDIRMIN
;
2119 argp
->flags
|= NFSMNT_ACREGMIN
;
2120 argp
->acdirmin
= argp
->acregmin
= argp
->acdirmax
2123 if (nopt(&m
, MNTOPT_ACREGMIN
, &argp
->acregmin
)) {
2124 argp
->flags
|= NFSMNT_ACREGMIN
;
2126 if (nopt(&m
, MNTOPT_ACREGMAX
, &argp
->acregmax
)) {
2127 argp
->flags
|= NFSMNT_ACREGMAX
;
2129 if (nopt(&m
, MNTOPT_ACDIRMIN
, &argp
->acdirmin
)) {
2130 argp
->flags
|= NFSMNT_ACDIRMIN
;
2132 if (nopt(&m
, MNTOPT_ACDIRMAX
, &argp
->acdirmax
)) {
2133 argp
->flags
|= NFSMNT_ACDIRMAX
;
2138 argp
->pathconf
= NULL
;
2139 if (error
= get_pathconf(cl
, dir
, remname
,
2140 &argp
->pathconf
, retries
)) {
2141 if (secflags
& AUTH_F_RPCTIMESYNC
)
2142 netbuf_free(syncaddr
);
2144 netdir_free(retaddrs
, ND_ADDRLIST
);
2145 free_knconf(argp
->knconf
);
2146 netbuf_free(argp
->addr
);
2147 freenetconfigent(nconf
);
2149 argp
->nfs_ext_u
.nfs_extB
.secdata
);
2151 netbuf_free(argp
->syncaddr
);
2153 free(argp
->netname
);
2155 free(argp
->hostname
);
2161 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2162 last_error
= NFSERR_IO
;
2164 if (error
== RET_RETRY
&& retries
-- > 0) {
2165 destroy_auth_client_handle(cl
);
2170 destroy_auth_client_handle(cl
);
2172 mfs
->mfs_ignore
= 1;
2175 argp
->flags
|= NFSMNT_POSIX
;
2178 " nfsmount: have pathconf for %s\n",
2183 * free loop-specific data structures
2185 destroy_auth_client_handle(cl
);
2186 freenetconfigent(nconf
);
2187 if (secflags
& AUTH_F_RPCTIMESYNC
)
2188 netbuf_free(syncaddr
);
2190 netdir_free(retaddrs
, ND_ADDRLIST
);
2193 * Decide whether to use remote host's lockd or local locking.
2194 * If we are using the public fh, we've already turned
2197 if (hasmntopt(&m
, MNTOPT_LLOCK
))
2198 argp
->flags
|= NFSMNT_LLOCK
;
2199 if (!(argp
->flags
& NFSMNT_LLOCK
) && nfsvers
== NFS_VERSION
&&
2200 remote_lock(host
, argp
->fh
)) {
2201 syslog(loglevel
, "No network locking on %s : "
2202 "contact admin to install server change", host
);
2203 argp
->flags
|= NFSMNT_LLOCK
;
2207 * Build a string for /etc/mnttab.
2208 * If possible, coalesce strings with same 'dir' info.
2210 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
2214 p
= strrchr(mnttabtext
, (int)':');
2215 if (!p
|| strcmp(p
+1, dir
) != 0) {
2216 mnttabcnt
+= strlen(remname
) + 2;
2219 mnttabcnt
+= strlen(rhost
) + 2;
2221 if ((tmp
= realloc(mnttabtext
,
2222 mnttabcnt
)) != NULL
) {
2224 strcat(mnttabtext
, ",");
2230 mnttabcnt
= strlen(remname
) + 1;
2231 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2232 mnttabtext
[0] = '\0';
2235 if (mnttabtext
!= NULL
)
2236 strcat(mnttabtext
, remname
);
2243 more_cnt
+= strlen("nfs://");
2244 more_cnt
+= strlen(mfs
->mfs_host
);
2246 if (mfs
->mfs_port
!= 0) {
2247 (void) sprintf(sport
, ":%u", mfs
->mfs_port
);
2251 more_cnt
+= strlen(sport
);
2252 more_cnt
+= 1; /* "/" */
2253 more_cnt
+= strlen(mfs
->mfs_dir
);
2256 more_cnt
+= 1; /* "," */
2257 mnttabcnt
+= more_cnt
;
2259 if ((tmp
= realloc(mnttabtext
,
2260 mnttabcnt
)) != NULL
) {
2262 strcat(mnttabtext
, ",");
2268 mnttabcnt
= more_cnt
+ 1;
2269 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2270 mnttabtext
[0] = '\0';
2273 if (mnttabtext
!= NULL
) {
2274 strcat(mnttabtext
, "nfs://");
2275 strcat(mnttabtext
, mfs
->mfs_host
);
2276 strcat(mnttabtext
, sport
);
2277 strcat(mnttabtext
, "/");
2278 strcat(mnttabtext
, mfs
->mfs_dir
);
2283 syslog(LOG_ERR
, "nfsmount: no memory");
2284 last_error
= NFSERR_IO
;
2289 * At least one entry, can call mount(2).
2294 * If replication was defeated, don't do more work
2302 * Did we get through all possibilities without success?
2307 /* Make "xattr" the default if "noxattr" is not specified. */
2308 strcpy(mopts
, opts
);
2309 if (!hasmntopt(&m
, MNTOPT_NOXATTR
) && !hasmntopt(&m
, MNTOPT_XATTR
)) {
2310 if (strlen(mopts
) > 0)
2312 strcat(mopts
, "xattr");
2316 * enable services as needed.
2321 if (strcmp(fstype
, MNTTYPE_NFS4
) == 0)
2322 sl
= service_list_v4
;
2326 (void) _check_services(sl
);
2330 * Whew; do the mount, at last.
2333 trace_prt(1, " mount %s %s (%s)\n", mnttabtext
, mntpnt
, mopts
);
2337 * About to do a nfs mount, make sure the mount_to is set for
2338 * potential ephemeral mounts with NFSv4.
2340 set_nfsv4_ephemeral_mount_to();
2343 * If no action list pointer then do the mount, otherwise
2344 * build the actions list pointer with the mount information.
2345 * so the mount can be done in the kernel.
2348 if (mount(mnttabtext
, mntpnt
, flags
| MS_DATA
, fstype
,
2349 head
, sizeof (*head
), mopts
, MAX_MNTOPT_STR
) < 0) {
2351 trace_prt(1, " Mount of %s on %s: %d\n",
2352 mnttabtext
, mntpnt
, errno
);
2353 if (errno
!= EBUSY
|| verbose
)
2355 "Mount of %s on %s: %m", mnttabtext
, mntpnt
);
2356 last_error
= NFSERR_IO
;
2360 last_error
= NFS_OK
;
2361 if (stat(mntpnt
, &stbuf
) == 0) {
2363 trace_prt(1, " mount %s dev=%x rdev=%x OK\n",
2364 mnttabtext
, stbuf
.st_dev
, stbuf
.st_rdev
);
2368 trace_prt(1, " mount %s OK\n", mnttabtext
);
2369 trace_prt(1, " stat of %s failed\n", mntpnt
);
2374 alp
->action
.action
= AUTOFS_MOUNT_RQ
;
2375 alp
->action
.action_list_entry_u
.mounta
.spec
=
2377 alp
->action
.action_list_entry_u
.mounta
.dir
= strdup(mntpnt
);
2378 alp
->action
.action_list_entry_u
.mounta
.flags
=
2380 alp
->action
.action_list_entry_u
.mounta
.fstype
=
2382 alp
->action
.action_list_entry_u
.mounta
.dataptr
= (char *)head
;
2383 alp
->action
.action_list_entry_u
.mounta
.datalen
=
2385 mntopts
= malloc(strlen(mopts
) + 1);
2386 strcpy(mntopts
, mopts
);
2387 mntopts
[strlen(mopts
)] = '\0';
2388 alp
->action
.action_list_entry_u
.mounta
.optptr
= mntopts
;
2389 alp
->action
.action_list_entry_u
.mounta
.optlen
=
2390 strlen(mntopts
) + 1;
2391 last_error
= NFS_OK
;
2399 free(argp
->pathconf
);
2400 free_knconf(argp
->knconf
);
2401 netbuf_free(argp
->addr
);
2403 netbuf_free(argp
->syncaddr
);
2404 if (argp
->netname
) {
2405 free(argp
->netname
);
2408 free(argp
->hostname
);
2409 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
2412 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
2421 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
2423 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
) {
2425 mfs
->mfs_dir
= NULL
;
2426 mfs
->mfs_flags
&= ~MFS_ALLOC_DIR
;
2429 if (mfs
->mfs_args
!= NULL
&& alp
== NULL
) {
2430 free(mfs
->mfs_args
);
2431 mfs
->mfs_args
= NULL
;
2434 if (mfs
->mfs_nconf
!= NULL
) {
2435 freenetconfigent(mfs
->mfs_nconf
);
2436 mfs
->mfs_nconf
= NULL
;
2440 return (last_error
);
2444 * get_pathconf(cl, path, fsname, pcnf, cretries)
2445 * ugliness that requires that ppathcnf and pathcnf stay consistent
2446 * cretries is a copy of retries used to determine when to syslog
2447 * on retry situations.
2450 get_pathconf(CLIENT
*cl
, char *path
, char *fsname
, struct pathcnf
**pcnf
,
2453 struct ppathcnf
*p
= NULL
;
2454 enum clnt_stat rpc_stat
;
2455 struct timeval timeout
;
2457 p
= (struct ppathcnf
*)malloc(sizeof (struct ppathcnf
));
2459 syslog(LOG_ERR
, "get_pathconf: Out of memory");
2462 memset((caddr_t
)p
, 0, sizeof (struct ppathcnf
));
2464 timeout
.tv_sec
= 10;
2465 timeout
.tv_usec
= 0;
2466 rpc_stat
= clnt_call(cl
, MOUNTPROC_PATHCONF
,
2467 xdr_dirpath
, (caddr_t
)&path
, xdr_ppathcnf
, (caddr_t
)p
, timeout
);
2468 if (rpc_stat
!= RPC_SUCCESS
) {
2469 if (cretries
-- <= 0) {
2471 "get_pathconf: %s: server not responding: %s",
2472 fsname
, clnt_sperror(cl
, ""));
2477 if (_PC_ISSET(_PC_ERROR
, p
->pc_mask
)) {
2478 syslog(LOG_ERR
, "get_pathconf: no info for %s", fsname
);
2482 *pcnf
= (struct pathcnf
*)p
;
2497 #define SMALL_HOSTNAME 20
2498 #define SMALL_PROTONAME 10
2499 #define SMALL_PROTOFMLYNAME 10
2501 struct portmap_cache
{
2505 char cache_small_hosts
[SMALL_HOSTNAME
+ 1];
2506 char *cache_hostname
;
2508 char *cache_protofmly
;
2509 char cache_small_protofmly
[SMALL_PROTOFMLYNAME
+ 1];
2510 char cache_small_proto
[SMALL_PROTONAME
+ 1];
2511 struct netbuf cache_srv_addr
;
2512 struct portmap_cache
*cache_prev
, *cache_next
;
2515 rwlock_t portmap_cache_lock
;
2516 static int portmap_cache_valid_time
= 30;
2517 struct portmap_cache
*portmap_cache_head
, *portmap_cache_tail
;
2521 portmap_cache_flush()
2523 struct portmap_cache
*next
= NULL
, *cp
;
2525 (void) rw_wrlock(&portmap_cache_lock
);
2526 for (cp
= portmap_cache_head
; cp
; cp
= cp
->cache_next
) {
2527 if (cp
->cache_hostname
!= NULL
&&
2528 cp
->cache_hostname
!=
2529 cp
->cache_small_hosts
)
2530 free(cp
->cache_hostname
);
2531 if (cp
->cache_proto
!= NULL
&&
2533 cp
->cache_small_proto
)
2534 free(cp
->cache_proto
);
2535 if (cp
->cache_srv_addr
.buf
!= NULL
)
2536 free(cp
->cache_srv_addr
.buf
);
2537 next
= cp
->cache_next
;
2540 portmap_cache_head
= NULL
;
2541 portmap_cache_tail
= NULL
;
2542 (void) rw_unlock(&portmap_cache_lock
);
2547 * Returns 1 if the entry is found in the cache, 0 otherwise.
2550 portmap_cache_lookup(hostname
, prog
, vers
, nconf
, addrp
)
2554 struct netconfig
*nconf
;
2555 struct netbuf
*addrp
;
2557 struct portmap_cache
*cachep
, *prev
, *next
= NULL
, *cp
;
2560 timenow
= time(NULL
);
2562 (void) rw_rdlock(&portmap_cache_lock
);
2565 * Increment the portmap cache counters for # accesses and lookups
2566 * Use a smaller factor (100 vs 1000 for the host cache) since
2567 * initial analysis shows this cache is looked up 10% that of the
2571 portmap_cache_accesses
++;
2572 portmap_cache_lookups
++;
2573 if ((portmap_cache_lookups
%100) == 0)
2574 trace_portmap_cache();
2575 #endif /* CACHE_DEBUG */
2577 for (cachep
= portmap_cache_head
; cachep
;
2578 cachep
= cachep
->cache_next
) {
2579 if (timenow
> cachep
->cache_time
) {
2581 * We stumbled across an entry in the cache which
2582 * has timed out. Free up all the entries that
2583 * were added before it, which will positionally
2584 * be after this entry. And adjust neighboring
2586 * When we drop the lock and re-acquire it, we
2587 * need to start from the beginning.
2589 (void) rw_unlock(&portmap_cache_lock
);
2590 (void) rw_wrlock(&portmap_cache_lock
);
2591 for (cp
= portmap_cache_head
;
2592 cp
&& (cp
->cache_time
>= timenow
);
2593 cp
= cp
->cache_next
)
2598 * Adjust the link of the predecessor.
2599 * Make the tail point to the new last entry.
2601 prev
= cp
->cache_prev
;
2603 portmap_cache_head
= NULL
;
2604 portmap_cache_tail
= NULL
;
2606 prev
->cache_next
= NULL
;
2607 portmap_cache_tail
= prev
;
2609 for (; cp
; cp
= next
) {
2610 if (cp
->cache_hostname
!= NULL
&&
2611 cp
->cache_hostname
!=
2612 cp
->cache_small_hosts
)
2613 free(cp
->cache_hostname
);
2614 if (cp
->cache_proto
!= NULL
&&
2616 cp
->cache_small_proto
)
2617 free(cp
->cache_proto
);
2618 if (cp
->cache_srv_addr
.buf
!= NULL
)
2619 free(cp
->cache_srv_addr
.buf
);
2620 next
= cp
->cache_next
;
2625 if (cachep
->cache_hostname
== NULL
||
2626 prog
!= cachep
->cache_prog
|| vers
!= cachep
->cache_vers
||
2627 strcmp(nconf
->nc_proto
, cachep
->cache_proto
) != 0 ||
2628 strcmp(nconf
->nc_protofmly
, cachep
->cache_protofmly
) != 0 ||
2629 strcmp(hostname
, cachep
->cache_hostname
) != 0)
2635 portmap_cache_hits
++; /* up portmap cache hit counter */
2636 #endif /* CACHE_DEBUG */
2637 addrp
->len
= cachep
->cache_srv_addr
.len
;
2638 memcpy(addrp
->buf
, cachep
->cache_srv_addr
.buf
, addrp
->len
);
2643 (void) rw_unlock(&portmap_cache_lock
);
2648 portmap_cache_enter(hostname
, prog
, vers
, nconf
, addrp
)
2652 struct netconfig
*nconf
;
2653 struct netbuf
*addrp
;
2655 struct portmap_cache
*cachep
;
2657 int protolen
, hostnamelen
;
2659 timenow
= time(NULL
);
2661 cachep
= malloc(sizeof (struct portmap_cache
));
2664 memset((char *)cachep
, 0, sizeof (*cachep
));
2666 hostnamelen
= strlen(hostname
);
2667 if (hostnamelen
<= SMALL_HOSTNAME
)
2668 cachep
->cache_hostname
= cachep
->cache_small_hosts
;
2670 cachep
->cache_hostname
= malloc(hostnamelen
+ 1);
2671 if (cachep
->cache_hostname
== NULL
)
2674 strcpy(cachep
->cache_hostname
, hostname
);
2675 protolen
= strlen(nconf
->nc_proto
);
2676 if (protolen
<= SMALL_PROTONAME
)
2677 cachep
->cache_proto
= cachep
->cache_small_proto
;
2679 cachep
->cache_proto
= malloc(protolen
+ 1);
2680 if (cachep
->cache_proto
== NULL
)
2683 protofmlylen
= strlen(nconf
->nc_protofmly
);
2684 if (protofmlylen
<= SMALL_PROTOFMLYNAME
)
2685 cachep
->cache_protofmly
= cachep
->cache_small_protofmly
;
2687 cachep
->cache_protofmly
= malloc(protofmlylen
+ 1);
2688 if (cachep
->cache_protofmly
== NULL
)
2692 strcpy(cachep
->cache_proto
, nconf
->nc_proto
);
2693 cachep
->cache_prog
= prog
;
2694 cachep
->cache_vers
= vers
;
2695 cachep
->cache_time
= timenow
+ portmap_cache_valid_time
;
2696 cachep
->cache_srv_addr
.len
= addrp
->len
;
2697 cachep
->cache_srv_addr
.buf
= malloc(addrp
->len
);
2698 if (cachep
->cache_srv_addr
.buf
== NULL
)
2700 memcpy(cachep
->cache_srv_addr
.buf
, addrp
->buf
, addrp
->maxlen
);
2701 cachep
->cache_prev
= NULL
;
2702 (void) rw_wrlock(&portmap_cache_lock
);
2704 * There's a window in which we could have multiple threads making
2705 * the same cache entry. This can be avoided by walking the cache
2706 * once again here to check and see if there are duplicate entries
2707 * (after grabbing the write lock). This isn't fatal and I'm not
2708 * going to bother with this.
2711 portmap_cache_accesses
++; /* up portmap cache access counter */
2712 #endif /* CACHE_DEBUG */
2713 cachep
->cache_next
= portmap_cache_head
;
2714 if (portmap_cache_head
!= NULL
)
2715 portmap_cache_head
->cache_prev
= cachep
;
2716 portmap_cache_head
= cachep
;
2717 (void) rw_unlock(&portmap_cache_lock
);
2721 syslog(LOG_ERR
, "portmap_cache_enter: Memory allocation failed");
2722 if (cachep
->cache_srv_addr
.buf
)
2723 free(cachep
->cache_srv_addr
.buf
);
2724 if (cachep
->cache_proto
&& protolen
> SMALL_PROTONAME
)
2725 free(cachep
->cache_proto
);
2726 if (cachep
->cache_hostname
&& hostnamelen
> SMALL_HOSTNAME
)
2727 free(cachep
->cache_hostname
);
2728 if (cachep
->cache_protofmly
&& protofmlylen
> SMALL_PROTOFMLYNAME
)
2729 free(cachep
->cache_protofmly
);
2736 get_cached_srv_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2737 struct netconfig
*nconf
, struct netbuf
*addrp
)
2739 if (portmap_cache_lookup(hostname
, prog
, vers
, nconf
, addrp
))
2741 if (rpcb_getaddr(prog
, vers
, nconf
, addrp
, hostname
) == 0)
2743 portmap_cache_enter(hostname
, prog
, vers
, nconf
, addrp
);
2748 * Get a network address on "hostname" for program "prog"
2749 * with version "vers". If the port number is specified (non zero)
2750 * then try for a TCP/UDP transport and set the port number of the
2751 * resulting IP address.
2753 * If the address of a netconfig pointer was passed and
2754 * if it's not null, use it as the netconfig otherwise
2755 * assign the address of the netconfig that was used to
2756 * establish contact with the service.
2758 * tinfo argument is for matching the get_addr() defined in
2759 * ../nfs/mount/mount.c
2762 static struct netbuf
*
2763 get_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2764 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2765 struct t_info
*tinfo
)
2768 enum clnt_stat cstat
;
2770 return (get_server_netinfo(SERVER_ADDR
, hostname
, prog
, vers
, NULL
,
2771 nconfp
, proto
, port
, tinfo
, NULL
, FALSE
, NULL
, &cstat
));
2774 static struct netbuf
*
2775 get_pubfh(char *hostname
, rpcvers_t vers
, mfs_snego_t
*mfssnego
,
2776 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2777 struct t_info
*tinfo
, caddr_t
*fhp
, bool_t get_pubfh
, char *fspath
)
2779 enum clnt_stat cstat
;
2781 return (get_server_netinfo(SERVER_FH
, hostname
, NFS_PROGRAM
, vers
,
2782 mfssnego
, nconfp
, proto
, port
, tinfo
, fhp
, get_pubfh
, fspath
,
2786 static enum clnt_stat
2787 get_ping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2788 struct netconfig
**nconfp
, ushort_t port
, bool_t direct_to_server
)
2790 enum clnt_stat cstat
;
2792 (void) get_server_netinfo(SERVER_PING
, hostname
, prog
,
2793 vers
, NULL
, nconfp
, NULL
, port
, NULL
, NULL
,
2794 direct_to_server
, NULL
, &cstat
);
2801 enum type_of_stuff type_of_stuff
,
2805 mfs_snego_t
*mfssnego
,
2806 struct netconfig
**nconfp
,
2808 ushort_t port
, /* may be zero */
2809 struct t_info
*tinfo
,
2811 bool_t direct_to_server
,
2813 enum clnt_stat
*cstatp
)
2815 struct netbuf
*nb
= NULL
;
2816 struct netconfig
*nconf
= NULL
;
2817 NCONF_HANDLE
*nc
= NULL
;
2820 struct t_bind
*tbind
= NULL
;
2821 int nthtry
= FIRST_TRY
;
2823 if (nconfp
&& *nconfp
) {
2824 return (get_netconfig_info(type_of_stuff
, hostname
,
2825 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2826 direct_to_server
, fspath
, cstatp
, mfssnego
));
2830 * No nconf passed in.
2832 * Try to get a nconf from /etc/netconfig.
2833 * First choice is COTS, second is CLTS unless proto
2834 * is specified. When we retry, we reset the
2835 * netconfig list, so that we search the whole list
2836 * for the next choice.
2838 if ((nc
= setnetpath()) == NULL
)
2842 * If proto is specified, then only search for the match,
2843 * otherwise try COTS first, if failed, then try CLTS.
2846 while ((nconf
= getnetpath(nc
)) != NULL
) {
2847 if (strcmp(nconf
->nc_proto
, proto
))
2850 * If the port number is specified then TCP/UDP
2851 * is needed. Otherwise any cots/clts will do.
2854 if ((strcmp(nconf
->nc_protofmly
, NC_INET
) &&
2855 strcmp(nconf
->nc_protofmly
, NC_INET6
)) ||
2856 (strcmp(nconf
->nc_proto
, NC_TCP
) &&
2857 strcmp(nconf
->nc_proto
, NC_UDP
)))
2860 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2861 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2862 direct_to_server
, fspath
, cstatp
, mfssnego
);
2863 if (*cstatp
== RPC_SUCCESS
)
2873 while ((nconf
= getnetpath(nc
)) != NULL
) {
2874 if (nconf
->nc_flag
& NC_VISIBLE
) {
2875 if (nthtry
== FIRST_TRY
) {
2876 if ((nconf
->nc_semantics
==
2878 (nconf
->nc_semantics
==
2882 if ((strcmp(nconf
->nc_protofmly
,
2884 strcmp(nconf
->nc_protofmly
,
2886 (strcmp(nconf
->nc_proto
,
2891 if (nthtry
== SECOND_TRY
) {
2892 if (nconf
->nc_semantics
==
2896 if ((strcmp(nconf
->nc_protofmly
,
2898 strcmp(nconf
->nc_protofmly
,
2900 (strcmp(nconf
->nc_proto
,
2908 if (nconf
== NULL
) {
2909 if (++nthtry
<= MNT_PREF_LISTLEN
) {
2911 if ((nc
= setnetpath()) == NULL
)
2917 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2918 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2919 direct_to_server
, fspath
, cstatp
, mfssnego
);
2920 if (*cstatp
!= RPC_SUCCESS
)
2922 * Continue the same search path in the
2923 * netconfig db until no more matched nconf
2931 * Got nconf and nb. Now dup the netconfig structure (nconf)
2932 * and return it thru nconfp.
2934 if (nconf
!= NULL
) {
2935 if ((*nconfp
= getnetconfigent(nconf
->nc_netid
)) == NULL
) {
2936 syslog(LOG_ERR
, "no memory\n");
2950 get_server_fh(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2951 mfs_snego_t
*mfssnego
, struct netconfig
*nconf
, ushort_t port
,
2952 struct t_info
*tinfo
, struct t_bind
*tbind
, caddr_t
*fhp
,
2953 bool_t direct_to_server
, char *fspath
, enum clnt_stat
*cstat
)
2956 AUTH
*new_ah
= NULL
;
2957 struct snego_t snego
;
2958 enum clnt_stat cs
= RPC_TIMEDOUT
;
2960 bool_t file_handle
= 1;
2961 enum snego_stat sec
;
2964 struct netbuf
*nb
= NULL
;
2966 if (direct_to_server
!= TRUE
)
2969 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
2970 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
2973 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0)
2976 /* LINTED pointer alignment */
2977 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
)) == NULL
)
2980 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
, fd
,
2981 direct_to_server
, port
, prog
, vers
, file_handle
) < 0) {
2985 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
, prog
, vers
, 0, 0);
2989 ah
= authsys_create_default();
2992 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
2993 __FILE__
, __LINE__
);
2995 AUTH_DESTROY(cl
->cl_auth
);
2998 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
2999 __FILE__
, __LINE__
);
3003 if (!mfssnego
->snego_done
&& vers
!= NFS_V4
) {
3005 * negotiate sec flavor.
3008 if ((sec
= nfs_sec_nego(vers
, cl
, fspath
, &snego
)) ==
3013 * check if server supports the one
3014 * specified in the sec= option.
3016 if (mfssnego
->sec_opt
) {
3017 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
3018 if (snego
.array
[jj
] ==
3019 mfssnego
->nfs_sec
.sc_nfsnum
) {
3020 mfssnego
->snego_done
= TRUE
;
3027 * find a common sec flavor
3029 if (!mfssnego
->snego_done
) {
3030 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
3031 if (!nfs_getseconfig_bynumber(
3033 &mfssnego
->nfs_sec
)) {
3034 mfssnego
->snego_done
= TRUE
;
3039 if (!mfssnego
->snego_done
)
3042 * Now that the flavor has been
3043 * negotiated, get the fh.
3045 * First, create an auth handle using the negotiated
3046 * sec flavor in the next lookup to
3047 * fetch the filehandle.
3049 new_ah
= nfs_create_ah(cl
, hostname
,
3050 &mfssnego
->nfs_sec
);
3054 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3055 __FILE__
, __LINE__
);
3057 AUTH_DESTROY(cl
->cl_auth
);
3058 cl
->cl_auth
= new_ah
;
3060 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3061 __FILE__
, __LINE__
);
3063 } else if (sec
== SNEGO_ARRAY_TOO_SMALL
||
3064 sec
== SNEGO_FAILURE
) {
3075 memset((char *)&arg
.dir
, 0, sizeof (wnl_fh
));
3076 memset((char *)&res
, 0, sizeof (wnl_diropres
));
3078 if (wnlproc_lookup_2(&arg
, &res
, cl
) !=
3079 RPC_SUCCESS
|| res
.status
!= WNL_OK
)
3081 *fhp
= malloc(sizeof (wnl_fh
));
3084 syslog(LOG_ERR
, "no memory\n");
3088 memcpy((char *)*fhp
,
3089 (char *)&res
.wnl_diropres_u
.wnl_diropres
.file
,
3096 WNL_LOOKUP3args arg
;
3100 memset((char *)&arg
.what
.dir
, 0, sizeof (wnl_fh3
));
3101 memset((char *)&res
, 0, sizeof (WNL_LOOKUP3res
));
3102 arg
.what
.name
= fspath
;
3103 if (wnlproc3_lookup_3(&arg
, &res
, cl
) !=
3104 RPC_SUCCESS
|| res
.status
!= WNL3_OK
)
3107 fh3p
= (nfs_fh3
*)malloc(sizeof (*fh3p
));
3110 syslog(LOG_ERR
, "no memory\n");
3115 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_len
;
3116 memcpy(fh3p
->fh3_u
.data
,
3117 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_val
,
3120 *fhp
= (caddr_t
)fh3p
;
3128 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
3130 if (cs
!= RPC_SUCCESS
)
3133 *fhp
= strdup(fspath
);
3135 cs
= RPC_SYSTEMERROR
;
3140 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
3142 syslog(LOG_ERR
, "no memory\n");
3143 cs
= RPC_SYSTEMERROR
;
3146 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
3147 if (nb
->buf
== NULL
) {
3148 syslog(LOG_ERR
, "no memory\n");
3151 cs
= RPC_SYSTEMERROR
;
3154 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
3155 nb
->len
= tbind
->addr
.len
;
3156 nb
->maxlen
= tbind
->addr
.maxlen
;
3160 destroy_auth_client_handle(cl
);
3161 cleanup_tli_parms(tbind
, fd
);
3166 * Sends a null call to the remote host's (NFS program, versp). versp
3167 * may be "NULL" in which case the default maximum version is used.
3168 * Upon return, versp contains the maximum version supported iff versp!= NULL.
3176 ushort_t port
, /* may be zero */
3182 struct timeval rpc_to_new
= {15, 0};
3183 static struct timeval rpc_rtrans_new
= {-1, -1};
3184 enum clnt_stat clnt_stat
;
3186 rpcvers_t versmax
; /* maximum version to try against server */
3187 rpcvers_t outvers
; /* version supported by host on last call */
3188 rpcvers_t vers_to_try
; /* to try different versions against host */
3190 struct netconfig
*nconf
;
3192 hostname
= strdup(hostpart
);
3193 if (hostname
== NULL
) {
3194 return (RPC_SYSTEMERROR
);
3196 unbracket(&hostname
);
3198 if (path
!= NULL
&& strcmp(hostname
, "nfs") == 0 &&
3199 strncmp(path
, "//", 2) == 0) {
3202 hostname
= strdup(path
+2);
3204 if (hostname
== NULL
)
3205 return (RPC_SYSTEMERROR
);
3207 path
= strchr(hostname
, '/');
3210 * This cannot happen. If it does, give up
3211 * on the ping as this is obviously a corrupt
3216 return (RPC_SUCCESS
);
3220 * Probable end point of host string.
3224 sport
= strchr(hostname
, ':');
3226 if (sport
!= NULL
&& sport
< path
) {
3229 * Actual end point of host string.
3232 port
= htons((ushort_t
)atoi(sport
+1));
3238 /* Pick up the default versions and then set them appropriately */
3241 /* use versmin passed in */
3244 set_versrange(0, &versmax
, &versmin
);
3248 strncasecmp(proto
, NC_UDP
, strlen(NC_UDP
)) == 0 &&
3249 versmax
== NFS_V4
) {
3250 if (versmin
== NFS_V4
) {
3252 *versp
= versmax
- 1;
3253 return (RPC_SUCCESS
);
3255 return (RPC_PROGUNAVAIL
);
3264 switch (cache_check(hostname
, versp
, proto
)) {
3266 if (hostname
!= hostpart
)
3268 return (RPC_SUCCESS
);
3270 if (hostname
!= hostpart
)
3272 return (RPC_TIMEDOUT
);
3279 * XXX The retransmission time rpcbrmttime is a global defined
3280 * in the rpc library (rpcb_clnt.c). We use (and like) the default
3281 * value of 15 sec in the rpc library. The code below is to protect
3282 * us in case it changes. This need not be done under a lock since
3283 * any # of threads entering this function will get the same
3284 * retransmission value.
3286 if (rpc_rtrans_new
.tv_sec
== -1 && rpc_rtrans_new
.tv_usec
== -1) {
3287 __rpc_control(CLCR_GET_RPCB_RMTTIME
, (char *)&rpc_rtrans_new
);
3288 if (rpc_rtrans_new
.tv_sec
!= 15 && rpc_rtrans_new
.tv_sec
!= 0)
3290 trace_prt(1, "RPC library rttimer changed\n");
3294 * XXX Manipulate the total timeout to get the number of
3295 * desired retransmissions. This code is heavily dependant on
3296 * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3298 for (i
= 0, j
= rpc_rtrans_new
.tv_sec
; i
< attempts
-1; i
++) {
3299 if (j
< RPC_MAX_BACKOFF
)
3302 j
= RPC_MAX_BACKOFF
;
3303 rpc_to_new
.tv_sec
+= j
;
3306 vers_to_try
= versmax
;
3309 * check the host's version within the timeout
3312 trace_prt(1, " ping: %s timeout=%ld request vers=%d min=%d\n",
3313 hostname
, rpc_to_new
.tv_sec
, versmax
, versmin
);
3315 if (usepub
== FALSE
) {
3318 * If NFSv4, then we do the same thing as is used
3319 * for public filehandles so that we avoid rpcbind
3321 if (vers_to_try
== NFS_V4
) {
3323 trace_prt(1, " pingnfs: Trying ping via "
3327 cl
= clnt_create_service_timed(hostname
, "nfs",
3328 NFS_PROGRAM
, vers_to_try
,
3329 port
, "circuit_v", &rpc_to_new
);
3331 outvers
= vers_to_try
;
3336 " pingnfs: Can't ping via "
3337 "\"circuit_v\" %s: RPC error=%d\n",
3338 hostname
, rpc_createerr
.cf_stat
);
3342 cl
= clnt_create_vers_timed(hostname
,
3343 NFS_PROGRAM
, &outvers
, versmin
, vers_to_try
,
3344 "datagram_v", &rpc_to_new
);
3349 " pingnfs: Can't ping via "
3350 "\"datagram_v\"%s: RPC error=%d\n",
3351 hostname
, rpc_createerr
.cf_stat
);
3353 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
3354 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
3356 if (rpc_createerr
.cf_stat
==
3357 RPC_PROGNOTREGISTERED
) {
3360 " pingnfs: Trying ping "
3361 "via \"circuit_v\"\n");
3363 cl
= clnt_create_vers_timed(hostname
,
3364 NFS_PROGRAM
, &outvers
,
3365 versmin
, vers_to_try
,
3366 "circuit_v", &rpc_to_new
);
3371 " pingnfs: Can't ping "
3372 "via \"circuit_v\" %s: "
3375 rpc_createerr
.cf_stat
);
3381 * backoff and return lower version to retry the ping.
3382 * XXX we should be more careful and handle
3383 * RPC_PROGVERSMISMATCH here, because that error is handled
3384 * in clnt_create_vers(). It's not done to stay in sync
3385 * with the nfs mount command.
3388 if (vers_to_try
< versmin
)
3390 if (versp
!= NULL
) { /* recheck the cache */
3391 *versp
= vers_to_try
;
3394 " pingnfs: check cache: vers=%d\n",
3397 switch (cache_check(hostname
, versp
, proto
)) {
3399 if (hostname
!= hostpart
)
3401 return (RPC_SUCCESS
);
3403 if (hostname
!= hostpart
)
3405 return (RPC_TIMEDOUT
);
3412 trace_prt(1, " pingnfs: Try version=%d\n",
3415 } while (cl
== NULL
);
3420 syslog(LOG_ERR
, "pingnfs: %s%s",
3421 hostname
, clnt_spcreateerror(""));
3422 clnt_stat
= rpc_createerr
.cf_stat
;
3425 clnt_stat
= RPC_SUCCESS
;
3429 for (vers_to_try
= versmax
; vers_to_try
>= versmin
;
3435 trace_prt(1, " pingnfs: Try version=%d "
3436 "using get_ping()\n", vers_to_try
);
3439 clnt_stat
= get_ping(hostname
, NFS_PROGRAM
,
3440 vers_to_try
, &nconf
, port
, TRUE
);
3443 freenetconfigent(nconf
);
3445 if (clnt_stat
== RPC_SUCCESS
) {
3446 outvers
= vers_to_try
;
3453 clnt_stat
== RPC_SUCCESS
?
3454 trace_prt(1, " pingnfs OK: nfs version=%d\n", outvers
):
3455 trace_prt(1, " pingnfs FAIL: can't get nfs version\n");
3457 if (clnt_stat
== RPC_SUCCESS
) {
3458 cache_enter(hostname
, versmax
, outvers
, proto
, GOODHOST
);
3462 cache_enter(hostname
, versmax
, versmax
, proto
, DEADHOST
);
3464 if (hostpart
!= hostname
)
3470 #define MNTTYPE_LOFS "lofs"
3473 loopbackmount(fsname
, dir
, mntopts
, overlay
)
3474 char *fsname
; /* Directory being mounted */
3475 char *dir
; /* Directory being mounted on */
3481 char fstype
[] = MNTTYPE_LOFS
;
3484 char optbuf
[MAX_MNTOPT_STR
];
3486 dirlen
= strlen(dir
);
3487 if (dir
[dirlen
-1] == ' ')
3490 if (dirlen
== strlen(fsname
) &&
3491 strncmp(fsname
, dir
, dirlen
) == 0) {
3493 "Mount of %s on %s would result in deadlock, aborted\n",
3497 mnt
.mnt_mntopts
= mntopts
;
3498 if (hasmntopt(&mnt
, MNTOPT_RO
) != NULL
)
3501 (void) strlcpy(optbuf
, mntopts
, sizeof (optbuf
));
3504 flags
|= MS_OVERLAY
;
3508 " loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3509 fsname
, dir
, flags
);
3511 if (is_system_labeled()) {
3512 if (create_homedir((const char *)fsname
,
3513 (const char *)dir
) == 0) {
3514 return (NFSERR_NOENT
);
3518 if (mount(fsname
, dir
, flags
| MS_DATA
| MS_OPTIONSTR
, fstype
,
3519 NULL
, 0, optbuf
, sizeof (optbuf
)) < 0) {
3520 syslog(LOG_ERR
, "Mount of %s on %s: %m", fsname
, dir
);
3524 if (stat(dir
, &st
) == 0) {
3527 " loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3528 fsname
, dir
, st
.st_dev
, st
.st_rdev
);
3533 " loopbackmount of %s on %s OK\n", fsname
, dir
);
3534 trace_prt(1, " stat of %s failed\n", dir
);
3542 * Look for the value of a numeric option of the form foo=x. If found, set
3543 * *valp to the value and return non-zero. If not found or the option is
3544 * malformed, return zero.
3548 nopt(mnt
, opt
, valp
)
3551 int *valp
; /* OUT */
3557 * We should never get a null pointer, but if we do, it's better to
3558 * ignore the option than to dump core.
3562 syslog(LOG_DEBUG
, "null pointer for %s option", opt
);
3566 if (str
= hasmntopt(mnt
, opt
)) {
3567 if (equal
= strchr(str
, '=')) {
3568 *valp
= atoi(&equal
[1]);
3571 syslog(LOG_ERR
, "Bad numeric option '%s'", str
);
3581 struct timeval timeout
;
3583 enum clnt_stat rpc_stat
;
3585 struct replica
*list
;
3587 int isv4mount
= is_v4_mount(mnt
->mnt_mountp
);
3590 trace_prt(1, " nfsunmount: umount %s\n", mnt
->mnt_mountp
);
3592 if (umount(mnt
->mnt_mountp
) < 0) {
3594 trace_prt(1, " nfsunmount: umount %s FAILED\n",
3601 * If this is a NFSv4 mount, the mount protocol was not used
3602 * so we just return.
3606 trace_prt(1, " nfsunmount: umount %s OK\n",
3612 * If mounted with -o public, then no need to contact server
3613 * because mount protocol was not used.
3615 if (hasmntopt(mnt
, MNTOPT_PUBLIC
) != NULL
) {
3620 * The rest of this code is advisory to the server.
3621 * If it fails return success anyway.
3624 list
= parse_replica(mnt
->mnt_special
, &count
);
3628 "Memory allocation failed: %m");
3632 for (i
= 0; i
< count
; i
++) {
3634 host
= list
[i
].host
;
3635 path
= list
[i
].path
;
3638 * Skip file systems mounted using WebNFS, because mount
3639 * protocol was not used.
3641 if (strcmp(host
, "nfs") == 0 && strncmp(path
, "//", 2) == 0)
3644 cl
= clnt_create(host
, MOUNTPROG
, MOUNTVERS
, "datagram_v");
3648 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
3649 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3650 __FILE__
, __LINE__
);
3652 if (__clnt_bindresvport(cl
) < 0) {
3654 syslog(LOG_ERR
, "umount %s:%s: %s",
3656 "Couldn't bind to reserved port");
3657 destroy_auth_client_handle(cl
);
3661 drop_alloc("AUTH_HANDLE", cl
->cl_auth
, __FILE__
, __LINE__
);
3663 AUTH_DESTROY(cl
->cl_auth
);
3664 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
3666 syslog(LOG_ERR
, "umount %s:%s: %s",
3668 "Failed creating default auth handle");
3669 destroy_auth_client_handle(cl
);
3673 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0, __FILE__
, __LINE__
);
3675 timeout
.tv_usec
= 0;
3677 rpc_stat
= clnt_call(cl
, MOUNTPROC_UMNT
, xdr_dirpath
,
3678 (caddr_t
)&path
, xdr_void
, (char *)NULL
, timeout
);
3679 if (verbose
&& rpc_stat
!= RPC_SUCCESS
)
3680 syslog(LOG_ERR
, "%s: %s",
3681 host
, clnt_sperror(cl
, "unmount"));
3682 destroy_auth_client_handle(cl
);
3685 free_replica(list
, count
);
3688 trace_prt(1, " nfsunmount: umount %s OK\n", mnt
->mnt_mountp
);
3695 * Put a new entry in the cache chain by prepending it to the front.
3696 * If there isn't enough memory then just give up.
3699 cache_enter(host
, reqvers
, outvers
, proto
, state
)
3706 struct cache_entry
*entry
;
3707 int cache_time
= 30; /* sec */
3709 timenow
= time(NULL
);
3711 entry
= (struct cache_entry
*)malloc(sizeof (struct cache_entry
));
3714 (void) memset((caddr_t
)entry
, 0, sizeof (struct cache_entry
));
3715 entry
->cache_host
= strdup(host
);
3716 if (entry
->cache_host
== NULL
) {
3720 entry
->cache_reqvers
= reqvers
;
3721 entry
->cache_outvers
= outvers
;
3722 entry
->cache_proto
= (proto
== NULL
? NULL
: strdup(proto
));
3723 entry
->cache_state
= state
;
3724 entry
->cache_time
= timenow
+ cache_time
;
3725 (void) rw_wrlock(&cache_lock
);
3727 host_cache_accesses
++; /* up host cache access counter */
3728 #endif /* CACHE DEBUG */
3729 entry
->cache_next
= cache_head
;
3731 (void) rw_unlock(&cache_lock
);
3735 cache_check(host
, versp
, proto
)
3741 struct cache_entry
*ce
, *prev
;
3743 timenow
= time(NULL
);
3745 (void) rw_rdlock(&cache_lock
);
3748 /* Increment the lookup and access counters for the host cache */
3749 host_cache_accesses
++;
3750 host_cache_lookups
++;
3751 if ((host_cache_lookups
%1000) == 0)
3753 #endif /* CACHE DEBUG */
3755 for (ce
= cache_head
; ce
; ce
= ce
->cache_next
) {
3756 if (timenow
> ce
->cache_time
) {
3757 (void) rw_unlock(&cache_lock
);
3758 (void) rw_wrlock(&cache_lock
);
3759 for (prev
= NULL
, ce
= cache_head
; ce
;
3760 prev
= ce
, ce
= ce
->cache_next
) {
3761 if (timenow
> ce
->cache_time
) {
3764 prev
->cache_next
= NULL
;
3770 (void) rw_unlock(&cache_lock
);
3773 if (strcmp(host
, ce
->cache_host
) != 0)
3775 if ((proto
== NULL
&& ce
->cache_proto
!= NULL
) ||
3776 (proto
!= NULL
&& ce
->cache_proto
== NULL
))
3778 if (proto
!= NULL
&&
3779 strcmp(proto
, ce
->cache_proto
) != 0)
3782 if (versp
== NULL
||
3783 (versp
!= NULL
&& *versp
== ce
->cache_reqvers
) ||
3784 (versp
!= NULL
&& *versp
== ce
->cache_outvers
)) {
3786 *versp
= ce
->cache_outvers
;
3787 state
= ce
->cache_state
;
3789 /* increment the host cache hit counters */
3791 if (state
== GOODHOST
)
3792 goodhost_cache_hits
++;
3793 if (state
== DEADHOST
)
3794 deadhost_cache_hits
++;
3795 #endif /* CACHE_DEBUG */
3796 (void) rw_unlock(&cache_lock
);
3800 (void) rw_unlock(&cache_lock
);
3805 * Free a cache entry and all entries
3806 * further down the chain since they
3807 * will also be expired.
3811 struct cache_entry
*entry
;
3813 struct cache_entry
*ce
, *next
= NULL
;
3815 for (ce
= entry
; ce
; ce
= next
) {
3817 free(ce
->cache_host
);
3818 if (ce
->cache_proto
)
3819 free(ce
->cache_proto
);
3820 next
= ce
->cache_next
;
3829 (void) rw_wrlock(&cache_lock
);
3830 cache_free(cache_head
);
3832 (void) rw_unlock(&cache_lock
);
3838 mutex_lock(&cleanup_lock
);
3839 cond_signal(&cleanup_start_cv
);
3840 (void) cond_wait(&cleanup_done_cv
, &cleanup_lock
);
3841 mutex_unlock(&cleanup_lock
);
3843 portmap_cache_flush();
3848 * Returns 1, if port option is NFS_PORT or
3849 * nfsd is running on the port given
3850 * Returns 0, if both port is not NFS_PORT and nfsd is not
3851 * running on the port.
3855 is_nfs_port(char *opts
)
3858 uint_t nfs_port
= 0;
3863 m
.mnt_mntopts
= opts
;
3866 * Get port specified in options list, if any.
3868 got_port
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
3871 * if no port specified or it is same as NFS_PORT return nfs
3872 * To use any other daemon the port number should be different
3874 if (!got_port
|| nfs_port
== NFS_PORT
)
3877 * If daemon is nfsd, return nfs
3879 if (getservbyport_r(nfs_port
, NULL
, &sv
, buf
, 256) == &sv
&&
3880 strcmp(sv
.s_name
, "nfsd") == 0)
3891 * destroy_auth_client_handle(cl)
3892 * destroys the created client handle
3895 destroy_auth_client_handle(CLIENT
*cl
)
3900 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3901 __FILE__
, __LINE__
);
3903 AUTH_DESTROY(cl
->cl_auth
);
3907 drop_alloc("CLNT_HANDLE", cl
,
3908 __FILE__
, __LINE__
);
3916 * Attempt to figure out which version of NFS to use in pingnfs(). If
3917 * the version number was specified (i.e., non-zero), then use it.
3918 * Otherwise, default to the compiled-in default or the default as set
3919 * by the /etc/default/nfs configuration (as read by read_default().
3922 set_versrange(rpcvers_t nfsvers
, rpcvers_t
*vers
, rpcvers_t
*versmin
)
3926 *vers
= vers_max_default
;
3927 *versmin
= vers_min_default
;
3938 *vers
= NFS_VERSION
; /* version 2 */
3939 *versmin
= NFS_VERSMIN
; /* version 2 */
3949 * trace_portmap_cache()
3950 * traces the portmap cache values at desired points
3953 trace_portmap_cache()
3955 syslog(LOG_ERR
, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
3956 portmap_cache_accesses
, portmap_cache_lookups
,
3957 portmap_cache_hits
);
3961 * trace_host_cache()
3962 * traces the host cache values at desired points
3968 "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
3969 host_cache_accesses
, host_cache_lookups
, deadhost_cache_hits
,
3970 goodhost_cache_hits
);
3972 #endif /* CACHE_DEBUG */
3975 * Read the NFS SMF properties to determine if the
3976 * client has been configured for a new min/max for the NFS version to
3980 #define SVC_NFS_CLIENT "svc:/network/nfs/client"
3983 read_default_nfs(void)
3985 static time_t lastread
= 0;
3992 ret
= nfs_smf_get_prop("client_versmin", defval
, DEFAULT_INSTANCE
,
3993 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
3996 tmp
= strtol(defval
, (char **)NULL
, 10);
3998 vers_min_default
= tmp
;
4003 ret
= nfs_smf_get_prop("client_versmax", defval
, DEFAULT_INSTANCE
,
4004 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
4007 tmp
= strtol(defval
, (char **)NULL
, 10);
4009 vers_max_default
= tmp
;
4013 lastread
= buf
.st_mtime
;
4016 * Quick sanity check on the values picked up from the
4017 * defaults file. Make sure that a mistake wasn't
4018 * made that will confuse things later on.
4019 * If so, reset to compiled-in defaults
4021 if (vers_min_default
> vers_max_default
||
4022 vers_min_default
< NFS_VERSMIN
||
4023 vers_max_default
> NFS_VERSMAX
) {
4026 " read_default: version minimum/maximum incorrectly configured\n");
4028 " read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
4029 vers_min_default
, vers_max_default
,
4030 NFS_VERSMIN_DEFAULT
,
4031 NFS_VERSMAX_DEFAULT
);
4033 vers_min_default
= NFS_VERSMIN_DEFAULT
;
4034 vers_max_default
= NFS_VERSMAX_DEFAULT
;
4039 * Find the mnttab entry that corresponds to "name".
4040 * We're not sure what the name represents: either
4041 * a mountpoint name, or a special name (server:/path).
4042 * Return the last entry in the file that matches.
4044 static struct extmnttab
*
4045 mnttab_find(dirname
)
4049 struct extmnttab mnt
;
4050 struct extmnttab
*res
= NULL
;
4052 fp
= fopen(MNTTAB
, "r");
4055 trace_prt(1, " mnttab_find: unable to open mnttab\n");
4058 while (getextmntent(fp
, &mnt
, sizeof (struct extmnttab
)) == 0) {
4059 if (strcmp(mnt
.mnt_mountp
, dirname
) == 0 ||
4060 strcmp(mnt
.mnt_special
, dirname
) == 0) {
4063 res
= fsdupmnttab(&mnt
);
4071 trace_prt(1, " mnttab_find: unable to find %s\n",
4078 * This function's behavior is taken from nfsstat.
4079 * Trying to determine what NFS version was used for the mount.
4082 is_v4_mount(char *mntpath
)
4084 kstat_ctl_t
*kc
= NULL
; /* libkstat cookie */
4087 struct mntinfo_kstat mik
;
4088 struct extmnttab
*mntp
;
4091 if ((mntp
= mnttab_find(mntpath
)) == NULL
)
4094 /* save the minor number and free the struct so we don't forget */
4095 mnt_minor
= mntp
->mnt_minor
;
4098 if ((kc
= kstat_open()) == NULL
)
4101 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
4102 if (ksp
->ks_type
!= KSTAT_TYPE_RAW
)
4104 if (strcmp(ksp
->ks_module
, "nfs") != 0)
4106 if (strcmp(ksp
->ks_name
, "mntinfo") != 0)
4108 if (mnt_minor
!= ksp
->ks_instance
)
4111 if (kstat_read(kc
, ksp
, &mik
) == -1)
4114 (void) kstat_close(kc
);
4115 if (mik
.mik_vers
== 4)
4120 (void) kstat_close(kc
);
4126 create_homedir(const char *src
, const char *dst
) {
4130 struct passwd
*pwd
, pwds
;
4131 char buf_pwd
[NSS_BUFLEN_PASSWD
];
4137 trace_prt(1, "entered create_homedir\n");
4139 if (stat(src
, &stbuf
) == 0) {
4141 trace_prt(1, "src exists\n");
4145 dst_username
= strrchr(dst
, '/');
4147 dst_username
++; /* Skip over slash */
4148 pwd
= getpwnam_r(dst_username
, &pwds
, buf_pwd
,
4157 homedir_len
= strlen(pwd
->pw_dir
);
4158 dst_dir_len
= strlen(dst
) - homedir_len
;
4159 src_dir_len
= strlen(src
) - homedir_len
;
4161 /* Check that the paths are in the same zone */
4162 if (src_dir_len
< dst_dir_len
||
4163 (strncmp(dst
, src
, dst_dir_len
) != 0)) {
4165 trace_prt(1, " paths don't match\n");
4168 /* Check that mountpoint is an auto_home entry */
4169 if (dst_dir_len
< 0 ||
4170 (strcmp(pwd
->pw_dir
, dst
+ dst_dir_len
) != 0)) {
4174 /* Check that source is an home directory entry */
4175 if (src_dir_len
< 0 ||
4176 (strcmp(pwd
->pw_dir
, src
+ src_dir_len
) != 0)) {
4178 trace_prt(1, " homedir (2) doesn't match %s\n",
4184 S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
) == -1) {
4186 trace_prt(1, " Couldn't mkdir %s\n", src
);
4191 if (chown(src
, pwd
->pw_uid
, pwd
->pw_gid
) == -1) {
4196 /* Created new home directory for the user */
4201 free_nfs_args(struct nfs_args
*argp
)
4203 struct nfs_args
*oldp
;
4206 free(argp
->pathconf
);
4208 free_knconf(argp
->knconf
);
4210 netbuf_free(argp
->addr
);
4212 netbuf_free(argp
->syncaddr
);
4214 free(argp
->netname
);
4216 free(argp
->hostname
);
4217 if (argp
->nfs_ext_u
.nfs_extB
.secdata
)
4218 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
4221 if (argp
->nfs_ext_u
.nfs_extA
.secdata
) {
4223 sd
= argp
->nfs_ext_u
.nfs_extA
.secdata
;
4226 switch (sd
->rpcflavor
) {
4233 dh_k4_clntdata_t
*dhk4
;
4234 dhk4
= (dh_k4_clntdata_t
*)sd
->data
;
4237 if (dhk4
->syncaddr
.buf
)
4238 free(dhk4
->syncaddr
.buf
);
4239 if (dhk4
->knconf
->knc_protofmly
)
4240 free(dhk4
->knconf
->knc_protofmly
);
4241 if (dhk4
->knconf
->knc_proto
)
4242 free(dhk4
->knconf
->knc_proto
);
4246 free(dhk4
->netname
);
4252 gss_clntdata_t
*gss
;
4253 gss
= (gss_clntdata_t
*)sd
->data
;
4256 if (gss
->mechanism
.elements
)
4257 free(gss
->mechanism
.elements
);
4264 if (argp
->nfs_args_ext
== NFS_ARGS_EXTB
)
4265 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
4273 get_netconfig_info(enum type_of_stuff type_of_stuff
, char *hostname
,
4274 rpcprog_t prog
, rpcvers_t vers
, struct netconfig
*nconf
,
4275 ushort_t port
, struct t_info
*tinfo
, struct t_bind
*tbind
,
4276 caddr_t
*fhp
, bool_t direct_to_server
, char *fspath
,
4277 enum clnt_stat
*cstat
, mfs_snego_t
*mfssnego
)
4279 struct netconfig
*nb
= NULL
;
4280 int ping_server
= 0;
4286 switch (type_of_stuff
) {
4288 nb
= get_server_fh(hostname
, prog
, vers
, mfssnego
,
4289 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4295 nb
= get_server_addrorping(hostname
, prog
, vers
,
4296 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4297 fspath
, cstat
, ping_server
);
4306 * Get the server address or can we ping it or not.
4307 * Check the portmap cache first for server address.
4308 * If no entries there, ping the server with a NULLPROC rpc.
4311 get_server_addrorping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
4312 struct netconfig
*nconf
, ushort_t port
, struct t_info
*tinfo
,
4313 struct t_bind
*tbind
, caddr_t
*fhp
, bool_t direct_to_server
,
4314 char *fspath
, enum clnt_stat
*cstat
, int ping_server
)
4317 enum clnt_stat cs
= RPC_TIMEDOUT
;
4318 struct netbuf
*nb
= NULL
;
4322 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
4323 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
4326 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0) {
4330 /* LINTED pointer alignment */
4331 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
))
4336 if (direct_to_server
!= TRUE
) {
4338 if (get_cached_srv_addr(hostname
, prog
, vers
,
4339 nconf
, &tbind
->addr
) == 0)
4346 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
,
4347 fd
, direct_to_server
, port
, prog
, vers
, 0) < 0)
4350 if (port
|| (direct_to_server
== TRUE
)) {
4353 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
,
4358 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
4360 if (cs
!= RPC_SUCCESS
) {
4361 syslog(LOG_ERR
, "error is %d", cs
);
4366 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
4368 syslog(LOG_ERR
, "no memory\n");
4371 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
4372 if (nb
->buf
== NULL
) {
4373 syslog(LOG_ERR
, "no memory\n");
4378 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
4379 nb
->len
= tbind
->addr
.len
;
4380 nb
->maxlen
= tbind
->addr
.maxlen
;
4384 destroy_auth_client_handle(cl
);
4385 cleanup_tli_parms(tbind
, fd
);