9198 libstand: Const-ify buffer argument of write(2) analog
[unleashed.git] / usr / src / boot / lib / libstand / nfs.c
blob1cf8f5af0b9b767fe36dc9f6f27d5001a62e49e6
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, rlen, 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;
466 char buf[2 * NFS_V3MAXFHSIZE + 3];
467 u_char *fh;
468 char *cp;
469 int i;
470 struct nfs_iodesc *newfd;
471 struct nfsv3_fattrs *fa;
472 char *ncp;
473 int c;
474 char namebuf[NFS_MAXPATHLEN + 1];
475 char linkbuf[NFS_MAXPATHLEN + 1];
476 int nlinks = 0;
477 int error;
478 char *path;
480 if (netproto != NET_NFS)
481 return (EINVAL);
483 #ifdef NFS_DEBUG
484 if (debug)
485 printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
486 #endif
487 if (!rootpath[0]) {
488 printf("no rootpath, no nfs\n");
489 return (ENXIO);
492 if (f->f_dev->dv_type != DEVT_NET)
493 return (EINVAL);
495 if (!(desc = socktodesc(*(int *)(f->f_devdata))))
496 return (EINVAL);
498 /* Bind to a reserved port. */
499 desc->myport = htons(--rpc_port);
500 desc->destip = rootip;
501 if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
502 nfs_root_node.fh)))
503 return (error);
504 nfs_root_node.fa.fa_type = htonl(NFDIR);
505 nfs_root_node.fa.fa_mode = htonl(0755);
506 nfs_root_node.fa.fa_nlink = htonl(2);
507 nfs_root_node.iodesc = desc;
509 fh = &nfs_root_node.fh[0];
510 buf[0] = 'X';
511 cp = &buf[1];
512 for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
513 sprintf(cp, "%02x", fh[i]);
514 sprintf(cp, "X");
515 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
516 setenv("boot.nfsroot.path", rootpath, 1);
517 setenv("boot.nfsroot.nfshandle", buf, 1);
518 sprintf(buf, "%d", nfs_root_node.fhsize);
519 setenv("boot.nfsroot.nfshandlelen", buf, 1);
521 /* Allocate file system specific data structure */
522 currfd = malloc(sizeof(*newfd));
523 if (currfd == NULL) {
524 error = ENOMEM;
525 goto out;
527 bcopy(&nfs_root_node, currfd, sizeof(*currfd));
528 newfd = NULL;
530 cp = path = strdup(upath);
531 if (path == NULL) {
532 error = ENOMEM;
533 goto out;
535 while (*cp) {
537 * Remove extra separators
539 while (*cp == '/')
540 cp++;
542 if (*cp == '\0')
543 break;
545 * Check that current node is a directory.
547 if (currfd->fa.fa_type != htonl(NFDIR)) {
548 error = ENOTDIR;
549 goto out;
552 /* allocate file system specific data structure */
553 newfd = malloc(sizeof(*newfd));
554 if (newfd == NULL) {
555 error = ENOMEM;
556 goto out;
558 newfd->iodesc = currfd->iodesc;
561 * Get next component of path name.
564 int len = 0;
566 ncp = cp;
567 while ((c = *cp) != '\0' && c != '/') {
568 if (++len > NFS_MAXNAMLEN) {
569 error = ENOENT;
570 goto out;
572 cp++;
574 *cp = '\0';
577 /* lookup a file handle */
578 error = nfs_lookupfh(currfd, ncp, newfd);
579 *cp = c;
580 if (error)
581 goto out;
584 * Check for symbolic link
586 if (newfd->fa.fa_type == htonl(NFLNK)) {
587 int link_len, len;
589 error = nfs_readlink(newfd, linkbuf);
590 if (error)
591 goto out;
593 link_len = strlen(linkbuf);
594 len = strlen(cp);
596 if (link_len + len > MAXPATHLEN
597 || ++nlinks > MAXSYMLINKS) {
598 error = ENOENT;
599 goto out;
602 bcopy(cp, &namebuf[link_len], len + 1);
603 bcopy(linkbuf, namebuf, link_len);
606 * If absolute pathname, restart at root.
607 * If relative pathname, restart at parent directory.
609 cp = namebuf;
610 if (*cp == '/')
611 bcopy(&nfs_root_node, currfd, sizeof(*currfd));
613 free(newfd);
614 newfd = NULL;
616 continue;
619 free(currfd);
620 currfd = newfd;
621 newfd = NULL;
624 error = 0;
626 out:
627 free(newfd);
628 free(path);
629 if (!error) {
630 currfd->off = 0;
631 currfd->cookie = 0;
632 f->f_fsdata = (void *)currfd;
633 return (0);
636 #ifdef NFS_DEBUG
637 if (debug)
638 printf("nfs_open: %s lookupfh failed: %s\n",
639 path, strerror(error));
640 #endif
641 free(currfd);
643 return (error);
647 nfs_close(struct open_file *f)
649 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
651 #ifdef NFS_DEBUG
652 if (debug)
653 printf("nfs_close: fp=0x%lx\n", (u_long)fp);
654 #endif
656 free(fp);
657 f->f_fsdata = NULL;
659 return (0);
663 * read a portion of a file
666 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
668 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
669 ssize_t cc;
670 char *addr = buf;
672 #ifdef NFS_DEBUG
673 if (debug)
674 printf("nfs_read: size=%lu off=%d\n", (u_long)size,
675 (int)fp->off);
676 #endif
677 while ((int)size > 0) {
678 twiddle(16);
679 cc = nfs_readdata(fp, fp->off, (void *)addr, size);
680 /* XXX maybe should retry on certain errors */
681 if (cc == -1) {
682 #ifdef NFS_DEBUG
683 if (debug)
684 printf("nfs_read: read: %s", strerror(errno));
685 #endif
686 return (errno); /* XXX - from nfs_readdata */
688 if (cc == 0) {
689 #ifdef NFS_DEBUG
690 if (debug)
691 printf("nfs_read: hit EOF unexpectantly");
692 #endif
693 goto ret;
695 fp->off += cc;
696 addr += cc;
697 size -= cc;
699 ret:
700 if (resid)
701 *resid = size;
703 return (0);
706 off_t
707 nfs_seek(struct open_file *f, off_t offset, int where)
709 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
710 uint32_t size = ntohl(d->fa.fa_size.val[1]);
712 switch (where) {
713 case SEEK_SET:
714 d->off = offset;
715 break;
716 case SEEK_CUR:
717 d->off += offset;
718 break;
719 case SEEK_END:
720 d->off = size - offset;
721 break;
722 default:
723 errno = EINVAL;
724 return (-1);
727 return (d->off);
730 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
731 int nfs_stat_types[9] = {
732 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
735 nfs_stat(struct open_file *f, struct stat *sb)
737 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
738 uint32_t ftype, mode;
740 ftype = ntohl(fp->fa.fa_type);
741 mode = ntohl(fp->fa.fa_mode);
742 mode |= nfs_stat_types[ftype & 7];
744 sb->st_mode = mode;
745 sb->st_nlink = ntohl(fp->fa.fa_nlink);
746 sb->st_uid = ntohl(fp->fa.fa_uid);
747 sb->st_gid = ntohl(fp->fa.fa_gid);
748 sb->st_size = ntohl(fp->fa.fa_size.val[1]);
750 return (0);
753 static int
754 nfs_readdir(struct open_file *f, struct dirent *d)
756 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
757 struct nfsv3_readdir_repl *repl;
758 struct nfsv3_readdir_entry *rent;
759 static void *pkt = NULL;
760 static char *buf;
761 static struct nfs_iodesc *pfp = NULL;
762 static uint64_t cookie = 0;
763 size_t cc;
764 int pos, rc;
766 struct args {
767 uint32_t fhsize;
768 uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
769 } *args;
770 struct {
771 uint32_t h[RPC_HEADER_WORDS];
772 struct args d;
773 } sdata;
775 if (fp != pfp || fp->off != cookie) {
776 pfp = NULL;
777 refill:
778 free(pkt);
779 pkt = NULL;
780 args = &sdata.d;
781 bzero(args, sizeof(*args));
783 args->fhsize = htonl(fp->fhsize);
784 bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
785 pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
786 args->fhpluscookie[pos++] = htonl(fp->off >> 32);
787 args->fhpluscookie[pos++] = htonl(fp->off);
788 args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
789 args->fhpluscookie[pos++] = htonl(fp->cookie);
790 args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
792 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
793 args, 6 * sizeof(uint32_t) +
794 roundup(fp->fhsize, sizeof(uint32_t)),
795 (void **)&buf, &pkt);
796 if (cc == -1) {
797 rc = errno;
798 goto err;
800 repl = (struct nfsv3_readdir_repl *)buf;
801 if (repl->errno != 0) {
802 rc = ntohl(repl->errno);
803 goto err;
805 pfp = fp;
806 cookie = fp->off;
807 fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
808 ntohl(repl->cookiev1);
809 buf += sizeof (struct nfsv3_readdir_repl);
811 rent = (struct nfsv3_readdir_entry *)buf;
813 if (rent->follows == 0) {
814 /* fid0 is actually eof */
815 if (rent->fid0 != 0) {
816 rc = ENOENT;
817 goto err;
819 goto refill;
822 d->d_namlen = ntohl(rent->len);
823 bcopy(rent->nameplus, d->d_name, d->d_namlen);
824 d->d_name[d->d_namlen] = '\0';
826 pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
827 fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
828 ntohl(rent->nameplus[pos + 1]);
829 pos += 2;
830 buf = (u_char *)&rent->nameplus[pos];
831 return (0);
833 err:
834 free(pkt);
835 pkt = NULL;
836 pfp = NULL;
837 cookie = 0;
838 return (rc);