Initial import from FreeBSD RELENG_4:
[games.git] / sbin / mount_nfs / mount_nfs.c
1 /*
2  * Copyright (c) 1992, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1992, 1993, 1994\n\
40         The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95";
46 #endif
47 static const char rcsid[] =
48   "$FreeBSD: src/sbin/mount_nfs/mount_nfs.c,v 1.36.2.6 2003/05/13 14:45:40 trhodes Exp $";
49 #endif /* not lint */
50
51 #include <sys/param.h>
52 #include <sys/mount.h>
53 #include <sys/stat.h>
54 #include <sys/syslog.h>
55
56 #include <rpc/rpc.h>
57 #include <rpc/pmap_clnt.h>
58 #include <rpc/pmap_prot.h>
59
60 #ifdef NFSKERB
61 #include <kerberosIV/des.h>
62 #include <kerberosIV/krb.h>
63 #endif
64
65 #include <nfs/rpcv2.h>
66 #include <nfs/nfsproto.h>
67 #include <nfs/nfs.h>
68 #include <nfs/nqnfs.h>
69
70 #include <arpa/inet.h>
71
72 #include <ctype.h>
73 #include <err.h>
74 #include <errno.h>
75 #include <netdb.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <strings.h>
79 #include <sysexits.h>
80 #include <unistd.h>
81
82 #include "mntopts.h"
83 #include "mounttab.h"
84
85 #define ALTF_BG         0x1
86 #define ALTF_NOCONN     0x2
87 #define ALTF_DUMBTIMR   0x4
88 #define ALTF_INTR       0x8
89 #define ALTF_KERB       0x10
90 #define ALTF_NFSV3      0x20
91 #define ALTF_RDIRPLUS   0x40
92 #define ALTF_MNTUDP     0x80
93 #define ALTF_RESVPORT   0x100
94 #define ALTF_SEQPACKET  0x200
95 #define ALTF_NQNFS      0x400
96 #define ALTF_SOFT       0x800
97 #define ALTF_TCP        0x1000
98 #define ALTF_PORT       0x2000
99 #define ALTF_NFSV2      0x4000
100 #define ALTF_ACREGMIN   0x8000
101 #define ALTF_ACREGMAX   0x10000
102 #define ALTF_ACDIRMIN   0x20000
103 #define ALTF_ACDIRMAX   0x40000
104
105 struct mntopt mopts[] = {
106         MOPT_STDOPTS,
107         MOPT_FORCE,
108         MOPT_UPDATE,
109         MOPT_ASYNC,
110         { "bg", 0, ALTF_BG, 1 },
111         { "conn", 1, ALTF_NOCONN, 1 },
112         { "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
113         { "intr", 0, ALTF_INTR, 1 },
114 #ifdef NFSKERB
115         { "kerb", 0, ALTF_KERB, 1 },
116 #endif
117         { "nfsv3", 0, ALTF_NFSV3, 1 },
118         { "rdirplus", 0, ALTF_RDIRPLUS, 1 },
119         { "mntudp", 0, ALTF_MNTUDP, 1 },
120         { "resvport", 0, ALTF_RESVPORT, 1 },
121         { "nqnfs", 0, ALTF_NQNFS, 1 },
122         { "soft", 0, ALTF_SOFT, 1 },
123         { "tcp", 0, ALTF_TCP, 1 },
124         { "port=", 0, ALTF_PORT, 1 },
125         { "nfsv2", 0, ALTF_NFSV2, 1 },
126         { "acregmin=", 0, ALTF_ACREGMIN, 1 },
127         { "acregmax=", 0, ALTF_ACREGMAX, 1 },
128         { "acdirmin=", 0, ALTF_ACDIRMIN, 1 },
129         { "acdirmax=", 0, ALTF_ACDIRMAX, 1 },
130         { NULL }
131 };
132
133 struct nfs_args nfsdefargs = {
134         NFS_ARGSVERSION,
135         (struct sockaddr *)0,
136         sizeof (struct sockaddr_in),
137         SOCK_DGRAM,
138         0,
139         (u_char *)0,
140         0,
141         NFSMNT_RESVPORT,
142         NFS_WSIZE,
143         NFS_RSIZE,
144         NFS_READDIRSIZE,
145         10,
146         NFS_RETRANS,
147         NFS_MAXGRPS,
148         NFS_DEFRAHEAD,
149         NQ_DEFLEASE,
150         NQ_DEADTHRESH,
151         (char *)0,
152         /* args version 4 */
153         NFS_MINATTRTIMO,
154         NFS_MAXATTRTIMO,
155         NFS_MINDIRATTRTIMO,
156         NFS_MAXDIRATTRTIMO,
157 };
158
159 struct nfhret {
160         u_long          stat;
161         long            vers;
162         long            auth;
163         long            fhsize;
164         u_char          nfh[NFSX_V3FHMAX];
165 };
166 #define BGRND   1
167 #define ISBGRND 2
168 int retrycnt = -1;
169 int opflags = 0;
170 int nfsproto = IPPROTO_UDP;
171 int mnttcp_ok = 1;
172 u_short port_no = 0;
173 enum mountmode {
174         ANY,
175         V2,
176         V3
177 } mountmode = ANY;
178
179 #ifdef NFSKERB
180 char inst[INST_SZ];
181 char realm[REALM_SZ];
182 struct {
183         u_long          kind;
184         KTEXT_ST        kt;
185 } ktick;
186 struct nfsrpc_nickverf kverf;
187 struct nfsrpc_fullblock kin, kout;
188 NFSKERBKEY_T kivec;
189 CREDENTIALS kcr;
190 struct timeval ktv;
191 NFSKERBKEYSCHED_T kerb_keysched;
192 #endif
193
194 /* Return codes for nfs_tryproto. */
195 enum tryret {
196         TRYRET_SUCCESS,
197         TRYRET_TIMEOUT,         /* No response received. */
198         TRYRET_REMOTEERR,       /* Error received from remote server. */
199         TRYRET_LOCALERR         /* Local failure. */
200 };
201
202 int     getnfsargs __P((char *, struct nfs_args *));
203 void    set_rpc_maxgrouplist __P((int));
204 void    usage __P((void)) __dead2;
205 int     xdr_dir __P((XDR *, char *));
206 int     xdr_fh __P((XDR *, struct nfhret *));
207 enum tryret nfs_tryproto(struct nfs_args *nfsargsp, struct sockaddr_in *sin,
208     char *hostp, char *spec, char **errstr);
209 enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr);
210
211 /*
212  * Used to set mount flags with getmntopts.  Call with dir=TRUE to
213  * initialize altflags from the current mount flags.  Call with
214  * dir=FALSE to update mount flags with the new value of altflags after
215  * the call to getmntopts.
216  */
217 static void
218 set_flags(int* altflags, int* nfsflags, int dir)
219 {
220 #define F2(af, nf)                                      \
221         if (dir) {                                      \
222                 if (*nfsflags & NFSMNT_##nf)            \
223                         *altflags |= ALTF_##af;         \
224                 else                                    \
225                         *altflags &= ~ALTF_##af;        \
226         } else {                                        \
227                 if (*altflags & ALTF_##af)              \
228                         *nfsflags |= NFSMNT_##nf;       \
229                 else                                    \
230                         *nfsflags &= ~NFSMNT_##nf;      \
231         }
232 #define F(f)    F2(f,f)
233
234         F(NOCONN);
235         F(DUMBTIMR);
236         F2(INTR, INT);
237 #ifdef NFSKERB
238         F(KERB);
239 #endif
240         F(RDIRPLUS);
241         F(RESVPORT);
242         F(NQNFS);
243         F(SOFT);
244         F(ACREGMIN);
245         F(ACREGMAX);
246         F(ACDIRMIN);
247         F(ACDIRMAX);
248
249 #undef F
250 #undef F2
251 }
252
253 int
254 main(argc, argv)
255         int argc;
256         char *argv[];
257 {
258         register int c;
259         register struct nfs_args *nfsargsp;
260         struct nfs_args nfsargs;
261         struct nfsd_cargs ncd;
262         int mntflags, altflags, nfssvc_flag, num;
263         char *name, *p, *spec;
264         char mntpath[MAXPATHLEN];
265         struct vfsconf vfc;
266         int error = 0;
267 #ifdef NFSKERB
268         uid_t last_ruid;
269
270         last_ruid = -1;
271         (void)strcpy(realm, KRB_REALM);
272         if (sizeof (struct nfsrpc_nickverf) != RPCX_NICKVERF ||
273             sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK ||
274             ((char *)&ktick.kt) - ((char *)&ktick) != NFSX_UNSIGNED ||
275             ((char *)ktick.kt.dat) - ((char *)&ktick) != 2 * NFSX_UNSIGNED)
276                 fprintf(stderr, "Yikes! NFSKERB structs not packed!!\n");
277 #endif /* NFSKERB */
278
279         mntflags = 0;
280         altflags = 0;
281         nfsargs = nfsdefargs;
282         nfsargsp = &nfsargs;
283         while ((c = getopt(argc, argv,
284             "23a:bcdD:g:I:iKL:lm:No:PqR:r:sTt:w:x:U")) != -1)
285                 switch (c) {
286                 case '2':
287                         mountmode = V2;
288                         break;
289                 case '3':
290                         mountmode = V3;
291                         break;
292                 case 'a':
293                         num = strtol(optarg, &p, 10);
294                         if (*p || num < 0)
295                                 errx(1, "illegal -a value -- %s", optarg);
296                         nfsargsp->readahead = num;
297                         nfsargsp->flags |= NFSMNT_READAHEAD;
298                         break;
299                 case 'b':
300                         opflags |= BGRND;
301                         break;
302                 case 'c':
303                         nfsargsp->flags |= NFSMNT_NOCONN;
304                         break;
305                 case 'D':
306                         num = strtol(optarg, &p, 10);
307                         if (*p || num <= 0)
308                                 errx(1, "illegal -D value -- %s", optarg);
309                         nfsargsp->deadthresh = num;
310                         nfsargsp->flags |= NFSMNT_DEADTHRESH;
311                         break;
312                 case 'd':
313                         nfsargsp->flags |= NFSMNT_DUMBTIMR;
314                         break;
315                 case 'g':
316                         num = strtol(optarg, &p, 10);
317                         if (*p || num <= 0)
318                                 errx(1, "illegal -g value -- %s", optarg);
319                         set_rpc_maxgrouplist(num);
320                         nfsargsp->maxgrouplist = num;
321                         nfsargsp->flags |= NFSMNT_MAXGRPS;
322                         break;
323                 case 'I':
324                         num = strtol(optarg, &p, 10);
325                         if (*p || num <= 0)
326                                 errx(1, "illegal -I value -- %s", optarg);
327                         nfsargsp->readdirsize = num;
328                         nfsargsp->flags |= NFSMNT_READDIRSIZE;
329                         break;
330                 case 'i':
331                         nfsargsp->flags |= NFSMNT_INT;
332                         break;
333 #ifdef NFSKERB
334                 case 'K':
335                         nfsargsp->flags |= NFSMNT_KERB;
336                         break;
337 #endif
338                 case 'L':
339                         num = strtol(optarg, &p, 10);
340                         if (*p || num < 2)
341                                 errx(1, "illegal -L value -- %s", optarg);
342                         nfsargsp->leaseterm = num;
343                         nfsargsp->flags |= NFSMNT_LEASETERM;
344                         break;
345                 case 'l':
346                         nfsargsp->flags |= NFSMNT_RDIRPLUS;
347                         break;
348 #ifdef NFSKERB
349                 case 'm':
350                         (void)strncpy(realm, optarg, REALM_SZ - 1);
351                         realm[REALM_SZ - 1] = '\0';
352                         break;
353 #endif
354                 case 'N':
355                         nfsargsp->flags &= ~NFSMNT_RESVPORT;
356                         break;
357                 case 'o':
358                         altflags = 0;
359                         set_flags(&altflags, &nfsargsp->flags, TRUE);
360                         if (mountmode == V2)
361                                 altflags |= ALTF_NFSV2;
362                         else if (mountmode == V3)
363                                 altflags |= ALTF_NFSV3;
364                         getmntopts(optarg, mopts, &mntflags, &altflags);
365                         set_flags(&altflags, &nfsargsp->flags, FALSE);
366                         /*
367                          * Handle altflags which don't map directly to
368                          * mount flags.
369                          */
370                         if(altflags & ALTF_BG)
371                                 opflags |= BGRND;
372                         if(altflags & ALTF_MNTUDP)
373                                 mnttcp_ok = 0;
374                         if(altflags & ALTF_TCP) {
375                                 nfsargsp->sotype = SOCK_STREAM;
376                                 nfsproto = IPPROTO_TCP;
377                         }
378                         if(altflags & ALTF_PORT)
379                                 port_no = atoi(strstr(optarg, "port=") + 5);
380                         mountmode = ANY;
381                         if(altflags & ALTF_NFSV2)
382                                 mountmode = V2;
383                         if(altflags & ALTF_NFSV3)
384                                 mountmode = V3;
385                         if(altflags & ALTF_ACREGMIN)
386                                 nfsargsp->acregmin = atoi(strstr(optarg,
387                                     "acregmin=") + 9);
388                         if(altflags & ALTF_ACREGMAX)
389                                 nfsargsp->acregmax = atoi(strstr(optarg,
390                                     "acregmax=") + 9);
391                         if(altflags & ALTF_ACDIRMIN)
392                                 nfsargsp->acdirmin = atoi(strstr(optarg,
393                                     "acdirmin=") + 9);
394                         if(altflags & ALTF_ACDIRMAX)
395                                 nfsargsp->acdirmax = atoi(strstr(optarg,
396                                     "acdirmax=") + 9);
397                         break;
398                 case 'P':
399                         /* obsolete for NFSMNT_RESVPORT, now default */
400                         break;
401                 case 'q':
402                         mountmode = V3;
403                         nfsargsp->flags |= NFSMNT_NQNFS;
404                         break;
405                 case 'R':
406                         num = strtol(optarg, &p, 10);
407                         if (*p || num < 0)
408                                 errx(1, "illegal -R value -- %s", optarg);
409                         retrycnt = num;
410                         break;
411                 case 'r':
412                         num = strtol(optarg, &p, 10);
413                         if (*p || num <= 0)
414                                 errx(1, "illegal -r value -- %s", optarg);
415                         nfsargsp->rsize = num;
416                         nfsargsp->flags |= NFSMNT_RSIZE;
417                         break;
418                 case 's':
419                         nfsargsp->flags |= NFSMNT_SOFT;
420                         break;
421                 case 'T':
422                         nfsargsp->sotype = SOCK_STREAM;
423                         nfsproto = IPPROTO_TCP;
424                         break;
425                 case 't':
426                         num = strtol(optarg, &p, 10);
427                         if (*p || num <= 0)
428                                 errx(1, "illegal -t value -- %s", optarg);
429                         nfsargsp->timeo = num;
430                         nfsargsp->flags |= NFSMNT_TIMEO;
431                         break;
432                 case 'w':
433                         num = strtol(optarg, &p, 10);
434                         if (*p || num <= 0)
435                                 errx(1, "illegal -w value -- %s", optarg);
436                         nfsargsp->wsize = num;
437                         nfsargsp->flags |= NFSMNT_WSIZE;
438                         break;
439                 case 'x':
440                         num = strtol(optarg, &p, 10);
441                         if (*p || num <= 0)
442                                 errx(1, "illegal -x value -- %s", optarg);
443                         nfsargsp->retrans = num;
444                         nfsargsp->flags |= NFSMNT_RETRANS;
445                         break;
446                 case 'U':
447                         mnttcp_ok = 0;
448                         break;
449                 default:
450                         usage();
451                         break;
452                 }
453         argc -= optind;
454         argv += optind;
455
456         if (argc != 2) {
457                 usage();
458                 /* NOTREACHED */
459         }
460
461         spec = *argv++;
462         name = *argv;
463
464         if (retrycnt == -1)
465                 /* The default is to keep retrying forever. */
466                 retrycnt = 0;
467         if (!getnfsargs(spec, nfsargsp))
468                 exit(1);
469
470         /* resolve the mountpoint with realpath(3) */
471         (void)checkpath(name, mntpath);
472
473         error = getvfsbyname("nfs", &vfc);
474         if (error && vfsisloadable("nfs")) {
475                 if(vfsload("nfs"))
476                         err(EX_OSERR, "vfsload(nfs)");
477                 endvfsent();    /* clear cache */
478                 error = getvfsbyname("nfs", &vfc);
479         }
480         if (error)
481                 errx(EX_OSERR, "nfs filesystem is not available");
482
483         if (mount(vfc.vfc_name, mntpath, mntflags, nfsargsp))
484                 err(1, "%s", mntpath);
485         if (nfsargsp->flags & (NFSMNT_NQNFS | NFSMNT_KERB)) {
486                 if ((opflags & ISBGRND) == 0) {
487                         if (daemon(0, 0) != 0)
488                                 err(1, "daemon");
489                 }
490                 openlog("mount_nfs", LOG_PID, LOG_DAEMON);
491                 nfssvc_flag = NFSSVC_MNTD;
492                 ncd.ncd_dirp = mntpath;
493                 while (nfssvc(nfssvc_flag, (caddr_t)&ncd) < 0) {
494                         if (errno != ENEEDAUTH) {
495                                 syslog(LOG_ERR, "nfssvc err %m");
496                                 continue;
497                         }
498                         nfssvc_flag =
499                             NFSSVC_MNTD | NFSSVC_GOTAUTH | NFSSVC_AUTHINFAIL;
500 #ifdef NFSKERB
501                         /*
502                          * Set up as ncd_authuid for the kerberos call.
503                          * Must set ruid to ncd_authuid and reset the
504                          * ticket name iff ncd_authuid is not the same
505                          * as last time, so that the right ticket file
506                          * is found.
507                          * Get the Kerberos credential structure so that
508                          * we have the session key and get a ticket for
509                          * this uid.
510                          * For more info see the IETF Draft "Authentication
511                          * in ONC RPC".
512                          */
513                         if (ncd.ncd_authuid != last_ruid) {
514                                 char buf[512];
515                                 (void)sprintf(buf, "%s%d",
516                                               TKT_ROOT, ncd.ncd_authuid);
517                                 krb_set_tkt_string(buf);
518                                 last_ruid = ncd.ncd_authuid;
519                         }
520                         setreuid(ncd.ncd_authuid, 0);
521                         kret = krb_get_cred(NFS_KERBSRV, inst, realm, &kcr);
522                         if (kret == RET_NOTKT) {
523                             kret = get_ad_tkt(NFS_KERBSRV, inst, realm,
524                                 DEFAULT_TKT_LIFE);
525                             if (kret == KSUCCESS)
526                                 kret = krb_get_cred(NFS_KERBSRV, inst, realm,
527                                     &kcr);
528                         }
529                         if (kret == KSUCCESS)
530                             kret = krb_mk_req(&ktick.kt, NFS_KERBSRV, inst,
531                                 realm, 0);
532
533                         /*
534                          * Fill in the AKN_FULLNAME authenticator and verifier.
535                          * Along with the Kerberos ticket, we need to build
536                          * the timestamp verifier and encrypt it in CBC mode.
537                          */
538                         if (kret == KSUCCESS &&
539                             ktick.kt.length <= (RPCAUTH_MAXSIZ-3*NFSX_UNSIGNED)
540                             && gettimeofday(&ktv, (struct timezone *)0) == 0) {
541                             ncd.ncd_authtype = RPCAUTH_KERB4;
542                             ncd.ncd_authstr = (u_char *)&ktick;
543                             ncd.ncd_authlen = nfsm_rndup(ktick.kt.length) +
544                                 3 * NFSX_UNSIGNED;
545                             ncd.ncd_verfstr = (u_char *)&kverf;
546                             ncd.ncd_verflen = sizeof (kverf);
547                             memmove(ncd.ncd_key, kcr.session,
548                                 sizeof (kcr.session));
549                             kin.t1 = htonl(ktv.tv_sec);
550                             kin.t2 = htonl(ktv.tv_usec);
551                             kin.w1 = htonl(NFS_KERBTTL);
552                             kin.w2 = htonl(NFS_KERBTTL - 1);
553                             bzero((caddr_t)kivec, sizeof (kivec));
554
555                             /*
556                              * Encrypt kin in CBC mode using the session
557                              * key in kcr.
558                              */
559                             XXX
560
561                             /*
562                              * Finally, fill the timestamp verifier into the
563                              * authenticator and verifier.
564                              */
565                             ktick.kind = htonl(RPCAKN_FULLNAME);
566                             kverf.kind = htonl(RPCAKN_FULLNAME);
567                             NFS_KERBW1(ktick.kt) = kout.w1;
568                             ktick.kt.length = htonl(ktick.kt.length);
569                             kverf.verf.t1 = kout.t1;
570                             kverf.verf.t2 = kout.t2;
571                             kverf.verf.w2 = kout.w2;
572                             nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH;
573                         }
574                         setreuid(0, 0);
575 #endif /* NFSKERB */
576                 }
577         }
578         exit(0);
579 }
580
581 int
582 getnfsargs(spec, nfsargsp)
583         char *spec;
584         struct nfs_args *nfsargsp;
585 {
586         struct hostent *hp;
587         struct sockaddr_in saddr;
588         enum tryret ret;
589         int speclen, remoteerr;
590         char *hostp, *delimp, *errstr;
591 #ifdef NFSKERB
592         char *cp;
593 #endif
594         size_t len;
595         static char nam[MNAMELEN + 1];
596
597         if ((delimp = strrchr(spec, ':')) != NULL) {
598                 hostp = spec;
599                 spec = delimp + 1;
600         } else if ((delimp = strrchr(spec, '@')) != NULL) {
601                 warnx("path@server syntax is deprecated, use server:path");
602                 hostp = delimp + 1;
603         } else {
604                 warnx("no <host>:<dirpath> nfs-name");
605                 return (0);
606         }
607         *delimp = '\0';
608
609         /*
610          * If there has been a trailing slash at mounttime it seems
611          * that some mountd implementations fail to remove the mount
612          * entries from their mountlist while unmounting.
613          */
614         for (speclen = strlen(spec); 
615                 speclen > 1 && spec[speclen - 1] == '/';
616                 speclen--)
617                 spec[speclen - 1] = '\0';
618         if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
619                 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
620                 return (0);
621         }
622         /* Make both '@' and ':' notations equal */
623         if (*hostp != '\0') {
624                 len = strlen(hostp);
625                 memmove(nam, hostp, len);
626                 nam[len] = ':';
627                 memmove(nam + len + 1, spec, speclen);
628                 nam[len + speclen + 1] = '\0';
629         }
630
631         /*
632          * Handle an internet host address and reverse resolve it if
633          * doing Kerberos.
634          */
635         bzero(&saddr, sizeof saddr);
636         saddr.sin_family = AF_INET;
637         saddr.sin_len = sizeof saddr;
638         if (port_no != 0)
639                 saddr.sin_port = htons(port_no);
640         if (isdigit(*hostp)) {
641                 if ((saddr.sin_addr.s_addr = inet_addr(hostp)) == -1) {
642                         warnx("bad net address %s", hostp);
643                         return (0);
644                 }
645         } else if ((hp = gethostbyname(hostp)) != NULL)
646                 memmove(&saddr.sin_addr, hp->h_addr, 
647                     MIN(hp->h_length, sizeof(saddr.sin_addr)));
648         else {
649                 warnx("can't get net id for host");
650                 return (0);
651         }
652 #ifdef NFSKERB
653         if ((nfsargsp->flags & NFSMNT_KERB)) {
654                 if ((hp = gethostbyaddr((char *)&saddr.sin_addr.s_addr,
655                     sizeof (u_long), AF_INET)) == (struct hostent *)0) {
656                         warnx("can't reverse resolve net address");
657                         return (0);
658                 }
659                 memmove(&saddr.sin_addr, hp->h_addr, 
660                     MIN(hp->h_length, sizeof(saddr.sin_addr)));
661                 strncpy(inst, hp->h_name, INST_SZ);
662                 inst[INST_SZ - 1] = '\0';
663                 if (cp = strchr(inst, '.'))
664                         *cp = '\0';
665         }
666 #endif /* NFSKERB */
667
668         ret = TRYRET_LOCALERR;
669         for (;;) {
670                 remoteerr = 0;
671                 ret = nfs_tryproto(nfsargsp, &saddr, hostp, spec, &errstr);
672                 if (ret == TRYRET_SUCCESS)
673                         break;
674                 if (ret != TRYRET_LOCALERR)
675                         remoteerr = 1;
676                 if ((opflags & ISBGRND) == 0)
677                         fprintf(stderr, "%s\n", errstr);
678
679                 /* Exit if all errors were local. */
680                 if (!remoteerr)
681                         exit(1);
682
683                 /*
684                  * If retrycnt == 0, we are to keep retrying forever.
685                  * Otherwise decrement it, and exit if it hits zero.
686                  */
687                 if (retrycnt != 0 && --retrycnt == 0)
688                         exit(1);
689
690                 if ((opflags & (BGRND | ISBGRND)) == BGRND) {
691                         warnx("Cannot immediately mount %s:%s, backgrounding",
692                             hostp, spec);
693                         opflags |= ISBGRND;
694                         if (daemon(0, 0) != 0)
695                                 err(1, "daemon");
696                 }
697                 sleep(60);
698         }
699         nfsargsp->hostname = nam;
700         /* Add mounted filesystem to PATH_MOUNTTAB */
701         if (!add_mtab(hostp, spec))
702                 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
703         return (1);
704 }
705
706 /*
707  * Try to set up the NFS arguments according to the address
708  * (and possibly port) specified by `sinp'.
709  *
710  * Returns TRYRET_SUCCESS if successful, or:
711  *   TRYRET_TIMEOUT             The server did not respond.
712  *   TRYRET_REMOTEERR           The server reported an error.
713  *   TRYRET_LOCALERR            Local failure.
714  *
715  * In all error cases, *errstr will be set to a statically-allocated string
716  * describing the error.
717  */
718 enum tryret
719 nfs_tryproto(struct nfs_args *nfsargsp, struct sockaddr_in *sinp, char *hostp,
720     char *spec, char **errstr)
721 {
722         static char errbuf[256];
723         struct sockaddr_in sin, tmpsin;
724         struct nfhret nfhret;
725         struct timeval try;
726         struct rpc_err rpcerr;
727         CLIENT *clp;
728         int doconnect, nfsvers, mntvers, so;
729         enum clnt_stat stat;
730         enum mountmode trymntmode;
731
732         trymntmode = mountmode;
733         errbuf[0] = '\0';
734         *errstr = errbuf;
735         sin = tmpsin = *sinp;
736
737 tryagain:
738         if (trymntmode == V2) {
739                 nfsvers = 2;
740                 mntvers = 1;
741         } else {
742                 nfsvers = 3;
743                 mntvers = 3;
744         }
745
746         /* Check that the server (nfsd) responds on the port we have chosen. */
747         try.tv_sec = 10;
748         try.tv_usec = 0;
749         so = RPC_ANYSOCK;
750         if (nfsargsp->sotype == SOCK_STREAM)
751                 clp = clnttcp_create(&sin, RPCPROG_NFS, nfsvers, &so, 0, 0);
752         else
753                 clp = clntudp_create(&sin, RPCPROG_NFS, nfsvers, try, &so);
754         if (clp == NULL) {
755                 snprintf(errbuf, sizeof errbuf, "%s:%s: %s",
756                     hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
757                 return (returncode(rpc_createerr.cf_stat,
758                     &rpc_createerr.cf_error));
759         }
760         if (nfsargsp->sotype == SOCK_DGRAM &&
761             !(nfsargsp->flags & NFSMNT_NOCONN)) {
762                 /*
763                  * Use connect(), to match what the kernel does. This
764                  * catches cases where the server responds from the
765                  * wrong source address.
766                  */
767                 doconnect = 1;
768                 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
769                         clnt_destroy(clp);
770                         snprintf(errbuf, sizeof errbuf,
771                             "%s:%s: CLSET_CONNECT failed", hostp, spec);
772                         return (TRYRET_LOCALERR);
773                 }
774         }
775
776         try.tv_sec = 10;
777         try.tv_usec = 0;
778         stat = clnt_call(clp, NFSPROC_NULL, xdr_void, NULL, xdr_void, NULL,
779             try);
780         if (stat != RPC_SUCCESS) {
781                 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
782                         clnt_destroy(clp);
783                         trymntmode = V2;
784                         goto tryagain;
785                 }
786                 clnt_geterr(clp, &rpcerr);
787                 snprintf(errbuf, sizeof errbuf, "%s:%s: %s",
788                     hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
789                 clnt_destroy(clp);
790                 return (returncode(stat, &rpcerr));
791         }
792         clnt_destroy(clp);
793
794         /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */
795         tmpsin.sin_port = 0;
796         try.tv_sec = 10;
797         try.tv_usec = 0;
798         so = RPC_ANYSOCK;
799         if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM)
800                 clp = clnttcp_create(&tmpsin, RPCPROG_MNT, mntvers, &so, 0, 0);
801         else
802                 clp = clntudp_create(&tmpsin, RPCPROG_MNT, mntvers, try, &so);
803         if (clp == NULL) {
804                 snprintf(errbuf, sizeof errbuf, "%s:%s: %s",
805                     hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
806                 return (returncode(rpc_createerr.cf_stat,
807                     &rpc_createerr.cf_error));
808         }
809         clp->cl_auth = authunix_create_default();
810         if (nfsargsp->flags & NFSMNT_KERB)
811                 nfhret.auth = RPCAUTH_KERB4;
812         else
813                 nfhret.auth = RPCAUTH_UNIX;
814         nfhret.vers = mntvers;
815         stat = clnt_call(clp, RPCMNT_MOUNT, xdr_dir, spec, xdr_fh, &nfhret,
816             try);
817         auth_destroy(clp->cl_auth);
818         if (stat != RPC_SUCCESS) {
819                 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
820                         clnt_destroy(clp);
821                         trymntmode = V2;
822                         goto tryagain;
823                 }
824                 clnt_geterr(clp, &rpcerr);
825                 snprintf(errbuf, sizeof errbuf, "%s:%s: %s",
826                     hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
827                 clnt_destroy(clp);
828                 return (returncode(stat, &rpcerr));
829         }
830         clnt_destroy(clp);
831
832         if (nfhret.stat != 0) {
833                 snprintf(errbuf, sizeof errbuf, "%s:%s: %s",
834                     hostp, spec, strerror(nfhret.stat));
835                 return (TRYRET_REMOTEERR);
836         }
837
838         /*
839          * Store the filehandle and server address in nfsargsp, making
840          * sure to copy any locally allocated structures.
841          */
842         nfsargsp->addrlen = sin.sin_len;
843         nfsargsp->addr = malloc(nfsargsp->addrlen);
844         nfsargsp->fhsize = nfhret.fhsize;
845         nfsargsp->fh = malloc(nfsargsp->fhsize);
846         if (nfsargsp->addr == NULL || nfsargsp->fh == NULL)
847                 err(1, "malloc");
848         bcopy(&sin, nfsargsp->addr, nfsargsp->addrlen);
849         bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize);
850
851         if (nfsvers == 3)
852                 nfsargsp->flags |= NFSMNT_NFSV3;
853         else
854                 nfsargsp->flags &= ~NFSMNT_NFSV3;
855
856         return (TRYRET_SUCCESS);
857 }
858
859 /*
860  * Catagorise a RPC return status and error into an `enum tryret'
861  * return code.
862  */
863 enum tryret
864 returncode(enum clnt_stat stat, struct rpc_err *rpcerr)
865 {
866         switch (stat) {
867         case RPC_TIMEDOUT:
868                 return (TRYRET_TIMEOUT);
869         case RPC_PMAPFAILURE:
870         case RPC_PROGNOTREGISTERED:
871         case RPC_PROGVERSMISMATCH:
872         /* XXX, these can be local or remote. */
873         case RPC_CANTSEND:
874         case RPC_CANTRECV:
875                 return (TRYRET_REMOTEERR);
876         case RPC_SYSTEMERROR:
877                 switch (rpcerr->re_errno) {
878                 case ETIMEDOUT:
879                         return (TRYRET_TIMEOUT);
880                 case ENOMEM:
881                         break;
882                 default:
883                         return (TRYRET_REMOTEERR);
884                 }
885                 /* FALLTHROUGH */
886         default:
887                 break;
888         }
889         return (TRYRET_LOCALERR);
890 }
891
892 /*
893  * xdr routines for mount rpc's
894  */
895 int
896 xdr_dir(xdrsp, dirp)
897         XDR *xdrsp;
898         char *dirp;
899 {
900         return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
901 }
902
903 int
904 xdr_fh(xdrsp, np)
905         XDR *xdrsp;
906         register struct nfhret *np;
907 {
908         register int i;
909         long auth, authcnt, authfnd = 0;
910
911         if (!xdr_u_long(xdrsp, &np->stat))
912                 return (0);
913         if (np->stat)
914                 return (1);
915         switch (np->vers) {
916         case 1:
917                 np->fhsize = NFSX_V2FH;
918                 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
919         case 3:
920                 if (!xdr_long(xdrsp, &np->fhsize))
921                         return (0);
922                 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
923                         return (0);
924                 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
925                         return (0);
926                 if (!xdr_long(xdrsp, &authcnt))
927                         return (0);
928                 for (i = 0; i < authcnt; i++) {
929                         if (!xdr_long(xdrsp, &auth))
930                                 return (0);
931                         if (auth == np->auth)
932                                 authfnd++;
933                 }
934                 /*
935                  * Some servers, such as DEC's OSF/1 return a nil authenticator
936                  * list to indicate RPCAUTH_UNIX.
937                  */
938                 if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
939                         np->stat = EAUTH;
940                 return (1);
941         };
942         return (0);
943 }
944
945 void
946 usage()
947 {
948         (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
949 "usage: mount_nfs [-23KNPTUbcdilqs] [-D deadthresh] [-I readdirsize]",
950 "                 [-L leaseterm] [-R retrycnt] [-a maxreadahead]",
951 "                 [-g maxgroups] [-m realm] [-o options] [-r readsize]",
952 "                 [-t timeout] [-w writesize] [-x retrans] rhost:path node");
953         exit(1);
954 }