nfs.c revision 1.46
1/* $NetBSD: nfs.c,v 1.46 2011/06/16 13:27:58 joerg 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 57#include "stand.h" 58#include "net.h" 59#include "nfs.h" 60#include "rpc.h" 61 62/* Define our own NFS attributes */ 63struct nfsv2_fattrs { 64 n_long fa_type; 65 n_long fa_mode; 66 n_long fa_nlink; 67 n_long fa_uid; 68 n_long fa_gid; 69 n_long fa_size; 70 n_long fa_blocksize; 71 n_long fa_rdev; 72 n_long fa_blocks; 73 n_long fa_fsid; 74 n_long fa_fileid; 75 struct nfsv2_time fa_atime; 76 struct nfsv2_time fa_mtime; 77 struct nfsv2_time fa_ctime; 78}; 79 80 81struct nfs_read_args { 82 u_char fh[NFS_FHSIZE]; 83 n_long off; 84 n_long len; 85 n_long xxx; /* XXX what's this for? */ 86}; 87 88/* Data part of nfs rpc reply (also the largest thing we receive) */ 89#define NFSREAD_SIZE 1024 90struct nfs_read_repl { 91 n_long errno; 92 struct nfsv2_fattrs fa; 93 n_long count; 94 u_char data[NFSREAD_SIZE]; 95}; 96 97#ifndef NFS_NOSYMLINK 98struct nfs_readlnk_repl { 99 n_long errno; 100 n_long len; 101 char path[NFS_MAXPATHLEN]; 102}; 103#endif 104 105struct nfs_iodesc { 106 struct iodesc *iodesc; 107 off_t off; 108 u_char fh[NFS_FHSIZE]; 109 struct nfsv2_fattrs fa; /* all in network order */ 110}; 111 112struct nfs_iodesc nfs_root_node; 113 114int nfs_getrootfh(struct iodesc *, char *, u_char *); 115int nfs_lookupfh(struct nfs_iodesc *, const char *, int, 116 struct nfs_iodesc *); 117int nfs_readlink(struct nfs_iodesc *, char *); 118ssize_t nfs_readdata(struct nfs_iodesc *, off_t, void *, size_t); 119 120/* 121 * Fetch the root file handle (call mount daemon) 122 * On error, return non-zero and set errno. 123 */ 124int 125nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp) 126{ 127 int len; 128 struct args { 129 n_long len; 130 char path[FNAME_SIZE]; 131 } *args; 132 struct repl { 133 n_long errno; 134 u_char fh[NFS_FHSIZE]; 135 } *repl; 136 struct { 137 n_long h[RPC_HEADER_WORDS]; 138 struct args d; 139 } sdata; 140 struct { 141 n_long h[RPC_HEADER_WORDS]; 142 struct repl d; 143 } rdata; 144 ssize_t cc; 145 146#ifdef NFS_DEBUG 147 if (debug) 148 printf("nfs_getrootfh: %s\n", path); 149#endif 150 151 args = &sdata.d; 152 repl = &rdata.d; 153 154 (void)memset(args, 0, sizeof(*args)); 155 len = strlen(path); 156 if ((size_t)len > sizeof(args->path)) 157 len = sizeof(args->path); 158 args->len = htonl(len); 159 (void)memcpy(args->path, path, len); 160 len = 4 + roundup(len, 4); 161 162 cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT, 163 args, len, repl, sizeof(*repl)); 164 if (cc == -1) { 165 /* errno was set by rpc_call */ 166 return -1; 167 } 168 if (cc < 4) { 169 errno = EBADRPC; 170 return -1; 171 } 172 if (repl->errno) { 173 errno = ntohl(repl->errno); 174 return -1; 175 } 176 (void)memcpy(fhp, repl->fh, sizeof(repl->fh)); 177 return 0; 178} 179 180/* 181 * Lookup a file. Store handle and attributes. 182 * Return zero or error number. 183 */ 184int 185nfs_lookupfh(struct nfs_iodesc *d, const char *name, int len, 186 struct nfs_iodesc *newfd) 187{ 188 int rlen; 189 struct args { 190 u_char fh[NFS_FHSIZE]; 191 n_long len; 192 char name[FNAME_SIZE]; 193 } *args; 194 struct repl { 195 n_long errno; 196 u_char fh[NFS_FHSIZE]; 197 struct nfsv2_fattrs fa; 198 } *repl; 199 struct { 200 n_long h[RPC_HEADER_WORDS]; 201 struct args d; 202 } sdata; 203 struct { 204 n_long h[RPC_HEADER_WORDS]; 205 struct repl d; 206 } rdata; 207 ssize_t cc; 208 209#ifdef NFS_DEBUG 210 if (debug) 211 printf("lookupfh: called\n"); 212#endif 213 214 args = &sdata.d; 215 repl = &rdata.d; 216 217 (void)memset(args, 0, sizeof(*args)); 218 (void)memcpy(args->fh, d->fh, sizeof(args->fh)); 219 if ((size_t)len > sizeof(args->name)) 220 len = sizeof(args->name); 221 (void)memcpy(args->name, name, len); 222 args->len = htonl(len); 223 len = 4 + roundup(len, 4); 224 len += NFS_FHSIZE; 225 226 rlen = sizeof(*repl); 227 228 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP, 229 args, len, repl, rlen); 230 if (cc == -1) 231 return errno; /* XXX - from rpc_call */ 232 if (cc < 4) 233 return EIO; 234 if (repl->errno) { 235 /* saerrno.h now matches NFS error numbers. */ 236 return ntohl(repl->errno); 237 } 238 (void)memcpy(&newfd->fh, repl->fh, sizeof(newfd->fh)); 239 (void)memcpy(&newfd->fa, &repl->fa, sizeof(newfd->fa)); 240 return 0; 241} 242 243#ifndef NFS_NOSYMLINK 244/* 245 * Get the destination of a symbolic link. 246 */ 247int 248nfs_readlink(struct nfs_iodesc *d, char *buf) 249{ 250 struct { 251 n_long h[RPC_HEADER_WORDS]; 252 u_char fh[NFS_FHSIZE]; 253 } sdata; 254 struct { 255 n_long h[RPC_HEADER_WORDS]; 256 struct nfs_readlnk_repl d; 257 } rdata; 258 ssize_t cc; 259 260#ifdef NFS_DEBUG 261 if (debug) 262 printf("readlink: called\n"); 263#endif 264 265 (void)memcpy(sdata.fh, d->fh, NFS_FHSIZE); 266 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK, 267 sdata.fh, NFS_FHSIZE, 268 &rdata.d, sizeof(rdata.d)); 269 if (cc == -1) 270 return errno; 271 272 if (cc < 4) 273 return EIO; 274 275 if (rdata.d.errno) 276 return ntohl(rdata.d.errno); 277 278 rdata.d.len = ntohl(rdata.d.len); 279 if (rdata.d.len > NFS_MAXPATHLEN) 280 return ENAMETOOLONG; 281 282 (void)memcpy(buf, rdata.d.path, rdata.d.len); 283 buf[rdata.d.len] = 0; 284 return 0; 285} 286#endif 287 288/* 289 * Read data from a file. 290 * Return transfer count or -1 (and set errno) 291 */ 292ssize_t 293nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) 294{ 295 struct nfs_read_args *args; 296 struct nfs_read_repl *repl; 297 struct { 298 n_long h[RPC_HEADER_WORDS]; 299 struct nfs_read_args d; 300 } sdata; 301 struct { 302 n_long h[RPC_HEADER_WORDS]; 303 struct nfs_read_repl d; 304 } rdata; 305 ssize_t cc; 306 long x; 307 size_t hlen, rlen; 308 309 args = &sdata.d; 310 repl = &rdata.d; 311 312 (void)memcpy(args->fh, d->fh, NFS_FHSIZE); 313 args->off = htonl((n_long)off); 314 if (len > NFSREAD_SIZE) 315 len = NFSREAD_SIZE; 316 args->len = htonl((n_long)len); 317 args->xxx = htonl((n_long)0); 318 hlen = sizeof(*repl) - NFSREAD_SIZE; 319 320 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ, 321 args, sizeof(*args), 322 repl, sizeof(*repl)); 323 if (cc == -1) { 324 /* errno was already set by rpc_call */ 325 return -1; 326 } 327 if (cc < (ssize_t)hlen) { 328 errno = EBADRPC; 329 return -1; 330 } 331 if (repl->errno) { 332 errno = ntohl(repl->errno); 333 return -1; 334 } 335 rlen = cc - hlen; 336 x = ntohl(repl->count); 337 if (rlen < (size_t)x) { 338 printf("nfsread: short packet, %lu < %ld\n", (u_long) rlen, x); 339 errno = EBADRPC; 340 return -1; 341 } 342 (void)memcpy(addr, repl->data, x); 343 return x; 344} 345 346/* 347 * nfs_mount - mount this nfs filesystem to a host 348 * On error, return non-zero and set errno. 349 */ 350int 351nfs_mount(int sock, struct in_addr ip, char *path) 352{ 353 struct iodesc *desc; 354 struct nfsv2_fattrs *fa; 355 356 if (!(desc = socktodesc(sock))) { 357 errno = EINVAL; 358 return -1; 359 } 360 361 /* Bind to a reserved port. */ 362 desc->myport = htons(--rpc_port); 363 desc->destip = ip; 364 if (nfs_getrootfh(desc, path, nfs_root_node.fh)) 365 return -1; 366 nfs_root_node.iodesc = desc; 367 /* Fake up attributes for the root dir. */ 368 fa = &nfs_root_node.fa; 369 fa->fa_type = htonl(NFDIR); 370 fa->fa_mode = htonl(0755); 371 fa->fa_nlink = htonl(2); 372 373#ifdef NFS_DEBUG 374 if (debug) 375 printf("nfs_mount: got fh for %s\n", path); 376#endif 377 378 return 0; 379} 380 381/* 382 * Open a file. 383 * return zero or error number 384 */ 385__compactcall int 386nfs_open(const char *path, struct open_file *f) 387{ 388 struct nfs_iodesc *newfd, *currfd; 389 const char *cp; 390#ifndef NFS_NOSYMLINK 391 const char *ncp; 392 int c; 393 char namebuf[NFS_MAXPATHLEN + 1]; 394 char linkbuf[NFS_MAXPATHLEN + 1]; 395 int nlinks = 0; 396#endif 397 int error = 0; 398 399#ifdef NFS_DEBUG 400 if (debug) 401 printf("nfs_open: %s\n", path); 402#endif 403 if (nfs_root_node.iodesc == NULL) { 404 printf("nfs_open: must mount first.\n"); 405 return ENXIO; 406 } 407 408 currfd = &nfs_root_node; 409 newfd = 0; 410 411#ifndef NFS_NOSYMLINK 412 cp = path; 413 while (*cp) { 414 /* 415 * Remove extra separators 416 */ 417 while (*cp == '/') 418 cp++; 419 420 if (*cp == '\0') 421 break; 422 /* 423 * Check that current node is a directory. 424 */ 425 if (currfd->fa.fa_type != htonl(NFDIR)) { 426 error = ENOTDIR; 427 goto out; 428 } 429 430 /* allocate file system specific data structure */ 431 newfd = alloc(sizeof(*newfd)); 432 newfd->iodesc = currfd->iodesc; 433 newfd->off = 0; 434 435 /* 436 * Get next component of path name. 437 */ 438 { 439 int len = 0; 440 441 ncp = cp; 442 while ((c = *cp) != '\0' && c != '/') { 443 if (++len > NFS_MAXNAMLEN) { 444 error = ENOENT; 445 goto out; 446 } 447 cp++; 448 } 449 } 450 451 /* lookup a file handle */ 452 error = nfs_lookupfh(currfd, ncp, cp - ncp, newfd); 453 if (error) 454 goto out; 455 456 /* 457 * Check for symbolic link 458 */ 459 if (newfd->fa.fa_type == htonl(NFLNK)) { 460 int link_len, len; 461 462 error = nfs_readlink(newfd, linkbuf); 463 if (error) 464 goto out; 465 466 link_len = strlen(linkbuf); 467 len = strlen(cp); 468 469 if (link_len + len > MAXPATHLEN 470 || ++nlinks > MAXSYMLINKS) { 471 error = ENOENT; 472 goto out; 473 } 474 475 (void)memcpy(&namebuf[link_len], cp, len + 1); 476 (void)memcpy(namebuf, linkbuf, link_len); 477 478 /* 479 * If absolute pathname, restart at root. 480 * If relative pathname, restart at parent directory. 481 */ 482 cp = namebuf; 483 if (*cp == '/') { 484 if (currfd != &nfs_root_node) 485 dealloc(currfd, sizeof(*currfd)); 486 currfd = &nfs_root_node; 487 } 488 489 dealloc(newfd, sizeof(*newfd)); 490 newfd = 0; 491 492 continue; 493 } 494 495 if (currfd != &nfs_root_node) 496 dealloc(currfd, sizeof(*currfd)); 497 currfd = newfd; 498 newfd = 0; 499 } 500 501 error = 0; 502 503out: 504#else 505 /* allocate file system specific data structure */ 506 currfd = alloc(sizeof(*currfd)); 507 currfd->iodesc = nfs_root_node.iodesc; 508 currfd->off = 0; 509 510 cp = path; 511 /* 512 * Remove extra separators 513 */ 514 while (*cp == '/') 515 cp++; 516 517 /* XXX: Check for empty path here? */ 518 519 error = nfs_lookupfh(&nfs_root_node, cp, strlen(cp), currfd); 520#endif 521 if (!error) { 522 f->f_fsdata = (void *)currfd; 523 fsmod = "nfs"; 524 return 0; 525 } 526 527#ifdef NFS_DEBUG 528 if (debug) 529 printf("nfs_open: %s lookupfh failed: %s\n", 530 path, strerror(error)); 531#endif 532 if (currfd != &nfs_root_node) 533 dealloc(currfd, sizeof(*currfd)); 534 if (newfd) 535 dealloc(newfd, sizeof(*newfd)); 536 537 return error; 538} 539 540__compactcall int 541nfs_close(struct open_file *f) 542{ 543 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 544 545#ifdef NFS_DEBUG 546 if (debug) 547 printf("nfs_close: fp=0x%lx\n", (u_long)fp); 548#endif 549 550 if (fp) 551 dealloc(fp, sizeof(struct nfs_iodesc)); 552 f->f_fsdata = (void *)0; 553 554 return 0; 555} 556 557/* 558 * read a portion of a file 559 */ 560__compactcall int 561nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 562{ 563 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 564 ssize_t cc; 565 char *addr = buf; 566 567#ifdef NFS_DEBUG 568 if (debug) 569 printf("nfs_read: size=%lu off=%d\n", (u_long)size, 570 (int)fp->off); 571#endif 572 while ((int)size > 0) { 573#if !defined(LIBSA_NO_TWIDDLE) 574 twiddle(); 575#endif 576 cc = nfs_readdata(fp, fp->off, (void *)addr, size); 577 /* XXX maybe should retry on certain errors */ 578 if (cc == -1) { 579#ifdef NFS_DEBUG 580 if (debug) 581 printf("nfs_read: read: %s\n", 582 strerror(errno)); 583#endif 584 return errno; /* XXX - from nfs_readdata */ 585 } 586 if (cc == 0) { 587#ifdef NFS_DEBUG 588 if (debug) 589 printf("nfs_read: hit EOF unexpectantly\n"); 590#endif 591 goto ret; 592 } 593 fp->off += cc; 594 addr += cc; 595 size -= cc; 596 } 597ret: 598 if (resid) 599 *resid = size; 600 601 return 0; 602} 603 604/* 605 * Not implemented. 606 */ 607__compactcall int 608nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid) 609{ 610 return EROFS; 611} 612 613__compactcall off_t 614nfs_seek(struct open_file *f, off_t offset, int where) 615{ 616 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata; 617 n_long size = ntohl(d->fa.fa_size); 618 619 switch (where) { 620 case SEEK_SET: 621 d->off = offset; 622 break; 623 case SEEK_CUR: 624 d->off += offset; 625 break; 626 case SEEK_END: 627 d->off = size - offset; 628 break; 629 default: 630 return -1; 631 } 632 633 return d->off; 634} 635 636/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */ 637const int nfs_stat_types[8] = { 638 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 }; 639 640__compactcall int 641nfs_stat(struct open_file *f, struct stat *sb) 642{ 643 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; 644 n_long ftype, mode; 645 646 ftype = ntohl(fp->fa.fa_type); 647 mode = ntohl(fp->fa.fa_mode); 648 mode |= nfs_stat_types[ftype & 7]; 649 650 sb->st_mode = mode; 651 sb->st_nlink = ntohl(fp->fa.fa_nlink); 652 sb->st_uid = ntohl(fp->fa.fa_uid); 653 sb->st_gid = ntohl(fp->fa.fa_gid); 654 sb->st_size = ntohl(fp->fa.fa_size); 655 656 return 0; 657} 658