Give the kernel a native NFS mount rpc capability for mounting NFS roots by
[dragonfly.git] / sys / vfs / nfs / nfs_mountrpc.c
1 /*
2  * Copyright (c) 1995 Gordon Ross, Adam Glass
3  * Copyright (c) 1992 Regents of the University of California.
4  * All rights reserved.
5  *
6  * This software was developed by the Computer Systems Engineering group
7  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
8  * contributed to Berkeley.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Lawrence Berkeley Laboratory and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * nfs/krpc_subr.c
39  * $NetBSD: krpc_subr.c,v 1.10 1995/08/08 20:43:43 gwr Exp $
40  * $FreeBSD: src/sys/nfs/bootp_subr.c,v 1.20.2.9 2003/04/24 16:51:08 ambrisko Exp $
41  * $DragonFly: src/sys/vfs/nfs/nfs_mountrpc.c,v 1.1 2005/09/04 01:29:00 dillon Exp $
42  */
43 /*
44  * Procedures used by NFS_ROOT and BOOTP to do an NFS mount rpc to obtain
45  * the nfs root file handle for a NFS-based root mount point.  This module 
46  * is not used by normal operating code because the 'mount' command has a
47  * far more sophisticated implementation.
48  */
49 #include "opt_bootp.h"
50 #include "opt_nfsroot.h"
51
52 #if defined(BOOTP) || defined(NFS_ROOT)
53
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/kernel.h>
57 #include <sys/sockio.h>
58 #include <sys/proc.h>
59 #include <sys/malloc.h>
60 #include <sys/mount.h>
61 #include <sys/mbuf.h>
62 #include <sys/socket.h>
63 #include <sys/socketvar.h>
64 #include <sys/sysctl.h>
65 #include <sys/uio.h>
66
67 #include <net/if.h>
68 #include <net/route.h>
69
70 #include <netinet/in.h>
71 #include <net/if_types.h>
72 #include <net/if_dl.h>
73
74 #include "rpcv2.h"
75 #include "nfsproto.h"
76 #include "nfs.h"
77 #include "nfsdiskless.h"
78 #include "krpc.h"
79 #include "xdr_subs.h"
80 #include "nfsmountrpc.h"
81
82 /*
83  * What is the longest we will wait before re-sending a request?
84  * Note this is also the frequency of "RPC timeout" messages.
85  * The re-send loop count sup linearly to this maximum, so the
86  * first complaint will happen after (1+2+3+4+5)=15 seconds.
87  */
88
89 extern struct nfsv3_diskless nfsv3_diskless;
90
91 static int getdec(char **ptr);
92 static char *substr(char *a,char *b);
93 static int xdr_opaque_decode(struct mbuf **ptr, u_char *buf, int len);
94 static int xdr_int_decode(struct mbuf **ptr, int *iptr);
95
96 void
97 mountopts(struct nfs_args *args, char *p)
98 {
99         char *tmp;
100         
101         args->version = NFS_ARGSVERSION;
102         args->rsize = 8192;
103         args->wsize = 8192;
104         args->flags = NFSMNT_RSIZE | NFSMNT_WSIZE | NFSMNT_RESVPORT;
105         args->sotype = SOCK_DGRAM;
106         if (p == NULL)
107                 return;
108         if ((tmp = (char *)substr(p, "rsize=")))
109                 args->rsize = getdec(&tmp);
110         if ((tmp = (char *)substr(p, "wsize=")))
111                 args->wsize = getdec(&tmp);
112         if ((tmp = (char *)substr(p, "intr")))
113                 args->flags |= NFSMNT_INT;
114         if ((tmp = (char *)substr(p, "soft")))
115                 args->flags |= NFSMNT_SOFT;
116         if ((tmp = (char *)substr(p, "noconn")))
117                 args->flags |= NFSMNT_NOCONN;
118         if ((tmp = (char *)substr(p, "tcp")))
119                 args->sotype = SOCK_STREAM;
120 }
121
122 /*
123  * RPC: mountd/mount
124  * Given a server pathname, get an NFS file handle.
125  * Also, sets sin->sin_port to the NFS service port.
126  */
127 int
128 md_mount(struct sockaddr_in *mdsin,             /* mountd server address */
129          char *path,
130          u_char *fhp,
131          int *fhsizep,
132          struct nfs_args *args,
133          struct thread *td)
134 {
135         struct mbuf *m;
136         int error;
137         int authunixok;
138         int authcount;
139         int authver;
140         
141 #ifdef BOOTP_NFSV3
142         /* First try NFS v3 */
143         /* Get port number for MOUNTD. */
144         error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER3,
145                              &mdsin->sin_port, td);
146         if (error == 0) {
147                 m = xdr_string_encode(path, strlen(path));
148                 
149                 /* Do RPC to mountd. */
150                 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER3,
151                                   RPCMNT_MOUNT, &m, NULL, td);
152         }
153         if (error == 0) {
154                 args->flags |= NFSMNT_NFSV3;
155         } else {
156 #endif
157                 /* Fallback to NFS v2 */
158                 
159                 /* Get port number for MOUNTD. */
160                 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
161                                      &mdsin->sin_port, td);
162                 if (error != 0)
163                         return error;
164                 
165                 m = xdr_string_encode(path, strlen(path));
166                 
167                 /* Do RPC to mountd. */
168                 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
169                                   RPCMNT_MOUNT, &m, NULL, td);
170                 if (error != 0)
171                         return error;   /* message already freed */
172                 
173 #ifdef BOOTP_NFSV3
174         }
175 #endif
176
177         if (xdr_int_decode(&m, &error) != 0 || error != 0)
178                 goto bad;
179         
180         if ((args->flags & NFSMNT_NFSV3) != 0) {
181                 if (xdr_int_decode(&m, fhsizep) != 0 ||
182                     *fhsizep > NFSX_V3FHMAX ||
183                     *fhsizep <= 0)
184                         goto bad;
185         } else
186                 *fhsizep = NFSX_V2FH;
187
188         if (xdr_opaque_decode(&m, fhp, *fhsizep) != 0)
189                 goto bad;
190
191         if (args->flags & NFSMNT_NFSV3) {
192                 if (xdr_int_decode(&m, &authcount) != 0)
193                         goto bad;
194                 authunixok = 0;
195                 if (authcount < 0 || authcount > 100)
196                         goto bad;
197                 while (authcount > 0) {
198                         if (xdr_int_decode(&m, &authver) != 0)
199                                 goto bad;
200                         if (authver == RPCAUTH_UNIX)
201                                 authunixok = 1;
202                         authcount--;
203                 }
204                 if (authunixok == 0)
205                         goto bad;
206         }
207           
208         /* Set port number for NFS use. */
209         error = krpc_portmap(mdsin, NFS_PROG,
210                              (args->flags &
211                               NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2,
212                              &mdsin->sin_port, td);
213         
214         goto out;
215         
216 bad:
217         error = EBADRPC;
218         
219 out:
220         m_freem(m);
221         return error;
222 }
223
224 int
225 md_lookup_swap(struct sockaddr_in *mdsin,       /* mountd server address */
226                char *path,
227                u_char *fhp,
228                int *fhsizep,
229                struct nfs_args *args,
230                struct thread *td)
231 {
232         struct mbuf *m;
233         int error;
234         int size = -1;
235         int attribs_present;
236         int status;
237         union {
238                 u_int32_t v2[17];
239                 u_int32_t v3[21];
240         } fattribs;
241         
242         m = m_get(MB_WAIT,MT_DATA);
243         if (m == NULL)
244                 return ENOBUFS;
245         
246         if ((args->flags & NFSMNT_NFSV3) != 0) {
247                 *mtod(m, u_int32_t *) = txdr_unsigned(*fhsizep);
248                 bcopy(fhp, mtod(m, u_char *) + sizeof(u_int32_t), *fhsizep);
249                 m->m_len = *fhsizep + sizeof(u_int32_t);
250         } else {
251                 bcopy(fhp, mtod(m, u_char *), NFSX_V2FH);
252                 m->m_len = NFSX_V2FH;
253         }
254         
255         m->m_next = xdr_string_encode(path, strlen(path));
256         if (m->m_next == NULL) {
257                 error = ENOBUFS;
258                 goto out;
259         }
260
261         /* Do RPC to nfsd. */
262         if ((args->flags & NFSMNT_NFSV3) != 0)
263                 error = krpc_call(mdsin, NFS_PROG, NFS_VER3,
264                                   NFSPROC_LOOKUP, &m, NULL, td);
265         else
266                 error = krpc_call(mdsin, NFS_PROG, NFS_VER2,
267                                   NFSV2PROC_LOOKUP, &m, NULL, td);
268         if (error != 0)
269                 return error;   /* message already freed */
270
271         if (xdr_int_decode(&m, &status) != 0)
272                 goto bad;
273         if (status != 0) {
274                 error = ENOENT;
275                 goto out;
276         }
277         
278         if ((args->flags & NFSMNT_NFSV3) != 0) {
279                 if (xdr_int_decode(&m, fhsizep) != 0 ||
280                     *fhsizep > NFSX_V3FHMAX ||
281                     *fhsizep <= 0)
282                         goto bad;
283         } else
284                 *fhsizep = NFSX_V2FH;
285         
286         if (xdr_opaque_decode(&m, fhp, *fhsizep) != 0)
287                 goto bad;
288         
289         if ((args->flags & NFSMNT_NFSV3) != 0) {
290                 if (xdr_int_decode(&m, &attribs_present) != 0)
291                         goto bad;
292                 if (attribs_present != 0) {
293                         if (xdr_opaque_decode(&m, (u_char *) &fattribs.v3,
294                                               sizeof(u_int32_t) * 21) != 0)
295                                 goto bad;
296                         size = fxdr_unsigned(u_int32_t, fattribs.v3[6]);
297                 }
298         } else {
299                 if (xdr_opaque_decode(&m,(u_char *) &fattribs.v2,
300                                       sizeof(u_int32_t) * 17) != 0)
301                         goto bad;
302                 size = fxdr_unsigned(u_int32_t, fattribs.v2[5]);
303         }
304           
305         if (nfsv3_diskless.swap_nblks == 0 && size != -1) {
306                 nfsv3_diskless.swap_nblks = size / 1024;
307                 printf("md_lookup_swap: Swap size is %d KB\n",
308                        nfsv3_diskless.swap_nblks);
309         }
310         
311         goto out;
312         
313 bad:
314         error = EBADRPC;
315         
316 out:
317         m_freem(m);
318         return error;
319 }
320
321 int
322 setfs(struct sockaddr_in *addr, char *path, char *p)
323 {
324         unsigned int ip;
325         int val;
326         
327         ip = 0;
328         if (((val = getdec(&p)) < 0) || (val > 255))
329                 return 0;
330         ip = val << 24;
331         if (*p != '.')
332                 return 0;
333         p++;
334         if (((val = getdec(&p)) < 0) || (val > 255))
335                 return 0;
336         ip |= (val << 16);
337         if (*p != '.')
338                 return 0;
339         p++;
340         if (((val = getdec(&p)) < 0) || (val > 255))
341                 return 0;
342         ip |= (val << 8);
343         if (*p != '.')
344                 return 0;
345         p++;
346         if (((val = getdec(&p)) < 0) || (val > 255))
347                 return 0;
348         ip |= val;
349         if (*p != ':')
350                 return 0;
351         p++;
352         
353         addr->sin_addr.s_addr = htonl(ip);
354         addr->sin_len = sizeof(struct sockaddr_in);
355         addr->sin_family = AF_INET;
356         
357         strncpy(path, p, MNAMELEN - 1);
358         return 1;
359 }
360
361 static int
362 getdec(char **ptr)
363 {
364         char *p;
365         int ret;
366
367         p = *ptr;
368         ret = 0;
369         if ((*p < '0') || (*p > '9'))
370                 return -1;
371         while ((*p >= '0') && (*p <= '9')) {
372                 ret = ret * 10 + (*p - '0');
373                 p++;
374         }
375         *ptr = p;
376         return ret;
377 }
378
379 static char *
380 substr(char *a, char *b)
381 {
382         char *loc1;
383         char *loc2;
384         
385         while (*a != '\0') {
386                 loc1 = a;
387                 loc2 = b;
388                 while (*loc1 == *loc2++) {
389                         if (*loc1 == '\0')
390                                 return 0;
391                         loc1++;
392                         if (*loc2 == '\0')
393                                 return loc1;
394                 }
395                 a++;
396         }
397         return 0;
398 }
399
400 static int
401 xdr_opaque_decode(struct mbuf **mptr, u_char *buf, int len)
402 {
403         struct mbuf *m;
404         int alignedlen;
405         
406         m = *mptr;
407         alignedlen = ( len + 3 ) & ~3;
408         
409         if (m->m_len < alignedlen) {
410                 m = m_pullup(m, alignedlen);
411                 if (m == NULL) {
412                         *mptr = NULL;
413                         return EBADRPC;
414                 }
415         }
416         bcopy(mtod(m, u_char *), buf, len);
417         m_adj(m, alignedlen);
418         *mptr = m;
419         return 0;
420 }
421
422 static int
423 xdr_int_decode(struct mbuf **mptr, int *iptr)
424 {
425         u_int32_t i;
426         if (xdr_opaque_decode(mptr, (u_char *) &i, sizeof(u_int32_t)) != 0)
427                 return EBADRPC;
428         *iptr = fxdr_unsigned(u_int32_t, i);
429         return 0;
430 }
431
432 #endif  /* BOOTP && NFS_ROOT */
433