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 #include <sys/param.h> 33 #include <sys/time.h> 34 #include <sys/socket.h> 35 #include <sys/stat.h> 36 #include <string.h> 37 #include <stddef.h> 38 39 #include <netinet/in.h> 40 #include <netinet/in_systm.h> 41 42 #include "rpcv2.h" 43 #include "nfsv2.h" 44 45 #include "stand.h" 46 #include "net.h" 47 #include "netif.h" 48 #include "rpc.h" 49 50 #define NFS_DEBUGxx 51 52 #define NFSREAD_MIN_SIZE 1024 53 #define NFSREAD_MAX_SIZE 16384 54 55 /* NFSv3 definitions */ 56 #define NFS_V3MAXFHSIZE 64 57 #define NFS_VER3 3 58 #define RPCMNT_VER3 3 59 #define NFSPROCV3_LOOKUP 3 60 #define NFSPROCV3_READLINK 5 61 #define NFSPROCV3_READ 6 62 #define NFSPROCV3_READDIR 16 63 64 typedef struct { 65 uint32_t val[2]; 66 } n_quad; 67 68 struct nfsv3_time { 69 uint32_t nfs_sec; 70 uint32_t nfs_nsec; 71 }; 72 73 struct nfsv3_fattrs { 74 uint32_t fa_type; 75 uint32_t fa_mode; 76 uint32_t fa_nlink; 77 uint32_t fa_uid; 78 uint32_t fa_gid; 79 n_quad fa_size; 80 n_quad fa_used; 81 n_quad fa_rdev; 82 n_quad fa_fsid; 83 n_quad fa_fileid; 84 struct nfsv3_time fa_atime; 85 struct nfsv3_time fa_mtime; 86 struct nfsv3_time fa_ctime; 87 }; 88 89 /* 90 * For NFSv3, the file handle is variable in size, so most fixed sized 91 * structures for arguments won't work. For most cases, a structure 92 * that starts with any fixed size section is followed by an array 93 * that covers the maximum size required. 94 */ 95 struct nfsv3_readdir_repl { 96 uint32_t errno; 97 uint32_t ok; 98 struct nfsv3_fattrs fa; 99 uint32_t cookiev0; 100 uint32_t cookiev1; 101 }; 102 103 struct nfsv3_readdir_entry { 104 uint32_t follows; 105 uint32_t fid0; 106 uint32_t fid1; 107 uint32_t len; 108 uint32_t nameplus[0]; 109 }; 110 111 struct nfs_iodesc { 112 struct iodesc *iodesc; 113 off_t off; 114 uint32_t fhsize; 115 u_char fh[NFS_V3MAXFHSIZE]; 116 struct nfsv3_fattrs fa; /* all in network order */ 117 uint64_t cookie; 118 }; 119 120 /* 121 * XXX interactions with tftp? See nfswrapper.c for a confusing 122 * issue. 123 */ 124 int nfs_open(const char *path, struct open_file *f); 125 static int nfs_close(struct open_file *f); 126 static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 127 static off_t nfs_seek(struct open_file *f, off_t offset, int where); 128 static int nfs_stat(struct open_file *f, struct stat *sb); 129 static int nfs_readdir(struct open_file *f, struct dirent *d); 130 131 struct nfs_iodesc nfs_root_node; 132 133 struct fs_ops nfs_fsops = { 134 .fs_name = "nfs", 135 .fo_open = nfs_open, 136 .fo_close = nfs_close, 137 .fo_read = nfs_read, 138 .fo_write = null_write, 139 .fo_seek = nfs_seek, 140 .fo_stat = nfs_stat, 141 .fo_readdir = nfs_readdir, 142 }; 143 144 static int nfs_read_size = NFSREAD_MIN_SIZE; 145 146 /* 147 * Improve boot performance over NFS 148 */ 149 static void 150 set_nfs_read_size(void) 151 { 152 char *env, *end; 153 char buf[10]; 154 155 if ((env = getenv("nfs.read_size")) != NULL) { 156 errno = 0; 157 nfs_read_size = (int)strtol(env, &end, 0); 158 if (errno != 0 || *env == '\0' || *end != '\0') { 159 printf("%s: bad value: \"%s\", defaulting to %d\n", 160 "nfs.read_size", env, NFSREAD_MIN_SIZE); 161 nfs_read_size = NFSREAD_MIN_SIZE; 162 } 163 } 164 if (nfs_read_size < NFSREAD_MIN_SIZE) { 165 printf("%s: bad value: \"%d\", defaulting to %d\n", 166 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE); 167 nfs_read_size = NFSREAD_MIN_SIZE; 168 } 169 if (nfs_read_size > NFSREAD_MAX_SIZE) { 170 printf("%s: bad value: \"%d\", defaulting to %d\n", 171 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE); 172 nfs_read_size = NFSREAD_MAX_SIZE; 173 } 174 snprintf(buf, sizeof (buf), "%d", nfs_read_size); 175 setenv("nfs.read_size", buf, 1); 176 } 177 178 /* 179 * Fetch the root file handle (call mount daemon) 180 * Return zero or error number. 181 */ 182 int 183 nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) 184 { 185 void *pkt = NULL; 186 int len; 187 struct args { 188 uint32_t len; 189 char path[FNAME_SIZE]; 190 } *args; 191 struct repl { 192 uint32_t errno; 193 uint32_t fhsize; 194 u_char fh[NFS_V3MAXFHSIZE]; 195 uint32_t authcnt; 196 uint32_t auth[7]; 197 } *repl; 198 struct { 199 uint32_t h[RPC_HEADER_WORDS]; 200 struct args d; 201 } sdata; 202 size_t cc; 203 204 #ifdef NFS_DEBUG 205 if (debug) 206 printf("nfs_getrootfh: %s\n", path); 207 #endif 208 209 args = &sdata.d; 210 211 bzero(args, sizeof(*args)); 212 len = strlen(path); 213 if (len > sizeof(args->path)) 214 len = sizeof(args->path); 215 args->len = htonl(len); 216 bcopy(path, args->path, len); 217 len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t)); 218 219 cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT, 220 args, len, (void **)&repl, &pkt); 221 if (cc == -1) { 222 free(pkt); 223 /* errno was set by rpc_call */ 224 return (errno); 225 } 226 if (cc < 2 * sizeof (uint32_t)) { 227 free(pkt); 228 return (EBADRPC); 229 } 230 if (repl->errno != 0) { 231 free(pkt); 232 return (ntohl(repl->errno)); 233 } 234 *fhlenp = ntohl(repl->fhsize); 235 bcopy(repl->fh, fhp, *fhlenp); 236 237 set_nfs_read_size(); 238 free(pkt); 239 return (0); 240 } 241 242 /* 243 * Lookup a file. Store handle and attributes. 244 * Return zero or error number. 245 */ 246 int 247 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) 248 { 249 void *pkt = NULL; 250 int len, pos; 251 struct args { 252 uint32_t fhsize; 253 uint32_t fhplusname[1 + 254 (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)]; 255 } *args; 256 struct repl { 257 uint32_t errno; 258 uint32_t fhsize; 259 uint32_t fhplusattr[(NFS_V3MAXFHSIZE + 260 2 * (sizeof(uint32_t) + 261 sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)]; 262 } *repl; 263 struct { 264 uint32_t h[RPC_HEADER_WORDS]; 265 struct args d; 266 } sdata; 267 ssize_t cc; 268 269 #ifdef NFS_DEBUG 270 if (debug) 271 printf("lookupfh: called\n"); 272 #endif 273 274 args = &sdata.d; 275 276 bzero(args, sizeof(*args)); 277 args->fhsize = htonl(d->fhsize); 278 bcopy(d->fh, args->fhplusname, d->fhsize); 279 len = strlen(name); 280 if (len > FNAME_SIZE) 281 len = FNAME_SIZE; 282 pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); 283 args->fhplusname[pos++] = htonl(len); 284 bcopy(name, &args->fhplusname[pos], len); 285 len = sizeof(uint32_t) + pos * sizeof(uint32_t) + 286 roundup(len, sizeof(uint32_t)); 287 288 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP, 289 args, len, (void **)&repl, &pkt); 290 if (cc == -1) { 291 free(pkt); 292 return (errno); /* XXX - from rpc_call */ 293 } 294 if (cc < 2 * sizeof(uint32_t)) { 295 free(pkt); 296 return (EIO); 297 } 298 if (repl->errno != 0) { 299 free(pkt); 300 /* saerrno.h now matches NFS error numbers. */ 301 return (ntohl(repl->errno)); 302 } 303 newfd->fhsize = ntohl(repl->fhsize); 304 bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize); 305 pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); 306 if (repl->fhplusattr[pos++] == 0) { 307 free(pkt); 308 return (EIO); 309 } 310 bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa)); 311 free(pkt); 312 return (0); 313 } 314 315 #ifndef NFS_NOSYMLINK 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 #endif 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 devdesc *dev; 466 struct iodesc *desc; 467 struct nfs_iodesc *currfd = NULL; 468 char buf[2 * NFS_V3MAXFHSIZE + 3]; 469 u_char *fh; 470 char *cp; 471 int i; 472 #ifndef NFS_NOSYMLINK 473 struct nfs_iodesc *newfd = NULL; 474 char *ncp; 475 int c; 476 char namebuf[NFS_MAXPATHLEN + 1]; 477 char linkbuf[NFS_MAXPATHLEN + 1]; 478 int nlinks = 0; 479 #endif 480 int error; 481 char *path = NULL; 482 483 if (netproto != NET_NFS) 484 return (EINVAL); 485 486 dev = f->f_devdata; 487 #ifdef NFS_DEBUG 488 if (debug) 489 printf("nfs_open: %s (rootip=%s rootpath=%s)\n", upath, 490 inet_ntoa(rootip), rootpath); 491 #endif 492 if (!rootpath[0]) { 493 printf("no rootpath, no nfs\n"); 494 return (ENXIO); 495 } 496 497 if (f->f_dev->dv_type != DEVT_NET) 498 return (EINVAL); 499 500 if (!(desc = socktodesc(*(int *)(dev->d_opendata)))) 501 return (EINVAL); 502 503 /* Bind to a reserved port. */ 504 desc->myport = htons(--rpc_port); 505 desc->destip = rootip; 506 if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize, 507 nfs_root_node.fh))) 508 return (error); 509 nfs_root_node.fa.fa_type = htonl(NFDIR); 510 nfs_root_node.fa.fa_mode = htonl(0755); 511 nfs_root_node.fa.fa_nlink = htonl(2); 512 nfs_root_node.iodesc = desc; 513 514 fh = &nfs_root_node.fh[0]; 515 buf[0] = 'X'; 516 cp = &buf[1]; 517 for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2) 518 sprintf(cp, "%02x", fh[i]); 519 sprintf(cp, "X"); 520 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); 521 setenv("boot.nfsroot.path", rootpath, 1); 522 setenv("boot.nfsroot.nfshandle", buf, 1); 523 sprintf(buf, "%d", nfs_root_node.fhsize); 524 setenv("boot.nfsroot.nfshandlelen", buf, 1); 525 526 /* Allocate file system specific data structure */ 527 currfd = malloc(sizeof(*newfd)); 528 if (currfd == NULL) { 529 error = ENOMEM; 530 goto out; 531 } 532 #ifndef NFS_NOSYMLINK 533 bcopy(&nfs_root_node, currfd, sizeof(*currfd)); 534 newfd = NULL; 535 536 cp = path = strdup(upath); 537 if (path == NULL) { 538 error = ENOMEM; 539 goto out; 540 } 541 while (*cp) { 542 /* 543 * Remove extra separators 544 */ 545 while (*cp == '/') 546 cp++; 547 548 if (*cp == '\0') 549 break; 550 /* 551 * Check that current node is a directory. 552 */ 553 if (currfd->fa.fa_type != htonl(NFDIR)) { 554 error = ENOTDIR; 555 goto out; 556 } 557 558 /* allocate file system specific data structure */ 559 newfd = malloc(sizeof(*newfd)); 560 if (newfd == NULL) { 561 error = ENOMEM; 562 goto out; 563 } 564 newfd->iodesc = currfd->iodesc; 565 566 /* 567 * Get next component of path name. 568 */ 569 { 570 int len = 0; 571 572 ncp = cp; 573 while ((c = *cp) != '\0' && c != '/') { 574 if (++len > NFS_MAXNAMLEN) { 575 error = ENOENT; 576 goto out; 577 } 578 cp++; 579 } 580 *cp = '\0'; 581 } 582 583 /* lookup a file handle */ 584 error = nfs_lookupfh(currfd, ncp, newfd); 585 *cp = c; 586 if (error) 587 goto out; 588 589 /* 590 * Check for symbolic link 591 */ 592 if (newfd->fa.fa_type == htonl(NFLNK)) { 593 int link_len, len; 594 595 error = nfs_readlink(newfd, linkbuf); 596 if (error) 597 goto out; 598 599 link_len = strlen(linkbuf); 600 len = strlen(cp); 601 602 if (link_len + len > MAXPATHLEN 603 || ++nlinks > MAXSYMLINKS) { 604 error = ENOENT; 605 goto out; 606 } 607 608 bcopy(cp, &namebuf[link_len], len + 1); 609 bcopy(linkbuf, namebuf, link_len); 610 611 /* 612 * If absolute pathname, restart at root. 613 * If relative pathname, restart at parent directory. 614 */ 615 cp = namebuf; 616 if (*cp == '/') 617 bcopy(&nfs_root_node, currfd, sizeof(*currfd)); 618 619 free(newfd); 620 newfd = NULL; 621 622 continue; 623 } 624 625 free(currfd); 626 currfd = newfd; 627 newfd = NULL; 628 } 629 630 error = 0; 631 632 out: 633 free(newfd); 634 free(path); 635 #else 636 currfd->iodesc = desc; 637 638 error = nfs_lookupfh(&nfs_root_node, upath, currfd); 639 #endif 640 if (!error) { 641 currfd->off = 0; 642 currfd->cookie = 0; 643 f->f_fsdata = (void *)currfd; 644 return (0); 645 } 646 647 #ifdef NFS_DEBUG 648 if (debug) 649 printf("nfs_open: %s lookupfh failed: %s\n", 650 path, strerror(error)); 651 #endif 652 free(currfd); 653 654 return (error); 655 } 656 657 int 658 nfs_close(struct open_file *f) 659 { 660 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 661 662 #ifdef NFS_DEBUG 663 if (debug) 664 printf("nfs_close: fp=0x%lx\n", (u_long)fp); 665 #endif 666 667 free(fp); 668 f->f_fsdata = NULL; 669 670 return (0); 671 } 672 673 /* 674 * read a portion of a file 675 */ 676 int 677 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 678 { 679 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 680 ssize_t cc; 681 char *addr = buf; 682 683 #ifdef NFS_DEBUG 684 if (debug) 685 printf("nfs_read: size=%lu off=%d\n", (u_long)size, 686 (int)fp->off); 687 #endif 688 while ((int)size > 0) { 689 twiddle(16); 690 cc = nfs_readdata(fp, fp->off, (void *)addr, size); 691 /* XXX maybe should retry on certain errors */ 692 if (cc == -1) { 693 #ifdef NFS_DEBUG 694 if (debug) 695 printf("nfs_read: read: %s\n", strerror(errno)); 696 #endif 697 return (errno); /* XXX - from nfs_readdata */ 698 } 699 if (cc == 0) { 700 #ifdef NFS_DEBUG 701 if (debug) 702 printf("nfs_read: hit EOF unexpectedly\n"); 703 #endif 704 goto ret; 705 } 706 fp->off += cc; 707 addr += cc; 708 size -= cc; 709 } 710 ret: 711 if (resid) 712 *resid = size; 713 714 return (0); 715 } 716 717 off_t 718 nfs_seek(struct open_file *f, off_t offset, int where) 719 { 720 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata; 721 uint32_t size = ntohl(d->fa.fa_size.val[1]); 722 723 switch (where) { 724 case SEEK_SET: 725 d->off = offset; 726 break; 727 case SEEK_CUR: 728 d->off += offset; 729 break; 730 case SEEK_END: 731 d->off = size - offset; 732 break; 733 default: 734 errno = EINVAL; 735 return (-1); 736 } 737 738 return (d->off); 739 } 740 741 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */ 742 int nfs_stat_types[9] = { 743 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 }; 744 745 int 746 nfs_stat(struct open_file *f, struct stat *sb) 747 { 748 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 749 uint32_t ftype, mode; 750 751 ftype = ntohl(fp->fa.fa_type); 752 mode = ntohl(fp->fa.fa_mode); 753 mode |= nfs_stat_types[ftype & 7]; 754 755 sb->st_mode = mode; 756 sb->st_nlink = ntohl(fp->fa.fa_nlink); 757 sb->st_uid = ntohl(fp->fa.fa_uid); 758 sb->st_gid = ntohl(fp->fa.fa_gid); 759 sb->st_size = ntohl(fp->fa.fa_size.val[1]); 760 761 return (0); 762 } 763 764 static int 765 nfs_readdir(struct open_file *f, struct dirent *d) 766 { 767 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 768 struct nfsv3_readdir_repl *repl; 769 struct nfsv3_readdir_entry *rent; 770 static void *pkt = NULL; 771 static char *buf; 772 static struct nfs_iodesc *pfp = NULL; 773 static uint64_t cookie = 0; 774 size_t cc; 775 int pos, rc; 776 777 struct args { 778 uint32_t fhsize; 779 uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE]; 780 } *args; 781 struct { 782 uint32_t h[RPC_HEADER_WORDS]; 783 struct args d; 784 } sdata; 785 786 if (fp != pfp || fp->off != cookie) { 787 pfp = NULL; 788 refill: 789 free(pkt); 790 pkt = NULL; 791 args = &sdata.d; 792 bzero(args, sizeof(*args)); 793 794 args->fhsize = htonl(fp->fhsize); 795 bcopy(fp->fh, args->fhpluscookie, fp->fhsize); 796 pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); 797 args->fhpluscookie[pos++] = htonl(fp->off >> 32); 798 args->fhpluscookie[pos++] = htonl(fp->off); 799 args->fhpluscookie[pos++] = htonl(fp->cookie >> 32); 800 args->fhpluscookie[pos++] = htonl(fp->cookie); 801 args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE); 802 803 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR, 804 args, 6 * sizeof(uint32_t) + 805 roundup(fp->fhsize, sizeof(uint32_t)), 806 (void **)&buf, &pkt); 807 if (cc == -1) { 808 rc = errno; 809 goto err; 810 } 811 repl = (struct nfsv3_readdir_repl *)buf; 812 if (repl->errno != 0) { 813 rc = ntohl(repl->errno); 814 goto err; 815 } 816 pfp = fp; 817 cookie = fp->off; 818 fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) | 819 ntohl(repl->cookiev1); 820 buf += sizeof (struct nfsv3_readdir_repl); 821 } 822 rent = (struct nfsv3_readdir_entry *)buf; 823 824 if (rent->follows == 0) { 825 /* fid0 is actually eof */ 826 if (rent->fid0 != 0) { 827 rc = ENOENT; 828 goto err; 829 } 830 goto refill; 831 } 832 833 d->d_namlen = ntohl(rent->len); 834 bcopy(rent->nameplus, d->d_name, d->d_namlen); 835 d->d_name[d->d_namlen] = '\0'; 836 837 pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t); 838 fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) | 839 ntohl(rent->nameplus[pos + 1]); 840 pos += 2; 841 buf = (char *)&rent->nameplus[pos]; 842 return (0); 843 844 err: 845 free(pkt); 846 pkt = NULL; 847 pfp = NULL; 848 cookie = 0; 849 return (rc); 850 } 851