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