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