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();
100 #define MNTTYPE_CACHEFS "cachefs"
109 #define NFS_ARGS_EXTB_secdata(args, secdata) \
110 { (args).nfs_args_ext = NFS_ARGS_EXTB, \
111 (args).nfs_ext_u.nfs_extB.secdata = secdata; }
114 struct cache_entry
*cache_next
;
118 rpcvers_t cache_reqvers
;
119 rpcvers_t cache_outvers
;
129 typedef struct mfs_snego_t mfs_snego_t
;
131 static struct cache_entry
*cache_head
= NULL
;
132 rwlock_t cache_lock
; /* protect the cache chain */
134 static enum nfsstat
nfsmount(struct mapfs
*, char *, char *, int, int, uid_t
,
136 static int is_nfs_port(char *);
138 static void netbuf_free(struct netbuf
*);
139 static int get_pathconf(CLIENT
*, char *, char *, struct pathcnf
**, int);
140 static struct mapfs
*enum_servers(struct mapent
*, char *);
141 static struct mapfs
*get_mysubnet_servers(struct mapfs
*);
142 static int subnet_test(int af
, struct sioc_addrreq
*);
143 static struct netbuf
*get_addr(char *, rpcprog_t
, rpcvers_t
,
144 struct netconfig
**, char *, ushort_t
, struct t_info
*);
146 static struct netbuf
*get_pubfh(char *, rpcvers_t
, mfs_snego_t
*,
147 struct netconfig
**, char *, ushort_t
, struct t_info
*, caddr_t
*,
150 static int create_homedir(const char *, const char *);
158 static void *get_server_netinfo(enum type_of_stuff
, char *, rpcprog_t
,
159 rpcvers_t
, mfs_snego_t
*, struct netconfig
**, char *, ushort_t
,
160 struct t_info
*, caddr_t
*, bool_t
, char *, enum clnt_stat
*);
161 static void *get_netconfig_info(enum type_of_stuff
, char *, rpcprog_t
,
162 rpcvers_t
, struct netconfig
*, ushort_t
, struct t_info
*,
163 struct t_bind
*, caddr_t
*, bool_t
, char *, enum clnt_stat
*,
165 static void *get_server_addrorping(char *, rpcprog_t
, rpcvers_t
,
166 struct netconfig
*, ushort_t
, struct t_info
*, struct t_bind
*,
167 caddr_t
*, bool_t
, char *, enum clnt_stat
*, int);
168 static void *get_server_fh(char *, rpcprog_t
, rpcvers_t
, mfs_snego_t
*,
169 struct netconfig
*, ushort_t
, struct t_info
*, struct t_bind
*,
170 caddr_t
*, bool_t
, char *, enum clnt_stat
*);
172 struct mapfs
*add_mfs(struct mapfs
*, int, struct mapfs
**, struct mapfs
**);
173 void free_mfs(struct mapfs
*);
174 static void dump_mfs(struct mapfs
*, char *, int);
175 static char *dump_distance(struct mapfs
*);
176 static void cache_free(struct cache_entry
*);
177 static int cache_check(char *, rpcvers_t
*, char *);
178 static void cache_enter(char *, rpcvers_t
, rpcvers_t
, char *, int);
179 void destroy_auth_client_handle(CLIENT
*cl
);
182 static void trace_host_cache();
183 static void trace_portmap_cache();
184 #endif /* CACHE_DEBUG */
186 static int rpc_timeout
= 20;
190 * host cache counters. These variables do not need to be protected
191 * by mutex's. They have been added to measure the utility of the
192 * goodhost/deadhost cache in the lazy hierarchical mounting scheme.
194 static int host_cache_accesses
= 0;
195 static int host_cache_lookups
= 0;
196 static int deadhost_cache_hits
= 0;
197 static int goodhost_cache_hits
= 0;
200 * portmap cache counters. These variables do not need to be protected
201 * by mutex's. They have been added to measure the utility of the portmap
202 * cache in the lazy hierarchical mounting scheme.
204 static int portmap_cache_accesses
= 0;
205 static int portmap_cache_lookups
= 0;
206 static int portmap_cache_hits
= 0;
207 #endif /* CACHE_DEBUG */
210 * There are the defaults (range) for the client when determining
211 * which NFS version to use when probing the server (see above).
212 * These will only be used when the vers mount option is not used and
213 * these may be reset if /etc/default/nfs is configured to do so.
215 static rpcvers_t vers_max_default
= NFS_VERSMAX_DEFAULT
;
216 static rpcvers_t vers_min_default
= NFS_VERSMIN_DEFAULT
;
219 * list of support services needed
221 static char *service_list
[] = { STATD
, LOCKD
, NULL
};
222 static char *service_list_v4
[] = { STATD
, LOCKD
, NFS4CBD
, NFSMAPID
, NULL
};
224 static void read_default_nfs(void);
225 static int is_v4_mount(char *);
226 static void start_nfs4cbd(void);
237 struct mapfs
*mfs
, *mp
;
248 mfs
= enum_servers(me
, prevhost
);
253 * Try loopback if we have something on localhost; if nothing
254 * works, we will fall back to NFS
256 if (is_nfs_port(me
->map_mntopts
)) {
257 for (mp
= mfs
; mp
; mp
= mp
->mfs_next
) {
258 if (self_check(mp
->mfs_host
)) {
259 err
= loopbackmount(mp
->mfs_dir
,
260 mntpnt
, me
->map_mntopts
, overlay
);
265 * Free action_list if there
266 * is one as it is not needed.
267 * Make sure to set alpp to null
268 * so caller doesn't try to free it
281 cached
= strcmp(me
->map_mounter
, MNTTYPE_CACHEFS
) == 0;
282 dir
= strdup(mfs
->mfs_dir
);
283 err
= nfsmount(mfs
, mntpnt
, me
->map_mntopts
,
284 cached
, overlay
, uid
, alp
);
285 if (err
&& trace
> 1) {
286 trace_prt(1, " Couldn't mount %s:%s, err=%d\n",
287 mfs
->mfs_host
? mfs
->mfs_host
: "",
288 mfs
->mfs_dir
? mfs
->mfs_dir
: dir
, err
);
298 * Using the new ioctl SIOCTONLINK to determine if a host is on the same
299 * subnet. Remove the old network, subnet check.
302 static struct mapfs
*
303 get_mysubnet_servers(struct mapfs
*mfs_in
)
306 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
308 struct netconfig
*nconf
;
309 NCONF_HANDLE
*nc
= NULL
;
310 struct nd_hostserv hs
;
311 struct nd_addrlist
*retaddrs
;
313 struct sioc_addrreq areq
;
319 hs
.h_serv
= "rpcbind";
321 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
324 while (nconf
= getnetconfig(nc
)) {
327 * Care about INET family only. proto_done flag
328 * indicates if we have already covered this
329 * protocol family. If so skip it
331 if (((strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0) ||
332 (strcmp(nconf
->nc_protofmly
, NC_INET
) == 0)) &&
333 (nconf
->nc_semantics
== NC_TPI_CLTS
)) {
337 hs
.h_host
= mfs
->mfs_host
;
339 if (netdir_getbyname(nconf
, &hs
, &retaddrs
) != ND_OK
)
343 * For each host address see if it's on our
347 if (strcmp(nconf
->nc_protofmly
, NC_INET6
) == 0)
351 nb
= retaddrs
->n_addrs
;
352 for (i
= 0; i
< retaddrs
->n_cnt
; i
++, nb
++) {
353 memset(&areq
.sa_addr
, 0, sizeof (areq
.sa_addr
));
354 memcpy(&areq
.sa_addr
, nb
->buf
, MIN(nb
->len
,
355 sizeof (areq
.sa_addr
)));
356 if (res
= subnet_test(af
, &areq
)) {
357 p
= add_mfs(mfs
, DIST_MYNET
,
358 &mfs_head
, &mfs_tail
);
360 netdir_free(retaddrs
,
367 } /* end of every host */
369 trace_prt(1, "get_mysubnet_servers: host=%s "
370 "netid=%s res=%s\n", mfs
->mfs_host
,
371 nconf
->nc_netid
, res
== 1?"SUC":"FAIL");
374 netdir_free(retaddrs
, ND_ADDRLIST
);
379 } /* end of every map */
386 subnet_test(int af
, struct sioc_addrreq
*areq
)
390 if ((s
= socket(af
, SOCK_DGRAM
, 0)) < 0) {
396 if (ioctl(s
, SIOCTONLINK
, (caddr_t
)areq
) < 0) {
397 syslog(LOG_ERR
, "subnet_test:SIOCTONLINK failed");
401 if (areq
->sa_res
== 1)
410 * ping a bunch of hosts at once and sort by who responds first
412 static struct mapfs
*
413 sort_servers(struct mapfs
*mfs_in
, int timeout
)
415 struct mapfs
*m1
= NULL
;
416 enum clnt_stat clnt_stat
;
421 clnt_stat
= nfs_cast(mfs_in
, &m1
, timeout
);
424 char buff
[2048] = {'\0'};
426 for (m1
= mfs_in
; m1
; m1
= m1
->mfs_next
) {
427 (void) strcat(buff
, m1
->mfs_host
);
429 (void) strcat(buff
, ",");
432 syslog(LOG_ERR
, "servers %s not responding: %s",
433 buff
, clnt_sperrno(clnt_stat
));
440 * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
441 * provided it is not marked "ignored" and isn't a dupe of ones we've
445 add_mfs(struct mapfs
*mfs
, int distance
, struct mapfs
**mfs_head
,
446 struct mapfs
**mfs_tail
)
448 struct mapfs
*tmp
, *new;
450 for (tmp
= *mfs_head
; tmp
; tmp
= tmp
->mfs_next
)
451 if ((strcmp(tmp
->mfs_host
, mfs
->mfs_host
) == 0 &&
452 strcmp(tmp
->mfs_dir
, mfs
->mfs_dir
) == 0) ||
455 new = (struct mapfs
*)malloc(sizeof (struct mapfs
));
457 syslog(LOG_ERR
, "Memory allocation failed: %m");
460 bcopy(mfs
, new, sizeof (struct mapfs
));
461 new->mfs_next
= NULL
;
463 new->mfs_distance
= distance
;
465 *mfs_tail
= *mfs_head
= new;
467 (*mfs_tail
)->mfs_next
= new;
474 dump_mfs(struct mapfs
*mfs
, char *message
, int level
)
481 trace_prt(1, "%s", message
);
483 trace_prt(0, "mfs is null\n");
486 for (m1
= mfs
; m1
; m1
= m1
->mfs_next
)
487 trace_prt(0, "%s[%s] ", m1
->mfs_host
, dump_distance(m1
));
492 dump_distance(struct mapfs
*mfs
)
494 switch (mfs
->mfs_distance
) {
495 case 0: return ("zero");
496 case DIST_SELF
: return ("self");
497 case DIST_MYSUB
: return ("mysub");
498 case DIST_MYNET
: return ("mynet");
499 case DIST_OTHER
: return ("other");
500 default: return ("other");
505 * Walk linked list "raw", building a new list consisting of members
506 * NOT found in list "filter", returning the result.
508 static struct mapfs
*
509 filter_mfs(struct mapfs
*raw
, struct mapfs
*filter
)
511 struct mapfs
*mfs
, *p
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
516 for (mfs
= raw
; mfs
; mfs
= mfs
->mfs_next
) {
517 for (skip
= 0, p
= filter
; p
; p
= p
->mfs_next
) {
518 if (strcmp(p
->mfs_host
, mfs
->mfs_host
) == 0 &&
519 strcmp(p
->mfs_dir
, mfs
->mfs_dir
) == 0) {
526 p
= add_mfs(mfs
, 0, &mfs_head
, &mfs_tail
);
534 * Walk a linked list of mapfs structs, freeing each member.
537 free_mfs(struct mapfs
*mfs
)
549 * New code for NFS client failover: we need to carry and sort
550 * lists of server possibilities rather than return a single
551 * entry. It preserves previous behaviour of sorting first by
552 * locality (loopback-or-preferred/subnet/net/other) and then
553 * by ping times. We'll short-circuit this process when we
554 * have ENOUGH or more entries.
556 static struct mapfs
*
557 enum_servers(struct mapent
*me
, char *preferred
)
559 struct mapfs
*p
, *m1
, *m2
, *mfs_head
= NULL
, *mfs_tail
= NULL
;
562 * Short-circuit for simple cases.
564 if (!me
->map_fs
->mfs_next
) {
565 p
= add_mfs(me
->map_fs
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
571 dump_mfs(me
->map_fs
, " enum_servers: mapent: ", 2);
574 * get addresses & see if any are myself
575 * or were mounted from previously in a
576 * hierarchical mount.
579 trace_prt(1, " enum_servers: looking for pref/self\n");
580 for (m1
= me
->map_fs
; m1
; m1
= m1
->mfs_next
) {
583 if (self_check(m1
->mfs_host
) ||
584 strcmp(m1
->mfs_host
, preferred
) == 0) {
585 p
= add_mfs(m1
, DIST_SELF
, &mfs_head
, &mfs_tail
);
591 trace_prt(1, " enum_servers: pref/self found, %s\n",
595 * look for entries on this subnet
597 dump_mfs(m1
, " enum_servers: input of get_mysubnet_servers: ", 2);
598 m1
= get_mysubnet_servers(me
->map_fs
);
599 dump_mfs(m1
, " enum_servers: output of get_mysubnet_servers: ", 3);
600 if (m1
&& m1
->mfs_next
) {
601 m2
= sort_servers(m1
, rpc_timeout
/ 2);
602 dump_mfs(m2
, " enum_servers: output of sort_servers: ", 3);
607 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
608 p
= add_mfs(m2
, 0, &mfs_head
, &mfs_tail
);
616 * add the rest of the entries at the end
618 m1
= filter_mfs(me
->map_fs
, mfs_head
);
619 dump_mfs(m1
, " enum_servers: etc: output of filter_mfs: ", 3);
620 m2
= sort_servers(m1
, rpc_timeout
/ 2);
621 dump_mfs(m2
, " enum_servers: etc: output of sort_servers: ", 3);
625 for (m2
= m1
; m2
; m2
= m2
->mfs_next
) {
626 p
= add_mfs(m2
, DIST_OTHER
, &mfs_head
, &mfs_tail
);
634 dump_mfs(mfs_head
, " enum_servers: output: ", 1);
640 struct mapfs
*mfs_in
,
641 char *mntpnt
, char *opts
,
642 int cached
, int overlay
,
647 char remname
[MAXPATHLEN
], *mnttabtext
= NULL
;
648 char mopts
[MAX_MNTOPT_STR
];
649 char netname
[MAXNETNAMELEN
+1];
650 char *mntopts
= NULL
;
654 struct nfs_args
*argp
= NULL
, *head
= NULL
, *tail
= NULL
,
655 *prevhead
, *prevtail
;
658 struct timeval timeout
;
659 enum clnt_stat rpc_stat
;
662 struct netconfig
*nconf
;
663 rpcvers_t vers
, versmin
; /* used to negotiate nfs version in pingnfs */
664 /* and mount version with mountd */
665 rpcvers_t outvers
; /* final version to be used during mount() */
666 rpcvers_t nfsvers
; /* version in map options, 0 if not there */
667 rpcvers_t mountversmax
; /* tracks the max mountvers during retries */
669 /* used to negotiate nfs version using webnfs */
670 rpcvers_t pubvers
, pubversmin
, pubversmax
;
672 struct nd_addrlist
*retaddrs
;
673 struct mountres3 res3
;
677 char scerror_msg
[MAXMSGLEN
];
681 char *nfs_proto
= NULL
;
683 char *p
, *host
, *rhost
, *dir
;
684 struct mapfs
*mfs
= NULL
;
685 int error
, last_error
= 0;
688 int v2cnt
= 0, v3cnt
= 0, v4cnt
= 0;
689 int v2near
= 0, v3near
= 0, v4near
= 0;
693 int sec_opt
, scerror
;
694 struct sec_data
*secdata
;
696 struct netbuf
*syncaddr
;
700 mfs_snego_t mfssnego_init
, mfssnego
;
702 dump_mfs(mfs_in
, " nfsmount: input: ", 2);
703 replicated
= (mfs_in
->mfs_next
!= NULL
);
704 m
.mnt_mntopts
= opts
;
705 if (replicated
&& hasmntopt(&m
, MNTOPT_SOFT
)) {
708 "mount on %s is soft and will not be replicated.", mntpnt
);
711 if (replicated
&& !hasmntopt(&m
, MNTOPT_RO
)) {
714 "mount on %s is not read-only and will not be replicated.",
718 if (replicated
&& cached
) {
721 "mount on %s is cached and will not be replicated.",
726 loglevel
= LOG_WARNING
;
732 trace_prt(1, " nfsmount: replicated mount on %s %s:\n",
735 trace_prt(1, " nfsmount: standard mount on %s %s:\n",
737 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
)
738 trace_prt(1, " %s:%s\n",
739 mfs
->mfs_host
, mfs
->mfs_dir
);
743 * Make sure mountpoint is safe to mount on
745 if (lstat(mntpnt
, &stbuf
) < 0) {
746 syslog(LOG_ERR
, "Couldn't stat %s: %m", mntpnt
);
747 return (NFSERR_NOENT
);
751 * Get protocol specified in options list, if any.
753 if ((str_opt(&m
, "proto", &nfs_proto
)) == -1) {
754 return (NFSERR_NOENT
);
758 * Get port specified in options list, if any.
760 got_val
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
762 nfs_port
= 0; /* "unspecified" */
763 if (nfs_port
> USHRT_MAX
) {
764 syslog(LOG_ERR
, "%s: invalid port number %d", mntpnt
, nfs_port
);
765 return (NFSERR_NOENT
);
769 * Set mount(2) flags here, outside of the loop.
771 flags
= MS_OPTIONSTR
;
772 flags
|= (hasmntopt(&m
, MNTOPT_RO
) == NULL
) ? 0 : MS_RDONLY
;
773 flags
|= (hasmntopt(&m
, MNTOPT_NOSUID
) == NULL
) ? 0 : MS_NOSUID
;
774 flags
|= overlay
? MS_OVERLAY
: 0;
775 if (mntpnt
[strlen(mntpnt
) - 1] != ' ')
776 /* direct mount point without offsets */
779 use_pubfh
= (hasmntopt(&m
, MNTOPT_PUBLIC
) == NULL
) ? FALSE
: TRUE
;
781 (void) memset(&mfssnego_init
, 0, sizeof (mfs_snego_t
));
782 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
783 if (++mfssnego_init
.sec_opt
> 1) {
785 "conflicting security options");
788 if (nfs_getseconfig_byname("dh", &mfssnego_init
.nfs_sec
)) {
790 "error getting dh information from %s",
796 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
797 if ((str_opt(&m
, MNTOPT_SEC
,
798 &mfssnego_init
.nfs_flavor
)) == -1) {
799 syslog(LOG_ERR
, "nfsmount: no memory");
804 if (mfssnego_init
.nfs_flavor
) {
805 if (++mfssnego_init
.sec_opt
> 1) {
807 "conflicting security options");
808 free(mfssnego_init
.nfs_flavor
);
811 if (nfs_getseconfig_byname(mfssnego_init
.nfs_flavor
,
812 &mfssnego_init
.nfs_sec
)) {
814 "error getting %s information from %s",
815 mfssnego_init
.nfs_flavor
, NFSSEC_CONF
);
816 free(mfssnego_init
.nfs_flavor
);
819 free(mfssnego_init
.nfs_flavor
);
825 got_val
= nopt(&m
, MNTOPT_VERS
, (int *)&nfsvers
);
827 nfsvers
= 0; /* "unspecified" */
828 if (set_versrange(nfsvers
, &vers
, &versmin
) != 0) {
829 syslog(LOG_ERR
, "Incorrect NFS version specified for %s",
831 last_error
= NFSERR_NOENT
;
836 pubversmax
= pubversmin
= nfsvers
;
839 pubversmin
= versmin
;
843 * Walk the whole list, pinging and collecting version
844 * info so that we can make sure the mount will be
845 * homogeneous with respect to version.
847 * If we have a version preference, this is easy; we'll
848 * just reject anything that doesn't match.
850 * If not, we want to try to provide the best compromise
851 * that considers proximity, preference for a higher version,
852 * sorted order, and number of replicas. We will count
853 * the number of V2 and V3 replicas and also the number
854 * which are "near", i.e. the localhost or on the same
857 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
864 * If the host is '[a:d:d:r:e:s:s'],
865 * only use 'a:d:d:r:e:s:s' for communication
867 host
= strdup(mfs
->mfs_host
);
869 syslog(LOG_ERR
, "nfsmount: no memory");
870 last_error
= NFSERR_IO
;
875 (void) memcpy(&mfssnego
, &mfssnego_init
, sizeof (mfs_snego_t
));
877 if (use_pubfh
== TRUE
|| mfs
->mfs_flags
& MFS_URL
) {
880 if (nfs_port
!= 0 && mfs
->mfs_port
!= 0 &&
881 nfs_port
!= mfs
->mfs_port
) {
883 syslog(LOG_ERR
, "nfsmount: port (%u) in nfs URL"
884 " not the same as port (%d) in port "
885 "option\n", mfs
->mfs_port
, nfs_port
);
886 last_error
= NFSERR_IO
;
889 } else if (nfs_port
!= 0)
892 thisport
= mfs
->mfs_port
;
896 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
897 path
= malloc(strlen(dir
) + 2);
899 syslog(LOG_ERR
, "nfsmount: no memory");
900 last_error
= NFSERR_IO
;
903 path
[0] = (char)WNL_NATIVEPATH
;
904 (void) strcpy(&path
[1], dir
);
909 argp
= (struct nfs_args
*)
910 malloc(sizeof (struct nfs_args
));
915 syslog(LOG_ERR
, "nfsmount: no memory");
916 last_error
= NFSERR_IO
;
919 (void) memset(argp
, 0, sizeof (*argp
));
923 * By now Mount argument struct has been allocated,
924 * either a pub_fh path will be taken or the regular
925 * one. So here if a protocol was specified and it
926 * was not rdma we let it be, else we set DO_RDMA.
927 * If no proto was there we advise on trying RDMA.
930 if (strcmp(nfs_proto
, "rdma") == 0) {
933 argp
->flags
|= NFSMNT_DORDMA
;
936 argp
->flags
|= NFSMNT_TRYRDMA
;
938 for (pubvers
= pubversmax
; pubvers
>= pubversmin
;
942 argp
->addr
= get_pubfh(host
, pubvers
, &mfssnego
,
943 &nconf
, nfs_proto
, thisport
, NULL
,
944 &argp
->fh
, TRUE
, path
);
946 if (argp
->addr
!= NULL
)
950 freenetconfigent(nconf
);
956 if (argp
->addr
!= NULL
) {
959 * The use of llock option for NFSv4
960 * mounts is not required since file
961 * locking is included within the protocol
963 if (pubvers
!= NFS_V4
)
964 argp
->flags
|= NFSMNT_LLOCK
;
966 argp
->flags
|= NFSMNT_PUBLIC
;
969 mfs
->mfs_args
= argp
;
970 mfs
->mfs_version
= pubvers
;
971 mfs
->mfs_nconf
= nconf
;
972 mfs
->mfs_flags
|= MFS_FH_VIA_WEBNFS
;
978 * If -public was specified, give up
981 if (use_pubfh
== TRUE
) {
983 "%s: no public file handle support",
985 last_error
= NFSERR_NOENT
;
991 * Back off to a conventional mount.
993 * URL's can contain escape characters. Get
996 path
= malloc(strlen(dir
) + 2);
999 syslog(LOG_ERR
, "nfsmount: no memory");
1000 last_error
= NFSERR_IO
;
1006 mfs
->mfs_dir
= path
;
1007 mfs
->mfs_flags
|= MFS_ALLOC_DIR
;
1008 mfs
->mfs_flags
&= ~MFS_URL
;
1012 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
1013 i
= pingnfs(host
, get_retry(opts
) + 1, &vers
, versmin
,
1014 0, FALSE
, NULL
, nfs_proto
);
1015 if (i
!= RPC_SUCCESS
) {
1016 if (i
== RPC_PROGVERSMISMATCH
) {
1017 syslog(loglevel
, "server %s: NFS "
1018 "protocol version mismatch",
1021 syslog(loglevel
, "server %s not "
1022 "responding", host
);
1024 mfs
->mfs_ignore
= 1;
1025 last_error
= NFSERR_NOENT
;
1028 if (nfsvers
!= 0 && nfsvers
!= vers
) {
1029 if (nfs_proto
== NULL
)
1032 "not supported by %s",
1038 "not supported by %s",
1039 nfsvers
, nfs_proto
, host
);
1040 mfs
->mfs_ignore
= 1;
1041 last_error
= NFSERR_NOENT
;
1049 case NFS_V4
: v4cnt
++; break;
1050 case NFS_V3
: v3cnt
++; break;
1051 case NFS_VERSION
: v2cnt
++; break;
1056 * It's not clear how useful this stuff is if
1057 * we are using webnfs across the internet, but it
1060 if (mfs
->mfs_distance
&&
1061 mfs
->mfs_distance
<= DIST_MYSUB
) {
1063 case NFS_V4
: v4near
++; break;
1064 case NFS_V3
: v3near
++; break;
1065 case NFS_VERSION
: v2near
++; break;
1071 * If the mount is not replicated, we don't want to
1072 * ping every entry, so we'll stop here. This means
1073 * that we may have to go back to "nextentry" above
1074 * to consider another entry if we can't get
1075 * all the way to mount(2) with this one.
1084 * Choose the NFS version.
1085 * We prefer higher versions, but will choose a one-
1086 * version downgrade in service if we can use a local
1087 * network interface and avoid a router.
1089 if (v4cnt
&& v4cnt
>= v3cnt
&& (v4near
|| !v3near
))
1091 else if (v3cnt
&& v3cnt
>= v2cnt
&& (v3near
|| !v2near
))
1094 nfsvers
= NFS_VERSION
;
1097 " nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1098 v4cnt
, v4near
, v3cnt
, v3near
,
1099 v2cnt
, v2near
, nfsvers
);
1103 * Since we don't support different NFS versions in replicated
1104 * mounts, set fstype now.
1105 * Also take the opportunity to set
1106 * the mount protocol version as appropriate.
1110 fstype
= MNTTYPE_NFS4
;
1113 fstype
= MNTTYPE_NFS3
;
1114 if (use_pubfh
== FALSE
) {
1115 mountversmax
= MOUNTVERS3
;
1116 versmin
= MOUNTVERS3
;
1120 fstype
= MNTTYPE_NFS
;
1121 if (use_pubfh
== FALSE
) {
1122 mountversmax
= MOUNTVERS_POSIX
;
1123 versmin
= MOUNTVERS
;
1129 * Our goal here is to evaluate each of several possible
1130 * replicas and try to come up with a list we can hand
1131 * to mount(2). If we don't have a valid "head" at the
1132 * end of this process, it means we have rejected all
1133 * potential server:/path tuples. We will fail quietly
1134 * in front of mount(2), and will have printed errors
1135 * where we found them.
1136 * XXX - do option work outside loop w careful design
1137 * XXX - use macro for error condition free handling
1139 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
1142 * Initialize retry and delay values on a per-server basis.
1144 retries
= get_retry(opts
);
1147 if (mfs
->mfs_ignore
)
1151 * If we don't have a fh yet, and if this is not a replicated
1152 * mount, we haven't done a pingnfs() on the next entry,
1153 * so we don't know if the next entry is up or if it
1154 * supports an NFS version we like. So if we had a problem
1155 * with an entry, we need to go back and run through some new
1158 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1159 !replicated
&& skipentry
)
1162 vers
= mountversmax
;
1163 host
= mfs
->mfs_host
;
1167 * Remember the possible '[a:d:d:r:e:s:s]' as the address to be
1168 * later passed to mount(2) and used in the mnttab line, but
1169 * only use 'a:d:d:r:e:s:s' for communication
1171 rhost
= strdup(host
);
1172 if (rhost
== NULL
) {
1173 syslog(LOG_ERR
, "nfsmount: no memory");
1174 last_error
= NFSERR_IO
;
1179 (void) sprintf(remname
, "%s:%s", rhost
, dir
);
1180 if (trace
> 4 && replicated
)
1181 trace_prt(1, " nfsmount: examining %s\n", remname
);
1184 * If it's cached we need to get cachefs to mount it.
1190 * If we started with a URL we need to turn on
1191 * -o public if not on already
1193 if (use_pubfh
== FALSE
&&
1194 (mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
)) {
1196 copts
= malloc(strlen(opts
) +
1197 strlen(",public")+1);
1199 if (copts
== NULL
) {
1200 syslog(LOG_ERR
, "nfsmount: no memory");
1201 last_error
= NFSERR_IO
;
1205 strcpy(copts
, opts
);
1207 if (strlen(copts
) != 0)
1210 strcat(copts
, "public");
1213 last_error
= mount_generic(remname
, MNTTYPE_CACHEFS
,
1214 copts
, mntpnt
, overlay
);
1221 mfs
->mfs_ignore
= 1;
1227 if (mfs
->mfs_args
== NULL
) {
1230 * Allocate nfs_args structure
1232 argp
= (struct nfs_args
*)
1233 malloc(sizeof (struct nfs_args
));
1236 syslog(LOG_ERR
, "nfsmount: no memory");
1237 last_error
= NFSERR_IO
;
1241 (void) memset(argp
, 0, sizeof (*argp
));
1245 * By now Mount argument struct has been allocated,
1246 * either a pub_fh path will be taken or the regular
1247 * one. So here if a protocol was specified and it
1248 * was not rdma we let it be, else we set DO_RDMA.
1249 * If no proto was there we advise on trying RDMA.
1252 if (strcmp(nfs_proto
, "rdma") == 0) {
1255 argp
->flags
|= NFSMNT_DORDMA
;
1258 argp
->flags
|= NFSMNT_TRYRDMA
;
1260 argp
= mfs
->mfs_args
;
1261 mfs
->mfs_args
= NULL
;
1264 * Skip entry if we already have file handle but the
1265 * NFS version is wrong.
1267 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) &&
1268 mfs
->mfs_version
!= nfsvers
) {
1272 mfs
->mfs_ignore
= 1;
1282 tail
= tail
->nfs_ext_u
.nfs_extB
.next
= argp
;
1285 * WebNFS and NFSv4 behave similarly in that they
1286 * don't use the mount protocol. Therefore, avoid
1287 * mount protocol like things when version 4 is being
1290 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1291 nfsvers
!= NFS_V4
) {
1292 timeout
.tv_usec
= 0;
1293 timeout
.tv_sec
= rpc_timeout
;
1294 rpc_stat
= RPC_TIMEDOUT
;
1296 /* Create the client handle. */
1300 " nfsmount: Get mount version: request "
1301 "vers=%d min=%d\n", vers
, versmin
);
1304 while ((cl
= clnt_create_vers(host
, MOUNTPROG
, &outvers
,
1305 versmin
, vers
, "udp")) == NULL
) {
1308 " nfsmount: Can't get mount "
1309 "version: rpcerr=%d\n",
1310 rpc_createerr
.cf_stat
);
1312 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
1313 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
1317 * backoff and return lower version to retry the ping.
1318 * XXX we should be more careful and handle
1319 * RPC_PROGVERSMISMATCH here, because that error
1320 * is handled in clnt_create_vers(). It's not done to
1321 * stay in sync with the nfs mount command.
1328 " nfsmount: Try version=%d\n",
1338 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1339 last_error
= NFSERR_NOENT
;
1341 if (rpc_createerr
.cf_stat
!= RPC_UNKNOWNHOST
&&
1342 rpc_createerr
.cf_stat
!=
1343 RPC_PROGVERSMISMATCH
&&
1349 syslog(loglevel
, "%s %s", host
,
1351 "server not responding"));
1353 mfs
->mfs_ignore
= 1;
1358 " nfsmount: mount version=%d\n", outvers
);
1361 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
1362 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1363 __FILE__
, __LINE__
);
1366 if (__clnt_bindresvport(cl
) < 0) {
1371 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1372 last_error
= NFSERR_NOENT
;
1374 if (retries
-- > 0) {
1375 destroy_auth_client_handle(cl
);
1380 syslog(loglevel
, "mount %s: %s", host
,
1381 "Couldn't bind to reserved port");
1382 destroy_auth_client_handle(cl
);
1384 mfs
->mfs_ignore
= 1;
1389 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
1390 __FILE__
, __LINE__
);
1392 AUTH_DESTROY(cl
->cl_auth
);
1393 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
1398 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1399 last_error
= NFSERR_NOENT
;
1401 if (retries
-- > 0) {
1402 destroy_auth_client_handle(cl
);
1407 syslog(loglevel
, "mount %s: %s", host
,
1408 "Failed creating default auth handle");
1409 destroy_auth_client_handle(cl
);
1411 mfs
->mfs_ignore
= 1;
1415 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
1416 __FILE__
, __LINE__
);
1422 * set security options
1425 (void) memset(&nfs_sec
, 0, sizeof (nfs_sec
));
1426 if (hasmntopt(&m
, MNTOPT_SECURE
) != NULL
) {
1427 if (++sec_opt
> 1) {
1429 "conflicting security options for %s",
1435 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1436 last_error
= NFSERR_IO
;
1437 destroy_auth_client_handle(cl
);
1439 mfs
->mfs_ignore
= 1;
1442 if (nfs_getseconfig_byname("dh", &nfs_sec
)) {
1444 "error getting dh information from %s",
1450 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1451 last_error
= NFSERR_IO
;
1452 destroy_auth_client_handle(cl
);
1454 mfs
->mfs_ignore
= 1;
1460 if (hasmntopt(&m
, MNTOPT_SEC
) != NULL
) {
1461 if ((str_opt(&m
, MNTOPT_SEC
, &nfs_flavor
)) == -1) {
1462 syslog(LOG_ERR
, "nfsmount: no memory");
1463 last_error
= NFSERR_IO
;
1464 destroy_auth_client_handle(cl
);
1470 if (++sec_opt
> 1) {
1472 "conflicting security options for %s",
1479 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1480 last_error
= NFSERR_IO
;
1481 destroy_auth_client_handle(cl
);
1483 mfs
->mfs_ignore
= 1;
1486 if (nfs_getseconfig_byname(nfs_flavor
, &nfs_sec
)) {
1488 "error getting %s information from %s",
1489 nfs_flavor
, NFSSEC_CONF
);
1495 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1496 last_error
= NFSERR_IO
;
1497 destroy_auth_client_handle(cl
);
1499 mfs
->mfs_ignore
= 1;
1505 posix
= (nfsvers
!= NFS_V4
&&
1506 hasmntopt(&m
, MNTOPT_POSIX
) != NULL
) ? 1 : 0;
1508 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
1509 nfsvers
!= NFS_V4
) {
1510 bool_t give_up_on_mnt
;
1511 bool_t got_mnt_error
;
1513 * If we started with a URL, if first byte of path is not "/",
1514 * then the mount will likely fail, so we should try again
1515 * with a prepended "/".
1517 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
&& *dir
!= '/')
1518 give_up_on_mnt
= FALSE
;
1520 give_up_on_mnt
= TRUE
;
1522 got_mnt_error
= FALSE
;
1525 if (got_mnt_error
== TRUE
) {
1528 give_up_on_mnt
= TRUE
;
1532 * Insert a "/" to front of mfs_dir.
1534 for (i
= l
; i
> 0; i
--)
1540 /* Get fhandle of remote path from server's mountd */
1549 tail
->nfs_ext_u
.nfs_extB
.next
=
1551 last_error
= NFSERR_NOENT
;
1553 "can't get posix info for %s",
1555 destroy_auth_client_handle(cl
);
1557 mfs
->mfs_ignore
= 1;
1561 case MOUNTVERS_POSIX
:
1562 if (nfsvers
== NFS_V3
) {
1567 tail
->nfs_ext_u
.nfs_extB
.next
=
1569 last_error
= NFSERR_NOENT
;
1571 "%s doesn't support NFS Version 3",
1573 destroy_auth_client_handle(cl
);
1575 mfs
->mfs_ignore
= 1;
1578 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1579 xdr_dirpath
, (caddr_t
)&dir
,
1580 xdr_fhstatus
, (caddr_t
)&fhs
, timeout
);
1581 if (rpc_stat
!= RPC_SUCCESS
) {
1583 if (give_up_on_mnt
== FALSE
) {
1584 got_mnt_error
= TRUE
;
1589 * Given the way "clnt_sperror" works, the "%s"
1590 * immediately following the "not responding"
1597 tail
->nfs_ext_u
.nfs_extB
.next
=
1599 last_error
= NFSERR_NOENT
;
1601 if (retries
-- > 0) {
1602 destroy_auth_client_handle(cl
);
1609 " nfsmount: mount RPC "
1614 "%s server not responding%s",
1615 host
, clnt_sperror(cl
, ""));
1616 destroy_auth_client_handle(cl
);
1618 mfs
->mfs_ignore
= 1;
1621 if ((errno
= fhs
.fhs_status
) != MNT_OK
) {
1623 if (give_up_on_mnt
== FALSE
) {
1624 got_mnt_error
= TRUE
;
1632 tail
->nfs_ext_u
.nfs_extB
.next
=
1634 if (errno
== EACCES
) {
1635 status
= NFSERR_ACCES
;
1637 syslog(loglevel
, "%s: %m",
1643 " nfsmount: mount RPC gave"
1647 last_error
= status
;
1648 destroy_auth_client_handle(cl
);
1650 mfs
->mfs_ignore
= 1;
1653 argp
->fh
= malloc((sizeof (fhandle
)));
1655 syslog(LOG_ERR
, "nfsmount: no memory");
1656 last_error
= NFSERR_IO
;
1657 destroy_auth_client_handle(cl
);
1660 (void) memcpy(argp
->fh
,
1661 &fhs
.fhstatus_u
.fhs_fhandle
,
1666 (void) memset((char *)&res3
, '\0',
1668 rpc_stat
= clnt_call(cl
, MOUNTPROC_MNT
,
1669 xdr_dirpath
, (caddr_t
)&dir
,
1670 xdr_mountres3
, (caddr_t
)&res3
, timeout
);
1671 if (rpc_stat
!= RPC_SUCCESS
) {
1673 if (give_up_on_mnt
== FALSE
) {
1674 got_mnt_error
= TRUE
;
1679 * Given the way "clnt_sperror" works, the "%s"
1680 * immediately following the "not responding"
1687 tail
->nfs_ext_u
.nfs_extB
.next
=
1689 last_error
= NFSERR_NOENT
;
1691 if (retries
-- > 0) {
1692 destroy_auth_client_handle(cl
);
1699 " nfsmount: mount RPC "
1704 "%s server not responding%s",
1705 remname
, clnt_sperror(cl
, ""));
1706 destroy_auth_client_handle(cl
);
1708 mfs
->mfs_ignore
= 1;
1711 if ((errno
= res3
.fhs_status
) != MNT_OK
) {
1713 if (give_up_on_mnt
== FALSE
) {
1714 got_mnt_error
= TRUE
;
1722 tail
->nfs_ext_u
.nfs_extB
.next
=
1724 if (errno
== EACCES
) {
1725 status
= NFSERR_ACCES
;
1727 syslog(loglevel
, "%s: %m",
1733 " nfsmount: mount RPC gave"
1737 last_error
= status
;
1738 destroy_auth_client_handle(cl
);
1740 mfs
->mfs_ignore
= 1;
1745 * Negotiate the security flavor for nfs_mount
1747 auths
= res3
.mountres3_u
.mountinfo
.
1748 auth_flavors
.auth_flavors_val
;
1749 count
= res3
.mountres3_u
.mountinfo
.
1750 auth_flavors
.auth_flavors_len
;
1753 for (i
= 0; i
< count
; i
++)
1755 nfs_sec
.sc_nfsnum
) {
1760 "%s: does not support "
1761 "security \"%s\"\n",
1762 remname
, nfs_sec
.sc_name
);
1763 clnt_freeres(cl
, xdr_mountres3
,
1772 last_error
= NFSERR_IO
;
1773 destroy_auth_client_handle(cl
);
1775 mfs
->mfs_ignore
= 1;
1778 } else if (count
> 0) {
1779 for (i
= 0; i
< count
; i
++) {
1781 nfs_getseconfig_bynumber(
1782 auths
[i
], &nfs_sec
))) {
1788 if (nfs_syslog_scerr(scerror
,
1793 "mounted because it"
1795 "security flavor %d"
1801 clnt_freeres(cl
, xdr_mountres3
,
1810 last_error
= NFSERR_IO
;
1811 destroy_auth_client_handle(cl
);
1813 mfs
->mfs_ignore
= 1;
1819 res3
.mountres3_u
.mountinfo
.fhandle
.
1821 (void) memcpy(fh3
.fh3_u
.data
,
1822 res3
.mountres3_u
.mountinfo
.fhandle
.
1825 clnt_freeres(cl
, xdr_mountres3
,
1827 argp
->fh
= malloc(sizeof (nfs_fh3
));
1829 syslog(LOG_ERR
, "nfsmount: no memory");
1830 last_error
= NFSERR_IO
;
1831 destroy_auth_client_handle(cl
);
1834 (void) memcpy(argp
->fh
, &fh3
, sizeof (nfs_fh3
));
1841 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1842 last_error
= NFSERR_NOENT
;
1844 "unknown MOUNT version %ld on %s",
1846 destroy_auth_client_handle(cl
);
1848 mfs
->mfs_ignore
= 1;
1852 if (nfsvers
== NFS_V4
) {
1853 argp
->fh
= strdup(dir
);
1854 if (argp
->fh
== NULL
) {
1855 syslog(LOG_ERR
, "nfsmount: no memory");
1856 last_error
= NFSERR_IO
;
1862 trace_prt(1, " nfsmount: have %s filehandle for %s\n",
1865 argp
->flags
|= NFSMNT_NEWARGS
;
1866 argp
->flags
|= NFSMNT_INT
; /* default is "intr" */
1867 argp
->flags
|= NFSMNT_HOSTNAME
;
1868 argp
->hostname
= strdup(host
);
1869 if (argp
->hostname
== NULL
) {
1870 syslog(LOG_ERR
, "nfsmount: no memory");
1871 last_error
= NFSERR_IO
;
1876 * In this case, we want NFSv4 to behave like
1877 * non-WebNFS so that we get the server address.
1879 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0) {
1883 thisport
= nfs_port
;
1885 thisport
= mfs
->mfs_port
;
1888 * For NFSv4, we want to avoid rpcbind, so call
1889 * get_server_netinfo() directly to tell it that
1890 * we want to go "direct_to_server". Otherwise,
1891 * do what has always been done.
1893 if (nfsvers
== NFS_V4
) {
1894 enum clnt_stat cstat
;
1896 argp
->addr
= get_server_netinfo(SERVER_ADDR
,
1897 host
, NFS_PROGRAM
, nfsvers
, NULL
,
1898 &nconf
, nfs_proto
, thisport
, NULL
,
1899 NULL
, TRUE
, NULL
, &cstat
);
1901 argp
->addr
= get_addr(host
, NFS_PROGRAM
,
1902 nfsvers
, &nconf
, nfs_proto
,
1906 if (argp
->addr
== NULL
) {
1908 free(argp
->hostname
);
1914 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1915 last_error
= NFSERR_NOENT
;
1917 if (retries
-- > 0) {
1918 destroy_auth_client_handle(cl
);
1923 syslog(loglevel
, "%s: no NFS service", host
);
1924 destroy_auth_client_handle(cl
);
1926 mfs
->mfs_ignore
= 1;
1931 "\tnfsmount: have net address for %s\n",
1935 nconf
= mfs
->mfs_nconf
;
1936 mfs
->mfs_nconf
= NULL
;
1939 argp
->flags
|= NFSMNT_KNCONF
;
1940 argp
->knconf
= get_knconf(nconf
);
1941 if (argp
->knconf
== NULL
) {
1942 netbuf_free(argp
->addr
);
1943 freenetconfigent(nconf
);
1945 free(argp
->hostname
);
1951 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
1952 last_error
= NFSERR_NOSPC
;
1953 destroy_auth_client_handle(cl
);
1955 mfs
->mfs_ignore
= 1;
1960 "\tnfsmount: have net config for %s\n",
1963 if (hasmntopt(&m
, MNTOPT_SOFT
) != NULL
) {
1964 argp
->flags
|= NFSMNT_SOFT
;
1966 if (hasmntopt(&m
, MNTOPT_NOINTR
) != NULL
) {
1967 argp
->flags
&= ~(NFSMNT_INT
);
1969 if (hasmntopt(&m
, MNTOPT_NOAC
) != NULL
) {
1970 argp
->flags
|= NFSMNT_NOAC
;
1972 if (hasmntopt(&m
, MNTOPT_NOCTO
) != NULL
) {
1973 argp
->flags
|= NFSMNT_NOCTO
;
1975 if (hasmntopt(&m
, MNTOPT_FORCEDIRECTIO
) != NULL
) {
1976 argp
->flags
|= NFSMNT_DIRECTIO
;
1978 if (hasmntopt(&m
, MNTOPT_NOFORCEDIRECTIO
) != NULL
) {
1979 argp
->flags
&= ~(NFSMNT_DIRECTIO
);
1983 * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1985 if (mfssnego
.snego_done
) {
1986 memcpy(&nfs_sec
, &mfssnego
.nfs_sec
,
1987 sizeof (seconfig_t
));
1988 } else if (!sec_opt
) {
1990 * Get default security mode.
1992 if (nfs_getseconfig_default(&nfs_sec
)) {
1994 "error getting default security entry\n");
1995 free_knconf(argp
->knconf
);
1996 netbuf_free(argp
->addr
);
1997 freenetconfigent(nconf
);
1999 free(argp
->hostname
);
2005 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2006 last_error
= NFSERR_NOSPC
;
2007 destroy_auth_client_handle(cl
);
2009 mfs
->mfs_ignore
= 1;
2012 argp
->flags
|= NFSMNT_SECDEFAULT
;
2017 * get the network address for the time service on
2018 * the server. If an RPC based time service is
2019 * not available then try the IP time service.
2021 * Eventurally, we want to move this code to nfs_clnt_secdata()
2022 * when autod_nfs.c and mount.c can share the same
2023 * get_the_addr/get_netconfig_info routine.
2029 if (nfs_sec
.sc_rpcnum
== AUTH_DH
|| nfsvers
== NFS_V4
) {
2031 * If not using the public fh and not NFS_V4, we can try
2032 * talking RPCBIND. Otherwise, assume that firewalls
2033 * prevent us from doing that.
2035 if ((mfs
->mfs_flags
& MFS_FH_VIA_WEBNFS
) == 0 &&
2036 nfsvers
!= NFS_V4
) {
2037 enum clnt_stat cstat
;
2038 syncaddr
= get_server_netinfo(SERVER_ADDR
,
2039 host
, RPCBPROG
, RPCBVERS
, NULL
, &nconf
,
2040 NULL
, 0, NULL
, NULL
, FALSE
, NULL
, &cstat
);
2043 if (syncaddr
!= NULL
) {
2044 /* for flags in sec_data */
2045 secflags
|= AUTH_F_RPCTIMESYNC
;
2047 struct nd_hostserv hs
;
2051 hs
.h_serv
= "timserver";
2052 error
= netdir_getbyname(nconf
, &hs
, &retaddrs
);
2054 if (error
!= ND_OK
&&
2055 nfs_sec
.sc_rpcnum
== AUTH_DH
) {
2057 "%s: secure: no time service\n",
2059 free_knconf(argp
->knconf
);
2060 netbuf_free(argp
->addr
);
2061 freenetconfigent(nconf
);
2063 free(argp
->hostname
);
2069 tail
->nfs_ext_u
.nfs_extB
.next
=
2071 last_error
= NFSERR_IO
;
2072 destroy_auth_client_handle(cl
);
2074 mfs
->mfs_ignore
= 1;
2079 syncaddr
= retaddrs
->n_addrs
;
2082 * For potential usage by NFS V4 when AUTH_DH
2083 * is negotiated via SECINFO in the kernel.
2085 if (nfsvers
== NFS_V4
&& syncaddr
&&
2086 host2netname(netname
, host
, NULL
)) {
2088 malloc(sizeof (struct netbuf
));
2089 argp
->syncaddr
->buf
=
2090 malloc(syncaddr
->len
);
2091 (void) memcpy(argp
->syncaddr
->buf
,
2092 syncaddr
->buf
, syncaddr
->len
);
2093 argp
->syncaddr
->len
= syncaddr
->len
;
2094 argp
->syncaddr
->maxlen
=
2096 argp
->netname
= strdup(netname
);
2097 argp
->flags
|= NFSMNT_SECURE
;
2103 * TSOL notes: automountd in tsol extension
2104 * has "read down" capability, i.e. we allow
2105 * a user to trigger an nfs mount into a lower
2106 * labeled zone. We achieve this by always having
2107 * root issue the mount request so that the
2108 * lookup ops can go past /zone/<zone_name>
2109 * on the server side.
2111 if (is_system_labeled())
2112 nfs_sec
.sc_uid
= (uid_t
)0;
2114 nfs_sec
.sc_uid
= uid
;
2116 * If AUTH_DH is a chosen flavor now, its data will be stored
2117 * in the sec_data structure via nfs_clnt_secdata().
2119 if (!(secdata
= nfs_clnt_secdata(&nfs_sec
, host
, argp
->knconf
,
2120 syncaddr
, secflags
))) {
2122 "errors constructing security related data\n");
2123 if (secflags
& AUTH_F_RPCTIMESYNC
)
2124 netbuf_free(syncaddr
);
2126 netdir_free(retaddrs
, ND_ADDRLIST
);
2128 netbuf_free(argp
->syncaddr
);
2130 free(argp
->netname
);
2132 free(argp
->hostname
);
2133 free_knconf(argp
->knconf
);
2134 netbuf_free(argp
->addr
);
2135 freenetconfigent(nconf
);
2141 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2142 last_error
= NFSERR_IO
;
2143 destroy_auth_client_handle(cl
);
2145 mfs
->mfs_ignore
= 1;
2148 NFS_ARGS_EXTB_secdata(*argp
, secdata
);
2149 /* end of security stuff */
2153 " nfsmount: have secure info for %s\n", remname
);
2155 if (hasmntopt(&m
, MNTOPT_GRPID
) != NULL
) {
2156 argp
->flags
|= NFSMNT_GRPID
;
2158 if (nopt(&m
, MNTOPT_RSIZE
, &argp
->rsize
)) {
2159 argp
->flags
|= NFSMNT_RSIZE
;
2161 if (nopt(&m
, MNTOPT_WSIZE
, &argp
->wsize
)) {
2162 argp
->flags
|= NFSMNT_WSIZE
;
2164 if (nopt(&m
, MNTOPT_TIMEO
, &argp
->timeo
)) {
2165 argp
->flags
|= NFSMNT_TIMEO
;
2167 if (nopt(&m
, MNTOPT_RETRANS
, &argp
->retrans
)) {
2168 argp
->flags
|= NFSMNT_RETRANS
;
2170 if (nopt(&m
, MNTOPT_ACTIMEO
, &argp
->acregmax
)) {
2171 argp
->flags
|= NFSMNT_ACREGMAX
;
2172 argp
->flags
|= NFSMNT_ACDIRMAX
;
2173 argp
->flags
|= NFSMNT_ACDIRMIN
;
2174 argp
->flags
|= NFSMNT_ACREGMIN
;
2175 argp
->acdirmin
= argp
->acregmin
= argp
->acdirmax
2178 if (nopt(&m
, MNTOPT_ACREGMIN
, &argp
->acregmin
)) {
2179 argp
->flags
|= NFSMNT_ACREGMIN
;
2181 if (nopt(&m
, MNTOPT_ACREGMAX
, &argp
->acregmax
)) {
2182 argp
->flags
|= NFSMNT_ACREGMAX
;
2184 if (nopt(&m
, MNTOPT_ACDIRMIN
, &argp
->acdirmin
)) {
2185 argp
->flags
|= NFSMNT_ACDIRMIN
;
2187 if (nopt(&m
, MNTOPT_ACDIRMAX
, &argp
->acdirmax
)) {
2188 argp
->flags
|= NFSMNT_ACDIRMAX
;
2193 argp
->pathconf
= NULL
;
2194 if (error
= get_pathconf(cl
, dir
, remname
,
2195 &argp
->pathconf
, retries
)) {
2196 if (secflags
& AUTH_F_RPCTIMESYNC
)
2197 netbuf_free(syncaddr
);
2199 netdir_free(retaddrs
, ND_ADDRLIST
);
2200 free_knconf(argp
->knconf
);
2201 netbuf_free(argp
->addr
);
2202 freenetconfigent(nconf
);
2204 argp
->nfs_ext_u
.nfs_extB
.secdata
);
2206 netbuf_free(argp
->syncaddr
);
2208 free(argp
->netname
);
2210 free(argp
->hostname
);
2216 tail
->nfs_ext_u
.nfs_extB
.next
= NULL
;
2217 last_error
= NFSERR_IO
;
2219 if (error
== RET_RETRY
&& retries
-- > 0) {
2220 destroy_auth_client_handle(cl
);
2225 destroy_auth_client_handle(cl
);
2227 mfs
->mfs_ignore
= 1;
2230 argp
->flags
|= NFSMNT_POSIX
;
2233 " nfsmount: have pathconf for %s\n",
2238 * free loop-specific data structures
2240 destroy_auth_client_handle(cl
);
2241 freenetconfigent(nconf
);
2242 if (secflags
& AUTH_F_RPCTIMESYNC
)
2243 netbuf_free(syncaddr
);
2245 netdir_free(retaddrs
, ND_ADDRLIST
);
2248 * Decide whether to use remote host's lockd or local locking.
2249 * If we are using the public fh, we've already turned
2252 if (hasmntopt(&m
, MNTOPT_LLOCK
))
2253 argp
->flags
|= NFSMNT_LLOCK
;
2254 if (!(argp
->flags
& NFSMNT_LLOCK
) && nfsvers
== NFS_VERSION
&&
2255 remote_lock(host
, argp
->fh
)) {
2256 syslog(loglevel
, "No network locking on %s : "
2257 "contact admin to install server change", host
);
2258 argp
->flags
|= NFSMNT_LLOCK
;
2262 * Build a string for /etc/mnttab.
2263 * If possible, coalesce strings with same 'dir' info.
2265 if ((mfs
->mfs_flags
& MFS_URL
) == 0) {
2269 p
= strrchr(mnttabtext
, (int)':');
2270 if (!p
|| strcmp(p
+1, dir
) != 0) {
2271 mnttabcnt
+= strlen(remname
) + 2;
2274 mnttabcnt
+= strlen(rhost
) + 2;
2276 if ((tmp
= realloc(mnttabtext
,
2277 mnttabcnt
)) != NULL
) {
2279 strcat(mnttabtext
, ",");
2285 mnttabcnt
= strlen(remname
) + 1;
2286 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2287 mnttabtext
[0] = '\0';
2290 if (mnttabtext
!= NULL
)
2291 strcat(mnttabtext
, remname
);
2298 more_cnt
+= strlen("nfs://");
2299 more_cnt
+= strlen(mfs
->mfs_host
);
2301 if (mfs
->mfs_port
!= 0) {
2302 (void) sprintf(sport
, ":%u", mfs
->mfs_port
);
2306 more_cnt
+= strlen(sport
);
2307 more_cnt
+= 1; /* "/" */
2308 more_cnt
+= strlen(mfs
->mfs_dir
);
2311 more_cnt
+= 1; /* "," */
2312 mnttabcnt
+= more_cnt
;
2314 if ((tmp
= realloc(mnttabtext
,
2315 mnttabcnt
)) != NULL
) {
2317 strcat(mnttabtext
, ",");
2323 mnttabcnt
= more_cnt
+ 1;
2324 if ((mnttabtext
= malloc(mnttabcnt
)) != NULL
)
2325 mnttabtext
[0] = '\0';
2328 if (mnttabtext
!= NULL
) {
2329 strcat(mnttabtext
, "nfs://");
2330 strcat(mnttabtext
, mfs
->mfs_host
);
2331 strcat(mnttabtext
, sport
);
2332 strcat(mnttabtext
, "/");
2333 strcat(mnttabtext
, mfs
->mfs_dir
);
2338 syslog(LOG_ERR
, "nfsmount: no memory");
2339 last_error
= NFSERR_IO
;
2344 * At least one entry, can call mount(2).
2349 * If replication was defeated, don't do more work
2357 * Did we get through all possibilities without success?
2362 /* Make "xattr" the default if "noxattr" is not specified. */
2363 strcpy(mopts
, opts
);
2364 if (!hasmntopt(&m
, MNTOPT_NOXATTR
) && !hasmntopt(&m
, MNTOPT_XATTR
)) {
2365 if (strlen(mopts
) > 0)
2367 strcat(mopts
, "xattr");
2371 * enable services as needed.
2376 if (strcmp(fstype
, MNTTYPE_NFS4
) == 0)
2377 sl
= service_list_v4
;
2381 (void) _check_services(sl
);
2385 * Whew; do the mount, at last.
2388 trace_prt(1, " mount %s %s (%s)\n", mnttabtext
, mntpnt
, mopts
);
2392 * About to do a nfs mount, make sure the mount_to is set for
2393 * potential ephemeral mounts with NFSv4.
2395 set_nfsv4_ephemeral_mount_to();
2398 * If no action list pointer then do the mount, otherwise
2399 * build the actions list pointer with the mount information.
2400 * so the mount can be done in the kernel.
2403 if (mount(mnttabtext
, mntpnt
, flags
| MS_DATA
, fstype
,
2404 head
, sizeof (*head
), mopts
, MAX_MNTOPT_STR
) < 0) {
2406 trace_prt(1, " Mount of %s on %s: %d\n",
2407 mnttabtext
, mntpnt
, errno
);
2408 if (errno
!= EBUSY
|| verbose
)
2410 "Mount of %s on %s: %m", mnttabtext
, mntpnt
);
2411 last_error
= NFSERR_IO
;
2415 last_error
= NFS_OK
;
2416 if (stat(mntpnt
, &stbuf
) == 0) {
2418 trace_prt(1, " mount %s dev=%x rdev=%x OK\n",
2419 mnttabtext
, stbuf
.st_dev
, stbuf
.st_rdev
);
2423 trace_prt(1, " mount %s OK\n", mnttabtext
);
2424 trace_prt(1, " stat of %s failed\n", mntpnt
);
2429 alp
->action
.action
= AUTOFS_MOUNT_RQ
;
2430 alp
->action
.action_list_entry_u
.mounta
.spec
=
2432 alp
->action
.action_list_entry_u
.mounta
.dir
= strdup(mntpnt
);
2433 alp
->action
.action_list_entry_u
.mounta
.flags
=
2435 alp
->action
.action_list_entry_u
.mounta
.fstype
=
2437 alp
->action
.action_list_entry_u
.mounta
.dataptr
= (char *)head
;
2438 alp
->action
.action_list_entry_u
.mounta
.datalen
=
2440 mntopts
= malloc(strlen(mopts
) + 1);
2441 strcpy(mntopts
, mopts
);
2442 mntopts
[strlen(mopts
)] = '\0';
2443 alp
->action
.action_list_entry_u
.mounta
.optptr
= mntopts
;
2444 alp
->action
.action_list_entry_u
.mounta
.optlen
=
2445 strlen(mntopts
) + 1;
2446 last_error
= NFS_OK
;
2454 free(argp
->pathconf
);
2455 free_knconf(argp
->knconf
);
2456 netbuf_free(argp
->addr
);
2458 netbuf_free(argp
->syncaddr
);
2459 if (argp
->netname
) {
2460 free(argp
->netname
);
2463 free(argp
->hostname
);
2464 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
2467 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
2476 for (mfs
= mfs_in
; mfs
; mfs
= mfs
->mfs_next
) {
2478 if (mfs
->mfs_flags
& MFS_ALLOC_DIR
) {
2480 mfs
->mfs_dir
= NULL
;
2481 mfs
->mfs_flags
&= ~MFS_ALLOC_DIR
;
2484 if (mfs
->mfs_args
!= NULL
&& alp
== NULL
) {
2485 free(mfs
->mfs_args
);
2486 mfs
->mfs_args
= NULL
;
2489 if (mfs
->mfs_nconf
!= NULL
) {
2490 freenetconfigent(mfs
->mfs_nconf
);
2491 mfs
->mfs_nconf
= NULL
;
2495 return (last_error
);
2499 * get_pathconf(cl, path, fsname, pcnf, cretries)
2500 * ugliness that requires that ppathcnf and pathcnf stay consistent
2501 * cretries is a copy of retries used to determine when to syslog
2502 * on retry situations.
2505 get_pathconf(CLIENT
*cl
, char *path
, char *fsname
, struct pathcnf
**pcnf
,
2508 struct ppathcnf
*p
= NULL
;
2509 enum clnt_stat rpc_stat
;
2510 struct timeval timeout
;
2512 p
= (struct ppathcnf
*)malloc(sizeof (struct ppathcnf
));
2514 syslog(LOG_ERR
, "get_pathconf: Out of memory");
2517 memset((caddr_t
)p
, 0, sizeof (struct ppathcnf
));
2519 timeout
.tv_sec
= 10;
2520 timeout
.tv_usec
= 0;
2521 rpc_stat
= clnt_call(cl
, MOUNTPROC_PATHCONF
,
2522 xdr_dirpath
, (caddr_t
)&path
, xdr_ppathcnf
, (caddr_t
)p
, timeout
);
2523 if (rpc_stat
!= RPC_SUCCESS
) {
2524 if (cretries
-- <= 0) {
2526 "get_pathconf: %s: server not responding: %s",
2527 fsname
, clnt_sperror(cl
, ""));
2532 if (_PC_ISSET(_PC_ERROR
, p
->pc_mask
)) {
2533 syslog(LOG_ERR
, "get_pathconf: no info for %s", fsname
);
2537 *pcnf
= (struct pathcnf
*)p
;
2552 #define SMALL_HOSTNAME 20
2553 #define SMALL_PROTONAME 10
2554 #define SMALL_PROTOFMLYNAME 10
2556 struct portmap_cache
{
2560 char cache_small_hosts
[SMALL_HOSTNAME
+ 1];
2561 char *cache_hostname
;
2563 char *cache_protofmly
;
2564 char cache_small_protofmly
[SMALL_PROTOFMLYNAME
+ 1];
2565 char cache_small_proto
[SMALL_PROTONAME
+ 1];
2566 struct netbuf cache_srv_addr
;
2567 struct portmap_cache
*cache_prev
, *cache_next
;
2570 rwlock_t portmap_cache_lock
;
2571 static int portmap_cache_valid_time
= 30;
2572 struct portmap_cache
*portmap_cache_head
, *portmap_cache_tail
;
2576 portmap_cache_flush()
2578 struct portmap_cache
*next
= NULL
, *cp
;
2580 (void) rw_wrlock(&portmap_cache_lock
);
2581 for (cp
= portmap_cache_head
; cp
; cp
= cp
->cache_next
) {
2582 if (cp
->cache_hostname
!= NULL
&&
2583 cp
->cache_hostname
!=
2584 cp
->cache_small_hosts
)
2585 free(cp
->cache_hostname
);
2586 if (cp
->cache_proto
!= NULL
&&
2588 cp
->cache_small_proto
)
2589 free(cp
->cache_proto
);
2590 if (cp
->cache_srv_addr
.buf
!= NULL
)
2591 free(cp
->cache_srv_addr
.buf
);
2592 next
= cp
->cache_next
;
2595 portmap_cache_head
= NULL
;
2596 portmap_cache_tail
= NULL
;
2597 (void) rw_unlock(&portmap_cache_lock
);
2602 * Returns 1 if the entry is found in the cache, 0 otherwise.
2605 portmap_cache_lookup(hostname
, prog
, vers
, nconf
, addrp
)
2609 struct netconfig
*nconf
;
2610 struct netbuf
*addrp
;
2612 struct portmap_cache
*cachep
, *prev
, *next
= NULL
, *cp
;
2615 timenow
= time(NULL
);
2617 (void) rw_rdlock(&portmap_cache_lock
);
2620 * Increment the portmap cache counters for # accesses and lookups
2621 * Use a smaller factor (100 vs 1000 for the host cache) since
2622 * initial analysis shows this cache is looked up 10% that of the
2626 portmap_cache_accesses
++;
2627 portmap_cache_lookups
++;
2628 if ((portmap_cache_lookups
%100) == 0)
2629 trace_portmap_cache();
2630 #endif /* CACHE_DEBUG */
2632 for (cachep
= portmap_cache_head
; cachep
;
2633 cachep
= cachep
->cache_next
) {
2634 if (timenow
> cachep
->cache_time
) {
2636 * We stumbled across an entry in the cache which
2637 * has timed out. Free up all the entries that
2638 * were added before it, which will positionally
2639 * be after this entry. And adjust neighboring
2641 * When we drop the lock and re-acquire it, we
2642 * need to start from the beginning.
2644 (void) rw_unlock(&portmap_cache_lock
);
2645 (void) rw_wrlock(&portmap_cache_lock
);
2646 for (cp
= portmap_cache_head
;
2647 cp
&& (cp
->cache_time
>= timenow
);
2648 cp
= cp
->cache_next
)
2653 * Adjust the link of the predecessor.
2654 * Make the tail point to the new last entry.
2656 prev
= cp
->cache_prev
;
2658 portmap_cache_head
= NULL
;
2659 portmap_cache_tail
= NULL
;
2661 prev
->cache_next
= NULL
;
2662 portmap_cache_tail
= prev
;
2664 for (; cp
; cp
= next
) {
2665 if (cp
->cache_hostname
!= NULL
&&
2666 cp
->cache_hostname
!=
2667 cp
->cache_small_hosts
)
2668 free(cp
->cache_hostname
);
2669 if (cp
->cache_proto
!= NULL
&&
2671 cp
->cache_small_proto
)
2672 free(cp
->cache_proto
);
2673 if (cp
->cache_srv_addr
.buf
!= NULL
)
2674 free(cp
->cache_srv_addr
.buf
);
2675 next
= cp
->cache_next
;
2680 if (cachep
->cache_hostname
== NULL
||
2681 prog
!= cachep
->cache_prog
|| vers
!= cachep
->cache_vers
||
2682 strcmp(nconf
->nc_proto
, cachep
->cache_proto
) != 0 ||
2683 strcmp(nconf
->nc_protofmly
, cachep
->cache_protofmly
) != 0 ||
2684 strcmp(hostname
, cachep
->cache_hostname
) != 0)
2690 portmap_cache_hits
++; /* up portmap cache hit counter */
2691 #endif /* CACHE_DEBUG */
2692 addrp
->len
= cachep
->cache_srv_addr
.len
;
2693 memcpy(addrp
->buf
, cachep
->cache_srv_addr
.buf
, addrp
->len
);
2698 (void) rw_unlock(&portmap_cache_lock
);
2703 portmap_cache_enter(hostname
, prog
, vers
, nconf
, addrp
)
2707 struct netconfig
*nconf
;
2708 struct netbuf
*addrp
;
2710 struct portmap_cache
*cachep
;
2712 int protolen
, hostnamelen
;
2714 timenow
= time(NULL
);
2716 cachep
= malloc(sizeof (struct portmap_cache
));
2719 memset((char *)cachep
, 0, sizeof (*cachep
));
2721 hostnamelen
= strlen(hostname
);
2722 if (hostnamelen
<= SMALL_HOSTNAME
)
2723 cachep
->cache_hostname
= cachep
->cache_small_hosts
;
2725 cachep
->cache_hostname
= malloc(hostnamelen
+ 1);
2726 if (cachep
->cache_hostname
== NULL
)
2729 strcpy(cachep
->cache_hostname
, hostname
);
2730 protolen
= strlen(nconf
->nc_proto
);
2731 if (protolen
<= SMALL_PROTONAME
)
2732 cachep
->cache_proto
= cachep
->cache_small_proto
;
2734 cachep
->cache_proto
= malloc(protolen
+ 1);
2735 if (cachep
->cache_proto
== NULL
)
2738 protofmlylen
= strlen(nconf
->nc_protofmly
);
2739 if (protofmlylen
<= SMALL_PROTOFMLYNAME
)
2740 cachep
->cache_protofmly
= cachep
->cache_small_protofmly
;
2742 cachep
->cache_protofmly
= malloc(protofmlylen
+ 1);
2743 if (cachep
->cache_protofmly
== NULL
)
2747 strcpy(cachep
->cache_proto
, nconf
->nc_proto
);
2748 cachep
->cache_prog
= prog
;
2749 cachep
->cache_vers
= vers
;
2750 cachep
->cache_time
= timenow
+ portmap_cache_valid_time
;
2751 cachep
->cache_srv_addr
.len
= addrp
->len
;
2752 cachep
->cache_srv_addr
.buf
= malloc(addrp
->len
);
2753 if (cachep
->cache_srv_addr
.buf
== NULL
)
2755 memcpy(cachep
->cache_srv_addr
.buf
, addrp
->buf
, addrp
->maxlen
);
2756 cachep
->cache_prev
= NULL
;
2757 (void) rw_wrlock(&portmap_cache_lock
);
2759 * There's a window in which we could have multiple threads making
2760 * the same cache entry. This can be avoided by walking the cache
2761 * once again here to check and see if there are duplicate entries
2762 * (after grabbing the write lock). This isn't fatal and I'm not
2763 * going to bother with this.
2766 portmap_cache_accesses
++; /* up portmap cache access counter */
2767 #endif /* CACHE_DEBUG */
2768 cachep
->cache_next
= portmap_cache_head
;
2769 if (portmap_cache_head
!= NULL
)
2770 portmap_cache_head
->cache_prev
= cachep
;
2771 portmap_cache_head
= cachep
;
2772 (void) rw_unlock(&portmap_cache_lock
);
2776 syslog(LOG_ERR
, "portmap_cache_enter: Memory allocation failed");
2777 if (cachep
->cache_srv_addr
.buf
)
2778 free(cachep
->cache_srv_addr
.buf
);
2779 if (cachep
->cache_proto
&& protolen
> SMALL_PROTONAME
)
2780 free(cachep
->cache_proto
);
2781 if (cachep
->cache_hostname
&& hostnamelen
> SMALL_HOSTNAME
)
2782 free(cachep
->cache_hostname
);
2783 if (cachep
->cache_protofmly
&& protofmlylen
> SMALL_PROTOFMLYNAME
)
2784 free(cachep
->cache_protofmly
);
2791 get_cached_srv_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2792 struct netconfig
*nconf
, struct netbuf
*addrp
)
2794 if (portmap_cache_lookup(hostname
, prog
, vers
, nconf
, addrp
))
2796 if (rpcb_getaddr(prog
, vers
, nconf
, addrp
, hostname
) == 0)
2798 portmap_cache_enter(hostname
, prog
, vers
, nconf
, addrp
);
2803 * Get a network address on "hostname" for program "prog"
2804 * with version "vers". If the port number is specified (non zero)
2805 * then try for a TCP/UDP transport and set the port number of the
2806 * resulting IP address.
2808 * If the address of a netconfig pointer was passed and
2809 * if it's not null, use it as the netconfig otherwise
2810 * assign the address of the netconfig that was used to
2811 * establish contact with the service.
2813 * tinfo argument is for matching the get_addr() defined in
2814 * ../nfs/mount/mount.c
2817 static struct netbuf
*
2818 get_addr(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2819 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2820 struct t_info
*tinfo
)
2823 enum clnt_stat cstat
;
2825 return (get_server_netinfo(SERVER_ADDR
, hostname
, prog
, vers
, NULL
,
2826 nconfp
, proto
, port
, tinfo
, NULL
, FALSE
, NULL
, &cstat
));
2829 static struct netbuf
*
2830 get_pubfh(char *hostname
, rpcvers_t vers
, mfs_snego_t
*mfssnego
,
2831 struct netconfig
**nconfp
, char *proto
, ushort_t port
,
2832 struct t_info
*tinfo
, caddr_t
*fhp
, bool_t get_pubfh
, char *fspath
)
2834 enum clnt_stat cstat
;
2836 return (get_server_netinfo(SERVER_FH
, hostname
, NFS_PROGRAM
, vers
,
2837 mfssnego
, nconfp
, proto
, port
, tinfo
, fhp
, get_pubfh
, fspath
,
2841 static enum clnt_stat
2842 get_ping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
2843 struct netconfig
**nconfp
, ushort_t port
, bool_t direct_to_server
)
2845 enum clnt_stat cstat
;
2847 (void) get_server_netinfo(SERVER_PING
, hostname
, prog
,
2848 vers
, NULL
, nconfp
, NULL
, port
, NULL
, NULL
,
2849 direct_to_server
, NULL
, &cstat
);
2856 enum type_of_stuff type_of_stuff
,
2860 mfs_snego_t
*mfssnego
,
2861 struct netconfig
**nconfp
,
2863 ushort_t port
, /* may be zero */
2864 struct t_info
*tinfo
,
2866 bool_t direct_to_server
,
2868 enum clnt_stat
*cstatp
)
2870 struct netbuf
*nb
= NULL
;
2871 struct netconfig
*nconf
= NULL
;
2872 NCONF_HANDLE
*nc
= NULL
;
2875 struct t_bind
*tbind
= NULL
;
2876 int nthtry
= FIRST_TRY
;
2878 if (nconfp
&& *nconfp
) {
2879 return (get_netconfig_info(type_of_stuff
, hostname
,
2880 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2881 direct_to_server
, fspath
, cstatp
, mfssnego
));
2885 * No nconf passed in.
2887 * Try to get a nconf from /etc/netconfig.
2888 * First choice is COTS, second is CLTS unless proto
2889 * is specified. When we retry, we reset the
2890 * netconfig list, so that we search the whole list
2891 * for the next choice.
2893 if ((nc
= setnetpath()) == NULL
)
2897 * If proto is specified, then only search for the match,
2898 * otherwise try COTS first, if failed, then try CLTS.
2901 while ((nconf
= getnetpath(nc
)) != NULL
) {
2902 if (strcmp(nconf
->nc_proto
, proto
))
2905 * If the port number is specified then TCP/UDP
2906 * is needed. Otherwise any cots/clts will do.
2909 if ((strcmp(nconf
->nc_protofmly
, NC_INET
) &&
2910 strcmp(nconf
->nc_protofmly
, NC_INET6
)) ||
2911 (strcmp(nconf
->nc_proto
, NC_TCP
) &&
2912 strcmp(nconf
->nc_proto
, NC_UDP
)))
2915 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2916 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2917 direct_to_server
, fspath
, cstatp
, mfssnego
);
2918 if (*cstatp
== RPC_SUCCESS
)
2928 while ((nconf
= getnetpath(nc
)) != NULL
) {
2929 if (nconf
->nc_flag
& NC_VISIBLE
) {
2930 if (nthtry
== FIRST_TRY
) {
2931 if ((nconf
->nc_semantics
==
2933 (nconf
->nc_semantics
==
2937 if ((strcmp(nconf
->nc_protofmly
,
2939 strcmp(nconf
->nc_protofmly
,
2941 (strcmp(nconf
->nc_proto
,
2946 if (nthtry
== SECOND_TRY
) {
2947 if (nconf
->nc_semantics
==
2951 if ((strcmp(nconf
->nc_protofmly
,
2953 strcmp(nconf
->nc_protofmly
,
2955 (strcmp(nconf
->nc_proto
,
2963 if (nconf
== NULL
) {
2964 if (++nthtry
<= MNT_PREF_LISTLEN
) {
2966 if ((nc
= setnetpath()) == NULL
)
2972 nb
= get_netconfig_info(type_of_stuff
, hostname
,
2973 prog
, vers
, nconf
, port
, tinfo
, tbind
, fhp
,
2974 direct_to_server
, fspath
, cstatp
, mfssnego
);
2975 if (*cstatp
!= RPC_SUCCESS
)
2977 * Continue the same search path in the
2978 * netconfig db until no more matched nconf
2986 * Got nconf and nb. Now dup the netconfig structure (nconf)
2987 * and return it thru nconfp.
2989 if (nconf
!= NULL
) {
2990 if ((*nconfp
= getnetconfigent(nconf
->nc_netid
)) == NULL
) {
2991 syslog(LOG_ERR
, "no memory\n");
3005 get_server_fh(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
3006 mfs_snego_t
*mfssnego
, struct netconfig
*nconf
, ushort_t port
,
3007 struct t_info
*tinfo
, struct t_bind
*tbind
, caddr_t
*fhp
,
3008 bool_t direct_to_server
, char *fspath
, enum clnt_stat
*cstat
)
3011 AUTH
*new_ah
= NULL
;
3012 struct snego_t snego
;
3013 enum clnt_stat cs
= RPC_TIMEDOUT
;
3015 bool_t file_handle
= 1;
3016 enum snego_stat sec
;
3019 struct netbuf
*nb
= NULL
;
3021 if (direct_to_server
!= TRUE
)
3024 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
3025 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
3028 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0)
3031 /* LINTED pointer alignment */
3032 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
)) == NULL
)
3035 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
, fd
,
3036 direct_to_server
, port
, prog
, vers
, file_handle
) < 0) {
3040 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
, prog
, vers
, 0, 0);
3044 ah
= authsys_create_default();
3047 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3048 __FILE__
, __LINE__
);
3050 AUTH_DESTROY(cl
->cl_auth
);
3053 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3054 __FILE__
, __LINE__
);
3058 if (!mfssnego
->snego_done
&& vers
!= NFS_V4
) {
3060 * negotiate sec flavor.
3063 if ((sec
= nfs_sec_nego(vers
, cl
, fspath
, &snego
)) ==
3068 * check if server supports the one
3069 * specified in the sec= option.
3071 if (mfssnego
->sec_opt
) {
3072 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
3073 if (snego
.array
[jj
] ==
3074 mfssnego
->nfs_sec
.sc_nfsnum
) {
3075 mfssnego
->snego_done
= TRUE
;
3082 * find a common sec flavor
3084 if (!mfssnego
->snego_done
) {
3085 for (jj
= 0; jj
< snego
.cnt
; jj
++) {
3086 if (!nfs_getseconfig_bynumber(
3088 &mfssnego
->nfs_sec
)) {
3089 mfssnego
->snego_done
= TRUE
;
3094 if (!mfssnego
->snego_done
)
3097 * Now that the flavor has been
3098 * negotiated, get the fh.
3100 * First, create an auth handle using the negotiated
3101 * sec flavor in the next lookup to
3102 * fetch the filehandle.
3104 new_ah
= nfs_create_ah(cl
, hostname
,
3105 &mfssnego
->nfs_sec
);
3109 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3110 __FILE__
, __LINE__
);
3112 AUTH_DESTROY(cl
->cl_auth
);
3113 cl
->cl_auth
= new_ah
;
3115 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3116 __FILE__
, __LINE__
);
3118 } else if (sec
== SNEGO_ARRAY_TOO_SMALL
||
3119 sec
== SNEGO_FAILURE
) {
3130 memset((char *)&arg
.dir
, 0, sizeof (wnl_fh
));
3131 memset((char *)&res
, 0, sizeof (wnl_diropres
));
3133 if (wnlproc_lookup_2(&arg
, &res
, cl
) !=
3134 RPC_SUCCESS
|| res
.status
!= WNL_OK
)
3136 *fhp
= malloc(sizeof (wnl_fh
));
3139 syslog(LOG_ERR
, "no memory\n");
3143 memcpy((char *)*fhp
,
3144 (char *)&res
.wnl_diropres_u
.wnl_diropres
.file
,
3151 WNL_LOOKUP3args arg
;
3155 memset((char *)&arg
.what
.dir
, 0, sizeof (wnl_fh3
));
3156 memset((char *)&res
, 0, sizeof (WNL_LOOKUP3res
));
3157 arg
.what
.name
= fspath
;
3158 if (wnlproc3_lookup_3(&arg
, &res
, cl
) !=
3159 RPC_SUCCESS
|| res
.status
!= WNL3_OK
)
3162 fh3p
= (nfs_fh3
*)malloc(sizeof (*fh3p
));
3165 syslog(LOG_ERR
, "no memory\n");
3170 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_len
;
3171 memcpy(fh3p
->fh3_u
.data
,
3172 res
.WNL_LOOKUP3res_u
.res_ok
.object
.data
.data_val
,
3175 *fhp
= (caddr_t
)fh3p
;
3183 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
3185 if (cs
!= RPC_SUCCESS
)
3188 *fhp
= strdup(fspath
);
3190 cs
= RPC_SYSTEMERROR
;
3195 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
3197 syslog(LOG_ERR
, "no memory\n");
3198 cs
= RPC_SYSTEMERROR
;
3201 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
3202 if (nb
->buf
== NULL
) {
3203 syslog(LOG_ERR
, "no memory\n");
3206 cs
= RPC_SYSTEMERROR
;
3209 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
3210 nb
->len
= tbind
->addr
.len
;
3211 nb
->maxlen
= tbind
->addr
.maxlen
;
3215 destroy_auth_client_handle(cl
);
3216 cleanup_tli_parms(tbind
, fd
);
3221 * Sends a null call to the remote host's (NFS program, versp). versp
3222 * may be "NULL" in which case the default maximum version is used.
3223 * Upon return, versp contains the maximum version supported iff versp!= NULL.
3231 ushort_t port
, /* may be zero */
3237 struct timeval rpc_to_new
= {15, 0};
3238 static struct timeval rpc_rtrans_new
= {-1, -1};
3239 enum clnt_stat clnt_stat
;
3241 rpcvers_t versmax
; /* maximum version to try against server */
3242 rpcvers_t outvers
; /* version supported by host on last call */
3243 rpcvers_t vers_to_try
; /* to try different versions against host */
3245 struct netconfig
*nconf
;
3247 hostname
= strdup(hostpart
);
3248 if (hostname
== NULL
) {
3249 return (RPC_SYSTEMERROR
);
3251 unbracket(&hostname
);
3253 if (path
!= NULL
&& strcmp(hostname
, "nfs") == 0 &&
3254 strncmp(path
, "//", 2) == 0) {
3257 hostname
= strdup(path
+2);
3259 if (hostname
== NULL
)
3260 return (RPC_SYSTEMERROR
);
3262 path
= strchr(hostname
, '/');
3265 * This cannot happen. If it does, give up
3266 * on the ping as this is obviously a corrupt
3271 return (RPC_SUCCESS
);
3275 * Probable end point of host string.
3279 sport
= strchr(hostname
, ':');
3281 if (sport
!= NULL
&& sport
< path
) {
3284 * Actual end point of host string.
3287 port
= htons((ushort_t
)atoi(sport
+1));
3293 /* Pick up the default versions and then set them appropriately */
3296 /* use versmin passed in */
3299 set_versrange(0, &versmax
, &versmin
);
3303 strncasecmp(proto
, NC_UDP
, strlen(NC_UDP
)) == 0 &&
3304 versmax
== NFS_V4
) {
3305 if (versmin
== NFS_V4
) {
3307 *versp
= versmax
- 1;
3308 return (RPC_SUCCESS
);
3310 return (RPC_PROGUNAVAIL
);
3319 switch (cache_check(hostname
, versp
, proto
)) {
3321 if (hostname
!= hostpart
)
3323 return (RPC_SUCCESS
);
3325 if (hostname
!= hostpart
)
3327 return (RPC_TIMEDOUT
);
3334 * XXX The retransmission time rpcbrmttime is a global defined
3335 * in the rpc library (rpcb_clnt.c). We use (and like) the default
3336 * value of 15 sec in the rpc library. The code below is to protect
3337 * us in case it changes. This need not be done under a lock since
3338 * any # of threads entering this function will get the same
3339 * retransmission value.
3341 if (rpc_rtrans_new
.tv_sec
== -1 && rpc_rtrans_new
.tv_usec
== -1) {
3342 __rpc_control(CLCR_GET_RPCB_RMTTIME
, (char *)&rpc_rtrans_new
);
3343 if (rpc_rtrans_new
.tv_sec
!= 15 && rpc_rtrans_new
.tv_sec
!= 0)
3345 trace_prt(1, "RPC library rttimer changed\n");
3349 * XXX Manipulate the total timeout to get the number of
3350 * desired retransmissions. This code is heavily dependant on
3351 * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3353 for (i
= 0, j
= rpc_rtrans_new
.tv_sec
; i
< attempts
-1; i
++) {
3354 if (j
< RPC_MAX_BACKOFF
)
3357 j
= RPC_MAX_BACKOFF
;
3358 rpc_to_new
.tv_sec
+= j
;
3361 vers_to_try
= versmax
;
3364 * check the host's version within the timeout
3367 trace_prt(1, " ping: %s timeout=%ld request vers=%d min=%d\n",
3368 hostname
, rpc_to_new
.tv_sec
, versmax
, versmin
);
3370 if (usepub
== FALSE
) {
3373 * If NFSv4, then we do the same thing as is used
3374 * for public filehandles so that we avoid rpcbind
3376 if (vers_to_try
== NFS_V4
) {
3378 trace_prt(1, " pingnfs: Trying ping via "
3382 cl
= clnt_create_service_timed(hostname
, "nfs",
3383 NFS_PROGRAM
, vers_to_try
,
3384 port
, "circuit_v", &rpc_to_new
);
3386 outvers
= vers_to_try
;
3391 " pingnfs: Can't ping via "
3392 "\"circuit_v\" %s: RPC error=%d\n",
3393 hostname
, rpc_createerr
.cf_stat
);
3397 cl
= clnt_create_vers_timed(hostname
,
3398 NFS_PROGRAM
, &outvers
, versmin
, vers_to_try
,
3399 "datagram_v", &rpc_to_new
);
3404 " pingnfs: Can't ping via "
3405 "\"datagram_v\"%s: RPC error=%d\n",
3406 hostname
, rpc_createerr
.cf_stat
);
3408 if (rpc_createerr
.cf_stat
== RPC_UNKNOWNHOST
||
3409 rpc_createerr
.cf_stat
== RPC_TIMEDOUT
)
3411 if (rpc_createerr
.cf_stat
==
3412 RPC_PROGNOTREGISTERED
) {
3415 " pingnfs: Trying ping "
3416 "via \"circuit_v\"\n");
3418 cl
= clnt_create_vers_timed(hostname
,
3419 NFS_PROGRAM
, &outvers
,
3420 versmin
, vers_to_try
,
3421 "circuit_v", &rpc_to_new
);
3426 " pingnfs: Can't ping "
3427 "via \"circuit_v\" %s: "
3430 rpc_createerr
.cf_stat
);
3436 * backoff and return lower version to retry the ping.
3437 * XXX we should be more careful and handle
3438 * RPC_PROGVERSMISMATCH here, because that error is handled
3439 * in clnt_create_vers(). It's not done to stay in sync
3440 * with the nfs mount command.
3443 if (vers_to_try
< versmin
)
3445 if (versp
!= NULL
) { /* recheck the cache */
3446 *versp
= vers_to_try
;
3449 " pingnfs: check cache: vers=%d\n",
3452 switch (cache_check(hostname
, versp
, proto
)) {
3454 if (hostname
!= hostpart
)
3456 return (RPC_SUCCESS
);
3458 if (hostname
!= hostpart
)
3460 return (RPC_TIMEDOUT
);
3467 trace_prt(1, " pingnfs: Try version=%d\n",
3470 } while (cl
== NULL
);
3475 syslog(LOG_ERR
, "pingnfs: %s%s",
3476 hostname
, clnt_spcreateerror(""));
3477 clnt_stat
= rpc_createerr
.cf_stat
;
3480 clnt_stat
= RPC_SUCCESS
;
3484 for (vers_to_try
= versmax
; vers_to_try
>= versmin
;
3490 trace_prt(1, " pingnfs: Try version=%d "
3491 "using get_ping()\n", vers_to_try
);
3494 clnt_stat
= get_ping(hostname
, NFS_PROGRAM
,
3495 vers_to_try
, &nconf
, port
, TRUE
);
3498 freenetconfigent(nconf
);
3500 if (clnt_stat
== RPC_SUCCESS
) {
3501 outvers
= vers_to_try
;
3508 clnt_stat
== RPC_SUCCESS
?
3509 trace_prt(1, " pingnfs OK: nfs version=%d\n", outvers
):
3510 trace_prt(1, " pingnfs FAIL: can't get nfs version\n");
3512 if (clnt_stat
== RPC_SUCCESS
) {
3513 cache_enter(hostname
, versmax
, outvers
, proto
, GOODHOST
);
3517 cache_enter(hostname
, versmax
, versmax
, proto
, DEADHOST
);
3519 if (hostpart
!= hostname
)
3525 #define MNTTYPE_LOFS "lofs"
3528 loopbackmount(fsname
, dir
, mntopts
, overlay
)
3529 char *fsname
; /* Directory being mounted */
3530 char *dir
; /* Directory being mounted on */
3536 char fstype
[] = MNTTYPE_LOFS
;
3539 char optbuf
[MAX_MNTOPT_STR
];
3541 dirlen
= strlen(dir
);
3542 if (dir
[dirlen
-1] == ' ')
3545 if (dirlen
== strlen(fsname
) &&
3546 strncmp(fsname
, dir
, dirlen
) == 0) {
3548 "Mount of %s on %s would result in deadlock, aborted\n",
3552 mnt
.mnt_mntopts
= mntopts
;
3553 if (hasmntopt(&mnt
, MNTOPT_RO
) != NULL
)
3556 (void) strlcpy(optbuf
, mntopts
, sizeof (optbuf
));
3559 flags
|= MS_OVERLAY
;
3563 " loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3564 fsname
, dir
, flags
);
3566 if (is_system_labeled()) {
3567 if (create_homedir((const char *)fsname
,
3568 (const char *)dir
) == 0) {
3569 return (NFSERR_NOENT
);
3573 if (mount(fsname
, dir
, flags
| MS_DATA
| MS_OPTIONSTR
, fstype
,
3574 NULL
, 0, optbuf
, sizeof (optbuf
)) < 0) {
3575 syslog(LOG_ERR
, "Mount of %s on %s: %m", fsname
, dir
);
3579 if (stat(dir
, &st
) == 0) {
3582 " loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3583 fsname
, dir
, st
.st_dev
, st
.st_rdev
);
3588 " loopbackmount of %s on %s OK\n", fsname
, dir
);
3589 trace_prt(1, " stat of %s failed\n", dir
);
3597 * Look for the value of a numeric option of the form foo=x. If found, set
3598 * *valp to the value and return non-zero. If not found or the option is
3599 * malformed, return zero.
3603 nopt(mnt
, opt
, valp
)
3606 int *valp
; /* OUT */
3612 * We should never get a null pointer, but if we do, it's better to
3613 * ignore the option than to dump core.
3617 syslog(LOG_DEBUG
, "null pointer for %s option", opt
);
3621 if (str
= hasmntopt(mnt
, opt
)) {
3622 if (equal
= strchr(str
, '=')) {
3623 *valp
= atoi(&equal
[1]);
3626 syslog(LOG_ERR
, "Bad numeric option '%s'", str
);
3636 struct timeval timeout
;
3638 enum clnt_stat rpc_stat
;
3640 struct replica
*list
;
3642 int isv4mount
= is_v4_mount(mnt
->mnt_mountp
);
3645 trace_prt(1, " nfsunmount: umount %s\n", mnt
->mnt_mountp
);
3647 if (umount(mnt
->mnt_mountp
) < 0) {
3649 trace_prt(1, " nfsunmount: umount %s FAILED\n",
3656 * If this is a NFSv4 mount, the mount protocol was not used
3657 * so we just return.
3661 trace_prt(1, " nfsunmount: umount %s OK\n",
3667 * If mounted with -o public, then no need to contact server
3668 * because mount protocol was not used.
3670 if (hasmntopt(mnt
, MNTOPT_PUBLIC
) != NULL
) {
3675 * The rest of this code is advisory to the server.
3676 * If it fails return success anyway.
3679 list
= parse_replica(mnt
->mnt_special
, &count
);
3683 "Memory allocation failed: %m");
3687 for (i
= 0; i
< count
; i
++) {
3689 host
= list
[i
].host
;
3690 path
= list
[i
].path
;
3693 * Skip file systems mounted using WebNFS, because mount
3694 * protocol was not used.
3696 if (strcmp(host
, "nfs") == 0 && strncmp(path
, "//", 2) == 0)
3699 cl
= clnt_create(host
, MOUNTPROG
, MOUNTVERS
, "datagram_v");
3703 add_alloc("CLNT_HANDLE", cl
, 0, __FILE__
, __LINE__
);
3704 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0,
3705 __FILE__
, __LINE__
);
3707 if (__clnt_bindresvport(cl
) < 0) {
3709 syslog(LOG_ERR
, "umount %s:%s: %s",
3711 "Couldn't bind to reserved port");
3712 destroy_auth_client_handle(cl
);
3716 drop_alloc("AUTH_HANDLE", cl
->cl_auth
, __FILE__
, __LINE__
);
3718 AUTH_DESTROY(cl
->cl_auth
);
3719 if ((cl
->cl_auth
= authsys_create_default()) == NULL
) {
3721 syslog(LOG_ERR
, "umount %s:%s: %s",
3723 "Failed creating default auth handle");
3724 destroy_auth_client_handle(cl
);
3728 add_alloc("AUTH_HANDLE", cl
->cl_auth
, 0, __FILE__
, __LINE__
);
3730 timeout
.tv_usec
= 0;
3732 rpc_stat
= clnt_call(cl
, MOUNTPROC_UMNT
, xdr_dirpath
,
3733 (caddr_t
)&path
, xdr_void
, (char *)NULL
, timeout
);
3734 if (verbose
&& rpc_stat
!= RPC_SUCCESS
)
3735 syslog(LOG_ERR
, "%s: %s",
3736 host
, clnt_sperror(cl
, "unmount"));
3737 destroy_auth_client_handle(cl
);
3740 free_replica(list
, count
);
3743 trace_prt(1, " nfsunmount: umount %s OK\n", mnt
->mnt_mountp
);
3750 * Put a new entry in the cache chain by prepending it to the front.
3751 * If there isn't enough memory then just give up.
3754 cache_enter(host
, reqvers
, outvers
, proto
, state
)
3761 struct cache_entry
*entry
;
3762 int cache_time
= 30; /* sec */
3764 timenow
= time(NULL
);
3766 entry
= (struct cache_entry
*)malloc(sizeof (struct cache_entry
));
3769 (void) memset((caddr_t
)entry
, 0, sizeof (struct cache_entry
));
3770 entry
->cache_host
= strdup(host
);
3771 if (entry
->cache_host
== NULL
) {
3775 entry
->cache_reqvers
= reqvers
;
3776 entry
->cache_outvers
= outvers
;
3777 entry
->cache_proto
= (proto
== NULL
? NULL
: strdup(proto
));
3778 entry
->cache_state
= state
;
3779 entry
->cache_time
= timenow
+ cache_time
;
3780 (void) rw_wrlock(&cache_lock
);
3782 host_cache_accesses
++; /* up host cache access counter */
3783 #endif /* CACHE DEBUG */
3784 entry
->cache_next
= cache_head
;
3786 (void) rw_unlock(&cache_lock
);
3790 cache_check(host
, versp
, proto
)
3796 struct cache_entry
*ce
, *prev
;
3798 timenow
= time(NULL
);
3800 (void) rw_rdlock(&cache_lock
);
3803 /* Increment the lookup and access counters for the host cache */
3804 host_cache_accesses
++;
3805 host_cache_lookups
++;
3806 if ((host_cache_lookups
%1000) == 0)
3808 #endif /* CACHE DEBUG */
3810 for (ce
= cache_head
; ce
; ce
= ce
->cache_next
) {
3811 if (timenow
> ce
->cache_time
) {
3812 (void) rw_unlock(&cache_lock
);
3813 (void) rw_wrlock(&cache_lock
);
3814 for (prev
= NULL
, ce
= cache_head
; ce
;
3815 prev
= ce
, ce
= ce
->cache_next
) {
3816 if (timenow
> ce
->cache_time
) {
3819 prev
->cache_next
= NULL
;
3825 (void) rw_unlock(&cache_lock
);
3828 if (strcmp(host
, ce
->cache_host
) != 0)
3830 if ((proto
== NULL
&& ce
->cache_proto
!= NULL
) ||
3831 (proto
!= NULL
&& ce
->cache_proto
== NULL
))
3833 if (proto
!= NULL
&&
3834 strcmp(proto
, ce
->cache_proto
) != 0)
3837 if (versp
== NULL
||
3838 (versp
!= NULL
&& *versp
== ce
->cache_reqvers
) ||
3839 (versp
!= NULL
&& *versp
== ce
->cache_outvers
)) {
3841 *versp
= ce
->cache_outvers
;
3842 state
= ce
->cache_state
;
3844 /* increment the host cache hit counters */
3846 if (state
== GOODHOST
)
3847 goodhost_cache_hits
++;
3848 if (state
== DEADHOST
)
3849 deadhost_cache_hits
++;
3850 #endif /* CACHE_DEBUG */
3851 (void) rw_unlock(&cache_lock
);
3855 (void) rw_unlock(&cache_lock
);
3860 * Free a cache entry and all entries
3861 * further down the chain since they
3862 * will also be expired.
3866 struct cache_entry
*entry
;
3868 struct cache_entry
*ce
, *next
= NULL
;
3870 for (ce
= entry
; ce
; ce
= next
) {
3872 free(ce
->cache_host
);
3873 if (ce
->cache_proto
)
3874 free(ce
->cache_proto
);
3875 next
= ce
->cache_next
;
3884 (void) rw_wrlock(&cache_lock
);
3885 cache_free(cache_head
);
3887 (void) rw_unlock(&cache_lock
);
3893 mutex_lock(&cleanup_lock
);
3894 cond_signal(&cleanup_start_cv
);
3895 (void) cond_wait(&cleanup_done_cv
, &cleanup_lock
);
3896 mutex_unlock(&cleanup_lock
);
3898 portmap_cache_flush();
3903 * Returns 1, if port option is NFS_PORT or
3904 * nfsd is running on the port given
3905 * Returns 0, if both port is not NFS_PORT and nfsd is not
3906 * running on the port.
3910 is_nfs_port(char *opts
)
3913 uint_t nfs_port
= 0;
3918 m
.mnt_mntopts
= opts
;
3921 * Get port specified in options list, if any.
3923 got_port
= nopt(&m
, MNTOPT_PORT
, (int *)&nfs_port
);
3926 * if no port specified or it is same as NFS_PORT return nfs
3927 * To use any other daemon the port number should be different
3929 if (!got_port
|| nfs_port
== NFS_PORT
)
3932 * If daemon is nfsd, return nfs
3934 if (getservbyport_r(nfs_port
, NULL
, &sv
, buf
, 256) == &sv
&&
3935 strcmp(sv
.s_name
, "nfsd") == 0)
3946 * destroy_auth_client_handle(cl)
3947 * destroys the created client handle
3950 destroy_auth_client_handle(CLIENT
*cl
)
3955 drop_alloc("AUTH_HANDLE", cl
->cl_auth
,
3956 __FILE__
, __LINE__
);
3958 AUTH_DESTROY(cl
->cl_auth
);
3962 drop_alloc("CLNT_HANDLE", cl
,
3963 __FILE__
, __LINE__
);
3971 * Attempt to figure out which version of NFS to use in pingnfs(). If
3972 * the version number was specified (i.e., non-zero), then use it.
3973 * Otherwise, default to the compiled-in default or the default as set
3974 * by the /etc/default/nfs configuration (as read by read_default().
3977 set_versrange(rpcvers_t nfsvers
, rpcvers_t
*vers
, rpcvers_t
*versmin
)
3981 *vers
= vers_max_default
;
3982 *versmin
= vers_min_default
;
3993 *vers
= NFS_VERSION
; /* version 2 */
3994 *versmin
= NFS_VERSMIN
; /* version 2 */
4004 * trace_portmap_cache()
4005 * traces the portmap cache values at desired points
4008 trace_portmap_cache()
4010 syslog(LOG_ERR
, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
4011 portmap_cache_accesses
, portmap_cache_lookups
,
4012 portmap_cache_hits
);
4016 * trace_host_cache()
4017 * traces the host cache values at desired points
4023 "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
4024 host_cache_accesses
, host_cache_lookups
, deadhost_cache_hits
,
4025 goodhost_cache_hits
);
4027 #endif /* CACHE_DEBUG */
4030 * Read the NFS SMF properties to determine if the
4031 * client has been configured for a new min/max for the NFS version to
4035 #define SVC_NFS_CLIENT "svc:/network/nfs/client"
4038 read_default_nfs(void)
4040 static time_t lastread
= 0;
4047 ret
= nfs_smf_get_prop("client_versmin", defval
, DEFAULT_INSTANCE
,
4048 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
4051 tmp
= strtol(defval
, (char **)NULL
, 10);
4053 vers_min_default
= tmp
;
4058 ret
= nfs_smf_get_prop("client_versmax", defval
, DEFAULT_INSTANCE
,
4059 SCF_TYPE_INTEGER
, SVC_NFS_CLIENT
, &bufsz
);
4062 tmp
= strtol(defval
, (char **)NULL
, 10);
4064 vers_max_default
= tmp
;
4068 lastread
= buf
.st_mtime
;
4071 * Quick sanity check on the values picked up from the
4072 * defaults file. Make sure that a mistake wasn't
4073 * made that will confuse things later on.
4074 * If so, reset to compiled-in defaults
4076 if (vers_min_default
> vers_max_default
||
4077 vers_min_default
< NFS_VERSMIN
||
4078 vers_max_default
> NFS_VERSMAX
) {
4081 " read_default: version minimum/maximum incorrectly configured\n");
4083 " read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
4084 vers_min_default
, vers_max_default
,
4085 NFS_VERSMIN_DEFAULT
,
4086 NFS_VERSMAX_DEFAULT
);
4088 vers_min_default
= NFS_VERSMIN_DEFAULT
;
4089 vers_max_default
= NFS_VERSMAX_DEFAULT
;
4094 * Find the mnttab entry that corresponds to "name".
4095 * We're not sure what the name represents: either
4096 * a mountpoint name, or a special name (server:/path).
4097 * Return the last entry in the file that matches.
4099 static struct extmnttab
*
4100 mnttab_find(dirname
)
4104 struct extmnttab mnt
;
4105 struct extmnttab
*res
= NULL
;
4107 fp
= fopen(MNTTAB
, "r");
4110 trace_prt(1, " mnttab_find: unable to open mnttab\n");
4113 while (getextmntent(fp
, &mnt
, sizeof (struct extmnttab
)) == 0) {
4114 if (strcmp(mnt
.mnt_mountp
, dirname
) == 0 ||
4115 strcmp(mnt
.mnt_special
, dirname
) == 0) {
4118 res
= fsdupmnttab(&mnt
);
4126 trace_prt(1, " mnttab_find: unable to find %s\n",
4133 * This function's behavior is taken from nfsstat.
4134 * Trying to determine what NFS version was used for the mount.
4137 is_v4_mount(char *mntpath
)
4139 kstat_ctl_t
*kc
= NULL
; /* libkstat cookie */
4142 struct mntinfo_kstat mik
;
4143 struct extmnttab
*mntp
;
4146 if ((mntp
= mnttab_find(mntpath
)) == NULL
)
4149 /* save the minor number and free the struct so we don't forget */
4150 mnt_minor
= mntp
->mnt_minor
;
4153 if ((kc
= kstat_open()) == NULL
)
4156 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
4157 if (ksp
->ks_type
!= KSTAT_TYPE_RAW
)
4159 if (strcmp(ksp
->ks_module
, "nfs") != 0)
4161 if (strcmp(ksp
->ks_name
, "mntinfo") != 0)
4163 if (mnt_minor
!= ksp
->ks_instance
)
4166 if (kstat_read(kc
, ksp
, &mik
) == -1)
4169 (void) kstat_close(kc
);
4170 if (mik
.mik_vers
== 4)
4175 (void) kstat_close(kc
);
4181 create_homedir(const char *src
, const char *dst
) {
4185 struct passwd
*pwd
, pwds
;
4186 char buf_pwd
[NSS_BUFLEN_PASSWD
];
4192 trace_prt(1, "entered create_homedir\n");
4194 if (stat(src
, &stbuf
) == 0) {
4196 trace_prt(1, "src exists\n");
4200 dst_username
= strrchr(dst
, '/');
4202 dst_username
++; /* Skip over slash */
4203 pwd
= getpwnam_r(dst_username
, &pwds
, buf_pwd
,
4212 homedir_len
= strlen(pwd
->pw_dir
);
4213 dst_dir_len
= strlen(dst
) - homedir_len
;
4214 src_dir_len
= strlen(src
) - homedir_len
;
4216 /* Check that the paths are in the same zone */
4217 if (src_dir_len
< dst_dir_len
||
4218 (strncmp(dst
, src
, dst_dir_len
) != 0)) {
4220 trace_prt(1, " paths don't match\n");
4223 /* Check that mountpoint is an auto_home entry */
4224 if (dst_dir_len
< 0 ||
4225 (strcmp(pwd
->pw_dir
, dst
+ dst_dir_len
) != 0)) {
4229 /* Check that source is an home directory entry */
4230 if (src_dir_len
< 0 ||
4231 (strcmp(pwd
->pw_dir
, src
+ src_dir_len
) != 0)) {
4233 trace_prt(1, " homedir (2) doesn't match %s\n",
4239 S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
) == -1) {
4241 trace_prt(1, " Couldn't mkdir %s\n", src
);
4246 if (chown(src
, pwd
->pw_uid
, pwd
->pw_gid
) == -1) {
4251 /* Created new home directory for the user */
4256 free_nfs_args(struct nfs_args
*argp
)
4258 struct nfs_args
*oldp
;
4261 free(argp
->pathconf
);
4263 free_knconf(argp
->knconf
);
4265 netbuf_free(argp
->addr
);
4267 netbuf_free(argp
->syncaddr
);
4269 free(argp
->netname
);
4271 free(argp
->hostname
);
4272 if (argp
->nfs_ext_u
.nfs_extB
.secdata
)
4273 nfs_free_secdata(argp
->nfs_ext_u
.nfs_extB
.secdata
);
4276 if (argp
->nfs_ext_u
.nfs_extA
.secdata
) {
4278 sd
= argp
->nfs_ext_u
.nfs_extA
.secdata
;
4281 switch (sd
->rpcflavor
) {
4288 dh_k4_clntdata_t
*dhk4
;
4289 dhk4
= (dh_k4_clntdata_t
*)sd
->data
;
4292 if (dhk4
->syncaddr
.buf
)
4293 free(dhk4
->syncaddr
.buf
);
4294 if (dhk4
->knconf
->knc_protofmly
)
4295 free(dhk4
->knconf
->knc_protofmly
);
4296 if (dhk4
->knconf
->knc_proto
)
4297 free(dhk4
->knconf
->knc_proto
);
4301 free(dhk4
->netname
);
4307 gss_clntdata_t
*gss
;
4308 gss
= (gss_clntdata_t
*)sd
->data
;
4311 if (gss
->mechanism
.elements
)
4312 free(gss
->mechanism
.elements
);
4319 if (argp
->nfs_args_ext
== NFS_ARGS_EXTB
)
4320 argp
= argp
->nfs_ext_u
.nfs_extB
.next
;
4328 get_netconfig_info(enum type_of_stuff type_of_stuff
, char *hostname
,
4329 rpcprog_t prog
, rpcvers_t vers
, struct netconfig
*nconf
,
4330 ushort_t port
, struct t_info
*tinfo
, struct t_bind
*tbind
,
4331 caddr_t
*fhp
, bool_t direct_to_server
, char *fspath
,
4332 enum clnt_stat
*cstat
, mfs_snego_t
*mfssnego
)
4334 struct netconfig
*nb
= NULL
;
4335 int ping_server
= 0;
4341 switch (type_of_stuff
) {
4343 nb
= get_server_fh(hostname
, prog
, vers
, mfssnego
,
4344 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4350 nb
= get_server_addrorping(hostname
, prog
, vers
,
4351 nconf
, port
, tinfo
, tbind
, fhp
, direct_to_server
,
4352 fspath
, cstat
, ping_server
);
4361 * Get the server address or can we ping it or not.
4362 * Check the portmap cache first for server address.
4363 * If no entries there, ping the server with a NULLPROC rpc.
4366 get_server_addrorping(char *hostname
, rpcprog_t prog
, rpcvers_t vers
,
4367 struct netconfig
*nconf
, ushort_t port
, struct t_info
*tinfo
,
4368 struct t_bind
*tbind
, caddr_t
*fhp
, bool_t direct_to_server
,
4369 char *fspath
, enum clnt_stat
*cstat
, int ping_server
)
4372 enum clnt_stat cs
= RPC_TIMEDOUT
;
4373 struct netbuf
*nb
= NULL
;
4377 if (prog
== NFS_PROGRAM
&& vers
== NFS_V4
)
4378 if (strncasecmp(nconf
->nc_proto
, NC_UDP
, strlen(NC_UDP
)) == 0)
4381 if ((fd
= t_open(nconf
->nc_device
, O_RDWR
, tinfo
)) < 0) {
4385 /* LINTED pointer alignment */
4386 if ((tbind
= (struct t_bind
*)t_alloc(fd
, T_BIND
, T_ADDR
))
4391 if (direct_to_server
!= TRUE
) {
4393 if (get_cached_srv_addr(hostname
, prog
, vers
,
4394 nconf
, &tbind
->addr
) == 0)
4401 if (setup_nb_parms(nconf
, tbind
, tinfo
, hostname
,
4402 fd
, direct_to_server
, port
, prog
, vers
, 0) < 0)
4405 if (port
|| (direct_to_server
== TRUE
)) {
4408 cl
= clnt_tli_create(fd
, nconf
, &tbind
->addr
,
4413 cs
= clnt_call(cl
, NULLPROC
, xdr_void
, 0,
4415 if (cs
!= RPC_SUCCESS
) {
4416 syslog(LOG_ERR
, "error is %d", cs
);
4421 nb
= (struct netbuf
*)malloc(sizeof (struct netbuf
));
4423 syslog(LOG_ERR
, "no memory\n");
4426 nb
->buf
= (char *)malloc(tbind
->addr
.maxlen
);
4427 if (nb
->buf
== NULL
) {
4428 syslog(LOG_ERR
, "no memory\n");
4433 (void) memcpy(nb
->buf
, tbind
->addr
.buf
, tbind
->addr
.len
);
4434 nb
->len
= tbind
->addr
.len
;
4435 nb
->maxlen
= tbind
->addr
.maxlen
;
4439 destroy_auth_client_handle(cl
);
4440 cleanup_tli_parms(tbind
, fd
);