autod_nfs.c revision d7c6abe8e540900fa433af7a78a3f74392bb6a25
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <syslog.h>
#include <string.h>
#include <deflt.h>
#include <kstat.h>
#include <signal.h>
#include <rpc/pmap_clnt.h>
#include <netdb.h>
#include <netconfig.h>
#include <netdir.h>
#include <errno.h>
#define NFSCLIENT
#include <locale.h>
#include <setjmp.h>
#include <thread.h>
#include <limits.h>
#include <nss_dbdefs.h> /* for NSS_BUFLEN_HOSTS */
#include <assert.h>
#include <nfs/nfs_clnt.h>
#include <rpcsvc/nfs4_prot.h>
#define NO_RDDIR_CACHE
#include "automount.h"
#include "replica.h"
#include "nfs_subr.h"
#include "webnfs.h"
#include <assert.h>
#include <rpcsvc/daemon_utils.h>
#include <pwd.h>
#include <strings.h>
#include <zone.h>
extern char *nfs_get_qop_name();
extern AUTH *nfs_create_ah();
extern enum snego_stat nfs_sec_nego();
#define MAXHOSTS 512
/* number of transports to try */
#define MNT_PREF_LISTLEN 2
#define FIRST_TRY 1
#define SECOND_TRY 2
#define MNTTYPE_CACHEFS "cachefs"
/*
* host cache states
*/
#define NOHOST 0
#define GOODHOST 1
#define DEADHOST 2
struct cache_entry {
struct cache_entry *cache_next;
char *cache_host;
int cache_state;
char *cache_proto;
};
struct mfs_snego_t {
int sec_opt;
char *nfs_flavor;
};
typedef struct mfs_snego_t mfs_snego_t;
action_list *);
static int is_nfs_port(char *);
void netbuf_free(struct netbuf *);
void free_knconf(struct knetconfig *);
bool_t, char *);
static int create_homedir(const char *, const char *);
enum type_of_stuff {
SERVER_ADDR = 0,
SERVER_PING = 1,
SERVER_FH = 2
};
static char *dump_distance(struct mapfs *);
static void cache_free(struct cache_entry *);
static int cache_check(char *, rpcvers_t *, char *);
#ifdef CACHE_DEBUG
static void trace_host_cache();
static void trace_portmap_cache();
#endif /* CACHE_DEBUG */
static int rpc_timeout = 20;
#ifdef CACHE_DEBUG
/*
* host cache counters. These variables do not need to be protected
* by mutex's. They have been added to measure the utility of the
*/
static int host_cache_accesses = 0;
static int host_cache_lookups = 0;
static int deadhost_cache_hits = 0;
static int goodhost_cache_hits = 0;
/*
* portmap cache counters. These variables do not need to be protected
* by mutex's. They have been added to measure the utility of the portmap
* cache in the lazy hierarchical mounting scheme.
*/
static int portmap_cache_accesses = 0;
static int portmap_cache_lookups = 0;
static int portmap_cache_hits = 0;
#endif /* CACHE_DEBUG */
/*
* There are the defaults (range) for the client when determining
* which NFS version to use when probing the server (see above).
* These will only be used when the vers mount option is not used and
*/
/*
* list of support services needed
*/
static void read_default_nfs(void);
static int is_v4_mount(char *);
static void start_nfs4cbd(void);
int
char *mntpnt,
char *prevhost,
int overlay,
action_list **alpp)
{
int err = -1;
int cached;
return (ENOENT);
/*
* Try loopback if we have something on localhost; if nothing
* works, we will fall back to NFS
*/
if (err) {
} else {
/*
* Free action_list if there
* is one as it is not needed.
* Make sure to set alpp to null
* so caller doesn't try to free it
* again.
*/
if (*alpp) {
}
break;
}
}
}
}
if (err) {
}
}
return (err);
}
/*
* Using the new ioctl SIOCTONLINK to determine if a host is on the same
* subnet. Remove the old network, subnet check.
*/
static struct mapfs *
{
int s;
struct nd_hostserv hs;
struct nd_addrlist *retaddrs;
struct sioc_addrreq areq;
int res;
int af;
int i;
int sa_size;
nc = setnetconfig();
/*
* Care about INET family only. proto_done flag
* indicates if we have already covered this
* protocol family. If so skip it
*/
} else
continue;
continue;
/*
* For each host address see if it's on our
* local subnet.
*/
else
if (!p) {
return (NULL);
}
break;
}
} /* end of every host */
if (trace > 2) {
}
} /* end of while */
} /* end of every map */
return (mfs_head);
}
int
{
int s;
return (0);
}
return (0);
}
close(s);
return (1);
else
return (0);
}
/*
* ping a bunch of hosts at once and sort by who responds first
*/
static struct mapfs *
{
if (!mfs_in)
return (NULL);
if (!m1) {
}
}
return (m1);
}
/*
* Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
* provided it is not marked "ignored" and isn't a dupe of ones we've
* already seen.
*/
struct mapfs *
{
return (*mfs_head);
if (!new) {
return (NULL);
}
if (distance)
if (!*mfs_head)
else {
}
return (*mfs_head);
}
static void
{
return;
if (!mfs) {
trace_prt(0, "mfs is null\n");
return;
}
trace_prt(0, "\n");
}
static char *
{
switch (mfs->mfs_distance) {
case 0: return ("zero");
case DIST_SELF: return ("self");
case DIST_MYSUB: return ("mysub");
case DIST_MYNET: return ("mynet");
case DIST_OTHER: return ("other");
default: return ("other");
}
}
/*
* Walk linked list "raw", building a new list consisting of members
* NOT found in list "filter", returning the result.
*/
static struct mapfs *
{
int skip;
if (!raw)
return (NULL);
skip = 1;
break;
}
}
if (skip)
continue;
if (!p)
return (NULL);
}
return (mfs_head);
}
/*
* Walk a linked list of mapfs structs, freeing each member.
*/
void
{
while (mfs) {
}
}
/*
* New code for NFS client failover: we need to carry and sort
* lists of server possibilities rather than return a single
* entry. It preserves previous behaviour of sorting first by
* locality (loopback-or-preferred/subnet/net/other) and then
* by ping times. We'll short-circuit this process when we
* have ENOUGH or more entries.
*/
static struct mapfs *
{
/*
* Short-circuit for simple cases.
*/
if (!p)
return (NULL);
return (mfs_head);
}
/*
* get addresses & see if any are myself
* or were mounted from previously in a
* hierarchical mount.
*/
if (trace > 2)
if (m1->mfs_ignore)
continue;
if (!p)
return (NULL);
}
}
/*
* look for entries on this subnet
*/
}
if (!p)
return (NULL);
}
if (m1)
/*
* add the rest of the entries at the end
*/
if (m1)
if (!p)
return (NULL);
}
if (m1)
done:
return (mfs_head);
}
static enum nfsstat
{
char mopts[MAX_MNTOPT_STR];
int mnttabcnt = 0;
int loglevel;
struct mnttab m;
int flags;
/* and mount version with mountd */
/* used to negotiate nfs version using webnfs */
int posix;
struct nd_addrlist *retaddrs;
char *fstype;
int count, i;
char scerror_msg[MAXMSGLEN];
int *auths;
int delay;
int retries;
int error, last_error = 0;
int replicated;
int entries = 0;
int skipentry = 0;
char *nfs_flavor;
int secflags;
int got_val;
m.mnt_mntopts = opts;
if (verbose)
"mount on %s is soft and will not be replicated.", mntpnt);
replicated = 0;
}
if (verbose)
"mount on %s is not read-only and will not be replicated.",
mntpnt);
replicated = 0;
}
if (replicated && cached) {
if (verbose)
"mount on %s is cached and will not be replicated.",
mntpnt);
replicated = 0;
}
if (replicated)
else
if (trace > 1) {
if (replicated)
else
}
/*
* Make sure mountpoint is safe to mount on
*/
return (NFSERR_NOENT);
}
/*
* Get protocol specified in options list, if any.
*/
return (NFSERR_NOENT);
}
/*
* Get port specified in options list, if any.
*/
if (!got_val)
nfs_port = 0; /* "unspecified" */
return (NFSERR_NOENT);
}
/*
* Set mount(2) flags here, outside of the loop.
*/
/* direct mount point without offsets */
flags |= MS_OVERLAY;
"conflicting security options");
return (NFSERR_IO);
}
"error getting dh information from %s",
return (NFSERR_IO);
}
}
if ((str_opt(&m, MNTOPT_SEC,
return (NFSERR_IO);
}
}
if (mfssnego_init.nfs_flavor) {
"conflicting security options");
return (NFSERR_IO);
}
&mfssnego_init.nfs_sec)) {
"error getting %s information from %s",
return (NFSERR_IO);
}
}
skipentry = 0;
if (!got_val)
nfsvers = 0; /* "unspecified" */
mntpnt);
goto ret;
}
if (nfsvers != 0) {
} else {
pubversmax = vers;
}
/*
* Walk the whole list, pinging and collecting version
* info so that we can make sure the mount will be
* homogeneous with respect to version.
*
* If we have a version preference, this is easy; we'll
* just reject anything that doesn't match.
*
* If not, we want to try to provide the best compromise
* that considers proximity, preference for a higher version,
* sorted order, and number of replicas. We will count
* the number of V2 and V3 replicas and also the number
* which are "near", i.e. the localhost or on the same
* subnet.
*/
if (mfs->mfs_ignore)
continue;
/*
* If the host is '[a:d:d:r:e:s:s'],
* only use 'a:d:d:r:e:s:s' for communication
*/
goto out;
}
char *path;
" not the same as port (%d) in port "
goto out;
} else if (nfs_port != 0)
else
goto out;
}
path[0] = (char)WNL_NATIVEPATH;
} else {
}
if (!argp) {
goto out;
}
/*
* RDMA support
* By now Mount argument struct has been allocated,
* either a pub_fh path will be taken or the regular
* one. So here if a protocol was specified and it
* was not rdma we let it be, else we set DO_RDMA.
* If no proto was there we advise on trying RDMA.
*/
if (nfs_proto) {
}
} else
pubvers--) {
break;
}
/*
* The use of llock option for NFSv4
* mounts is not required since file
* locking is included within the protocol
*/
} else {
/*
* If -public was specified, give up
* on this entry now.
*/
"%s: no public file handle support",
host);
continue;
}
/*
* Back off to a conventional mount.
*
* URL's can contain escape characters. Get
* rid of them.
*/
goto out;
}
}
}
if (i != RPC_SUCCESS) {
if (i == RPC_PROGVERSMISMATCH) {
"protocol version mismatch",
host);
} else {
"responding", host);
}
continue;
}
"NFS version %d "
"not supported by %s",
else
"NFS version %d "
"with proto %s "
"not supported by %s",
continue;
}
}
switch (vers) {
case NFS_VERSION: v2cnt++; break;
default: break;
}
/*
* It's not clear how useful this stuff is if
* we are using webnfs across the internet, but it
* can't hurt.
*/
if (mfs->mfs_distance &&
switch (vers) {
case NFS_VERSION: v2near++; break;
default: break;
}
}
/*
* If the mount is not replicated, we don't want to
* ping every entry, so we'll stop here. This means
* that we may have to go back to "nextentry" above
* to consider another entry if we can't get
* all the way to mount(2) with this one.
*/
if (!replicated)
break;
}
if (nfsvers == 0) {
/*
* Choose the NFS version.
* We prefer higher versions, but will choose a one-
* version downgrade in service if we can use a local
* network interface and avoid a router.
*/
else
if (trace > 2)
trace_prt(1,
" nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
}
/*
* Since we don't support different NFS versions in replicated
* mounts, set fstype now.
* Also take the opportunity to set
* the mount protocol version as appropriate.
*/
switch (nfsvers) {
case NFS_V4:
break;
case NFS_V3:
}
break;
case NFS_VERSION:
}
break;
}
/*
* Our goal here is to evaluate each of several possible
* replicas and try to come up with a list we can hand
* to mount(2). If we don't have a valid "head" at the
* end of this process, it means we have rejected all
* potential server:/path tuples. We will fail quietly
* in front of mount(2), and will have printed errors
* where we found them.
* XXX - do option work outside loop w careful design
* XXX - use macro for error condition free handling
*/
/*
* Initialize retry and delay values on a per-server basis.
*/
if (mfs->mfs_ignore)
continue;
/*
* If we don't have a fh yet, and if this is not a replicated
* mount, we haven't done a pingnfs() on the next entry,
* so we don't know if the next entry is up or if it
* supports an NFS version we like. So if we had a problem
* with an entry, we need to go back and run through some new
* code.
*/
!replicated && skipentry)
goto nextentry;
vers = mountversmax;
/*
* Remember the possible '[a:d:d:r:e:s:s]' as the address to be
* later passed to mount(2) and used in the mnttab line, but
* only use 'a:d:d:r:e:s:s' for communication
*/
goto out;
}
/*
* If it's cached we need to get cachefs to mount it.
*/
if (cached) {
/*
* If we started with a URL we need to turn on
* -o public if not on already
*/
goto out;
}
}
if (last_error) {
skipentry = 1;
continue;
}
goto out;
}
/*
* Allocate nfs_args structure
*/
if (!argp) {
goto out;
}
/*
* RDMA support
* By now Mount argument struct has been allocated,
* either a pub_fh path will be taken or the regular
* one. So here if a protocol was specified and it
* was not rdma we let it be, else we set DO_RDMA.
* If no proto was there we advise on trying RDMA.
*/
if (nfs_proto) {
}
} else
} else {
/*
* Skip entry if we already have file handle but the
* NFS version is wrong.
*/
skipentry = 1;
continue;
}
}
if (!head)
else
/*
* WebNFS and NFSv4 behave similarly in that they
* don't use the mount protocol. Therefore, avoid
* mount protocol like things when version 4 is being
* used.
*/
/* Create the client handle. */
if (trace > 1) {
trace_prt(1,
" nfsmount: Get mount version: request "
}
if (trace > 4) {
trace_prt(1,
" nfsmount: Can't get mount "
"version: rpcerr=%d\n",
}
break;
/*
* backoff and return lower version to retry the ping.
* XXX we should be more careful and handle
* RPC_PROGVERSMISMATCH here, because that error
* is handled in clnt_create_vers(). It's not done to
* stay in sync with the nfs mount command.
*/
vers--;
break;
if (trace > 4) {
trace_prt(1,
" nfsmount: Try version=%d\n",
vers);
}
}
if (tail)
retries-- > 0) {
goto retry;
}
"server not responding"));
skipentry = 1;
continue;
}
if (trace > 1) {
trace_prt(1,
" nfsmount: mount version=%d\n", outvers);
}
#ifdef MALLOC_DEBUG
#endif
if (__clnt_bindresvport(cl) < 0) {
if (tail)
if (retries-- > 0) {
goto retry;
}
"Couldn't bind to reserved port");
skipentry = 1;
continue;
}
#ifdef MALLOC_DEBUG
#endif
if (tail)
if (retries-- > 0) {
goto retry;
}
"Failed creating default auth handle");
skipentry = 1;
continue;
}
#ifdef MALLOC_DEBUG
#endif
} else
/*
* set security options
*/
sec_opt = 0;
if (++sec_opt > 1) {
"conflicting security options for %s",
remname);
if (tail)
skipentry = 1;
continue;
}
"error getting dh information from %s",
if (tail)
skipentry = 1;
continue;
}
}
nfs_flavor = NULL;
goto out;
}
}
if (nfs_flavor) {
if (++sec_opt > 1) {
"conflicting security options for %s",
remname);
if (tail)
skipentry = 1;
continue;
}
"error getting %s information from %s",
if (tail)
skipentry = 1;
continue;
}
}
/*
* If we started with a URL, if first byte of path is not "/",
* then the mount will likely fail, so we should try again
* with a prepended "/".
*/
else
if (got_mnt_error == TRUE) {
int i, l;
/*
* Insert a "/" to front of mfs_dir.
*/
for (i = l; i > 0; i--)
dir[0] = '/';
}
/* Get fhandle of remote path from server's mountd */
switch (outvers) {
case MOUNTVERS:
if (posix) {
if (tail)
NULL;
"can't get posix info for %s",
host);
skipentry = 1;
continue;
}
/* FALLTHRU */
case MOUNTVERS_POSIX:
if (tail)
NULL;
"%s doesn't support NFS Version 3",
host);
skipentry = 1;
continue;
}
if (rpc_stat != RPC_SUCCESS) {
if (give_up_on_mnt == FALSE) {
goto try_mnt_slash;
}
/*
* Given the way "clnt_sperror" works, the "%s"
* immediately following the "not responding"
* is correct.
*/
if (tail)
NULL;
if (retries-- > 0) {
goto retry;
}
if (trace > 3) {
trace_prt(1,
" nfsmount: mount RPC "
"failed for %s\n",
host);
}
"%s server not responding%s",
skipentry = 1;
continue;
}
if (give_up_on_mnt == FALSE) {
goto try_mnt_slash;
}
if (tail)
NULL;
} else {
host);
}
if (trace > 3) {
trace_prt(1,
" nfsmount: mount RPC gave"
" %d for %s:%s\n",
}
last_error = status;
skipentry = 1;
continue;
}
goto out;
}
sizeof (fhandle));
break;
case MOUNTVERS3:
posix = 0;
sizeof (res3));
if (rpc_stat != RPC_SUCCESS) {
if (give_up_on_mnt == FALSE) {
goto try_mnt_slash;
}
/*
* Given the way "clnt_sperror" works, the "%s"
* immediately following the "not responding"
* is correct.
*/
if (tail)
NULL;
if (retries-- > 0) {
goto retry;
}
if (trace > 3) {
trace_prt(1,
" nfsmount: mount RPC "
"failed for %s\n",
host);
}
"%s server not responding%s",
skipentry = 1;
continue;
}
if (give_up_on_mnt == FALSE) {
goto try_mnt_slash;
}
if (tail)
NULL;
} else {
remname);
}
if (trace > 3) {
trace_prt(1,
" nfsmount: mount RPC gave"
" %d for %s:%s\n",
}
last_error = status;
skipentry = 1;
continue;
}
/*
* Negotiate the security flavor for nfs_mount
*/
if (sec_opt) {
for (i = 0; i < count; i++)
if (auths[i] ==
break;
}
if (i >= count) {
"%s: does not support "
"security \"%s\"\n",
if (tail)
NULL;
skipentry = 1;
continue;
}
} else if (count > 0) {
for (i = 0; i < count; i++) {
if (!(scerror =
sec_opt++;
break;
}
}
if (i >= count) {
if (nfs_syslog_scerr(scerror,
!= -1) {
"%s cannot be "
"mounted because it"
" is shared with "
"security flavor %d"
" which %s",
auths[i-1],
}
if (tail)
NULL;
skipentry = 1;
continue;
}
}
goto out;
}
break;
default:
if (tail)
"unknown MOUNT version %ld on %s",
skipentry = 1;
continue;
} /* switch */
}
goto out;
}
}
if (trace > 4)
goto out;
}
/*
* In this case, we want NFSv4 to behave like
* non-WebNFS so that we get the server address.
*/
if (nfs_port != 0)
else
/*
* For NFSv4, we want to avoid rpcbind, so call
* get_server_stuff() directly to tell it that
* we want to go "direct_to_server". Otherwise,
* do what has always been done.
*/
} else {
}
if (tail)
if (retries-- > 0) {
goto retry;
}
skipentry = 1;
continue;
}
if (trace > 4)
trace_prt(1,
"\tnfsmount: have net address for %s\n",
remname);
} else {
}
if (tail)
skipentry = 1;
continue;
}
if (trace > 4)
trace_prt(1,
"\tnfsmount: have net config for %s\n",
remname);
}
}
}
}
}
}
/*
* Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
*/
if (mfssnego.snego_done) {
sizeof (seconfig_t));
} else if (!sec_opt) {
/*
* Get default security mode.
*/
if (nfs_getseconfig_default(&nfs_sec)) {
"error getting default security entry\n");
if (tail)
skipentry = 1;
continue;
}
}
/*
* For AUTH_DH
* get the network address for the time service on
* the server. If an RPC based time service is
* not available then try the IP time service.
*
* Eventurally, we want to move this code to nfs_clnt_secdata()
* when autod_nfs.c and mount.c can share the same
* get_the_addr/get_the_stuff routine.
*/
secflags = 0;
/*
* If not using the public fh and not NFS_V4, we can try
* talking RPCBIND. Otherwise, assume that firewalls
* prevent us from doing that.
*/
}
/* for flags in sec_data */
} else {
struct nd_hostserv hs;
int error;
"%s: secure: no time service\n",
host);
if (tail)
NULL;
skipentry = 1;
continue;
}
/*
* For potential usage by NFS V4 when AUTH_DH
* is negotiated via SECINFO in the kernel.
*/
}
} /* syncaddr */
} /* AUTH_DH */
/*
* TSOL notes: automountd in tsol extension
* has "read down" capability, i.e. we allow
* a user to trigger an nfs mount into a lower
* labeled zone. We achieve this by always having
* root issue the mount request so that the
* lookup ops can go past /zone/<zone_name>
* on the server side.
*/
if (is_system_labeled())
else
/*
* If AUTH_DH is a chosen flavor now, its data will be stored
* in the sec_data structure via nfs_clnt_secdata().
*/
"errors constructing security related data\n");
if (secflags & AUTH_F_RPCTIMESYNC)
else if (retaddrs)
if (tail)
skipentry = 1;
continue;
}
/* end of security stuff */
if (trace > 4)
trace_prt(1,
" nfsmount: have secure info for %s\n", remname);
}
}
}
}
}
} else {
}
}
}
}
}
if (posix) {
if (secflags & AUTH_F_RPCTIMESYNC)
else if (retaddrs)
if (tail)
goto retry;
}
skipentry = 1;
continue;
}
if (trace > 4)
trace_prt(1,
" nfsmount: have pathconf for %s\n",
remname);
}
/*
* free loop-specific data structures
*/
if (secflags & AUTH_F_RPCTIMESYNC)
else if (retaddrs)
/*
* Decide whether to use remote host's lockd or local locking.
* If we are using the public fh, we've already turned
* LLOCK on.
*/
if (hasmntopt(&m, MNTOPT_LLOCK))
"contact admin to install server change", host);
}
/*
* If possible, coalesce strings with same 'dir' info.
*/
char *tmp;
if (mnttabcnt) {
} else {
*p = '\0';
}
mnttabtext = tmp;
} else {
mnttabtext = NULL;
}
} else {
mnttabtext[0] = '\0';
}
if (mnttabtext != NULL)
} else {
char *tmp;
int more_cnt = 0;
char sport[16];
} else
sport[0] = '\0';
if (mnttabcnt) {
mnttabtext = tmp;
} else {
mnttabtext = NULL;
}
} else {
mnttabtext[0] = '\0';
}
if (mnttabtext != NULL) {
}
}
if (!mnttabtext) {
goto out;
}
/*
* At least one entry, can call mount(2).
*/
entries++;
/*
* If replication was defeated, don't do more work
*/
if (!replicated)
break;
}
/*
* Did we get through all possibilities without success?
*/
if (!entries)
goto out;
/* Make "xattr" the default if "noxattr" is not specified. */
}
/*
* enable services as needed.
*/
{
char **sl;
else
sl = service_list;
(void) _check_services(sl);
}
/*
* Whew; do the mount, at last.
*/
if (trace > 1) {
}
/*
* If no action list pointer then do the mount, otherwise
* build the actions list pointer with the mount information.
* so the mount can be done in the kernel.
*/
if (trace > 1)
goto out;
}
last_error = NFS_OK;
if (trace > 1) {
}
} else {
if (trace > 1) {
}
}
} else {
sizeof (*head);
last_error = NFS_OK;
goto ret;
}
out:
while (argp) {
}
}
ret:
if (nfs_proto)
if (mnttabtext)
}
}
}
}
return (last_error);
}
/*
* get_pathconf(cl, path, fsname, pcnf, cretries)
* ugliness that requires that ppathcnf and pathcnf stay consistent
* cretries is a copy of retries used to determine when to syslog
* on retry situations.
*/
static int
int cretries)
{
if (p == NULL) {
return (RET_ERR);
}
if (rpc_stat != RPC_SUCCESS) {
if (cretries-- <= 0) {
"get_pathconf: %s: server not responding: %s",
}
free(p);
return (RET_RETRY);
}
free(p);
return (RET_ERR);
}
return (RET_OK);
}
struct knetconfig *
{
struct knetconfig *k;
return (NULL);
}
k = (struct knetconfig *)malloc(sizeof (*k));
if (k == NULL)
goto nomem;
if (k->knc_protofmly == NULL)
goto nomem;
goto nomem;
return (k);
free_knconf(k);
return (NULL);
}
void
free_knconf(k)
struct knetconfig *k;
{
if (k == NULL)
return;
if (k->knc_protofmly)
free(k->knc_protofmly);
if (k->knc_proto)
free(k);
}
void
{
return;
}
#define SMALL_HOSTNAME 20
#define SMALL_PROTONAME 10
#define SMALL_PROTOFMLYNAME 10
struct portmap_cache {
int cache_prog;
int cache_vers;
char *cache_hostname;
char *cache_proto;
char *cache_protofmly;
struct netbuf cache_srv_addr;
};
static int portmap_cache_valid_time = 30;
#ifdef MALLOC_DEBUG
void
{
(void) rw_wrlock(&portmap_cache_lock);
cp->cache_hostname !=
cp->cache_proto !=
}
(void) rw_unlock(&portmap_cache_lock);
}
#endif
/*
* Returns 1 if the entry is found in the cache, 0 otherwise.
*/
static int
char *hostname;
{
int retval = 0;
(void) rw_rdlock(&portmap_cache_lock);
/*
* Increment the portmap cache counters for # accesses and lookups
* Use a smaller factor (100 vs 1000 for the host cache) since
* initial analysis shows this cache is looked up 10% that of the
* host cache.
*/
#ifdef CACHE_DEBUG
if ((portmap_cache_lookups%100) == 0)
#endif /* CACHE_DEBUG */
/*
* We stumbled across an entry in the cache which
* has timed out. Free up all the entries that
* were added before it, which will positionally
* be after this entry. And adjust neighboring
* pointers.
* When we drop the lock and re-acquire it, we
* need to start from the beginning.
*/
(void) rw_unlock(&portmap_cache_lock);
(void) rw_wrlock(&portmap_cache_lock);
for (cp = portmap_cache_head;
;
goto done;
/*
* Adjust the link of the predecessor.
* Make the tail point to the new last entry.
*/
} else {
}
cp->cache_hostname !=
cp->cache_proto !=
}
goto done;
}
continue;
/*
* Cache Hit.
*/
#ifdef CACHE_DEBUG
portmap_cache_hits++; /* up portmap cache hit counter */
#endif /* CACHE_DEBUG */
retval = 1;
break;
}
done:
(void) rw_unlock(&portmap_cache_lock);
return (retval);
}
static void
char *hostname;
{
struct portmap_cache *cachep;
int protofmlylen;
int protolen, hostnamelen;
return;
if (hostnamelen <= SMALL_HOSTNAME)
else {
goto nomem;
}
if (protolen <= SMALL_PROTONAME)
else {
goto nomem;
}
if (protofmlylen <= SMALL_PROTOFMLYNAME)
else {
goto nomem;
}
goto nomem;
(void) rw_wrlock(&portmap_cache_lock);
/*
* There's a window in which we could have multiple threads making
* the same cache entry. This can be avoided by walking the cache
* once again here to check and see if there are duplicate entries
* (after grabbing the write lock). This isn't fatal and I'm not
* going to bother with this.
*/
#ifdef CACHE_DEBUG
portmap_cache_accesses++; /* up portmap cache access counter */
#endif /* CACHE_DEBUG */
if (portmap_cache_head != NULL)
(void) rw_unlock(&portmap_cache_lock);
return;
if (cachep)
}
static int
{
return (1);
return (0);
return (1);
}
/*
* Get the network address on "hostname" for program "prog"
* with version "vers" by using the nconf configuration data
* passed in.
*
* If the address of a netconfig pointer is null then
* information is not sufficient and no netbuf will be returned.
*
* tinfo argument is for matching the get_the_addr() defined in
*/
void *
enum type_of_stuff type_of_stuff,
char *hostname,
char *fspath,
{
int fd = -1;
goto done;
}
goto done;
goto done;
}
/* LINTED pointer alignment */
== NULL) {
goto done;
}
if (direct_to_server == TRUE) {
struct nd_hostserv hs;
struct nd_addrlist *retaddrs;
if (trace > 1)
"direct to server %s\n",
"unknown", hostname);
if (port == 0)
else
goto done;
}
if (port) {
/* LINTED pointer alignment */
((struct sockaddr_in *)
((struct sockaddr_in6 *)
}
if (type_of_stuff == SERVER_FH) {
NULL) == -1)
if (trace > 1)
"ND_SET_RESERVEDPORT(%s) "
"failed\n", hostname);
}
vers, 0, 0);
if (trace > 1)
goto done;
#ifdef MALLOC_DEBUG
#endif
switch (type_of_stuff) {
case SERVER_FH:
{
enum snego_stat sec;
ah = authsys_create_default();
#ifdef MALLOC_DEBUG
#endif
#ifdef MALLOC_DEBUG
#endif
}
/*
* negotiate sec flavor.
*/
int jj;
/*
* check if server supports the one
* specified in the sec= option.
*/
break;
}
}
}
/*
* find a common sec flavor
*/
if (!mfssnego->snego_done) {
if (!nfs_getseconfig_bynumber(
break;
}
}
}
if (!mfssnego->snego_done)
return (NULL);
/*
* Now that the flavor has been
* negotiated, get the fh.
*
* First, create an auth handle using the negotiated
* sec flavor in the next lookup to
* fetch the filehandle.
*/
goto done;
#ifdef MALLOC_DEBUG
#endif
#ifdef MALLOC_DEBUG
#endif
} else if (sec == SNEGO_ARRAY_TOO_SMALL ||
sec == SNEGO_FAILURE) {
goto done;
}
/*
* Note that if sec == SNEGO_DEF_VALID
* the default sec flavor is acceptable.
* Use it to get the filehandle.
*/
}
}
switch (vers) {
case NFS_VERSION:
{
goto done;
goto done;
}
sizeof (wnl_fh));
cs = RPC_SUCCESS;
}
break;
case NFS_V3:
{
goto done;
(char *)res);
goto done;
}
fh3p->fh3_length);
cs = RPC_SUCCESS;
}
break;
case NFS_V4:
if (cs != RPC_SUCCESS)
goto done;
break;
}
break;
case SERVER_ADDR:
case SERVER_PING:
if (trace > 1)
trace_prt(1,
"get_the_stuff: clnt_call(%s) "
"returned %s\n",
"failure");
if (cs != RPC_SUCCESS)
goto done;
break;
}
} else if (type_of_stuff != SERVER_FH) {
if (type_of_stuff == SERVER_ADDR) {
goto done;
}
if (port) {
/* LINTED pointer alignment */
((struct sockaddr_in *)
((struct sockaddr_in6 *)
goto done;
#ifdef MALLOC_DEBUG
#endif
0, tv);
if (cs != RPC_SUCCESS)
goto done;
}
} else {
/* can't happen */
goto done;
}
if (type_of_stuff != SERVER_PING) {
/*
* Make a copy of the netbuf to return
*/
goto done;
}
goto done;
}
cs = RPC_SUCCESS;
}
done:
#ifdef MALLOC_DEBUG
#endif
}
#ifdef MALLOC_DEBUG
#endif
}
if (tbind) {
}
if (fd >= 0)
return (nb);
}
/*
* Get a network address on "hostname" for program "prog"
* with version "vers". If the port number is specified (non zero)
* resulting IP address.
*
* If the address of a netconfig pointer was passed and
* if it's not null, use it as the netconfig otherwise
* assign the address of the netconfig that was used to
* establish contact with the service.
*
* tinfo argument is for matching the get_addr() defined in
*/
static struct netbuf *
{
}
static struct netbuf *
{
&cstat));
}
static enum clnt_stat
{
return (cstat);
}
void *
enum type_of_stuff type_of_stuff,
char *hostname,
char *proto,
char *fspath,
{
/*
* No nconf passed in.
*
* First choice is COTS, second is CLTS unless proto
* is specified. When we retry, we reset the
* netconfig list, so that we search the whole list
* for the next choice.
*/
goto done;
/*
* If proto is specified, then only search for the match,
* otherwise try COTS first, if failed, then try CLTS.
*/
if (proto) {
continue;
/*
*/
if (port) {
continue;
}
if (*cstatp == RPC_SUCCESS)
break;
} /* end of while */
goto done;
} else {
if ((nconf->nc_semantics ==
NC_TPI_COTS_ORD) ||
(nconf->nc_semantics ==
NC_TPI_COTS)) {
if (port == 0)
break;
NC_INET) == 0 ||
NC_INET6) == 0) &&
NC_TCP) == 0))
break;
}
}
if (nthtry == SECOND_TRY) {
if (nconf->nc_semantics ==
NC_TPI_CLTS) {
if (port == 0)
break;
NC_INET) == 0 ||
NC_INET6) == 0) &&
NC_UDP) == 0))
break;
}
}
}
} /* while */
if (++nthtry <= MNT_PREF_LISTLEN) {
endnetpath(nc);
goto done;
goto retry;
} else
goto done;
} else {
if (*cstatp != RPC_SUCCESS)
/*
* Continue the same search path in the
* netconfig db until no more matched nconf
* (nconf == NULL).
*/
goto retry;
}
} /* if !proto */
/*
* Got nconf and nb. Now dup the netconfig structure (nconf)
* and return it thru nconfp.
*/
}
done:
if (nc)
endnetpath(nc);
return (nb);
}
/*
* Sends a null call to the remote host's (NFS program, versp). versp
* may be "NULL" in which case the default maximum version is used.
* Upon return, versp contains the maximum version supported iff versp!= NULL.
*/
enum clnt_stat
char *hostpart,
int attempts,
char *path,
char *proto)
{
int i, j;
char *hostname;
return (RPC_SYSTEMERROR);
}
char *sport;
return (RPC_SYSTEMERROR);
/*
* This cannot happen. If it does, give up
* on the ping as this is obviously a corrupt
* entry.
*/
return (RPC_SUCCESS);
}
/*
* Probable end point of host string.
*/
*path = '\0';
/*
* Actual end point of host string.
*/
*sport = '\0';
}
}
/* Pick up the default versions and then set them appropriately */
if (versp) {
/* use versmin passed in */
} else {
}
if (proto &&
if (versp) {
return (RPC_SUCCESS);
}
return (RPC_PROGUNAVAIL);
} else {
versmax--;
}
}
if (versp)
case GOODHOST:
return (RPC_SUCCESS);
case DEADHOST:
return (RPC_TIMEDOUT);
case NOHOST:
default:
break;
}
/*
* XXX The retransmission time rpcbrmttime is a global defined
* in the rpc library (rpcb_clnt.c). We use (and like) the default
* value of 15 sec in the rpc library. The code below is to protect
* us in case it changes. This need not be done under a lock since
* any # of threads entering this function will get the same
* retransmission value.
*/
if (trace > 1)
}
/*
* XXX Manipulate the total timeout to get the number of
* desired retransmissions. This code is heavily dependant on
* the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
*/
if (j < RPC_MAX_BACKOFF)
j *= 2;
else
j = RPC_MAX_BACKOFF;
rpc_to_new.tv_sec += j;
}
/*
* check the host's version within the timeout
*/
if (trace > 1)
do {
/*
* If NFSv4, then we do the same thing as is used
* for public filehandles so that we avoid rpcbind
*/
if (vers_to_try == NFS_V4) {
if (trace > 4) {
"\"circuit_v\"\n");
}
break;
}
if (trace > 4) {
trace_prt(1,
" pingnfs: Can't ping via "
"\"circuit_v\" %s: RPC error=%d\n",
}
} else {
"datagram_v", &rpc_to_new);
break;
if (trace > 4) {
trace_prt(1,
" pingnfs: Can't ping via "
"\"datagram_v\"%s: RPC error=%d\n",
}
break;
if (rpc_createerr.cf_stat ==
if (trace > 4) {
trace_prt(1,
" pingnfs: Trying ping "
"via \"circuit_v\"\n");
}
"circuit_v", &rpc_to_new);
break;
if (trace > 4) {
trace_prt(1,
" pingnfs: Can't ping "
"via \"circuit_v\" %s: "
"RPC error=%d\n",
}
}
}
/*
* backoff and return lower version to retry the ping.
* XXX we should be more careful and handle
* RPC_PROGVERSMISMATCH here, because that error is handled
* in clnt_create_vers(). It's not done to stay in sync
* with the nfs mount command.
*/
vers_to_try--;
if (vers_to_try < versmin)
break;
*versp = vers_to_try;
if (trace > 4) {
trace_prt(1,
" pingnfs: check cache: vers=%d\n",
*versp);
}
case GOODHOST:
return (RPC_SUCCESS);
case DEADHOST:
return (RPC_TIMEDOUT);
case NOHOST:
default:
break;
}
}
if (trace > 4) {
}
if (verbose)
} else {
}
} else {
vers_to_try--) {
if (trace > 4) {
"using get_ping()\n", vers_to_try);
}
if (clnt_stat == RPC_SUCCESS) {
break;
}
}
}
if (trace > 1)
clnt_stat == RPC_SUCCESS ?
if (clnt_stat == RPC_SUCCESS) {
} else
return (clnt_stat);
}
#define MNTTYPE_LOFS "lofs"
int
char *fsname; /* Directory being mounted */
char *dir; /* Directory being mounted on */
char *mntopts;
int overlay;
{
int flags = 0;
char fstype[] = MNTTYPE_LOFS;
int dirlen;
char optbuf[MAX_MNTOPT_STR];
dirlen--;
"Mount of %s on %s would result in deadlock, aborted\n",
return (RET_ERR);
}
if (overlay)
flags |= MS_OVERLAY;
if (trace > 1)
trace_prt(1,
" loopbackmount: fsname=%s, dir=%s, flags=%d\n",
if (is_system_labeled()) {
if (create_homedir((const char *)fsname,
(const char *)dir) == 0) {
return (NFSERR_NOENT);
}
}
return (RET_ERR);
}
if (trace > 1) {
trace_prt(1,
" loopbackmount of %s on %s dev=%x rdev=%x OK\n",
}
} else {
if (trace > 1) {
trace_prt(1,
}
}
return (0);
}
/*
* Look for the value of a numeric option of the form foo=x. If found, set
* *valp to the value and return non-zero. If not found or the option is
* malformed, return zero.
*/
int
char *opt;
int *valp; /* OUT */
{
char *equal;
char *str;
/*
* We should never get a null pointer, but if we do, it's better to
* ignore the option than to dump core.
*/
return (0);
}
return (1);
} else {
}
}
return (0);
}
int
{
int i, count = 0;
if (trace > 1)
if (trace > 1)
mnt->mnt_mountp);
if (errno)
return (errno);
}
/*
* If this is a NFSv4 mount, the mount protocol was not used
* so we just return.
*/
if (isv4mount) {
if (trace > 1)
mnt->mnt_mountp);
return (0);
}
/*
* If mounted with -o public, then no need to contact server
* because mount protocol was not used.
*/
return (0);
}
/*
* The rest of this code is advisory to the server.
* If it fails return success anyway.
*/
if (!list) {
if (count >= 0)
"Memory allocation failed: %m");
return (ENOMEM);
}
for (i = 0; i < count; i++) {
/*
* Skip file systems mounted using WebNFS, because mount
* protocol was not used.
*/
continue;
break;
#ifdef MALLOC_DEBUG
#endif
if (__clnt_bindresvport(cl) < 0) {
if (verbose)
"Couldn't bind to reserved port");
continue;
}
#ifdef MALLOC_DEBUG
#endif
if (verbose)
"Failed creating default auth handle");
continue;
}
#ifdef MALLOC_DEBUG
#endif
}
if (trace > 1)
done:
return (0);
}
/*
* Put a new entry in the cache chain by prepending it to the front.
* If there isn't enough memory then just give up.
*/
static void
char *host;
char *proto;
int state;
{
struct cache_entry *entry;
return;
return;
}
(void) rw_wrlock(&cache_lock);
#ifdef CACHE_DEBUG
host_cache_accesses++; /* up host cache access counter */
#endif /* CACHE DEBUG */
cache_head = entry;
(void) rw_unlock(&cache_lock);
}
static int
char *host;
char *proto;
{
(void) rw_rdlock(&cache_lock);
#ifdef CACHE_DEBUG
/* Increment the lookup and access counters for the host cache */
if ((host_cache_lookups%1000) == 0)
#endif /* CACHE DEBUG */
(void) rw_unlock(&cache_lock);
(void) rw_wrlock(&cache_lock);
cache_free(ce);
if (prev)
else
cache_head = NULL;
break;
}
}
(void) rw_unlock(&cache_lock);
return (state);
}
continue;
continue;
continue;
/* increment the host cache hit counters */
#ifdef CACHE_DEBUG
#endif /* CACHE_DEBUG */
(void) rw_unlock(&cache_lock);
return (state);
}
}
(void) rw_unlock(&cache_lock);
return (state);
}
/*
* Free a cache entry and all entries
* further down the chain since they
* will also be expired.
*/
static void
struct cache_entry *entry;
{
if (ce->cache_host)
if (ce->cache_proto)
}
}
#ifdef MALLOC_DEBUG
void
{
(void) rw_wrlock(&cache_lock);
cache_head = NULL;
(void) rw_unlock(&cache_lock);
}
void
{
cache_flush();
}
#endif
/*
* Returns 1, if port option is NFS_PORT or
* nfsd is running on the port given
* Returns 0, if both port is not NFS_PORT and nfsd is not
* running on the port.
*/
static int
is_nfs_port(char *opts)
{
struct mnttab m;
char buf[256];
int got_port;
m.mnt_mntopts = opts;
/*
* Get port specified in options list, if any.
*/
/*
* if no port specified or it is same as NFS_PORT return nfs
* To use any other daemon the port number should be different
*/
return (1);
/*
* If daemon is nfsd, return nfs
*/
return (1);
/*
* daemon is not nfs
*/
return (0);
}
/*
* destroy_auth_client_handle(cl)
* destroys the created client handle
*/
void
{
if (cl) {
#ifdef MALLOC_DEBUG
#endif
}
#ifdef MALLOC_DEBUG
#endif
}
}
/*
* Attempt to figure out which version of NFS to use in pingnfs(). If
* the version number was specified (i.e., non-zero), then use it.
* Otherwise, default to the compiled-in default or the default as set
*/
int
{
switch (nfsvers) {
case 0:
*vers = vers_max_default;
break;
case NFS_V4:
break;
case NFS_V3:
break;
case NFS_VERSION:
break;
default:
return (-1);
}
return (0);
}
#ifdef CACHE_DEBUG
/*
* trace_portmap_cache()
* traces the portmap cache values at desired points
*/
static void
{
}
/*
* trace_host_cache()
* traces the host cache values at desired points
*/
static void
{
"host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
}
#endif /* CACHE_DEBUG */
/*
* use.
*/
static void
read_default_nfs(void)
{
char *defval;
int errno;
int tmp;
/*
* Fail silently if we can't stat the default nfs config file
*/
return;
return;
/*
* Fail silently if error in opening the default nfs config file
* We'll check back in NFS_DEFAULT_CHECK seconds
*/
errno = 0;
if (errno == 0) {
}
}
errno = 0;
if (errno == 0) {
}
}
/* close defaults file */
/*
* Quick sanity check on the values picked up from the
* defaults file. Make sure that a mistake wasn't
* made that will confuse things later on.
* If so, reset to compiled-in defaults
*/
if (vers_min_default > vers_max_default ||
if (trace > 1) {
trace_prt(1,
trace_prt(1,
" read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
}
}
}
}
/*
* Find the mnttab entry that corresponds to "name".
* We're not sure what the name represents: either
* a mountpoint name, or a special name (server:/path).
* Return the last entry in the file that matches.
*/
static struct extmnttab *
char *dirname;
{
if (trace > 1)
return (NULL);
}
if (res)
}
}
if (trace > 1)
dirname);
}
return (res);
}
/*
* This function's behavior is taken from nfsstat.
* Trying to determine what NFS version was used for the mount.
*/
static int
is_v4_mount(char *mntpath)
{
struct mntinfo_kstat mik;
return (FALSE);
/* save the minor number and free the struct so we don't forget */
return (FALSE);
continue;
continue;
continue;
continue;
continue;
(void) kstat_close(kc);
return (TRUE);
else
return (FALSE);
}
(void) kstat_close(kc);
return (FALSE);
}
static int
char *dst_username;
char buf_pwd[NSS_BUFLEN_PASSWD];
int homedir_len;
int dst_dir_len;
int src_dir_len;
if (trace > 1)
if (trace > 1)
return (1);
}
if (dst_username) {
dst_username++; /* Skip over slash */
sizeof (buf_pwd));
return (0);
}
} else {
return (0);
}
/* Check that the paths are in the same zone */
if (src_dir_len < dst_dir_len ||
if (trace > 1)
return (0);
}
/* Check that mountpoint is an auto_home entry */
if (dst_dir_len < 0 ||
return (0);
}
/* Check that source is an home directory entry */
if (src_dir_len < 0 ||
if (trace > 1)
return (0);
}
if (trace > 1) {
}
return (0);
}
return (0);
}
/* Created new home directory for the user */
return (1);
}
void
{
while (argp) {
sec_data_t *sd;
break;
case AUTH_NONE:
case AUTH_UNIX:
case AUTH_LOOPBACK:
break;
case AUTH_DES:
{
break;
break;
}
case RPCSEC_GSS:
{
break;
break;
}
}
}
else
}
}