forsyth20121205-174063a80cc13 years agoHistory
630 lines · 11.8 KB · Brainfuck
RawDownload
implement Archfs; include "sys.m";	sys: Sys;include "draw.m"; include "bufio.m";	bufio: Bufio; include "string.m";	str: String; include "daytime.m";	daytime: Daytime; include "styx.m";	styx: Styx;	NOFID: import Styx; include "arg.m"; Archfs: module{	init:	fn(nil: ref Draw->Context, nil: list of string);}; Ahdr: adt {	name: string;	modestr: string;	d: ref Sys->Dir;}; Archive: adt {	b: ref Bufio->Iobuf;	nexthdr: big;	canseek: int;	hdr: ref Ahdr;	err: string;}; Iobuf: import bufio;Tmsg, Rmsg: import styx; Einuse		: con "fid already in use";Ebadfid		: con "bad fid";Eopen		: con "fid already opened";Enotfound	: con "file does not exist";Enotdir		: con "not a directory";Eperm		: con "permission denied"; UID: con "inferno";GID: con "inferno"; debug := 0; Dir: adt {	dir: Sys->Dir;	offset: big;	parent: cyclic ref Dir;	child: cyclic ref Dir;	sibling: cyclic ref Dir;}; Fid: adt {	fid:	int;	open:	int;	dir:	ref Dir;}; HTSZ: con 32;fidtab := array[HTSZ] of list of ref Fid; root: ref Dir;qid: int;mtpt := "/mnt/arch";bio: ref Iobuf;buf: array of byte;skip := 0; init(nil: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	bufio = load Bufio Bufio->PATH;	str = load String String->PATH;	daytime = load Daytime Daytime->PATH;	styx = load Styx Styx->PATH;	if(bufio == nil || styx == nil || daytime == nil || str == nil)		fatal("failed to load modules");	styx->init(); 	flags := Sys->MREPL;	arg := load Arg Arg->PATH;	if(arg == nil)		fatal("failed to load "+Arg->PATH);	arg->init(args);	arg->setusage("archfs [-ab] [-m mntpt] archive [prefix ...]");	while((c := arg->opt()) != 0){		case c {		'D' =>			debug = 1;		'a' =>			flags = Sys->MAFTER;		'b' =>			flags = Sys->MBEFORE;		'm' =>			mtpt = arg->earg();		's' =>			skip = 1;		* =>			arg->usage();		}	}	args = arg->argv();	if(args == nil)		arg->usage();	arg = nil; 	buf = array[Sys->ATOMICIO] of byte;	# root = newdir("/", UID, GID, 8r755|Sys->DMDIR, daytime->now());	root = newdir(basename(mtpt), UID, GID, 8r555|Sys->DMDIR, daytime->now());	root.parent = root;	readarch(hd args, tl args);	p := array[2] of ref Sys->FD;	if(sys->pipe(p) < 0)		fatal("can't create pipe");	pidch := chan of int;	spawn serve(p[1], pidch);	<- pidch;	if(sys->mount(p[0], nil, mtpt, flags, nil) < 0)		fatal(sys->sprint("cannot mount archive on %s: %r", mtpt));} reply(fd: ref Sys->FD, m: ref Rmsg): int{	if(debug)		sys->fprint(sys->fildes(2), "-> %s\n", m.text());	s := m.pack();	if(s == nil)		return -1;	return sys->write(fd, s, len s);} error(fd: ref Sys->FD, m: ref Tmsg, e: string){	reply(fd, ref Rmsg.Error(m.tag, e));} serve(fd: ref Sys->FD, pidch: chan of int){	e: string;	f: ref Fid; 	pidch <-= sys->pctl(Sys->NEWNS|Sys->NEWFD, 1 :: 2 :: fd.fd :: bio.fd.fd :: nil);	bio.fd = sys->fildes(bio.fd.fd);	fd = sys->fildes(fd.fd);Work:	while((m0 := Tmsg.read(fd, Styx->MAXRPC)) != nil){		if(debug)			sys->fprint(sys->fildes(2), "<- %s\n", m0.text());		pick m := m0 {		Readerror =>			fatal("read error on styx server");		Version =>			(s, v) := styx->compatible(m, Styx->MAXRPC, Styx->VERSION);			reply(fd, ref Rmsg.Version(m.tag, s, v));		Auth =>			error(fd, m, "authentication not required");		Flush =>			reply(fd, ref Rmsg.Flush(m.tag));		Walk =>			(f, e) = mapfid(m.fid);			if(e != nil){				error(fd, m, e);				continue;			}			if(f.open){				error(fd, m, Eopen);				continue;			}			dir := f.dir;			nq := 0;			nn := len m.names;			qids := array[nn] of Sys->Qid;			if(nn > 0){				for(k := 0; k < nn; k++){					if((dir.dir.mode & Sys->DMDIR) == 0){						if(k == 0){							error(fd, m, Enotdir);							continue Work;						}						break;					}					dir  = lookup(dir, m.names[k]);					if(dir == nil){						if(k == 0){							error(fd, m, Enotfound);							continue Work;						}						break;					}					qids[nq++] = dir.dir.qid;				}			}			if(nq < nn)				qids = qids[0: nq];			if(nq == nn){				if(m.newfid != m.fid){					f = newfid(m.newfid);					if(f == nil){						error(fd, m, Einuse);						continue Work;					}				}				f.dir = dir;			}			reply(fd, ref Rmsg.Walk(m.tag, qids));		Open =>			(f, e) = mapfid(m.fid);			if(e != nil){				error(fd, m, e);				continue;			}			if(m.mode != Sys->OREAD){				error(fd, m, Eperm);				continue;			}			f.open = 1;			reply(fd, ref Rmsg.Open(m.tag, f.dir.dir.qid, Styx->MAXFDATA));		Create =>			error(fd, m, Eperm);		Read =>			(f, e) = mapfid(m.fid);			if(e != nil){				error(fd, m, e);				continue;			}			data := read(f.dir, m.offset, m.count);			reply(fd, ref Rmsg.Read(m.tag, data));		Write =>			error(fd, m, Eperm);						Clunk =>			(f, e) = mapfid(m.fid);			if(e != nil){				error(fd, m, e);				continue;			}			freefid(f);			reply(fd, ref Rmsg.Clunk(m.tag));		Stat =>			(f, e) = mapfid(m.fid);			if(e != nil){				error(fd, m, e);				continue;			}			reply(fd, ref Rmsg.Stat(m.tag, f.dir.dir));		Remove =>			error(fd, m, Eperm);		Wstat =>			error(fd, m, Eperm);		Attach =>			f = newfid(m.fid);			if(f == nil){				error(fd, m, Einuse);				continue;			}			f.dir = root;			reply(fd, ref Rmsg.Attach(m.tag, f.dir.dir.qid));		* =>			fatal("unknown styx message");		}	}} newfid(fid: int): ref Fid{	if(fid == NOFID)		return nil;	hv := hashval(fid);	ff: ref Fid;	for(l := fidtab[hv]; l != nil; l = tl l){		f := hd l;		if(f.fid == fid)			return nil;		if(ff == nil && f.fid == NOFID)			ff = f;	}	if((f := ff) == nil){		f = ref Fid;		fidtab[hv] = f :: fidtab[hv];	}	f.fid = fid;	f.open = 0;	return f;} freefid(f: ref Fid){	hv := hashval(f.fid);	for(l := fidtab[hv]; l != nil; l = tl l)		if(hd l == f){			f.fid = NOFID;			f.dir = nil;			f.open = 0;			return;		}	fatal("cannot find fid");}	mapfid(fid: int): (ref Fid, string){	if(fid == NOFID)		return (nil, Ebadfid);	hv := hashval(fid);	for(l := fidtab[hv]; l != nil; l = tl l){		f := hd l;		if(f.fid == fid){			if(f.dir == nil)				return (nil, Enotfound);			return (f, nil);		}	}	return (nil, Ebadfid);} hashval(n: int): int{	n %= HTSZ;	if(n < 0)		n += HTSZ;	return n;} readarch(f: string, args: list of string){	ar := openarch(f);	if(ar == nil || ar.b == nil)		fatal(sys->sprint("cannot open %s: %r", f));	bio = ar.b;	while((a := gethdr(ar)) != nil){		if(args != nil){			if(!selected(a.name, args)){				if(skip)					return;				#drain(ar, int a.d.length);				continue;			}			mkdirs("/", a.name);		}		d := mkdir(a.name, a.d.mode, a.d.mtime, a.d.uid, a.d.gid, 0);		if((a.d.mode & Sys->DMDIR) == 0){			d.dir.length = a.d.length;			d.offset = bio.offset();		}		#drain(ar, int a.d.length);	}	if(ar.err != nil)		fatal(ar.err);} selected(s: string, args: list of string): int{	for(; args != nil; args = tl args)		if(fileprefix(hd args, s))			return 1;	return 0;} fileprefix(prefix, s: string): int{	n := len prefix;	m := len s;	if(n > m || !str->prefix(prefix, s))		return 0;	if(m > n && s[n] != '/')		return 0;	return 1;} basename(f: string): string{	for(i := len f; i > 0; ) 		if(f[--i] == '/')			return f[i+1:];	return f;} split(p: string): (string, string){	if(p == nil)		fatal("nil string in split");	if(p[0] != '/')		fatal("p0 not / in split");	while(p[0] == '/')		p = p[1:];	i := 0;	while(i < len p && p[i] != '/')		i++;	if(i == len p)		return (p, nil);	else		return (p[0:i], p[i:]);} mkdirs(basedir, name: string){	(nil, names) := sys->tokenize(name, "/");	while(names != nil){		# sys->print("mkdir %s\n", basedir);		mkdir(basedir, 8r775|Sys->DMDIR, daytime->now(), UID, GID, 1);		if(tl names == nil)			break;		basedir = basedir + "/" + hd names;		names = tl names;	}} read(d: ref Dir, offset: big, n: int): array of byte{	if(d.dir.mode & Sys->DMDIR)		return readdir(d, int offset, n);	return readfile(d, offset, n);}	readdir(d: ref Dir, o: int, n: int): array of byte{	k := 0;	m := 0;	b := array[n] of byte;	for(s := d.child; s != nil; s = s.sibling){		l := styx->packdirsize(s.dir);		if(k < o){			k += l;			continue;		}		if(m+l > n)			break;		b[m: ] = styx->packdir(s.dir);		m += l;	}	return b[0: m];} readfile(d: ref Dir, offset: big, n: int): array of byte{	if(offset+big n > d.dir.length)		n = int(d.dir.length-offset);	if(n <= 0 || offset < big 0)		return nil;	bio.seek(d.offset+offset, Bufio->SEEKSTART);	a := array[n] of byte;	p := 0;	m := 0;	for( ; n != 0; n -= m){		l := len buf;		if(n < l)			l = n;		m = bio.read(buf, l);		if(m <= 0 || m != l)			fatal("premature eof");		a[p:] = buf[0:m];		p += m;	}	return a;} mkdir(f: string, mode: int, mtime: int, uid: string, gid: string, existsok: int): ref Dir{	if(f == "/")		return nil;	d := newdir(basename(f), uid, gid, mode, mtime);	addfile(d, f, existsok);	return d;} addfile(d: ref Dir, path: string, existsok: int){	elem: string; 	opath := path;	p := prev := root;	basedir := "";# sys->print("addfile %s: %s\n", d.dir.name, path);	while(path != nil){		(elem, path) = split(path);		basedir += "/" + elem;		op := p;		p = lookup(p, elem);		if(path == nil){			if(p != nil){				if(!existsok && (p.dir.mode&Sys->DMDIR) == 0)					sys->fprint(sys->fildes(2), "addfile: %s already there", opath);					# fatal(sys->sprint("addfile: %s already there", opath));				return;			}			if(prev.child == nil)				prev.child = d;			else {				for(s := prev.child; s.sibling != nil; s = s.sibling)					;				s.sibling = d;

Lines 1–500 of 630.