9708 loader: libstand warning: pointer targets differ in signedness
[unleashed.git] / usr / src / boot / lib / libstand / nfs.c
blob1ca0547c9df89af866583d3ed6b1cd39631687e1
1 /* $NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $ */
3 /*
4 * Copyright (c) 1993 John Brezak
5 * All rights reserved.
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.
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.
31 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/time.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <string.h>
38 #include <stddef.h>
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
43 #include "rpcv2.h"
44 #include "nfsv2.h"
46 #include "stand.h"
47 #include "net.h"
48 #include "netif.h"
49 #include "rpc.h"
51 #define NFS_DEBUGxx
53 #define NFSREAD_MIN_SIZE 1024
54 #define NFSREAD_MAX_SIZE 16384
56 /* NFSv3 definitions */
57 #define NFS_V3MAXFHSIZE 64
58 #define NFS_VER3 3
59 #define RPCMNT_VER3 3
60 #define NFSPROCV3_LOOKUP 3
61 #define NFSPROCV3_READLINK 5
62 #define NFSPROCV3_READ 6
63 #define NFSPROCV3_READDIR 16
65 typedef struct {
66 uint32_t val[2];
67 } n_quad;
69 struct nfsv3_time {
70 uint32_t nfs_sec;
71 uint32_t nfs_nsec;
74 struct nfsv3_fattrs {
75 uint32_t fa_type;
76 uint32_t fa_mode;
77 uint32_t fa_nlink;
78 uint32_t fa_uid;
79 uint32_t fa_gid;
80 n_quad fa_size;
81 n_quad fa_used;
82 n_quad fa_rdev;
83 n_quad fa_fsid;
84 n_quad fa_fileid;
85 struct nfsv3_time fa_atime;
86 struct nfsv3_time fa_mtime;
87 struct nfsv3_time fa_ctime;
91 * For NFSv3, the file handle is variable in size, so most fixed sized
92 * structures for arguments won't work. For most cases, a structure
93 * that starts with any fixed size section is followed by an array
94 * that covers the maximum size required.
96 struct nfsv3_readdir_repl {
97 uint32_t errno;
98 uint32_t ok;
99 struct nfsv3_fattrs fa;
100 uint32_t cookiev0;
101 uint32_t cookiev1;
104 struct nfsv3_readdir_entry {
105 uint32_t follows;
106 uint32_t fid0;
107 uint32_t fid1;
108 uint32_t len;
109 uint32_t nameplus[0];
112 struct nfs_iodesc {
113 struct iodesc *iodesc;
114 off_t off;
115 uint32_t fhsize;
116 u_char fh[NFS_V3MAXFHSIZE];
117 struct nfsv3_fattrs fa; /* all in network order */
118 uint64_t cookie;
122 * XXX interactions with tftp? See nfswrapper.c for a confusing
123 * issue.
125 int nfs_open(const char *path, struct open_file *f);
126 static int nfs_close(struct open_file *f);
127 static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
128 static off_t nfs_seek(struct open_file *f, off_t offset, int where);
129 static int nfs_stat(struct open_file *f, struct stat *sb);
130 static int nfs_readdir(struct open_file *f, struct dirent *d);
132 struct nfs_iodesc nfs_root_node;
134 struct fs_ops nfs_fsops = {
135 "nfs",
136 nfs_open,
137 nfs_close,
138 nfs_read,
139 null_write,
140 nfs_seek,
141 nfs_stat,
142 nfs_readdir
145 static int nfs_read_size = NFSREAD_MIN_SIZE;
148 * Improve boot performance over NFS
150 static void
151 set_nfs_read_size(void)
153 char *env, *end;
154 char buf[10];
156 if ((env = getenv("nfs.read_size")) != NULL) {
157 errno = 0;
158 nfs_read_size = strtol(env, &end, 0);
159 if (errno != 0 || *env == '\0' || *end != '\0') {
160 printf("%s: bad value: \"%s\", defaulting to %d\n",
161 "nfs.read_size", env, NFSREAD_MIN_SIZE);
162 nfs_read_size = NFSREAD_MIN_SIZE;
165 if (nfs_read_size < NFSREAD_MIN_SIZE) {
166 printf("%s: bad value: \"%d\", defaulting to %d\n",
167 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
168 nfs_read_size = NFSREAD_MIN_SIZE;
170 if (nfs_read_size > NFSREAD_MAX_SIZE) {
171 printf("%s: bad value: \"%d\", defaulting to %d\n",
172 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
173 nfs_read_size = NFSREAD_MAX_SIZE;
175 snprintf(buf, sizeof (buf), "%d", nfs_read_size);
176 setenv("nfs.read_size", buf, 1);
180 * Fetch the root file handle (call mount daemon)
181 * Return zero or error number.
184 nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
186 void *pkt = NULL;
187 int len;
188 struct args {
189 uint32_t len;
190 char path[FNAME_SIZE];
191 } *args;
192 struct repl {
193 uint32_t errno;
194 uint32_t fhsize;
195 u_char fh[NFS_V3MAXFHSIZE];
196 uint32_t authcnt;
197 uint32_t auth[7];
198 } *repl;
199 struct {
200 uint32_t h[RPC_HEADER_WORDS];
201 struct args d;
202 } sdata;
203 size_t cc;
205 #ifdef NFS_DEBUG
206 if (debug)
207 printf("nfs_getrootfh: %s\n", path);
208 #endif
210 args = &sdata.d;
212 bzero(args, sizeof(*args));
213 len = strlen(path);
214 if (len > sizeof(args->path))
215 len = sizeof(args->path);
216 args->len = htonl(len);
217 bcopy(path, args->path, len);
218 len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
220 cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
221 args, len, (void **)&repl, &pkt);
222 if (cc == -1) {
223 free(pkt);
224 /* errno was set by rpc_call */
225 return (errno);
227 if (cc < 2 * sizeof (uint32_t)) {
228 free(pkt);
229 return (EBADRPC);
231 if (repl->errno != 0) {
232 free(pkt);
233 return (ntohl(repl->errno));
235 *fhlenp = ntohl(repl->fhsize);
236 bcopy(repl->fh, fhp, *fhlenp);
238 set_nfs_read_size();
239 free(pkt);
240 return (0);
244 * Lookup a file. Store handle and attributes.
245 * Return zero or error number.
248 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
250 void *pkt = NULL;
251 int len, pos;
252 struct args {
253 uint32_t fhsize;
254 uint32_t fhplusname[1 +
255 (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
256 } *args;
257 struct repl {
258 uint32_t errno;
259 uint32_t fhsize;
260 uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
261 2 * (sizeof(uint32_t) +
262 sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
263 } *repl;
264 struct {
265 uint32_t h[RPC_HEADER_WORDS];
266 struct args d;
267 } sdata;
268 ssize_t cc;
270 #ifdef NFS_DEBUG
271 if (debug)
272 printf("lookupfh: called\n");
273 #endif
275 args = &sdata.d;
277 bzero(args, sizeof(*args));
278 args->fhsize = htonl(d->fhsize);
279 bcopy(d->fh, args->fhplusname, d->fhsize);
280 len = strlen(name);
281 if (len > FNAME_SIZE)
282 len = FNAME_SIZE;
283 pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
284 args->fhplusname[pos++] = htonl(len);
285 bcopy(name, &args->fhplusname[pos], len);
286 len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
287 roundup(len, sizeof(uint32_t));
289 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
290 args, len, (void **)&repl, &pkt);
291 if (cc == -1) {
292 free(pkt);
293 return (errno); /* XXX - from rpc_call */
295 if (cc < 2 * sizeof(uint32_t)) {
296 free(pkt);
297 return (EIO);
299 if (repl->errno != 0) {
300 free(pkt);
301 /* saerrno.h now matches NFS error numbers. */
302 return (ntohl(repl->errno));
304 newfd->fhsize = ntohl(repl->fhsize);
305 bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
306 pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
307 if (repl->fhplusattr[pos++] == 0) {
308 free(pkt);
309 return (EIO);
311 bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
312 free(pkt);
313 return (0);
317 * Get the destination of a symbolic link.
320 nfs_readlink(struct nfs_iodesc *d, char *buf)
322 void *pkt = NULL;
323 struct args {
324 uint32_t fhsize;
325 u_char fh[NFS_V3MAXFHSIZE];
326 } *args;
327 struct repl {
328 uint32_t errno;
329 uint32_t ok;
330 struct nfsv3_fattrs fa;
331 uint32_t len;
332 u_char path[NFS_MAXPATHLEN];
333 } *repl;
334 struct {
335 uint32_t h[RPC_HEADER_WORDS];
336 struct args d;
337 } sdata;
338 ssize_t cc;
339 int rc = 0;
341 #ifdef NFS_DEBUG
342 if (debug)
343 printf("readlink: called\n");
344 #endif
346 args = &sdata.d;
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 (void **)&repl, &pkt);
354 if (cc == -1)
355 return (errno);
357 if (cc < 2 * sizeof(uint32_t)) {
358 rc = EIO;
359 goto done;
362 if (repl->errno != 0) {
363 rc = ntohl(repl->errno);
364 goto done;
367 if (repl->ok == 0) {
368 rc = EIO;
369 goto done;
372 repl->len = ntohl(repl->len);
373 if (repl->len > NFS_MAXPATHLEN) {
374 rc = ENAMETOOLONG;
375 goto done;
378 bcopy(repl->path, buf, repl->len);
379 buf[repl->len] = 0;
380 done:
381 free(pkt);
382 return (rc);
386 * Read data from a file.
387 * Return transfer count or -1 (and set errno)
389 ssize_t
390 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
392 void *pkt = NULL;
393 struct args {
394 uint32_t fhsize;
395 uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
396 } *args;
397 struct repl {
398 uint32_t errno;
399 uint32_t ok;
400 struct nfsv3_fattrs fa;
401 uint32_t count;
402 uint32_t eof;
403 uint32_t len;
404 u_char data[NFSREAD_MAX_SIZE];
405 } *repl;
406 struct {
407 uint32_t h[RPC_HEADER_WORDS];
408 struct args d;
409 } sdata;
410 size_t cc;
411 long x;
412 int hlen, rlen, pos;
414 args = &sdata.d;
416 bzero(args, sizeof(*args));
417 args->fhsize = htonl(d->fhsize);
418 bcopy(d->fh, args->fhoffcnt, d->fhsize);
419 pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
420 args->fhoffcnt[pos++] = 0;
421 args->fhoffcnt[pos++] = htonl((uint32_t)off);
422 if (len > nfs_read_size)
423 len = nfs_read_size;
424 args->fhoffcnt[pos] = htonl((uint32_t)len);
425 hlen = offsetof(struct repl, data[0]);
427 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
428 args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
429 (void **)&repl, &pkt);
430 if (cc == -1) {
431 /* errno was already set by rpc_call */
432 return (-1);
434 if (cc < hlen) {
435 errno = EBADRPC;
436 free(pkt);
437 return (-1);
439 if (repl->errno != 0) {
440 errno = ntohl(repl->errno);
441 free(pkt);
442 return (-1);
444 rlen = cc - hlen;
445 x = ntohl(repl->count);
446 if (rlen < x) {
447 printf("nfsread: short packet, %d < %ld\n", rlen, x);
448 errno = EBADRPC;
449 free(pkt);
450 return (-1);
452 bcopy(repl->data, addr, x);
453 free(pkt);
454 return (x);
458 * Open a file.
459 * return zero or error number
462 nfs_open(const char *upath, struct open_file *f)
464 struct iodesc *desc;
465 struct nfs_iodesc *currfd = NULL;
466 char buf[2 * NFS_V3MAXFHSIZE + 3];
467 u_char *fh;
468 char *cp;
469 int i;
470 struct nfs_iodesc *newfd = NULL;
471 char *ncp;
472 int c;
473 char namebuf[NFS_MAXPATHLEN + 1];
474 char linkbuf[NFS_MAXPATHLEN + 1];
475 int nlinks = 0;
476 int error;
477 char *path = NULL;
479 if (netproto != NET_NFS)
480 return (EINVAL);
482 #ifdef NFS_DEBUG
483 if (debug)
484 printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
485 #endif
486 if (!rootpath[0]) {
487 printf("no rootpath, no nfs\n");
488 return (ENXIO);
491 if (f->f_dev->dv_type != DEVT_NET)
492 return (EINVAL);
494 if (!(desc = socktodesc(*(int *)(f->f_devdata))))
495 return (EINVAL);
497 /* Bind to a reserved port. */
498 desc->myport = htons(--rpc_port);
499 desc->destip = rootip;
500 if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
501 nfs_root_node.fh)))
502 return (error);
503 nfs_root_node.fa.fa_type = htonl(NFDIR);
504 nfs_root_node.fa.fa_mode = htonl(0755);
505 nfs_root_node.fa.fa_nlink = htonl(2);
506 nfs_root_node.iodesc = desc;
508 fh = &nfs_root_node.fh[0];
509 buf[0] = 'X';
510 cp = &buf[1];
511 for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
512 sprintf(cp, "%02x", fh[i]);
513 sprintf(cp, "X");
514 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
515 setenv("boot.nfsroot.path", rootpath, 1);
516 setenv("boot.nfsroot.nfshandle", buf, 1);
517 sprintf(buf, "%d", nfs_root_node.fhsize);
518 setenv("boot.nfsroot.nfshandlelen", buf, 1);
520 /* Allocate file system specific data structure */
521 currfd = malloc(sizeof(*newfd));
522 if (currfd == NULL) {
523 error = ENOMEM;
524 goto out;
526 bcopy(&nfs_root_node, currfd, sizeof(*currfd));
527 newfd = NULL;
529 cp = path = strdup(upath);
530 if (path == NULL) {
531 error = ENOMEM;
532 goto out;
534 while (*cp) {
536 * Remove extra separators
538 while (*cp == '/')
539 cp++;
541 if (*cp == '\0')
542 break;
544 * Check that current node is a directory.
546 if (currfd->fa.fa_type != htonl(NFDIR)) {
547 error = ENOTDIR;
548 goto out;
551 /* allocate file system specific data structure */
552 newfd = malloc(sizeof(*newfd));
553 if (newfd == NULL) {
554 error = ENOMEM;
555 goto out;
557 newfd->iodesc = currfd->iodesc;
560 * Get next component of path name.
563 int len = 0;
565 ncp = cp;
566 while ((c = *cp) != '\0' && c != '/') {
567 if (++len > NFS_MAXNAMLEN) {
568 error = ENOENT;
569 goto out;
571 cp++;
573 *cp = '\0';
576 /* lookup a file handle */
577 error = nfs_lookupfh(currfd, ncp, newfd);
578 *cp = c;
579 if (error)
580 goto out;
583 * Check for symbolic link
585 if (newfd->fa.fa_type == htonl(NFLNK)) {
586 int link_len, len;
588 error = nfs_readlink(newfd, linkbuf);
589 if (error)
590 goto out;
592 link_len = strlen(linkbuf);
593 len = strlen(cp);
595 if (link_len + len > MAXPATHLEN
596 || ++nlinks > MAXSYMLINKS) {
597 error = ENOENT;
598 goto out;
601 bcopy(cp, &namebuf[link_len], len + 1);
602 bcopy(linkbuf, namebuf, link_len);
605 * If absolute pathname, restart at root.
606 * If relative pathname, restart at parent directory.
608 cp = namebuf;
609 if (*cp == '/')
610 bcopy(&nfs_root_node, currfd, sizeof(*currfd));
612 free(newfd);
613 newfd = NULL;
615 continue;
618 free(currfd);
619 currfd = newfd;
620 newfd = NULL;
623 error = 0;
625 out:
626 free(newfd);
627 free(path);
628 if (!error) {
629 currfd->off = 0;
630 currfd->cookie = 0;
631 f->f_fsdata = (void *)currfd;
632 return (0);
635 #ifdef NFS_DEBUG
636 if (debug)
637 printf("nfs_open: %s lookupfh failed: %s\n",
638 path, strerror(error));
639 #endif
640 free(currfd);
642 return (error);
646 nfs_close(struct open_file *f)
648 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
650 #ifdef NFS_DEBUG
651 if (debug)
652 printf("nfs_close: fp=0x%lx\n", (u_long)fp);
653 #endif
655 free(fp);
656 f->f_fsdata = NULL;
658 return (0);
662 * read a portion of a file
665 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
667 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
668 ssize_t cc;
669 char *addr = buf;
671 #ifdef NFS_DEBUG
672 if (debug)
673 printf("nfs_read: size=%lu off=%d\n", (u_long)size,
674 (int)fp->off);
675 #endif
676 while ((int)size > 0) {
677 twiddle(16);
678 cc = nfs_readdata(fp, fp->off, (void *)addr, size);
679 /* XXX maybe should retry on certain errors */
680 if (cc == -1) {
681 #ifdef NFS_DEBUG
682 if (debug)
683 printf("nfs_read: read: %s", strerror(errno));
684 #endif
685 return (errno); /* XXX - from nfs_readdata */
687 if (cc == 0) {
688 #ifdef NFS_DEBUG
689 if (debug)
690 printf("nfs_read: hit EOF unexpectantly");
691 #endif
692 goto ret;
694 fp->off += cc;
695 addr += cc;
696 size -= cc;
698 ret:
699 if (resid)
700 *resid = size;
702 return (0);
705 off_t
706 nfs_seek(struct open_file *f, off_t offset, int where)
708 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
709 uint32_t size = ntohl(d->fa.fa_size.val[1]);
711 switch (where) {
712 case SEEK_SET:
713 d->off = offset;
714 break;
715 case SEEK_CUR:
716 d->off += offset;
717 break;
718 case SEEK_END:
719 d->off = size - offset;
720 break;
721 default:
722 errno = EINVAL;
723 return (-1);
726 return (d->off);
729 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
730 int nfs_stat_types[9] = {
731 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
734 nfs_stat(struct open_file *f, struct stat *sb)
736 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
737 uint32_t ftype, mode;
739 ftype = ntohl(fp->fa.fa_type);
740 mode = ntohl(fp->fa.fa_mode);
741 mode |= nfs_stat_types[ftype & 7];
743 sb->st_mode = mode;
744 sb->st_nlink = ntohl(fp->fa.fa_nlink);
745 sb->st_uid = ntohl(fp->fa.fa_uid);
746 sb->st_gid = ntohl(fp->fa.fa_gid);
747 sb->st_size = ntohl(fp->fa.fa_size.val[1]);
749 return (0);
752 static int
753 nfs_readdir(struct open_file *f, struct dirent *d)
755 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
756 struct nfsv3_readdir_repl *repl;
757 struct nfsv3_readdir_entry *rent;
758 static void *pkt = NULL;
759 static char *buf;
760 static struct nfs_iodesc *pfp = NULL;
761 static uint64_t cookie = 0;
762 size_t cc;
763 int pos, rc;
765 struct args {
766 uint32_t fhsize;
767 uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
768 } *args;
769 struct {
770 uint32_t h[RPC_HEADER_WORDS];
771 struct args d;
772 } sdata;
774 if (fp != pfp || fp->off != cookie) {
775 pfp = NULL;
776 refill:
777 free(pkt);
778 pkt = NULL;
779 args = &sdata.d;
780 bzero(args, sizeof(*args));
782 args->fhsize = htonl(fp->fhsize);
783 bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
784 pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
785 args->fhpluscookie[pos++] = htonl(fp->off >> 32);
786 args->fhpluscookie[pos++] = htonl(fp->off);
787 args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
788 args->fhpluscookie[pos++] = htonl(fp->cookie);
789 args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
791 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
792 args, 6 * sizeof(uint32_t) +
793 roundup(fp->fhsize, sizeof(uint32_t)),
794 (void **)&buf, &pkt);
795 if (cc == -1) {
796 rc = errno;
797 goto err;
799 repl = (struct nfsv3_readdir_repl *)buf;
800 if (repl->errno != 0) {
801 rc = ntohl(repl->errno);
802 goto err;
804 pfp = fp;
805 cookie = fp->off;
806 fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
807 ntohl(repl->cookiev1);
808 buf += sizeof (struct nfsv3_readdir_repl);
810 rent = (struct nfsv3_readdir_entry *)buf;
812 if (rent->follows == 0) {
813 /* fid0 is actually eof */
814 if (rent->fid0 != 0) {
815 rc = ENOENT;
816 goto err;
818 goto refill;
821 d->d_namlen = ntohl(rent->len);
822 bcopy(rent->nameplus, d->d_name, d->d_namlen);
823 d->d_name[d->d_namlen] = '\0';
825 pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
826 fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
827 ntohl(rent->nameplus[pos + 1]);
828 pos += 2;
829 buf = (char *)&rent->nameplus[pos];
830 return (0);
832 err:
833 free(pkt);
834 pkt = NULL;
835 pfp = NULL;
836 cookie = 0;
837 return (rc);