xref: /titanic_51/usr/src/boot/lib/libstand/nfs.c (revision 546a5316dc64f8723d8e04d43ca4cd2503252fd8)
1 /*	$NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $	*/
2 
3 /*-
4  *  Copyright (c) 1993 John Brezak
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *  1. Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  *  2. Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *  3. The name of the author may not be used to endorse or promote products
16  *     derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 
33 #include <sys/param.h>
34 #include <sys/time.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <string.h>
38 #include <stddef.h>
39 
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42 
43 #include "rpcv2.h"
44 #include "nfsv2.h"
45 
46 #include "stand.h"
47 #include "net.h"
48 #include "netif.h"
49 #include "rpc.h"
50 
51 #define NFS_DEBUGxx
52 
53 #define NFSREAD_MIN_SIZE 1024
54 #define NFSREAD_MAX_SIZE 4096
55 
56 /* Define our own NFS attributes without NQNFS stuff. */
57 #ifdef OLD_NFSV2
58 struct nfsv2_fattrs {
59 	n_long	fa_type;
60 	n_long	fa_mode;
61 	n_long	fa_nlink;
62 	n_long	fa_uid;
63 	n_long	fa_gid;
64 	n_long	fa_size;
65 	n_long	fa_blocksize;
66 	n_long	fa_rdev;
67 	n_long	fa_blocks;
68 	n_long	fa_fsid;
69 	n_long	fa_fileid;
70 	struct nfsv2_time fa_atime;
71 	struct nfsv2_time fa_mtime;
72 	struct nfsv2_time fa_ctime;
73 };
74 
75 struct nfs_read_args {
76 	u_char	fh[NFS_FHSIZE];
77 	n_long	off;
78 	n_long	len;
79 	n_long	xxx;			/* XXX what's this for? */
80 };
81 
82 /* Data part of nfs rpc reply (also the largest thing we receive) */
83 struct nfs_read_repl {
84 	n_long	errno;
85 	struct	nfsv2_fattrs fa;
86 	n_long	count;
87 	u_char	data[NFSREAD_MAX_SIZE];
88 };
89 
90 #ifndef NFS_NOSYMLINK
91 struct nfs_readlnk_repl {
92 	n_long	errno;
93 	n_long	len;
94 	char	path[NFS_MAXPATHLEN];
95 };
96 #endif
97 
98 struct nfs_readdir_args {
99 	u_char	fh[NFS_FHSIZE];
100 	n_long	cookie;
101 	n_long	count;
102 };
103 
104 struct nfs_readdir_data {
105 	n_long	fileid;
106 	n_long	len;
107 	char	name[0];
108 };
109 
110 struct nfs_readdir_off {
111 	n_long	cookie;
112 	n_long	follows;
113 };
114 
115 struct nfs_iodesc {
116 	struct	iodesc	*iodesc;
117 	off_t	off;
118 	u_char	fh[NFS_FHSIZE];
119 	struct nfsv2_fattrs fa;	/* all in network order */
120 };
121 #else	/* !OLD_NFSV2 */
122 
123 /* NFSv3 definitions */
124 #define	NFS_V3MAXFHSIZE		64
125 #define	NFS_VER3		3
126 #define	RPCMNT_VER3		3
127 #define	NFSPROCV3_LOOKUP	3
128 #define	NFSPROCV3_READLINK	5
129 #define	NFSPROCV3_READ		6
130 #define	NFSPROCV3_READDIR	16
131 
132 typedef struct {
133 	uint32_t val[2];
134 } n_quad;
135 
136 struct nfsv3_time {
137 	uint32_t nfs_sec;
138 	uint32_t nfs_nsec;
139 };
140 
141 struct nfsv3_fattrs {
142 	uint32_t fa_type;
143 	uint32_t fa_mode;
144 	uint32_t fa_nlink;
145 	uint32_t fa_uid;
146 	uint32_t fa_gid;
147 	n_quad fa_size;
148 	n_quad fa_used;
149 	n_quad fa_rdev;
150 	n_quad fa_fsid;
151 	n_quad fa_fileid;
152 	struct nfsv3_time fa_atime;
153 	struct nfsv3_time fa_mtime;
154 	struct nfsv3_time fa_ctime;
155 };
156 
157 /*
158  * For NFSv3, the file handle is variable in size, so most fixed sized
159  * structures for arguments won't work. For most cases, a structure
160  * that starts with any fixed size section is followed by an array
161  * that covers the maximum size required.
162  */
163 struct nfsv3_readdir_repl {
164 	uint32_t errno;
165 	uint32_t ok;
166 	struct nfsv3_fattrs fa;
167 	uint32_t cookiev0;
168 	uint32_t cookiev1;
169 };
170 
171 struct nfsv3_readdir_entry {
172 	uint32_t follows;
173 	uint32_t fid0;
174 	uint32_t fid1;
175 	uint32_t len;
176 	uint32_t nameplus[0];
177 };
178 
179 struct nfs_iodesc {
180 	struct iodesc *iodesc;
181 	off_t off;
182 	uint32_t fhsize;
183 	u_char fh[NFS_V3MAXFHSIZE];
184 	struct nfsv3_fattrs fa;	/* all in network order */
185 	uint64_t cookie;
186 };
187 #endif	/* OLD_NFSV2 */
188 
189 /*
190  * XXX interactions with tftp? See nfswrapper.c for a confusing
191  *     issue.
192  */
193 int		nfs_open(const char *path, struct open_file *f);
194 static int	nfs_close(struct open_file *f);
195 static int	nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
196 static int	nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
197 static off_t	nfs_seek(struct open_file *f, off_t offset, int where);
198 static int	nfs_stat(struct open_file *f, struct stat *sb);
199 static int	nfs_readdir(struct open_file *f, struct dirent *d);
200 
201 struct	nfs_iodesc nfs_root_node;
202 
203 struct fs_ops nfs_fsops = {
204 	"nfs",
205 	nfs_open,
206 	nfs_close,
207 	nfs_read,
208 	nfs_write,
209 	nfs_seek,
210 	nfs_stat,
211 	nfs_readdir
212 };
213 
214 static int nfs_read_size = NFSREAD_MIN_SIZE;
215 
216 /*
217  * Improve boot performance over NFS
218  */
219 static void
220 set_nfs_read_size(void)
221 {
222 	char *env, *end;
223 	char buf[10];
224 
225 	if ((env = getenv("nfs.read_size")) != NULL) {
226 		errno = 0;
227 		nfs_read_size = strtol(env, &end, 0);
228 		if (errno != 0 || *env == '\0' || *end != '\0') {
229 			printf("%s: bad value: \"%s\", defaulting to %d\n",
230 			    "nfs.read_size", env, NFSREAD_MIN_SIZE);
231 			nfs_read_size = NFSREAD_MIN_SIZE;
232 		}
233 	}
234 	if (nfs_read_size < NFSREAD_MIN_SIZE) {
235 		printf("%s: bad value: \"%d\", defaulting to %d\n",
236 		    "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
237 		nfs_read_size = NFSREAD_MIN_SIZE;
238 	}
239 	if (nfs_read_size > NFSREAD_MAX_SIZE) {
240 		printf("%s: bad value: \"%d\", defaulting to %d\n",
241 		    "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
242 		nfs_read_size = NFSREAD_MAX_SIZE;
243 	}
244 	snprintf(buf, sizeof (buf), "%d", nfs_read_size);
245 	setenv("nfs.read_size", buf, 1);
246 }
247 
248 #ifdef	OLD_NFSV2
249 /*
250  * Fetch the root file handle (call mount daemon)
251  * Return zero or error number.
252  */
253 int
254 nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp)
255 {
256 	int len;
257 	struct args {
258 		n_long	len;
259 		char	path[FNAME_SIZE];
260 	} *args;
261 	struct repl {
262 		n_long	errno;
263 		u_char	fh[NFS_FHSIZE];
264 	} *repl;
265 	struct {
266 		n_long	h[RPC_HEADER_WORDS];
267 		struct args d;
268 	} sdata;
269 	struct {
270 		n_long	h[RPC_HEADER_WORDS];
271 		struct repl d;
272 	} rdata;
273 	size_t cc;
274 
275 #ifdef NFS_DEBUG
276 	if (debug)
277 		printf("nfs_getrootfh: %s\n", path);
278 #endif
279 
280 	args = &sdata.d;
281 	repl = &rdata.d;
282 
283 	bzero(args, sizeof(*args));
284 	len = strlen(path);
285 	if (len > sizeof(args->path))
286 		len = sizeof(args->path);
287 	args->len = htonl(len);
288 	bcopy(path, args->path, len);
289 	len = 4 + roundup(len, 4);
290 
291 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
292 	    args, len, repl, sizeof(*repl));
293 	if (cc == -1) {
294 		/* errno was set by rpc_call */
295 		return (errno);
296 	}
297 	if (cc < 4)
298 		return (EBADRPC);
299 	if (repl->errno)
300 		return (ntohl(repl->errno));
301 	bcopy(repl->fh, fhp, sizeof(repl->fh));
302 
303 	set_nfs_read_size();
304 	return (0);
305 }
306 
307 /*
308  * Lookup a file.  Store handle and attributes.
309  * Return zero or error number.
310  */
311 int
312 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
313 {
314 	int len, rlen;
315 	struct args {
316 		u_char	fh[NFS_FHSIZE];
317 		n_long	len;
318 		char	name[FNAME_SIZE];
319 	} *args;
320 	struct repl {
321 		n_long	errno;
322 		u_char	fh[NFS_FHSIZE];
323 		struct	nfsv2_fattrs fa;
324 	} *repl;
325 	struct {
326 		n_long	h[RPC_HEADER_WORDS];
327 		struct args d;
328 	} sdata;
329 	struct {
330 		n_long	h[RPC_HEADER_WORDS];
331 		struct repl d;
332 	} rdata;
333 	ssize_t cc;
334 
335 #ifdef NFS_DEBUG
336 	if (debug)
337 		printf("lookupfh: called\n");
338 #endif
339 
340 	args = &sdata.d;
341 	repl = &rdata.d;
342 
343 	bzero(args, sizeof(*args));
344 	bcopy(d->fh, args->fh, sizeof(args->fh));
345 	len = strlen(name);
346 	if (len > sizeof(args->name))
347 		len = sizeof(args->name);
348 	bcopy(name, args->name, len);
349 	args->len = htonl(len);
350 	len = 4 + roundup(len, 4);
351 	len += NFS_FHSIZE;
352 
353 	rlen = sizeof(*repl);
354 
355 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
356 	    args, len, repl, rlen);
357 	if (cc == -1)
358 		return (errno);		/* XXX - from rpc_call */
359 	if (cc < 4)
360 		return (EIO);
361 	if (repl->errno) {
362 		/* saerrno.h now matches NFS error numbers. */
363 		return (ntohl(repl->errno));
364 	}
365 	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
366 	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
367 	return (0);
368 }
369 
370 #ifndef NFS_NOSYMLINK
371 /*
372  * Get the destination of a symbolic link.
373  */
374 int
375 nfs_readlink(struct nfs_iodesc *d, char *buf)
376 {
377 	struct {
378 		n_long	h[RPC_HEADER_WORDS];
379 		u_char fh[NFS_FHSIZE];
380 	} sdata;
381 	struct {
382 		n_long	h[RPC_HEADER_WORDS];
383 		struct nfs_readlnk_repl d;
384 	} rdata;
385 	ssize_t cc;
386 
387 #ifdef NFS_DEBUG
388 	if (debug)
389 		printf("readlink: called\n");
390 #endif
391 
392 	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
393 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
394 		      sdata.fh, NFS_FHSIZE,
395 		      &rdata.d, sizeof(rdata.d));
396 	if (cc == -1)
397 		return (errno);
398 
399 	if (cc < 4)
400 		return (EIO);
401 
402 	if (rdata.d.errno)
403 		return (ntohl(rdata.d.errno));
404 
405 	rdata.d.len = ntohl(rdata.d.len);
406 	if (rdata.d.len > NFS_MAXPATHLEN)
407 		return (ENAMETOOLONG);
408 
409 	bcopy(rdata.d.path, buf, rdata.d.len);
410 	buf[rdata.d.len] = 0;
411 	return (0);
412 }
413 #endif
414 
415 /*
416  * Read data from a file.
417  * Return transfer count or -1 (and set errno)
418  */
419 ssize_t
420 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
421 {
422 	struct nfs_read_args *args;
423 	struct nfs_read_repl *repl;
424 	struct {
425 		n_long	h[RPC_HEADER_WORDS];
426 		struct nfs_read_args d;
427 	} sdata;
428 	struct {
429 		n_long	h[RPC_HEADER_WORDS];
430 		struct nfs_read_repl d;
431 	} rdata;
432 	size_t cc;
433 	long x;
434 	int hlen, rlen;
435 
436 	args = &sdata.d;
437 	repl = &rdata.d;
438 
439 	bcopy(d->fh, args->fh, NFS_FHSIZE);
440 	args->off = htonl((n_long)off);
441 	if (len > nfs_read_size)
442 		len = nfs_read_size;
443 	args->len = htonl((n_long)len);
444 	args->xxx = htonl((n_long)0);
445 	hlen = offsetof(struct nfs_read_rpl, data[0]);
446 
447 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
448 	    args, sizeof(*args),
449 	    repl, sizeof(*repl));
450 	if (cc == -1) {
451 		/* errno was already set by rpc_call */
452 		return (-1);
453 	}
454 	if (cc < hlen) {
455 		errno = EBADRPC;
456 		return (-1);
457 	}
458 	if (repl->errno) {
459 		errno = ntohl(repl->errno);
460 		return (-1);
461 	}
462 	rlen = cc - hlen;
463 	x = ntohl(repl->count);
464 	if (rlen < x) {
465 		printf("nfsread: short packet, %d < %ld\n", rlen, x);
466 		errno = EBADRPC;
467 		return(-1);
468 	}
469 	bcopy(repl->data, addr, x);
470 	return (x);
471 }
472 
473 /*
474  * Open a file.
475  * return zero or error number
476  */
477 int
478 nfs_open(const char *upath, struct open_file *f)
479 {
480 	struct iodesc *desc;
481 	struct nfs_iodesc *currfd;
482 	char buf[2 * NFS_FHSIZE + 3];
483 	u_char *fh;
484 	char *cp;
485 	int i;
486 #ifndef NFS_NOSYMLINK
487 	struct nfs_iodesc *newfd;
488 	struct nfsv2_fattrs *fa;
489 	char *ncp;
490 	int c;
491 	char namebuf[NFS_MAXPATHLEN + 1];
492 	char linkbuf[NFS_MAXPATHLEN + 1];
493 	int nlinks = 0;
494 #endif
495 	int error;
496 	char *path;
497 
498 	if (netproto != NET_NFS)
499 		return (EINVAL);
500 
501 #ifdef NFS_DEBUG
502  	if (debug)
503  	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
504 #endif
505 	if (!rootpath[0]) {
506 		printf("no rootpath, no nfs\n");
507 		return (ENXIO);
508 	}
509 
510 	/*
511 	 * This is silly - we should look at dv_type but that value is
512 	 * arch dependant and we can't use it here.
513 	 */
514 #ifndef __i386__
515 	if (strcmp(f->f_dev->dv_name, "net") != 0)
516 		return(EINVAL);
517 #else
518 	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
519 		return(EINVAL);
520 #endif
521 
522 	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
523 		return(EINVAL);
524 
525 	/* Bind to a reserved port. */
526 	desc->myport = htons(--rpc_port);
527 	desc->destip = rootip;
528 	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
529 		return (error);
530 	nfs_root_node.fa.fa_type  = htonl(NFDIR);
531 	nfs_root_node.fa.fa_mode  = htonl(0755);
532 	nfs_root_node.fa.fa_nlink = htonl(2);
533 	nfs_root_node.iodesc = desc;
534 
535 	fh = &nfs_root_node.fh[0];
536 	buf[0] = 'X';
537 	cp = &buf[1];
538 	for (i = 0; i < NFS_FHSIZE; i++, cp += 2)
539 		sprintf(cp, "%02x", fh[i]);
540 	sprintf(cp, "X");
541 	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
542 	setenv("boot.nfsroot.path", rootpath, 1);
543 	setenv("boot.nfsroot.nfshandle", buf, 1);
544 
545 	/* Allocate file system specific data structure */
546 	currfd = malloc(sizeof(*newfd));
547 	if (currfd == NULL) {
548 		error = ENOMEM;
549 		goto out;
550 	}
551 
552 #ifndef NFS_NOSYMLINK
553 	bcopy(&nfs_root_node, currfd, sizeof(*currfd));
554 	newfd = NULL;
555 
556 	cp = path = strdup(upath);
557 	if (path == NULL) {
558 	    error = ENOMEM;
559 	    goto out;
560 	}
561 	while (*cp) {
562 		/*
563 		 * Remove extra separators
564 		 */
565 		while (*cp == '/')
566 			cp++;
567 
568 		if (*cp == '\0')
569 			break;
570 		/*
571 		 * Check that current node is a directory.
572 		 */
573 		if (currfd->fa.fa_type != htonl(NFDIR)) {
574 			error = ENOTDIR;
575 			goto out;
576 		}
577 
578 		/* allocate file system specific data structure */
579 		newfd = malloc(sizeof(*newfd));
580 		newfd->iodesc = currfd->iodesc;
581 
582 		/*
583 		 * Get next component of path name.
584 		 */
585 		{
586 			int len = 0;
587 
588 			ncp = cp;
589 			while ((c = *cp) != '\0' && c != '/') {
590 				if (++len > NFS_MAXNAMLEN) {
591 					error = ENOENT;
592 					goto out;
593 				}
594 				cp++;
595 			}
596 			*cp = '\0';
597 		}
598 
599 		/* lookup a file handle */
600 		error = nfs_lookupfh(currfd, ncp, newfd);
601 		*cp = c;
602 		if (error)
603 			goto out;
604 
605 		/*
606 		 * Check for symbolic link
607 		 */
608 		if (newfd->fa.fa_type == htonl(NFLNK)) {
609 			int link_len, len;
610 
611 			error = nfs_readlink(newfd, linkbuf);
612 			if (error)
613 				goto out;
614 
615 			link_len = strlen(linkbuf);
616 			len = strlen(cp);
617 
618 			if (link_len + len > MAXPATHLEN
619 			    || ++nlinks > MAXSYMLINKS) {
620 				error = ENOENT;
621 				goto out;
622 			}
623 
624 			bcopy(cp, &namebuf[link_len], len + 1);
625 			bcopy(linkbuf, namebuf, link_len);
626 
627 			/*
628 			 * If absolute pathname, restart at root.
629 			 * If relative pathname, restart at parent directory.
630 			 */
631 			cp = namebuf;
632 			if (*cp == '/')
633 				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
634 
635 			free(newfd);
636 			newfd = NULL;
637 
638 			continue;
639 		}
640 
641 		free(currfd);
642 		currfd = newfd;
643 		newfd = NULL;
644 	}
645 
646 	error = 0;
647 
648 out:
649 	free(newfd);
650 	free(path);
651 #else
652         currfd->iodesc = desc;
653 
654         error = nfs_lookupfh(&nfs_root_node, upath, currfd);
655 #endif
656 	if (!error) {
657 		currfd->off = 0;
658 		f->f_fsdata = (void *)currfd;
659 		return (0);
660 	}
661 
662 #ifdef NFS_DEBUG
663 	if (debug)
664 		printf("nfs_open: %s lookupfh failed: %s\n",
665 		    path, strerror(error));
666 #endif
667 	free(currfd);
668 
669 	return (error);
670 }
671 
672 int
673 nfs_close(struct open_file *f)
674 {
675 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
676 
677 #ifdef NFS_DEBUG
678 	if (debug)
679 		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
680 #endif
681 
682 	if (fp)
683 		free(fp);
684 	f->f_fsdata = (void *)0;
685 
686 	return (0);
687 }
688 
689 /*
690  * read a portion of a file
691  */
692 int
693 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
694 {
695 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
696 	ssize_t cc;
697 	char *addr = buf;
698 
699 #ifdef NFS_DEBUG
700 	if (debug)
701 		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
702 		       (int)fp->off);
703 #endif
704 	while ((int)size > 0) {
705 		twiddle(16);
706 		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
707 		/* XXX maybe should retry on certain errors */
708 		if (cc == -1) {
709 #ifdef NFS_DEBUG
710 			if (debug)
711 				printf("nfs_read: read: %s", strerror(errno));
712 #endif
713 			return (errno);	/* XXX - from nfs_readdata */
714 		}
715 		if (cc == 0) {
716 #ifdef NFS_DEBUG
717 			if (debug)
718 				printf("nfs_read: hit EOF unexpectantly");
719 #endif
720 			goto ret;
721 		}
722 		fp->off += cc;
723 		addr += cc;
724 		size -= cc;
725 	}
726 ret:
727 	if (resid)
728 		*resid = size;
729 
730 	return (0);
731 }
732 
733 /*
734  * Not implemented.
735  */
736 int
737 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
738 {
739 	return (EROFS);
740 }
741 
742 off_t
743 nfs_seek(struct open_file *f, off_t offset, int where)
744 {
745 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
746 	n_long size = ntohl(d->fa.fa_size);
747 
748 	switch (where) {
749 	case SEEK_SET:
750 		d->off = offset;
751 		break;
752 	case SEEK_CUR:
753 		d->off += offset;
754 		break;
755 	case SEEK_END:
756 		d->off = size - offset;
757 		break;
758 	default:
759 		errno = EINVAL;
760 		return (-1);
761 	}
762 
763 	return (d->off);
764 }
765 
766 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
767 int nfs_stat_types[8] = {
768 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
769 
770 int
771 nfs_stat(struct open_file *f, struct stat *sb)
772 {
773 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
774 	n_long ftype, mode;
775 
776 	ftype = ntohl(fp->fa.fa_type);
777 	mode  = ntohl(fp->fa.fa_mode);
778 	mode |= nfs_stat_types[ftype & 7];
779 
780 	sb->st_mode  = mode;
781 	sb->st_nlink = ntohl(fp->fa.fa_nlink);
782 	sb->st_uid   = ntohl(fp->fa.fa_uid);
783 	sb->st_gid   = ntohl(fp->fa.fa_gid);
784 	sb->st_size  = ntohl(fp->fa.fa_size);
785 
786 	return (0);
787 }
788 
789 static int
790 nfs_readdir(struct open_file *f, struct dirent *d)
791 {
792 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
793 	struct nfs_readdir_args *args;
794 	struct nfs_readdir_data *rd;
795 	struct nfs_readdir_off  *roff = NULL;
796 	static char *buf;
797 	static struct nfs_iodesc *pfp = NULL;
798 	static n_long cookie = 0;
799 	size_t cc;
800 	n_long eof;
801 
802 	struct {
803 		n_long h[RPC_HEADER_WORDS];
804 		struct nfs_readdir_args d;
805 	} sdata;
806 	static struct {
807 		n_long h[RPC_HEADER_WORDS];
808 		u_char d[NFS_READDIRSIZE];
809 	} rdata;
810 
811 	if (fp != pfp || fp->off != cookie) {
812 		pfp = NULL;
813 	refill:
814 		args = &sdata.d;
815 		bzero(args, sizeof(*args));
816 
817 		bcopy(fp->fh, args->fh, NFS_FHSIZE);
818 		args->cookie = htonl(fp->off);
819 		args->count  = htonl(NFS_READDIRSIZE);
820 
821 		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
822 			      args, sizeof(*args),
823 			      rdata.d, sizeof(rdata.d));
824 		buf  = rdata.d;
825 		roff = (struct nfs_readdir_off *)buf;
826 		if (ntohl(roff->cookie) != 0)
827 			return EIO;
828 		pfp = fp;
829 		cookie = fp->off;
830 	}
831 	roff = (struct nfs_readdir_off *)buf;
832 
833 	if (ntohl(roff->follows) == 0) {
834 		eof = ntohl((roff+1)->cookie);
835 		if (eof) {
836 			cookie = 0;
837 			return ENOENT;
838 		}
839 		goto refill;
840 	}
841 
842 	buf += sizeof(struct nfs_readdir_off);
843 	rd = (struct nfs_readdir_data *)buf;
844 	d->d_namlen = ntohl(rd->len);
845 	bcopy(rd->name, d->d_name, d->d_namlen);
846 	d->d_name[d->d_namlen] = '\0';
847 
848 	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
849 	roff = (struct nfs_readdir_off *)buf;
850 	fp->off = cookie = ntohl(roff->cookie);
851 	return 0;
852 }
853 #else	/* !OLD_NFSV2 */
854 /*
855  * Fetch the root file handle (call mount daemon)
856  * Return zero or error number.
857  */
858 int
859 nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
860 {
861 	int len;
862 	struct args {
863 		uint32_t len;
864 		char path[FNAME_SIZE];
865 	} *args;
866 	struct repl {
867 		uint32_t errno;
868 		uint32_t fhsize;
869 		u_char fh[NFS_V3MAXFHSIZE];
870 		uint32_t authcnt;
871 		uint32_t auth[7];
872 	} *repl;
873 	struct {
874 		uint32_t h[RPC_HEADER_WORDS];
875 		struct args d;
876 	} sdata;
877 	struct {
878 		uint32_t h[RPC_HEADER_WORDS];
879 		struct repl d;
880 	} rdata;
881 	size_t cc;
882 
883 #ifdef NFS_DEBUG
884 	if (debug)
885 		printf("nfs_getrootfh: %s\n", path);
886 #endif
887 
888 	args = &sdata.d;
889 	repl = &rdata.d;
890 
891 	bzero(args, sizeof(*args));
892 	len = strlen(path);
893 	if (len > sizeof(args->path))
894 		len = sizeof(args->path);
895 	args->len = htonl(len);
896 	bcopy(path, args->path, len);
897 	len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
898 
899 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
900 	    args, len, repl, sizeof(*repl));
901 	if (cc == -1)
902 		/* errno was set by rpc_call */
903 		return (errno);
904 	if (cc < 2 * sizeof (uint32_t))
905 		return (EBADRPC);
906 	if (repl->errno != 0)
907 		return (ntohl(repl->errno));
908 	*fhlenp = ntohl(repl->fhsize);
909 	bcopy(repl->fh, fhp, *fhlenp);
910 
911 	set_nfs_read_size();
912 	return (0);
913 }
914 
915 /*
916  * Lookup a file.  Store handle and attributes.
917  * Return zero or error number.
918  */
919 int
920 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
921 {
922 	int len, rlen, pos;
923 	struct args {
924 		uint32_t fhsize;
925 		uint32_t fhplusname[1 +
926 		    (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
927 	} *args;
928 	struct repl {
929 		uint32_t errno;
930 		uint32_t fhsize;
931 		uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
932 		    2 * (sizeof(uint32_t) +
933 		    sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
934 	} *repl;
935 	struct {
936 		uint32_t h[RPC_HEADER_WORDS];
937 		struct args d;
938 	} sdata;
939 	struct {
940 		uint32_t h[RPC_HEADER_WORDS];
941 		struct repl d;
942 	} rdata;
943 	ssize_t cc;
944 
945 #ifdef NFS_DEBUG
946 	if (debug)
947 		printf("lookupfh: called\n");
948 #endif
949 
950 	args = &sdata.d;
951 	repl = &rdata.d;
952 
953 	bzero(args, sizeof(*args));
954 	args->fhsize = htonl(d->fhsize);
955 	bcopy(d->fh, args->fhplusname, d->fhsize);
956 	len = strlen(name);
957 	if (len > FNAME_SIZE)
958 		len = FNAME_SIZE;
959 	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
960 	args->fhplusname[pos++] = htonl(len);
961 	bcopy(name, &args->fhplusname[pos], len);
962 	len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
963 	    roundup(len, sizeof(uint32_t));
964 
965 	rlen = sizeof(*repl);
966 
967 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
968 	    args, len, repl, rlen);
969 	if (cc == -1)
970 		return (errno);		/* XXX - from rpc_call */
971 	if (cc < 2 * sizeof(uint32_t))
972 		return (EIO);
973 	if (repl->errno != 0)
974 		/* saerrno.h now matches NFS error numbers. */
975 		return (ntohl(repl->errno));
976 	newfd->fhsize = ntohl(repl->fhsize);
977 	bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
978 	pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
979 	if (repl->fhplusattr[pos++] == 0)
980 		return (EIO);
981 	bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
982 	return (0);
983 }
984 
985 #ifndef NFS_NOSYMLINK
986 /*
987  * Get the destination of a symbolic link.
988  */
989 int
990 nfs_readlink(struct nfs_iodesc *d, char *buf)
991 {
992 	struct args {
993 		uint32_t fhsize;
994 		u_char fh[NFS_V3MAXFHSIZE];
995 	} *args;
996 	struct repl {
997 		uint32_t errno;
998 		uint32_t ok;
999 		struct nfsv3_fattrs fa;
1000 		uint32_t len;
1001 		u_char path[NFS_MAXPATHLEN];
1002 	} *repl;
1003 	struct {
1004 		uint32_t h[RPC_HEADER_WORDS];
1005 		struct args d;
1006 	} sdata;
1007 	struct {
1008 		uint32_t h[RPC_HEADER_WORDS];
1009 		struct repl d;
1010 	} rdata;
1011 	ssize_t cc;
1012 
1013 #ifdef NFS_DEBUG
1014 	if (debug)
1015 		printf("readlink: called\n");
1016 #endif
1017 
1018 	args = &sdata.d;
1019 	repl = &rdata.d;
1020 
1021 	bzero(args, sizeof(*args));
1022 	args->fhsize = htonl(d->fhsize);
1023 	bcopy(d->fh, args->fh, d->fhsize);
1024 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
1025 	    args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
1026 	    repl, sizeof(*repl));
1027 	if (cc == -1)
1028 		return (errno);
1029 
1030 	if (cc < 2 * sizeof(uint32_t))
1031 		return (EIO);
1032 
1033 	if (repl->errno != 0)
1034 		return (ntohl(repl->errno));
1035 
1036 	if (repl->ok == 0)
1037 		return (EIO);
1038 
1039 	repl->len = ntohl(repl->len);
1040 	if (repl->len > NFS_MAXPATHLEN)
1041 		return (ENAMETOOLONG);
1042 
1043 	bcopy(repl->path, buf, repl->len);
1044 	buf[repl->len] = 0;
1045 	return (0);
1046 }
1047 #endif
1048 
1049 /*
1050  * Read data from a file.
1051  * Return transfer count or -1 (and set errno)
1052  */
1053 ssize_t
1054 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
1055 {
1056 	struct args {
1057 		uint32_t fhsize;
1058 		uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
1059 	} *args;
1060 	struct repl {
1061 		uint32_t errno;
1062 		uint32_t ok;
1063 		struct nfsv3_fattrs fa;
1064 		uint32_t count;
1065 		uint32_t eof;
1066 		uint32_t len;
1067 		u_char data[NFSREAD_MAX_SIZE];
1068 	} *repl;
1069 	struct {
1070 		uint32_t h[RPC_HEADER_WORDS];
1071 		struct args d;
1072 	} sdata;
1073 	struct {
1074 		uint32_t h[RPC_HEADER_WORDS];
1075 		struct repl d;
1076 	} rdata;
1077 	size_t cc;
1078 	long x;
1079 	int hlen, rlen, pos;
1080 
1081 	args = &sdata.d;
1082 	repl = &rdata.d;
1083 
1084 	bzero(args, sizeof(*args));
1085 	args->fhsize = htonl(d->fhsize);
1086 	bcopy(d->fh, args->fhoffcnt, d->fhsize);
1087 	pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1088 	args->fhoffcnt[pos++] = 0;
1089 	args->fhoffcnt[pos++] = htonl((uint32_t)off);
1090 	if (len > nfs_read_size)
1091 		len = nfs_read_size;
1092 	args->fhoffcnt[pos] = htonl((uint32_t)len);
1093 	hlen = offsetof(struct repl, data[0]);
1094 
1095 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
1096 	    args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
1097 	    repl, sizeof(*repl));
1098 	if (cc == -1)
1099 		/* errno was already set by rpc_call */
1100 		return (-1);
1101 	if (cc < hlen) {
1102 		errno = EBADRPC;
1103 		return (-1);
1104 	}
1105 	if (repl->errno != 0) {
1106 		errno = ntohl(repl->errno);
1107 		return (-1);
1108 	}
1109 	rlen = cc - hlen;
1110 	x = ntohl(repl->count);
1111 	if (rlen < x) {
1112 		printf("nfsread: short packet, %d < %ld\n", rlen, x);
1113 		errno = EBADRPC;
1114 		return (-1);
1115 	}
1116 	bcopy(repl->data, addr, x);
1117 	return (x);
1118 }
1119 
1120 /*
1121  * Open a file.
1122  * return zero or error number
1123  */
1124 int
1125 nfs_open(const char *upath, struct open_file *f)
1126 {
1127 	struct iodesc *desc;
1128 	struct nfs_iodesc *currfd;
1129 	char buf[2 * NFS_V3MAXFHSIZE + 3];
1130 	u_char *fh;
1131 	char *cp;
1132 	int i;
1133 #ifndef NFS_NOSYMLINK
1134 	struct nfs_iodesc *newfd;
1135 	struct nfsv3_fattrs *fa;
1136 	char *ncp;
1137 	int c;
1138 	char namebuf[NFS_MAXPATHLEN + 1];
1139 	char linkbuf[NFS_MAXPATHLEN + 1];
1140 	int nlinks = 0;
1141 #endif
1142 	int error;
1143 	char *path;
1144 
1145 	if (netproto != NET_NFS)
1146 		return (EINVAL);
1147 
1148 #ifdef NFS_DEBUG
1149  	if (debug)
1150  	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
1151 #endif
1152 	if (!rootpath[0]) {
1153 		printf("no rootpath, no nfs\n");
1154 		return (ENXIO);
1155 	}
1156 
1157 	/*
1158 	 * This is silly - we should look at dv_type but that value is
1159 	 * arch dependant and we can't use it here.
1160 	 */
1161 #ifndef __i386__
1162 	if (strcmp(f->f_dev->dv_name, "net") != 0)
1163 		return (EINVAL);
1164 #else
1165 	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
1166 		return (EINVAL);
1167 #endif
1168 
1169 	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
1170 		return (EINVAL);
1171 
1172 	/* Bind to a reserved port. */
1173 	desc->myport = htons(--rpc_port);
1174 	desc->destip = rootip;
1175 	if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
1176 	    nfs_root_node.fh)))
1177 		return (error);
1178 	nfs_root_node.fa.fa_type  = htonl(NFDIR);
1179 	nfs_root_node.fa.fa_mode  = htonl(0755);
1180 	nfs_root_node.fa.fa_nlink = htonl(2);
1181 	nfs_root_node.iodesc = desc;
1182 
1183 	fh = &nfs_root_node.fh[0];
1184 	buf[0] = 'X';
1185 	cp = &buf[1];
1186 	for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
1187 		sprintf(cp, "%02x", fh[i]);
1188 	sprintf(cp, "X");
1189 	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
1190 	setenv("boot.nfsroot.path", rootpath, 1);
1191 	setenv("boot.nfsroot.nfshandle", buf, 1);
1192 	sprintf(buf, "%d", nfs_root_node.fhsize);
1193 	setenv("boot.nfsroot.nfshandlelen", buf, 1);
1194 
1195 	/* Allocate file system specific data structure */
1196 	currfd = malloc(sizeof(*newfd));
1197 	if (currfd == NULL) {
1198 		error = ENOMEM;
1199 		goto out;
1200 	}
1201 #ifndef NFS_NOSYMLINK
1202 	bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1203 	newfd = NULL;
1204 
1205 	cp = path = strdup(upath);
1206 	if (path == NULL) {
1207 		error = ENOMEM;
1208 		goto out;
1209 	}
1210 	while (*cp) {
1211 		/*
1212 		 * Remove extra separators
1213 		 */
1214 		while (*cp == '/')
1215 			cp++;
1216 
1217 		if (*cp == '\0')
1218 			break;
1219 		/*
1220 		 * Check that current node is a directory.
1221 		 */
1222 		if (currfd->fa.fa_type != htonl(NFDIR)) {
1223 			error = ENOTDIR;
1224 			goto out;
1225 		}
1226 
1227 		/* allocate file system specific data structure */
1228 		newfd = malloc(sizeof(*newfd));
1229 		if (newfd == NULL) {
1230 			error = ENOMEM;
1231 			goto out;
1232 		}
1233 		newfd->iodesc = currfd->iodesc;
1234 
1235 		/*
1236 		 * Get next component of path name.
1237 		 */
1238 		{
1239 			int len = 0;
1240 
1241 			ncp = cp;
1242 			while ((c = *cp) != '\0' && c != '/') {
1243 				if (++len > NFS_MAXNAMLEN) {
1244 					error = ENOENT;
1245 					goto out;
1246 				}
1247 				cp++;
1248 			}
1249 			*cp = '\0';
1250 		}
1251 
1252 		/* lookup a file handle */
1253 		error = nfs_lookupfh(currfd, ncp, newfd);
1254 		*cp = c;
1255 		if (error)
1256 			goto out;
1257 
1258 		/*
1259 		 * Check for symbolic link
1260 		 */
1261 		if (newfd->fa.fa_type == htonl(NFLNK)) {
1262 			int link_len, len;
1263 
1264 			error = nfs_readlink(newfd, linkbuf);
1265 			if (error)
1266 				goto out;
1267 
1268 			link_len = strlen(linkbuf);
1269 			len = strlen(cp);
1270 
1271 			if (link_len + len > MAXPATHLEN
1272 			    || ++nlinks > MAXSYMLINKS) {
1273 				error = ENOENT;
1274 				goto out;
1275 			}
1276 
1277 			bcopy(cp, &namebuf[link_len], len + 1);
1278 			bcopy(linkbuf, namebuf, link_len);
1279 
1280 			/*
1281 			 * If absolute pathname, restart at root.
1282 			 * If relative pathname, restart at parent directory.
1283 			 */
1284 			cp = namebuf;
1285 			if (*cp == '/')
1286 				bcopy(&nfs_root_node, currfd, sizeof(*currfd));
1287 
1288 			free(newfd);
1289 			newfd = NULL;
1290 
1291 			continue;
1292 		}
1293 
1294 		free(currfd);
1295 		currfd = newfd;
1296 		newfd = NULL;
1297 	}
1298 
1299 	error = 0;
1300 
1301 out:
1302 	free(newfd);
1303 	free(path);
1304 #else
1305 	currfd->iodesc = desc;
1306 
1307 	error = nfs_lookupfh(&nfs_root_node, upath, currfd);
1308 #endif
1309 	if (!error) {
1310 		currfd->off = 0;
1311 		currfd->cookie = 0;
1312 		f->f_fsdata = (void *)currfd;
1313 		return (0);
1314 	}
1315 
1316 #ifdef NFS_DEBUG
1317 	if (debug)
1318 		printf("nfs_open: %s lookupfh failed: %s\n",
1319 		    path, strerror(error));
1320 #endif
1321 	free(currfd);
1322 
1323 	return (error);
1324 }
1325 
1326 int
1327 nfs_close(struct open_file *f)
1328 {
1329 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1330 
1331 #ifdef NFS_DEBUG
1332 	if (debug)
1333 		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
1334 #endif
1335 
1336 	if (fp)
1337 		free(fp);
1338 	f->f_fsdata = (void *)0;
1339 
1340 	return (0);
1341 }
1342 
1343 /*
1344  * read a portion of a file
1345  */
1346 int
1347 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
1348 {
1349 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1350 	ssize_t cc;
1351 	char *addr = buf;
1352 
1353 #ifdef NFS_DEBUG
1354 	if (debug)
1355 		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
1356 		       (int)fp->off);
1357 #endif
1358 	while ((int)size > 0) {
1359 		twiddle(16);
1360 		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
1361 		/* XXX maybe should retry on certain errors */
1362 		if (cc == -1) {
1363 #ifdef NFS_DEBUG
1364 			if (debug)
1365 				printf("nfs_read: read: %s", strerror(errno));
1366 #endif
1367 			return (errno);	/* XXX - from nfs_readdata */
1368 		}
1369 		if (cc == 0) {
1370 #ifdef NFS_DEBUG
1371 			if (debug)
1372 				printf("nfs_read: hit EOF unexpectantly");
1373 #endif
1374 			goto ret;
1375 		}
1376 		fp->off += cc;
1377 		addr += cc;
1378 		size -= cc;
1379 	}
1380 ret:
1381 	if (resid)
1382 		*resid = size;
1383 
1384 	return (0);
1385 }
1386 
1387 /*
1388  * Not implemented.
1389  */
1390 int
1391 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
1392 {
1393 	return (EROFS);
1394 }
1395 
1396 off_t
1397 nfs_seek(struct open_file *f, off_t offset, int where)
1398 {
1399 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
1400 	uint32_t size = ntohl(d->fa.fa_size.val[1]);
1401 
1402 	switch (where) {
1403 	case SEEK_SET:
1404 		d->off = offset;
1405 		break;
1406 	case SEEK_CUR:
1407 		d->off += offset;
1408 		break;
1409 	case SEEK_END:
1410 		d->off = size - offset;
1411 		break;
1412 	default:
1413 		errno = EINVAL;
1414 		return (-1);
1415 	}
1416 
1417 	return (d->off);
1418 }
1419 
1420 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
1421 int nfs_stat_types[9] = {
1422 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
1423 
1424 int
1425 nfs_stat(struct open_file *f, struct stat *sb)
1426 {
1427 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1428 	uint32_t ftype, mode;
1429 
1430 	ftype = ntohl(fp->fa.fa_type);
1431 	mode  = ntohl(fp->fa.fa_mode);
1432 	mode |= nfs_stat_types[ftype & 7];
1433 
1434 	sb->st_mode  = mode;
1435 	sb->st_nlink = ntohl(fp->fa.fa_nlink);
1436 	sb->st_uid   = ntohl(fp->fa.fa_uid);
1437 	sb->st_gid   = ntohl(fp->fa.fa_gid);
1438 	sb->st_size  = ntohl(fp->fa.fa_size.val[1]);
1439 
1440 	return (0);
1441 }
1442 
1443 static int
1444 nfs_readdir(struct open_file *f, struct dirent *d)
1445 {
1446 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
1447 	struct nfsv3_readdir_repl *repl;
1448 	struct nfsv3_readdir_entry *rent;
1449 	static char *buf;
1450 	static struct nfs_iodesc *pfp = NULL;
1451 	static uint64_t cookie = 0;
1452 	size_t cc;
1453 	int pos;
1454 
1455 	struct args {
1456 		uint32_t fhsize;
1457 		uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
1458 	} *args;
1459 	struct {
1460 		uint32_t h[RPC_HEADER_WORDS];
1461 		struct args d;
1462 	} sdata;
1463 	static struct {
1464 		uint32_t h[RPC_HEADER_WORDS];
1465 		u_char d[NFS_READDIRSIZE];
1466 	} rdata;
1467 
1468 	if (fp != pfp || fp->off != cookie) {
1469 		pfp = NULL;
1470 	refill:
1471 		args = &sdata.d;
1472 		bzero(args, sizeof(*args));
1473 
1474 		args->fhsize = htonl(fp->fhsize);
1475 		bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
1476 		pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
1477 		args->fhpluscookie[pos++] = htonl(fp->off >> 32);
1478 		args->fhpluscookie[pos++] = htonl(fp->off);
1479 		args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
1480 		args->fhpluscookie[pos++] = htonl(fp->cookie);
1481 		args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
1482 
1483 		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
1484 		    args, 6 * sizeof(uint32_t) +
1485 		    roundup(fp->fhsize, sizeof(uint32_t)),
1486 		    rdata.d, sizeof(rdata.d));
1487 		buf  = rdata.d;
1488 		repl = (struct nfsv3_readdir_repl *)buf;
1489 		if (repl->errno != 0)
1490 			return (ntohl(repl->errno));
1491 		pfp = fp;
1492 		cookie = fp->off;
1493 		fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
1494 		    ntohl(repl->cookiev1);
1495 		buf += sizeof (struct nfsv3_readdir_repl);
1496 	}
1497 	rent = (struct nfsv3_readdir_entry *)buf;
1498 
1499 	if (rent->follows == 0) {
1500 		/* fid0 is actually eof */
1501 		if (rent->fid0 != 0) {
1502 			cookie = 0;
1503 			return (ENOENT);
1504 		}
1505 		goto refill;
1506 	}
1507 
1508 	d->d_namlen = ntohl(rent->len);
1509 	bcopy(rent->nameplus, d->d_name, d->d_namlen);
1510 	d->d_name[d->d_namlen] = '\0';
1511 
1512 	pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
1513 	fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
1514 	    ntohl(rent->nameplus[pos + 1]);
1515 	pos += 2;
1516 	buf = (u_char *)&rent->nameplus[pos];
1517 	return (0);
1518 }
1519 #endif	/* OLD_NFSV2 */
1520