]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/mount_nfs/mount_nfs.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[FreeBSD/FreeBSD.git] / sbin / mount_nfs / mount_nfs.c
1 /*
2  * copyright (c) 2003
3  * the regents of the university of michigan
4  * all rights reserved
5  * 
6  * permission is granted to use, copy, create derivative works and redistribute
7  * this software and such derivative works for any purpose, so long as the name
8  * of the university of michigan is not used in any advertising or publicity
9  * pertaining to the use or distribution of this software without specific,
10  * written prior authorization.  if the above copyright notice or any other
11  * identification of the university of michigan is included in any copy of any
12  * portion of this software, then the disclaimer below must also be included.
13  * 
14  * this software is provided as is, without representation from the university
15  * of michigan as to its fitness for any purpose, and without warranty by the
16  * university of michigan of any kind, either express or implied, including
17  * without limitation the implied warranties of merchantability and fitness for
18  * a particular purpose. the regents of the university of michigan shall not be
19  * liable for any damages, including special, indirect, incidental, or
20  * consequential damages, with respect to any claim arising out of or in
21  * connection with the use of the software, even if it has been or is hereafter
22  * advised of the possibility of such damages.
23  */
24
25 /*
26  * Copyright (c) 1992, 1993, 1994
27  *      The Regents of the University of California.  All rights reserved.
28  *
29  * This code is derived from software contributed to Berkeley by
30  * Rick Macklem at The University of Guelph.
31  *
32  * Redistribution and use in source and binary forms, with or without
33  * modification, are permitted provided that the following conditions
34  * are met:
35  * 1. Redistributions of source code must retain the above copyright
36  *    notice, this list of conditions and the following disclaimer.
37  * 2. Redistributions in binary form must reproduce the above copyright
38  *    notice, this list of conditions and the following disclaimer in the
39  *    documentation and/or other materials provided with the distribution.
40  * 4. Neither the name of the University nor the names of its contributors
41  *    may be used to endorse or promote products derived from this software
42  *    without specific prior written permission.
43  *
44  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54  * SUCH DAMAGE.
55  */
56
57 #if 0
58 #ifndef lint
59 static const char copyright[] =
60 "@(#) Copyright (c) 1992, 1993, 1994\n\
61         The Regents of the University of California.  All rights reserved.\n";
62 #endif /* not lint */
63
64 #ifndef lint
65 static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95";
66 #endif /* not lint */
67 #endif
68 #include <sys/cdefs.h>
69 __FBSDID("$FreeBSD$");
70
71 #include <sys/param.h>
72 #include <sys/mount.h>
73 #include <sys/socket.h>
74 #include <sys/stat.h>
75 #include <sys/syslog.h>
76 #include <sys/uio.h>
77
78 #include <rpc/rpc.h>
79 #include <rpc/pmap_clnt.h>
80 #include <rpc/pmap_prot.h>
81
82 #include <nfs/rpcv2.h>
83 #include <nfs/nfsproto.h>
84 #include <nfsclient/nfs.h>
85
86 #include <arpa/inet.h>
87
88 #include <ctype.h>
89 #include <err.h>
90 #include <errno.h>
91 #include <fcntl.h>
92 #include <netdb.h>
93 #include <stdio.h>
94 #include <stdlib.h>
95 #include <string.h>
96 #include <strings.h>
97 #include <sysexits.h>
98 #include <unistd.h>
99
100 #include "mntopts.h"
101 #include "mounttab.h"
102
103 /* Table for af,sotype -> netid conversions. */
104 struct nc_protos {
105         const char *netid;
106         int af;
107         int sotype;
108 } nc_protos[] = {
109         {"udp",         AF_INET,        SOCK_DGRAM},
110         {"tcp",         AF_INET,        SOCK_STREAM},
111         {"udp6",        AF_INET6,       SOCK_DGRAM},
112         {"tcp6",        AF_INET6,       SOCK_STREAM},
113         {NULL,          0,              0}
114 };
115
116 struct nfhret {
117         u_long          stat;
118         long            vers;
119         long            auth;
120         long            fhsize;
121         u_char          nfh[NFSX_V3FHMAX];
122 };
123 #define BGRND   1
124 #define ISBGRND 2
125 #define OF_NOINET4      4
126 #define OF_NOINET6      8
127 int retrycnt = -1;
128 int opflags = 0;
129 int nfsproto = IPPROTO_UDP;
130 int mnttcp_ok = 1;
131 int noconn = 0;
132 char *portspec = NULL;  /* Server nfs port; NULL means look up via rpcbind. */
133 struct sockaddr *addr;
134 int addrlen = 0;
135 u_char *fh = NULL;
136 int fhsize = 0;
137
138 enum mountmode {
139         ANY,
140         V2,
141         V3,
142         V4
143 } mountmode = ANY;
144
145 /* Return codes for nfs_tryproto. */
146 enum tryret {
147         TRYRET_SUCCESS,
148         TRYRET_TIMEOUT,         /* No response received. */
149         TRYRET_REMOTEERR,       /* Error received from remote server. */
150         TRYRET_LOCALERR         /* Local failure. */
151 };
152
153 int     fallback_mount(struct iovec *iov, int iovlen, int mntflags);
154 int     getnfsargs(char *, struct iovec **iov, int *iovlen);
155 int     getnfs4args(char *, struct iovec **iov, int *iovlen);
156 /* void set_rpc_maxgrouplist(int); */
157 struct netconfig *getnetconf_cached(const char *netid);
158 const char      *netidbytype(int af, int sotype);
159 void    usage(void) __dead2;
160 int     xdr_dir(XDR *, char *);
161 int     xdr_fh(XDR *, struct nfhret *);
162 enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec,
163     char **errstr, struct iovec **iov, int *iovlen);
164 enum tryret nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec,
165     char **errstr);
166 enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr);
167 extern int getosreldate(void);
168
169 int
170 main(int argc, char *argv[])
171 {
172         int c;
173         struct iovec *iov;
174         int mntflags, num, iovlen;
175         int osversion;
176         char *name, *p, *spec, *fstype;
177         char mntpath[MAXPATHLEN], errmsg[255];
178
179         mntflags = 0;
180         iov = NULL;
181         iovlen = 0;
182         memset(errmsg, 0, sizeof(errmsg));
183
184         fstype = strrchr(argv[0], '_');
185         if (fstype == NULL)
186                 errx(EX_USAGE, "argv[0] must end in _fstype");
187
188         ++fstype;
189
190         if (strcmp(fstype, "nfs4") == 0) {
191                 nfsproto = IPPROTO_TCP;
192                 portspec = "2049";
193                 build_iovec(&iov, &iovlen, "tcp", NULL, 0);
194                 mountmode = V4;
195         }
196
197         while ((c = getopt(argc, argv,
198             "234a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1)
199                 switch (c) {
200                 case '2':
201                         mountmode = V2;
202                         break;
203                 case '3':
204                         mountmode = V3;
205                         break;
206                 case '4':
207                         mountmode = V4;
208                         fstype = "nfs4";
209                         break;
210                 case 'a':
211                         printf("-a deprecated, use -o readhead=<value>\n");
212                         build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1);
213                         break;
214                 case 'b':
215                         opflags |= BGRND;
216                         break;
217                 case 'c':
218                         printf("-c deprecated, use -o noconn\n");
219                         build_iovec(&iov, &iovlen, "noconn", NULL, 0);
220                         noconn = 1;
221                         break;
222                 case 'D':
223                         printf("-D deprecated, use -o deadthresh=<value>\n");
224                         build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1);
225                         break;
226                 case 'd':
227                         printf("-d deprecated, use -o dumbtimer");
228                         build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0);
229                         break;
230                 case 'g':
231                         printf("-g deprecated, use -o maxgroups");
232                         num = strtol(optarg, &p, 10);
233                         if (*p || num <= 0)
234                                 errx(1, "illegal -g value -- %s", optarg);
235                         //set_rpc_maxgrouplist(num);
236                         build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1);
237                         break;
238                 case 'I':
239                         printf("-I deprecated, use -o readdirsize=<value>\n");
240                         build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1);
241                         break;
242                 case 'i':
243                         printf("-i deprecated, use -o intr\n");
244                         build_iovec(&iov, &iovlen, "intr", NULL, 0);
245                         break;
246                 case 'L':
247                         printf("-i deprecated, use -o nolockd\n");
248                         build_iovec(&iov, &iovlen, "nolockd", NULL, 0);
249                         break;
250                 case 'l':
251                         printf("-l deprecated, -o rdirplus\n");
252                         build_iovec(&iov, &iovlen, "rdirplus", NULL, 0);
253                         break;
254                 case 'N':
255                         printf("-N deprecated, do not specify -o resvport\n");
256                         break;
257                 case 'o': {
258                         int pass_flag_to_nmount;
259                         char *opt = optarg;
260                         while (opt) {
261                                 char *pval = NULL;
262                                 char *pnextopt = NULL;
263                                 char *val = "";
264                                 pass_flag_to_nmount = 1;
265                                 pval = strchr(opt, '=');
266                                 pnextopt = strchr(opt, ',');
267                                 if (pval != NULL) {
268                                         *pval = '\0';
269                                         val = pval + 1;
270                                 }
271                                 if (pnextopt) {
272                                         *pnextopt = '\0';
273                                         pnextopt++;
274                                 }
275                                 if (strcmp(opt, "bg") == 0) {
276                                         opflags |= BGRND;
277                                         pass_flag_to_nmount=0;
278                                 } else if (strcmp(opt, "fg") == 0) {
279                                         /* same as not specifying -o bg */
280                                         pass_flag_to_nmount=0;
281                                 } else if (strcmp(opt, "mntudp") == 0) {
282                                         mnttcp_ok = 0;
283                                         nfsproto = IPPROTO_UDP;
284                                 } else if (strcmp(opt, "udp") == 0) {
285                                         nfsproto = IPPROTO_UDP;
286                                 } else if (strcmp(opt, "tcp") == 0) {
287                                         nfsproto = IPPROTO_TCP;
288                                 } else if (strcmp(opt, "noinet4") == 0) {
289                                         pass_flag_to_nmount=0;
290                                         opflags |= OF_NOINET4;
291                                 } else if (strcmp(opt, "noinet6") == 0) {
292                                         pass_flag_to_nmount=0;
293                                         opflags |= OF_NOINET6;
294                                 } else if (strcmp(opt, "noconn") == 0) {
295                                         noconn = 1; 
296                                 } else if (strcmp(opt, "nfsv2") == 0) {
297                                         pass_flag_to_nmount=0;
298                                         mountmode = V2;
299                                 } else if (strcmp(opt, "nfsv3") == 0) {
300                                         mountmode = V3;
301                                 } else if (strcmp(opt, "nfsv4") == 0) {
302                                         pass_flag_to_nmount=0;
303                                         mountmode = V4;
304                                         fstype = "nfs4";
305                                 } else if (strcmp(opt, "port") == 0) {
306                                         pass_flag_to_nmount=0;
307                                         asprintf(&portspec, "%d",
308                                             atoi(val));
309                                         if (portspec == NULL)
310                                                 err(1, "asprintf");
311                                 } else if (strcmp(opt, "retrycnt") == 0) {
312                                         pass_flag_to_nmount=0;
313                                         num = strtol(val, &p, 10);
314                                         if (*p || num < 0)
315                                                 errx(1, "illegal retrycnt value -- %s", val);
316                                         retrycnt = num;
317                                 } else if (strcmp(opt, "maxgroups") == 0) {
318                                         num = strtol(val, &p, 10);
319                                         if (*p || num <= 0)
320                                                 errx(1, "illegal maxgroups value -- %s", val);
321                                         //set_rpc_maxgrouplist(num);
322                                 }
323                                 if (pass_flag_to_nmount)
324                                         build_iovec(&iov, &iovlen, opt, val,
325                                             strlen(val) + 1);
326                                 opt = pnextopt;
327                         }
328                         }
329                         break;
330                 case 'P':
331                         /* obsolete for -o noresvport now default */
332                         printf("-P deprecated, use -o noresvport\n");
333                         build_iovec(&iov, &iovlen, "noresvport", NULL, 0);
334                         break;
335                 case 'R':
336                         printf("-R deprecated, use -o retrycnt=<retrycnt>\n");
337                         num = strtol(optarg, &p, 10);
338                         if (*p || num < 0)
339                                 errx(1, "illegal -R value -- %s", optarg);
340                         retrycnt = num;
341                         break;
342                 case 'r':
343                         printf("-r deprecated, use -o rsize=<rsize>\n");
344                         build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1);
345                         break;
346                 case 's':
347                         printf("-s deprecated, use -o soft\n");
348                         build_iovec(&iov, &iovlen, "soft", NULL, 0);
349                         break;
350                 case 'T':
351                         nfsproto = IPPROTO_TCP;
352                         printf("-T deprecated, use -o tcp\n");
353                         break;
354                 case 't':
355                         printf("-t deprecated, use -o timeout=<value>\n");
356                         build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1);
357                         break;
358                 case 'w':
359                         printf("-w deprecated, use -o wsize=<value>\n");
360                         build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1);
361                         break;
362                 case 'x':
363                         printf("-x deprecated, use -o retrans=<value>\n");
364                         build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1);
365                         break;
366                 case 'U':
367                         printf("-U deprecated, use -o mntudp\n");
368                         mnttcp_ok = 0;
369                         nfsproto = IPPROTO_UDP;
370                         build_iovec(&iov, &iovlen, "mntudp", NULL, 0);
371                         break;
372                 default:
373                         usage();
374                         break;
375                 }
376         argc -= optind;
377         argv += optind;
378
379         if (argc != 2) {
380                 usage();
381                 /* NOTREACHED */
382         }
383
384         spec = *argv++;
385         name = *argv;
386
387         if (retrycnt == -1)
388                 /* The default is to keep retrying forever. */
389                 retrycnt = 0;
390
391         if (mountmode == V4) {
392                 if (!getnfs4args(spec, &iov, &iovlen))
393                         exit(1);
394         } else { 
395                 if (!getnfsargs(spec, &iov, &iovlen))
396                         exit(1);
397         }
398
399         /* resolve the mountpoint with realpath(3) */
400         (void)checkpath(name, mntpath);
401
402         build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
403         build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
404         build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
405
406         /*
407          * XXX:
408          * Backwards compatibility routines for older kernels.
409          * Remove this and fallback_mount() code when we do not need to support
410          * NFS mounts against older kernels which still need
411          * struct nfs_args to be passed in via nmount().
412          */
413         osversion = getosreldate();
414         if (osversion >= 800048) {
415                 if (nmount(iov, iovlen, mntflags))
416                         err(1, "%s, %s", mntpath, errmsg);
417         } else {
418                 if (fallback_mount(iov, iovlen, mntflags))
419                         err(1, "%s, %s", mntpath, errmsg);
420         }
421
422         exit(0);
423 }
424
425 static int
426 findopt(struct iovec *iov, int iovlen, const char *name,
427     char **valuep, int *lenp)
428 {
429         int i;
430
431         for (i = 0; i < iovlen/2; i++, iov += 2) {
432                 if (strcmp(name, iov[0].iov_base) == 0) {
433                         if (valuep)
434                                 *valuep = iov[1].iov_base;
435                         if (lenp)
436                                 *lenp = iov[1].iov_len;
437                         return (0);
438                 }
439         }
440         return (ENOENT);
441 }
442
443 static void
444 copyopt(struct iovec **newiov, int *newiovlen,
445     struct iovec *iov, int iovlen, const char *name)
446 {
447         char *value;
448         int len;
449
450         if (findopt(iov, iovlen, name, &value, &len) == 0)
451                 build_iovec(newiov, newiovlen, name, value, len);
452 }
453
454 int
455 fallback_mount(struct iovec *iov, int iovlen, int mntflags)
456 {
457         struct nfs_args args = {
458             .version = NFS_ARGSVERSION,
459             .addr = NULL,
460             .addrlen = sizeof (struct sockaddr_in),
461             .sotype = SOCK_STREAM,
462             .proto = 0,
463             .fh = NULL,
464             .fhsize = 0,
465             .flags = NFSMNT_RESVPORT,
466             .wsize = NFS_WSIZE,
467             .rsize = NFS_RSIZE,
468             .readdirsize = NFS_READDIRSIZE,
469             .timeo = 10,
470             .retrans = NFS_RETRANS,
471             .maxgrouplist = NFS_MAXGRPS,
472             .readahead = NFS_DEFRAHEAD,
473             .wcommitsize = 0,                   /* was: NQ_DEFLEASE */
474             .deadthresh = NFS_MAXDEADTHRESH,    /* was: NQ_DEADTHRESH */
475             .hostname = NULL,
476             /* args version 4 */
477             .acregmin = NFS_MINATTRTIMO,
478             .acregmax = NFS_MAXATTRTIMO,
479             .acdirmin = NFS_MINDIRATTRTIMO,
480             .acdirmax = NFS_MAXDIRATTRTIMO,
481         };
482         int ret;
483         char *opt;
484         struct iovec *newiov;
485         int newiovlen;
486
487         if (findopt(iov, iovlen, "dumbtimer", NULL, NULL) == 0)
488                 args.flags |= NFSMNT_DUMBTIMR;
489         if (findopt(iov, iovlen, "noconn", NULL, NULL) == 0)
490                 args.flags |= NFSMNT_NOCONN;
491         if (findopt(iov, iovlen, "conn", NULL, NULL) == 0)
492                 args.flags |= NFSMNT_NOCONN;
493         if (findopt(iov, iovlen, "nolockd", NULL, NULL) == 0)
494                 args.flags |= NFSMNT_NOLOCKD;
495         if (findopt(iov, iovlen, "lockd", NULL, NULL) == 0)
496                 args.flags &= ~NFSMNT_NOLOCKD;
497         if (findopt(iov, iovlen, "intr", NULL, NULL) == 0)
498                 args.flags |= NFSMNT_INT;
499         if (findopt(iov, iovlen, "rdirplus", NULL, NULL) == 0)
500                 args.flags |= NFSMNT_RDIRPLUS;
501         if (findopt(iov, iovlen, "resvport", NULL, NULL) == 0)
502                 args.flags |= NFSMNT_RESVPORT;
503         if (findopt(iov, iovlen, "noresvport", NULL, NULL) == 0)
504                 args.flags &= ~NFSMNT_RESVPORT;
505         if (findopt(iov, iovlen, "soft", NULL, NULL) == 0)
506                 args.flags |= NFSMNT_SOFT;
507         if (findopt(iov, iovlen, "hard", NULL, NULL) == 0)
508                 args.flags &= ~NFSMNT_SOFT;
509         if (findopt(iov, iovlen, "mntudp", NULL, NULL) == 0)
510                 args.sotype = SOCK_DGRAM;
511         if (findopt(iov, iovlen, "udp", NULL, NULL) == 0)
512                 args.sotype = SOCK_DGRAM;
513         if (findopt(iov, iovlen, "tcp", NULL, NULL) == 0)
514                 args.sotype = SOCK_STREAM;
515         if (findopt(iov, iovlen, "nfsv3", NULL, NULL) == 0)
516                 args.flags |= NFSMNT_NFSV3;
517         if (findopt(iov, iovlen, "readdirsize", &opt, NULL) == 0) {
518                 if (opt == NULL) { 
519                         errx(1, "illegal readdirsize");
520                 }
521                 ret = sscanf(opt, "%d", &args.readdirsize);
522                 if (ret != 1 || args.readdirsize <= 0) {
523                         errx(1, "illegal readdirsize: %s", opt);
524                 }
525                 args.flags |= NFSMNT_READDIRSIZE;
526         }
527         if (findopt(iov, iovlen, "readahead", &opt, NULL) == 0) {
528                 if (opt == NULL) { 
529                         errx(1, "illegal readahead");
530                 }
531                 ret = sscanf(opt, "%d", &args.readahead);
532                 if (ret != 1 || args.readahead <= 0) {
533                         errx(1, "illegal readahead: %s", opt);
534                 }
535                 args.flags |= NFSMNT_READAHEAD;
536         }
537         if (findopt(iov, iovlen, "wsize", &opt, NULL) == 0) {
538                 if (opt == NULL) { 
539                         errx(1, "illegal wsize");
540                 }
541                 ret = sscanf(opt, "%d", &args.wsize);
542                 if (ret != 1 || args.wsize <= 0) {
543                         errx(1, "illegal wsize: %s", opt);
544                 }
545                 args.flags |= NFSMNT_WSIZE;
546         }
547         if (findopt(iov, iovlen, "rsize", &opt, NULL) == 0) {
548                 if (opt == NULL) { 
549                         errx(1, "illegal rsize");
550                 }
551                 ret = sscanf(opt, "%d", &args.rsize);
552                 if (ret != 1 || args.rsize <= 0) {
553                         errx(1, "illegal wsize: %s", opt);
554                 }
555                 args.flags |= NFSMNT_RSIZE;
556         }
557         if (findopt(iov, iovlen, "retrans", &opt, NULL) == 0) {
558                 if (opt == NULL) { 
559                         errx(1, "illegal retrans");
560                 }
561                 ret = sscanf(opt, "%d", &args.retrans);
562                 if (ret != 1 || args.retrans <= 0) {
563                         errx(1, "illegal retrans: %s", opt);
564                 }
565                 args.flags |= NFSMNT_RETRANS;
566         }
567         if (findopt(iov, iovlen, "acregmin", &opt, NULL) == 0) {
568                 ret = sscanf(opt, "%d", &args.acregmin);
569                 if (ret != 1 || args.acregmin <= 0) {
570                         errx(1, "illegal acregmin: %s", opt);
571                 }
572         }
573         if (findopt(iov, iovlen, "acregmax", &opt, NULL) == 0) {
574                 ret = sscanf(opt, "%d", &args.acregmax);
575                 if (ret != 1 || args.acregmax <= 0) {
576                         errx(1, "illegal acregmax: %s", opt);
577                 }
578         }
579         if (findopt(iov, iovlen, "acdirmin", &opt, NULL) == 0) {
580                 ret = sscanf(opt, "%d", &args.acdirmin);
581                 if (ret != 1 || args.acdirmin <= 0) {
582                         errx(1, "illegal acdirmin: %s", opt);
583                 }
584         }
585         if (findopt(iov, iovlen, "acdirmax", &opt, NULL) == 0) {
586                 ret = sscanf(opt, "%d", &args.acdirmax);
587                 if (ret != 1 || args.acdirmax <= 0) {
588                         errx(1, "illegal acdirmax: %s", opt);
589                 }
590         }
591         if (findopt(iov, iovlen, "deadthresh", &opt, NULL) == 0) {
592                 ret = sscanf(opt, "%d", &args.deadthresh);
593                 if (ret != 1 || args.deadthresh <= 0) {
594                         errx(1, "illegal deadthresh: %s", opt);
595                 }
596                 args.flags |= NFSMNT_DEADTHRESH;
597         }
598         if (findopt(iov, iovlen, "timeout", &opt, NULL) == 0) {
599                 ret = sscanf(opt, "%d", &args.timeo);
600                 if (ret != 1 || args.timeo <= 0) {
601                         errx(1, "illegal timeout: %s", opt);
602                 }
603                 args.flags |= NFSMNT_TIMEO;
604         }
605         if (findopt(iov, iovlen, "maxgroups", &opt, NULL) == 0) {
606                 ret = sscanf(opt, "%d", &args.maxgrouplist);
607                 if (ret != 1 || args.timeo <= 0) {
608                         errx(1, "illegal maxgroups: %s", opt);
609                 }
610                 args.flags |= NFSMNT_MAXGRPS;
611         }
612         if (findopt(iov, iovlen, "addr", &opt,
613                 &args.addrlen) == 0) {
614                 args.addr = (struct sockaddr *) opt;
615         }
616         if (findopt(iov, iovlen, "fh", &opt, &args.fhsize) == 0) {
617                 args.fh = opt;
618         }
619         if (findopt(iov, iovlen, "hostname", &args.hostname,
620                 NULL) == 0) {
621         }
622         if (args.hostname == NULL) {
623                 errx(1, "Invalid hostname");
624         }
625
626         newiov = NULL;
627         newiovlen = 0;
628
629         build_iovec(&newiov, &newiovlen, "nfs_args", &args, sizeof(args));
630         copyopt(&newiov, &newiovlen, iov, iovlen, "fstype");
631         copyopt(&newiov, &newiovlen, iov, iovlen, "fspath");
632         copyopt(&newiov, &newiovlen, iov, iovlen, "errmsg");
633
634         return nmount(newiov, newiovlen, mntflags);
635 }
636
637 int
638 getnfsargs(char *spec, struct iovec **iov, int *iovlen)
639 {
640         struct addrinfo hints, *ai_nfs, *ai;
641         enum tryret ret;
642         int ecode, speclen, remoteerr;
643         char *hostp, *delimp, *errstr;
644         size_t len;
645         static char nam[MNAMELEN + 1];
646
647         if ((delimp = strrchr(spec, ':')) != NULL) {
648                 hostp = spec;
649                 spec = delimp + 1;
650         } else if ((delimp = strrchr(spec, '@')) != NULL) {
651                 warnx("path@server syntax is deprecated, use server:path");
652                 hostp = delimp + 1;
653         } else {
654                 warnx("no <host>:<dirpath> nfs-name");
655                 return (0);
656         }
657         *delimp = '\0';
658
659         /*
660          * If there has been a trailing slash at mounttime it seems
661          * that some mountd implementations fail to remove the mount
662          * entries from their mountlist while unmounting.
663          */
664         for (speclen = strlen(spec); 
665                 speclen > 1 && spec[speclen - 1] == '/';
666                 speclen--)
667                 spec[speclen - 1] = '\0';
668         if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
669                 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
670                 return (0);
671         }
672         /* Make both '@' and ':' notations equal */
673         if (*hostp != '\0') {
674                 len = strlen(hostp);
675                 memmove(nam, hostp, len);
676                 nam[len] = ':';
677                 memmove(nam + len + 1, spec, speclen);
678                 nam[len + speclen + 1] = '\0';
679         }
680
681         /*
682          * Handle an internet host address.
683          */
684         memset(&hints, 0, sizeof hints);
685         hints.ai_flags = AI_NUMERICHOST;
686         if (nfsproto == IPPROTO_TCP)
687                 hints.ai_socktype = SOCK_STREAM;
688         else if (nfsproto == IPPROTO_UDP)
689                 hints.ai_socktype = SOCK_DGRAM;
690
691         if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
692                 hints.ai_flags = 0;
693                 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
694                     != 0) {
695                         if (portspec == NULL)
696                                 errx(1, "%s: %s", hostp, gai_strerror(ecode));
697                         else
698                                 errx(1, "%s:%s: %s", hostp, portspec,
699                                     gai_strerror(ecode));
700                         return (0);
701                 }
702         }
703
704         ret = TRYRET_LOCALERR;
705         for (;;) {
706                 /*
707                  * Try each entry returned by getaddrinfo(). Note the
708                  * occurence of remote errors by setting `remoteerr'.
709                  */
710                 remoteerr = 0;
711                 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
712                         if ((ai->ai_family == AF_INET6) &&
713                             (opflags & OF_NOINET6))
714                                 continue;
715                         if ((ai->ai_family == AF_INET) && 
716                             (opflags & OF_NOINET4))
717                                 continue;
718                         ret = nfs_tryproto(ai, hostp, spec, &errstr, iov,
719                             iovlen);
720                         if (ret == TRYRET_SUCCESS)
721                                 break;
722                         if (ret != TRYRET_LOCALERR)
723                                 remoteerr = 1;
724                         if ((opflags & ISBGRND) == 0)
725                                 fprintf(stderr, "%s\n", errstr);
726                 }
727                 if (ret == TRYRET_SUCCESS)
728                         break;
729
730                 /* Exit if all errors were local. */
731                 if (!remoteerr)
732                         exit(1);
733
734                 /*
735                  * If retrycnt == 0, we are to keep retrying forever.
736                  * Otherwise decrement it, and exit if it hits zero.
737                  */
738                 if (retrycnt != 0 && --retrycnt == 0)
739                         exit(1);
740
741                 if ((opflags & (BGRND | ISBGRND)) == BGRND) {
742                         warnx("Cannot immediately mount %s:%s, backgrounding",
743                             hostp, spec);
744                         opflags |= ISBGRND;
745                         if (daemon(0, 0) != 0)
746                                 err(1, "daemon");
747                 }
748                 sleep(60);
749         }
750         freeaddrinfo(ai_nfs);
751
752         build_iovec(iov, iovlen, "hostname", nam, (size_t)-1);
753         /* Add mounted file system to PATH_MOUNTTAB */
754         if (!add_mtab(hostp, spec))
755                 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
756         return (1);
757 }
758
759
760 int
761 getnfs4args(char *spec, struct iovec **iov, int *iovlen)
762 {
763         struct addrinfo hints, *ai_nfs, *ai;
764         enum tryret ret;
765         int ecode, speclen, remoteerr, sotype;
766         char *hostp, *delimp, *errstr;
767         size_t len;
768         static char nam[MNAMELEN + 1];
769
770         if (nfsproto == IPPROTO_TCP)
771                 sotype = SOCK_STREAM;
772         else if (nfsproto == IPPROTO_UDP)
773                 sotype = SOCK_DGRAM;
774
775
776         if ((delimp = strrchr(spec, ':')) != NULL) {
777                 hostp = spec;
778                 spec = delimp + 1;
779         } else if ((delimp = strrchr(spec, '@')) != NULL) {
780                 warnx("path@server syntax is deprecated, use server:path");
781                 hostp = delimp + 1;
782         } else {
783                 warnx("no <host>:<dirpath> nfs-name");
784                 return (0);
785         }
786         *delimp = '\0';
787
788         /*
789          * If there has been a trailing slash at mounttime it seems
790          * that some mountd implementations fail to remove the mount
791          * entries from their mountlist while unmounting.
792          */
793         for (speclen = strlen(spec); 
794                 speclen > 1 && spec[speclen - 1] == '/';
795                 speclen--)
796                 spec[speclen - 1] = '\0';
797         if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
798                 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
799                 return (0);
800         }
801         /* Make both '@' and ':' notations equal */
802         if (*hostp != '\0') {
803                 len = strlen(hostp);
804                 memmove(nam, hostp, len);
805                 nam[len] = ':';
806                 memmove(nam + len + 1, spec, speclen);
807                 nam[len + speclen + 1] = '\0';
808         }
809
810         /*
811          * Handle an internet host address.
812          */
813         memset(&hints, 0, sizeof hints);
814         hints.ai_flags = AI_NUMERICHOST;
815         hints.ai_socktype = sotype;
816         if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
817                 hints.ai_flags = 0;
818                 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
819                     != 0) {
820                         if (portspec == NULL)
821                                 errx(1, "%s: %s", hostp, gai_strerror(ecode));
822                         else
823                                 errx(1, "%s:%s: %s", hostp, portspec,
824                                     gai_strerror(ecode));
825                         return (0);
826                 }
827         }
828
829         ret = TRYRET_LOCALERR;
830         for (;;) {
831                 /*
832                  * Try each entry returned by getaddrinfo(). Note the
833                  * occurence of remote errors by setting `remoteerr'.
834                  */
835                 remoteerr = 0;
836                 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
837                         if ((ai->ai_family == AF_INET6) &&
838                             (opflags & OF_NOINET6))
839                                 continue;
840                         if ((ai->ai_family == AF_INET) && 
841                             (opflags & OF_NOINET4))
842                                 continue;
843                         ret = nfs4_tryproto(ai, hostp, spec, &errstr);
844                         if (ret == TRYRET_SUCCESS)
845                                 break;
846                         if (ret != TRYRET_LOCALERR)
847                                 remoteerr = 1;
848                         if ((opflags & ISBGRND) == 0)
849                                 fprintf(stderr, "%s\n", errstr);
850                 }
851                 if (ret == TRYRET_SUCCESS)
852                         break;
853
854                 /* Exit if all errors were local. */
855                 if (!remoteerr)
856                         exit(1);
857
858                 /*
859                  * If retrycnt == 0, we are to keep retrying forever.
860                  * Otherwise decrement it, and exit if it hits zero.
861                  */
862                 if (retrycnt != 0 && --retrycnt == 0)
863                         exit(1);
864
865                 if ((opflags & (BGRND | ISBGRND)) == BGRND) {
866                         warnx("Cannot immediately mount %s:%s, backgrounding",
867                             hostp, spec);
868                         opflags |= ISBGRND;
869                         if (daemon(0, 0) != 0)
870                                 err(1, "daemon");
871                 }
872                 sleep(60);
873         }
874         freeaddrinfo(ai_nfs);
875         build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 
876         /* Add mounted file system to PATH_MOUNTTAB */
877         if (!add_mtab(hostp, spec))
878                 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
879         return (1);
880 }
881
882 /*
883  * Try to set up the NFS arguments according to the address
884  * family, protocol (and possibly port) specified in `ai'.
885  *
886  * Returns TRYRET_SUCCESS if successful, or:
887  *   TRYRET_TIMEOUT             The server did not respond.
888  *   TRYRET_REMOTEERR           The server reported an error.
889  *   TRYRET_LOCALERR            Local failure.
890  *
891  * In all error cases, *errstr will be set to a statically-allocated string
892  * describing the error.
893  */
894 enum tryret
895 nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr,
896     struct iovec **iov, int *iovlen)
897 {
898         static char errbuf[256];
899         struct sockaddr_storage nfs_ss;
900         struct netbuf nfs_nb;
901         struct nfhret nfhret;
902         struct timeval try;
903         struct rpc_err rpcerr;
904         CLIENT *clp;
905         struct netconfig *nconf, *nconf_mnt;
906         const char *netid, *netid_mnt;
907         int doconnect, nfsvers, mntvers, sotype;
908         enum clnt_stat stat;
909         enum mountmode trymntmode;
910
911         trymntmode = mountmode;
912         errbuf[0] = '\0';
913         *errstr = errbuf;
914
915         if (nfsproto == IPPROTO_TCP)
916                 sotype = SOCK_STREAM;
917         else if (nfsproto == IPPROTO_UDP)
918                 sotype = SOCK_DGRAM;
919
920         if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) {
921                 snprintf(errbuf, sizeof errbuf,
922                     "af %d sotype %d not supported", ai->ai_family, sotype);
923                 return (TRYRET_LOCALERR);
924         }
925         if ((nconf = getnetconf_cached(netid)) == NULL) {
926                 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
927                 return (TRYRET_LOCALERR);
928         }
929         /* The RPCPROG_MNT netid may be different. */
930         if (mnttcp_ok) {
931                 netid_mnt = netid;
932                 nconf_mnt = nconf;
933         } else {
934                 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM))
935                      == NULL) {
936                         snprintf(errbuf, sizeof errbuf,
937                             "af %d sotype SOCK_DGRAM not supported",
938                              ai->ai_family);
939                         return (TRYRET_LOCALERR);
940                 }
941                 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) {
942                         snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt,
943                             nc_sperror());
944                         return (TRYRET_LOCALERR);
945                 }
946         }
947
948 tryagain:
949         if (trymntmode == V2) {
950                 nfsvers = 2;
951                 mntvers = 1;
952         } else {
953                 nfsvers = 3;
954                 mntvers = 3;
955         }
956
957         if (portspec != NULL) {
958                 /* `ai' contains the complete nfsd sockaddr. */
959                 nfs_nb.buf = ai->ai_addr;
960                 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
961         } else {
962                 /* Ask the remote rpcbind. */
963                 nfs_nb.buf = &nfs_ss;
964                 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
965
966                 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
967                     hostp)) {
968                         if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH &&
969                             trymntmode == ANY) {
970                                 trymntmode = V2;
971                                 goto tryagain;
972                         }
973                         snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
974                             netid, hostp, spec,
975                             clnt_spcreateerror("RPCPROG_NFS"));
976                         return (returncode(rpc_createerr.cf_stat,
977                             &rpc_createerr.cf_error));
978                 }
979         }
980
981         /* Check that the server (nfsd) responds on the port we have chosen. */
982         clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers,
983             0, 0);
984         if (clp == NULL) {
985                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
986                     hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
987                 return (returncode(rpc_createerr.cf_stat,
988                     &rpc_createerr.cf_error));
989         }
990         if (sotype == SOCK_DGRAM && noconn == 0) {
991                 /*
992                  * Use connect(), to match what the kernel does. This
993                  * catches cases where the server responds from the
994                  * wrong source address.
995                  */
996                 doconnect = 1;
997                 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
998                         clnt_destroy(clp);
999                         snprintf(errbuf, sizeof errbuf,
1000                             "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp,
1001                             spec);
1002                         return (TRYRET_LOCALERR);
1003                 }
1004         }
1005
1006         try.tv_sec = 10;
1007         try.tv_usec = 0;
1008         stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
1009                          (xdrproc_t)xdr_void, NULL,
1010             try);
1011         if (stat != RPC_SUCCESS) {
1012                 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
1013                         clnt_destroy(clp);
1014                         trymntmode = V2;
1015                         goto tryagain;
1016                 }
1017                 clnt_geterr(clp, &rpcerr);
1018                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
1019                     hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
1020                 clnt_destroy(clp);
1021                 return (returncode(stat, &rpcerr));
1022         }
1023         clnt_destroy(clp);
1024
1025         /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */
1026         try.tv_sec = 10;
1027         try.tv_usec = 0;
1028         clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt);
1029         if (clp == NULL) {
1030                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
1031                     hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
1032                 return (returncode(rpc_createerr.cf_stat,
1033                     &rpc_createerr.cf_error));
1034         }
1035         clp->cl_auth = authsys_create_default();
1036         nfhret.auth = -1;
1037         nfhret.vers = mntvers;
1038         stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec, 
1039                          (xdrproc_t)xdr_fh, &nfhret,
1040             try);
1041         auth_destroy(clp->cl_auth);
1042         if (stat != RPC_SUCCESS) {
1043                 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
1044                         clnt_destroy(clp);
1045                         trymntmode = V2;
1046                         goto tryagain;
1047                 }
1048                 clnt_geterr(clp, &rpcerr);
1049                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
1050                     hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
1051                 clnt_destroy(clp);
1052                 return (returncode(stat, &rpcerr));
1053         }
1054         clnt_destroy(clp);
1055
1056         if (nfhret.stat != 0) {
1057                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
1058                     hostp, spec, strerror(nfhret.stat));
1059                 return (TRYRET_REMOTEERR);
1060         }
1061
1062         /*
1063          * Store the filehandle and server address in nfsargsp, making
1064          * sure to copy any locally allocated structures.
1065          */
1066         addrlen = nfs_nb.len;
1067         addr = malloc(addrlen);
1068         fhsize = nfhret.fhsize;
1069         fh = malloc(fhsize);
1070         if (addr == NULL || fh == NULL)
1071                 err(1, "malloc");
1072         bcopy(nfs_nb.buf, addr, addrlen);
1073         bcopy(nfhret.nfh, fh, fhsize);
1074
1075         build_iovec(iov, iovlen, "addr", addr, addrlen);
1076         build_iovec(iov, iovlen, "fh", fh, fhsize);
1077         if (nfsvers == 3)
1078                 build_iovec(iov, iovlen, "nfsv3", NULL, 0);
1079
1080         return (TRYRET_SUCCESS);
1081 }
1082
1083
1084 /*
1085  * Try to set up the NFS arguments according to the address
1086  * family, protocol (and possibly port) specified in `ai'.
1087  *
1088  * Returns TRYRET_SUCCESS if successful, or:
1089  *   TRYRET_TIMEOUT             The server did not respond.
1090  *   TRYRET_REMOTEERR           The server reported an error.
1091  *   TRYRET_LOCALERR            Local failure.
1092  *
1093  * In all error cases, *errstr will be set to a statically-allocated string
1094  * describing the error.
1095  */
1096 enum tryret
1097 nfs4_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr)
1098 {
1099         static char errbuf[256];
1100         struct sockaddr_storage nfs_ss;
1101         struct netbuf nfs_nb;
1102         struct netconfig *nconf;
1103         const char *netid;
1104         int nfsvers, sotype;
1105
1106         errbuf[0] = '\0';
1107         *errstr = errbuf;
1108
1109         if (nfsproto == IPPROTO_TCP)
1110                 sotype = SOCK_STREAM;
1111         else if (nfsproto == IPPROTO_UDP)
1112                 sotype = SOCK_DGRAM;
1113
1114         if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) {
1115                 snprintf(errbuf, sizeof errbuf,
1116                     "af %d sotype %d not supported", ai->ai_family, sotype);
1117                 return (TRYRET_LOCALERR);
1118         }
1119         if ((nconf = getnetconf_cached(netid)) == NULL) {
1120                 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
1121                 return (TRYRET_LOCALERR);
1122         }
1123
1124         nfsvers = 4;
1125
1126         if (portspec != NULL && atoi(portspec) != 0) {
1127                 /* `ai' contains the complete nfsd sockaddr. */
1128                 nfs_nb.buf = ai->ai_addr;
1129                 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
1130         } else {
1131                 /* Ask the remote rpcbind. */
1132                 nfs_nb.buf = &nfs_ss;
1133                 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
1134
1135                 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
1136                     hostp)) {
1137                         snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
1138                             netid, hostp, spec,
1139                             clnt_spcreateerror("RPCPROG_NFS"));
1140                         return (returncode(rpc_createerr.cf_stat,
1141                             &rpc_createerr.cf_error));
1142                 }
1143         }
1144
1145         /*
1146          * Store the filehandle and server address in nfsargsp, making
1147          * sure to copy any locally allocated structures.
1148          */
1149         addrlen = nfs_nb.len;
1150         addr = malloc(addrlen);
1151
1152         if (addr == NULL)
1153                 err(1, "malloc");
1154         bcopy(nfs_nb.buf, addr, addrlen);
1155
1156         return (TRYRET_SUCCESS);
1157 }
1158
1159 /*
1160  * Catagorise a RPC return status and error into an `enum tryret'
1161  * return code.
1162  */
1163 enum tryret
1164 returncode(enum clnt_stat stat, struct rpc_err *rpcerr)
1165 {
1166         switch (stat) {
1167         case RPC_TIMEDOUT:
1168                 return (TRYRET_TIMEOUT);
1169         case RPC_PMAPFAILURE:
1170         case RPC_PROGNOTREGISTERED:
1171         case RPC_PROGVERSMISMATCH:
1172         /* XXX, these can be local or remote. */
1173         case RPC_CANTSEND:
1174         case RPC_CANTRECV:
1175                 return (TRYRET_REMOTEERR);
1176         case RPC_SYSTEMERROR:
1177                 switch (rpcerr->re_errno) {
1178                 case ETIMEDOUT:
1179                         return (TRYRET_TIMEOUT);
1180                 case ENOMEM:
1181                         break;
1182                 default:
1183                         return (TRYRET_REMOTEERR);
1184                 }
1185                 /* FALLTHROUGH */
1186         default:
1187                 break;
1188         }
1189         return (TRYRET_LOCALERR);
1190 }
1191
1192 /*
1193  * Look up a netid based on an address family and socket type.
1194  * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
1195  *
1196  * XXX there should be a library function for this.
1197  */
1198 const char *
1199 netidbytype(int af, int sotype)
1200 {
1201         struct nc_protos *p;
1202
1203         for (p = nc_protos; p->netid != NULL; p++) {
1204                 if (af != p->af || sotype != p->sotype)
1205                         continue;
1206                 return (p->netid);
1207         }
1208         return (NULL);
1209 }
1210
1211 /*
1212  * Look up a netconfig entry based on a netid, and cache the result so
1213  * that we don't need to remember to call freenetconfigent().
1214  *
1215  * Otherwise it behaves just like getnetconfigent(), so nc_*error()
1216  * work on failure.
1217  */
1218 struct netconfig *
1219 getnetconf_cached(const char *netid)
1220 {
1221         static struct nc_entry {
1222                 struct netconfig *nconf;
1223                 struct nc_entry *next;
1224         } *head;
1225         struct nc_entry *p;
1226         struct netconfig *nconf;
1227
1228         for (p = head; p != NULL; p = p->next)
1229                 if (strcmp(netid, p->nconf->nc_netid) == 0)
1230                         return (p->nconf);
1231
1232         if ((nconf = getnetconfigent(netid)) == NULL)
1233                 return (NULL);
1234         if ((p = malloc(sizeof(*p))) == NULL)
1235                 err(1, "malloc");
1236         p->nconf = nconf;
1237         p->next = head;
1238         head = p;
1239
1240         return (p->nconf);
1241 }
1242
1243 /*
1244  * xdr routines for mount rpc's
1245  */
1246 int
1247 xdr_dir(XDR *xdrsp, char *dirp)
1248 {
1249         return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
1250 }
1251
1252 int
1253 xdr_fh(XDR *xdrsp, struct nfhret *np)
1254 {
1255         int i;
1256         long auth, authcnt, authfnd = 0;
1257
1258         if (!xdr_u_long(xdrsp, &np->stat))
1259                 return (0);
1260         if (np->stat)
1261                 return (1);
1262         switch (np->vers) {
1263         case 1:
1264                 np->fhsize = NFSX_V2FH;
1265                 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
1266         case 3:
1267                 if (!xdr_long(xdrsp, &np->fhsize))
1268                         return (0);
1269                 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
1270                         return (0);
1271                 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
1272                         return (0);
1273                 if (!xdr_long(xdrsp, &authcnt))
1274                         return (0);
1275                 for (i = 0; i < authcnt; i++) {
1276                         if (!xdr_long(xdrsp, &auth))
1277                                 return (0);
1278                         if (np->auth == -1) {
1279                                 np->auth = auth;
1280                                 authfnd++;
1281                         } else if (auth == np->auth) {
1282                                 authfnd++;
1283                         }
1284                 }
1285                 /*
1286                  * Some servers, such as DEC's OSF/1 return a nil authenticator
1287                  * list to indicate RPCAUTH_UNIX.
1288                  */
1289                 if (authcnt == 0 && np->auth == -1)
1290                         np->auth = AUTH_SYS;
1291                 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS))
1292                         np->stat = EAUTH;
1293                 return (1);
1294         };
1295         return (0);
1296 }
1297
1298 void
1299 usage()
1300 {
1301         (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1302 "usage: mount_nfs [-234bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]",
1303 "                 [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]",
1304 "                 [-r readsize] [-t timeout] [-w writesize] [-x retrans]",
1305 "                 rhost:path node");
1306         exit(1);
1307 }