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 
  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>
  39 
  40 #include <netinet/in.h>
  41 #include <netinet/in_systm.h>
  42 
  43 #include "rpcv2.h"
  44 #include "nfsv2.h"
  45 
  46 #include "stand.h"
  47 #include "net.h"
  48 #include "netif.h"
  49 #include "rpc.h"
  50 
  51 #define NFS_DEBUGxx
  52 
  53 #define NFSREAD_MIN_SIZE 1024
  54 #define NFSREAD_MAX_SIZE 16384
  55 
  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
  64 
  65 typedef struct {
  66         uint32_t val[2];
  67 } n_quad;
  68 
  69 struct nfsv3_time {
  70         uint32_t nfs_sec;
  71         uint32_t nfs_nsec;
  72 };
  73 
  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;
  88 };
  89 
  90 /*
  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.
  95  */
  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;
 102 };
 103 
 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];
 110 };
 111 
 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;
 119 };
 120 
 121 /*
 122  * XXX interactions with tftp? See nfswrapper.c for a confusing
 123  *     issue.
 124  */
 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);
 131 
 132 struct  nfs_iodesc nfs_root_node;
 133 
 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
 143 };
 144 
 145 static int nfs_read_size = NFSREAD_MIN_SIZE;
 146 
 147 /*
 148  * Improve boot performance over NFS
 149  */
 150 static void
 151 set_nfs_read_size(void)
 152 {
 153         char *env, *end;
 154         char buf[10];
 155 
 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;
 163                 }
 164         }
 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;
 169         }
 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;
 174         }
 175         snprintf(buf, sizeof (buf), "%d", nfs_read_size);
 176         setenv("nfs.read_size", buf, 1);
 177 }
 178 
 179 /*
 180  * Fetch the root file handle (call mount daemon)
 181  * Return zero or error number.
 182  */
 183 int
 184 nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
 185 {
 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;
 204 
 205 #ifdef NFS_DEBUG
 206         if (debug)
 207                 printf("nfs_getrootfh: %s\n", path);
 208 #endif
 209 
 210         args = &sdata.d;
 211 
 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));
 219 
 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);
 226         }
 227         if (cc < 2 * sizeof (uint32_t)) {
 228                 free(pkt);
 229                 return (EBADRPC);
 230         }
 231         if (repl->errno != 0) {
 232                 free(pkt);
 233                 return (ntohl(repl->errno));
 234         }
 235         *fhlenp = ntohl(repl->fhsize);
 236         bcopy(repl->fh, fhp, *fhlenp);
 237 
 238         set_nfs_read_size();
 239         free(pkt);
 240         return (0);
 241 }
 242 
 243 /*
 244  * Lookup a file.  Store handle and attributes.
 245  * Return zero or error number.
 246  */
 247 int
 248 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
 249 {
 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;
 269 
 270 #ifdef NFS_DEBUG
 271         if (debug)
 272                 printf("lookupfh: called\n");
 273 #endif
 274 
 275         args = &sdata.d;
 276 
 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));
 288 
 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 */
 294         }
 295         if (cc < 2 * sizeof(uint32_t)) {
 296                 free(pkt);
 297                 return (EIO);
 298         }
 299         if (repl->errno != 0) {
 300                 free(pkt);
 301                 /* saerrno.h now matches NFS error numbers. */
 302                 return (ntohl(repl->errno));
 303         }
 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);
 310         }
 311         bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
 312         free(pkt);
 313         return (0);
 314 }
 315 
 316 /*
 317  * Get the destination of a symbolic link.
 318  */
 319 int
 320 nfs_readlink(struct nfs_iodesc *d, char *buf)
 321 {
 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;
 340 
 341 #ifdef NFS_DEBUG
 342         if (debug)
 343                 printf("readlink: called\n");
 344 #endif
 345 
 346         args = &sdata.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             (void **)&repl, &pkt);
 354         if (cc == -1)
 355                 return (errno);
 356 
 357         if (cc < 2 * sizeof(uint32_t)) {
 358                 rc = EIO;
 359                 goto done;
 360         }
 361 
 362         if (repl->errno != 0) {
 363                 rc = ntohl(repl->errno);
 364                 goto done;
 365         }
 366 
 367         if (repl->ok == 0) {
 368                 rc = EIO;
 369                 goto done;
 370         }
 371 
 372         repl->len = ntohl(repl->len);
 373         if (repl->len > NFS_MAXPATHLEN) {
 374                 rc = ENAMETOOLONG;
 375                 goto done;
 376         }
 377 
 378         bcopy(repl->path, buf, repl->len);
 379         buf[repl->len] = 0;
 380 done:
 381         free(pkt);
 382         return (rc);
 383 }
 384 
 385 /*
 386  * Read data from a file.
 387  * Return transfer count or -1 (and set errno)
 388  */
 389 ssize_t
 390 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
 391 {
 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;
 413 
 414         args = &sdata.d;
 415 
 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]);
 426 
 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);
 433         }
 434         if (cc < hlen) {
 435                 errno = EBADRPC;
 436                 free(pkt);
 437                 return (-1);
 438         }
 439         if (repl->errno != 0) {
 440                 errno = ntohl(repl->errno);
 441                 free(pkt);
 442                 return (-1);
 443         }
 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);
 451         }
 452         bcopy(repl->data, addr, x);
 453         free(pkt);
 454         return (x);
 455 }
 456 
 457 /*
 458  * Open a file.
 459  * return zero or error number
 460  */
 461 int
 462 nfs_open(const char *upath, struct open_file *f)
 463 {
 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;
 479 
 480         if (netproto != NET_NFS)
 481                 return (EINVAL);
 482 
 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);
 490         }
 491 
 492         if (f->f_dev->dv_type != DEVT_NET)
 493                 return (EINVAL);
 494 
 495         if (!(desc = socktodesc(*(int *)(f->f_devdata))))
 496                 return (EINVAL);
 497 
 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;
 508 
 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);
 520 
 521         /* Allocate file system specific data structure */
 522         currfd = malloc(sizeof(*newfd));
 523         if (currfd == NULL) {
 524                 error = ENOMEM;
 525                 goto out;
 526         }
 527         bcopy(&nfs_root_node, currfd, sizeof(*currfd));
 528         newfd = NULL;
 529 
 530         cp = path = strdup(upath);
 531         if (path == NULL) {
 532                 error = ENOMEM;
 533                 goto out;
 534         }
 535         while (*cp) {
 536                 /*
 537                  * Remove extra separators
 538                  */
 539                 while (*cp == '/')
 540                         cp++;
 541 
 542                 if (*cp == '\0')
 543                         break;
 544                 /*
 545                  * Check that current node is a directory.
 546                  */
 547                 if (currfd->fa.fa_type != htonl(NFDIR)) {
 548                         error = ENOTDIR;
 549                         goto out;
 550                 }
 551 
 552                 /* allocate file system specific data structure */
 553                 newfd = malloc(sizeof(*newfd));
 554                 if (newfd == NULL) {
 555                         error = ENOMEM;
 556                         goto out;
 557                 }
 558                 newfd->iodesc = currfd->iodesc;
 559 
 560                 /*
 561                  * Get next component of path name.
 562                  */
 563                 {
 564                         int len = 0;
 565 
 566                         ncp = cp;
 567                         while ((c = *cp) != '\0' && c != '/') {
 568                                 if (++len > NFS_MAXNAMLEN) {
 569                                         error = ENOENT;
 570                                         goto out;
 571                                 }
 572                                 cp++;
 573                         }
 574                         *cp = '\0';
 575                 }
 576 
 577                 /* lookup a file handle */
 578                 error = nfs_lookupfh(currfd, ncp, newfd);
 579                 *cp = c;
 580                 if (error)
 581                         goto out;
 582 
 583                 /*
 584                  * Check for symbolic link
 585                  */
 586                 if (newfd->fa.fa_type == htonl(NFLNK)) {
 587                         int link_len, len;
 588 
 589                         error = nfs_readlink(newfd, linkbuf);
 590                         if (error)
 591                                 goto out;
 592 
 593                         link_len = strlen(linkbuf);
 594                         len = strlen(cp);
 595 
 596                         if (link_len + len > MAXPATHLEN
 597                             || ++nlinks > MAXSYMLINKS) {
 598                                 error = ENOENT;
 599                                 goto out;
 600                         }
 601 
 602                         bcopy(cp, &namebuf[link_len], len + 1);
 603                         bcopy(linkbuf, namebuf, link_len);
 604 
 605                         /*
 606                          * If absolute pathname, restart at root.
 607                          * If relative pathname, restart at parent directory.
 608                          */
 609                         cp = namebuf;
 610                         if (*cp == '/')
 611                                 bcopy(&nfs_root_node, currfd, sizeof(*currfd));
 612 
 613                         free(newfd);
 614                         newfd = NULL;
 615 
 616                         continue;
 617                 }
 618 
 619                 free(currfd);
 620                 currfd = newfd;
 621                 newfd = NULL;
 622         }
 623 
 624         error = 0;
 625 
 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);
 634         }
 635 
 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);
 642 
 643         return (error);
 644 }
 645 
 646 int
 647 nfs_close(struct open_file *f)
 648 {
 649         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
 650 
 651 #ifdef NFS_DEBUG
 652         if (debug)
 653                 printf("nfs_close: fp=0x%lx\n", (u_long)fp);
 654 #endif
 655 
 656         free(fp);
 657         f->f_fsdata = NULL;
 658 
 659         return (0);
 660 }
 661 
 662 /*
 663  * read a portion of a file
 664  */
 665 int
 666 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
 667 {
 668         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
 669         ssize_t cc;
 670         char *addr = buf;
 671 
 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 */
 687                 }
 688                 if (cc == 0) {
 689 #ifdef NFS_DEBUG
 690                         if (debug)
 691                                 printf("nfs_read: hit EOF unexpectantly");
 692 #endif
 693                         goto ret;
 694                 }
 695                 fp->off += cc;
 696                 addr += cc;
 697                 size -= cc;
 698         }
 699 ret:
 700         if (resid)
 701                 *resid = size;
 702 
 703         return (0);
 704 }
 705 
 706 off_t
 707 nfs_seek(struct open_file *f, off_t offset, int where)
 708 {
 709         struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
 710         uint32_t size = ntohl(d->fa.fa_size.val[1]);
 711 
 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);
 725         }
 726 
 727         return (d->off);
 728 }
 729 
 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 };
 733 
 734 int
 735 nfs_stat(struct open_file *f, struct stat *sb)
 736 {
 737         struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
 738         uint32_t ftype, mode;
 739 
 740         ftype = ntohl(fp->fa.fa_type);
 741         mode  = ntohl(fp->fa.fa_mode);
 742         mode |= nfs_stat_types[ftype & 7];
 743 
 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]);
 749 
 750         return (0);
 751 }
 752 
 753 static int
 754 nfs_readdir(struct open_file *f, struct dirent *d)
 755 {
 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;
 765 
 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;
 774 
 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));
 782 
 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);
 791 
 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;
 799                 }
 800                 repl = (struct nfsv3_readdir_repl *)buf;
 801                 if (repl->errno != 0) {
 802                         rc = ntohl(repl->errno);
 803                         goto err;
 804                 }
 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);
 810         }
 811         rent = (struct nfsv3_readdir_entry *)buf;
 812 
 813         if (rent->follows == 0) {
 814                 /* fid0 is actually eof */
 815                 if (rent->fid0 != 0) {
 816                         rc = ENOENT;
 817                         goto err;
 818                 }
 819                 goto refill;
 820         }
 821 
 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';
 825 
 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);
 832 
 833 err:
 834         free(pkt);
 835         pkt = NULL;
 836         pfp = NULL;
 837         cookie = 0;
 838         return (rc);
 839 }