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