summaryrefslogtreecommitdiff
path: root/appl/cmd/memfs.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/memfs.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/memfs.b')
-rw-r--r--appl/cmd/memfs.b648
1 files changed, 648 insertions, 0 deletions
diff --git a/appl/cmd/memfs.b b/appl/cmd/memfs.b
new file mode 100644
index 00000000..e18388c5
--- /dev/null
+++ b/appl/cmd/memfs.b
@@ -0,0 +1,648 @@
+implement MemFS;
+
+include "sys.m";
+ sys: Sys;
+ OTRUNC, ORCLOSE, OREAD, OWRITE: import Sys;
+include "styx.m";
+ styx: Styx;
+ Tmsg, Rmsg: import styx;
+include "styxlib.m";
+ styxlib: Styxlib;
+ Styxserver: import styxlib;
+include "draw.m";
+include "arg.m";
+
+MemFS: module {
+ init: fn(ctxt: ref Draw->Context, args: list of string);
+};
+
+
+blksz : con 512;
+Efull : con "filesystem full";
+
+Memfile : adt {
+ name : string;
+ owner : string;
+ qid : Sys->Qid;
+ perm : int;
+ atime : int;
+ mtime : int;
+ nopen : int;
+ data : array of array of byte; # allocated in blks, no holes
+ length : int;
+ parent : cyclic ref Memfile; # Dir entry linkage
+ kids : cyclic ref Memfile;
+ prev : cyclic ref Memfile;
+ next : cyclic ref Memfile;
+ hashnext : cyclic ref Memfile; # Qid hash linkage
+};
+
+Qidhash : adt {
+ buckets : array of ref Memfile;
+ nextqid : int;
+ new : fn () : ref Qidhash;
+ add : fn (h : self ref Qidhash, mf : ref Memfile);
+ remove : fn (h : self ref Qidhash, mf : ref Memfile);
+ lookup : fn (h : self ref Qidhash, qid : Sys->Qid) : ref Memfile;
+};
+
+timefd: ref Sys->FD;
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ styx = checkload(load Styx Styx->PATH, Styx->PATH);
+ styxlib = checkload(load Styxlib Styxlib->PATH, Styxlib->PATH);
+ arg := checkload(load Arg Arg->PATH, Arg->PATH);
+
+ amode := Sys->MREPL;
+ maxsz := 16r7fffffff;
+ srv := 0;
+ mntpt := "/tmp";
+
+ arg->init(argv);
+ arg->setusage("memfs [-s] [-rab] [-m size] [mountpoint]");
+ while((opt := arg->opt()) != 0) {
+ case opt{
+ 's' =>
+ srv = 1;
+ 'r' =>
+ amode = Sys->MREPL;
+ 'a' =>
+ amode = Sys->MAFTER;
+ 'b' =>
+ amode = Sys->MBEFORE;
+ 'm' =>
+ maxsz = int arg->earg();
+ * =>
+ arg->usage();
+ }
+ }
+ argv = arg->argv();
+ arg = nil;
+ if (argv != nil)
+ mntpt = hd argv;
+
+ srvfd: ref Sys->FD;
+ mntfd: ref Sys->FD;
+ if (srv)
+ srvfd = sys->fildes(0);
+ else {
+ p := array [2] of ref Sys->FD;
+ if (sys->pipe(p) == -1)
+ error(sys->sprint("cannot create pipe: %r"));
+ mntfd = p[0];
+ srvfd = p[1];
+ }
+ styx->init();
+ styxlib->init(styx);
+ timefd = sys->open("/dev/time", sys->OREAD);
+
+ (tc, styxsrv) := Styxserver.new(srvfd);
+ if (srv)
+ memfs(maxsz, tc, styxsrv, nil);
+ else {
+ sync := chan of int;
+ spawn memfs(maxsz, tc, styxsrv, sync);
+ <-sync;
+ if (sys->mount(mntfd, nil, mntpt, amode | Sys->MCREATE, nil) == -1)
+ error(sys->sprint("failed to mount onto %s: %r", mntpt));
+ }
+}
+
+checkload[T](x: T, p: string): T
+{
+ if(x == nil)
+ error(sys->sprint("cannot load %s: %r", p));
+ return x;
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+error(e: string)
+{
+ sys->fprint(stderr(), "memfs: %s\n", e);
+ raise "fail:error";
+}
+
+freeblks: int;
+
+memfs(maxsz : int, tc : chan of ref Tmsg, srv : ref Styxserver, sync: chan of int)
+{
+ sys->pctl(Sys->NEWNS, nil);
+ if (sync != nil)
+ sync <-= 1;
+ freeblks = (maxsz / blksz);
+ qhash := Qidhash.new();
+
+ # init root
+ root := newmf(qhash, nil, "memfs", srv.uname, 8r755 | Sys->DMDIR);
+ root.parent = root;
+
+ while((tmsg := <-tc) != nil) {
+# sys->print("%s\n", tmsg.text());
+ Msg:
+ pick tm := tmsg {
+ Readerror =>
+ break;
+ Version =>
+ srv.devversion(tm);
+ Auth =>
+ srv.devauth(tm);
+ Flush =>
+ srv.reply(ref Rmsg.Flush(tm.tag));
+ Walk =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ if (err != "") {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+ nc: ref styxlib->Chan;
+ if (tm.newfid != tm.fid) {
+ nc = srv.clone(c, tm.newfid);
+ if (nc == nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, "fid in use"));
+ continue;
+ }
+ c = nc;
+ }
+ qids: array of Sys->Qid;
+ if (len tm.names > 0) {
+ oqid := c.qid;
+ opath := c.path;
+ qids = array[len tm.names] of Sys->Qid;
+ wmf := mf;
+ for (i := 0; i < len tm.names; i++) {
+ wmf = dirlookup(wmf, tm.names[i]);
+ if (wmf == nil) {
+ if (nc == nil) {
+ c.qid = oqid;
+ c.path = opath;
+ } else
+ srv.chanfree(nc);
+ if (i == 0)
+ srv.reply(ref Rmsg.Error(tm.tag, Styxlib->Enotfound));
+ else
+ srv.reply(ref Rmsg.Walk(tm.tag, qids[0:i]));
+ break Msg;
+ }
+ c.qid = wmf.qid;
+ qids[i] = wmf.qid;
+ }
+ }
+ srv.reply(ref Rmsg.Walk(tm.tag, qids));
+ Open =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ if (err == "" && c.open)
+ err = Styxlib->Eopen;
+ if (err == "" && !modeok(tm.mode, mf.perm, c.uname, mf.owner))
+ err = Styxlib->Eperm;
+ if (err == "" && (mf.perm & Sys->DMDIR) && (tm.mode & (OTRUNC|OWRITE|ORCLOSE)))
+ err = Styxlib->Eperm;
+ if (err == "" && (tm.mode & ORCLOSE)) {
+ p := mf.parent;
+ if (p == nil || !modeok(OWRITE, p.perm, c.uname, p.owner))
+ err = Styxlib->Eperm;
+ }
+
+ if (err != "") {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+
+ c.open = 1;
+ c.mode = tm.mode;
+ c.qid.vers = mf.qid.vers;
+ mf.nopen++;
+ if (tm.mode & OTRUNC) {
+ # OTRUNC cannot be set for a directory
+ # always at least one blk so don't need to check fs limit
+ freeblks += (len mf.data);
+ mf.data = nil;
+ freeblks--;
+ mf.data = array[1] of {* => array [blksz] of byte};
+ mf.length = 0;
+ mf.mtime = now();
+ }
+ srv.reply(ref Rmsg.Open(tm.tag, mf.qid, Styx->MAXFDATA));
+ Create =>
+ (err, c, parent) := fidtomf(srv, qhash, tm.fid);
+ if (err == "" && c.open)
+ err = Styxlib->Eopen;
+ if (err == "" && !(parent.qid.qtype & Sys->QTDIR))
+ err = Styxlib->Enotdir;
+ if (err == "" && !modeok(OWRITE, parent.perm, c.uname, parent.owner))
+ err = Styxlib->Eperm;
+ if (err == "" && (tm.perm & Sys->DMDIR) && (tm.mode & (OTRUNC|OWRITE|ORCLOSE)))
+ err = Styxlib->Eperm;
+ if (err == "" && dirlookup(parent, tm.name) != nil)
+ err = Styxlib->Eexists;
+
+ if (err != "") {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+
+ isdir := tm.perm & Sys->DMDIR;
+ if (!isdir && freeblks <= 0) {
+ srv.reply(ref Rmsg.Error(tm.tag, Efull));
+ continue;
+ }
+
+ # modify perms as per Styx specification...
+ perm : int;
+ if (isdir)
+ perm = (tm.perm&~8r777) | (parent.perm&tm.perm&8r777);
+ else
+ perm = (tm.perm&(~8r777|8r111)) | (parent.perm&tm.perm& 8r666);
+
+ nmf := newmf(qhash, parent, tm.name, c.uname, perm);
+ if (!isdir) {
+ freeblks--;
+ nmf.data = array[1] of {* => array [blksz] of byte};
+ }
+
+ # link in the new MemFile
+ nmf.next = parent.kids;
+ if (parent.kids != nil)
+ parent.kids.prev = nmf;
+ parent.kids = nmf;
+
+ c.open = 1;
+ c.mode = tm.mode;
+ c.qid = nmf.qid;
+ nmf.nopen = 1;
+ srv.reply(ref Rmsg.Create(tm.tag, nmf.qid, Styx->MAXFDATA));
+ Read =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ if (err == "" && !c.open)
+ err = Styxlib->Ebadfid;
+
+ if (err != "") {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+ data: array of byte = nil;
+ if (mf.perm & Sys->DMDIR)
+ data = dirdata(mf, int tm.offset, tm.count);
+ else
+ data = filedata(mf, int tm.offset, tm.count);
+ mf.atime = now();
+ srv.reply(ref Rmsg.Read(tm.tag, data));
+ Write =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ if (c != nil && !c.open)
+ err = Styxlib->Ebadfid;
+ if (err == nil && (mf.perm & Sys->DMDIR))
+ err = Styxlib->Eperm;
+ if (err == nil)
+ err = writefile(mf, int tm.offset, tm.data);
+ if (err != nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+ srv.reply(ref Rmsg.Write(tm.tag, len tm.data));
+ Clunk =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ if (c != nil)
+ srv.chanfree(c);
+ if (err != nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+ if (c.open) {
+ if (c.mode & ORCLOSE)
+ unlink(mf);
+ mf.nopen--;
+ freeblks += delfile(qhash, mf);
+ }
+ srv.reply(ref Rmsg.Clunk(tm.tag));
+ Stat =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ if (err != nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+ srv.reply(ref Rmsg.Stat(tm.tag, fileinfo(mf)));
+ Remove =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ if (err != nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+ srv.chanfree(c);
+ parent := mf.parent;
+ if (!modeok(OWRITE, parent.perm, c.uname, parent.owner))
+ err = Styxlib->Eperm;
+ if (err == "" && (mf.perm & Sys->DMDIR) && mf.kids != nil)
+ err = "directory not empty";
+ if (err == "" && mf == root)
+ err = "root directory";
+ if (err != nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+
+ unlink(mf);
+ if (c.open)
+ mf.nopen--;
+ freeblks += delfile(qhash, mf);
+ srv.reply(ref Rmsg.Remove(tm.tag));
+ Wstat =>
+ (err, c, mf) := fidtomf(srv, qhash, tm.fid);
+ stat := tm.stat;
+ perm := mf.perm & ~Sys->DMDIR;
+ if (err == nil && stat.name != mf.name) {
+ parent := mf.parent;
+ if (!modeok(OWRITE, parent.perm, c.uname, parent.owner))
+ err = Styxlib->Eperm;
+ else if (dirlookup(parent, stat.name) != nil)
+ err = Styxlib->Eexists;
+ }
+ if (err == nil && (stat.mode != mf.perm || stat.mtime != mf.mtime)) {
+ if (c.uname != mf.owner)
+ err = Styxlib->Eperm;
+ }
+ if (err != nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, err));
+ continue;
+ }
+ isdir := mf.perm & Sys->DMDIR;
+ if(stat.name != nil)
+ mf.name = stat.name;
+ if(stat.mode != ~0)
+ mf.perm = stat.mode | isdir;
+ if(stat.mtime != ~0)
+ mf.mtime = stat.mtime;
+ if(stat.uid != nil)
+ mf.owner = stat.uid;
+ t := now();
+ mf.atime = t;
+ mf.parent.mtime = t;
+ # not supporting group id at the moment
+ srv.reply(ref Rmsg.Wstat(tm.tag));
+ Attach =>
+ c := srv.newchan(tm.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(tm.tag, Styxlib->Einuse));
+ continue;
+ }
+ c.uname = tm.uname;
+ c.qid = root.qid;
+ srv.reply(ref Rmsg.Attach(tm.tag, c.qid));
+ }
+ }
+}
+
+writefile(mf: ref Memfile, offset: int, data: array of byte): string
+{
+ if(mf.perm & Sys->DMAPPEND)
+ offset = mf.length;
+ startblk := offset/blksz;
+ nblks := ((len data + offset) - (startblk * blksz))/blksz;
+ lastblk := startblk + nblks;
+ need := lastblk + 1 - len mf.data;
+ if (need > 0) {
+ if (need > freeblks)
+ return Efull;
+ mf.data = (array [lastblk+1] of array of byte)[:] = mf.data;
+ freeblks -= need;
+ }
+ mf.length = max(mf.length, offset + len data);
+
+ # handle (possibly incomplete first block) separately
+ offset %= blksz;
+ end := min(blksz-offset, len data);
+ if (mf.data[startblk] == nil)
+ mf.data[startblk] = array [blksz] of byte;
+ mf.data[startblk++][offset:] = data[:end];
+
+ ix := blksz - offset;
+ while (ix < len data) {
+ if (mf.data[startblk] == nil)
+ mf.data[startblk] = array [blksz] of byte;
+ end = min(ix+blksz,len data);
+ mf.data[startblk++][:] = data[ix:end];
+ ix += blksz;
+ }
+ mf.mtime = now();
+ return nil;
+}
+
+filedata(mf: ref Memfile, offset, n: int): array of byte
+{
+ if (offset +n > mf.length)
+ n = mf.length - offset;
+ if (n == 0)
+ return nil;
+
+ data := array [n] of byte;
+ startblk := offset/blksz;
+ offset %= blksz;
+ rn := min(blksz - offset, n);
+ data[:] = mf.data[startblk++][offset:offset+rn];
+ ix := blksz - offset;
+ while (ix < n) {
+ rn = blksz;
+ if (ix+rn > n)
+ rn = n - ix;
+ data[ix:] = mf.data[startblk++][:rn];
+ ix += blksz;
+ }
+ return data;
+}
+
+QHSIZE: con 256;
+QHMASK: con QHSIZE-1;
+
+Qidhash.new() : ref Qidhash
+{
+ qh := ref Qidhash;
+ qh.buckets = array [QHSIZE] of ref Memfile;
+ qh.nextqid = 0;
+ return qh;
+}
+
+Qidhash.add(h : self ref Qidhash, mf : ref Memfile)
+{
+ path := h.nextqid++;
+ mf.qid = Sys->Qid(big path, 0, Sys->QTFILE);
+ bix := path & QHMASK;
+ mf.hashnext = h.buckets[bix];
+ h.buckets[bix] = mf;
+}
+
+Qidhash.remove(h : self ref Qidhash, mf : ref Memfile)
+{
+
+ bix := int mf.qid.path & QHMASK;
+ prev : ref Memfile;
+ for (cur := h.buckets[bix]; cur != nil; cur = cur.hashnext) {
+ if (cur == mf)
+ break;
+ prev = cur;
+ }
+ if (cur != nil) {
+ if (prev != nil)
+ prev.hashnext = cur.hashnext;
+ else
+ h.buckets[bix] = cur.hashnext;
+ cur.hashnext = nil;
+ }
+}
+
+Qidhash.lookup(h : self ref Qidhash, qid : Sys->Qid) : ref Memfile
+{
+ bix := int qid.path & QHMASK;
+ for (mf := h.buckets[bix]; mf != nil; mf = mf.hashnext)
+ if (mf.qid.path == qid.path)
+ break;
+ return mf;
+}
+
+newmf(qh : ref Qidhash, parent : ref Memfile, name, owner : string, perm : int) : ref Memfile
+{
+ # qid gets set by Qidhash.add()
+ t := now();
+ mf := ref Memfile (name, owner, Sys->Qid(big 0,0,Sys->QTFILE), perm, t, t, 0, nil, 0, parent, nil, nil, nil, nil);
+ qh.add(mf);
+ if(perm & Sys->DMDIR)
+ mf.qid.qtype = Sys->QTDIR;
+ return mf;
+}
+
+fidtomf(srv : ref Styxserver, qh : ref Qidhash, fid : int) : (string, ref Styxlib->Chan, ref Memfile)
+{
+ c := srv.fidtochan(fid);
+ if (c == nil)
+ return (Styxlib->Ebadfid, nil, nil);
+ mf := qh.lookup(c.qid);
+ if (mf == nil)
+ return (Styxlib->Enotfound, c, nil);
+ return (nil, c, mf);
+}
+
+unlink(mf : ref Memfile)
+{
+ parent := mf.parent;
+ if (parent == nil)
+ return;
+ if (mf.next != nil)
+ mf.next.prev = mf.prev;
+ if (mf.prev != nil)
+ mf.prev.next = mf.next;
+ else
+ mf.parent.kids = mf.next;
+ mf.parent = nil;
+ mf.prev = nil;
+ mf.next = nil;
+}
+
+delfile(qh : ref Qidhash, mf : ref Memfile) : int
+{
+ if (mf.nopen <= 0 && mf.parent == nil && mf.kids == nil
+ && mf.prev == nil && mf.next == nil) {
+ qh.remove(mf);
+ nblks := len mf.data;
+ mf.data = nil;
+ return nblks;
+ }
+ return 0;
+}
+
+dirlookup(dir : ref Memfile, name : string) : ref Memfile
+{
+ if (name == ".")
+ return dir;
+ if (name == "..")
+ return dir.parent;
+ for (mf := dir.kids; mf != nil; mf = mf.next) {
+ if (mf.name == name)
+ break;
+ }
+ return mf;
+}
+
+access := array[] of {8r400, 8r200, 8r600, 8r100};
+modeok(mode, perm : int, user, owner : string) : int
+{
+ if(mode >= (OTRUNC|ORCLOSE|OREAD|OWRITE))
+ return 0;
+
+ # not handling groups!
+ if (user != owner)
+ perm <<= 6;
+
+ if ((mode & OTRUNC) && !(perm & 8r200))
+ return 0;
+
+ a := access[mode &3];
+ if ((a & perm) != a)
+ return 0;
+ return 1;
+}
+
+dirdata(dir : ref Memfile, start, n : int) : array of byte
+{
+ data := array[Styx->MAXFDATA] of byte;
+ for (k := dir.kids; start > 0 && k != nil; k = k.next) {
+ a := styx->packdir(fileinfo(k));
+ start -= len a;
+ }
+ r := 0;
+ for (; r < n && k != nil; k = k.next) {
+ a := styx->packdir(fileinfo(k));
+ if(r+len a > n)
+ break;
+ data[r:] = a;
+ r += len a;
+ }
+ return data[0:r];
+}
+
+fileinfo(f : ref Memfile) : Sys->Dir
+{
+ dir := sys->zerodir;
+ dir.name = f.name;
+ dir.uid = f.owner;
+ dir.gid = "memfs";
+ dir.qid = f.qid;
+ dir.mode = f.perm;
+ dir.atime = f.atime;
+ dir.mtime = f.mtime;
+ dir.length = big f.length;
+ dir.dtype = 0;
+ dir.dev = 0;
+ return dir;
+}
+
+min(a, b : int) : int
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+max(a, b : int) : int
+{
+ if (a > b)
+ return a;
+ return b;
+}
+
+now(): int
+{
+ if (timefd == nil)
+ return 0;
+ buf := array[128] of byte;
+ sys->seek(timefd, big 0, 0);
+ n := sys->read(timefd, buf, len buf);
+ if(n < 0)
+ return 0;
+
+ t := (big string buf[0:n]) / big 1000000;
+ return int t;
+}