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 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/time.h> 36 #include <sys/socket.h> 37 #include <sys/stat.h> 38 #include <string.h> 39 #include <stddef.h> 40 41 #include <netinet/in.h> 42 #include <netinet/in_systm.h> 43 44 #include "rpcv2.h" 45 #include "nfsv2.h" 46 47 #include "stand.h" 48 #include "net.h" 49 #include "netif.h" 50 #include "rpc.h" 51 52 #define NFS_DEBUGxx 53 54 #define NFSREAD_MIN_SIZE 1024 55 #define NFSREAD_MAX_SIZE 16384 56 57 /* NFSv3 definitions */ 58 #define NFS_V3MAXFHSIZE 64 59 #define NFS_VER3 3 60 #define RPCMNT_VER3 3 61 #define NFSPROCV3_LOOKUP 3 62 #define NFSPROCV3_READLINK 5 63 #define NFSPROCV3_READ 6 64 #define NFSPROCV3_READDIR 16 65 66 typedef struct { 67 uint32_t val[2]; 68 } n_quad; 69 70 struct nfsv3_time { 71 uint32_t nfs_sec; 72 uint32_t nfs_nsec; 73 }; 74 75 struct nfsv3_fattrs { 76 uint32_t fa_type; 77 uint32_t fa_mode; 78 uint32_t fa_nlink; 79 uint32_t fa_uid; 80 uint32_t fa_gid; 81 n_quad fa_size; 82 n_quad fa_used; 83 n_quad fa_rdev; 84 n_quad fa_fsid; 85 n_quad fa_fileid; 86 struct nfsv3_time fa_atime; 87 struct nfsv3_time fa_mtime; 88 struct nfsv3_time fa_ctime; 89 }; 90 91 /* 92 * For NFSv3, the file handle is variable in size, so most fixed sized 93 * structures for arguments won't work. For most cases, a structure 94 * that starts with any fixed size section is followed by an array 95 * that covers the maximum size required. 96 */ 97 struct nfsv3_readdir_repl { 98 uint32_t errno; 99 uint32_t ok; 100 struct nfsv3_fattrs fa; 101 uint32_t cookiev0; 102 uint32_t cookiev1; 103 }; 104 105 struct nfsv3_readdir_entry { 106 uint32_t follows; 107 uint32_t fid0; 108 uint32_t fid1; 109 uint32_t len; 110 uint32_t nameplus[0]; 111 }; 112 113 struct nfs_iodesc { 114 struct iodesc *iodesc; 115 off_t off; 116 uint32_t fhsize; 117 u_char fh[NFS_V3MAXFHSIZE]; 118 struct nfsv3_fattrs fa; /* all in network order */ 119 uint64_t cookie; 120 }; 121 122 /* 123 * XXX interactions with tftp? See nfswrapper.c for a confusing 124 * issue. 125 */ 126 int nfs_open(const char *path, struct open_file *f); 127 static int nfs_close(struct open_file *f); 128 static int nfs_read(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 null_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 = (int)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, 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 #ifndef NFS_NOSYMLINK 318 /* 319 * Get the destination of a symbolic link. 320 */ 321 int 322 nfs_readlink(struct nfs_iodesc *d, char *buf) 323 { 324 void *pkt = NULL; 325 struct args { 326 uint32_t fhsize; 327 u_char fh[NFS_V3MAXFHSIZE]; 328 } *args; 329 struct repl { 330 uint32_t errno; 331 uint32_t ok; 332 struct nfsv3_fattrs fa; 333 uint32_t len; 334 u_char path[NFS_MAXPATHLEN]; 335 } *repl; 336 struct { 337 uint32_t h[RPC_HEADER_WORDS]; 338 struct args d; 339 } sdata; 340 ssize_t cc; 341 int rc = 0; 342 343 #ifdef NFS_DEBUG 344 if (debug) 345 printf("readlink: called\n"); 346 #endif 347 348 args = &sdata.d; 349 350 bzero(args, sizeof(*args)); 351 args->fhsize = htonl(d->fhsize); 352 bcopy(d->fh, args->fh, d->fhsize); 353 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK, 354 args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)), 355 (void **)&repl, &pkt); 356 if (cc == -1) 357 return (errno); 358 359 if (cc < 2 * sizeof(uint32_t)) { 360 rc = EIO; 361 goto done; 362 } 363 364 if (repl->errno != 0) { 365 rc = ntohl(repl->errno); 366 goto done; 367 } 368 369 if (repl->ok == 0) { 370 rc = EIO; 371 goto done; 372 } 373 374 repl->len = ntohl(repl->len); 375 if (repl->len > NFS_MAXPATHLEN) { 376 rc = ENAMETOOLONG; 377 goto done; 378 } 379 380 bcopy(repl->path, buf, repl->len); 381 buf[repl->len] = 0; 382 done: 383 free(pkt); 384 return (rc); 385 } 386 #endif 387 388 /* 389 * Read data from a file. 390 * Return transfer count or -1 (and set errno) 391 */ 392 ssize_t 393 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) 394 { 395 void *pkt = NULL; 396 struct args { 397 uint32_t fhsize; 398 uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3]; 399 } *args; 400 struct repl { 401 uint32_t errno; 402 uint32_t ok; 403 struct nfsv3_fattrs fa; 404 uint32_t count; 405 uint32_t eof; 406 uint32_t len; 407 u_char data[NFSREAD_MAX_SIZE]; 408 } *repl; 409 struct { 410 uint32_t h[RPC_HEADER_WORDS]; 411 struct args d; 412 } sdata; 413 size_t cc; 414 long x; 415 int hlen, rlen, pos; 416 417 args = &sdata.d; 418 419 bzero(args, sizeof(*args)); 420 args->fhsize = htonl(d->fhsize); 421 bcopy(d->fh, args->fhoffcnt, d->fhsize); 422 pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); 423 args->fhoffcnt[pos++] = 0; 424 args->fhoffcnt[pos++] = htonl((uint32_t)off); 425 if (len > nfs_read_size) 426 len = nfs_read_size; 427 args->fhoffcnt[pos] = htonl((uint32_t)len); 428 hlen = offsetof(struct repl, data[0]); 429 430 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ, 431 args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)), 432 (void **)&repl, &pkt); 433 if (cc == -1) { 434 /* errno was already set by rpc_call */ 435 return (-1); 436 } 437 if (cc < hlen) { 438 errno = EBADRPC; 439 free(pkt); 440 return (-1); 441 } 442 if (repl->errno != 0) { 443 errno = ntohl(repl->errno); 444 free(pkt); 445 return (-1); 446 } 447 rlen = cc - hlen; 448 x = ntohl(repl->count); 449 if (rlen < x) { 450 printf("nfsread: short packet, %d < %ld\n", rlen, x); 451 errno = EBADRPC; 452 free(pkt); 453 return (-1); 454 } 455 bcopy(repl->data, addr, x); 456 free(pkt); 457 return (x); 458 } 459 460 /* 461 * Open a file. 462 * return zero or error number 463 */ 464 int 465 nfs_open(const char *upath, struct open_file *f) 466 { 467 struct devdesc *dev; 468 struct iodesc *desc; 469 struct nfs_iodesc *currfd = NULL; 470 char buf[2 * NFS_V3MAXFHSIZE + 3]; 471 u_char *fh; 472 char *cp; 473 int i; 474 #ifndef NFS_NOSYMLINK 475 struct nfs_iodesc *newfd = NULL; 476 char *ncp; 477 int c; 478 char namebuf[NFS_MAXPATHLEN + 1]; 479 char linkbuf[NFS_MAXPATHLEN + 1]; 480 int nlinks = 0; 481 #endif 482 int error; 483 char *path = NULL; 484 485 if (netproto != NET_NFS) 486 return (EINVAL); 487 488 dev = f->f_devdata; 489 #ifdef NFS_DEBUG 490 if (debug) 491 printf("nfs_open: %s (rootip=%s rootpath=%s)\n", upath, 492 inet_ntoa(rootip), rootpath); 493 #endif 494 if (!rootpath[0]) { 495 printf("no rootpath, no nfs\n"); 496 return (ENXIO); 497 } 498 499 if (f->f_dev->dv_type != DEVT_NET) 500 return (EINVAL); 501 502 if (!(desc = socktodesc(*(int *)(dev->d_opendata)))) 503 return (EINVAL); 504 505 /* Bind to a reserved port. */ 506 desc->myport = htons(--rpc_port); 507 desc->destip = rootip; 508 if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize, 509 nfs_root_node.fh))) 510 return (error); 511 nfs_root_node.fa.fa_type = htonl(NFDIR); 512 nfs_root_node.fa.fa_mode = htonl(0755); 513 nfs_root_node.fa.fa_nlink = htonl(2); 514 nfs_root_node.iodesc = desc; 515 516 fh = &nfs_root_node.fh[0]; 517 buf[0] = 'X'; 518 cp = &buf[1]; 519 for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2) 520 sprintf(cp, "%02x", fh[i]); 521 sprintf(cp, "X"); 522 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); 523 setenv("boot.nfsroot.path", rootpath, 1); 524 setenv("boot.nfsroot.nfshandle", buf, 1); 525 sprintf(buf, "%d", nfs_root_node.fhsize); 526 setenv("boot.nfsroot.nfshandlelen", buf, 1); 527 528 /* Allocate file system specific data structure */ 529 currfd = malloc(sizeof(*newfd)); 530 if (currfd == NULL) { 531 error = ENOMEM; 532 goto out; 533 } 534 #ifndef NFS_NOSYMLINK 535 bcopy(&nfs_root_node, currfd, sizeof(*currfd)); 536 newfd = NULL; 537 538 cp = path = strdup(upath); 539 if (path == NULL) { 540 error = ENOMEM; 541 goto out; 542 } 543 while (*cp) { 544 /* 545 * Remove extra separators 546 */ 547 while (*cp == '/') 548 cp++; 549 550 if (*cp == '\0') 551 break; 552 /* 553 * Check that current node is a directory. 554 */ 555 if (currfd->fa.fa_type != htonl(NFDIR)) { 556 error = ENOTDIR; 557 goto out; 558 } 559 560 /* allocate file system specific data structure */ 561 newfd = malloc(sizeof(*newfd)); 562 if (newfd == NULL) { 563 error = ENOMEM; 564 goto out; 565 } 566 newfd->iodesc = currfd->iodesc; 567 568 /* 569 * Get next component of path name. 570 */ 571 { 572 int len = 0; 573 574 ncp = cp; 575 while ((c = *cp) != '\0' && c != '/') { 576 if (++len > NFS_MAXNAMLEN) { 577 error = ENOENT; 578 goto out; 579 } 580 cp++; 581 } 582 *cp = '\0'; 583 } 584 585 /* lookup a file handle */ 586 error = nfs_lookupfh(currfd, ncp, newfd); 587 *cp = c; 588 if (error) 589 goto out; 590 591 /* 592 * Check for symbolic link 593 */ 594 if (newfd->fa.fa_type == htonl(NFLNK)) { 595 int link_len, len; 596 597 error = nfs_readlink(newfd, linkbuf); 598 if (error) 599 goto out; 600 601 link_len = strlen(linkbuf); 602 len = strlen(cp); 603 604 if (link_len + len > MAXPATHLEN 605 || ++nlinks > MAXSYMLINKS) { 606 error = ENOENT; 607 goto out; 608 } 609 610 bcopy(cp, &namebuf[link_len], len + 1); 611 bcopy(linkbuf, namebuf, link_len); 612 613 /* 614 * If absolute pathname, restart at root. 615 * If relative pathname, restart at parent directory. 616 */ 617 cp = namebuf; 618 if (*cp == '/') 619 bcopy(&nfs_root_node, currfd, sizeof(*currfd)); 620 621 free(newfd); 622 newfd = NULL; 623 624 continue; 625 } 626 627 free(currfd); 628 currfd = newfd; 629 newfd = NULL; 630 } 631 632 error = 0; 633 634 out: 635 free(newfd); 636 free(path); 637 #else 638 currfd->iodesc = desc; 639 640 error = nfs_lookupfh(&nfs_root_node, upath, currfd); 641 #endif 642 if (!error) { 643 currfd->off = 0; 644 currfd->cookie = 0; 645 f->f_fsdata = (void *)currfd; 646 return (0); 647 } 648 649 #ifdef NFS_DEBUG 650 if (debug) 651 printf("nfs_open: %s lookupfh failed: %s\n", 652 path, strerror(error)); 653 #endif 654 free(currfd); 655 656 return (error); 657 } 658 659 int 660 nfs_close(struct open_file *f) 661 { 662 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 663 664 #ifdef NFS_DEBUG 665 if (debug) 666 printf("nfs_close: fp=0x%lx\n", (u_long)fp); 667 #endif 668 669 free(fp); 670 f->f_fsdata = NULL; 671 672 return (0); 673 } 674 675 /* 676 * read a portion of a file 677 */ 678 int 679 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 680 { 681 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 682 ssize_t cc; 683 char *addr = buf; 684 685 #ifdef NFS_DEBUG 686 if (debug) 687 printf("nfs_read: size=%lu off=%d\n", (u_long)size, 688 (int)fp->off); 689 #endif 690 while ((int)size > 0) { 691 twiddle(16); 692 cc = nfs_readdata(fp, fp->off, (void *)addr, size); 693 /* XXX maybe should retry on certain errors */ 694 if (cc == -1) { 695 #ifdef NFS_DEBUG 696 if (debug) 697 printf("nfs_read: read: %s\n", strerror(errno)); 698 #endif 699 return (errno); /* XXX - from nfs_readdata */ 700 } 701 if (cc == 0) { 702 #ifdef NFS_DEBUG 703 if (debug) 704 printf("nfs_read: hit EOF unexpectedly\n"); 705 #endif 706 goto ret; 707 } 708 fp->off += cc; 709 addr += cc; 710 size -= cc; 711 } 712 ret: 713 if (resid) 714 *resid = size; 715 716 return (0); 717 } 718 719 off_t 720 nfs_seek(struct open_file *f, off_t offset, int where) 721 { 722 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata; 723 uint32_t size = ntohl(d->fa.fa_size.val[1]); 724 725 switch (where) { 726 case SEEK_SET: 727 d->off = offset; 728 break; 729 case SEEK_CUR: 730 d->off += offset; 731 break; 732 case SEEK_END: 733 d->off = size - offset; 734 break; 735 default: 736 errno = EINVAL; 737 return (-1); 738 } 739 740 return (d->off); 741 } 742 743 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */ 744 int nfs_stat_types[9] = { 745 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 }; 746 747 int 748 nfs_stat(struct open_file *f, struct stat *sb) 749 { 750 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 751 uint32_t ftype, mode; 752 753 ftype = ntohl(fp->fa.fa_type); 754 mode = ntohl(fp->fa.fa_mode); 755 mode |= nfs_stat_types[ftype & 7]; 756 757 sb->st_mode = mode; 758 sb->st_nlink = ntohl(fp->fa.fa_nlink); 759 sb->st_uid = ntohl(fp->fa.fa_uid); 760 sb->st_gid = ntohl(fp->fa.fa_gid); 761 sb->st_size = ntohl(fp->fa.fa_size.val[1]); 762 763 return (0); 764 } 765 766 static int 767 nfs_readdir(struct open_file *f, struct dirent *d) 768 { 769 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 770 struct nfsv3_readdir_repl *repl; 771 struct nfsv3_readdir_entry *rent; 772 static void *pkt = NULL; 773 static char *buf; 774 static struct nfs_iodesc *pfp = NULL; 775 static uint64_t cookie = 0; 776 size_t cc; 777 int pos, rc; 778 779 struct args { 780 uint32_t fhsize; 781 uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE]; 782 } *args; 783 struct { 784 uint32_t h[RPC_HEADER_WORDS]; 785 struct args d; 786 } sdata; 787 788 if (fp != pfp || fp->off != cookie) { 789 pfp = NULL; 790 refill: 791 free(pkt); 792 pkt = NULL; 793 args = &sdata.d; 794 bzero(args, sizeof(*args)); 795 796 args->fhsize = htonl(fp->fhsize); 797 bcopy(fp->fh, args->fhpluscookie, fp->fhsize); 798 pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); 799 args->fhpluscookie[pos++] = htonl(fp->off >> 32); 800 args->fhpluscookie[pos++] = htonl(fp->off); 801 args->fhpluscookie[pos++] = htonl(fp->cookie >> 32); 802 args->fhpluscookie[pos++] = htonl(fp->cookie); 803 args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE); 804 805 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR, 806 args, 6 * sizeof(uint32_t) + 807 roundup(fp->fhsize, sizeof(uint32_t)), 808 (void **)&buf, &pkt); 809 if (cc == -1) { 810 rc = errno; 811 goto err; 812 } 813 repl = (struct nfsv3_readdir_repl *)buf; 814 if (repl->errno != 0) { 815 rc = ntohl(repl->errno); 816 goto err; 817 } 818 pfp = fp; 819 cookie = fp->off; 820 fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) | 821 ntohl(repl->cookiev1); 822 buf += sizeof (struct nfsv3_readdir_repl); 823 } 824 rent = (struct nfsv3_readdir_entry *)buf; 825 826 if (rent->follows == 0) { 827 /* fid0 is actually eof */ 828 if (rent->fid0 != 0) { 829 rc = ENOENT; 830 goto err; 831 } 832 goto refill; 833 } 834 835 d->d_namlen = ntohl(rent->len); 836 bcopy(rent->nameplus, d->d_name, d->d_namlen); 837 d->d_name[d->d_namlen] = '\0'; 838 839 pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t); 840 fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) | 841 ntohl(rent->nameplus[pos + 1]); 842 pos += 2; 843 buf = (char *)&rent->nameplus[pos]; 844 return (0); 845 846 err: 847 free(pkt); 848 pkt = NULL; 849 pfp = NULL; 850 cookie = 0; 851 return (rc); 852 } 853