nfs.c revision 65496
138774Snsouch/* $FreeBSD: head/lib/libstand/nfs.c 65496 2000-09-05 22:11:41Z msmith $ */
293023Snsouch/*	$NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $	*/
338774Snsouch
438774Snsouch/*-
538774Snsouch *  Copyright (c) 1993 John Brezak
638774Snsouch *  All rights reserved.
738774Snsouch *
838774Snsouch *  Redistribution and use in source and binary forms, with or without
938774Snsouch *  modification, are permitted provided that the following conditions
1038774Snsouch *  are met:
1138774Snsouch *  1. Redistributions of source code must retain the above copyright
1238774Snsouch *     notice, this list of conditions and the following disclaimer.
1338774Snsouch *  2. Redistributions in binary form must reproduce the above copyright
1438774Snsouch *     notice, this list of conditions and the following disclaimer in the
1538774Snsouch *     documentation and/or other materials provided with the distribution.
1638774Snsouch *  3. The name of the author may not be used to endorse or promote products
1738774Snsouch *     derived from this software without specific prior written permission.
1838774Snsouch *
1938774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
2038774Snsouch * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2138774Snsouch * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2238774Snsouch * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2338774Snsouch * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2438774Snsouch * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2538774Snsouch * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2638774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27119418Sobrien * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28119418Sobrien * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29119418Sobrien * POSSIBILITY OF SUCH DAMAGE.
3038774Snsouch */
3138774Snsouch
3238774Snsouch#include <sys/param.h>
3338774Snsouch#include <sys/time.h>
3438774Snsouch#include <sys/socket.h>
3538774Snsouch#include <sys/stat.h>
3638774Snsouch#include <string.h>
37181304Sjhb
38167856Simp#include <netinet/in.h>
3938774Snsouch#include <netinet/in_systm.h>
40181304Sjhb
4138774Snsouch#include "rpcv2.h"
4238774Snsouch#include "nfsv2.h"
4338774Snsouch
4438774Snsouch#include "stand.h"
4538774Snsouch#include "net.h"
4638774Snsouch#include "netif.h"
4738774Snsouch#include "rpc.h"
48129152Sjoerg
49129152Sjoerg#define NFS_DEBUGxx
50129152Sjoerg
5140782Snsouch/* Define our own NFS attributes without NQNFS stuff. */
5240782Snsouchstruct nfsv2_fattrs {
5340782Snsouch	n_long	fa_type;
54160372Simp	n_long	fa_mode;
5542442Snsouch	n_long	fa_nlink;
56186833Snwhitehorn	n_long	fa_uid;
57186833Snwhitehorn	n_long	fa_gid;
58187457Snwhitehorn	n_long	fa_size;
5940782Snsouch	n_long	fa_blocksize;
6040782Snsouch	n_long	fa_rdev;
61129152Sjoerg	n_long	fa_blocks;
6240782Snsouch	n_long	fa_fsid;
6340782Snsouch	n_long	fa_fileid;
6440782Snsouch	struct nfsv2_time fa_atime;
6540782Snsouch	struct nfsv2_time fa_mtime;
6640782Snsouch	struct nfsv2_time fa_ctime;
6740782Snsouch};
6840782Snsouch
6940782Snsouch
7040782Snsouchstruct nfs_read_args {
7140782Snsouch	u_char	fh[NFS_FHSIZE];
7240782Snsouch	n_long	off;
7340782Snsouch	n_long	len;
7440782Snsouch	n_long	xxx;			/* XXX what's this for? */
7540782Snsouch};
7640782Snsouch
7740782Snsouch/* Data part of nfs rpc reply (also the largest thing we receive) */
7840782Snsouch#define NFSREAD_SIZE 1024
7940782Snsouchstruct nfs_read_repl {
8040782Snsouch	n_long	errno;
8140782Snsouch	struct	nfsv2_fattrs fa;
8242442Snsouch	n_long	count;
8340782Snsouch	u_char	data[NFSREAD_SIZE];
8438774Snsouch};
8540782Snsouch
8640782Snsouch#ifndef NFS_NOSYMLINK
8738774Snsouchstruct nfs_readlnk_repl {
8838774Snsouch	n_long	errno;
8940782Snsouch	n_long	len;
9038774Snsouch	char	path[NFS_MAXPATHLEN];
91129152Sjoerg};
92129152Sjoerg#endif
93129152Sjoerg
94167856Simpstruct nfs_readdir_args {
95129152Sjoerg	u_char	fh[NFS_FHSIZE];
96167856Simp	n_long	cookie;
97181304Sjhb	n_long	count;
9840782Snsouch};
9938774Snsouch
10042442Snsouchstruct nfs_readdir_data {
10142442Snsouch	n_long	fileid;
10242442Snsouch	n_long	len;
10342442Snsouch	char	name[0];
10442442Snsouch};
105129152Sjoerg
10640782Snsouchstruct nfs_readdir_off {
10738774Snsouch	n_long	cookie;
10840782Snsouch	n_long	follows;
109129152Sjoerg};
11040782Snsouch
11140782Snsouchstruct nfs_iodesc {
11240782Snsouch	struct	iodesc	*iodesc;
11340782Snsouch	off_t	off;
11440782Snsouch	u_char	fh[NFS_FHSIZE];
11542442Snsouch	struct nfsv2_fattrs fa;	/* all in network order */
116181304Sjhb};
117167856Simp
11838774Snsouch/*
11938774Snsouch * XXX interactions with tftp? See nfswrapper.c for a confusing
12038774Snsouch *     issue.
12193023Snsouch */
12293023Snsouchint		nfs_open(const char *path, struct open_file *f);
12393023Snsouchstatic int	nfs_close(struct open_file *f);
12493023Snsouchstatic int	nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
125181304Sjhbstatic int	nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
126160372Simpstatic off_t	nfs_seek(struct open_file *f, off_t offset, int where);
12793023Snsouchstatic int	nfs_stat(struct open_file *f, struct stat *sb);
12893023Snsouchstatic int	nfs_readdir(struct open_file *f, struct dirent *d);
129181304Sjhb
13093023Snsouchstruct	nfs_iodesc nfs_root_node;
13193023Snsouch
13293023Snsouchstruct fs_ops nfs_fsops = {
13393023Snsouch	"nfs",
134167856Simp	nfs_open,
13593023Snsouch	nfs_close,
136167856Simp	nfs_read,
137167856Simp	nfs_write,
138160372Simp	nfs_seek,
139167856Simp	nfs_stat,
140167856Simp	nfs_readdir
141167856Simp};
142167856Simp
143167856Simp/*
144167856Simp * Fetch the root file handle (call mount daemon)
145167856Simp * Return zero or error number.
146167856Simp */
147167856Simpint
148167856Simpnfs_getrootfh(d, path, fhp)
149167856Simp	register struct iodesc *d;
150167856Simp	char *path;
151167856Simp	u_char *fhp;
152167856Simp{
153167856Simp	register int len;
154167856Simp	struct args {
155167856Simp		n_long	len;
156167856Simp		char	path[FNAME_SIZE];
157167856Simp	} *args;
158167856Simp	struct repl {
159167856Simp		n_long	errno;
160167856Simp		u_char	fh[NFS_FHSIZE];
161167856Simp	} *repl;
162167856Simp	struct {
163167856Simp		n_long	h[RPC_HEADER_WORDS];
16493023Snsouch		struct args d;
16593023Snsouch	} sdata;
16693023Snsouch	struct {
167167856Simp		n_long	h[RPC_HEADER_WORDS];
168167856Simp		struct repl d;
169167856Simp	} rdata;
170167856Simp	size_t cc;
171167856Simp
172167856Simp#ifdef NFS_DEBUG
173167856Simp	if (debug)
174167856Simp		printf("nfs_getrootfh: %s\n", path);
175167856Simp#endif
176188461Simp
177167856Simp	args = &sdata.d;
178167856Simp	repl = &rdata.d;
179167856Simp
180167856Simp	bzero(args, sizeof(*args));
181167856Simp	len = strlen(path);
182167856Simp	if (len > sizeof(args->path))
183167856Simp		len = sizeof(args->path);
184167856Simp	args->len = htonl(len);
185167856Simp	bcopy(path, args->path, len);
186167856Simp	len = 4 + roundup(len, 4);
187167856Simp
188167856Simp	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
189167856Simp	    args, len, repl, sizeof(*repl));
190167856Simp	if (cc == -1) {
191167856Simp		/* errno was set by rpc_call */
192167856Simp		return (errno);
193167856Simp	}
194167856Simp	if (cc < 4)
195167856Simp		return (EBADRPC);
196167856Simp	if (repl->errno)
197167856Simp		return (ntohl(repl->errno));
198167856Simp	bcopy(repl->fh, fhp, sizeof(repl->fh));
199167856Simp	return (0);
200167856Simp}
201167856Simp
202167856Simp/*
203167856Simp * Lookup a file.  Store handle and attributes.
204167856Simp * Return zero or error number.
205167856Simp */
206167856Simpint
207167856Simpnfs_lookupfh(d, name, newfd)
208167856Simp	struct nfs_iodesc *d;
209167856Simp	const char *name;
210167856Simp	struct nfs_iodesc *newfd;
211167856Simp{
212167856Simp	register int len, rlen;
213167856Simp	struct args {
214167856Simp		u_char	fh[NFS_FHSIZE];
215167856Simp		n_long	len;
216167856Simp		char	name[FNAME_SIZE];
217167856Simp	} *args;
218167856Simp	struct repl {
21938774Snsouch		n_long	errno;
22038774Snsouch		u_char	fh[NFS_FHSIZE];
22138774Snsouch		struct	nfsv2_fattrs fa;
222160372Simp	} *repl;
22338774Snsouch	struct {
22438774Snsouch		n_long	h[RPC_HEADER_WORDS];
22538774Snsouch		struct args d;
22640782Snsouch	} sdata;
22740782Snsouch	struct {
22840782Snsouch		n_long	h[RPC_HEADER_WORDS];
229160372Simp		struct repl d;
23040782Snsouch	} rdata;
23140782Snsouch	ssize_t cc;
23240782Snsouch
23340782Snsouch#ifdef NFS_DEBUG
23440782Snsouch	if (debug)
23540782Snsouch		printf("lookupfh: called\n");
236160372Simp#endif
23740782Snsouch
23840782Snsouch	args = &sdata.d;
23940782Snsouch	repl = &rdata.d;
240167856Simp
241167856Simp	bzero(args, sizeof(*args));
242167856Simp	bcopy(d->fh, args->fh, sizeof(args->fh));
243167856Simp	len = strlen(name);
244167856Simp	if (len > sizeof(args->name))
245167856Simp		len = sizeof(args->name);
246167856Simp	bcopy(name, args->name, len);
247167856Simp	args->len = htonl(len);
248167856Simp	len = 4 + roundup(len, 4);
249167856Simp	len += NFS_FHSIZE;
250167856Simp
251167856Simp	rlen = sizeof(*repl);
252167856Simp
253167856Simp	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
254167856Simp	    args, len, repl, rlen);
255167856Simp	if (cc == -1)
256167856Simp		return (errno);		/* XXX - from rpc_call */
257167856Simp	if (cc < 4)
258167856Simp		return (EIO);
259167856Simp	if (repl->errno) {
260167856Simp		/* saerrno.h now matches NFS error numbers. */
261167856Simp		return (ntohl(repl->errno));
262167856Simp	}
263167856Simp	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
264167856Simp	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
265167856Simp	return (0);
266167856Simp}
267167856Simp
268167856Simp#ifndef NFS_NOSYMLINK
269167856Simp/*
27093023Snsouch * Get the destination of a symbolic link.
271187261Snwhitehorn */
272187261Snwhitehornint
273nfs_readlink(d, buf)
274	struct nfs_iodesc *d;
275	char *buf;
276{
277	struct {
278		n_long	h[RPC_HEADER_WORDS];
279		u_char fh[NFS_FHSIZE];
280	} sdata;
281	struct {
282		n_long	h[RPC_HEADER_WORDS];
283		struct nfs_readlnk_repl d;
284	} rdata;
285	ssize_t cc;
286
287#ifdef NFS_DEBUG
288	if (debug)
289		printf("readlink: called\n");
290#endif
291
292	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
293	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
294		      sdata.fh, NFS_FHSIZE,
295		      &rdata.d, sizeof(rdata.d));
296	if (cc == -1)
297		return (errno);
298
299	if (cc < 4)
300		return (EIO);
301
302	if (rdata.d.errno)
303		return (ntohl(rdata.d.errno));
304
305	rdata.d.len = ntohl(rdata.d.len);
306	if (rdata.d.len > NFS_MAXPATHLEN)
307		return (ENAMETOOLONG);
308
309	bcopy(rdata.d.path, buf, rdata.d.len);
310	buf[rdata.d.len] = 0;
311	return (0);
312}
313#endif
314
315/*
316 * Read data from a file.
317 * Return transfer count or -1 (and set errno)
318 */
319ssize_t
320nfs_readdata(d, off, addr, len)
321	struct nfs_iodesc *d;
322	off_t off;
323	void *addr;
324	size_t len;
325{
326	struct nfs_read_args *args;
327	struct nfs_read_repl *repl;
328	struct {
329		n_long	h[RPC_HEADER_WORDS];
330		struct nfs_read_args d;
331	} sdata;
332	struct {
333		n_long	h[RPC_HEADER_WORDS];
334		struct nfs_read_repl d;
335	} rdata;
336	size_t cc;
337	long x;
338	int hlen, rlen;
339
340	args = &sdata.d;
341	repl = &rdata.d;
342
343	bcopy(d->fh, args->fh, NFS_FHSIZE);
344	args->off = htonl((n_long)off);
345	if (len > NFSREAD_SIZE)
346		len = NFSREAD_SIZE;
347	args->len = htonl((n_long)len);
348	args->xxx = htonl((n_long)0);
349	hlen = sizeof(*repl) - NFSREAD_SIZE;
350
351	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
352	    args, sizeof(*args),
353	    repl, sizeof(*repl));
354	if (cc == -1) {
355		/* errno was already set by rpc_call */
356		return (-1);
357	}
358	if (cc < hlen) {
359		errno = EBADRPC;
360		return (-1);
361	}
362	if (repl->errno) {
363		errno = ntohl(repl->errno);
364		return (-1);
365	}
366	rlen = cc - hlen;
367	x = ntohl(repl->count);
368	if (rlen < x) {
369		printf("nfsread: short packet, %d < %ld\n", rlen, x);
370		errno = EBADRPC;
371		return(-1);
372	}
373	bcopy(repl->data, addr, x);
374	return (x);
375}
376
377/*
378 * Open a file.
379 * return zero or error number
380 */
381int
382nfs_open(upath, f)
383	const char *upath;
384	struct open_file *f;
385{
386	struct iodesc *desc;
387	struct nfs_iodesc *currfd;
388#ifndef NFS_NOSYMLINK
389	struct nfs_iodesc *newfd;
390	struct nfsv2_fattrs *fa;
391	register char *cp, *ncp;
392	register int c;
393	char namebuf[NFS_MAXPATHLEN + 1];
394	char linkbuf[NFS_MAXPATHLEN + 1];
395	int nlinks = 0;
396#endif
397	int error;
398	char *path;
399
400#ifdef NFS_DEBUG
401 	if (debug)
402 	    printf("nfs_open: %s (rootpath=%s)\n", path, rootpath);
403#endif
404	if (!rootpath[0]) {
405		printf("no rootpath, no nfs\n");
406		return (ENXIO);
407	}
408
409	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
410		return(EINVAL);
411
412	/* Bind to a reserved port. */
413	desc->myport = htons(--rpc_port);
414	desc->destip = rootip;
415	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
416		return (error);
417	nfs_root_node.iodesc = desc;
418
419#ifndef NFS_NOSYMLINK
420	/* Fake up attributes for the root dir. */
421	fa = &nfs_root_node.fa;
422	fa->fa_type  = htonl(NFDIR);
423	fa->fa_mode  = htonl(0755);
424	fa->fa_nlink = htonl(2);
425
426	currfd = &nfs_root_node;
427	newfd = 0;
428
429	cp = path = strdup(upath);
430	if (path == NULL) {
431	    error = ENOMEM;
432	    goto out;
433	}
434	while (*cp) {
435		/*
436		 * Remove extra separators
437		 */
438		while (*cp == '/')
439			cp++;
440
441		if (*cp == '\0')
442			break;
443		/*
444		 * Check that current node is a directory.
445		 */
446		if (currfd->fa.fa_type != htonl(NFDIR)) {
447			error = ENOTDIR;
448			goto out;
449		}
450
451		/* allocate file system specific data structure */
452		newfd = malloc(sizeof(*newfd));
453		newfd->iodesc = currfd->iodesc;
454		newfd->off = 0;
455
456		/*
457		 * Get next component of path name.
458		 */
459		{
460			register int len = 0;
461
462			ncp = cp;
463			while ((c = *cp) != '\0' && c != '/') {
464				if (++len > NFS_MAXNAMLEN) {
465					error = ENOENT;
466					goto out;
467				}
468				cp++;
469			}
470			*cp = '\0';
471		}
472
473		/* lookup a file handle */
474		error = nfs_lookupfh(currfd, ncp, newfd);
475		*cp = c;
476		if (error)
477			goto out;
478
479		/*
480		 * Check for symbolic link
481		 */
482		if (newfd->fa.fa_type == htonl(NFLNK)) {
483			int link_len, len;
484
485			error = nfs_readlink(newfd, linkbuf);
486			if (error)
487				goto out;
488
489			link_len = strlen(linkbuf);
490			len = strlen(cp);
491
492			if (link_len + len > MAXPATHLEN
493			    || ++nlinks > MAXSYMLINKS) {
494				error = ENOENT;
495				goto out;
496			}
497
498			bcopy(cp, &namebuf[link_len], len + 1);
499			bcopy(linkbuf, namebuf, link_len);
500
501			/*
502			 * If absolute pathname, restart at root.
503			 * If relative pathname, restart at parent directory.
504			 */
505			cp = namebuf;
506			if (*cp == '/') {
507				if (currfd != &nfs_root_node)
508					free(currfd);
509				currfd = &nfs_root_node;
510			}
511
512			free(newfd);
513			newfd = 0;
514
515			continue;
516		}
517
518		if (currfd != &nfs_root_node)
519			free(currfd);
520		currfd = newfd;
521		newfd = 0;
522	}
523
524	error = 0;
525
526out:
527	if (newfd)
528		free(newfd);
529	if (path)
530		free(path);
531#else
532        /* allocate file system specific data structure */
533        currfd = malloc(sizeof(*currfd));
534        currfd->iodesc = desc;
535        currfd->off = 0;
536
537        error = nfs_lookupfh(&nfs_root_node, upath, currfd);
538#endif
539	if (!error) {
540		f->f_fsdata = (void *)currfd;
541		return (0);
542	}
543
544#ifdef NFS_DEBUG
545	if (debug)
546		printf("nfs_open: %s lookupfh failed: %s\n",
547		    path, strerror(error));
548#endif
549#ifndef NFS_NOSYMLINK
550	if (currfd != &nfs_root_node)
551#endif
552		free(currfd);
553
554	return (error);
555}
556
557int
558nfs_close(f)
559	struct open_file *f;
560{
561	register struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
562
563#ifdef NFS_DEBUG
564	if (debug)
565		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
566#endif
567
568	if (fp != &nfs_root_node && fp)
569		free(fp);
570	f->f_fsdata = (void *)0;
571
572	return (0);
573}
574
575/*
576 * read a portion of a file
577 */
578int
579nfs_read(f, buf, size, resid)
580	struct open_file *f;
581	void *buf;
582	size_t size;
583	size_t *resid;	/* out */
584{
585	register struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
586	register ssize_t cc;
587	register char *addr = buf;
588
589#ifdef NFS_DEBUG
590	if (debug)
591		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
592		       (int)fp->off);
593#endif
594	while ((int)size > 0) {
595		twiddle();
596		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
597		/* XXX maybe should retry on certain errors */
598		if (cc == -1) {
599#ifdef NFS_DEBUG
600			if (debug)
601				printf("nfs_read: read: %s", strerror(errno));
602#endif
603			return (errno);	/* XXX - from nfs_readdata */
604		}
605		if (cc == 0) {
606#ifdef NFS_DEBUG
607			if (debug)
608				printf("nfs_read: hit EOF unexpectantly");
609#endif
610			goto ret;
611		}
612		fp->off += cc;
613		addr += cc;
614		size -= cc;
615	}
616ret:
617	if (resid)
618		*resid = size;
619
620	return (0);
621}
622
623/*
624 * Not implemented.
625 */
626int
627nfs_write(f, buf, size, resid)
628	struct open_file *f;
629	void *buf;
630	size_t size;
631	size_t *resid;	/* out */
632{
633	return (EROFS);
634}
635
636off_t
637nfs_seek(f, offset, where)
638	struct open_file *f;
639	off_t offset;
640	int where;
641{
642	register struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
643	n_long size = ntohl(d->fa.fa_size);
644
645	switch (where) {
646	case SEEK_SET:
647		d->off = offset;
648		break;
649	case SEEK_CUR:
650		d->off += offset;
651		break;
652	case SEEK_END:
653		d->off = size - offset;
654		break;
655	default:
656		return (-1);
657	}
658
659	return (d->off);
660}
661
662/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
663int nfs_stat_types[8] = {
664	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
665
666int
667nfs_stat(f, sb)
668	struct open_file *f;
669	struct stat *sb;
670{
671	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
672	register n_long ftype, mode;
673
674	ftype = ntohl(fp->fa.fa_type);
675	mode  = ntohl(fp->fa.fa_mode);
676	mode |= nfs_stat_types[ftype & 7];
677
678	sb->st_mode  = mode;
679	sb->st_nlink = ntohl(fp->fa.fa_nlink);
680	sb->st_uid   = ntohl(fp->fa.fa_uid);
681	sb->st_gid   = ntohl(fp->fa.fa_gid);
682	sb->st_size  = ntohl(fp->fa.fa_size);
683
684	return (0);
685}
686
687static int
688nfs_readdir(struct open_file *f, struct dirent *d)
689{
690	register struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
691	struct nfs_readdir_args *args;
692	struct nfs_readdir_data *rd;
693	struct nfs_readdir_off  *roff = NULL;
694	static char *buf;
695	static n_long cookie = 0;
696	size_t cc;
697	n_long eof;
698
699	struct {
700		n_long h[RPC_HEADER_WORDS];
701		struct nfs_readdir_args d;
702	} sdata;
703	static struct {
704		n_long h[RPC_HEADER_WORDS];
705		u_char d[NFS_READDIRSIZE];
706	} rdata;
707
708	if (cookie == 0) {
709	refill:
710		args = &sdata.d;
711		bzero(args, sizeof(*args));
712
713		bcopy(fp->fh, args->fh, NFS_FHSIZE);
714		args->cookie = htonl(cookie);
715		args->count  = htonl(NFS_READDIRSIZE);
716
717		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
718			      args, sizeof(*args),
719			      rdata.d, sizeof(rdata.d));
720		buf  = rdata.d;
721		roff = (struct nfs_readdir_off *)buf;
722		if (ntohl(roff->cookie) != 0)
723			return 1;
724	}
725	roff = (struct nfs_readdir_off *)buf;
726
727	if (ntohl(roff->follows) == 0) {
728		eof = ntohl((roff+1)->cookie);
729		if (eof) {
730			cookie = 0;
731			return 1;
732		}
733		goto refill;
734	}
735
736	buf += sizeof(struct nfs_readdir_off);
737	rd = (struct nfs_readdir_data *)buf;
738	d->d_namlen = ntohl(rd->len);
739	bcopy(rd->name, d->d_name, d->d_namlen);
740	d->d_name[d->d_namlen] = '\0';
741
742	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
743	roff = (struct nfs_readdir_off *)buf;
744	cookie = ntohl(roff->cookie);
745	return 0;
746}
747