xref: /openbsd/sbin/mount_nfs/mount_nfs.c (revision a6445c1d)
1 /*	$OpenBSD: mount_nfs.c,v 1.51 2014/05/21 06:23:01 guenther Exp $	*/
2 /*	$NetBSD: mount_nfs.c,v 1.12.4.1 1996/05/25 22:48:05 fvdl Exp $	*/
3 
4 /*
5  * Copyright (c) 1992, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Rick Macklem at The University of Guelph.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/param.h>
37 #include <sys/mount.h>
38 #include <sys/socket.h>
39 #include <sys/socketvar.h>
40 #include <sys/stat.h>
41 #include <syslog.h>
42 
43 #include <rpc/rpc.h>
44 #include <rpc/pmap_clnt.h>
45 #include <rpc/pmap_prot.h>
46 
47 #include <nfs/rpcv2.h>
48 #include <nfs/nfsproto.h>
49 #include <nfs/nfs.h>
50 
51 #include <arpa/inet.h>
52 
53 #include <ctype.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <netdb.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include "mntopts.h"
65 
66 #define	ALTF_BG		0x1
67 #define ALTF_NOCONN	0x2
68 #define ALTF_DUMBTIMR	0x4
69 #define ALTF_INTR	0x8
70 #define ALTF_NFSV3	0x20
71 #define ALTF_RDIRPLUS	0x40
72 #define	ALTF_MNTUDP	0x80
73 #define ALTF_RESVPORT	0x100
74 #define ALTF_SEQPACKET	0x200
75 #define ALTF_SOFT	0x800
76 #define ALTF_TCP	0x1000
77 #define ALTF_PORT	0x2000
78 #define ALTF_NFSV2	0x4000
79 #define ALTF_NOAC       0x8000
80 #define ALTF_ACREGMIN	0x10000
81 #define ALTF_ACREGMAX	0x20000
82 #define ALTF_ACDIRMIN	0x40000
83 #define ALTF_ACDIRMAX	0x80000
84 
85 const struct mntopt mopts[] = {
86 	MOPT_STDOPTS,
87 	MOPT_FORCE,
88 	MOPT_UPDATE,
89 	MOPT_SYNC,
90 	{ "bg", ALTF_BG, 0 },
91 	{ "conn", ALTF_NOCONN, MFLAG_INVERSE },
92 	{ "dumbtimer", ALTF_DUMBTIMR, 0 },
93 	{ "intr", ALTF_INTR, 0 },
94 	{ "nfsv3", ALTF_NFSV3, 0 },
95 	{ "rdirplus", ALTF_RDIRPLUS, 0 },
96 	{ "mntudp", ALTF_MNTUDP, 0 },
97 	{ "resvport", ALTF_RESVPORT, 0 },
98 	{ "soft", ALTF_SOFT, 0 },
99 	{ "tcp", ALTF_TCP, 0 },
100 	{ "port", ALTF_PORT, MFLAG_INTVAL },
101 	{ "nfsv2", ALTF_NFSV2, 0 },
102 	{ "ac", ALTF_NOAC, MFLAG_INVERSE },
103 	{ "acregmin", ALTF_ACREGMIN, MFLAG_INTVAL },
104 	{ "acregmax", ALTF_ACREGMAX, MFLAG_INTVAL },
105 	{ "acdirmin", ALTF_ACDIRMIN, MFLAG_INTVAL },
106 	{ "acdirmax", ALTF_ACDIRMAX, MFLAG_INTVAL },
107 	{ NULL }
108 };
109 
110 struct nfs_args nfsdefargs = {
111 	NFS_ARGSVERSION,
112 	NULL,
113 	sizeof (struct sockaddr_in),
114 	SOCK_DGRAM,
115 	0,
116 	NULL,
117 	0,
118 	NFSMNT_NFSV3,
119 	NFS_WSIZE,
120 	NFS_RSIZE,
121 	NFS_READDIRSIZE,
122 	10,
123 	NFS_RETRANS,
124 	NFS_MAXGRPS,
125 	NFS_DEFRAHEAD,
126 	0,
127 	0,
128 	NULL,
129 	0,
130 	0,
131 	0,
132 	0
133 };
134 
135 struct nfhret {
136 	u_long		stat;
137 	long		vers;
138 	long		auth;
139 	long		fhsize;
140 	u_char		nfh[NFSX_V3FHMAX];
141 };
142 #define	DEF_RETRY	10000
143 #define	BGRND	1
144 #define	ISBGRND	2
145 int retrycnt;
146 int opflags = 0;
147 int nfsproto = IPPROTO_UDP;
148 int mnttcp_ok = 1;
149 u_short port_no = 0;
150 int force2 = 0;
151 int force3 = 0;
152 
153 int	getnfsargs(char *, struct nfs_args *);
154 void	set_rpc_maxgrouplist(int);
155 __dead	void usage(void);
156 int	xdr_dir(XDR *, char *);
157 int	xdr_fh(XDR *, struct nfhret *);
158 
159 int
160 main(int argc, char *argv[])
161 {
162 	int c;
163 	struct nfs_args *nfsargsp;
164 	struct nfs_args nfsargs;
165 	int mntflags, num;
166 	char name[MAXPATHLEN], *options = NULL, *spec;
167 	const char *p;
168 	union mntval value;
169 
170 	retrycnt = DEF_RETRY;
171 
172 	mntflags = 0;
173 	nfsargs = nfsdefargs;
174 	nfsargsp = &nfsargs;
175 	while ((c = getopt(argc, argv,
176 	    "23a:bcdD:g:I:iL:lo:PR:r:sTt:w:x:U")) != -1)
177 		switch (c) {
178 		case '3':
179 			if (force2)
180 				errx(1, "-2 and -3 are mutually exclusive");
181 			force3 = 1;
182 			break;
183 		case '2':
184 			if (force3)
185 				errx(1, "-2 and -3 are mutually exclusive");
186 			force2 = 1;
187 			nfsargsp->flags &= ~NFSMNT_NFSV3;
188 			break;
189 		case 'a':
190 			num = (int) strtonum(optarg, 0, 4, &p);
191 			if (p)
192 				errx(1, "illegal -a value %s: %s", optarg, p);
193 			nfsargsp->readahead = num;
194 			nfsargsp->flags |= NFSMNT_READAHEAD;
195 			break;
196 		case 'b':
197 			opflags |= BGRND;
198 			break;
199 		case 'c':
200 			nfsargsp->flags |= NFSMNT_NOCONN;
201 			break;
202 		case 'D':
203 			/* backward compatibility */
204 			break;
205 		case 'd':
206 			nfsargsp->flags |= NFSMNT_DUMBTIMR;
207 			break;
208 		case 'g':
209 			num = (int) strtonum(optarg, 1, NGROUPS_MAX, &p);
210 			if (p)
211 				errx(1, "illegal -g value %s: %s", optarg, p);
212 			set_rpc_maxgrouplist(num);
213 			nfsargsp->maxgrouplist = num;
214 			nfsargsp->flags |= NFSMNT_MAXGRPS;
215 			break;
216 		case 'I':
217 			num = (int) strtonum(optarg, 1, INT_MAX, &p);
218 			if (p)
219 				errx(1, "illegal -I value %s: %s", optarg, p);
220 			nfsargsp->readdirsize = num;
221 			nfsargsp->flags |= NFSMNT_READDIRSIZE;
222 			break;
223 		case 'i':
224 			nfsargsp->flags |= NFSMNT_INT;
225 			break;
226 		case 'L':
227 			/* backward compatibility */
228 			break;
229 		case 'l':
230 			nfsargsp->flags |= NFSMNT_RDIRPLUS;
231 			break;
232 		case 'o':
233 			options = optarg;
234 			while (options != NULL) {
235 				switch (getmntopt(&options, &value, mopts,
236 				    &mntflags)) {
237 				case ALTF_BG:
238 					opflags |= BGRND;
239 					break;
240 				case ALTF_NOCONN:
241 					nfsargsp->flags |= NFSMNT_NOCONN;
242 					break;
243 				case ALTF_DUMBTIMR:
244 					nfsargsp->flags |= NFSMNT_DUMBTIMR;
245 					break;
246 				case ALTF_INTR:
247 					nfsargsp->flags |= NFSMNT_INT;
248 					break;
249 				case ALTF_NFSV3:
250 					if (force2)
251 						errx(1,
252 						    "conflicting version options");
253 					force3 = 1;
254 					break;
255 				case ALTF_NFSV2:
256 					if (force3)
257 						errx(1,
258 						    "conflicting version options");
259 					force2 = 1;
260 					nfsargsp->flags &= ~NFSMNT_NFSV3;
261 					break;
262 				case ALTF_RDIRPLUS:
263 					nfsargsp->flags |= NFSMNT_RDIRPLUS;
264 					break;
265 				case ALTF_MNTUDP:
266 					mnttcp_ok = 0;
267 					break;
268 				case ALTF_RESVPORT:
269 					nfsargsp->flags |= NFSMNT_RESVPORT;
270 					break;
271 				case ALTF_SOFT:
272 					nfsargsp->flags |= NFSMNT_SOFT;
273 					break;
274 				case ALTF_TCP:
275 					nfsargsp->sotype = SOCK_STREAM;
276 					nfsproto = IPPROTO_TCP;
277 					break;
278 				case ALTF_PORT:
279 					port_no = value.ival;
280 					break;
281 				case ALTF_NOAC:
282 					nfsargsp->flags |= (NFSMNT_ACREGMIN |
283 					    NFSMNT_ACREGMAX | NFSMNT_ACDIRMIN |
284 					    NFSMNT_ACDIRMAX);
285 					nfsargsp->acregmin = 0;
286 					nfsargsp->acregmax = 0;
287 					nfsargsp->acdirmin = 0;
288 					nfsargsp->acdirmax = 0;
289 					break;
290 				case ALTF_ACREGMIN:
291 					nfsargsp->flags |= NFSMNT_ACREGMIN;
292 					nfsargsp->acregmin = value.ival;
293 					break;
294 				case ALTF_ACREGMAX:
295 					nfsargsp->flags |= NFSMNT_ACREGMAX;
296 					nfsargsp->acregmax = value.ival;
297 					break;
298 				case ALTF_ACDIRMIN:
299 					nfsargsp->flags |= NFSMNT_ACDIRMIN;
300 					nfsargsp->acdirmin = value.ival;
301 					break;
302 				case ALTF_ACDIRMAX:
303 					nfsargsp->flags |= NFSMNT_ACDIRMAX;
304 					nfsargsp->acdirmax = value.ival;
305 					break;
306 				}
307 			}
308 			break;
309 		case 'P':
310 			/* backward compatibility */
311 			break;
312 		case 'R':
313 			num = (int) strtonum(optarg, 1, INT_MAX, &p);
314 			if (p)
315 				errx(1, "illegal -R value %s: %s", optarg, p);
316 			retrycnt = num;
317 			break;
318 		case 'r':
319 			num = (int) strtonum(optarg, 1, INT_MAX, &p);
320 			if (p)
321 				errx(1, "illegal -r value %s: %s", optarg, p);
322 			nfsargsp->rsize = num;
323 			nfsargsp->flags |= NFSMNT_RSIZE;
324 			break;
325 		case 's':
326 			nfsargsp->flags |= NFSMNT_SOFT;
327 			break;
328 		case 'T':
329 			nfsargsp->sotype = SOCK_STREAM;
330 			nfsproto = IPPROTO_TCP;
331 			break;
332 		case 't':
333 			num = (int) strtonum(optarg, 1, INT_MAX, &p);
334 			if (p)
335 				errx(1, "illegal -t value %s: %s", optarg, p);
336 			nfsargsp->timeo = num;
337 			nfsargsp->flags |= NFSMNT_TIMEO;
338 			break;
339 		case 'w':
340 			num = (int) strtonum(optarg, 1, INT_MAX, &p);
341 			if (p)
342 				errx(1, "illegal -w value %s: %s", optarg, p);
343 			nfsargsp->wsize = num;
344 			nfsargsp->flags |= NFSMNT_WSIZE;
345 			break;
346 		case 'x':
347 			num = (int) strtonum(optarg, 1, INT_MAX, &p);
348 			if (p)
349 				errx(1, "illegal -x value %s: %s", optarg, p);
350 			nfsargsp->retrans = num;
351 			nfsargsp->flags |= NFSMNT_RETRANS;
352 			break;
353 		case 'U':
354 			mnttcp_ok = 0;
355 			break;
356 		default:
357 			usage();
358 			/* NOTREACHED */
359 		}
360 	argc -= optind;
361 	argv += optind;
362 
363 	if (argc != 2)
364 		usage();
365 
366 	spec = *argv++;
367 	if (realpath(*argv, name) == NULL)
368 		err(1, "realpath %s", *argv);
369 
370 	if (!getnfsargs(spec, nfsargsp))
371 		exit(1);
372 	if (mount(MOUNT_NFS, name, mntflags, nfsargsp)) {
373 		if (errno == EOPNOTSUPP)
374 			errx(1, "%s: Filesystem not supported by kernel", name);
375 		else
376 			err(1, "%s", name);
377 	}
378 	exit(0);
379 }
380 
381 int
382 getnfsargs(char *spec, struct nfs_args *nfsargsp)
383 {
384 	CLIENT *clp;
385 	struct hostent *hp;
386 	static struct sockaddr_in saddr;
387 	struct timeval pertry, try;
388 	enum clnt_stat clnt_stat;
389 	int so = RPC_ANYSOCK, i, nfsvers, mntvers, orgcnt;
390 	char *hostp, *delimp;
391 	u_short tport;
392 	static struct nfhret nfhret;
393 	static char nam[MNAMELEN + 1];
394 
395 	if (strlcpy(nam, spec, sizeof(nam)) >= sizeof(nam)) {
396 		errx(1, "hostname too long");
397 	}
398 
399 	if ((delimp = strchr(spec, '@')) != NULL) {
400 		hostp = delimp + 1;
401 	} else if ((delimp = strchr(spec, ':')) != NULL) {
402 		hostp = spec;
403 		spec = delimp + 1;
404 	} else {
405 		warnx("no <host>:<dirpath> or <dirpath>@<host> spec");
406 		return (0);
407 	}
408 	*delimp = '\0';
409 
410 	/*
411 	 * Handle an internet host address
412 	 */
413 	if (inet_aton(hostp, &saddr.sin_addr) == 0) {
414 		hp = gethostbyname(hostp);
415 		if (hp == NULL) {
416 			warnx("can't resolve address for host %s", hostp);
417 			return (0);
418 		}
419 		memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
420 	}
421 
422 	if (force2) {
423 		nfsvers = NFS_VER2;
424 		mntvers = RPCMNT_VER1;
425 	} else {
426 		nfsvers = NFS_VER3;
427 		mntvers = RPCMNT_VER3;
428 	}
429 	orgcnt = retrycnt;
430 tryagain:
431 	nfhret.stat = EACCES;	/* Mark not yet successful */
432 	while (retrycnt > 0) {
433 		saddr.sin_family = AF_INET;
434 		saddr.sin_port = htons(PMAPPORT);
435 		if ((tport = port_no ? port_no : pmap_getport(&saddr,
436 		    RPCPROG_NFS, nfsvers, nfsargsp->sotype == SOCK_STREAM ?
437 		    IPPROTO_TCP : IPPROTO_UDP)) == 0) {
438 			if ((opflags & ISBGRND) == 0)
439 				clnt_pcreateerror("NFS Portmap");
440 		} else {
441 			saddr.sin_port = 0;
442 			pertry.tv_sec = 10;
443 			pertry.tv_usec = 0;
444 			if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM)
445 			    clp = clnttcp_create(&saddr, RPCPROG_MNT, mntvers,
446 				&so, 0, 0);
447 			else
448 			    clp = clntudp_create(&saddr, RPCPROG_MNT, mntvers,
449 				pertry, &so);
450 			if (clp == NULL) {
451 				if ((opflags & ISBGRND) == 0)
452 					clnt_pcreateerror("Cannot MNT RPC");
453 			} else {
454 				clp->cl_auth = authunix_create_default();
455 				try.tv_sec = 10;
456 				try.tv_usec = 0;
457 				nfhret.auth = RPCAUTH_UNIX;
458 				nfhret.vers = mntvers;
459 				clnt_stat = clnt_call(clp, RPCMNT_MOUNT,
460 				    xdr_dir, spec, xdr_fh, &nfhret, try);
461 				if (clnt_stat != RPC_SUCCESS) {
462 					if (clnt_stat == RPC_PROGVERSMISMATCH) {
463 						if (nfsvers == NFS_VER3 &&
464 						    !force3) {
465 							retrycnt = orgcnt;
466 							nfsvers = NFS_VER2;
467 							mntvers = RPCMNT_VER1;
468 							nfsargsp->flags &=
469 							    ~NFSMNT_NFSV3;
470 							goto tryagain;
471 						} else {
472 							warnx("%s",
473 							    clnt_sperror(clp,
474 								"MNT RPC"));
475 						}
476 					}
477 					if ((opflags & ISBGRND) == 0)
478 						warnx("%s", clnt_sperror(clp,
479 						    "bad MNT RPC"));
480 				} else {
481 					auth_destroy(clp->cl_auth);
482 					clnt_destroy(clp);
483 					retrycnt = 0;
484 				}
485 			}
486 		}
487 		if (--retrycnt > 0) {
488 			if (opflags & BGRND) {
489 				opflags &= ~BGRND;
490 				if ((i = fork())) {
491 					if (i == -1)
492 						err(1, "fork");
493 					exit(0);
494 				}
495 				(void) setsid();
496 				(void) close(STDIN_FILENO);
497 				(void) close(STDOUT_FILENO);
498 				(void) close(STDERR_FILENO);
499 				(void) chdir("/");
500 				opflags |= ISBGRND;
501 			}
502 			sleep(60);
503 		}
504 	}
505 	if (nfhret.stat) {
506 		if (opflags & ISBGRND)
507 			exit(1);
508 		warnc(nfhret.stat, "can't access %s", spec);
509 		return (0);
510 	}
511 	saddr.sin_port = htons(tport);
512 	nfsargsp->addr = (struct sockaddr *) &saddr;
513 	nfsargsp->addrlen = sizeof (saddr);
514 	nfsargsp->fh = nfhret.nfh;
515 	nfsargsp->fhsize = nfhret.fhsize;
516 	nfsargsp->hostname = nam;
517 	return (1);
518 }
519 
520 /*
521  * xdr routines for mount rpc's
522  */
523 int
524 xdr_dir(XDR *xdrsp, char *dirp)
525 {
526 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
527 }
528 
529 int
530 xdr_fh(XDR *xdrsp, struct nfhret *np)
531 {
532 	int i;
533 	long auth, authcnt, authfnd = 0;
534 
535 	if (!xdr_u_long(xdrsp, &np->stat))
536 		return (0);
537 	if (np->stat)
538 		return (1);
539 	switch (np->vers) {
540 	case 1:
541 		np->fhsize = NFSX_V2FH;
542 		return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
543 	case 3:
544 		if (!xdr_long(xdrsp, &np->fhsize))
545 			return (0);
546 		if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
547 			return (0);
548 		if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
549 			return (0);
550 		if (!xdr_long(xdrsp, &authcnt))
551 			return (0);
552 		for (i = 0; i < authcnt; i++) {
553 			if (!xdr_long(xdrsp, &auth))
554 				return (0);
555 			if (auth == np->auth)
556 				authfnd++;
557 		}
558 		/*
559 		 * Some servers, such as DEC's OSF/1 return a nil authenticator
560 		 * list to indicate RPCAUTH_UNIX.
561 		 */
562 		if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
563 			np->stat = EAUTH;
564 		return (1);
565 	};
566 	return (0);
567 }
568 
569 __dead void
570 usage(void)
571 {
572 	extern char *__progname;
573 
574 	(void)fprintf(stderr,
575 	    "usage: %s [-23bcdilsTU] [-a maxreadahead] [-g maxgroups]\n"
576 	    "\t[-I readdirsize] [-o options] [-R retrycnt] [-r readsize]\n"
577 	    "\t[-t timeout] [-w writesize] [-x retrans] rhost:path node\n",
578 	    __progname);
579 	exit(1);
580 }
581