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/param.h> 32 #include <sys/time.h> 33 #include <sys/socket.h> 34 #include <sys/stat.h> 35 #include <string.h> 36 #include <stddef.h> 37 38 #include <netinet/in.h> 39 #include <netinet/in_systm.h> 40 41 #include "rpcv2.h" 42 #include "nfsv2.h" 43 44 #include "stand.h" 45 #include "net.h" 46 #include "netif.h" 47 #include "rpc.h" 48 49 #define NFS_DEBUGxx 50 51 #define NFSREAD_MIN_SIZE 1024 52 #define NFSREAD_MAX_SIZE 16384 53 54 /* NFSv3 definitions */ 55 #define NFS_V3MAXFHSIZE 64 56 #define NFS_VER3 3 57 #define RPCMNT_VER3 3 58 #define NFSPROCV3_LOOKUP 3 59 #define NFSPROCV3_READLINK 5 60 #define NFSPROCV3_READ 6 61 #define NFSPROCV3_READDIR 16 62 63 typedef struct { 64 uint32_t val[2]; 65 } n_quad; 66 67 struct nfsv3_time { 68 uint32_t nfs_sec; 69 uint32_t nfs_nsec; 70 }; 71 72 struct nfsv3_fattrs { 73 uint32_t fa_type; 74 uint32_t fa_mode; 75 uint32_t fa_nlink; 76 uint32_t fa_uid; 77 uint32_t fa_gid; 78 n_quad fa_size; 79 n_quad fa_used; 80 n_quad fa_rdev; 81 n_quad fa_fsid; 82 n_quad fa_fileid; 83 struct nfsv3_time fa_atime; 84 struct nfsv3_time fa_mtime; 85 struct nfsv3_time fa_ctime; 86 }; 87 88 /* 89 * For NFSv3, the file handle is variable in size, so most fixed sized 90 * structures for arguments won't work. For most cases, a structure 91 * that starts with any fixed size section is followed by an array 92 * that covers the maximum size required. 93 */ 94 struct nfsv3_readdir_repl { 95 uint32_t errno; 96 uint32_t ok; 97 struct nfsv3_fattrs fa; 98 uint32_t cookiev0; 99 uint32_t cookiev1; 100 }; 101 102 struct nfsv3_readdir_entry { 103 uint32_t follows; 104 uint32_t fid0; 105 uint32_t fid1; 106 uint32_t len; 107 uint32_t nameplus[0]; 108 }; 109 110 struct nfs_iodesc { 111 struct iodesc *iodesc; 112 off_t off; 113 uint32_t fhsize; 114 u_char fh[NFS_V3MAXFHSIZE]; 115 struct nfsv3_fattrs fa; /* all in network order */ 116 uint64_t cookie; 117 }; 118 119 /* 120 * XXX interactions with tftp? See nfswrapper.c for a confusing 121 * issue. 122 */ 123 int nfs_open(const char *path, struct open_file *f); 124 static int nfs_close(struct open_file *f); 125 static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 126 static off_t nfs_seek(struct open_file *f, off_t offset, int where); 127 static int nfs_stat(struct open_file *f, struct stat *sb); 128 static int nfs_readdir(struct open_file *f, struct dirent *d); 129 130 struct nfs_iodesc nfs_root_node; 131 132 struct fs_ops nfs_fsops = { 133 .fs_name = "nfs", 134 .fo_open = nfs_open, 135 .fo_close = nfs_close, 136 .fo_read = nfs_read, 137 .fo_write = null_write, 138 .fo_seek = nfs_seek, 139 .fo_stat = nfs_stat, 140 .fo_readdir = nfs_readdir, 141 }; 142 143 static int nfs_read_size = NFSREAD_MIN_SIZE; 144 145 /* 146 * Improve boot performance over NFS 147 */ 148 static void 149 set_nfs_read_size(void) 150 { 151 char *env, *end; 152 char buf[10]; 153 154 if ((env = getenv("nfs.read_size")) != NULL) { 155 errno = 0; 156 nfs_read_size = (int)strtol(env, &end, 0); 157 if (errno != 0 || *env == '\0' || *end != '\0') { 158 printf("%s: bad value: \"%s\", defaulting to %d\n", 159 "nfs.read_size", env, NFSREAD_MIN_SIZE); 160 nfs_read_size = NFSREAD_MIN_SIZE; 161 } 162 } 163 if (nfs_read_size < NFSREAD_MIN_SIZE) { 164 printf("%s: bad value: \"%d\", defaulting to %d\n", 165 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE); 166 nfs_read_size = NFSREAD_MIN_SIZE; 167 } 168 if (nfs_read_size > NFSREAD_MAX_SIZE) { 169 printf("%s: bad value: \"%d\", defaulting to %d\n", 170 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE); 171 nfs_read_size = NFSREAD_MAX_SIZE; 172 } 173 snprintf(buf, sizeof (buf), "%d", nfs_read_size); 174 setenv("nfs.read_size", buf, 1); 175 } 176 177 /* 178 * Fetch the root file handle (call mount daemon) 179 * Return zero or error number. 180 */ 181 int 182 nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) 183 { 184 void *pkt = NULL; 185 int len; 186 struct args { 187 uint32_t len; 188 char path[FNAME_SIZE]; 189 } *args; 190 struct repl { 191 uint32_t errno; 192 uint32_t fhsize; 193 u_char fh[NFS_V3MAXFHSIZE]; 194 uint32_t authcnt; 195 uint32_t auth[7]; 196 } *repl; 197 struct { 198 uint32_t h[RPC_HEADER_WORDS]; 199 struct args d; 200 } sdata; 201 size_t cc; 202 203 #ifdef NFS_DEBUG 204 if (debug) 205 printf("nfs_getrootfh: %s\n", path); 206 #endif 207 208 args = &sdata.d; 209 210 bzero(args, sizeof(*args)); 211 len = strlen(path); 212 if (len > sizeof(args->path)) 213 len = sizeof(args->path); 214 args->len = htonl(len); 215 bcopy(path, args->path, len); 216 len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t)); 217 218 cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT, 219 args, len, (void **)&repl, &pkt); 220 if (cc == -1) { 221 free(pkt); 222 /* errno was set by rpc_call */ 223 return (errno); 224 } 225 if (cc < 2 * sizeof (uint32_t)) { 226 free(pkt); 227 return (EBADRPC); 228 } 229 if (repl->errno != 0) { 230 free(pkt); 231 return (ntohl(repl->errno)); 232 } 233 *fhlenp = ntohl(repl->fhsize); 234 bcopy(repl->fh, fhp, *fhlenp); 235 236 set_nfs_read_size(); 237 free(pkt); 238 return (0); 239 } 240 241 /* 242 * Lookup a file. Store handle and attributes. 243 * Return zero or error number. 244 */ 245 int 246 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) 247 { 248 void *pkt = NULL; 249 int len, pos; 250 struct args { 251 uint32_t fhsize; 252 uint32_t fhplusname[1 + 253 (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)]; 254 } *args; 255 struct repl { 256 uint32_t errno; 257 uint32_t fhsize; 258 uint32_t fhplusattr[(NFS_V3MAXFHSIZE + 259 2 * (sizeof(uint32_t) + 260 sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)]; 261 } *repl; 262 struct { 263 uint32_t h[RPC_HEADER_WORDS]; 264 struct args d; 265 } sdata; 266 ssize_t cc; 267 268 #ifdef NFS_DEBUG 269 if (debug) 270 printf("lookupfh: called\n"); 271 #endif 272 273 args = &sdata.d; 274 275 bzero(args, sizeof(*args)); 276 args->fhsize = htonl(d->fhsize); 277 bcopy(d->fh, args->fhplusname, d->fhsize); 278 len = strlen(name); 279 if (len > FNAME_SIZE) 280 len = FNAME_SIZE; 281 pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); 282 args->fhplusname[pos++] = htonl(len); 283 bcopy(name, &args->fhplusname[pos], len); 284 len = sizeof(uint32_t) + pos * sizeof(uint32_t) + 285 roundup(len, sizeof(uint32_t)); 286 287 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP, 288 args, len, (void **)&repl, &pkt); 289 if (cc == -1) { 290 free(pkt); 291 return (errno); /* XXX - from rpc_call */ 292 } 293 if (cc < 2 * sizeof(uint32_t)) { 294 free(pkt); 295 return (EIO); 296 } 297 if (repl->errno != 0) { 298 free(pkt); 299 /* saerrno.h now matches NFS error numbers. */ 300 return (ntohl(repl->errno)); 301 } 302 newfd->fhsize = ntohl(repl->fhsize); 303 bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize); 304 pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); 305 if (repl->fhplusattr[pos++] == 0) { 306 free(pkt); 307 return (EIO); 308 } 309 bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa)); 310 free(pkt); 311 return (0); 312 } 313 314 #ifndef NFS_NOSYMLINK 315 /* 316 * Get the destination of a symbolic link. 317 */ 318 int 319 nfs_readlink(struct nfs_iodesc *d, char *buf) 320 { 321 void *pkt = NULL; 322 struct args { 323 uint32_t fhsize; 324 u_char fh[NFS_V3MAXFHSIZE]; 325 } *args; 326 struct repl { 327 uint32_t errno; 328 uint32_t ok; 329 struct nfsv3_fattrs fa; 330 uint32_t len; 331 u_char path[NFS_MAXPATHLEN]; 332 } *repl; 333 struct { 334 uint32_t h[RPC_HEADER_WORDS]; 335 struct args d; 336 } sdata; 337 ssize_t cc; 338 int rc = 0; 339 340 #ifdef NFS_DEBUG 341 if (debug) 342 printf("readlink: called\n"); 343 #endif 344 345 args = &sdata.d; 346 347 bzero(args, sizeof(*args)); 348 args->fhsize = htonl(d->fhsize); 349 bcopy(d->fh, args->fh, d->fhsize); 350 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK, 351 args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)), 352 (void **)&repl, &pkt); 353 if (cc == -1) 354 return (errno); 355 356 if (cc < 2 * sizeof(uint32_t)) { 357 rc = EIO; 358 goto done; 359 } 360 361 if (repl->errno != 0) { 362 rc = ntohl(repl->errno); 363 goto done; 364 } 365 366 if (repl->ok == 0) { 367 rc = EIO; 368 goto done; 369 } 370 371 repl->len = ntohl(repl->len); 372 if (repl->len > NFS_MAXPATHLEN) { 373 rc = ENAMETOOLONG; 374 goto done; 375 } 376 377 bcopy(repl->path, buf, repl->len); 378 buf[repl->len] = 0; 379 done: 380 free(pkt); 381 return (rc); 382 } 383 #endif 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 devdesc *dev; 465 struct iodesc *desc; 466 struct nfs_iodesc *currfd = NULL; 467 char buf[2 * NFS_V3MAXFHSIZE + 3]; 468 u_char *fh; 469 char *cp; 470 int i; 471 #ifndef NFS_NOSYMLINK 472 struct nfs_iodesc *newfd = NULL; 473 char *ncp; 474 int c; 475 char namebuf[NFS_MAXPATHLEN + 1]; 476 char linkbuf[NFS_MAXPATHLEN + 1]; 477 int nlinks = 0; 478 #endif 479 int error; 480 char *path = NULL; 481 482 if (netproto != NET_NFS) 483 return (EINVAL); 484 485 dev = f->f_devdata; 486 #ifdef NFS_DEBUG 487 if (debug) 488 printf("nfs_open: %s (rootip=%s rootpath=%s)\n", upath, 489 inet_ntoa(rootip), rootpath); 490 #endif 491 if (!rootpath[0]) { 492 printf("no rootpath, no nfs\n"); 493 return (ENXIO); 494 } 495 496 if (f->f_dev->dv_type != DEVT_NET) 497 return (EINVAL); 498 499 if (!(desc = socktodesc(*(int *)(dev->d_opendata)))) 500 return (EINVAL); 501 502 /* Bind to a reserved port. */ 503 desc->myport = htons(--rpc_port); 504 desc->destip = rootip; 505 if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize, 506 nfs_root_node.fh))) 507 return (error); 508 nfs_root_node.fa.fa_type = htonl(NFDIR); 509 nfs_root_node.fa.fa_mode = htonl(0755); 510 nfs_root_node.fa.fa_nlink = htonl(2); 511 nfs_root_node.iodesc = desc; 512 513 fh = &nfs_root_node.fh[0]; 514 buf[0] = 'X'; 515 cp = &buf[1]; 516 for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2) 517 sprintf(cp, "%02x", fh[i]); 518 sprintf(cp, "X"); 519 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); 520 setenv("boot.nfsroot.path", rootpath, 1); 521 setenv("boot.nfsroot.nfshandle", buf, 1); 522 sprintf(buf, "%d", nfs_root_node.fhsize); 523 setenv("boot.nfsroot.nfshandlelen", buf, 1); 524 525 /* Allocate file system specific data structure */ 526 currfd = malloc(sizeof(*newfd)); 527 if (currfd == NULL) { 528 error = ENOMEM; 529 goto out; 530 } 531 #ifndef NFS_NOSYMLINK 532 bcopy(&nfs_root_node, currfd, sizeof(*currfd)); 533 newfd = NULL; 534 535 cp = path = strdup(upath); 536 if (path == NULL) { 537 error = ENOMEM; 538 goto out; 539 } 540 while (*cp) { 541 /* 542 * Remove extra separators 543 */ 544 while (*cp == '/') 545 cp++; 546 547 if (*cp == '\0') 548 break; 549 /* 550 * Check that current node is a directory. 551 */ 552 if (currfd->fa.fa_type != htonl(NFDIR)) { 553 error = ENOTDIR; 554 goto out; 555 } 556 557 /* allocate file system specific data structure */ 558 newfd = malloc(sizeof(*newfd)); 559 if (newfd == NULL) { 560 error = ENOMEM; 561 goto out; 562 } 563 newfd->iodesc = currfd->iodesc; 564 565 /* 566 * Get next component of path name. 567 */ 568 { 569 int len = 0; 570 571 ncp = cp; 572 while ((c = *cp) != '\0' && c != '/') { 573 if (++len > NFS_MAXNAMLEN) { 574 error = ENOENT; 575 goto out; 576 } 577 cp++; 578 } 579 *cp = '\0'; 580 } 581 582 /* lookup a file handle */ 583 error = nfs_lookupfh(currfd, ncp, newfd); 584 *cp = c; 585 if (error) 586 goto out; 587 588 /* 589 * Check for symbolic link 590 */ 591 if (newfd->fa.fa_type == htonl(NFLNK)) { 592 int link_len, len; 593 594 error = nfs_readlink(newfd, linkbuf); 595 if (error) 596 goto out; 597 598 link_len = strlen(linkbuf); 599 len = strlen(cp); 600 601 if (link_len + len > MAXPATHLEN 602 || ++nlinks > MAXSYMLINKS) { 603 error = ENOENT; 604 goto out; 605 } 606 607 bcopy(cp, &namebuf[link_len], len + 1); 608 bcopy(linkbuf, namebuf, link_len); 609 610 /* 611 * If absolute pathname, restart at root. 612 * If relative pathname, restart at parent directory. 613 */ 614 cp = namebuf; 615 if (*cp == '/') 616 bcopy(&nfs_root_node, currfd, sizeof(*currfd)); 617 618 free(newfd); 619 newfd = NULL; 620 621 continue; 622 } 623 624 free(currfd); 625 currfd = newfd; 626 newfd = NULL; 627 } 628 629 error = 0; 630 631 out: 632 free(newfd); 633 free(path); 634 #else 635 currfd->iodesc = desc; 636 637 error = nfs_lookupfh(&nfs_root_node, upath, currfd); 638 #endif 639 if (!error) { 640 currfd->off = 0; 641 currfd->cookie = 0; 642 f->f_fsdata = (void *)currfd; 643 return (0); 644 } 645 646 #ifdef NFS_DEBUG 647 if (debug) 648 printf("nfs_open: %s lookupfh failed: %s\n", 649 path, strerror(error)); 650 #endif 651 free(currfd); 652 653 return (error); 654 } 655 656 int 657 nfs_close(struct open_file *f) 658 { 659 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 660 661 #ifdef NFS_DEBUG 662 if (debug) 663 printf("nfs_close: fp=0x%lx\n", (u_long)fp); 664 #endif 665 666 free(fp); 667 f->f_fsdata = NULL; 668 669 return (0); 670 } 671 672 /* 673 * read a portion of a file 674 */ 675 int 676 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 677 { 678 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 679 ssize_t cc; 680 char *addr = buf; 681 682 #ifdef NFS_DEBUG 683 if (debug) 684 printf("nfs_read: size=%lu off=%d\n", (u_long)size, 685 (int)fp->off); 686 #endif 687 while ((int)size > 0) { 688 twiddle(16); 689 cc = nfs_readdata(fp, fp->off, (void *)addr, size); 690 /* XXX maybe should retry on certain errors */ 691 if (cc == -1) { 692 #ifdef NFS_DEBUG 693 if (debug) 694 printf("nfs_read: read: %s\n", strerror(errno)); 695 #endif 696 return (errno); /* XXX - from nfs_readdata */ 697 } 698 if (cc == 0) { 699 #ifdef NFS_DEBUG 700 if (debug) 701 printf("nfs_read: hit EOF unexpectedly\n"); 702 #endif 703 goto ret; 704 } 705 fp->off += cc; 706 addr += cc; 707 size -= cc; 708 } 709 ret: 710 if (resid) 711 *resid = size; 712 713 return (0); 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 = (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 } 850