1implement Ftpfs; 2 3include "sys.m"; 4 sys: Sys; 5 FD, Dir: import Sys; 6 7include "draw.m"; 8 9include "arg.m"; 10 11include "bufio.m"; 12 bufio: Bufio; 13 Iobuf: import bufio; 14 15include "daytime.m"; 16 time: Daytime; 17 Tm: import time; 18 19include "string.m"; 20 str: String; 21 22include "styx.m"; 23 styx: Styx; 24 Tmsg, Rmsg: import styx; 25 26include "dial.m"; 27 dial: Dial; 28 Connection: import dial; 29 30include "factotum.m"; 31 32Ftpfs: module 33{ 34 init: fn(nil: ref Draw->Context, argv: list of string); 35}; 36 37# 38# File system node. Refers to parent and file structure. 39# Siblings are linked. The head is parent.children. 40# 41 42Node: adt 43{ 44 dir: Dir; 45 uniq: int; 46 parent: cyclic ref Node; 47 sibs: cyclic ref Node; 48 children: cyclic ref Node; 49 file: cyclic ref File; 50 depth: int; 51 remname: string; 52 cached: int; 53 valid: int; 54 55 extendpath: fn(parent: self ref Node, elem: string): ref Node; 56 fixsymbolic: fn(n: self ref Node); 57 invalidate: fn(n: self ref Node); 58 markcached: fn(n: self ref Node); 59 uncache: fn(n: self ref Node); 60 uncachedir: fn(parent: self ref Node, child: ref Node); 61 62 stat: fn(n: self ref Node): array of byte; 63 qid: fn(n: self ref Node): Sys->Qid; 64 65 fileget: fn(n: self ref Node): ref File; 66 filefree: fn(n: self ref Node); 67 fileclean: fn(n: self ref Node); 68 fileisdirty: fn(n: self ref Node): int; 69 filedirty: fn(n: self ref Node); 70 fileread: fn(n: self ref Node, b: array of byte, off, c: int): int; 71 filewrite: fn(n: self ref Node, b: array of byte, off, c: int): int; 72 73 action: fn(n: self ref Node, cmd: string): int; 74 createdir: fn(n: self ref Node): int; 75 createfile: fn(n: self ref Node): int; 76 changedir: fn(n: self ref Node): int; 77 docreate: fn(n: self ref Node): int; 78 pathname: fn(n: self ref Node): string; 79 readdir: fn(n: self ref Node): int; 80 readfile: fn(n: self ref Node): int; 81 removedir: fn(n: self ref Node): int; 82 removefile: fn(n: self ref Node): int; 83}; 84 85# 86# Styx protocol file identifier. 87# 88 89Fid: adt 90{ 91 fid: int; 92 node: ref Node; 93 busy: int; 94}; 95 96# 97# Foreign file with cache. 98# 99 100File: adt 101{ 102 cache: array of byte; 103 length: int; 104 offset: int; 105 fd: ref FD; 106 inuse, dirty: int; 107 atime: int; 108 node: cyclic ref Node; 109 tempname: string; 110 111 createtmp: fn(f: self ref File): ref FD; 112}; 113 114ftp: ref Connection; 115dfid: ref FD; 116dfidiob: ref Iobuf; 117buffresidue: int = 0; 118tbuff: array of byte; 119rbuff: array of byte; 120ccfd: ref FD; 121stdin, stderr: ref FD; 122 123fids: list of ref Fid; 124 125BSZ: con 8192; 126Chunk: con 1024; 127Nfiles: con 128; 128 129CHSYML: con 16r40000000; 130 131mountpoint: string = "/n/ftp"; 132user: string = nil; 133password: string; 134hostname: string = "kremvax"; 135anon: string = "anon"; 136 137firewall: string = "tcp!$proxy!402"; 138myname: string = "anon"; 139myhost: string = "lucent.com"; 140proxyid: string; 141proxyhost: string; 142 143errstr: string; 144net: string; 145port: int; 146 147Enosuchfile: con "file does not exist"; 148Eftpproto: con "ftp protocol error"; 149Eshutdown: con "remote shutdown"; 150Eioerror: con "io error"; 151Enotadirectory: con "not a directory"; 152Eisadirectory: con "is a directory"; 153Epermission: con "permission denied"; 154Ebadoffset: con "bad offset"; 155Ebadlength: con "bad length"; 156Enowstat: con "wstat not implemented"; 157Emesgmismatch: con "message size mismatch"; 158 159remdir: ref Node; 160remroot: ref Node; 161remrootpath: string; 162 163heartbeatpid: int; 164 165# 166# FTP protocol codes are 3 digits >= 100. 167# The code type is obtained by dividing by 100. 168# 169 170Syserr: con -2; 171Syntax: con -1; 172Shutdown: con 0; 173Extra: con 1; 174Success: con 2; 175Incomplete: con 3; 176TempFail: con 4; 177PermFail: con 5; 178Impossible: con 6; 179Err: con 7; 180 181debug: int = 0; 182quiet: int = 0; 183active: int = 0; 184cdtoroot: int = 0; 185 186proxy: int = 0; 187 188mountfd: ref FD; 189styxfd: ref FD; 190 191# 192# Set up FDs for service. 193# 194 195connect(): string 196{ 197 pip := array[2] of ref Sys->FD; 198 if(sys->pipe(pip) < 0) 199 return sys->sprint("can't create pipe: %r"); 200 mountfd = pip[0]; 201 styxfd = pip[1]; 202 return nil; 203} 204 205error(s: string) 206{ 207 sys->fprint(sys->fildes(2), "ftpfs: %s\n", s); 208 raise "fail:"+s; 209} 210 211# 212# Mount server. Must be spawned because it does 213# an attach transaction. 214# 215 216mount(mountpoint: string) 217{ 218 if (sys->mount(mountfd, nil, mountpoint, Sys->MREPL | Sys->MCREATE, nil) < 0) { 219 sys->fprint(sys->fildes(2), "ftpfs: mount %s failed: %r\n", mountpoint); 220 shutdown(); 221 } 222 mountfd = nil; 223} 224 225# 226# Keep the link alive. 227# 228 229beatquanta: con 10; 230beatlimit: con 10; 231beatcount: int; 232activity: int; 233transfer: int; 234 235heartbeat(pidc: chan of int) 236{ 237 pid := sys->pctl(0, nil); 238 pidc <-= pid; 239 for (;;) { 240 sys->sleep(beatquanta * 1000); 241 if (activity || transfer) { 242 beatcount = 0; 243 activity = 0; 244 continue; 245 } 246 beatcount++; 247 if (beatcount == beatlimit) { 248 acquire(); 249 if (sendrequest("NOOP", 0) == Success) 250 getreply(0); 251 release(); 252 beatcount = 0; 253 activity = 0; 254 } 255 } 256} 257 258# 259# Control lock. 260# 261 262ctllock: chan of int; 263 264acquire() 265{ 266 ctllock <-= 1; 267} 268 269release() 270{ 271 <-ctllock; 272} 273 274# 275# Data formatting routines. 276# 277 278sendreply(r: ref Rmsg) 279{ 280 if (debug) 281 sys->print("> %s\n", r.text()); 282 a := r.pack(); 283 if(sys->write(styxfd, a, len a) != len a) 284 sys->print("ftpfs: error replying: %r\n"); 285} 286 287rerror(tag: int, s: string) 288{ 289 if (debug) 290 sys->print("error: %s\n", s); 291 sendreply(ref Rmsg.Error(tag, s)); 292} 293 294seterr(e: int, s: string): int 295{ 296 case e { 297 Syserr => 298 errstr = Eioerror; 299 Syntax => 300 errstr = Eftpproto; 301 Shutdown => 302 errstr = Eshutdown; 303 * => 304 errstr = s; 305 } 306 return -1; 307} 308 309# 310# Node routines. 311# 312 313anode: Node; 314npath: int = 1; 315 316newnode(parent: ref Node, name: string): ref Node 317{ 318 n := ref anode; 319 n.dir.name = name; 320 n.dir.atime = time->now(); 321 n.children = nil; 322 n.remname = name; 323 if (parent != nil) { 324 n.parent = parent; 325 n.sibs = parent.children; 326 parent.children = n; 327 n.depth = parent.depth + 1; 328 n.valid = 0; 329 } else { 330 n.parent = n; 331 n.sibs = nil; 332 n.depth = 0; 333 n.valid = 1; 334 n.dir.uid = anon; 335 n.dir.gid = anon; 336 n.dir.mtime = n.dir.atime; 337 } 338 n.file = nil; 339 n.uniq = npath++; 340 n.cached = 0; 341 return n; 342} 343 344Node.extendpath(parent: self ref Node, elem: string): ref Node 345{ 346 n: ref Node; 347 348 for (n = parent.children; n != nil; n = n.sibs) 349 if (n.dir.name == elem) 350 return n; 351 return newnode(parent, elem); 352} 353 354Node.markcached(n: self ref Node) 355{ 356 n.cached = 1; 357 n.dir.atime = time->now(); 358} 359 360Node.uncache(n: self ref Node) 361{ 362 if (n.fileisdirty()) 363 n.createfile(); 364 n.filefree(); 365 n.cached = 0; 366} 367 368Node.uncachedir(parent: self ref Node, child: ref Node) 369{ 370 sp: ref Node; 371 372 if (parent == nil || parent == child) 373 return; 374 for (sp = parent.children; sp != nil; sp = sp.sibs) 375 if (sp != child && sp.file != nil && !sp.file.dirty && sp.file.fd != nil) { 376 sp.filefree(); 377 sp.cached = 0; 378 } 379} 380 381Node.invalidate(node: self ref Node) 382{ 383 n: ref Node; 384 385 node.uncachedir(nil); 386 for (n = node.children; n != nil; n = n.sibs) { 387 n.cached = 0; 388 n.invalidate(); 389 n.valid = 0; 390 } 391} 392 393Node.fixsymbolic(n: self ref Node) 394{ 395 if (n.changedir() == 0) { 396 n.dir.mode |= Sys->DMDIR; 397 n.dir.qid.qtype = Sys->QTDIR; 398 } else 399 n.dir.qid.qtype = Sys->QTFILE; 400 n.dir.mode &= ~CHSYML; 401} 402 403Node.stat(n: self ref Node): array of byte 404{ 405 return styx->packdir(n.dir); 406} 407 408Node.qid(n: self ref Node): Sys->Qid 409{ 410 if(n.dir.mode & Sys->DMDIR) 411 return Sys->Qid(big n.uniq, 0, Sys->QTDIR); 412 return Sys->Qid(big n.uniq, 0, Sys->QTFILE); 413} 414 415# 416# File routines. 417# 418 419ntmp: int; 420files: list of ref File; 421nfiles: int; 422afile: File; 423atime: int; 424 425# 426# Allocate a file structure for a node. If too many 427# are already allocated discard the oldest. 428# 429 430Node.fileget(n: self ref Node): ref File 431{ 432 f, o: ref File; 433 l: list of ref File; 434 435 if (n.file != nil) 436 return n.file; 437 o = nil; 438 for (l = files; l != nil; l = tl l) { 439 f = hd l; 440 if (f.inuse == 0) 441 break; 442 if (!f.dirty && (o == nil || o.atime > f.atime)) 443 o = f; 444 } 445 if (l == nil) { 446 if (nfiles == Nfiles && o != nil) { 447 o.node.uncache(); 448 f = o; 449 } 450 else { 451 f = ref afile; 452 files = f :: files; 453 nfiles++; 454 } 455 } 456 n.file = f; 457 f.node = n; 458 f.atime = atime++; 459 f.inuse = 1; 460 f.dirty = 0; 461 f.length = 0; 462 f.fd = nil; 463 return f; 464} 465 466# 467# Create a temporary file for a local copy of a file. 468# If too many are open uncache parent. 469# 470 471File.createtmp(f: self ref File): ref FD 472{ 473 t := "/tmp/ftp." + string time->now() + "." + string ntmp; 474 if (ntmp >= 16) 475 f.node.parent.uncachedir(f.node); 476 f.fd = sys->create(t, Sys->ORDWR | Sys->ORCLOSE, 8r600); 477 f.tempname = t; 478 f.offset = 0; 479 ntmp++; 480 return f.fd; 481} 482 483# 484# Read 'c' bytes at offset 'off' from a file into buffer 'b'. 485# 486 487Node.fileread(n: self ref Node, b: array of byte, off, c: int): int 488{ 489 f: ref File; 490 t, i: int; 491 492 f = n.file; 493 if (off + c > f.length) 494 c = f.length - off; 495 for (t = 0; t < c; t += i) { 496 if (off >= f.length) 497 return t; 498 if (off < Chunk) { 499 i = c; 500 if (off + i > Chunk) 501 i = Chunk - off; 502 b[t:] = f.cache[off: off + i]; 503 } 504 else { 505 if (f.offset != off) { 506 if (sys->seek(f.fd, big off, Sys->SEEKSTART) < big 0) { 507 f.offset = -1; 508 return seterr(Err, sys->sprint("seek temp failed: %r")); 509 } 510 } 511 if (t == 0) 512 i = sys->read(f.fd, b, c - t); 513 else 514 i = sys->read(f.fd, rbuff, c - t); 515 if (i < 0) { 516 f.offset = -1; 517 return seterr(Err, sys->sprint("read temp failed: %r")); 518 } 519 if (i == 0) 520 break; 521 if (t > 0) 522 b[t:] = rbuff[0: i]; 523 f.offset = off + i; 524 } 525 off += i; 526 } 527 return t; 528} 529 530# 531# Write 'c' bytes at offset 'off' to a file from buffer 'b'. 532# 533 534Node.filewrite(n: self ref Node, b: array of byte, off, c: int): int 535{ 536 f: ref File; 537 t, i: int; 538 539 f = n.fileget(); 540 if (f.cache == nil) 541 f.cache = array[Chunk] of byte; 542 for (t = 0; t < c; t += i) { 543 if (off < Chunk) { 544 i = c; 545 if (off + i > Chunk) 546 i = Chunk - off; 547 f.cache[off:] = b[t: t + i]; 548 } 549 else { 550 if (f.fd == nil) { 551 if (f.createtmp() == nil) 552 return seterr(Err, sys->sprint("temp file: %r")); 553 if (sys->write(f.fd, f.cache, Chunk) != Chunk) { 554 f.offset = -1; 555 return seterr(Err, sys->sprint("write temp failed: %r")); 556 } 557 f.offset = Chunk; 558 f.length = Chunk; 559 } 560 if (f.offset != off) { 561 if (off > f.length) { 562 # extend the file with zeroes 563 # sparse files may not be supported 564 } 565 if (sys->seek(f.fd, big off, Sys->SEEKSTART) < big 0) { 566 f.offset = -1; 567 return seterr(Err, sys->sprint("seek temp failed: %r")); 568 } 569 } 570 i = sys->write(f.fd, b[t:len b], c - t); 571 if (i != c - t) { 572 f.offset = -1; 573 return seterr(Err, sys->sprint("write temp failed: %r")); 574 } 575 } 576 off += i; 577 f.offset = off; 578 } 579 if (off > f.length) 580 f.length = off; 581 return t; 582} 583 584Node.filefree(n: self ref Node) 585{ 586 f: ref File; 587 588 f = n.file; 589 if (f == nil) 590 return; 591 if (f.fd != nil) { 592 ntmp--; 593 f.fd = nil; 594 f.tempname = nil; 595 } 596 f.cache = nil; 597 f.length = 0; 598 f.inuse = 0; 599 f.dirty = 0; 600 n.file = nil; 601} 602 603Node.fileclean(n: self ref Node) 604{ 605 if (n.file != nil) 606 n.file.dirty = 0; 607} 608 609Node.fileisdirty(n: self ref Node): int 610{ 611 return n.file != nil && n.file.dirty; 612} 613 614Node.filedirty(n: self ref Node) 615{ 616 f: ref File; 617 618 f = n.fileget(); 619 f.dirty = 1; 620} 621 622# 623# Fid management. 624# 625 626afid: Fid; 627 628getfid(fid: int): ref Fid 629{ 630 l: list of ref Fid; 631 f, ff: ref Fid; 632 633 ff = nil; 634 for (l = fids; l != nil; l = tl l) { 635 f = hd l; 636 if (f.fid == fid) { 637 if (f.busy) 638 return f; 639 else { 640 ff = f; 641 break; 642 } 643 } else if (ff == nil && !f.busy) 644 ff = f; 645 } 646 if (ff == nil) { 647 ff = ref afid; 648 fids = ff :: fids; 649 } 650 ff.node = nil; 651 ff.fid = fid; 652 return ff; 653} 654 655# 656# FTP protocol. 657# 658 659fail(s: int, l: string) 660{ 661 case s { 662 Syserr => 663 sys->print("read fail: %r\n"); 664 Syntax => 665 sys->print("%s\n", Eftpproto); 666 Shutdown => 667 sys->print("%s\n", Eshutdown); 668 * => 669 sys->print("unexpected response: %s\n", l); 670 } 671 exit; 672} 673 674getfullreply(echo: int): (int, int, string) 675{ 676 reply := ""; 677 s: string; 678 code := -1; 679 do{ 680 s = dfidiob.gets('\n'); 681 if(s == nil) 682 return (Shutdown, 0, nil); 683 if(len s >= 2 && s[len s-1] == '\n'){ 684 if (s[len s - 2] == '\r') 685 s = s[0: len s - 2]; 686 else 687 s = s[0: len s - 1]; 688 } 689 if (debug || echo) 690 sys->print("%s\n", s); 691 reply = reply+s; 692 if(code < 0){ 693 if(len s < 3) 694 return (Syntax, 0, nil); 695 code = int s[0:3]; 696 if(s[3] != '-') 697 break; 698 } 699 }while(len s < 4 || int s[0:3] != code || s[3] != ' '); 700 701 if(code < 100) 702 return (Syntax, 0, nil); 703 return (code / 100, code, reply); 704} 705 706getreply(echo: int): (int, string) 707{ 708 (c, nil, s) := getfullreply(echo); 709 return (c, s); 710} 711 712sendrequest2(req: string, echo: int, figleaf: string): int 713{ 714 activity = 1; 715 if (debug || echo) { 716 if (figleaf == nil) 717 figleaf = req; 718 sys->print("%s\n", figleaf); 719 } 720 b := array of byte (req + "\r\n"); 721 n := sys->write(dfid, b, len b); 722 if (n < 0) 723 return Syserr; 724 if (n != len b) 725 return Shutdown; 726 return Success; 727} 728 729sendrequest(req: string, echo: int): int 730{ 731 return sendrequest2(req, echo, req); 732} 733 734sendfail(s: int) 735{ 736 case s { 737 Syserr => 738 sys->print("write fail: %r\n"); 739 Shutdown => 740 sys->print("%s\n", Eshutdown); 741 * => 742 sys->print("internal error\n"); 743 } 744 exit; 745} 746 747dataport(l: list of string): string 748{ 749 s := "tcp!" + hd l; 750 l = tl l; 751 s = s + "." + hd l; 752 l = tl l; 753 s = s + "." + hd l; 754 l = tl l; 755 s = s + "." + hd l; 756 l = tl l; 757 return s + "!" + string ((int hd l * 256) + (int hd tl l)); 758} 759 760commas(l: list of string): string 761{ 762 s := hd l; 763 l = tl l; 764 while (l != nil) { 765 s = s + "," + hd l; 766 l = tl l; 767 } 768 return s; 769} 770 771third(cmd: string): ref FD 772{ 773 acquire(); 774 for (;;) { 775 data := dial->dial(firewall, nil); 776 if(data == nil) { 777 if (debug) 778 sys->print("dial %s failed: %r\n", firewall); 779 break; 780 } 781 t := sys->sprint("\n%s!*\n\n%s\n%s\n1\n-1\n-1\n", proxyhost, myhost, myname); 782 b := array of byte t; 783 n := sys->write(data.dfd, b, len b); 784 if (n < 0) { 785 if (debug) 786 sys->print("firewall write failed: %r\n"); 787 break; 788 } 789 b = array[256] of byte; 790 n = sys->read(data.dfd, b, len b); 791 if (n < 0) { 792 if (debug) 793 sys->print("firewall read failed: %r\n"); 794 break; 795 } 796 (c, k) := sys->tokenize(string b[:n], "\n"); 797 if (c < 2) { 798 if (debug) 799 sys->print("bad response from firewall\n"); 800 break; 801 } 802 if (hd k != "0") { 803 if (debug) 804 sys->print("firewall connect: %s\n", hd tl k); 805 break; 806 } 807 p := hd tl k; 808 if (debug) 809 sys->print("portid %s\n", p); 810 (c, k) = sys->tokenize(p, "!"); 811 if (c < 3) { 812 if (debug) 813 sys->print("bad portid from firewall\n"); 814 break; 815 } 816 n = int hd tl tl k; 817 (c, k) = sys->tokenize(hd tl k, "."); 818 if (c != 4) { 819 if (debug) 820 sys->print("bad portid ip address\n"); 821 break; 822 } 823 t = sys->sprint("PORT %s,%d,%d", commas(k), n / 256, n & 255); 824 r := sendrequest(t, 0); 825 if (r != Success) 826 break; 827 (r, nil) = getreply(0); 828 if (r != Success) 829 break; 830 r = sendrequest(cmd, 0); 831 if (r != Success) 832 break; 833 (r, nil) = getreply(0); 834 if (r != Extra) 835 break; 836 n = sys->read(data.dfd, b, len b); 837 if (n < 0) { 838 if (debug) 839 sys->print("firewall read failed: %r\n"); 840 break; 841 } 842 b = array of byte "0\n?\n"; 843 n = sys->write(data.dfd, b, len b); 844 if (n < 0) { 845 if (debug) 846 sys->print("firewall write failed: %r\n"); 847 break; 848 } 849 release(); 850 return data.dfd; 851 } 852 release(); 853 return nil; 854} 855 856passive(cmd: string): ref FD 857{ 858 acquire(); 859 if (sendrequest("PASV", 0) != Success) { 860 release(); 861 return nil; 862 } 863 (r, m) := getreply(0); 864 release(); 865 if (r != Success) 866 return nil; 867 (nil, p) := str->splitl(m, "("); 868 if (p == nil) 869 str->splitl(m, "0-9"); 870 else 871 p = p[1:len p]; 872 (c, l) := sys->tokenize(p, ","); 873 if (c < 6) { 874 sys->print("data: %s\n", m); 875 return nil; 876 } 877 a := dataport(l); 878 if (debug) 879 sys->print("data dial %s\n", a); 880 d := dial->dial(a, nil); 881 if(d == nil) 882 return nil; 883 acquire(); 884 r = sendrequest(cmd, 0); 885 if (r != Success) { 886 release(); 887 return nil; 888 } 889 (r, m) = getreply(0); 890 release(); 891 if (r != Extra) 892 return nil; 893 return d.dfd; 894} 895 896getnet(dir: string): (string, int) 897{ 898 buf := array[50] of byte; 899 n := dir + "/local"; 900 lfd := sys->open(n, Sys->OREAD); 901 if (lfd == nil) { 902 if (debug) 903 sys->fprint(stderr, "open %s: %r\n", n); 904 return (nil, 0); 905 } 906 length := sys->read(lfd, buf, len buf); 907 if (length < 0) { 908 if (debug) 909 sys->fprint(stderr, "read%s: %r\n", n); 910 return (nil, 0); 911 } 912 (r, l) := sys->tokenize(string buf[0:length], "!"); 913 if (r != 2) { 914 if (debug) 915 sys->fprint(stderr, "tokenize(%s) returned (%d)\n", string buf[0:length], r); 916 return (nil, 0); 917 } 918 if (debug) 919 sys->print("net is %s!%d\n", hd l, int hd tl l); 920 return (hd l, int hd tl l); 921} 922 923activate(cmd: string): ref FD 924{ 925 r: int; 926 927 listenport, dataport: ref Connection; 928 m: string; 929 930 listenport = dial->announce("tcp!" + net + "!0"); 931 if(listenport == nil) 932 return nil; 933 (x1, x2) := getnet(listenport.dir); 934 (nil, x4) := sys->tokenize(x1, "."); 935 t := sys->sprint("PORT %s,%d,%d", commas(x4), int x2 / 256, int x2&255); 936 acquire(); 937 r = sendrequest(t, 0); 938 if (r != Success) { 939 release(); 940 return nil; 941 } 942 (r, m) = getreply(0); 943 if (r != Success) { 944 release(); 945 return nil; 946 } 947 r = sendrequest(cmd, 0); 948 if (r != Success) { 949 release(); 950 return nil; 951 } 952 (r, m) = getreply(0); 953 release(); 954 if (r != Extra) 955 return nil; 956 dataport = dial->listen(listenport); 957 if(dataport == nil) { 958 sys->fprint(stderr, "activate: listen failed: %r\n"); 959 return nil; 960 } 961 fd := sys->open(dataport.dir + "/data", sys->ORDWR); 962 if (debug) 963 sys->print("activate: data connection on %s\n", dataport.dir); 964 if (fd == nil) { 965 sys->fprint(stderr, "activate: open of %s failed: %r\n", dataport.dir); 966 return nil; 967 } 968 return fd; 969} 970 971data(cmd: string): ref FD 972{ 973 if (proxy) 974 return third(cmd); 975 else if (active) 976 return activate(cmd); 977 else 978 return passive(cmd); 979} 980 981# 982# File list cracking routines. 983# 984 985fields(l: list of string, n: int): array of string 986{ 987 a := array[n] of string; 988 for (i := 0; i < n; i++) { 989 a[i] = hd l; 990 l = tl l; 991 } 992 return a; 993} 994 995now: ref Tm; 996months: con "janfebmaraprmayjunjulaugsepoctnovdec"; 997 998cracktime(month, day, year, hms: string): int 999{ 1000 tm: Tm; 1001 1002 if (now == nil) 1003 now = time->local(time->now()); 1004 tm = *now; 1005 if (month[0] >= '0' && month[0] <= '9') { 1006 tm.mon = int month - 1; 1007 if (tm.mon < 0 || tm.mon > 11) 1008 tm.mon = 5; 1009 } 1010 else if (len month >= 3) { 1011 month = str->tolower(month[0:3]); 1012 for (i := 0; i < 36; i += 3) 1013 if (month == months[i:i+3]) { 1014 tm.mon = i / 3; 1015 break; 1016 } 1017 } 1018 tm.mday = int day; 1019 if (hms != nil) { 1020 (h, z) := str->splitl(hms, "apAP"); 1021 (a, b) := str->splitl(h, ":"); 1022 tm.hour = int a; 1023 if (b != nil) { 1024 (c, d) := str->splitl(b[1:len b], ":"); 1025 tm.min = int c; 1026 if (d != nil) 1027 tm.sec = int d[1:len d]; 1028 } 1029 if (z != nil && str->tolower(z)[0] == 'p') 1030 tm.hour += 12; 1031 } 1032 if (year != nil) { 1033 tm.year = int year; 1034 if (tm.year >= 1900) 1035 tm.year -= 1900; 1036 } 1037 else { 1038 if (tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1)) 1039 tm.year--; 1040 } 1041 return time->tm2epoch(ref tm); 1042} 1043 1044crackmode(p: string): int 1045{ 1046 flags := 0; 1047 case len p { 1048 10 => # unix and new style plan 9 1049 case p[0] { 1050 'l' => 1051 return CHSYML | 0777; 1052 'd' => 1053 flags = Sys->DMDIR; 1054 } 1055 p = p[1:10]; 1056 11 => # old style plan 9 1057 if (p[0] == 'l') 1058 flags = Sys->DMDIR; 1059 p = p[2:11]; 1060 * => 1061 return Sys->DMDIR | 0777; 1062 } 1063 mode := 0; 1064 n := 0; 1065 for (i := 0; i < 3; i++) { 1066 mode <<= 3; 1067 if (p[n] == 'r') 1068 mode |= 4; 1069 if (p[n+1] == 'w') 1070 mode |= 2; 1071 case p[n+2] { 1072 'x' or 's' or 'S' => 1073 mode |= 1; 1074 } 1075 n += 3; 1076 } 1077 return mode | flags; 1078} 1079 1080crackdir(p: string): (string, Dir) 1081{ 1082 d: Dir; 1083 ln, a: string; 1084 1085 (n, l) := sys->tokenize(p, " \t\r\n"); 1086 f := fields(l, n); 1087 if (n > 2 && f[n - 2] == "->") 1088 n -= 2; 1089 case n { 1090 8 => # ls -l 1091 ln = f[7]; 1092 d.uid = f[2]; 1093 d.gid = f[2]; 1094 d.mode = crackmode(f[0]); 1095 d.length = big f[3]; 1096 (a, nil) = str->splitl(f[6], ":"); 1097 if (len a != len f[6]) 1098 d.atime = cracktime(f[4], f[5], nil, f[6]); 1099 else 1100 d.atime = cracktime(f[4], f[5], f[6], nil); 1101 9 => # ls -lg 1102 ln = f[8]; 1103 d.uid = f[2]; 1104 d.gid = f[3]; 1105 d.mode = crackmode(f[0]); 1106 d.length = big f[4]; 1107 (a, nil) = str->splitl(f[7], ":"); 1108 if (len a != len f[7]) 1109 d.atime = cracktime(f[5], f[6], nil, f[7]); 1110 else 1111 d.atime = cracktime(f[5], f[6], f[7], nil); 1112 10 => # plan 9 1113 ln = f[9]; 1114 d.uid = f[3]; 1115 d.gid = f[4]; 1116 d.mode = crackmode(f[0]); 1117 d.length = big f[5]; 1118 (a, nil) = str->splitl(f[8], ":"); 1119 if (len a != len f[8]) 1120 d.atime = cracktime(f[6], f[7], nil, f[8]); 1121 else 1122 d.atime = cracktime(f[6], f[7], f[8], nil); 1123 4 => # NT 1124 ln = f[3]; 1125 d.uid = anon; 1126 d.gid = anon; 1127 if (f[2] == "<DIR>") { 1128 d.length = big 0; 1129 d.mode = Sys->DMDIR | 8r777; 1130 } 1131 else { 1132 d.mode = 8r666; 1133 d.length = big f[2]; 1134 } 1135 (n, l) = sys->tokenize(f[0], "/-"); 1136 if (n == 3) 1137 d.atime = cracktime(hd l, hd tl l, f[2], f[1]); 1138 1 => # ls 1139 ln = f[0]; 1140 d.uid = anon; 1141 d.gid = anon; 1142 d.mode = 0777; 1143 d.atime = 0; 1144 * => 1145 return (nil, d); 1146 } 1147 if (ln == "." || ln == "..") 1148 return (nil, d); 1149 d.mtime = d.atime; 1150 d.name = ln; 1151 return (ln, d); 1152} 1153 1154longls := 1; 1155 1156Node.readdir(n: self ref Node): int 1157{ 1158 f: ref FD; 1159 p: ref Node; 1160 1161 if (n.changedir() < 0) 1162 return -1; 1163 transfer = 1; 1164 for (;;) { 1165 if (longls) { 1166 f = data("LIST -la"); 1167 if (f == nil) { 1168 longls = 0; 1169 continue; 1170 } 1171 } 1172 else { 1173 f = data("LIST"); 1174 if (f == nil) { 1175 transfer = 0; 1176 return seterr(Err, Enosuchfile); 1177 } 1178 } 1179 break; 1180 } 1181 b := bufio->fopen(f, sys->OREAD); 1182 if (b == nil) { 1183 transfer = 0; 1184 return seterr(Err, Eioerror); 1185 } 1186 while ((s := b.gets('\n')) != nil) { 1187 if (debug) 1188 sys->print("%s", s); 1189 (l, d) := crackdir(s); 1190 if (l == nil) 1191 continue; 1192 p = n.extendpath(l); 1193 p.dir = d; 1194 p.valid = 1; 1195 } 1196 b = nil; 1197 f = nil; 1198 (r, nil) := getreply(0); 1199 transfer = 0; 1200 if (r != Success) 1201 return seterr(Err, Enosuchfile); 1202 return 0; 1203} 1204 1205Node.readfile(n: self ref Node): int 1206{ 1207 c: int; 1208 1209 if (n.parent.changedir() < 0) 1210 return -1; 1211 transfer = 1; 1212 f := data("RETR " + n.remname); 1213 if (f == nil) { 1214 transfer = 0; 1215 return seterr(Err, Enosuchfile); 1216 } 1217 off := 0; 1218 while ((c = sys->read(f, tbuff, BSZ)) > 0) { 1219 if (n.filewrite(tbuff, off, c) != c) { 1220 off = -1; 1221 break; 1222 } 1223 off += c; 1224 } 1225 if (c < 0) { 1226 transfer = 0; 1227 return seterr(Err, Eioerror); 1228 } 1229 f = nil; 1230 if(off == 0) 1231 n.filewrite(tbuff, off, 0); 1232 (s, nil) := getreply(0); 1233 transfer = 0; 1234 if (s != Success) 1235 return seterr(s, Enosuchfile); 1236 return off; 1237} 1238 1239path(a, b: string): string 1240{ 1241 if (a == nil) 1242 return b; 1243 if (b == nil) 1244 return a; 1245 if (a[len a - 1] == '/') 1246 return a + b; 1247 else 1248 return a + "/" + b; 1249} 1250 1251Node.pathname(n: self ref Node): string 1252{ 1253 s: string; 1254 1255 while (n != n.parent) { 1256 s = path(n.remname, s); 1257 n = n.parent; 1258 } 1259 return path(remrootpath, s); 1260} 1261 1262Node.changedir(n: self ref Node): int 1263{ 1264 t: ref Node; 1265 d: string; 1266 1267 t = n; 1268 if (t == remdir) 1269 return 0; 1270 if (n.depth == 0) 1271 d = remrootpath; 1272 else 1273 d = n.pathname(); 1274 remdir.uncachedir(nil); 1275 acquire(); 1276 r := sendrequest("CWD " + d, 0); 1277 if (r == Success) 1278 (r, nil) = getreply(0); 1279 release(); 1280 case r { 1281 Success 1282# or Incomplete 1283 => 1284 remdir = n; 1285 return 0; 1286 * => 1287 return seterr(r, Enosuchfile); 1288 } 1289} 1290 1291Node.docreate(n: self ref Node): int 1292{ 1293 f: ref FD; 1294 1295 transfer = 1; 1296 f = data("STOR " + n.remname); 1297 if (f == nil) { 1298 transfer = 0; 1299 return -1; 1300 } 1301 off := 0; 1302 for (;;) { 1303 r := n.fileread(tbuff, off, BSZ); 1304 if (r <= 0) 1305 break; 1306 if (sys->write(f, tbuff, r) < 0) { 1307 off = -1; 1308 break; 1309 } 1310 off += r; 1311 } 1312 transfer = 0; 1313 return off; 1314} 1315 1316Node.createfile(n: self ref Node): int 1317{ 1318 if (n.parent.changedir() < 0) 1319 return -1; 1320 off := n.docreate(); 1321 if (off < 0) 1322 return -1; 1323 (r, nil) := getreply(0); 1324 if (r != Success) 1325 return -1; 1326 return off; 1327} 1328 1329Node.action(n: self ref Node, cmd: string): int 1330{ 1331 if (n.parent.changedir() < 0) 1332 return -1; 1333 acquire(); 1334 r := sendrequest(cmd + " " + n.dir.name, 0); 1335 if (r == Success) 1336 (r, nil) = getreply(0); 1337 release(); 1338 if (r != Success) 1339 return -1; 1340 return 0; 1341} 1342 1343Node.createdir(n: self ref Node): int 1344{ 1345 return n.action("MKD"); 1346} 1347 1348Node.removefile(n: self ref Node): int 1349{ 1350 return n.action("DELE"); 1351} 1352 1353Node.removedir(n: self ref Node): int 1354{ 1355 return n.action("RMD"); 1356} 1357 1358pwd(s: string): string 1359{ 1360 (nil, s) = str->splitl(s, "\""); 1361 if (s == nil || len s < 2) 1362 return "/"; 1363 (s, nil) = str->splitl(s[1:len s], "\""); 1364 return s; 1365} 1366 1367# 1368# User info for firewall. 1369# 1370getuser() 1371{ 1372 b := array[Sys->NAMEMAX] of byte; 1373 f := sys->open("/dev/user", Sys->OREAD); 1374 if (f != nil) { 1375 n := sys->read(f, b, len b); 1376 if (n > 0) 1377 myname = string b[:n]; 1378 else if (n == 0) 1379 sys->print("warning: empty /dev/user\n"); 1380 else 1381 sys->print("warning: could not read /dev/user: %r\n"); 1382 } else 1383 sys->print("warning: could not open /dev/user: %r\n"); 1384 f = sys->open("/dev/sysname", Sys->OREAD); 1385 if (f != nil) { 1386 n := sys->read(f, b, len b); 1387 if (n > 0) 1388 myhost = string b[:n]; 1389 else if (n == 0) 1390 sys->print("warning: empty /dev/sysname\n"); 1391 else 1392 sys->print("warning: could not read /dev/sysname: %r\n"); 1393 } else 1394 sys->print("warning: could not open /dev/sysname: %r\n"); 1395 if (debug) 1396 sys->print("proxy %s for %s@%s\n", firewall, myname, myhost); 1397} 1398 1399server() 1400{ 1401 while((t := Tmsg.read(styxfd, 0)) != nil){ 1402 if (debug) 1403 sys->print("< %s\n", t.text()); 1404 pick x := t { 1405 Readerror => 1406 sys->print("ftpfs: read error on mount point: %s\n", x.error); 1407 kill(heartbeatpid); 1408 exit; 1409 Version => 1410 versionT(x); 1411 Auth => 1412 authT(x); 1413 Attach => 1414 attachT(x); 1415 Clunk => 1416 clunkT(x); 1417 Create => 1418 createT(x); 1419 Flush => 1420 flushT(x); 1421 Open => 1422 openT(x); 1423 Read => 1424 readT(x); 1425 Remove => 1426 removeT(x); 1427 Stat => 1428 statT(x); 1429 Walk => 1430 walkT(x); 1431 Write => 1432 writeT(x); 1433 Wstat => 1434 wstatT(x); 1435 * => 1436 rerror(t.tag, "unimp"); 1437 } 1438 } 1439 if (debug) 1440 sys->print("ftpfs: server: exiting\n"); 1441 kill(heartbeatpid); 1442} 1443 1444raw(on: int) 1445{ 1446 if(ccfd == nil) { 1447 ccfd = sys->open("/dev/consctl", Sys->OWRITE); 1448 if(ccfd == nil) { 1449 sys->fprint(stderr, "ftpfs: cannot open /dev/consctl: %r\n"); 1450 return; 1451 } 1452 } 1453 if(on) 1454 sys->fprint(ccfd, "rawon"); 1455 else 1456 sys->fprint(ccfd, "rawoff"); 1457} 1458 1459prompt(p: string, def: string, echo: int): string 1460{ 1461 if (def == nil) 1462 sys->print("%s: ", p); 1463 else 1464 sys->print("%s[%s]: ", p, def); 1465 if (!echo) 1466 raw(1); 1467 b := bufio->fopen(stdin, Sys->OREAD); 1468 s := b.gets(int '\n'); 1469 if (!echo) { 1470 raw(0); 1471 sys->print("\n"); 1472 } 1473 if(s != nil) 1474 s = s[0:len s - 1]; 1475 if (s == "") 1476 return def; 1477 return s; 1478} 1479 1480# 1481# Entry point. Load modules and initiate protocol. 1482# 1483 1484nomod(s: string) 1485{ 1486 sys->fprint(sys->fildes(2), "ftpfs: can't load %s: %r\n", s); 1487 raise "fail:load"; 1488} 1489 1490init(nil: ref Draw->Context, args: list of string) 1491{ 1492 l: string; 1493 rv: int; 1494 code: int; 1495 1496 sys = load Sys Sys->PATH; 1497 dial = load Dial Dial->PATH; 1498 stdin = sys->fildes(0); 1499 stderr = sys->fildes(2); 1500 1501 time = load Daytime Daytime->PATH; 1502 if (time == nil) 1503 nomod(Daytime->PATH); 1504 str = load String String->PATH; 1505 if (str == nil) 1506 nomod(String->PATH); 1507 bufio = load Bufio Bufio->PATH; 1508 if (bufio == nil) 1509 nomod(Bufio->PATH); 1510 styx = load Styx Styx->PATH; 1511 if (styx == nil) 1512 nomod(Styx->PATH); 1513 styx->init(); 1514 arg := load Arg Arg->PATH; 1515 if(arg == nil) 1516 nomod(Arg->PATH); 1517 1518 # parse arguments 1519 # [-/dpq] [-m mountpoint] [-a password] host 1520 arg->init(args); 1521 arg->setusage("ftpfs [-/dpq] [-m mountpoint] [-a password] ftphost"); 1522 keyspec := ""; 1523 while((op := arg->opt()) != 0) 1524 case op { 1525 'd' => 1526 debug++; 1527 '/' => 1528 cdtoroot = 1; 1529 'p' => 1530 active = 1; 1531 'q' => 1532 quiet = 1; 1533 'm' => 1534 mountpoint = arg->earg(); 1535 'a' => 1536 password = arg->earg(); 1537 user = "anonymous"; 1538 'k' => 1539 keyspec = arg->earg(); 1540 * => 1541 arg->usage(); 1542 } 1543 argv := arg->argv(); 1544 if (len argv != 1) 1545 arg->usage(); 1546 arg = nil; 1547 hostname = hd argv; 1548 1549 if (len hostname > 6 && hostname[:6] == "proxy!") { 1550 hostname = hostname[6:]; 1551 proxy = 1; 1552 } 1553 1554 if (proxy) { 1555 if (!quiet) 1556 sys->print("dial firewall service %s\n", firewall); 1557 ftp = dial->dial(firewall, nil); 1558 if(ftp == nil) { 1559 sys->print("dial %s failed: %r\n", firewall); 1560 exit; 1561 } 1562 dfid = ftp.dfd; 1563 getuser(); 1564 t := sys->sprint("\ntcp!%s!tcp.21\n\n%s\n%s\n0\n-1\n-1\n", hostname, myhost, myname); 1565 if (debug) 1566 sys->print("request%s\n", t); 1567 b := array of byte t; 1568 rv = sys->write(dfid, b, len b); 1569 if (rv < 0) { 1570 sys->print("firewall write failed: %r\n"); 1571 exit; 1572 } 1573 b = array[256] of byte; 1574 rv = sys->read(dfid, b, len b); 1575 if (rv < 0) { 1576 sys->print("firewall read failed: %r\n"); 1577 return; 1578 } 1579 (c, k) := sys->tokenize(string b[:rv], "\n"); 1580 if (c < 2) { 1581 sys->print("bad response from firewall\n"); 1582 exit; 1583 } 1584 if (hd k != "0") { 1585 sys->print("firewall connect: %s\n", hd tl k); 1586 exit; 1587 } 1588 proxyid = hd tl k; 1589 if (debug) 1590 sys->print("proxyid %s\n", proxyid); 1591 (c, k) = sys->tokenize(proxyid, "!"); 1592 if (c < 3) { 1593 sys->print("bad proxyid from firewall\n"); 1594 exit; 1595 } 1596 proxyhost = (hd k) + "!" + (hd tl k); 1597 if (debug) 1598 sys->print("proxyhost %s\n", proxyhost); 1599 } else { 1600 d := dial->netmkaddr(hostname, "tcp", "ftp"); 1601 ftp = dial->dial(d, nil); 1602 if(ftp == nil) 1603 error(sys->sprint("dial %s failed: %r", d)); 1604 if(debug) 1605 sys->print("localdir %s\n", ftp.dir); 1606 dfid = ftp.dfd; 1607 } 1608 dfidiob = bufio->fopen(dfid, sys->OREAD); 1609 (net, port) = getnet(ftp.dir); 1610 tbuff = array[BSZ] of byte; 1611 rbuff = array[BSZ] of byte; 1612 (rv, l) = getreply(!quiet); 1613 if (rv != Success) 1614 fail(rv, l); 1615 if (user == nil) { 1616 getuser(); 1617 user = myname; 1618 user = prompt("User", user, 1); 1619 } 1620 rv = sendrequest("USER " + user, 0); 1621 if (rv != Success) 1622 sendfail(rv); 1623 (rv, code, l) = getfullreply(!quiet); 1624 if (rv != Success) { 1625 if (rv != Incomplete) 1626 fail(rv, l); 1627 if (code == 331) { 1628 if(password == nil){ 1629 factotum := load Factotum Factotum->PATH; 1630 if(factotum != nil){ 1631 factotum->init(); 1632 if(user != nil && keyspec == nil) 1633 keyspec = sys->sprint("user=%q", user); 1634 (nil, password) = factotum->getuserpasswd(sys->sprint("proto=pass server=%s service=ftp %s", hostname, keyspec)); 1635 } 1636 if(password == nil) 1637 password = prompt("Password", nil, 0); 1638 } 1639 rv = sendrequest2("PASS " + password, 0, "PASS XXXX"); 1640 if (rv != Success) 1641 sendfail(rv); 1642 (rv, l) = getreply(0); 1643 if (rv != Success) 1644 fail(rv, l); 1645 } 1646 } 1647 if (cdtoroot) { 1648 rv = sendrequest("CWD /", 0); 1649 if (rv != Success) 1650 sendfail(rv); 1651 (rv, l) = getreply(0); 1652 if (rv != Success) 1653 fail(rv, l); 1654 } 1655 rv = sendrequest("TYPE I", 0); 1656 if (rv != Success) 1657 sendfail(rv); 1658 (rv, l) = getreply(0); 1659 if (rv != Success) 1660 fail(rv, l); 1661 rv = sendrequest("PWD", 0); 1662 if (rv != Success) 1663 sendfail(rv); 1664 (rv, l) = getreply(0); 1665 if (rv != Success) 1666 fail(rv, l); 1667 remrootpath = pwd(l); 1668 remroot = newnode(nil, "/"); 1669 remroot.dir.mode = Sys->DMDIR | 8r777; 1670 remroot.dir.qid.qtype = Sys->QTDIR; 1671 remdir = remroot; 1672 l = connect(); 1673 if (l != nil) { 1674 sys->print("%s\n", l); 1675 exit; 1676 } 1677 ctllock = chan[1] of int; 1678 spawn mount(mountpoint); 1679 pidc := chan of int; 1680 spawn heartbeat(pidc); 1681 heartbeatpid = <-pidc; 1682 if (debug) 1683 sys->print("heartbeatpid %d\n", heartbeatpid); 1684 spawn server(); # dies when receive on chan fails 1685} 1686 1687kill(pid: int) 1688{ 1689 if (debug) 1690 sys->print("killing %d\n", pid); 1691 fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); 1692 if(fd != nil) 1693 sys->fprint(fd, "kill"); 1694} 1695 1696shutdown() 1697{ 1698 mountfd = nil; 1699} 1700 1701# 1702# Styx transactions. 1703# 1704 1705versionT(t: ref Tmsg.Version) 1706{ 1707 (msize, version) := styx->compatible(t, Styx->MAXRPC, Styx->VERSION); 1708 sendreply(ref Rmsg.Version(t.tag, msize, version)); 1709} 1710 1711authT(t: ref Tmsg.Auth) 1712{ 1713 sendreply(ref Rmsg.Error(t.tag, "authentication not required")); 1714} 1715 1716flushT(t: ref Tmsg.Flush) 1717{ 1718 sendreply(ref Rmsg.Flush(t.tag)); 1719} 1720 1721attachT(t: ref Tmsg.Attach) 1722{ 1723 f := getfid(t.fid); 1724 f.busy = 1; 1725 f.node = remroot; 1726 sendreply(ref Rmsg.Attach(t.tag, remroot.qid())); 1727} 1728 1729walkT(t: ref Tmsg.Walk) 1730{ 1731 f := getfid(t.fid); 1732 qids: array of Sys->Qid; 1733 node := f.node; 1734 if(len t.names > 0){ 1735 qids = array[len t.names] of Sys->Qid; 1736 for(i := 0; i < len t.names; i++) { 1737 if ((node.dir.mode & Sys->DMDIR) == 0){ 1738 if(i == 0) 1739 return rerror(t.tag, Enotadirectory); 1740 break; 1741 } 1742 if (t.names[i] == "..") 1743 node = node.parent; 1744 else if (t.names[i] != ".") { 1745 if (t.names[i] == ".flush.ftpfs") { 1746 node.invalidate(); 1747 node.readdir(); 1748 qids[i] = node.qid(); 1749 continue; 1750 } 1751 node = node.extendpath(t.names[i]); 1752 if (node.parent.cached) { 1753 if (!node.valid) { 1754 if(i == 0) 1755 return rerror(t.tag, Enosuchfile); 1756 break; 1757 } 1758 if ((node.dir.mode & CHSYML) != 0) 1759 node.fixsymbolic(); 1760 } else if (!node.valid) { 1761 if (node.changedir() == 0){ 1762 node.dir.qid.qtype = Sys->QTDIR; 1763 node.dir.mode |= Sys->DMDIR; 1764 }else{ 1765 node.dir.qid.qtype = Sys->QTFILE; 1766 node.dir.mode &= ~Sys->DMDIR; 1767 } 1768 } 1769 qids[i] = node.qid(); 1770 } 1771 } 1772 if(i < len t.names){ 1773 sendreply(ref Rmsg.Walk(t.tag, qids[0:i])); 1774 return; 1775 } 1776 } 1777 if(t.newfid != t.fid){ 1778 n := getfid(t.newfid); 1779 if(n.busy) 1780 return rerror(t.tag, "fid in use"); 1781 n.busy = 1; 1782 n.node = node; 1783 }else 1784 f.node = node; 1785 sendreply(ref Rmsg.Walk(t.tag, qids)); 1786} 1787 1788openT(t: ref Tmsg.Open) 1789{ 1790 f := getfid(t.fid); 1791 if ((f.node.dir.mode & Sys->DMDIR) != 0 && t.mode != Sys->OREAD) { 1792 rerror(t.tag, Epermission); 1793 return; 1794 } 1795 if ((t.mode & Sys->OTRUNC) != 0) { 1796 f.node.uncache(); 1797 f.node.parent.uncache(); 1798 f.node.filedirty(); 1799 } else if (!f.node.cached) { 1800 f.node.filefree(); 1801 if ((f.node.dir.mode & Sys->DMDIR) != 0) { 1802 f.node.invalidate(); 1803 if (f.node.readdir() < 0) { 1804 rerror(t.tag, Enosuchfile); 1805 return; 1806 } 1807 } 1808 else { 1809 if (f.node.readfile() < 0) { 1810 rerror(t.tag, errstr); 1811 return; 1812 } 1813 } 1814 f.node.markcached(); 1815 } 1816 sendreply(ref Rmsg.Open(t.tag, f.node.qid(), Styx->MAXFDATA)); 1817} 1818 1819createT(t: ref Tmsg.Create) 1820{ 1821 f := getfid(t.fid); 1822 if ((f.node.dir.mode & Sys->DMDIR) == 0) { 1823 rerror(t.tag, Enotadirectory); 1824 return; 1825 } 1826 f.node = f.node.extendpath(t.name); 1827 f.node.uncache(); 1828 if ((t.perm & Sys->DMDIR) != 0) { 1829 if (f.node.createdir() < 0) { 1830 rerror(t.tag, Epermission); 1831 return; 1832 } 1833 } 1834 else 1835 f.node.filedirty(); 1836 f.node.parent.invalidate(); 1837 f.node.parent.uncache(); 1838 sendreply(ref Rmsg.Create(t.tag, f.node.qid(), Styx->MAXFDATA)); 1839} 1840 1841readT(t: ref Tmsg.Read) 1842{ 1843 f := getfid(t.fid); 1844 count := t.count; 1845 1846 if (count < 0) 1847 return rerror(t.tag, Ebadlength); 1848 if (count > Styx->MAXFDATA) 1849 count = Styx->MAXFDATA; 1850 if (t.offset < big 0) 1851 return rerror(t.tag, Ebadoffset); 1852 rv := 0; 1853 if ((f.node.dir.mode & Sys->DMDIR) != 0) { 1854 offset := int t.offset; 1855 for (p := f.node.children; offset > 0 && p != nil; p = p.sibs) 1856 if (p.valid) 1857 offset -= len p.stat(); 1858 for (; rv < count && p != nil; p = p.sibs) { 1859 if (p.valid) { 1860 if ((p.dir.mode & CHSYML) != 0) 1861 p.fixsymbolic(); 1862 a := p.stat(); 1863 size := len a; 1864 if(rv+size > count) 1865 break; 1866 tbuff[rv:] = a; 1867 rv += size; 1868 } 1869 } 1870 } else { 1871 if (!f.node.cached && f.node.readfile() < 0) { 1872 rerror(t.tag, errstr); 1873 return; 1874 } 1875 f.node.markcached(); 1876 rv = f.node.fileread(tbuff, int t.offset, count); 1877 if (rv < 0) { 1878 rerror(t.tag, errstr); 1879 return; 1880 } 1881 } 1882 sendreply(ref Rmsg.Read(t.tag, tbuff[0:rv])); 1883} 1884 1885writeT(t: ref Tmsg.Write) 1886{ 1887 f := getfid(t.fid); 1888 if ((f.node.dir.mode & Sys->DMDIR) != 0) { 1889 rerror(t.tag, Eisadirectory); 1890 return; 1891 } 1892 count := f.node.filewrite(t.data, int t.offset, len t.data); 1893 if (count < 0) { 1894 rerror(t.tag, errstr); 1895 return; 1896 } 1897 f.node.filedirty(); 1898 sendreply(ref Rmsg.Write(t.tag, count)); 1899} 1900 1901clunkT(t: ref Tmsg.Clunk) 1902{ 1903 f := getfid(t.fid); 1904 if (f.node.fileisdirty()) { 1905 if (f.node.createfile() < 0) 1906 sys->print("ftpfs: could not create %s: %r\n", f.node.pathname()); 1907 f.node.fileclean(); 1908 f.node.uncache(); 1909 } 1910 f.busy = 0; 1911 sendreply(ref Rmsg.Clunk(t.tag)); 1912} 1913 1914removeT(t: ref Tmsg.Remove) 1915{ 1916 f := getfid(t.fid); 1917 if ((f.node.dir.mode & Sys->DMDIR) != 0) { 1918 if (f.node.removedir() < 0) { 1919 rerror(t.tag, errstr); 1920 return; 1921 } 1922 } 1923 else { 1924 if (f.node.removefile() < 0) { 1925 rerror(t.tag, errstr); 1926 return; 1927 } 1928 } 1929 f.node.parent.uncache(); 1930 f.node.uncache(); 1931 f.node.valid = 0; 1932 f.busy = 0; 1933 sendreply(ref Rmsg.Remove(t.tag)); 1934} 1935 1936statT(t: ref Tmsg.Stat) 1937{ 1938 f := getfid(t.fid); 1939 n := f.node.parent; 1940 if (!n.cached) { 1941 n.invalidate(); 1942 n.readdir(); 1943 n.markcached(); 1944 } 1945 if (!f.node.valid) { 1946 rerror(t.tag, Enosuchfile); 1947 return; 1948 } 1949 sendreply(ref Rmsg.Stat(t.tag, f.node.dir)); 1950} 1951 1952wstatT(t: ref Tmsg.Wstat) 1953{ 1954 rerror(t.tag, Enowstat); 1955} 1956