xref: /netbsd-src/sbin/mount_nfs/getnfsargs.c (revision ec5c127714047ff7dfedf70650f88fd94a45bb29)
1*ec5c1277Schristos /*	$NetBSD: getnfsargs.c,v 1.18 2017/02/05 00:24:24 christos Exp $	*/
29abd2f56Sdsl 
39abd2f56Sdsl /*
49abd2f56Sdsl  * Copyright (c) 1992, 1993, 1994
59abd2f56Sdsl  *	The Regents of the University of California.  All rights reserved.
69abd2f56Sdsl  *
79abd2f56Sdsl  * This code is derived from software contributed to Berkeley by
89abd2f56Sdsl  * Rick Macklem at The University of Guelph.
99abd2f56Sdsl  *
109abd2f56Sdsl  * Redistribution and use in source and binary forms, with or without
119abd2f56Sdsl  * modification, are permitted provided that the following conditions
129abd2f56Sdsl  * are met:
139abd2f56Sdsl  * 1. Redistributions of source code must retain the above copyright
149abd2f56Sdsl  *    notice, this list of conditions and the following disclaimer.
159abd2f56Sdsl  * 2. Redistributions in binary form must reproduce the above copyright
169abd2f56Sdsl  *    notice, this list of conditions and the following disclaimer in the
179abd2f56Sdsl  *    documentation and/or other materials provided with the distribution.
189abd2f56Sdsl  * 3. Neither the name of the University nor the names of its contributors
199abd2f56Sdsl  *    may be used to endorse or promote products derived from this software
209abd2f56Sdsl  *    without specific prior written permission.
219abd2f56Sdsl  *
229abd2f56Sdsl  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
239abd2f56Sdsl  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249abd2f56Sdsl  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259abd2f56Sdsl  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
269abd2f56Sdsl  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279abd2f56Sdsl  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289abd2f56Sdsl  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299abd2f56Sdsl  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309abd2f56Sdsl  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319abd2f56Sdsl  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329abd2f56Sdsl  * SUCH DAMAGE.
339abd2f56Sdsl  */
349abd2f56Sdsl 
359abd2f56Sdsl #include <sys/cdefs.h>
369abd2f56Sdsl #ifndef lint
376543a91fSlukem __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
386543a91fSlukem  The Regents of the University of California.  All rights reserved.");
399abd2f56Sdsl #endif /* not lint */
409abd2f56Sdsl 
419abd2f56Sdsl #ifndef lint
429abd2f56Sdsl #if 0
439abd2f56Sdsl static char sccsid[] = "@(#)mount_nfs.c	8.11 (Berkeley) 5/4/95";
449abd2f56Sdsl #else
45*ec5c1277Schristos __RCSID("$NetBSD: getnfsargs.c,v 1.18 2017/02/05 00:24:24 christos Exp $");
469abd2f56Sdsl #endif
479abd2f56Sdsl #endif /* not lint */
489abd2f56Sdsl 
499abd2f56Sdsl #include <sys/param.h>
509abd2f56Sdsl #include <sys/mount.h>
519abd2f56Sdsl #include <sys/socket.h>
529abd2f56Sdsl #include <sys/stat.h>
539abd2f56Sdsl #include <syslog.h>
549abd2f56Sdsl 
559abd2f56Sdsl #include <rpc/rpc.h>
569abd2f56Sdsl #include <rpc/pmap_clnt.h>
579abd2f56Sdsl #include <rpc/pmap_prot.h>
589abd2f56Sdsl 
599abd2f56Sdsl #include <nfs/rpcv2.h>
609abd2f56Sdsl #include <nfs/nfsproto.h>
619abd2f56Sdsl #include <nfs/nfs.h>
629abd2f56Sdsl #include <nfs/nfsmount.h>
639abd2f56Sdsl 
649abd2f56Sdsl #include <arpa/inet.h>
659abd2f56Sdsl 
669abd2f56Sdsl #include <err.h>
679abd2f56Sdsl #include <errno.h>
689abd2f56Sdsl #include <fcntl.h>
699abd2f56Sdsl #include <netdb.h>
709abd2f56Sdsl #include <signal.h>
719abd2f56Sdsl #include <stdio.h>
729abd2f56Sdsl #include <stdlib.h>
739abd2f56Sdsl #include <string.h>
749abd2f56Sdsl #include <unistd.h>
759abd2f56Sdsl #include <util.h>
769abd2f56Sdsl 
779abd2f56Sdsl #include "mount_nfs.h"
789abd2f56Sdsl 
7918c826e1Schristos int retrycnt = DEF_RETRY;
8018c826e1Schristos int opflags = 0;
8118c826e1Schristos int force2 = 0;
8218c826e1Schristos int force3 = 0;
8318c826e1Schristos int mnttcp_ok = 1;
8418c826e1Schristos int port = 0;
8518c826e1Schristos 
869abd2f56Sdsl struct nfhret {
879abd2f56Sdsl 	u_long		stat;
889abd2f56Sdsl 	long		vers;
899abd2f56Sdsl 	long		auth;
909abd2f56Sdsl 	long		fhsize;
919abd2f56Sdsl 	u_char		nfh[NFSX_V3FHMAX];
929abd2f56Sdsl };
939abd2f56Sdsl 
949abd2f56Sdsl static int	xdr_dir(XDR *, char *);
959abd2f56Sdsl static int	xdr_fh(XDR *, struct nfhret *);
969abd2f56Sdsl 
9783c92bb4Spooka #ifndef MOUNTNFS_RETRYRPC
9883c92bb4Spooka #define MOUNTNFS_RETRYRPC 60
9983c92bb4Spooka #endif
10083c92bb4Spooka 
1019abd2f56Sdsl int
getnfsargs(char * spec,struct nfs_args * nfsargsp)1029abd2f56Sdsl getnfsargs(char *spec, struct nfs_args *nfsargsp)
1039abd2f56Sdsl {
1049abd2f56Sdsl 	CLIENT *clp;
1059abd2f56Sdsl 	struct addrinfo hints, *ai_nfs, *ai;
1069abd2f56Sdsl 	int ecode;
1079abd2f56Sdsl 	static struct netbuf nfs_nb;
1089abd2f56Sdsl 	static struct sockaddr_storage nfs_ss;
1099abd2f56Sdsl 	struct netconfig *nconf;
1109abd2f56Sdsl 	const char *netid;
1119abd2f56Sdsl 	struct timeval pertry, try;
1129abd2f56Sdsl 	enum clnt_stat clnt_stat;
113ebeaf83fSyamt 	int i, nfsvers, mntvers;
114ebeaf83fSyamt 	int retryleft;
1159abd2f56Sdsl 	char *hostp, *delimp;
1169abd2f56Sdsl 	static struct nfhret nfhret;
1179abd2f56Sdsl 	static char nam[MNAMELEN + 1];
1189abd2f56Sdsl 
11933bed52cShubertf 	strlcpy(nam, spec, sizeof(nam));
1209abd2f56Sdsl 	if ((delimp = strchr(spec, '@')) != NULL) {
1219abd2f56Sdsl 		hostp = delimp + 1;
1229abd2f56Sdsl 	} else if ((delimp = strrchr(spec, ':')) != NULL) {
1239abd2f56Sdsl 		hostp = spec;
1249abd2f56Sdsl 		spec = delimp + 1;
1259abd2f56Sdsl 	} else {
1269abd2f56Sdsl 		warnx("no <host>:<dirpath> or <dirpath>@<host> spec");
1279abd2f56Sdsl 		return (0);
1289abd2f56Sdsl 	}
1299abd2f56Sdsl 	*delimp = '\0';
1309abd2f56Sdsl 
1319abd2f56Sdsl 	/*
132dae26da4Syamt 	 * Handle an internet host address.
1339abd2f56Sdsl 	 */
1349abd2f56Sdsl 	memset(&hints, 0, sizeof hints);
1359abd2f56Sdsl 	hints.ai_flags = AI_NUMERICHOST;
1369abd2f56Sdsl 	hints.ai_socktype = nfsargsp->sotype;
137dae26da4Syamt 	if (getaddrinfo(hostp, "nfs", &hints, &ai_nfs) != 0) {
1389abd2f56Sdsl 		hints.ai_flags = 0;
1399abd2f56Sdsl 		if ((ecode = getaddrinfo(hostp, "nfs", &hints, &ai_nfs)) != 0) {
1409abd2f56Sdsl 			warnx("can't get net id for host \"%s\": %s", hostp,
1419abd2f56Sdsl 			    gai_strerror(ecode));
1429abd2f56Sdsl 			return (0);
1439abd2f56Sdsl 		}
1449abd2f56Sdsl 	}
1459abd2f56Sdsl 
146ebeaf83fSyamt 	if ((nfsargsp->flags & NFSMNT_NFSV3) != 0) {
1479abd2f56Sdsl 		nfsvers = NFS_VER3;
1489abd2f56Sdsl 		mntvers = RPCMNT_VER3;
149ebeaf83fSyamt 	} else {
150ebeaf83fSyamt 		nfsvers = NFS_VER2;
151ebeaf83fSyamt 		mntvers = RPCMNT_VER1;
1529abd2f56Sdsl 	}
1539abd2f56Sdsl 	nfhret.stat = EACCES;	/* Mark not yet successful */
1549abd2f56Sdsl 
1559abd2f56Sdsl     for (ai = ai_nfs; ai; ai = ai->ai_next) {
1569abd2f56Sdsl 	/*
157d8a28639Shubertf 	 * XXX. Need a generic (family, type, proto) -> nconf interface.
1589abd2f56Sdsl 	 * __rpc_*2nconf exist, maybe they should be exported.
1599abd2f56Sdsl 	 */
1609abd2f56Sdsl 	if (nfsargsp->sotype == SOCK_STREAM) {
1619abd2f56Sdsl 		if (ai->ai_family == AF_INET6)
1629abd2f56Sdsl 			netid = "tcp6";
1639abd2f56Sdsl 		else
1649abd2f56Sdsl 			netid = "tcp";
1659abd2f56Sdsl 	} else {
1669abd2f56Sdsl 		if (ai->ai_family == AF_INET6)
1679abd2f56Sdsl 			netid = "udp6";
1689abd2f56Sdsl 		else
1699abd2f56Sdsl 			netid = "udp";
1709abd2f56Sdsl 	}
1719abd2f56Sdsl 
1729abd2f56Sdsl 	nconf = getnetconfigent(netid);
1739abd2f56Sdsl 
1749abd2f56Sdsl tryagain:
175ebeaf83fSyamt 	retryleft = retrycnt;
1769abd2f56Sdsl 
177ebeaf83fSyamt 	while (retryleft > 0) {
1789abd2f56Sdsl 		nfs_nb.buf = &nfs_ss;
1799abd2f56Sdsl 		nfs_nb.maxlen = sizeof nfs_ss;
1809abd2f56Sdsl 		if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, hostp)){
1819abd2f56Sdsl 			if (rpc_createerr.cf_stat == RPC_SYSTEMERROR) {
1829abd2f56Sdsl 				nfhret.stat = rpc_createerr.cf_error.re_errno;
1839abd2f56Sdsl 				break;
1849abd2f56Sdsl 			}
1859abd2f56Sdsl 			if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO) {
1869abd2f56Sdsl 				nfhret.stat = EPROTONOSUPPORT;
1879abd2f56Sdsl 				break;
1889abd2f56Sdsl 			}
18956a6edc1Spooka 			if ((opflags & ISBGRND) == 0) {
19056a6edc1Spooka 				char buf[64];
19156a6edc1Spooka 
19256a6edc1Spooka 				snprintf(buf, sizeof(buf),
19356a6edc1Spooka 				    "%s: rpcbind to nfs on server",
19456a6edc1Spooka 				    getprogname());
19556a6edc1Spooka 				clnt_pcreateerror(buf);
19656a6edc1Spooka 			}
1979abd2f56Sdsl 		} else {
1983c955ef2Syamt 			pertry.tv_sec = 30;
1999abd2f56Sdsl 			pertry.tv_usec = 0;
2009abd2f56Sdsl 			/*
2019abd2f56Sdsl 			 * XXX relies on clnt_tcp_create to bind to a reserved
2029abd2f56Sdsl 			 * socket.
2039abd2f56Sdsl 			 */
2049abd2f56Sdsl 			clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers,
205e18b8724Schristos 			     mnttcp_ok ? nconf : getnetconfigent(netid));
2069abd2f56Sdsl 			if (clp == NULL) {
2079abd2f56Sdsl 				if ((opflags & ISBGRND) == 0) {
2089abd2f56Sdsl 					clnt_pcreateerror(
2099abd2f56Sdsl 					    "Cannot MNT RPC (mountd)");
2109abd2f56Sdsl 				}
2119abd2f56Sdsl 			} else {
2129abd2f56Sdsl 				CLNT_CONTROL(clp, CLSET_RETRY_TIMEOUT,
2139abd2f56Sdsl 				    (char *)&pertry);
2149abd2f56Sdsl 				clp->cl_auth = authsys_create_default();
2153c955ef2Syamt 				try.tv_sec = 30;
2169abd2f56Sdsl 				try.tv_usec = 0;
2179abd2f56Sdsl 				nfhret.auth = RPCAUTH_UNIX;
2189abd2f56Sdsl 				nfhret.vers = mntvers;
2199abd2f56Sdsl 				clnt_stat = clnt_call(clp, RPCMNT_MOUNT,
2209abd2f56Sdsl 				    xdr_dir, spec, xdr_fh, &nfhret, try);
2219abd2f56Sdsl 				switch (clnt_stat) {
2229abd2f56Sdsl 				case RPC_PROGVERSMISMATCH:
2239abd2f56Sdsl 					if (nfsvers == NFS_VER3 && !force3) {
2249abd2f56Sdsl 						nfsvers = NFS_VER2;
2259abd2f56Sdsl 						mntvers = RPCMNT_VER1;
2269abd2f56Sdsl 						nfsargsp->flags &=
2279abd2f56Sdsl 							~NFSMNT_NFSV3;
2289abd2f56Sdsl 						goto tryagain;
2299abd2f56Sdsl 					} else {
2309abd2f56Sdsl 						errx(1, "%s", clnt_sperror(clp,
2319abd2f56Sdsl 							"MNT RPC"));
2329abd2f56Sdsl 					}
2339abd2f56Sdsl 				case RPC_SUCCESS:
2349abd2f56Sdsl 					auth_destroy(clp->cl_auth);
2359abd2f56Sdsl 					clnt_destroy(clp);
236ebeaf83fSyamt 					retryleft = 0;
2379abd2f56Sdsl 					break;
2389abd2f56Sdsl 				default:
2399abd2f56Sdsl 					/* XXX should give up on some errors */
2409abd2f56Sdsl 					if ((opflags & ISBGRND) == 0)
2419abd2f56Sdsl 						warnx("%s", clnt_sperror(clp,
2429abd2f56Sdsl 						    "bad MNT RPC"));
2439abd2f56Sdsl 					break;
2449abd2f56Sdsl 				}
2459abd2f56Sdsl 			}
2469abd2f56Sdsl 		}
247ebeaf83fSyamt 		if (--retryleft > 0) {
2489abd2f56Sdsl 			if (opflags & BGRND) {
2499abd2f56Sdsl 				opflags &= ~BGRND;
2509abd2f56Sdsl 				if ((i = fork()) != 0) {
2519abd2f56Sdsl 					if (i == -1)
252084e92b6Spooka 						err(1, "fork");
2539abd2f56Sdsl 					exit(0);
2549abd2f56Sdsl 				}
2559abd2f56Sdsl 				(void) setsid();
2569abd2f56Sdsl 				(void) close(STDIN_FILENO);
2579abd2f56Sdsl 				(void) close(STDOUT_FILENO);
2589abd2f56Sdsl 				(void) close(STDERR_FILENO);
2599abd2f56Sdsl 				(void) chdir("/");
2609abd2f56Sdsl 				opflags |= ISBGRND;
2619abd2f56Sdsl 			}
26283c92bb4Spooka 			sleep(MOUNTNFS_RETRYRPC);
2639abd2f56Sdsl 		}
2649abd2f56Sdsl 	}
2659abd2f56Sdsl 	if (nfhret.stat == 0)
2669abd2f56Sdsl 		break;
2679abd2f56Sdsl     }
2689abd2f56Sdsl 	freeaddrinfo(ai_nfs);
2699abd2f56Sdsl 	if (nfhret.stat) {
2709abd2f56Sdsl 		if (opflags & ISBGRND)
2719abd2f56Sdsl 			exit(1);
2729abd2f56Sdsl 		errno = nfhret.stat;
2739abd2f56Sdsl 		warnx("can't access %s: %s", spec, strerror(nfhret.stat));
2749abd2f56Sdsl 		return (0);
275e240adbdSjoerg 	} else {
2769abd2f56Sdsl 		nfsargsp->addr = (struct sockaddr *) nfs_nb.buf;
2779abd2f56Sdsl 		nfsargsp->addrlen = nfs_nb.len;
2789abd2f56Sdsl 		if (port != 0) {
2799abd2f56Sdsl 			struct sockaddr *sa = nfsargsp->addr;
2809abd2f56Sdsl 			switch (sa->sa_family) {
2819abd2f56Sdsl 			case AF_INET:
2829abd2f56Sdsl 				((struct sockaddr_in *)sa)->sin_port = port;
283c23511b3Sdsl 				break;
2849abd2f56Sdsl #ifdef INET6
2859abd2f56Sdsl 			case AF_INET6:
2869abd2f56Sdsl 				((struct sockaddr_in6 *)sa)->sin6_port = port;
2879abd2f56Sdsl 				break;
2889abd2f56Sdsl #endif
2899abd2f56Sdsl 			default:
2909abd2f56Sdsl 				errx(1, "Unsupported socket family %d",
2919abd2f56Sdsl 				    sa->sa_family);
2929abd2f56Sdsl 			}
2939abd2f56Sdsl 		}
2949abd2f56Sdsl 	}
2959abd2f56Sdsl 	nfsargsp->fh = nfhret.nfh;
2969abd2f56Sdsl 	nfsargsp->fhsize = nfhret.fhsize;
2979abd2f56Sdsl 	nfsargsp->hostname = nam;
2989abd2f56Sdsl 	return (1);
2999abd2f56Sdsl }
3009abd2f56Sdsl 
3019abd2f56Sdsl /*
3029abd2f56Sdsl  * xdr routines for mount rpc's
3039abd2f56Sdsl  */
3049abd2f56Sdsl static int
xdr_dir(XDR * xdrsp,char * dirp)3059abd2f56Sdsl xdr_dir(XDR *xdrsp, char *dirp)
3069abd2f56Sdsl {
3079abd2f56Sdsl 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
3089abd2f56Sdsl }
3099abd2f56Sdsl 
3109abd2f56Sdsl static int
xdr_fh(XDR * xdrsp,struct nfhret * np)3119abd2f56Sdsl xdr_fh(XDR *xdrsp, struct nfhret *np)
3129abd2f56Sdsl {
3139abd2f56Sdsl 	int i;
3149abd2f56Sdsl 	long auth, authcnt, authfnd = 0;
3159abd2f56Sdsl 
3169abd2f56Sdsl 	if (!xdr_u_long(xdrsp, &np->stat))
3179abd2f56Sdsl 		return (0);
3189abd2f56Sdsl 	if (np->stat)
3199abd2f56Sdsl 		return (1);
3209abd2f56Sdsl 	switch (np->vers) {
3219abd2f56Sdsl 	case 1:
3229abd2f56Sdsl 		np->fhsize = NFSX_V2FH;
3239abd2f56Sdsl 		return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
3249abd2f56Sdsl 	case 3:
3259abd2f56Sdsl 		if (!xdr_long(xdrsp, &np->fhsize))
3269abd2f56Sdsl 			return (0);
3279abd2f56Sdsl 		if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
3289abd2f56Sdsl 			return (0);
3299abd2f56Sdsl 		if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
3309abd2f56Sdsl 			return (0);
3319abd2f56Sdsl 		if (!xdr_long(xdrsp, &authcnt))
3329abd2f56Sdsl 			return (0);
3339abd2f56Sdsl 		for (i = 0; i < authcnt; i++) {
3349abd2f56Sdsl 			if (!xdr_long(xdrsp, &auth))
3359abd2f56Sdsl 				return (0);
3369abd2f56Sdsl 			if (auth == np->auth)
3379abd2f56Sdsl 				authfnd++;
3389abd2f56Sdsl 		}
3399abd2f56Sdsl 		/*
3409abd2f56Sdsl 		 * Some servers, such as DEC's OSF/1 return a nil authenticator
3419abd2f56Sdsl 		 * list to indicate RPCAUTH_UNIX.
3429abd2f56Sdsl 		 */
3439abd2f56Sdsl 		if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
3449abd2f56Sdsl 			np->stat = EAUTH;
3459abd2f56Sdsl 		return (1);
3469abd2f56Sdsl 	};
3479abd2f56Sdsl 	return (0);
3489abd2f56Sdsl }
349