xref: /xnu-8020.121.3/bsd/nfs/nfs_boot.c (revision fdd8201d7b966f0c3ea610489d29bd841d358941)
1 /*
2  * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1995, 1997 NeXT Computer, Inc. All Rights Reserved */
29 /*
30  * Copyright (c) 1994 Adam Glass, Gordon Ross
31  * All rights reserved.
32  *
33  * This software was developed by the Computer Systems Engineering group
34  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
35  * contributed to Berkeley.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. All advertising materials mentioning features or use of this software
46  *    must display the following acknowledgement:
47  *      This product includes software developed by the University of
48  *      California, Lawrence Berkeley Laboratory and its contributors.
49  * 4. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  *
65  *  History:
66  *  14-March-97	Dieter Siegmund ([email protected])
67  *	- Use BOOTP instead of RARP to get the IP address at boot time
68  *
69  *  23-May-97  Umesh Vaishampayan  ([email protected])
70  *	- Added the ability to mount "/private" separately.
71  *
72  *  30-May-97	Dieter Siegmund	([email protected])
73  *	- Clear out the ireq structure before using it to prevent
74  *	  our sending using a bogus source IP address, we should use
75  *	  an IP address of all zeroes
76  *	- Right after BOOTP, get the correct netmask using AUTONETMASK
77  *  18-Jul-97	Dieter Siegmund	([email protected])
78  *	- we can't restrict the netmask until we have a default route,
79  *	  removed AUTONETMASK call (ifdef'd out)
80  *  5-Aug-97	Dieter Siegmund ([email protected])
81  *	- use the default route from the bpwhoami call, enabled autonetmask
82  *	  again
83  *  19-Feb-1999	Dieter Siegmund ([email protected])
84  *	- use new BOOTP routine to get the subnet mask and router
85  *        and stop using SIOCAUTOADDR
86  *      - don't bother mounting private separately if it's not
87  *        specified or not required because they are substrings of
88  *        one another ie. root=host:/A and private=host:/A/private
89  *      - allow the root path to be specified in the boot variable
90  *	  "rp" (AKA "rootpath")
91  *  19-Jul-1999 Dieter Siegmund ([email protected])
92  *	- replaced big automatic arrays with MALLOC'd data
93  */
94 
95 #include <nfs/nfs_conf.h>
96 #if CONFIG_NFS_CLIENT
97 
98 #include <sys/param.h>
99 #include <sys/systm.h>
100 #include <sys/kernel.h>
101 #include <sys/conf.h>
102 #include <sys/ioctl.h>
103 #include <sys/proc.h>
104 #include <sys/mount_internal.h>
105 #include <sys/vnode_internal.h>
106 #include <sys/kpi_mbuf.h>
107 
108 #include <sys/malloc.h>
109 #include <sys/socket.h>
110 
111 #include <net/if.h>
112 #include <net/if_dl.h>
113 #include <net/if_types.h>
114 #include <net/route.h>
115 
116 #include <netinet/in.h>
117 #include <netinet/if_ether.h>
118 
119 #include <nfs/rpcv2.h>
120 #include <nfs/nfsproto.h>
121 #include <nfs/nfs.h>
122 #include <nfs/nfsdiskless.h>
123 #include <nfs/krpc.h>
124 #include <nfs/xdr_subs.h>
125 
126 #if CONFIG_MACF
127 #include <security/mac_framework.h>
128 #endif
129 
130 #include <pexpert/pexpert.h>
131 
132 #include "ether.h"
133 
134 #include <libkern/libkern.h>
135 
136 #if CONFIG_NETBOOT
137 static int      nfs_mount_diskless(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *, vfs_context_t);
138 #if !defined(NO_MOUNT_PRIVATE)
139 static int      nfs_mount_diskless_private(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *, vfs_context_t);
140 #endif /* NO_MOUNT_PRIVATE */
141 #endif
142 
143 #if NETHER == 0
144 
145 int
nfs_boot_init(__unused struct nfs_diskless * nd)146 nfs_boot_init(__unused struct nfs_diskless *nd)
147 {
148 	panic("nfs_boot_init: no ether");
149 }
150 
151 int
nfs_boot_getfh(__unused struct nfs_diskless * nd,__unused int v3,__unused int sotype)152 nfs_boot_getfh(__unused struct nfs_diskless *nd, __unused int v3, __unused int sotype)
153 {
154 	panic("nfs_boot_getfh: no ether");
155 }
156 
157 #else /* NETHER */
158 
159 /*
160  * Support for NFS diskless booting, specifically getting information
161  * about where to boot from, what pathnames, etc.
162  *
163  * This implememtation uses RARP and the bootparam RPC.
164  * We are forced to implement RPC anyway (to get file handles)
165  * so we might as well take advantage of it for bootparam too.
166  *
167  * The diskless boot sequence goes as follows:
168  * (1) Use RARP to get our interface address
169  * (2) Use RPC/bootparam/whoami to get our hostname,
170  *     our IP address, and the server's IP address.
171  * (3) Use RPC/bootparam/getfile to get the root path
172  * (4) Use RPC/mountd to get the root file handle
173  * (5) Use RPC/bootparam/getfile to get the swap path
174  * (6) Use RPC/mountd to get the swap file handle
175  *
176  * (This happens to be the way Sun does it too.)
177  */
178 
179 /* bootparam RPC */
180 static int bp_whoami(struct sockaddr_in *bpsin,
181     struct in_addr *my_ip, struct in_addr *gw_ip);
182 static int bp_getfile(struct sockaddr_in *bpsin, const char *key,
183     struct sockaddr_in *mdsin, char *servname, char *path);
184 
185 /* mountd RPC */
186 static int md_mount(struct sockaddr_in *mdsin, char *path, int v3, int sotype,
187     u_char *fhp, u_int32_t *fhlenp);
188 
189 /* other helpers */
190 static int get_file_handle(struct nfs_dlmount *ndmntp);
191 
192 
193 #define IP_FORMAT       "%d.%d.%d.%d"
194 #define IP_CH(ip)       ((u_char *)ip)
195 #define IP_LIST(ip)     IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
196 
197 #include <sys/netboot.h>
198 
199 /*
200  * Called with an empty nfs_diskless struct to be filled in.
201  */
202 int
nfs_boot_init(struct nfs_diskless * nd)203 nfs_boot_init(struct nfs_diskless *nd)
204 {
205 	struct sockaddr_in      bp_sin;
206 	boolean_t               do_bpwhoami = TRUE;
207 	boolean_t               do_bpgetfile = TRUE;
208 	int                     error = 0;
209 	struct in_addr          my_ip;
210 	struct sockaddr_in *    sin_p;
211 
212 	/* make sure mbuf constants are set up */
213 	if (!nfs_mbuf_mhlen) {
214 		nfs_mbuf_init();
215 	}
216 
217 	/* by this point, networking must already have been configured */
218 	if (netboot_iaddr(&my_ip) == FALSE) {
219 		printf("nfs_boot: networking is not initialized\n");
220 		error = ENXIO;
221 		goto failed;
222 	}
223 
224 	/* get the root path information */
225 	nd->nd_root.ndm_path = zalloc(ZV_NAMEI);
226 	nd->nd_root.ndm_mntfrom = zalloc(ZV_NAMEI);
227 
228 	sin_p = &nd->nd_root.ndm_saddr;
229 	bzero((caddr_t)sin_p, sizeof(*sin_p));
230 	sin_p->sin_len = sizeof(*sin_p);
231 	sin_p->sin_family = AF_INET;
232 	if (netboot_rootpath(&sin_p->sin_addr, nd->nd_root.ndm_host,
233 	    sizeof(nd->nd_root.ndm_host),
234 	    nd->nd_root.ndm_path, MAXPATHLEN) == TRUE) {
235 		do_bpgetfile = FALSE;
236 		do_bpwhoami = FALSE;
237 	}
238 	nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
239 
240 	if (do_bpwhoami) {
241 		struct in_addr router;
242 		/*
243 		 * Get client name and gateway address.
244 		 * RPC: bootparam/whoami
245 		 * Use the old broadcast address for the WHOAMI
246 		 * call because we do not yet know our netmask.
247 		 * The server address returned by the WHOAMI call
248 		 * is used for all subsequent booptaram RPCs.
249 		 */
250 		bzero((caddr_t)&bp_sin, sizeof(bp_sin));
251 		bp_sin.sin_len = sizeof(bp_sin);
252 		bp_sin.sin_family = AF_INET;
253 		bp_sin.sin_addr.s_addr = INADDR_BROADCAST;
254 		router.s_addr = 0;
255 		error = bp_whoami(&bp_sin, &my_ip, &router);
256 		if (error) {
257 			printf("nfs_boot: bootparam whoami, error=%d", error);
258 			goto failed;
259 		}
260 		printf("nfs_boot: BOOTPARAMS server " IP_FORMAT "\n",
261 		    IP_LIST(&bp_sin.sin_addr));
262 		lck_mtx_lock(&hostname_lock);
263 		printf("nfs_boot: hostname %s\n", hostname);
264 		lck_mtx_unlock(&hostname_lock);
265 	}
266 	if (do_bpgetfile) {
267 		error = bp_getfile(&bp_sin, "root", &nd->nd_root.ndm_saddr,
268 		    nd->nd_root.ndm_host, nd->nd_root.ndm_path);
269 		if (error) {
270 			printf("nfs_boot: bootparam get root: %d\n", error);
271 			goto failed;
272 		}
273 	}
274 
275 #if !defined(NO_MOUNT_PRIVATE)
276 	if (do_bpgetfile) { /* get private path */
277 		nd->nd_private.ndm_path = zalloc(ZV_NAMEI);
278 		nd->nd_private.ndm_mntfrom = zalloc(ZV_NAMEI);
279 		error = bp_getfile(&bp_sin, "private",
280 		    &nd->nd_private.ndm_saddr,
281 		    nd->nd_private.ndm_host,
282 		    nd->nd_private.ndm_path);
283 		if (!error) {
284 			char * check_path = NULL;
285 
286 			check_path = zalloc(ZV_NAMEI);
287 			snprintf(check_path, MAXPATHLEN, "%s/private", nd->nd_root.ndm_path);
288 			if ((nd->nd_root.ndm_saddr.sin_addr.s_addr
289 			    == nd->nd_private.ndm_saddr.sin_addr.s_addr)
290 			    && (strncmp(check_path, nd->nd_private.ndm_path, MAXPATHLEN) == 0)) {
291 				/* private path is prefix of root path, don't mount */
292 				nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
293 			}
294 			NFS_ZFREE(ZV_NAMEI, check_path);
295 		} else {
296 			/* private key not defined, don't mount */
297 			nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
298 		}
299 	} else {
300 		error = 0;
301 	}
302 #endif /* NO_MOUNT_PRIVATE */
303 failed:
304 	return error;
305 }
306 
307 /*
308  * Called with a partially initialized nfs_diskless struct
309  * with file handles to be filled in.
310  */
311 int
nfs_boot_getfh(struct nfs_diskless * nd,int v3,int sotype)312 nfs_boot_getfh(struct nfs_diskless *nd, int v3, int sotype)
313 {
314 	int error = 0;
315 
316 	nd->nd_root.ndm_nfsv3 = v3;
317 	nd->nd_root.ndm_sotype = sotype;
318 	error = get_file_handle(&nd->nd_root);
319 	if (error) {
320 		printf("nfs_boot: get_file_handle(v%d) root failed, %d\n",
321 		    v3 ? 3 : 2, error);
322 		goto failed;
323 	}
324 
325 #if !defined(NO_MOUNT_PRIVATE)
326 	if (nd->nd_private.ndm_saddr.sin_addr.s_addr) {
327 		/* get private file handle */
328 		nd->nd_private.ndm_nfsv3 = v3;
329 		nd->nd_private.ndm_sotype = sotype;
330 		error = get_file_handle(&nd->nd_private);
331 		if (error) {
332 			printf("nfs_boot: get_file_handle(v%d) private failed, %d\n",
333 			    v3 ? 3 : 2, error);
334 			goto failed;
335 		}
336 	}
337 #endif /* NO_MOUNT_PRIVATE */
338 failed:
339 	return error;
340 }
341 
342 static int
get_file_handle(struct nfs_dlmount * ndmntp)343 get_file_handle(struct nfs_dlmount *ndmntp)
344 {
345 	char *sp, *dp, *endp;
346 	int error;
347 
348 	/*
349 	 * Get file handle for "key" (root or swap)
350 	 * using RPC to mountd/mount
351 	 */
352 	error = md_mount(&ndmntp->ndm_saddr, ndmntp->ndm_path, ndmntp->ndm_nfsv3,
353 	    ndmntp->ndm_sotype, ndmntp->ndm_fh, &ndmntp->ndm_fhlen);
354 	if (error) {
355 		return error;
356 	}
357 
358 	/* Construct remote path (for getmntinfo(3)) */
359 	dp = ndmntp->ndm_mntfrom;
360 	endp = dp + MAXPATHLEN - 1;
361 	for (sp = ndmntp->ndm_host; *sp && dp < endp;) {
362 		*dp++ = *sp++;
363 	}
364 	if (dp < endp) {
365 		*dp++ = ':';
366 	}
367 	for (sp = ndmntp->ndm_path; *sp && dp < endp;) {
368 		*dp++ = *sp++;
369 	}
370 	*dp = '\0';
371 	return 0;
372 }
373 
374 
375 /*
376  * Get an mbuf with the given length, and
377  * initialize the pkthdr length field.
378  */
379 static int
mbuf_get_with_len(size_t msg_len,mbuf_t * m)380 mbuf_get_with_len(size_t msg_len, mbuf_t *m)
381 {
382 	int error;
383 	error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, m);
384 	if (error) {
385 		return error;
386 	}
387 	if (msg_len > mbuf_maxlen(*m)) {
388 		error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, m);
389 		if (error) {
390 			mbuf_freem(*m);
391 			return error;
392 		}
393 		if (msg_len > mbuf_maxlen(*m)) {
394 			panic("nfs_boot: msg_len > MCLBYTES");
395 		}
396 	}
397 	mbuf_setlen(*m, msg_len);
398 	mbuf_pkthdr_setlen(*m, msg_len);
399 	return 0;
400 }
401 
402 
403 /*
404  * String representation for RPC.
405  */
406 struct rpc_string {
407 	size_t len;     /* length without null or padding */
408 	u_char data[4]; /* data (longer, of course) */
409 	/* data is padded to a long-word boundary */
410 };
411 /* Compute space used given string length. */
412 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
413 
414 /*
415  * Inet address in RPC messages
416  * (Note, really four 32-bit ints, NOT chars.  Blech.)
417  */
418 struct bp_inaddr {
419 	u_int32_t  atype;
420 	int32_t addr[4];
421 };
422 
423 
424 /*
425  * RPC: bootparam/whoami
426  * Given client IP address, get:
427  *	client name	(hostname)
428  *	domain name (domainname)
429  *	gateway address
430  *
431  * The hostname and domainname are set here for convenience.
432  *
433  * Note - bpsin is initialized to the broadcast address,
434  * and will be replaced with the bootparam server address
435  * after this call is complete.  Have to use PMAP_PROC_CALL
436  * to make sure we get responses only from a servers that
437  * know about us (don't want to broadcast a getport call).
438  */
439 static int
bp_whoami(struct sockaddr_in * bpsin,struct in_addr * my_ip,struct in_addr * gw_ip)440 bp_whoami(struct sockaddr_in *bpsin,
441     struct in_addr *my_ip,
442     struct in_addr *gw_ip)
443 {
444 	/* RPC structures for PMAPPROC_CALLIT */
445 	struct whoami_call {
446 		u_int32_t call_prog;
447 		u_int32_t call_vers;
448 		u_int32_t call_proc;
449 		u_int32_t call_arglen;
450 		struct bp_inaddr call_ia;
451 	} *call;
452 
453 	struct rpc_string *str;
454 	struct bp_inaddr *bia;
455 	mbuf_t m;
456 	struct sockaddr_in sin;
457 	int error;
458 	size_t msg_len, cn_len, dn_len;
459 	u_char *p;
460 	int32_t *lp;
461 	size_t encapsulated_size;
462 
463 	/*
464 	 * Get message buffer of sufficient size.
465 	 */
466 	msg_len = sizeof(*call);
467 	error = mbuf_get_with_len(msg_len, &m);
468 	if (error) {
469 		return error;
470 	}
471 
472 	/*
473 	 * Build request message for PMAPPROC_CALLIT.
474 	 */
475 	call = mbuf_data(m);
476 	call->call_prog = htonl(BOOTPARAM_PROG);
477 	call->call_vers = htonl(BOOTPARAM_VERS);
478 	call->call_proc = htonl(BOOTPARAM_WHOAMI);
479 	call->call_arglen = htonl(sizeof(struct bp_inaddr));
480 
481 	/* client IP address */
482 	call->call_ia.atype = htonl(1);
483 	p = (u_char*)my_ip;
484 	lp = call->call_ia.addr;
485 	*lp++ = htonl(*p); p++;
486 	*lp++ = htonl(*p); p++;
487 	*lp++ = htonl(*p); p++;
488 	*lp++ = htonl(*p); p++;
489 
490 	/* RPC: portmap/callit */
491 	bpsin->sin_port = htons(PMAPPORT);
492 
493 	error = krpc_call(bpsin, SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT, &m, &sin);
494 	if (error) {
495 		return error;
496 	}
497 
498 	/*
499 	 * Parse result message.
500 	 */
501 	msg_len = mbuf_len(m);
502 	lp = mbuf_data(m);
503 
504 	/* bootparam server port (also grab from address). */
505 	if (msg_len < sizeof(*lp)) {
506 		goto bad;
507 	}
508 	msg_len -= sizeof(*lp);
509 	bpsin->sin_port = htons((short)ntohl(*lp++));
510 	bpsin->sin_addr.s_addr = sin.sin_addr.s_addr;
511 
512 	/* length of encapsulated results */
513 	if (os_add_overflow((size_t) ntohl(*lp), sizeof(*lp), &encapsulated_size)
514 	    || msg_len < encapsulated_size) {
515 		goto bad;
516 	}
517 	msg_len = ntohl(*lp++);
518 	p = (u_char*)lp;
519 
520 	/* client name */
521 	if (msg_len < sizeof(*str)) {
522 		goto bad;
523 	}
524 	str = (struct rpc_string *)p;
525 	cn_len = ntohll(str->len);
526 	if ((msg_len - 4) < cn_len) {
527 		goto bad;
528 	}
529 	if (cn_len >= MAXHOSTNAMELEN) {
530 		goto bad;
531 	}
532 	lck_mtx_lock(&hostname_lock);
533 	bcopy(str->data, hostname, cn_len);
534 	hostname[cn_len] = '\0';
535 	lck_mtx_unlock(&hostname_lock);
536 	p += RPC_STR_SIZE(cn_len);
537 	msg_len -= RPC_STR_SIZE(cn_len);
538 
539 	/* domain name */
540 	if (msg_len < sizeof(*str)) {
541 		goto bad;
542 	}
543 	str = (struct rpc_string *)p;
544 	dn_len = ntohll(str->len);
545 	if ((msg_len - 4) < dn_len) {
546 		goto bad;
547 	}
548 	if (dn_len >= MAXHOSTNAMELEN) {
549 		goto bad;
550 	}
551 	lck_mtx_lock(&domainname_lock);
552 	bcopy(str->data, domainname, dn_len);
553 	domainname[dn_len] = '\0';
554 	lck_mtx_unlock(&domainname_lock);
555 	p += RPC_STR_SIZE(dn_len);
556 	msg_len -= RPC_STR_SIZE(dn_len);
557 
558 	/* gateway address */
559 	if (msg_len < sizeof(*bia)) {
560 		goto bad;
561 	}
562 	bia = (struct bp_inaddr *)p;
563 	if (bia->atype != htonl(1)) {
564 		goto bad;
565 	}
566 	p = (u_char*)gw_ip;
567 	*p++ = ntohl(bia->addr[0]) & 0xff;
568 	*p++ = ntohl(bia->addr[1]) & 0xff;
569 	*p++ = ntohl(bia->addr[2]) & 0xff;
570 	*p++ = ntohl(bia->addr[3]) & 0xff;
571 	goto out;
572 
573 bad:
574 	printf("nfs_boot: bootparam_whoami: bad reply\n");
575 	error = EBADRPC;
576 
577 out:
578 	mbuf_freem(m);
579 	return error;
580 }
581 
582 
583 /*
584  * RPC: bootparam/getfile
585  * Given client name and file "key", get:
586  *	server name
587  *	server IP address
588  *	server pathname
589  */
590 static int
bp_getfile(struct sockaddr_in * bpsin,const char * key,struct sockaddr_in * md_sin,char * serv_name,char * pathname)591 bp_getfile(struct sockaddr_in *bpsin,
592     const char *key,
593     struct sockaddr_in *md_sin,
594     char *serv_name,
595     char *pathname)
596 {
597 	struct rpc_string *str;
598 	mbuf_t m;
599 	struct bp_inaddr *bia;
600 	struct sockaddr_in *sin;
601 	u_char *p, *q;
602 	int error;
603 	size_t msg_len, cn_len, key_len, sn_len, path_len;
604 
605 	/*
606 	 * Get message buffer of sufficient size.
607 	 */
608 	lck_mtx_lock(&hostname_lock);
609 	cn_len = strlen(hostname);
610 	lck_mtx_unlock(&hostname_lock);
611 	key_len = strlen(key);
612 	msg_len = 0;
613 	msg_len += RPC_STR_SIZE(cn_len);
614 	msg_len += RPC_STR_SIZE(key_len);
615 	error = mbuf_get_with_len(msg_len, &m);
616 	if (error) {
617 		return error;
618 	}
619 
620 	/*
621 	 * Build request message.
622 	 */
623 	p = mbuf_data(m);
624 	bzero(p, msg_len);
625 	/* client name (hostname) */
626 	str = (struct rpc_string *)p;
627 	str->len = htonll(cn_len);
628 	lck_mtx_lock(&hostname_lock);
629 	bcopy(hostname, str->data, cn_len);
630 	lck_mtx_unlock(&hostname_lock);
631 	p += RPC_STR_SIZE(cn_len);
632 	/* key name (root or swap) */
633 	str = (struct rpc_string *)p;
634 	str->len = htonll(key_len);
635 	bcopy(key, str->data, key_len);
636 
637 	/* RPC: bootparam/getfile */
638 	error = krpc_call(bpsin, SOCK_DGRAM, BOOTPARAM_PROG, BOOTPARAM_VERS,
639 	    BOOTPARAM_GETFILE, &m, NULL);
640 	if (error) {
641 		return error;
642 	}
643 
644 	/*
645 	 * Parse result message.
646 	 */
647 	p = mbuf_data(m);
648 	msg_len = mbuf_len(m);
649 
650 	/* server name */
651 	if (msg_len < sizeof(*str)) {
652 		goto bad;
653 	}
654 	str = (struct rpc_string *)p;
655 	sn_len = ntohll(str->len);
656 	if ((msg_len - 4) < sn_len) {
657 		goto bad;
658 	}
659 	if (sn_len >= MAXHOSTNAMELEN) {
660 		goto bad;
661 	}
662 	bcopy(str->data, serv_name, sn_len);
663 	serv_name[sn_len] = '\0';
664 	p += RPC_STR_SIZE(sn_len);
665 	msg_len -= RPC_STR_SIZE(sn_len);
666 
667 	/* server IP address (mountd) */
668 	if (msg_len < sizeof(*bia)) {
669 		goto bad;
670 	}
671 	bia = (struct bp_inaddr *)p;
672 	if (bia->atype != htonl(1)) {
673 		goto bad;
674 	}
675 	sin = md_sin;
676 	bzero((caddr_t)sin, sizeof(*sin));
677 	sin->sin_len = sizeof(*sin);
678 	sin->sin_family = AF_INET;
679 	q = (u_char*) &sin->sin_addr;
680 	*q++ = ntohl(bia->addr[0]) & 0xff;
681 	*q++ = ntohl(bia->addr[1]) & 0xff;
682 	*q++ = ntohl(bia->addr[2]) & 0xff;
683 	*q++ = ntohl(bia->addr[3]) & 0xff;
684 	p += sizeof(*bia);
685 	msg_len -= sizeof(*bia);
686 
687 	/* server pathname */
688 	if (msg_len < sizeof(*str)) {
689 		goto bad;
690 	}
691 	str = (struct rpc_string *)p;
692 	path_len = ntohll(str->len);
693 	if ((msg_len - 4) < path_len) {
694 		goto bad;
695 	}
696 	if (path_len >= MAXPATHLEN) {
697 		goto bad;
698 	}
699 	bcopy(str->data, pathname, path_len);
700 	pathname[path_len] = '\0';
701 	goto out;
702 
703 bad:
704 	printf("nfs_boot: bootparam_getfile: bad reply\n");
705 	error = EBADRPC;
706 
707 out:
708 	mbuf_freem(m);
709 	return 0;
710 }
711 
712 
713 /*
714  * RPC: mountd/mount
715  * Given a server pathname, get an NFS file handle.
716  * Also, sets sin->sin_port to the NFS service port.
717  */
718 static int
md_mount(struct sockaddr_in * mdsin,char * path,int v3,int sotype,u_char * fhp,u_int32_t * fhlenp)719 md_mount(struct sockaddr_in *mdsin,             /* mountd server address */
720     char *path,
721     int v3,
722     int sotype,
723     u_char *fhp,
724     u_int32_t *fhlenp)
725 {
726 	/* The RPC structures */
727 	struct rpc_string *str;
728 	struct rdata {
729 		u_int32_t       errno;
730 		u_char  data[NFSX_V3FHMAX + sizeof(u_int32_t)];
731 	} *rdata;
732 	mbuf_t m;
733 	size_t mlen, slen;
734 	int error;
735 	int mntversion = v3 ? RPCMNT_VER3 : RPCMNT_VER1;
736 	int proto = (sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
737 	in_port_t mntport, nfsport;
738 
739 	/* Get port number for MOUNTD. */
740 	error = krpc_portmap(mdsin, RPCPROG_MNT, mntversion, proto, &mntport);
741 	if (error) {
742 		return error;
743 	}
744 
745 	/* Get port number for NFS use. */
746 	/* (If NFS/proto unavailable, don't bother with the mount call) */
747 	error = krpc_portmap(mdsin, NFS_PROG, v3 ? NFS_VER3 : NFS_VER2, proto, &nfsport);
748 	if (error) {
749 		return error;
750 	}
751 
752 	/* Set port number for MOUNTD */
753 	mdsin->sin_port = mntport;
754 
755 	slen = strlen(path);
756 	mlen = RPC_STR_SIZE(slen);
757 
758 	error = mbuf_get_with_len(mlen, &m);
759 	if (error) {
760 		return error;
761 	}
762 	str = mbuf_data(m);
763 	str->len = htonll(slen);
764 	bcopy(path, str->data, slen);
765 
766 	/* Do RPC to mountd. */
767 	error = krpc_call(mdsin, sotype, RPCPROG_MNT, mntversion, RPCMNT_MOUNT, &m, NULL);
768 	if (error) {
769 		return error;   /* message already freed */
770 	}
771 	/*
772 	 * the reply must be long enough to hold the errno plus either of:
773 	 * + a v2 filehandle
774 	 * + a v3 filehandle length + a v3 filehandle
775 	 */
776 	mlen = mbuf_len(m);
777 	if (mlen < sizeof(u_int32_t)) {
778 		goto bad;
779 	}
780 	rdata = mbuf_data(m);
781 	error = ntohl(rdata->errno);
782 	if (error) {
783 		goto out;
784 	}
785 	if (v3) {
786 		u_int32_t fhlen;
787 		u_char *fh;
788 		if (mlen < sizeof(u_int32_t) * 2) {
789 			goto bad;
790 		}
791 		fhlen = ntohl(*(u_int32_t*)rdata->data);
792 		fh = rdata->data + sizeof(u_int32_t);
793 		if (mlen < (sizeof(u_int32_t) * 2 + fhlen)
794 		    || fhlen >= (NFSX_V3FHMAX + sizeof(u_int32_t))) {
795 			goto bad;
796 		}
797 		bcopy(fh, fhp, fhlen);
798 		*fhlenp = fhlen;
799 	} else {
800 		if (mlen < (sizeof(u_int32_t) + NFSX_V2FH)) {
801 			goto bad;
802 		}
803 		bcopy(rdata->data, fhp, NFSX_V2FH);
804 		*fhlenp = NFSX_V2FH;
805 	}
806 
807 	/* Set port number for NFS use. */
808 	mdsin->sin_port = nfsport;
809 	goto out;
810 
811 bad:
812 	error = EBADRPC;
813 
814 out:
815 	mbuf_freem(m);
816 	return error;
817 }
818 
819 #endif /* NETHER */
820 
821 /*
822  * Mount a remote root fs via. nfs. This depends on the info in the
823  * nfs_diskless structure that has been filled in properly by some primary
824  * bootstrap.
825  * It goes something like this:
826  * - do enough of "ifconfig" by calling ifioctl() so that the system
827  *   can talk to the server
828  * - If nfs_diskless.mygateway is filled in, use that address as
829  *   a default gateway.
830  * - hand craft the swap nfs vnode hanging off a fake mount point
831  *	if swdevt[0].sw_dev == NODEV
832  * - build the rootfs mount point and call mountnfs() to do the rest.
833  */
834 #if CONFIG_NETBOOT
835 int
nfs_mountroot(void)836 nfs_mountroot(void)
837 {
838 	struct nfs_diskless nd;
839 	mount_t mp = NULL;
840 	vnode_t vp = NULL;
841 	vfs_context_t ctx;
842 	int error;
843 #if !defined(NO_MOUNT_PRIVATE)
844 	mount_t mppriv = NULL;
845 	vnode_t vppriv = NULL;
846 #endif /* NO_MOUNT_PRIVATE */
847 	int v3, sotype;
848 
849 	/*
850 	 * Call nfs_boot_init() to fill in the nfs_diskless struct.
851 	 * Note: networking must already have been configured before
852 	 * we're called.
853 	 */
854 	bzero((caddr_t) &nd, sizeof(nd));
855 	error = nfs_boot_init(&nd);
856 	if (error) {
857 		panic("nfs_boot_init: unable to initialize NFS root system information, "
858 		    "error %d, check configuration: %s\n", error, PE_boot_args());
859 	}
860 
861 	/*
862 	 * Try NFSv3 first, then fallback to NFSv2.
863 	 * Likewise, try TCP first, then fall back to UDP.
864 	 */
865 	v3 = 1;
866 	sotype = SOCK_STREAM;
867 
868 tryagain:
869 	error = nfs_boot_getfh(&nd, v3, sotype);
870 	if (error) {
871 		if (error == EHOSTDOWN || error == EHOSTUNREACH) {
872 			if (nd.nd_root.ndm_mntfrom) {
873 				NFS_ZFREE(ZV_NAMEI, nd.nd_root.ndm_mntfrom);
874 			}
875 			if (nd.nd_root.ndm_path) {
876 				NFS_ZFREE(ZV_NAMEI, nd.nd_root.ndm_path);
877 			}
878 			if (nd.nd_private.ndm_mntfrom) {
879 				NFS_ZFREE(ZV_NAMEI, nd.nd_private.ndm_mntfrom);
880 			}
881 			if (nd.nd_private.ndm_path) {
882 				NFS_ZFREE(ZV_NAMEI, nd.nd_private.ndm_path);
883 			}
884 			return error;
885 		}
886 		if (v3) {
887 			if (sotype == SOCK_STREAM) {
888 				printf("NFS mount (v3,TCP) failed with error %d, trying UDP...\n", error);
889 				sotype = SOCK_DGRAM;
890 				goto tryagain;
891 			}
892 			printf("NFS mount (v3,UDP) failed with error %d, trying v2...\n", error);
893 			v3 = 0;
894 			sotype = SOCK_STREAM;
895 			goto tryagain;
896 		} else if (sotype == SOCK_STREAM) {
897 			printf("NFS mount (v2,TCP) failed with error %d, trying UDP...\n", error);
898 			sotype = SOCK_DGRAM;
899 			goto tryagain;
900 		} else {
901 			printf("NFS mount (v2,UDP) failed with error %d, giving up...\n", error);
902 		}
903 		switch (error) {
904 		case EPROGUNAVAIL:
905 			panic("NFS mount failed: NFS server mountd not responding, check server configuration: %s", PE_boot_args());
906 		case EACCES:
907 		case EPERM:
908 			panic("NFS mount failed: NFS server refused mount, check server configuration: %s", PE_boot_args());
909 		default:
910 			panic("NFS mount failed with error %d, check configuration: %s", error, PE_boot_args());
911 		}
912 	}
913 
914 	ctx = vfs_context_kernel();
915 
916 	/*
917 	 * Create the root mount point.
918 	 */
919 #if !defined(NO_MOUNT_PRIVATE)
920 	{
921 		//PWC hack until we have a real "mount" tool to remount root rw
922 		int rw_root = 0;
923 		int flags = MNT_ROOTFS | MNT_RDONLY;
924 		PE_parse_boot_argn("-rwroot_hack", &rw_root, sizeof(rw_root));
925 		if (rw_root) {
926 			flags = MNT_ROOTFS;
927 			kprintf("-rwroot_hack in effect: mounting root fs read/write\n");
928 		}
929 
930 		if ((error = nfs_mount_diskless(&nd.nd_root, "/", flags, &vp, &mp, ctx)))
931 #else
932 	if ((error = nfs_mount_diskless(&nd.nd_root, "/", MNT_ROOTFS, &vp, &mp, ctx)))
933 #endif /* NO_MOUNT_PRIVATE */
934 		{
935 			if (v3) {
936 				if (sotype == SOCK_STREAM) {
937 					printf("NFS root mount (v3,TCP) failed with %d, trying UDP...\n", error);
938 					sotype = SOCK_DGRAM;
939 					goto tryagain;
940 				}
941 				printf("NFS root mount (v3,UDP) failed with %d, trying v2...\n", error);
942 				v3 = 0;
943 				sotype = SOCK_STREAM;
944 				goto tryagain;
945 			} else if (sotype == SOCK_STREAM) {
946 				printf("NFS root mount (v2,TCP) failed with %d, trying UDP...\n", error);
947 				sotype = SOCK_DGRAM;
948 				goto tryagain;
949 			} else {
950 				printf("NFS root mount (v2,UDP) failed with error %d, giving up...\n", error);
951 			}
952 			panic("NFS root mount failed with error %d, check configuration: %s", error, PE_boot_args());
953 		}
954 	}
955 	printf("root on %s\n", nd.nd_root.ndm_mntfrom);
956 
957 	vfs_unbusy(mp);
958 	mount_list_add(mp);
959 	rootvp = vp;
960 
961 #if !defined(NO_MOUNT_PRIVATE)
962 	if (nd.nd_private.ndm_saddr.sin_addr.s_addr) {
963 		error = nfs_mount_diskless_private(&nd.nd_private, "/private",
964 		    0, &vppriv, &mppriv, ctx);
965 		if (error) {
966 			panic("NFS /private mount failed with error %d, check configuration: %s", error, PE_boot_args());
967 		}
968 		printf("private on %s\n", nd.nd_private.ndm_mntfrom);
969 
970 		vfs_unbusy(mppriv);
971 		mount_list_add(mppriv);
972 	}
973 
974 #endif /* NO_MOUNT_PRIVATE */
975 
976 	if (nd.nd_root.ndm_mntfrom) {
977 		NFS_ZFREE(ZV_NAMEI, nd.nd_root.ndm_mntfrom);
978 	}
979 	if (nd.nd_root.ndm_path) {
980 		NFS_ZFREE(ZV_NAMEI, nd.nd_root.ndm_path);
981 	}
982 	if (nd.nd_private.ndm_mntfrom) {
983 		NFS_ZFREE(ZV_NAMEI, nd.nd_private.ndm_mntfrom);
984 	}
985 	if (nd.nd_private.ndm_path) {
986 		NFS_ZFREE(ZV_NAMEI, nd.nd_private.ndm_path);
987 	}
988 
989 	return 0;
990 }
991 
992 /*
993  * Internal version of mount system call for diskless setup.
994  */
995 static int
nfs_mount_diskless(struct nfs_dlmount * ndmntp,const char * mntname,int mntflag,vnode_t * vpp,mount_t * mpp,vfs_context_t ctx)996 nfs_mount_diskless(
997 	struct nfs_dlmount *ndmntp,
998 	const char *mntname,
999 	int mntflag,
1000 	vnode_t *vpp,
1001 	mount_t *mpp,
1002 	vfs_context_t ctx)
1003 {
1004 	mount_t mp;
1005 	vnode_t vp = NULLVP;
1006 	int error, numcomps;
1007 	char *xdrbuf, *p, *cp, *frompath, *endserverp;
1008 	char uaddr[MAX_IPv4_STR_LEN];
1009 	struct xdrbuf xb;
1010 	uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
1011 	uint32_t mflags_mask[NFS_MFLAG_BITMAP_LEN];
1012 	uint32_t mflags[NFS_MFLAG_BITMAP_LEN];
1013 	uint64_t argslength_offset, attrslength_offset, end_offset;
1014 
1015 	if ((error = vfs_rootmountalloc("nfs", ndmntp->ndm_mntfrom, &mp))) {
1016 		printf("nfs_mount_diskless: NFS not configured\n");
1017 		return error;
1018 	}
1019 
1020 	mp->mnt_kern_flag |= MNTK_KERNEL_MOUNT; /* mark as kernel mount */
1021 	vfs_setflags(mp, mntflag);
1022 	if (!vfs_isrdonly(mp)) {
1023 		vfs_clearflags(mp, MNT_RDONLY);
1024 	}
1025 
1026 	/* find the server-side path being mounted */
1027 	frompath = ndmntp->ndm_mntfrom;
1028 	if (*frompath == '[') {  /* skip IPv6 literal address */
1029 		while (*frompath && (*frompath != ']')) {
1030 			frompath++;
1031 		}
1032 		if (*frompath == ']') {
1033 			frompath++;
1034 		}
1035 	}
1036 	while (*frompath && (*frompath != ':')) {
1037 		frompath++;
1038 	}
1039 	endserverp = frompath;
1040 	while (*frompath && (*frompath == ':')) {
1041 		frompath++;
1042 	}
1043 	/* count fs location path components */
1044 	p = frompath;
1045 	while (*p && (*p == '/')) {
1046 		p++;
1047 	}
1048 	numcomps = 0;
1049 	while (*p) {
1050 		numcomps++;
1051 		while (*p && (*p != '/')) {
1052 			p++;
1053 		}
1054 		while (*p && (*p == '/')) {
1055 			p++;
1056 		}
1057 	}
1058 
1059 	/* convert address to universal address string */
1060 	if (inet_ntop(AF_INET, &ndmntp->ndm_saddr.sin_addr, uaddr, sizeof(uaddr)) != uaddr) {
1061 		printf("nfs_mount_diskless: bad address\n");
1062 		return EINVAL;
1063 	}
1064 
1065 	/* prepare mount attributes */
1066 	NFS_BITMAP_ZERO(mattrs, NFS_MATTR_BITMAP_LEN);
1067 	NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_VERSION);
1068 	NFS_BITMAP_SET(mattrs, NFS_MATTR_SOCKET_TYPE);
1069 	NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_PORT);
1070 	NFS_BITMAP_SET(mattrs, NFS_MATTR_FH);
1071 	NFS_BITMAP_SET(mattrs, NFS_MATTR_FS_LOCATIONS);
1072 	NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFLAGS);
1073 
1074 	/* prepare mount flags */
1075 	NFS_BITMAP_ZERO(mflags_mask, NFS_MFLAG_BITMAP_LEN);
1076 	NFS_BITMAP_ZERO(mflags, NFS_MFLAG_BITMAP_LEN);
1077 	NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RESVPORT);
1078 	NFS_BITMAP_SET(mflags, NFS_MFLAG_RESVPORT);
1079 
1080 	/* build xdr buffer */
1081 	xb_init_buffer(&xb, NULL, 0);
1082 	xb_add_32(error, &xb, NFS_ARGSVERSION_XDR);
1083 	argslength_offset = xb_offset(&xb);
1084 	xb_add_32(error, &xb, 0); // args length
1085 	xb_add_32(error, &xb, NFS_XDRARGS_VERSION_0);
1086 	xb_add_bitmap(error, &xb, mattrs, NFS_MATTR_BITMAP_LEN);
1087 	attrslength_offset = xb_offset(&xb);
1088 	xb_add_32(error, &xb, 0); // attrs length
1089 	xb_add_32(error, &xb, ndmntp->ndm_nfsv3 ? 3 : 2); // NFS version
1090 	xb_add_string(error, &xb, ((ndmntp->ndm_sotype == SOCK_DGRAM) ? "udp" : "tcp"), 3);
1091 	xb_add_32(error, &xb, ntohs(ndmntp->ndm_saddr.sin_port)); // NFS port
1092 	xb_add_fh(error, &xb, &ndmntp->ndm_fh[0], ndmntp->ndm_fhlen);
1093 	/* fs location */
1094 	xb_add_32(error, &xb, 1); /* fs location count */
1095 	xb_add_32(error, &xb, 1); /* server count */
1096 	xb_add_string(error, &xb, ndmntp->ndm_mntfrom, (endserverp - ndmntp->ndm_mntfrom)); /* server name */
1097 	xb_add_32(error, &xb, 1); /* address count */
1098 	xb_add_string(error, &xb, uaddr, strlen(uaddr)); /* address */
1099 	xb_add_32(error, &xb, 0); /* empty server info */
1100 	xb_add_32(error, &xb, numcomps); /* pathname component count */
1101 	p = frompath;
1102 	while (*p && (*p == '/')) {
1103 		p++;
1104 	}
1105 	while (*p) {
1106 		cp = p;
1107 		while (*p && (*p != '/')) {
1108 			p++;
1109 		}
1110 		xb_add_string(error, &xb, cp, (p - cp)); /* component */
1111 		if (error) {
1112 			break;
1113 		}
1114 		while (*p && (*p == '/')) {
1115 			p++;
1116 		}
1117 	}
1118 	xb_add_32(error, &xb, 0); /* empty fsl info */
1119 	xb_add_32(error, &xb, mntflag); /* MNT flags */
1120 	xb_build_done(error, &xb);
1121 
1122 	/* update opaque counts */
1123 	end_offset = xb_offset(&xb);
1124 	if (!error) {
1125 		error = xb_seek(&xb, argslength_offset);
1126 		xb_add_32(error, &xb, end_offset - argslength_offset + XDRWORD /*version*/);
1127 	}
1128 	if (!error) {
1129 		error = xb_seek(&xb, attrslength_offset);
1130 		xb_add_32(error, &xb, end_offset - attrslength_offset - XDRWORD /*don't include length field*/);
1131 	}
1132 	if (error) {
1133 		printf("nfs_mount_diskless: error %d assembling mount args\n", error);
1134 		xb_cleanup(&xb);
1135 		return error;
1136 	}
1137 	/* grab the assembled buffer */
1138 	xdrbuf = xb_buffer_base(&xb);
1139 
1140 	/* do the mount */
1141 	if ((error = VFS_MOUNT(mp, vp, CAST_USER_ADDR_T(xdrbuf), ctx))) {
1142 		printf("nfs_mountroot: mount %s failed: %d\n", mntname, error);
1143 		// XXX vfs_rootmountfailed(mp);
1144 		mount_list_lock();
1145 		mp->mnt_vtable->vfc_refcount--;
1146 		mount_list_unlock();
1147 		vfs_unbusy(mp);
1148 		mount_lock_destroy(mp);
1149 #if CONFIG_MACF
1150 		mac_mount_label_destroy(mp);
1151 #endif
1152 		NFS_ZFREE(mount_zone, mp);
1153 	} else {
1154 		*mpp = mp;
1155 		error = VFS_ROOT(mp, vpp, ctx);
1156 	}
1157 	xb_cleanup(&xb);
1158 	return error;
1159 }
1160 
1161 #if !defined(NO_MOUNT_PRIVATE)
1162 /*
1163  * Internal version of mount system call to mount "/private"
1164  * separately in diskless setup
1165  */
1166 static int
nfs_mount_diskless_private(struct nfs_dlmount * ndmntp,const char * mntname,int mntflag,vnode_t * vpp,mount_t * mpp,vfs_context_t ctx)1167 nfs_mount_diskless_private(
1168 	struct nfs_dlmount *ndmntp,
1169 	const char *mntname,
1170 	int mntflag,
1171 	vnode_t *vpp,
1172 	mount_t *mpp,
1173 	vfs_context_t ctx)
1174 {
1175 	mount_t mp;
1176 	vnode_t vp = NULLVP;
1177 	int error, numcomps;
1178 	proc_t procp;
1179 	struct vfstable *vfsp;
1180 	struct nameidata nd;
1181 	char *xdrbuf = NULL, *p, *cp, *frompath, *endserverp;
1182 	char uaddr[MAX_IPv4_STR_LEN];
1183 	struct xdrbuf xb;
1184 	uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
1185 	uint32_t mflags_mask[NFS_MFLAG_BITMAP_LEN], mflags[NFS_MFLAG_BITMAP_LEN];
1186 	uint64_t argslength_offset, attrslength_offset, end_offset;
1187 	struct vfsioattr ioattr;
1188 
1189 	procp = current_proc(); /* XXX */
1190 	xb_init(&xb, XDRBUF_NONE);
1191 
1192 	{
1193 		/*
1194 		 * mimic main()!. Temporarily set up rootvnode and other stuff so
1195 		 * that namei works. Need to undo this because main() does it, too
1196 		 */
1197 		struct filedesc *fdp = &procp->p_fd;
1198 		vfs_setflags(mountlist.tqh_first, MNT_ROOTFS);
1199 
1200 		/* Get the vnode for '/'. Set fdp->fd_cdir to reference it. */
1201 		if (VFS_ROOT(mountlist.tqh_first, &rootvnode, NULL)) {
1202 			panic("cannot find root vnode");
1203 		}
1204 		error = vnode_ref(rootvnode);
1205 		if (error) {
1206 			printf("nfs_mountroot: vnode_ref() failed on root vnode!\n");
1207 			goto out;
1208 		}
1209 		fdp->fd_cdir = rootvnode;
1210 		fdp->fd_rdir = NULL;
1211 	}
1212 
1213 	/*
1214 	 * Get vnode to be covered
1215 	 */
1216 	NDINIT(&nd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE,
1217 	    CAST_USER_ADDR_T(mntname), ctx);
1218 	error = namei(&nd);
1219 	{
1220 		/* undo vnode_ref() in mimic main()! */
1221 		vnode_rele(rootvnode);
1222 	}
1223 	if (error) {
1224 		printf("nfs_mountroot: private namei failed!\n");
1225 		goto out;
1226 	}
1227 	nameidone(&nd);
1228 	vp = nd.ni_vp;
1229 
1230 	if ((error = VNOP_FSYNC(vp, MNT_WAIT, ctx)) ||
1231 	    (error = buf_invalidateblks(vp, BUF_WRITE_DATA, 0, 0))) {
1232 		vnode_put(vp);
1233 		goto out;
1234 	}
1235 	if (vnode_vtype(vp) != VDIR) {
1236 		vnode_put(vp);
1237 		error = ENOTDIR;
1238 		goto out;
1239 	}
1240 	for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) {
1241 		if (!strncmp(vfsp->vfc_name, "nfs", sizeof(vfsp->vfc_name))) {
1242 			break;
1243 		}
1244 	}
1245 	if (vfsp == NULL) {
1246 		printf("nfs_mountroot: private NFS not configured\n");
1247 		vnode_put(vp);
1248 		error = ENODEV;
1249 		goto out;
1250 	}
1251 	if (vnode_mountedhere(vp) != NULL) {
1252 		vnode_put(vp);
1253 		error = EBUSY;
1254 		goto out;
1255 	}
1256 
1257 	/*
1258 	 * Allocate and initialize the filesystem.
1259 	 */
1260 	mp = zalloc_flags(mount_zone, Z_WAITOK | Z_ZERO);
1261 	/* Initialize the default IO constraints */
1262 	bzero(&ioattr, sizeof(ioattr));
1263 	ioattr.io_maxreadcnt = ioattr.io_maxwritecnt = MAXPHYS;
1264 	ioattr.io_segreadcnt = ioattr.io_segwritecnt = 32;
1265 	vfs_setioattr(mp, &ioattr);
1266 	mp->mnt_realrootvp = NULLVP;
1267 	vfs_setauthcache_ttl(mp, 0); /* Allways go to our lookup */
1268 	mp->mnt_kern_flag |= MNTK_KERNEL_MOUNT; /* mark as kernel mount */
1269 
1270 	mount_lock_init(mp);
1271 	TAILQ_INIT(&mp->mnt_vnodelist);
1272 	TAILQ_INIT(&mp->mnt_workerqueue);
1273 	TAILQ_INIT(&mp->mnt_newvnodes);
1274 	(void)vfs_busy(mp, LK_NOWAIT);
1275 	TAILQ_INIT(&mp->mnt_vnodelist);
1276 	mount_list_lock();
1277 	vfsp->vfc_refcount++;
1278 	mount_list_unlock();
1279 	mp->mnt_vtable = vfsp;
1280 	mp->mnt_op = vfsp->vfc_vfsops;
1281 	vfs_setflags(mp, mntflag);
1282 	vfs_setflags(mp, vfsp->vfc_flags);
1283 	strncpy(vfs_statfs(mp)->f_fstypename, vfsp->vfc_name, MFSNAMELEN - 1);
1284 	vp->v_mountedhere = mp;
1285 	mp->mnt_vnodecovered = vp;
1286 	vp = NULLVP;
1287 	vfs_statfs(mp)->f_owner = kauth_cred_getuid(kauth_cred_get());
1288 	(void) copystr(mntname, vfs_statfs(mp)->f_mntonname, MAXPATHLEN - 1, 0);
1289 	(void) copystr(ndmntp->ndm_mntfrom, vfs_statfs(mp)->f_mntfromname, MAXPATHLEN - 1, 0);
1290 #if CONFIG_MACF
1291 	mac_mount_label_init(mp);
1292 	mac_mount_label_associate(ctx, mp);
1293 #endif
1294 
1295 	/* find the server-side path being mounted */
1296 	frompath = ndmntp->ndm_mntfrom;
1297 	if (*frompath == '[') {  /* skip IPv6 literal address */
1298 		while (*frompath && (*frompath != ']')) {
1299 			frompath++;
1300 		}
1301 		if (*frompath == ']') {
1302 			frompath++;
1303 		}
1304 	}
1305 	while (*frompath && (*frompath != ':')) {
1306 		frompath++;
1307 	}
1308 	endserverp = frompath;
1309 	while (*frompath && (*frompath == ':')) {
1310 		frompath++;
1311 	}
1312 	/* count fs location path components */
1313 	p = frompath;
1314 	while (*p && (*p == '/')) {
1315 		p++;
1316 	}
1317 	numcomps = 0;
1318 	while (*p) {
1319 		numcomps++;
1320 		while (*p && (*p != '/')) {
1321 			p++;
1322 		}
1323 		while (*p && (*p == '/')) {
1324 			p++;
1325 		}
1326 	}
1327 
1328 	/* convert address to universal address string */
1329 	if (inet_ntop(AF_INET, &ndmntp->ndm_saddr.sin_addr, uaddr, sizeof(uaddr)) != uaddr) {
1330 		printf("nfs_mountroot: bad address\n");
1331 		error = EINVAL;
1332 		goto out;
1333 	}
1334 
1335 	/* prepare mount attributes */
1336 	NFS_BITMAP_ZERO(mattrs, NFS_MATTR_BITMAP_LEN);
1337 	NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_VERSION);
1338 	NFS_BITMAP_SET(mattrs, NFS_MATTR_SOCKET_TYPE);
1339 	NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_PORT);
1340 	NFS_BITMAP_SET(mattrs, NFS_MATTR_FH);
1341 	NFS_BITMAP_SET(mattrs, NFS_MATTR_FS_LOCATIONS);
1342 	NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFLAGS);
1343 
1344 	/* prepare mount flags */
1345 	NFS_BITMAP_ZERO(mflags_mask, NFS_MFLAG_BITMAP_LEN);
1346 	NFS_BITMAP_ZERO(mflags, NFS_MFLAG_BITMAP_LEN);
1347 	NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RESVPORT);
1348 	NFS_BITMAP_SET(mflags, NFS_MFLAG_RESVPORT);
1349 
1350 	/* build xdr buffer */
1351 	xb_init_buffer(&xb, NULL, 0);
1352 	xb_add_32(error, &xb, NFS_ARGSVERSION_XDR);
1353 	argslength_offset = xb_offset(&xb);
1354 	xb_add_32(error, &xb, 0); // args length
1355 	xb_add_32(error, &xb, NFS_XDRARGS_VERSION_0);
1356 	xb_add_bitmap(error, &xb, mattrs, NFS_MATTR_BITMAP_LEN);
1357 	attrslength_offset = xb_offset(&xb);
1358 	xb_add_32(error, &xb, 0); // attrs length
1359 	xb_add_32(error, &xb, ndmntp->ndm_nfsv3 ? 3 : 2); // NFS version
1360 	xb_add_string(error, &xb, ((ndmntp->ndm_sotype == SOCK_DGRAM) ? "udp" : "tcp"), 3);
1361 	xb_add_32(error, &xb, ntohs(ndmntp->ndm_saddr.sin_port)); // NFS port
1362 	xb_add_fh(error, &xb, &ndmntp->ndm_fh[0], ndmntp->ndm_fhlen);
1363 	/* fs location */
1364 	xb_add_32(error, &xb, 1); /* fs location count */
1365 	xb_add_32(error, &xb, 1); /* server count */
1366 	xb_add_string(error, &xb, ndmntp->ndm_mntfrom, (endserverp - ndmntp->ndm_mntfrom)); /* server name */
1367 	xb_add_32(error, &xb, 1); /* address count */
1368 	xb_add_string(error, &xb, uaddr, strlen(uaddr)); /* address */
1369 	xb_add_32(error, &xb, 0); /* empty server info */
1370 	xb_add_32(error, &xb, numcomps); /* pathname component count */
1371 	p = frompath;
1372 	while (*p && (*p == '/')) {
1373 		p++;
1374 	}
1375 	while (*p) {
1376 		cp = p;
1377 		while (*p && (*p != '/')) {
1378 			p++;
1379 		}
1380 		xb_add_string(error, &xb, cp, (p - cp)); /* component */
1381 		if (error) {
1382 			break;
1383 		}
1384 		while (*p && (*p == '/')) {
1385 			p++;
1386 		}
1387 	}
1388 	xb_add_32(error, &xb, 0); /* empty fsl info */
1389 	xb_add_32(error, &xb, mntflag); /* MNT flags */
1390 	xb_build_done(error, &xb);
1391 
1392 	/* update opaque counts */
1393 	end_offset = xb_offset(&xb);
1394 	if (!error) {
1395 		error = xb_seek(&xb, argslength_offset);
1396 		xb_add_32(error, &xb, end_offset - argslength_offset + XDRWORD /*version*/);
1397 	}
1398 	if (!error) {
1399 		error = xb_seek(&xb, attrslength_offset);
1400 		xb_add_32(error, &xb, end_offset - attrslength_offset - XDRWORD /*don't include length field*/);
1401 	}
1402 	if (error) {
1403 		printf("nfs_mountroot: error %d assembling mount args\n", error);
1404 		goto out;
1405 	}
1406 	/* grab the assembled buffer */
1407 	xdrbuf = xb_buffer_base(&xb);
1408 
1409 	/* do the mount */
1410 	if ((error = VFS_MOUNT(mp, vp, CAST_USER_ADDR_T(xdrbuf), ctx))) {
1411 		printf("nfs_mountroot: mount %s failed: %d\n", mntname, error);
1412 		vnode_put(mp->mnt_vnodecovered);
1413 		mount_list_lock();
1414 		vfsp->vfc_refcount--;
1415 		mount_list_unlock();
1416 		vfs_unbusy(mp);
1417 		mount_lock_destroy(mp);
1418 #if CONFIG_MACF
1419 		mac_mount_label_destroy(mp);
1420 #endif
1421 		NFS_ZFREE(mount_zone, mp);
1422 		goto out;
1423 	} else {
1424 		*mpp = mp;
1425 		error = VFS_ROOT(mp, vpp, ctx);
1426 	}
1427 out:
1428 	xb_cleanup(&xb);
1429 	return error;
1430 }
1431 #endif /* NO_MOUNT_PRIVATE */
1432 
1433 #endif /* CONFIG_NETBOOT */
1434 
1435 #endif /* CONFIG_NFS_CLIENT */
1436