implement Ftpfs; include "sys.m"; sys: Sys; FD, Dir: import Sys; include "draw.m"; include "arg.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "daytime.m"; time: Daytime; Tm: import time; include "string.m"; str: String; include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx; include "dial.m"; dial: Dial; Connection: import dial; include "factotum.m"; Ftpfs: module{ init: fn(nil: ref Draw->Context, argv: list of string);}; ## File system node. Refers to parent and file structure.# Siblings are linked. The head is parent.children.# Node: adt{ dir: Dir; uniq: int; parent: cyclic ref Node; sibs: cyclic ref Node; children: cyclic ref Node; file: cyclic ref File; depth: int; remname: string; cached: int; valid: int; extendpath: fn(parent: self ref Node, elem: string): ref Node; fixsymbolic: fn(n: self ref Node); invalidate: fn(n: self ref Node); markcached: fn(n: self ref Node); uncache: fn(n: self ref Node); uncachedir: fn(parent: self ref Node, child: ref Node); stat: fn(n: self ref Node): array of byte; qid: fn(n: self ref Node): Sys->Qid; fileget: fn(n: self ref Node): ref File; filefree: fn(n: self ref Node); fileclean: fn(n: self ref Node); fileisdirty: fn(n: self ref Node): int; filedirty: fn(n: self ref Node); fileread: fn(n: self ref Node, b: array of byte, off, c: int): int; filewrite: fn(n: self ref Node, b: array of byte, off, c: int): int; action: fn(n: self ref Node, cmd: string): int; createdir: fn(n: self ref Node): int; createfile: fn(n: self ref Node): int; changedir: fn(n: self ref Node): int; docreate: fn(n: self ref Node): int; pathname: fn(n: self ref Node): string; readdir: fn(n: self ref Node): int; readfile: fn(n: self ref Node): int; removedir: fn(n: self ref Node): int; removefile: fn(n: self ref Node): int;}; ## Styx protocol file identifier.# Fid: adt{ fid: int; node: ref Node; busy: int;}; ## Foreign file with cache.# File: adt{ cache: array of byte; length: int; offset: int; fd: ref FD; inuse, dirty: int; atime: int; node: cyclic ref Node; tempname: string; createtmp: fn(f: self ref File): ref FD;}; ftp: ref Connection;dfid: ref FD;dfidiob: ref Iobuf;buffresidue: int = 0;tbuff: array of byte;rbuff: array of byte;ccfd: ref FD;stdin, stderr: ref FD; fids: list of ref Fid; BSZ: con 8192;Chunk: con 1024;Nfiles: con 128; CHSYML: con 16r40000000; mountpoint: string = "/n/ftp";user: string = nil;password: string;hostname: string = "kremvax";anon: string = "anon"; firewall: string = "tcp!$proxy!402";myname: string = "anon";myhost: string = "lucent.com";proxyid: string;proxyhost: string; errstr: string;net: string;port: int; Enosuchfile: con "file does not exist";Eftpproto: con "ftp protocol error";Eshutdown: con "remote shutdown";Eioerror: con "io error";Enotadirectory: con "not a directory";Eisadirectory: con "is a directory";Epermission: con "permission denied";Ebadoffset: con "bad offset";Ebadlength: con "bad length";Enowstat: con "wstat not implemented";Emesgmismatch: con "message size mismatch"; remdir: ref Node;remroot: ref Node;remrootpath: string; heartbeatpid: int; ## FTP protocol codes are 3 digits >= 100.# The code type is obtained by dividing by 100.# Syserr: con -2;Syntax: con -1;Shutdown: con 0;Extra: con 1;Success: con 2;Incomplete: con 3;TempFail: con 4;PermFail: con 5;Impossible: con 6;Err: con 7; debug: int = 0;quiet: int = 0;active: int = 0;cdtoroot: int = 0; proxy: int = 0; mountfd: ref FD;styxfd: ref FD; ## Set up FDs for service.# connect(): string{ pip := array[2] of ref Sys->FD; if(sys->pipe(pip) < 0) return sys->sprint("can't create pipe: %r"); mountfd = pip[0]; styxfd = pip[1]; return nil;} #shut(s: string)#{# sys->print("ftpfs: %s shutdown\n", s);#} ## Mount server. Must be spawned because it does# an attach transaction.# mount(mountpoint: string){ if (sys->mount(mountfd, nil, mountpoint, sys->MREPL | sys->MCREATE, nil) < 0) { sys->print("mount %s failed: %r\n", mountpoint); shutdown(); } mountfd = nil;} ## Keep the link alive.# beatquanta: con 10;beatlimit: con 10;beatcount: int;activity: int;transfer: int; heartbeat(pidc: chan of int){ pid := sys->pctl(0, nil); pidc <-= pid; for (;;) { sys->sleep(beatquanta * 1000); if (activity || transfer) { beatcount = 0; activity = 0; continue; } beatcount++; if (beatcount == beatlimit) { acquire(); if (sendrequest("NOOP", 0) == Success) getreply(0); release(); beatcount = 0; activity = 0; } }} ## Control lock.# ctllock: chan of int; acquire(){ ctllock <-= 1;} release(){ <-ctllock;} ## Data formatting routines.# sendreply(r: ref Rmsg){ if (debug) sys->print("> %s\n", r.text()); a := r.pack(); if(styx->write(styxfd, a, len a) != len a) sys->print("ftpfs: error replying: %r\n");} rerror(tag: int, s: string){ if (debug) sys->print("error: %s\n", s); sendreply(ref Rmsg.Error(tag, s));} seterr(e: int, s: string): int{ case e { Syserr => errstr = Eioerror; Syntax => errstr = Eftpproto; Shutdown => errstr = Eshutdown; * => errstr = s; } return -1;} ## Node routines.# anode: Node;npath: int = 1; newnode(parent: ref Node, name: string): ref Node{ n := ref anode; n.dir.name = name; n.dir.atime = time->now(); n.children = nil; n.remname = name; if (parent != nil) { n.parent = parent; n.sibs = parent.children; parent.children = n; n.depth = parent.depth + 1; n.valid = 0; } else { n.parent = n; n.sibs = nil; n.depth = 0; n.valid = 1; n.dir.uid = anon; n.dir.gid = anon; n.dir.mtime = n.dir.atime; } n.file = nil; n.uniq = npath++; n.cached = 0; return n;} Node.extendpath(parent: self ref Node, elem: string): ref Node{ n: ref Node; for (n = parent.children; n != nil; n = n.sibs) if (n.dir.name == elem) return n; return newnode(parent, elem);} Node.markcached(n: self ref Node){ n.cached = 1; n.dir.atime = time->now();} Node.uncache(n: self ref Node){ if (n.fileisdirty()) n.createfile(); n.filefree(); n.cached = 0;} Node.uncachedir(parent: self ref Node, child: ref Node){ sp: ref Node; if (parent == nil || parent == child) return; for (sp = parent.children; sp != nil; sp = sp.sibs) if (sp != child && sp.file != nil && !sp.file.dirty && sp.file.fd != nil) { sp.filefree(); sp.cached = 0; }} Node.invalidate(node: self ref Node){ n: ref Node; node.uncachedir(nil); for (n = node.children; n != nil; n = n.sibs) { n.cached = 0; n.invalidate(); n.valid = 0; }} Node.fixsymbolic(n: self ref Node){ if (n.changedir() == 0) { n.dir.mode |= Sys->DMDIR; n.dir.qid.qtype = Sys->QTDIR; } else n.dir.qid.qtype = Sys->QTFILE; n.dir.mode &= ~CHSYML; } Node.stat(n: self ref Node): array of byte{ return styx->packdir(n.dir);} Node.qid(n: self ref Node): Sys->Qid{ if(n.dir.mode & Sys->DMDIR) return Sys->Qid(big n.uniq, 0, Sys->QTDIR); return Sys->Qid(big n.uniq, 0, Sys->QTFILE);} ## File routines.# ntmp: int;files: list of ref File;nfiles: int;afile: File;atime: int; ## Allocate a file structure for a node. If too many# are already allocated discard the oldest.# Node.fileget(n: self ref Node): ref File{ f, o: ref File; l: list of ref File; if (n.file != nil) return n.file; o = nil; for (l = files; l != nil; l = tl l) { f = hd l; if (f.inuse == 0) break; if (!f.dirty && (o == nil || o.atime > f.atime)) o = f; } if (l == nil) { if (nfiles == Nfiles && o != nil) { o.node.uncache(); f = o; } else { f = ref afile; files = f :: files; nfiles++; } } n.file = f; f.node = n; f.atime = atime++; f.inuse = 1; f.dirty = 0; f.length = 0; f.fd = nil; return f;} ## Create a temporary file for a local copy of a file.# If too many are open uncache parent.# File.createtmp(f: self ref File): ref FD{ t := "/tmp/ftp." + string time->now() + "." + string ntmp; if (ntmp >= 16) f.node.parent.uncachedir(f.node); f.fd = sys->create(t, Sys->ORDWR | Sys->ORCLOSE, 8r600); f.tempname = t; f.offset = 0; ntmp++; return f.fd;} ## Read 'c' bytes at offset 'off' from a file into buffer 'b'.# Node.fileread(n: self ref Node, b: array of byte, off, c: int): int{ f: ref File; t, i: int; f = n.file; if (off + c > f.length) c = f.length - off; for (t = 0; t < c; t += i) { if (off >= f.length) return t; if (off < Chunk) { i = c; if (off + i > Chunk) i = Chunk - off;Lines 1–500 of 1963. Load more
implement Ftpfs; include "sys.m"; sys: Sys; FD, Dir: import Sys; include "draw.m"; include "arg.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "daytime.m"; time: Daytime; Tm: import time; include "string.m"; str: String; include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx; include "dial.m"; dial: Dial; Connection: import dial; include "factotum.m"; Ftpfs: module { init: fn(nil: ref Draw->Context, argv: list of string); }; # # File system node. Refers to parent and file structure. # Siblings are linked. The head is parent.children. # Node: adt { dir: Dir; uniq: int; parent: cyclic ref Node; sibs: cyclic ref Node; children: cyclic ref Node; file: cyclic ref File; depth: int; remname: string; cached: int; valid: int; extendpath: fn(parent: self ref Node, elem: string): ref Node; fixsymbolic: fn(n: self ref Node); invalidate: fn(n: self ref Node); markcached: fn(n: self ref Node); uncache: fn(n: self ref Node); uncachedir: fn(parent: self ref Node, child: ref Node); stat: fn(n: self ref Node): array of byte; qid: fn(n: self ref Node): Sys->Qid; fileget: fn(n: self ref Node): ref File; filefree: fn(n: self ref Node); fileclean: fn(n: self ref Node); fileisdirty: fn(n: self ref Node): int; filedirty: fn(n: self ref Node); fileread: fn(n: self ref Node, b: array of byte, off, c: int): int; filewrite: fn(n: self ref Node, b: array of byte, off, c: int): int; action: fn(n: self ref Node, cmd: string): int; createdir: fn(n: self ref Node): int; createfile: fn(n: self ref Node): int; changedir: fn(n: self ref Node): int; docreate: fn(n: self ref Node): int; pathname: fn(n: self ref Node): string; readdir: fn(n: self ref Node): int; readfile: fn(n: self ref Node): int; removedir: fn(n: self ref Node): int; removefile: fn(n: self ref Node): int; }; # # Styx protocol file identifier. # Fid: adt { fid: int; node: ref Node; busy: int; }; # # Foreign file with cache. # File: adt { cache: array of byte; length: int; offset: int; fd: ref FD; inuse, dirty: int; atime: int; node: cyclic ref Node; tempname: string; createtmp: fn(f: self ref File): ref FD; }; ftp: ref Connection; dfid: ref FD; dfidiob: ref Iobuf; buffresidue: int = 0; tbuff: array of byte; rbuff: array of byte; ccfd: ref FD; stdin, stderr: ref FD; fids: list of ref Fid; BSZ: con 8192; Chunk: con 1024; Nfiles: con 128; CHSYML: con 16r40000000; mountpoint: string = "/n/ftp"; user: string = nil; password: string; hostname: string = "kremvax"; anon: string = "anon"; firewall: string = "tcp!$proxy!402"; myname: string = "anon"; myhost: string = "lucent.com"; proxyid: string; proxyhost: string; errstr: string; net: string; port: int; Enosuchfile: con "file does not exist"; Eftpproto: con "ftp protocol error"; Eshutdown: con "remote shutdown"; Eioerror: con "io error"; Enotadirectory: con "not a directory"; Eisadirectory: con "is a directory"; Epermission: con "permission denied"; Ebadoffset: con "bad offset"; Ebadlength: con "bad length"; Enowstat: con "wstat not implemented"; Emesgmismatch: con "message size mismatch"; remdir: ref Node; remroot: ref Node; remrootpath: string; heartbeatpid: int; # # FTP protocol codes are 3 digits >= 100. # The code type is obtained by dividing by 100. # Syserr: con -2; Syntax: con -1; Shutdown: con 0; Extra: con 1; Success: con 2; Incomplete: con 3; TempFail: con 4; PermFail: con 5; Impossible: con 6; Err: con 7; debug: int = 0; quiet: int = 0; active: int = 0; cdtoroot: int = 0; proxy: int = 0; mountfd: ref FD; styxfd: ref FD; # # Set up FDs for service. # connect(): string { pip := array[2] of ref Sys->FD; if(sys->pipe(pip) < 0) return sys->sprint("can't create pipe: %r"); mountfd = pip[0]; styxfd = pip[1]; return nil; } #shut(s: string) #{ # sys->print("ftpfs: %s shutdown\n", s); #} # # Mount server. Must be spawned because it does # an attach transaction. # mount(mountpoint: string) { if (sys->mount(mountfd, nil, mountpoint, sys->MREPL | sys->MCREATE, nil) < 0) { sys->print("mount %s failed: %r\n", mountpoint); shutdown(); } mountfd = nil; } # # Keep the link alive. # beatquanta: con 10; beatlimit: con 10; beatcount: int; activity: int; transfer: int; heartbeat(pidc: chan of int) { pid := sys->pctl(0, nil); pidc <-= pid; for (;;) { sys->sleep(beatquanta * 1000); if (activity || transfer) { beatcount = 0; activity = 0; continue; } beatcount++; if (beatcount == beatlimit) { acquire(); if (sendrequest("NOOP", 0) == Success) getreply(0); release(); beatcount = 0; activity = 0; } } } # # Control lock. # ctllock: chan of int; acquire() { ctllock <-= 1; } release() { <-ctllock; } # # Data formatting routines. # sendreply(r: ref Rmsg) { if (debug) sys->print("> %s\n", r.text()); a := r.pack(); if(styx->write(styxfd, a, len a) != len a) sys->print("ftpfs: error replying: %r\n"); } rerror(tag: int, s: string) { if (debug) sys->print("error: %s\n", s); sendreply(ref Rmsg.Error(tag, s)); } seterr(e: int, s: string): int { case e { Syserr => errstr = Eioerror; Syntax => errstr = Eftpproto; Shutdown => errstr = Eshutdown; * => errstr = s; } return -1; } # # Node routines. # anode: Node; npath: int = 1; newnode(parent: ref Node, name: string): ref Node { n := ref anode; n.dir.name = name; n.dir.atime = time->now(); n.children = nil; n.remname = name; if (parent != nil) { n.parent = parent; n.sibs = parent.children; parent.children = n; n.depth = parent.depth + 1; n.valid = 0; } else { n.parent = n; n.sibs = nil; n.depth = 0; n.valid = 1; n.dir.uid = anon; n.dir.gid = anon; n.dir.mtime = n.dir.atime; } n.file = nil; n.uniq = npath++; n.cached = 0; return n; } Node.extendpath(parent: self ref Node, elem: string): ref Node { n: ref Node; for (n = parent.children; n != nil; n = n.sibs) if (n.dir.name == elem) return n; return newnode(parent, elem); } Node.markcached(n: self ref Node) { n.cached = 1; n.dir.atime = time->now(); } Node.uncache(n: self ref Node) { if (n.fileisdirty()) n.createfile(); n.filefree(); n.cached = 0; } Node.uncachedir(parent: self ref Node, child: ref Node) { sp: ref Node; if (parent == nil || parent == child) return; for (sp = parent.children; sp != nil; sp = sp.sibs) if (sp != child && sp.file != nil && !sp.file.dirty && sp.file.fd != nil) { sp.filefree(); sp.cached = 0; } } Node.invalidate(node: self ref Node) { n: ref Node; node.uncachedir(nil); for (n = node.children; n != nil; n = n.sibs) { n.cached = 0; n.invalidate(); n.valid = 0; } } Node.fixsymbolic(n: self ref Node) { if (n.changedir() == 0) { n.dir.mode |= Sys->DMDIR; n.dir.qid.qtype = Sys->QTDIR; } else n.dir.qid.qtype = Sys->QTFILE; n.dir.mode &= ~CHSYML; } Node.stat(n: self ref Node): array of byte { return styx->packdir(n.dir); } Node.qid(n: self ref Node): Sys->Qid { if(n.dir.mode & Sys->DMDIR) return Sys->Qid(big n.uniq, 0, Sys->QTDIR); return Sys->Qid(big n.uniq, 0, Sys->QTFILE); } # # File routines. # ntmp: int; files: list of ref File; nfiles: int; afile: File; atime: int; # # Allocate a file structure for a node. If too many # are already allocated discard the oldest. # Node.fileget(n: self ref Node): ref File { f, o: ref File; l: list of ref File; if (n.file != nil) return n.file; o = nil; for (l = files; l != nil; l = tl l) { f = hd l; if (f.inuse == 0) break; if (!f.dirty && (o == nil || o.atime > f.atime)) o = f; } if (l == nil) { if (nfiles == Nfiles && o != nil) { o.node.uncache(); f = o; } else { f = ref afile; files = f :: files; nfiles++; } } n.file = f; f.node = n; f.atime = atime++; f.inuse = 1; f.dirty = 0; f.length = 0; f.fd = nil; return f; } # # Create a temporary file for a local copy of a file. # If too many are open uncache parent. # File.createtmp(f: self ref File): ref FD { t := "/tmp/ftp." + string time->now() + "." + string ntmp; if (ntmp >= 16) f.node.parent.uncachedir(f.node); f.fd = sys->create(t, Sys->ORDWR | Sys->ORCLOSE, 8r600); f.tempname = t; f.offset = 0; ntmp++; return f.fd; } # # Read 'c' bytes at offset 'off' from a file into buffer 'b'. # Node.fileread(n: self ref Node, b: array of byte, off, c: int): int { f: ref File; t, i: int; f = n.file; if (off + c > f.length) c = f.length - off; for (t = 0; t < c; t += i) { if (off >= f.length) return t; if (off < Chunk) { i = c; if (off + i > Chunk) i = Chunk - off;