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