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