]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_boot.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_boot.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* Copyright (c) 1995, 1997 NeXT Computer, Inc. All Rights Reserved */
23 /*
24 * Copyright (c) 1994 Adam Glass, Gordon Ross
25 * All rights reserved.
26 *
27 * This software was developed by the Computer Systems Engineering group
28 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
29 * contributed to Berkeley.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Lawrence Berkeley Laboratory and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * SUCH DAMAGE.
58 *
59 * History:
60 * 14-March-97 Dieter Siegmund (dieter@next.com)
61 * - Use BOOTP instead of RARP to get the IP address at boot time
62 *
63 * 23-May-97 Umesh Vaishampayan (umeshv@apple.com)
64 * - Added the ability to mount "/private" separately.
65 *
66 * 30-May-97 Dieter Siegmund (dieter@next.com)
67 * - Clear out the ireq structure before using it to prevent
68 * our sending using a bogus source IP address, we should use
69 * an IP address of all zeroes
70 * - Right after BOOTP, get the correct netmask using AUTONETMASK
71 * 18-Jul-97 Dieter Siegmund (dieter@apple.com)
72 * - we can't restrict the netmask until we have a default route,
73 * removed AUTONETMASK call (ifdef'd out)
74 * 5-Aug-97 Dieter Siegmund (dieter@apple.com)
75 * - use the default route from the bpwhoami call, enabled autonetmask
76 * again
77 * 19-Feb-1999 Dieter Siegmund (dieter@apple.com)
78 * - use new BOOTP routine to get the subnet mask and router
79 * and stop using SIOCAUTOADDR
80 * - don't bother mounting private separately if it's not
81 * specified or not required because they are substrings of
82 * one another ie. root=host:/A and private=host:/A/private
83 * - allow the root path to be specified in the boot variable
84 * "rp" (AKA "rootpath")
85 * 19-Jul-1999 Dieter Siegmund (dieter@apple.com)
86 * - replaced big automatic arrays with MALLOC'd data
87 */
88
89 #include <sys/param.h>
90 #include <sys/systm.h>
91 #include <sys/kernel.h>
92 #include <sys/conf.h>
93 #include <sys/ioctl.h>
94 #include <sys/proc.h>
95 #include <sys/mount.h>
96 #include <sys/mbuf.h>
97
98 #include <sys/malloc.h>
99 #include <sys/socket.h>
100 #include <sys/reboot.h>
101
102 #include <net/if.h>
103 #include <net/if_dl.h>
104 #include <net/if_types.h>
105 #include <net/route.h>
106
107 #include <netinet/in.h>
108 #include <netinet/if_ether.h>
109
110 #include <nfs/rpcv2.h>
111 #include <nfs/nfsproto.h>
112 #include <nfs/nfs.h>
113 #include <nfs/nfsdiskless.h>
114 #include <nfs/krpc.h>
115
116 #include <pexpert/pexpert.h>
117
118 #include "ether.h"
119
120 #include <libkern/libkern.h>
121
122 extern char *strchr(const char *str, int ch);
123
124 #if NETHER == 0
125
126 int nfs_boot_init(nd, procp)
127 struct nfs_diskless *nd;
128 struct proc *procp;
129 {
130 panic("nfs_boot_init: no ether");
131 }
132
133 #else /* NETHER */
134
135 /*
136 * Support for NFS diskless booting, specifically getting information
137 * about where to boot from, what pathnames, etc.
138 *
139 * This implememtation uses RARP and the bootparam RPC.
140 * We are forced to implement RPC anyway (to get file handles)
141 * so we might as well take advantage of it for bootparam too.
142 *
143 * The diskless boot sequence goes as follows:
144 * (1) Use RARP to get our interface address
145 * (2) Use RPC/bootparam/whoami to get our hostname,
146 * our IP address, and the server's IP address.
147 * (3) Use RPC/bootparam/getfile to get the root path
148 * (4) Use RPC/mountd to get the root file handle
149 * (5) Use RPC/bootparam/getfile to get the swap path
150 * (6) Use RPC/mountd to get the swap file handle
151 *
152 * (This happens to be the way Sun does it too.)
153 */
154
155 /* bootparam RPC */
156 static int bp_whoami __P((struct sockaddr_in *bpsin,
157 struct in_addr *my_ip, struct in_addr *gw_ip));
158 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key,
159 struct sockaddr_in *mdsin, char *servname, char *path));
160
161 /* mountd RPC */
162 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
163 u_char *fh));
164
165 /* other helpers */
166 static int get_file_handle __P((char *pathname, struct nfs_dlmount *ndmntp));
167
168
169 #define IP_FORMAT "%d.%d.%d.%d"
170 #define IP_CH(ip) ((u_char *)ip)
171 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
172
173 extern boolean_t
174 netboot_iaddr(struct in_addr * iaddr_p);
175
176 extern boolean_t
177 netboot_rootpath(struct in_addr * server_ip,
178 char * name, int name_len,
179 char * path, int path_len);
180
181 /*
182 * Called with an empty nfs_diskless struct to be filled in.
183 */
184 int
185 nfs_boot_init(nd, procp)
186 struct nfs_diskless *nd;
187 struct proc *procp;
188 {
189 struct sockaddr_in bp_sin;
190 boolean_t do_bpwhoami = TRUE;
191 boolean_t do_bpgetfile = TRUE;
192 int error = 0;
193 struct in_addr my_ip;
194 char * root_path = NULL;
195 struct sockaddr_in * sin_p;
196
197 /* by this point, networking must already have been configured */
198 if (netboot_iaddr(&my_ip) == FALSE) {
199 printf("nfs_boot: networking is not initialized\n");
200 error = ENXIO;
201 goto failed;
202 }
203
204 /* get the root path information */
205 MALLOC(root_path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
206 sin_p = &nd->nd_root.ndm_saddr;
207 bzero((caddr_t)sin_p, sizeof(*sin_p));
208 sin_p->sin_len = sizeof(*sin_p);
209 sin_p->sin_family = AF_INET;
210 if (netboot_rootpath(&sin_p->sin_addr, nd->nd_root.ndm_host,
211 sizeof(nd->nd_root.ndm_host),
212 root_path, MAXPATHLEN) == TRUE) {
213 do_bpgetfile = FALSE;
214 do_bpwhoami = FALSE;
215 }
216 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
217
218 thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
219
220 if (do_bpwhoami) {
221 struct in_addr router;
222 /*
223 * Get client name and gateway address.
224 * RPC: bootparam/whoami
225 * Use the old broadcast address for the WHOAMI
226 * call because we do not yet know our netmask.
227 * The server address returned by the WHOAMI call
228 * is used for all subsequent booptaram RPCs.
229 */
230 bzero((caddr_t)&bp_sin, sizeof(bp_sin));
231 bp_sin.sin_len = sizeof(bp_sin);
232 bp_sin.sin_family = AF_INET;
233 bp_sin.sin_addr.s_addr = INADDR_BROADCAST;
234 hostnamelen = MAXHOSTNAMELEN;
235 router.s_addr = 0;
236 error = bp_whoami(&bp_sin, &my_ip, &router);
237 if (error) {
238 printf("nfs_boot: bootparam whoami, error=%d", error);
239 goto failed;
240 }
241 printf("nfs_boot: BOOTPARAMS server " IP_FORMAT "\n",
242 IP_LIST(&bp_sin.sin_addr));
243 printf("nfs_boot: hostname %s\n", hostname);
244 }
245 if (do_bpgetfile) {
246 error = bp_getfile(&bp_sin, "root", &nd->nd_root.ndm_saddr,
247 nd->nd_root.ndm_host, root_path);
248 if (error) {
249 printf("nfs_boot: bootparam get root: %d\n", error);
250 goto failed;
251 }
252 }
253
254 error = get_file_handle(root_path, &nd->nd_root);
255 if (error) {
256 printf("nfs_boot: get_file_handle() root failed, %d\n", error);
257 goto failed;
258 }
259
260 #if !defined(NO_MOUNT_PRIVATE)
261 if (do_bpgetfile) { /* get private path */
262 char * private_path = NULL;
263
264 MALLOC(private_path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
265 error = bp_getfile(&bp_sin, "private",
266 &nd->nd_private.ndm_saddr,
267 nd->nd_private.ndm_host, private_path);
268 if (!error) {
269 char * check_path = NULL;
270
271 MALLOC(check_path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
272 sprintf(check_path, "%s/private", root_path);
273 if ((nd->nd_root.ndm_saddr.sin_addr.s_addr
274 == nd->nd_private.ndm_saddr.sin_addr.s_addr)
275 && (strcmp(check_path, private_path) == 0)) {
276 /* private path is prefix of root path, don't mount */
277 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
278 }
279 else {
280 error = get_file_handle(private_path,
281 &nd->nd_private);
282 if (error) {
283 printf("nfs_boot: get_file_handle() private failed, %d\n", error);
284 goto failed;
285 }
286 }
287 _FREE(check_path, M_TEMP);
288 }
289 else {
290 /* private key not defined, don't mount */
291 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
292 }
293 _FREE(private_path, M_TEMP);
294 }
295 else {
296 error = 0;
297 }
298 #endif NO_MOUNT_PRIVATE
299 failed:
300 thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
301 _FREE(root_path, M_TEMP);
302 return (error);
303 }
304
305 static int
306 get_file_handle(pathname, ndmntp)
307 char *pathname; /* path on server */
308 struct nfs_dlmount *ndmntp; /* output */
309 {
310 char *sp, *dp, *endp;
311 int error;
312
313 /*
314 * Get file handle for "key" (root or swap)
315 * using RPC to mountd/mount
316 */
317 error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh);
318 if (error)
319 return (error);
320
321 /* Construct remote path (for getmntinfo(3)) */
322 dp = ndmntp->ndm_host;
323 endp = dp + MNAMELEN - 1;
324 dp += strlen(dp);
325 *dp++ = ':';
326 for (sp = pathname; *sp && dp < endp;)
327 *dp++ = *sp++;
328 *dp = '\0';
329 return (0);
330
331 }
332
333
334 /*
335 * Get an mbuf with the given length, and
336 * initialize the pkthdr length field.
337 */
338 static struct mbuf *
339 m_get_len(int msg_len)
340 {
341 struct mbuf *m;
342 m = m_gethdr(M_WAIT, MT_DATA);
343 if (m == NULL)
344 return NULL;
345 if (msg_len > MHLEN) {
346 if (msg_len > MCLBYTES)
347 panic("nfs_boot: msg_len > MCLBYTES");
348 MCLGET(m, M_WAIT);
349 if (m == NULL)
350 return NULL;
351 }
352 m->m_len = msg_len;
353 m->m_pkthdr.len = m->m_len;
354 return (m);
355 }
356
357
358 /*
359 * String representation for RPC.
360 */
361 struct rpc_string {
362 u_long len; /* length without null or padding */
363 u_char data[4]; /* data (longer, of course) */
364 /* data is padded to a long-word boundary */
365 };
366 /* Compute space used given string length. */
367 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
368
369 /*
370 * Inet address in RPC messages
371 * (Note, really four longs, NOT chars. Blech.)
372 */
373 struct bp_inaddr {
374 u_long atype;
375 long addr[4];
376 };
377
378
379 /*
380 * RPC: bootparam/whoami
381 * Given client IP address, get:
382 * client name (hostname)
383 * domain name (domainname)
384 * gateway address
385 *
386 * The hostname and domainname are set here for convenience.
387 *
388 * Note - bpsin is initialized to the broadcast address,
389 * and will be replaced with the bootparam server address
390 * after this call is complete. Have to use PMAP_PROC_CALL
391 * to make sure we get responses only from a servers that
392 * know about us (don't want to broadcast a getport call).
393 */
394 static int
395 bp_whoami(bpsin, my_ip, gw_ip)
396 struct sockaddr_in *bpsin;
397 struct in_addr *my_ip;
398 struct in_addr *gw_ip;
399 {
400 /* RPC structures for PMAPPROC_CALLIT */
401 struct whoami_call {
402 u_long call_prog;
403 u_long call_vers;
404 u_long call_proc;
405 u_long call_arglen;
406 struct bp_inaddr call_ia;
407 } *call;
408
409 struct rpc_string *str;
410 struct bp_inaddr *bia;
411 struct mbuf *m;
412 struct sockaddr_in *sin;
413 int error, msg_len;
414 int cn_len, dn_len;
415 u_char *p;
416 long *lp;
417
418 /*
419 * Get message buffer of sufficient size.
420 */
421 msg_len = sizeof(*call);
422 m = m_get_len(msg_len);
423 if (m == NULL)
424 return ENOBUFS;
425
426 /*
427 * Build request message for PMAPPROC_CALLIT.
428 */
429 call = mtod(m, struct whoami_call *);
430 call->call_prog = htonl(BOOTPARAM_PROG);
431 call->call_vers = htonl(BOOTPARAM_VERS);
432 call->call_proc = htonl(BOOTPARAM_WHOAMI);
433 call->call_arglen = htonl(sizeof(struct bp_inaddr));
434
435 /* client IP address */
436 call->call_ia.atype = htonl(1);
437 p = (u_char*)my_ip;
438 lp = call->call_ia.addr;
439 *lp++ = htonl(*p); p++;
440 *lp++ = htonl(*p); p++;
441 *lp++ = htonl(*p); p++;
442 *lp++ = htonl(*p); p++;
443
444 /* RPC: portmap/callit */
445 bpsin->sin_port = htons(PMAPPORT);
446
447 error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
448 PMAPPROC_CALLIT, &m, &sin);
449 if (error)
450 return error;
451
452 /*
453 * Parse result message.
454 */
455 msg_len = m->m_len;
456 lp = mtod(m, long *);
457
458 /* bootparam server port (also grab from address). */
459 if (msg_len < sizeof(*lp))
460 goto bad;
461 msg_len -= sizeof(*lp);
462 bpsin->sin_port = htons((short)ntohl(*lp++));
463 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
464
465 /* length of encapsulated results */
466 if (msg_len < (ntohl(*lp) + sizeof(*lp)))
467 goto bad;
468 msg_len = ntohl(*lp++);
469 p = (char*)lp;
470
471 /* client name */
472 if (msg_len < sizeof(*str))
473 goto bad;
474 str = (struct rpc_string *)p;
475 cn_len = ntohl(str->len);
476 if (msg_len < cn_len)
477 goto bad;
478 if (cn_len >= MAXHOSTNAMELEN)
479 goto bad;
480 bcopy(str->data, hostname, cn_len);
481 hostname[cn_len] = '\0';
482 hostnamelen = cn_len;
483 p += RPC_STR_SIZE(cn_len);
484 msg_len -= RPC_STR_SIZE(cn_len);
485
486 /* domain name */
487 if (msg_len < sizeof(*str))
488 goto bad;
489 str = (struct rpc_string *)p;
490 dn_len = ntohl(str->len);
491 if (msg_len < dn_len)
492 goto bad;
493 if (dn_len >= MAXHOSTNAMELEN)
494 goto bad;
495 bcopy(str->data, domainname, dn_len);
496 domainname[dn_len] = '\0';
497 domainnamelen = dn_len;
498 p += RPC_STR_SIZE(dn_len);
499 msg_len -= RPC_STR_SIZE(dn_len);
500
501 /* gateway address */
502 if (msg_len < sizeof(*bia))
503 goto bad;
504 bia = (struct bp_inaddr *)p;
505 if (bia->atype != htonl(1))
506 goto bad;
507 p = (u_char*)gw_ip;
508 *p++ = ntohl(bia->addr[0]);
509 *p++ = ntohl(bia->addr[1]);
510 *p++ = ntohl(bia->addr[2]);
511 *p++ = ntohl(bia->addr[3]);
512 goto out;
513
514 bad:
515 printf("nfs_boot: bootparam_whoami: bad reply\n");
516 error = EBADRPC;
517
518 out:
519 if (sin)
520 FREE(sin, M_SONAME);
521
522 m_freem(m);
523 return(error);
524 }
525
526
527 /*
528 * RPC: bootparam/getfile
529 * Given client name and file "key", get:
530 * server name
531 * server IP address
532 * server pathname
533 */
534 static int
535 bp_getfile(bpsin, key, md_sin, serv_name, pathname)
536 struct sockaddr_in *bpsin;
537 char *key;
538 struct sockaddr_in *md_sin;
539 char *serv_name;
540 char *pathname;
541 {
542 struct rpc_string *str;
543 struct mbuf *m;
544 struct bp_inaddr *bia;
545 struct sockaddr_in *sin;
546 u_char *p, *q;
547 int error, msg_len;
548 int cn_len, key_len, sn_len, path_len;
549
550 /*
551 * Get message buffer of sufficient size.
552 */
553 cn_len = hostnamelen;
554 key_len = strlen(key);
555 msg_len = 0;
556 msg_len += RPC_STR_SIZE(cn_len);
557 msg_len += RPC_STR_SIZE(key_len);
558 m = m_get_len(msg_len);
559 if (m == NULL)
560 return ENOBUFS;
561
562 /*
563 * Build request message.
564 */
565 p = mtod(m, u_char *);
566 bzero(p, msg_len);
567 /* client name (hostname) */
568 str = (struct rpc_string *)p;
569 str->len = htonl(cn_len);
570 bcopy(hostname, str->data, cn_len);
571 p += RPC_STR_SIZE(cn_len);
572 /* key name (root or swap) */
573 str = (struct rpc_string *)p;
574 str->len = htonl(key_len);
575 bcopy(key, str->data, key_len);
576
577 /* RPC: bootparam/getfile */
578 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
579 BOOTPARAM_GETFILE, &m, NULL);
580 if (error)
581 return error;
582
583 /*
584 * Parse result message.
585 */
586 p = mtod(m, u_char *);
587 msg_len = m->m_len;
588
589 /* server name */
590 if (msg_len < sizeof(*str))
591 goto bad;
592 str = (struct rpc_string *)p;
593 sn_len = ntohl(str->len);
594 if (msg_len < sn_len)
595 goto bad;
596 if (sn_len >= MNAMELEN)
597 goto bad;
598 bcopy(str->data, serv_name, sn_len);
599 serv_name[sn_len] = '\0';
600 p += RPC_STR_SIZE(sn_len);
601 msg_len -= RPC_STR_SIZE(sn_len);
602
603 /* server IP address (mountd) */
604 if (msg_len < sizeof(*bia))
605 goto bad;
606 bia = (struct bp_inaddr *)p;
607 if (bia->atype != htonl(1))
608 goto bad;
609 sin = md_sin;
610 bzero((caddr_t)sin, sizeof(*sin));
611 sin->sin_len = sizeof(*sin);
612 sin->sin_family = AF_INET;
613 q = (u_char*) &sin->sin_addr;
614 *q++ = ntohl(bia->addr[0]);
615 *q++ = ntohl(bia->addr[1]);
616 *q++ = ntohl(bia->addr[2]);
617 *q++ = ntohl(bia->addr[3]);
618 p += sizeof(*bia);
619 msg_len -= sizeof(*bia);
620
621 /* server pathname */
622 if (msg_len < sizeof(*str))
623 goto bad;
624 str = (struct rpc_string *)p;
625 path_len = ntohl(str->len);
626 if (msg_len < path_len)
627 goto bad;
628 if (path_len >= MAXPATHLEN)
629 goto bad;
630 bcopy(str->data, pathname, path_len);
631 pathname[path_len] = '\0';
632 goto out;
633
634 bad:
635 printf("nfs_boot: bootparam_getfile: bad reply\n");
636 error = EBADRPC;
637
638 out:
639 m_freem(m);
640 return(0);
641 }
642
643
644 /*
645 * RPC: mountd/mount
646 * Given a server pathname, get an NFS file handle.
647 * Also, sets sin->sin_port to the NFS service port.
648 */
649 static int
650 md_mount(mdsin, path, fhp)
651 struct sockaddr_in *mdsin; /* mountd server address */
652 char *path;
653 u_char *fhp;
654 {
655 /* The RPC structures */
656 struct rpc_string *str;
657 struct rdata {
658 u_long errno;
659 u_char fh[NFSX_V2FH];
660 } *rdata;
661 struct mbuf *m;
662 int error, mlen, slen;
663
664 /* Get port number for MOUNTD. */
665 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
666 &mdsin->sin_port);
667 if (error) return error;
668
669 slen = strlen(path);
670 mlen = RPC_STR_SIZE(slen);
671
672 m = m_get_len(mlen);
673 if (m == NULL)
674 return ENOBUFS;
675 str = mtod(m, struct rpc_string *);
676 str->len = htonl(slen);
677 bcopy(path, str->data, slen);
678
679 /* Do RPC to mountd. */
680 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
681 RPCMNT_MOUNT, &m, NULL);
682 if (error)
683 return error; /* message already freed */
684
685 mlen = m->m_len;
686 if (mlen < sizeof(*rdata))
687 goto bad;
688 rdata = mtod(m, struct rdata *);
689 error = ntohl(rdata->errno);
690 if (error)
691 goto bad;
692 bcopy(rdata->fh, fhp, NFSX_V2FH);
693
694 /* Set port number for NFS use. */
695 error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2,
696 &mdsin->sin_port);
697 goto out;
698
699 bad:
700 error = EBADRPC;
701
702 out:
703 m_freem(m);
704 return error;
705 }
706
707 #endif /* NETHER */