nfs.c revision 1.52
1/* $NetBSD: nfs.c,v 1.52 2023/12/14 05:39:00 rin 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/* 32 * XXX Does not currently implement: 33 * XXX 34 * XXX LIBSA_NO_FS_CLOSE 35 * XXX LIBSA_NO_FS_SEEK 36 * XXX LIBSA_NO_FS_WRITE 37 * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?) 38 * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?) 39 */ 40 41#include <sys/param.h> 42#include <sys/time.h> 43#include <sys/socket.h> 44#include <sys/stat.h> 45#ifdef _STANDALONE 46#include <lib/libkern/libkern.h> 47#else 48#include <string.h> 49#endif 50 51#include <netinet/in.h> 52#include <netinet/in_systm.h> 53 54#include "rpcv2.h" 55#include "nfsv2.h" 56#include "nfsv3.h" 57 58#include "stand.h" 59#include "net.h" 60#include "nfs.h" 61#include "rpc.h" 62 63/* Storage for any filehandle (including length for V3) */ 64#define NFS_FHSTORE (NFS_FHSIZE < NFS_V3FHSIZE ? NFS_V3FHSIZE + 4: NFS_FHSIZE) 65 66/* Data part of nfs rpc reply (also the largest thing we receive) */ 67#define NFSREAD_SIZE 1024 68 69#ifndef NFS_NOSYMLINK 70struct nfs_readlnk_repl { 71 n_long errno; 72 n_long len; 73 char path[NFS_MAXPATHLEN]; 74}; 75#endif 76 77static inline uint64_t 78getnquad(n_long x[2]) { 79 return (uint64_t)ntohl(x[0]) << 32 | ntohl(x[1]); 80} 81 82static inline void 83setnquad(n_long x[2], uint64_t v) 84{ 85 x[0] = htonl((n_long)(v >> 32)); 86 x[1] = htonl((n_long)(v & 0xffffffff)); 87} 88 89struct nfs_iodesc { 90 struct iodesc *iodesc; 91 off_t off; 92 int version; 93 u_char fh[NFS_FHSTORE]; 94 union { 95 /* all in network order */ 96 struct nfsv2_fattr v2; 97 struct nfsv3_fattr v3; 98 } u_fa; 99}; 100 101static inline size_t 102fhstore(int version, u_char *fh) 103{ 104 size_t len; 105 106 switch (version) { 107 case NFS_VER2: 108 len = NFS_FHSIZE; 109 break; 110 case NFS_VER3: 111 len = fh[0] << 24 | fh[1] << 16 | fh[2] << 8 | fh[3]; 112 if (len > NFS_V3FHSIZE) 113 len = NFS_V3FHSIZE; 114 len = 4 + roundup(len, 4); 115 break; 116 default: 117 len = 0; 118 break; 119 } 120 121 return len; 122} 123 124static inline size_t 125fhcopy(int version, u_char *src, u_char *dst) 126{ 127 size_t len = fhstore(version, src); 128 memcpy(dst, src, len); 129 return len; 130} 131 132#define setfh(d, s) fhcopy((d)->version, (s), (d)->fh) 133#define getfh(d, s) fhcopy((d)->version, (d)->fh, (s)) 134 135 136struct nfs_iodesc nfs_root_node; 137 138int nfs_getrootfh(struct iodesc *, char *, u_char *, int *); 139int nfs_lookupfh(struct nfs_iodesc *, const char *, int, 140 struct nfs_iodesc *); 141int nfs_readlink(struct nfs_iodesc *, char *); 142ssize_t nfs_readdata(struct nfs_iodesc *, off_t, void *, size_t); 143 144/* 145 * Fetch the root file handle (call mount daemon) 146 * On error, return non-zero and set errno. 147 */ 148int 149nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp, int *versionp) 150{ 151 int len; 152 struct args { 153 n_long len; 154 char path[FNAME_SIZE]; 155 } *args; 156 struct repl { 157 n_long errno; 158 u_char fh[NFS_FHSTORE]; 159 } *repl; 160 struct { 161 n_long h[RPC_HEADER_WORDS]; 162 struct args d; 163 } sdata; 164 struct { 165 n_long h[RPC_HEADER_WORDS]; 166 struct repl d; 167 } rdata; 168 ssize_t cc; 169 170#ifdef NFS_DEBUG 171 if (debug) 172 printf("%s: %s\n", __func__, path); 173#endif 174 175 args = &sdata.d; 176 repl = &rdata.d; 177 178 (void)memset(args, 0, sizeof(*args)); 179 len = strlen(path); 180 if ((size_t)len > sizeof(args->path)) 181 len = sizeof(args->path); 182 args->len = htonl(len); 183 (void)memcpy(args->path, path, len); 184 len = 4 + roundup(len, 4); 185 186 *versionp = NFS_VER3; 187 cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT, 188 args, len, repl, sizeof(*repl)); 189 if (cc == -1 || cc < 4 || repl->errno) { 190 *versionp = NFS_VER2; 191 cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT, 192 args, len, repl, sizeof(*repl)); 193 } 194 if (cc == -1) { 195 /* errno was set by rpc_call */ 196 return -1; 197 } 198 if (cc < 4) { 199 errno = EBADRPC; 200 return -1; 201 } 202 if (repl->errno) { 203 errno = ntohl(repl->errno); 204 return -1; 205 } 206 fhcopy(*versionp, repl->fh, fhp); 207 return 0; 208} 209 210/* 211 * Lookup a file. Store handle and attributes. 212 * Return zero or error number. 213 */ 214int 215nfs_lookupfh(struct nfs_iodesc *d, const char *name, int len, 216 struct nfs_iodesc *newfd) 217{ 218 struct argsv2 { 219 u_char fh[NFS_FHSIZE]; 220 n_long len; 221 char name[FNAME_SIZE]; 222 } *argsv2; 223 struct argsv3 { 224 u_char fh[NFS_FHSTORE]; 225 n_long len; 226 char name[FNAME_SIZE]; 227 } *argsv3; 228 struct replv2 { 229 n_long errno; 230 u_char fh[NFS_FHSIZE]; 231 struct nfsv2_fattr fa; 232 } *replv2; 233 struct replv3 { 234 n_long errno; 235 u_char fh[NFS_FHSTORE]; 236 n_long fattrflag; 237 struct nfsv3_fattr fa; 238 n_long dattrflag; 239 struct nfsv3_fattr da; 240 } *replv3; 241 struct { 242 n_long h[RPC_HEADER_WORDS]; 243 union { 244 struct argsv2 v2; 245 struct argsv3 v3; 246 } u_d; 247 } sdata; 248 struct { 249 n_long h[RPC_HEADER_WORDS]; 250 union { 251 struct replv2 v2; 252 struct replv3 v3; 253 } u_d; 254 } rdata; 255 ssize_t cc; 256 size_t alen; 257 258#ifdef NFS_DEBUG 259 if (debug) 260 printf("%s: called\n", __func__); 261#endif 262 263 argsv2 = &sdata.u_d.v2; 264 argsv3 = &sdata.u_d.v3; 265 replv2 = &rdata.u_d.v2; 266 replv3 = &rdata.u_d.v3; 267 268 switch (d->version) { 269 case NFS_VER2: 270 (void)memset(argsv2, 0, sizeof(*argsv2)); 271 getfh(d, argsv2->fh); 272 if ((size_t)len > sizeof(argsv2->name)) 273 len = sizeof(argsv2->name); 274 (void)memcpy(argsv2->name, name, len); 275 argsv2->len = htonl(len); 276 277 /* padded name, name length */ 278 len = roundup(len, 4) + 4; 279 /* filehandle size */ 280 alen = fhstore(d->version, argsv2->fh); 281 282 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP, 283 argsv2, alen+len, replv2, sizeof(*replv2)); 284 break; 285 case NFS_VER3: 286 (void)memset(argsv3, 0, sizeof(*argsv3)); 287 getfh(d, argsv3->fh); 288 if ((size_t)len > sizeof(argsv3->name)) 289 len = sizeof(argsv3->name); 290 (void)memcpy(argsv3->name, name, len); 291 argsv3->len = htonl(len); 292 293 /* padded name, name length */ 294 len = roundup(len, 4) + 4; 295 /* filehandle size */ 296 alen = fhstore(d->version, argsv3->fh); 297 298 /* adjust for variable sized file handle */ 299 memmove(argsv3->fh + alen, &argsv3->len, len); 300 301 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSV3PROC_LOOKUP, 302 argsv3, alen+len, replv3, sizeof(*replv3)); 303 break; 304 default: 305 return ENOSYS; 306 } 307 308 if (cc == -1) 309 return errno; /* XXX - from rpc_call */ 310 if (cc < 4) 311 return EIO; 312 313 switch (d->version) { 314 case NFS_VER2: 315 if (replv2->errno) { 316 /* saerrno.h now matches NFS error numbers. */ 317 return ntohl(replv2->errno); 318 } 319 320 setfh(newfd, replv2->fh); 321 (void)memcpy(&newfd->u_fa.v2, &replv2->fa, 322 sizeof(newfd->u_fa.v2)); 323 break; 324 case NFS_VER3: 325 if (replv3->errno) { 326 /* saerrno.h now matches NFS error numbers. */ 327 return ntohl(replv3->errno); 328 } 329 330 setfh(newfd, replv3->fh); 331 332 if (replv3->fattrflag) { 333 (void)memcpy(&newfd->u_fa.v3, &replv3->fa, 334 sizeof(newfd->u_fa.v3)); 335 } 336 break; 337 } 338 return 0; 339} 340 341#ifndef NFS_NOSYMLINK 342/* 343 * Get the destination of a symbolic link. 344 */ 345int 346nfs_readlink(struct nfs_iodesc *d, char *buf) 347{ 348 struct { 349 n_long h[RPC_HEADER_WORDS]; 350 u_char fh[NFS_FHSTORE]; 351 } sdata; 352 struct { 353 n_long h[RPC_HEADER_WORDS]; 354 struct nfs_readlnk_repl d; 355 } rdata; 356 ssize_t cc; 357 358#ifdef NFS_DEBUG 359 if (debug) 360 printf("%s: called\n", __func__); 361#endif 362 363 getfh(d, sdata.fh); 364 cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READLINK, 365 sdata.fh, fhstore(d->version, sdata.fh), 366 &rdata.d, sizeof(rdata.d)); 367 if (cc == -1) 368 return errno; 369 370 if (cc < 4) 371 return EIO; 372 373 if (rdata.d.errno) 374 return ntohl(rdata.d.errno); 375 376 rdata.d.len = ntohl(rdata.d.len); 377 if (rdata.d.len > NFS_MAXPATHLEN) 378 return ENAMETOOLONG; 379 380 (void)memcpy(buf, rdata.d.path, rdata.d.len); 381 buf[rdata.d.len] = 0; 382 return 0; 383} 384#endif 385 386/* 387 * Read data from a file. 388 * Return transfer count or -1 (and set errno) 389 */ 390ssize_t 391nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) 392{ 393 struct argsv2 { 394 u_char fh[NFS_FHSIZE]; 395 n_long off; 396 n_long len; 397 n_long xxx; /* XXX what's this for? */ 398 } *argsv2; 399 struct argsv3 { 400 u_char fh[NFS_FHSTORE]; 401 n_long off[2]; 402 n_long len; 403 } *argsv3; 404 struct replv2 { 405 n_long errno; 406 struct nfsv2_fattr fa; 407 n_long count; 408 u_char data[NFSREAD_SIZE]; 409 } *replv2; 410 struct replv3 { 411 n_long errno; 412 n_long attrflag; 413 struct nfsv3_fattr fa; 414 n_long count; 415 n_long eof; 416 n_long length; 417 u_char data[NFSREAD_SIZE]; 418 } *replv3; 419 struct replv3_noattr { 420 n_long errno; 421 n_long attrflag; 422 n_long count; 423 n_long eof; 424 n_long length; 425 u_char data[NFSREAD_SIZE]; 426 } *replv3no; 427 struct { 428 n_long h[RPC_HEADER_WORDS]; 429 union { 430 struct argsv2 v2; 431 struct argsv3 v3; 432 } u_d; 433 } sdata; 434 struct { 435 n_long h[RPC_HEADER_WORDS]; 436 union { 437 struct replv2 v2; 438 struct replv3 v3; 439 } u_d; 440 } rdata; 441 ssize_t cc; 442 long x; 443 size_t hlen, rlen, alen; 444 u_char *data; 445 446 argsv2 = &sdata.u_d.v2; 447 argsv3 = &sdata.u_d.v3; 448 replv2 = &rdata.u_d.v2; 449 replv3 = &rdata.u_d.v3; 450 451 if (len > NFSREAD_SIZE) 452 len = NFSREAD_SIZE; 453 454 switch (d->version) { 455 case NFS_VER2: 456 getfh(d, argsv2->fh); 457 argsv2->off = htonl((n_long)off); 458 argsv2->len = htonl((n_long)len); 459 argsv2->xxx = htonl((n_long)0); 460 hlen = sizeof(*replv2) - NFSREAD_SIZE; 461 cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READ, 462 argsv2, sizeof(*argsv2), 463 replv2, sizeof(*replv2)); 464 break; 465 case NFS_VER3: 466 getfh(d, argsv3->fh); 467 setnquad(argsv3->off, (uint64_t)off); 468 argsv3->len = htonl((n_long)len); 469 hlen = sizeof(*replv3) - NFSREAD_SIZE; 470 471 /* adjust for variable sized file handle */ 472 alen = sizeof(*argsv3) - offsetof(struct argsv3, off); 473 memmove(argsv3->fh + fhstore(d->version, argsv3->fh), 474 &argsv3->off, alen); 475 alen += fhstore(d->version, argsv3->fh); 476 477 cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READ, 478 argsv3, alen, 479 replv3, sizeof(*replv3)); 480 break; 481 default: 482 errno = ENOSYS; 483 return -1; 484 } 485 486 if (cc == -1) { 487 /* errno was already set by rpc_call */ 488 return -1; 489 } 490 if (cc < (ssize_t)hlen) { 491 errno = EBADRPC; 492 return -1; 493 } 494 495 switch (d->version) { 496 case NFS_VER2: 497 if (replv2->errno) { 498 errno = ntohl(replv2->errno); 499 return -1; 500 } 501 x = ntohl(replv2->count); 502 data = replv2->data; 503 break; 504 case NFS_VER3: 505 if (replv3->errno) { 506 errno = ntohl(replv3->errno); 507 return -1; 508 } 509 510 /* adjust for optional attributes */ 511 if (replv3->attrflag) { 512 x = ntohl(replv3->length); 513 data = replv3->data; 514 } else { 515 replv3no = (struct replv3_noattr *)replv3; 516 x = ntohl(replv3no->length); 517 data = replv3no->data; 518 } 519 break; 520 default: 521 errno = ENOSYS; 522 return -1; 523 } 524 525 rlen = cc - hlen; 526 if (rlen < (size_t)x) { 527 printf("%s: short packet, %zu < %ld\n", __func__, rlen, x); 528 errno = EBADRPC; 529 return -1; 530 } 531 (void)memcpy(addr, data, x); 532 return x; 533} 534 535/* 536 * nfs_mount - mount this nfs filesystem to a host 537 * On error, return non-zero and set errno. 538 */ 539int 540nfs_mount(int sock, struct in_addr ip, char *path) 541{ 542 struct iodesc *desc; 543 struct nfsv2_fattr *fa2; 544 struct nfsv3_fattr *fa3; 545 546 if (!(desc = socktodesc(sock))) { 547 errno = EINVAL; 548 return -1; 549 } 550 551 /* Bind to a reserved port. */ 552 desc->myport = htons(--rpc_port); 553 desc->destip = ip; 554 if (nfs_getrootfh(desc, path, nfs_root_node.fh, &nfs_root_node.version)) 555 return -1; 556 nfs_root_node.iodesc = desc; 557 /* Fake up attributes for the root dir. */ 558 switch (nfs_root_node.version) { 559 case NFS_VER2: 560 fa2 = &nfs_root_node.u_fa.v2; 561 fa2->fa_type = htonl(NFDIR); 562 fa2->fa_mode = htonl(0755); 563 fa2->fa_nlink = htonl(2); 564 break; 565 case NFS_VER3: 566 fa3 = &nfs_root_node.u_fa.v3; 567 fa3->fa_type = htonl(NFDIR); 568 fa3->fa_mode = htonl(0755); 569 fa3->fa_nlink = htonl(2); 570 break; 571 default: 572 errno = ENOSYS; 573 return -1; 574 } 575 576#ifdef NFS_DEBUG 577 if (debug) 578 printf("%s: got fh for %s\n", __func__, path); 579#endif 580 581 return 0; 582} 583 584/* 585 * Open a file. 586 * return zero or error number 587 */ 588__compactcall int 589nfs_open(const char *path, struct open_file *f) 590{ 591 struct nfs_iodesc *newfd, *currfd; 592 const char *cp; 593#ifndef NFS_NOSYMLINK 594 const char *ncp; 595 int c; 596 char namebuf[NFS_MAXPATHLEN + 1]; 597 char linkbuf[NFS_MAXPATHLEN + 1]; 598 int nlinks = 0; 599 n_long fa_type; 600#endif 601 int error = 0; 602 603#ifdef NFS_DEBUG 604 if (debug) 605 printf("%s: %s\n", __func__, path); 606#endif 607 608 if (nfs_root_node.iodesc == NULL) { 609 printf("%s: must mount first.\n", __func__); 610 return ENXIO; 611 } 612 613 currfd = &nfs_root_node; 614 newfd = 0; 615 616#ifndef NFS_NOSYMLINK 617 cp = path; 618 while (*cp) { 619 /* 620 * Remove extra separators 621 */ 622 while (*cp == '/') 623 cp++; 624 625 if (*cp == '\0') 626 break; 627 /* 628 * Check that current node is a directory. 629 */ 630 switch (currfd->version) { 631 case NFS_VER2: 632 fa_type = currfd->u_fa.v2.fa_type; 633 break; 634 case NFS_VER3: 635 fa_type = currfd->u_fa.v3.fa_type; 636 break; 637 default: 638 fa_type = htonl(NFNON); 639 break; 640 } 641 if (fa_type != htonl(NFDIR)) { 642 error = ENOTDIR; 643 goto out; 644 } 645 646 /* allocate file system specific data structure */ 647 newfd = alloc(sizeof(*newfd)); 648 newfd->iodesc = currfd->iodesc; 649 newfd->off = 0; 650 newfd->version = currfd->version; 651 652 /* 653 * Get next component of path name. 654 */ 655 { 656 int len = 0; 657 658 ncp = cp; 659 while ((c = *cp) != '\0' && c != '/') { 660 if (++len > NFS_MAXNAMLEN) { 661 error = ENOENT; 662 goto out; 663 } 664 cp++; 665 } 666 } 667 668 /* lookup a file handle */ 669 error = nfs_lookupfh(currfd, ncp, cp - ncp, newfd); 670 if (error) 671 goto out; 672 673 /* 674 * Check for symbolic link 675 */ 676 switch (newfd->version) { 677 case NFS_VER2: 678 fa_type = newfd->u_fa.v2.fa_type; 679 break; 680 case NFS_VER3: 681 fa_type = newfd->u_fa.v3.fa_type; 682 break; 683 default: 684 fa_type = htonl(NFNON); 685 break; 686 } 687 if (fa_type == htonl(NFLNK)) { 688 int link_len, len; 689 690 error = nfs_readlink(newfd, linkbuf); 691 if (error) 692 goto out; 693 694 link_len = strlen(linkbuf); 695 len = strlen(cp); 696 697 if (link_len + len > MAXPATHLEN 698 || ++nlinks > MAXSYMLINKS) { 699 error = ENOENT; 700 goto out; 701 } 702 703 (void)memcpy(&namebuf[link_len], cp, len + 1); 704 (void)memcpy(namebuf, linkbuf, link_len); 705 706 /* 707 * If absolute pathname, restart at root. 708 * If relative pathname, restart at parent directory. 709 */ 710 cp = namebuf; 711 if (*cp == '/') { 712 if (currfd != &nfs_root_node) 713 dealloc(currfd, sizeof(*currfd)); 714 currfd = &nfs_root_node; 715 } 716 717 dealloc(newfd, sizeof(*newfd)); 718 newfd = 0; 719 720 continue; 721 } 722 723 if (currfd != &nfs_root_node) 724 dealloc(currfd, sizeof(*currfd)); 725 currfd = newfd; 726 newfd = 0; 727 } 728 729 error = 0; 730 731out: 732#else 733 /* allocate file system specific data structure */ 734 currfd = alloc(sizeof(*currfd)); 735 currfd->iodesc = nfs_root_node.iodesc; 736 currfd->off = 0; 737 currfd->version = nfs_root_node.version; 738 739 cp = path; 740 /* 741 * Remove extra separators 742 */ 743 while (*cp == '/') 744 cp++; 745 746 /* XXX: Check for empty path here? */ 747 748 error = nfs_lookupfh(&nfs_root_node, cp, strlen(cp), currfd); 749#endif 750 if (!error) { 751 f->f_fsdata = (void *)currfd; 752 fsmod = "nfs"; 753 return 0; 754 } 755 756#ifdef NFS_DEBUG 757 if (debug) 758 printf("%s: %s lookupfh failed: %s\n", __func__, 759 path, strerror(error)); 760#endif 761 if (currfd != &nfs_root_node) 762 dealloc(currfd, sizeof(*currfd)); 763 if (newfd) 764 dealloc(newfd, sizeof(*newfd)); 765 766 return error; 767} 768 769__compactcall int 770nfs_close(struct open_file *f) 771{ 772 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 773 774#ifdef NFS_DEBUG 775 if (debug) 776 printf("%s: fp=%p\n", __func__, fp); 777#endif 778 779 if (fp) 780 dealloc(fp, sizeof(struct nfs_iodesc)); 781 f->f_fsdata = (void *)0; 782 783 return 0; 784} 785 786/* 787 * read a portion of a file 788 */ 789__compactcall int 790nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 791{ 792 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 793 ssize_t cc; 794 char *addr = buf; 795 796#ifdef NFS_DEBUG 797 if (debug) 798 printf("%s: size=%zu off=%" PRIx64 "\n", __func__, size, fp->off); 799#endif 800 while ((int)size > 0) { 801#if !defined(LIBSA_NO_TWIDDLE) 802 twiddle(); 803#endif 804 cc = nfs_readdata(fp, fp->off, (void *)addr, size); 805 /* XXX maybe should retry on certain errors */ 806 if (cc == -1) { 807#ifdef NFS_DEBUG 808 if (debug) 809 printf("%s: read: %s\n", __func__, 810 strerror(errno)); 811#endif 812 return errno; /* XXX - from nfs_readdata */ 813 } 814 if (cc == 0) { 815#ifdef NFS_DEBUG 816 if (debug) 817 printf("%s: hit EOF unexpectedly\n", __func__); 818#endif 819 goto ret; 820 } 821 fp->off += cc; 822 addr += cc; 823 size -= cc; 824 } 825ret: 826 if (resid) 827 *resid = size; 828 829 return 0; 830} 831 832/* 833 * Not implemented. 834 */ 835__compactcall int 836nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid) 837{ 838 return EROFS; 839} 840 841__compactcall off_t 842nfs_seek(struct open_file *f, off_t offset, int where) 843{ 844 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata; 845 off_t size; 846 847 switch (d->version) { 848 case NFS_VER2: 849 size = ntohl(d->u_fa.v2.fa_size); 850 break; 851 case NFS_VER3: 852 size = getnquad(d->u_fa.v3.fa_size); 853 break; 854 default: 855 return -1; 856 } 857 858 switch (where) { 859 case SEEK_SET: 860 d->off = offset; 861 break; 862 case SEEK_CUR: 863 d->off += offset; 864 break; 865 case SEEK_END: 866 d->off = size - offset; 867 break; 868 default: 869 return -1; 870 } 871 872 return d->off; 873} 874 875/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */ 876const int nfs_stat_types[8] = { 877 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 }; 878 879__compactcall int 880nfs_stat(struct open_file *f, struct stat *sb) 881{ 882 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 883 n_long ftype, mode; 884 885 switch (fp->version) { 886 case NFS_VER2: 887 ftype = ntohl(fp->u_fa.v2.fa_type); 888 mode = ntohl(fp->u_fa.v2.fa_mode); 889 sb->st_nlink = ntohl(fp->u_fa.v2.fa_nlink); 890 sb->st_uid = ntohl(fp->u_fa.v2.fa_uid); 891 sb->st_gid = ntohl(fp->u_fa.v2.fa_gid); 892 sb->st_size = ntohl(fp->u_fa.v2.fa_size); 893 break; 894 case NFS_VER3: 895 ftype = ntohl(fp->u_fa.v3.fa_type); 896 mode = ntohl(fp->u_fa.v3.fa_mode); 897 sb->st_nlink = ntohl(fp->u_fa.v3.fa_nlink); 898 sb->st_uid = ntohl(fp->u_fa.v3.fa_uid); 899 sb->st_gid = ntohl(fp->u_fa.v3.fa_gid); 900 sb->st_size = getnquad(fp->u_fa.v3.fa_size); 901 break; 902 default: 903 return -1; 904 } 905 906 mode |= nfs_stat_types[ftype & 7]; 907 sb->st_mode = mode; 908 909 return 0; 910} 911 912#if defined(LIBSA_ENABLE_LS_OP) 913#include "ls.h" 914__compactcall void 915nfs_ls(struct open_file *f, const char *pattern) 916{ 917 lsunsup("nfs"); 918} 919#endif 920