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 uchar_t 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 .fs_name = "nfs", 136 .fo_open = nfs_open, 137 .fo_close = nfs_close, 138 .fo_read = nfs_read, 139 .fo_write = null_write, 140 .fo_seek = nfs_seek, 141 .fo_stat = nfs_stat, 142 .fo_readdir = 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, uchar_t *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 uchar_t 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, 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 uchar_t 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 uchar_t 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 uchar_t 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 devdesc *dev; 465 struct iodesc *desc; 466 struct nfs_iodesc *currfd = NULL; 467 char buf[2 * NFS_V3MAXFHSIZE + 3]; 468 uchar_t *fh; 469 char *cp; 470 int i; 471 struct nfs_iodesc *newfd = NULL; 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 = NULL; 479 480 if (netproto != NET_NFS) 481 return (EINVAL); 482 483 dev = f->f_devdata; 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 *)(dev->d_opendata)))) 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 = 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=%#p\n", 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=%zu off=%j\n", size, 676 (intmax_t)fp->off); 677 #endif 678 while (size > 0) { 679 twiddle(16); 680 cc = nfs_readdata(fp, fp->off, 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 off_t 708 nfs_seek(struct open_file *f, off_t offset, int where) 709 { 710 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata; 711 uint32_t size = ntohl(d->fa.fa_size.val[1]); 712 713 switch (where) { 714 case SEEK_SET: 715 d->off = offset; 716 break; 717 case SEEK_CUR: 718 d->off += offset; 719 break; 720 case SEEK_END: 721 d->off = size - offset; 722 break; 723 default: 724 errno = EINVAL; 725 return (-1); 726 } 727 728 return (d->off); 729 } 730 731 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */ 732 int nfs_stat_types[9] = { 733 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 }; 734 735 int 736 nfs_stat(struct open_file *f, struct stat *sb) 737 { 738 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 739 uint32_t ftype, mode; 740 741 ftype = ntohl(fp->fa.fa_type); 742 mode = ntohl(fp->fa.fa_mode); 743 mode |= nfs_stat_types[ftype & 7]; 744 745 sb->st_mode = mode; 746 sb->st_nlink = ntohl(fp->fa.fa_nlink); 747 sb->st_uid = ntohl(fp->fa.fa_uid); 748 sb->st_gid = ntohl(fp->fa.fa_gid); 749 sb->st_size = ntohl(fp->fa.fa_size.val[1]); 750 751 return (0); 752 } 753 754 static int 755 nfs_readdir(struct open_file *f, struct dirent *d) 756 { 757 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 758 struct nfsv3_readdir_repl *repl; 759 struct nfsv3_readdir_entry *rent; 760 static void *pkt = NULL; 761 static char *buf; 762 static struct nfs_iodesc *pfp = NULL; 763 static uint64_t cookie = 0; 764 size_t cc; 765 int pos, rc; 766 767 struct args { 768 uint32_t fhsize; 769 uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE]; 770 } *args; 771 struct { 772 uint32_t h[RPC_HEADER_WORDS]; 773 struct args d; 774 } sdata; 775 776 if (fp != pfp || fp->off != cookie) { 777 pfp = NULL; 778 refill: 779 free(pkt); 780 pkt = NULL; 781 args = &sdata.d; 782 bzero(args, sizeof (*args)); 783 784 args->fhsize = htonl(fp->fhsize); 785 bcopy(fp->fh, args->fhpluscookie, fp->fhsize); 786 pos = roundup(fp->fhsize, 787 sizeof (uint32_t)) / sizeof (uint32_t); 788 args->fhpluscookie[pos++] = htonl(fp->off >> 32); 789 args->fhpluscookie[pos++] = htonl(fp->off); 790 args->fhpluscookie[pos++] = htonl(fp->cookie >> 32); 791 args->fhpluscookie[pos++] = htonl(fp->cookie); 792 args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE); 793 794 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR, 795 args, 6 * sizeof (uint32_t) + 796 roundup(fp->fhsize, sizeof (uint32_t)), 797 (void **)&buf, &pkt); 798 if (cc == -1) { 799 rc = errno; 800 goto err; 801 } 802 repl = (struct nfsv3_readdir_repl *)buf; 803 if (repl->errno != 0) { 804 rc = ntohl(repl->errno); 805 goto err; 806 } 807 pfp = fp; 808 cookie = fp->off; 809 fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) | 810 ntohl(repl->cookiev1); 811 buf += sizeof (struct nfsv3_readdir_repl); 812 } 813 rent = (struct nfsv3_readdir_entry *)buf; 814 815 if (rent->follows == 0) { 816 /* fid0 is actually eof */ 817 if (rent->fid0 != 0) { 818 rc = ENOENT; 819 goto err; 820 } 821 goto refill; 822 } 823 824 d->d_namlen = ntohl(rent->len); 825 bcopy(rent->nameplus, d->d_name, d->d_namlen); 826 d->d_name[d->d_namlen] = '\0'; 827 828 pos = roundup(d->d_namlen, sizeof (uint32_t)) / sizeof (uint32_t); 829 fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) | 830 ntohl(rent->nameplus[pos + 1]); 831 pos += 2; 832 buf = (char *)&rent->nameplus[pos]; 833 return (0); 834 835 err: 836 free(pkt); 837 pkt = NULL; 838 pfp = NULL; 839 cookie = 0; 840 return (rc); 841 } 842