]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/nfs.c
Implement pci_enable_msi() and pci_disable_msi() in the LinuxKPI.
[FreeBSD/FreeBSD.git] / stand / libsa / 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 #include <stddef.h>
40
41 #include <netinet/in.h>
42 #include <netinet/in_systm.h>
43
44 #include "rpcv2.h"
45 #include "nfsv2.h"
46
47 #include "stand.h"
48 #include "net.h"
49 #include "netif.h"
50 #include "rpc.h"
51
52 #define NFS_DEBUGxx
53
54 #define NFSREAD_MIN_SIZE 1024
55 #define NFSREAD_MAX_SIZE 16384
56
57 /* NFSv3 definitions */
58 #define NFS_V3MAXFHSIZE         64
59 #define NFS_VER3                3
60 #define RPCMNT_VER3             3
61 #define NFSPROCV3_LOOKUP        3
62 #define NFSPROCV3_READLINK      5
63 #define NFSPROCV3_READ          6
64 #define NFSPROCV3_READDIR       16
65
66 typedef struct {
67         uint32_t val[2];
68 } n_quad;
69
70 struct nfsv3_time {
71         uint32_t nfs_sec;
72         uint32_t nfs_nsec;
73 };
74
75 struct nfsv3_fattrs {
76         uint32_t fa_type;
77         uint32_t fa_mode;
78         uint32_t fa_nlink;
79         uint32_t fa_uid;
80         uint32_t fa_gid;
81         n_quad fa_size;
82         n_quad fa_used;
83         n_quad fa_rdev;
84         n_quad fa_fsid;
85         n_quad fa_fileid;
86         struct nfsv3_time fa_atime;
87         struct nfsv3_time fa_mtime;
88         struct nfsv3_time fa_ctime;
89 };
90
91 /*
92  * For NFSv3, the file handle is variable in size, so most fixed sized
93  * structures for arguments won't work. For most cases, a structure
94  * that starts with any fixed size section is followed by an array
95  * that covers the maximum size required.
96  */
97 struct nfsv3_readdir_repl {
98         uint32_t errno;
99         uint32_t ok;
100         struct nfsv3_fattrs fa;
101         uint32_t cookiev0;
102         uint32_t cookiev1;
103 };
104
105 struct nfsv3_readdir_entry {
106         uint32_t follows;
107         uint32_t fid0;
108         uint32_t fid1;
109         uint32_t len;
110         uint32_t nameplus[0];
111 };
112
113 struct nfs_iodesc {
114         struct iodesc *iodesc;
115         off_t off;
116         uint32_t fhsize;
117         u_char fh[NFS_V3MAXFHSIZE];
118         struct nfsv3_fattrs fa; /* all in network order */
119         uint64_t cookie;
120 };
121
122 /*
123  * XXX interactions with tftp? See nfswrapper.c for a confusing
124  *     issue.
125  */
126 int             nfs_open(const char *path, struct open_file *f);
127 static int      nfs_close(struct open_file *f);
128 static int      nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
129 static off_t    nfs_seek(struct open_file *f, off_t offset, int where);
130 static int      nfs_stat(struct open_file *f, struct stat *sb);
131 static int      nfs_readdir(struct open_file *f, struct dirent *d);
132
133 struct  nfs_iodesc nfs_root_node;
134
135 struct fs_ops nfs_fsops = {
136         "nfs",
137         nfs_open,
138         nfs_close,
139         nfs_read,
140         null_write,
141         nfs_seek,
142         nfs_stat,
143         nfs_readdir
144 };
145
146 static int nfs_read_size = NFSREAD_MIN_SIZE;
147
148 /*
149  * Improve boot performance over NFS
150  */
151 static void
152 set_nfs_read_size(void)
153 {
154         char *env, *end;
155         char buf[10];
156
157         if ((env = getenv("nfs.read_size")) != NULL) {
158                 errno = 0;
159                 nfs_read_size = (int)strtol(env, &end, 0);
160                 if (errno != 0 || *env == '\0' || *end != '\0') {
161                         printf("%s: bad value: \"%s\", defaulting to %d\n",
162                             "nfs.read_size", env, NFSREAD_MIN_SIZE);
163                         nfs_read_size = NFSREAD_MIN_SIZE;
164                 }
165         }
166         if (nfs_read_size < NFSREAD_MIN_SIZE) {
167                 printf("%s: bad value: \"%d\", defaulting to %d\n",
168                     "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
169                 nfs_read_size = NFSREAD_MIN_SIZE;
170         }
171         if (nfs_read_size > NFSREAD_MAX_SIZE) {
172                 printf("%s: bad value: \"%d\", defaulting to %d\n",
173                     "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
174                 nfs_read_size = NFSREAD_MAX_SIZE;
175         }
176         snprintf(buf, sizeof (buf), "%d", nfs_read_size);
177         setenv("nfs.read_size", buf, 1);
178 }
179
180 /*
181  * Fetch the root file handle (call mount daemon)
182  * Return zero or error number.
183  */
184 int
185 nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
186 {
187         void *pkt = NULL;
188         int len;
189         struct args {
190                 uint32_t len;
191                 char path[FNAME_SIZE];
192         } *args;
193         struct repl {
194                 uint32_t errno;
195                 uint32_t fhsize;
196                 u_char fh[NFS_V3MAXFHSIZE];
197                 uint32_t authcnt;
198                 uint32_t auth[7];
199         } *repl;
200         struct {
201                 uint32_t h[RPC_HEADER_WORDS];
202                 struct args d;
203         } sdata;
204         size_t cc;
205
206 #ifdef NFS_DEBUG
207         if (debug)
208                 printf("nfs_getrootfh: %s\n", path);
209 #endif
210
211         args = &sdata.d;
212
213         bzero(args, sizeof(*args));
214         len = strlen(path);
215         if (len > sizeof(args->path))
216                 len = sizeof(args->path);
217         args->len = htonl(len);
218         bcopy(path, args->path, len);
219         len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
220
221         cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
222             args, len, (void **)&repl, &pkt);
223         if (cc == -1) {
224                 free(pkt);
225                 /* errno was set by rpc_call */
226                 return (errno);
227         }
228         if (cc < 2 * sizeof (uint32_t)) {
229                 free(pkt);
230                 return (EBADRPC);
231         }
232         if (repl->errno != 0) {
233                 free(pkt);
234                 return (ntohl(repl->errno));
235         }
236         *fhlenp = ntohl(repl->fhsize);
237         bcopy(repl->fh, fhp, *fhlenp);
238
239         set_nfs_read_size();
240         free(pkt);
241         return (0);
242 }
243
244 /*
245  * Lookup a file.  Store handle and attributes.
246  * Return zero or error number.
247  */
248 int
249 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
250 {
251         void *pkt = NULL;
252         int len, pos;
253         struct args {
254                 uint32_t fhsize;
255                 uint32_t fhplusname[1 +
256                     (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
257         } *args;
258         struct repl {
259                 uint32_t errno;
260                 uint32_t fhsize;
261                 uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
262                     2 * (sizeof(uint32_t) +
263                     sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
264         } *repl;
265         struct {
266                 uint32_t h[RPC_HEADER_WORDS];
267                 struct args d;
268         } sdata;
269         ssize_t cc;
270
271 #ifdef NFS_DEBUG
272         if (debug)
273                 printf("lookupfh: called\n");
274 #endif
275
276         args = &sdata.d;
277
278         bzero(args, sizeof(*args));
279         args->fhsize = htonl(d->fhsize);
280         bcopy(d->fh, args->fhplusname, d->fhsize);
281         len = strlen(name);
282         if (len > FNAME_SIZE)
283                 len = FNAME_SIZE;
284         pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
285         args->fhplusname[pos++] = htonl(len);
286         bcopy(name, &args->fhplusname[pos], len);
287         len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
288             roundup(len, sizeof(uint32_t));
289
290         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
291             args, len, (void **)&repl, &pkt);
292         if (cc == -1) {
293                 free(pkt);
294                 return (errno);         /* XXX - from rpc_call */
295         }
296         if (cc < 2 * sizeof(uint32_t)) {
297                 free(pkt);
298                 return (EIO);
299         }
300         if (repl->errno != 0) {
301                 free(pkt);
302                 /* saerrno.h now matches NFS error numbers. */
303                 return (ntohl(repl->errno));
304         }
305         newfd->fhsize = ntohl(repl->fhsize);
306         bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
307         pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
308         if (repl->fhplusattr[pos++] == 0) {
309                 free(pkt);
310                 return (EIO);
311         }
312         bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
313         free(pkt);
314         return (0);
315 }
316
317 #ifndef NFS_NOSYMLINK
318 /*
319  * Get the destination of a symbolic link.
320  */
321 int
322 nfs_readlink(struct nfs_iodesc *d, char *buf)
323 {
324         void *pkt = NULL;
325         struct args {
326                 uint32_t fhsize;
327                 u_char fh[NFS_V3MAXFHSIZE];
328         } *args;
329         struct repl {
330                 uint32_t errno;
331                 uint32_t ok;
332                 struct nfsv3_fattrs fa;
333                 uint32_t len;
334                 u_char path[NFS_MAXPATHLEN];
335         } *repl;
336         struct {
337                 uint32_t h[RPC_HEADER_WORDS];
338                 struct args d;
339         } sdata;
340         ssize_t cc;
341         int rc = 0;
342
343 #ifdef NFS_DEBUG
344         if (debug)
345                 printf("readlink: called\n");
346 #endif
347
348         args = &sdata.d;
349
350         bzero(args, sizeof(*args));
351         args->fhsize = htonl(d->fhsize);
352         bcopy(d->fh, args->fh, d->fhsize);
353         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
354             args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
355             (void **)&repl, &pkt);
356         if (cc == -1)
357                 return (errno);
358
359         if (cc < 2 * sizeof(uint32_t)) {
360                 rc = EIO;
361                 goto done;
362         }
363
364         if (repl->errno != 0) {
365                 rc = ntohl(repl->errno);
366                 goto done;
367         }
368
369         if (repl->ok == 0) {
370                 rc = EIO;
371                 goto done;
372         }
373
374         repl->len = ntohl(repl->len);
375         if (repl->len > NFS_MAXPATHLEN) {
376                 rc = ENAMETOOLONG;
377                 goto done;
378         }
379
380         bcopy(repl->path, buf, repl->len);
381         buf[repl->len] = 0;
382 done:
383         free(pkt);
384         return (rc);
385 }
386 #endif
387
388 /*
389  * Read data from a file.
390  * Return transfer count or -1 (and set errno)
391  */
392 ssize_t
393 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
394 {
395         void *pkt = NULL;
396         struct args {
397                 uint32_t fhsize;
398                 uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
399         } *args;
400         struct repl {
401                 uint32_t errno;
402                 uint32_t ok;
403                 struct nfsv3_fattrs fa;
404                 uint32_t count;
405                 uint32_t eof;
406                 uint32_t len;
407                 u_char data[NFSREAD_MAX_SIZE];
408         } *repl;
409         struct {
410                 uint32_t h[RPC_HEADER_WORDS];
411                 struct args d;
412         } sdata;
413         size_t cc;
414         long x;
415         int hlen, rlen, pos;
416
417         args = &sdata.d;
418
419         bzero(args, sizeof(*args));
420         args->fhsize = htonl(d->fhsize);
421         bcopy(d->fh, args->fhoffcnt, d->fhsize);
422         pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
423         args->fhoffcnt[pos++] = 0;
424         args->fhoffcnt[pos++] = htonl((uint32_t)off);
425         if (len > nfs_read_size)
426                 len = nfs_read_size;
427         args->fhoffcnt[pos] = htonl((uint32_t)len);
428         hlen = offsetof(struct repl, data[0]);
429
430         cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
431             args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
432             (void **)&repl, &pkt);
433         if (cc == -1) {
434                 /* errno was already set by rpc_call */
435                 return (-1);
436         }
437         if (cc < hlen) {
438                 errno = EBADRPC;
439                 free(pkt);
440                 return (-1);
441         }
442         if (repl->errno != 0) {
443                 errno = ntohl(repl->errno);
444                 free(pkt);
445                 return (-1);
446         }
447         rlen = cc - hlen;
448         x = ntohl(repl->count);
449         if (rlen < x) {
450                 printf("nfsread: short packet, %d < %ld\n", rlen, x);
451                 errno = EBADRPC;
452                 free(pkt);
453                 return (-1);
454         }
455         bcopy(repl->data, addr, x);
456         free(pkt);
457         return (x);
458 }
459
460 /*
461  * Open a file.
462  * return zero or error number
463  */
464 int
465 nfs_open(const char *upath, struct open_file *f)
466 {
467         struct iodesc *desc;
468         struct nfs_iodesc *currfd = NULL;
469         char buf[2 * NFS_V3MAXFHSIZE + 3];
470         u_char *fh;
471         char *cp;
472         int i;
473 #ifndef NFS_NOSYMLINK
474         struct nfs_iodesc *newfd = NULL;
475         char *ncp;
476         int c;
477         char namebuf[NFS_MAXPATHLEN + 1];
478         char linkbuf[NFS_MAXPATHLEN + 1];
479         int nlinks = 0;
480 #endif
481         int error;
482         char *path = NULL;
483
484         if (netproto != NET_NFS)
485                 return (EINVAL);
486
487 #ifdef NFS_DEBUG
488         if (debug)
489             printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
490 #endif
491         if (!rootpath[0]) {
492                 printf("no rootpath, no nfs\n");
493                 return (ENXIO);
494         }
495
496         if (f->f_dev->dv_type != DEVT_NET)
497                 return (EINVAL);
498
499         if (!(desc = socktodesc(*(int *)(f->f_devdata))))
500                 return (EINVAL);
501
502         /* Bind to a reserved port. */
503         desc->myport = htons(--rpc_port);
504         desc->destip = rootip;
505         if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
506             nfs_root_node.fh)))
507                 return (error);
508         nfs_root_node.fa.fa_type  = htonl(NFDIR);
509         nfs_root_node.fa.fa_mode  = htonl(0755);
510         nfs_root_node.fa.fa_nlink = htonl(2);
511         nfs_root_node.iodesc = desc;
512
513         fh = &nfs_root_node.fh[0];
514         buf[0] = 'X';
515         cp = &buf[1];
516         for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
517                 sprintf(cp, "%02x", fh[i]);
518         sprintf(cp, "X");
519         setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
520         setenv("boot.nfsroot.path", rootpath, 1);
521         setenv("boot.nfsroot.nfshandle", buf, 1);
522         sprintf(buf, "%d", nfs_root_node.fhsize);
523         setenv("boot.nfsroot.nfshandlelen", buf, 1);
524
525         /* Allocate file system specific data structure */
526         currfd = malloc(sizeof(*newfd));
527         if (currfd == NULL) {
528                 error = ENOMEM;
529                 goto out;
530         }
531 #ifndef NFS_NOSYMLINK
532         bcopy(&nfs_root_node, currfd, sizeof(*currfd));
533         newfd = NULL;
534
535         cp = path = strdup(upath);
536         if (path == NULL) {
537                 error = ENOMEM;
538                 goto out;
539         }
540         while (*cp) {
541                 /*
542                  * Remove extra separators
543                  */
544                 while (*cp == '/')
545                         cp++;
546
547                 if (*cp == '\0')
548                         break;
549                 /*
550                  * Check that current node is a directory.
551                  */
552                 if (currfd->fa.fa_type != htonl(NFDIR)) {
553                         error = ENOTDIR;
554                         goto out;
555                 }
556
557                 /* allocate file system specific data structure */
558                 newfd = malloc(sizeof(*newfd));
559                 if (newfd == NULL) {
560                         error = ENOMEM;
561                         goto out;
562                 }
563                 newfd->iodesc = currfd->iodesc;
564
565                 /*
566                  * Get next component of path name.
567                  */
568                 {
569                         int len = 0;
570
571                         ncp = cp;
572                         while ((c = *cp) != '\0' && c != '/') {
573                                 if (++len > NFS_MAXNAMLEN) {
574                                         error = ENOENT;
575                                         goto out;
576                                 }
577                                 cp++;
578                         }
579                         *cp = '\0';
580                 }
581
582                 /* lookup a file handle */
583                 error = nfs_lookupfh(currfd, ncp, newfd);
584                 *cp = c;
585                 if (error)
586                         goto out;
587
588                 /*
589                  * Check for symbolic link
590                  */
591                 if (newfd->fa.fa_type == htonl(NFLNK)) {
592                         int link_len, len;
593
594                         error = nfs_readlink(newfd, linkbuf);
595                         if (error)
596                                 goto out;
597
598                         link_len = strlen(linkbuf);
599                         len = strlen(cp);
600
601                         if (link_len + len > MAXPATHLEN
602                             || ++nlinks > MAXSYMLINKS) {
603                                 error = ENOENT;
604                                 goto out;
605                         }
606
607                         bcopy(cp, &namebuf[link_len], len + 1);
608                         bcopy(linkbuf, namebuf, link_len);
609
610                         /*
611                          * If absolute pathname, restart at root.
612                          * If relative pathname, restart at parent directory.
613                          */
614                         cp = namebuf;
615                         if (*cp == '/')
616                                 bcopy(&nfs_root_node, currfd, sizeof(*currfd));
617
618                         free(newfd);
619                         newfd = NULL;
620
621                         continue;
622                 }
623
624                 free(currfd);
625                 currfd = newfd;
626                 newfd = NULL;
627         }
628
629         error = 0;
630
631 out:
632         free(newfd);
633         free(path);
634 #else
635         currfd->iodesc = desc;
636
637         error = nfs_lookupfh(&nfs_root_node, upath, currfd);
638 #endif
639         if (!error) {
640                 currfd->off = 0;
641                 currfd->cookie = 0;
642                 f->f_fsdata = (void *)currfd;
643                 return (0);
644         }
645
646 #ifdef NFS_DEBUG
647         if (debug)
648                 printf("nfs_open: %s lookupfh failed: %s\n",
649                     path, strerror(error));
650 #endif
651         free(currfd);
652
653         return (error);
654 }
655
656 int
657 nfs_close(struct open_file *f)
658 {
659         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
660
661 #ifdef NFS_DEBUG
662         if (debug)
663                 printf("nfs_close: fp=0x%lx\n", (u_long)fp);
664 #endif
665
666         free(fp);
667         f->f_fsdata = NULL;
668
669         return (0);
670 }
671
672 /*
673  * read a portion of a file
674  */
675 int
676 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
677 {
678         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
679         ssize_t cc;
680         char *addr = buf;
681
682 #ifdef NFS_DEBUG
683         if (debug)
684                 printf("nfs_read: size=%lu off=%d\n", (u_long)size,
685                        (int)fp->off);
686 #endif
687         while ((int)size > 0) {
688                 twiddle(16);
689                 cc = nfs_readdata(fp, fp->off, (void *)addr, size);
690                 /* XXX maybe should retry on certain errors */
691                 if (cc == -1) {
692 #ifdef NFS_DEBUG
693                         if (debug)
694                                 printf("nfs_read: read: %s", strerror(errno));
695 #endif
696                         return (errno); /* XXX - from nfs_readdata */
697                 }
698                 if (cc == 0) {
699 #ifdef NFS_DEBUG
700                         if (debug)
701                                 printf("nfs_read: hit EOF unexpectantly");
702 #endif
703                         goto ret;
704                 }
705                 fp->off += cc;
706                 addr += cc;
707                 size -= cc;
708         }
709 ret:
710         if (resid)
711                 *resid = size;
712
713         return (0);
714 }
715
716 off_t
717 nfs_seek(struct open_file *f, off_t offset, int where)
718 {
719         struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
720         uint32_t size = ntohl(d->fa.fa_size.val[1]);
721
722         switch (where) {
723         case SEEK_SET:
724                 d->off = offset;
725                 break;
726         case SEEK_CUR:
727                 d->off += offset;
728                 break;
729         case SEEK_END:
730                 d->off = size - offset;
731                 break;
732         default:
733                 errno = EINVAL;
734                 return (-1);
735         }
736
737         return (d->off);
738 }
739
740 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
741 int nfs_stat_types[9] = {
742         0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
743
744 int
745 nfs_stat(struct open_file *f, struct stat *sb)
746 {
747         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
748         uint32_t ftype, mode;
749
750         ftype = ntohl(fp->fa.fa_type);
751         mode  = ntohl(fp->fa.fa_mode);
752         mode |= nfs_stat_types[ftype & 7];
753
754         sb->st_mode  = mode;
755         sb->st_nlink = ntohl(fp->fa.fa_nlink);
756         sb->st_uid   = ntohl(fp->fa.fa_uid);
757         sb->st_gid   = ntohl(fp->fa.fa_gid);
758         sb->st_size  = ntohl(fp->fa.fa_size.val[1]);
759
760         return (0);
761 }
762
763 static int
764 nfs_readdir(struct open_file *f, struct dirent *d)
765 {
766         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
767         struct nfsv3_readdir_repl *repl;
768         struct nfsv3_readdir_entry *rent;
769         static void *pkt = NULL;
770         static char *buf;
771         static struct nfs_iodesc *pfp = NULL;
772         static uint64_t cookie = 0;
773         size_t cc;
774         int pos, rc;
775
776         struct args {
777                 uint32_t fhsize;
778                 uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
779         } *args;
780         struct {
781                 uint32_t h[RPC_HEADER_WORDS];
782                 struct args d;
783         } sdata;
784
785         if (fp != pfp || fp->off != cookie) {
786                 pfp = NULL;
787         refill:
788                 free(pkt);
789                 pkt = NULL;
790                 args = &sdata.d;
791                 bzero(args, sizeof(*args));
792
793                 args->fhsize = htonl(fp->fhsize);
794                 bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
795                 pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
796                 args->fhpluscookie[pos++] = htonl(fp->off >> 32);
797                 args->fhpluscookie[pos++] = htonl(fp->off);
798                 args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
799                 args->fhpluscookie[pos++] = htonl(fp->cookie);
800                 args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
801
802                 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
803                     args, 6 * sizeof(uint32_t) +
804                     roundup(fp->fhsize, sizeof(uint32_t)),
805                     (void **)&buf, &pkt);
806                 if (cc == -1) {
807                         rc = errno;
808                         goto err;
809                 }
810                 repl = (struct nfsv3_readdir_repl *)buf;
811                 if (repl->errno != 0) {
812                         rc = ntohl(repl->errno);
813                         goto err;
814                 }
815                 pfp = fp;
816                 cookie = fp->off;
817                 fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
818                     ntohl(repl->cookiev1);
819                 buf += sizeof (struct nfsv3_readdir_repl);
820         }
821         rent = (struct nfsv3_readdir_entry *)buf;
822
823         if (rent->follows == 0) {
824                 /* fid0 is actually eof */
825                 if (rent->fid0 != 0) {
826                         rc = ENOENT;
827                         goto err;
828                 }
829                 goto refill;
830         }
831
832         d->d_namlen = ntohl(rent->len);
833         bcopy(rent->nameplus, d->d_name, d->d_namlen);
834         d->d_name[d->d_namlen] = '\0';
835
836         pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
837         fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
838             ntohl(rent->nameplus[pos + 1]);
839         pos += 2;
840         buf = (char *)&rent->nameplus[pos];
841         return (0);
842
843 err:
844         free(pkt);
845         pkt = NULL;
846         pfp = NULL;
847         cookie = 0;
848         return (rc);
849 }