# File hierarchy handling code, and related tools for omero # implement Merotree; include "sys.m"; sys: Sys; millisec, sprint, QTFILE, DMDIR, DMEXCL, Qid, Dir, QTDIR, fprint: import sys; include "styx.m"; include "styxservers.m"; Styxserver, Eexists, Enotfound: import Styxservers; include "daytime.m"; daytime: Daytime; now: import daytime; include "dat.m"; dat: Dat; Qdir, Qctl, Qdata, Qimage: import Dat; mnt, debug, appl, user, slash: import dat; include "string.m"; str: String; include "names.m"; names: Names; rooted, elements, dirname: import names; include "error.m"; err: Error; checkload, panic, stderr: import err; include "tbl.m"; tbl: Tbl; Table: import tbl; include "mpanel.m"; panels: Panels; Panel, Repl, Trepl, Tappl, qid2ids, mkqid, Amax: import panels; include "rand.m"; rand: Rand; include "merotree.m"; srv: ref Styxserver; fstab: array of ref Fholder; NOQID: con big ~0; Aorder: con Amax; Fholder: adt { parentqid: big; d: Sys->Dir; child: cyclic ref Fholder; sibling: cyclic ref Fholder; hash: cyclic ref Fholder; }; init(d: Dat): chan of ref Styxservers->Navop { dat = d; sys = dat->sys; err = dat->err; str = dat->str; srv = dat->srv; daytime = dat->daytime; panels = dat->panels; names = dat->names; rand = checkload(load Rand Rand->PATH, Rand->PATH); rand->init(millisec()); fstab = array[101] of ref Fholder; c := chan of ref Styxservers->Navop; spawn navproc(c); return c; } navproc(c: chan of ref Styxservers->Navop) { while((m:= <-c) != nil){ (q, reply) := (m.path, m.reply); pick rq := m { Stat => fh := findfile(q); if (fh == nil) reply <-= (nil, Enotfound); else reply <-= (ref fh.d, nil); Walk => sq := fwalk1(q, rq.name); if (sq == NOQID) reply <-= (nil, Enotfound); else { fh := findfile(sq); reply <-= (ref fh.d, nil); } Readdir => fh := findfile(q); if (fh == nil) reply <-= (nil, Enotfound); else { (start, end) := (rq.offset, rq.offset + rq.count); fh = fh.child; for (i := 0; i < end && fh != nil; i++) { if (i >= start) reply <-= (ref fh.d, nil); fh = fh.sibling; } reply <-= (nil, nil); } * => panic(sys->sprint("unknown op %d\n", tagof(m))); } } } pparent(r: ref Repl): (ref Panel, ref Repl) { fh := findfile(r.dirq); if (fh == nil) return (nil, nil); (pid, rid, nil) := qid2ids(fh.parentqid); return Panel.lookup(pid, rid); } pchilds(r: ref Repl) : array of (ref Panel, ref Repl) { fh := findfile(r.dirq); if (fh == nil) return nil; childs := array[512] of big; nchilds := 0; for (cfh := fh.child; nchilds < 512 && cfh != nil; cfh = cfh.sibling) if (cfh.d.qid.qtype&QTDIR) childs[nchilds++] = cfh.d.qid.path; childs = childs[0:nchilds]; if (nchilds == 512) panic("fix me: more than 512 childs for panel"); cl := array[nchilds] of (ref Panel, ref Repl); for (i := 0; i < nchilds; i++){ (pid, rid, nil) := qid2ids(childs[i]); cl[i] = Panel.lookup(pid, rid); } return cl; } pchanged(p: ref Panel, data: int, ctl: int) { d := sys->nulldir; if (data){ d.qid.vers = ++p.vers; d.length = big len p.data; d.mtime = now(); } for (i := 0; i < len p.repl; i++){ r := p.repl[i]; if (r != nil){ (pid, rid, nil) := qid2ids(r.dirq); if (data){ dataf := mkqid(pid, rid, Qdata); fwstat(dataf, d); } if (ctl){ d2 := sys->nulldir; d2.qid.vers = ++r.vers; d2.length = big len p.ctlstr(r); d2.mtime = now(); ctlf := mkqid(pid, rid, Qctl); fwstat(ctlf, d2); } d2 := sys->nulldir; d2.qid.vers = ++r.dirvers; d2.mtime = now(); fwstat(r.dirq, d2); # propagate changes to the update, up to / (pp, pr) := pparent(r); if (pr != nil && pr != r && pr.dirq != slash) pchanged(pp, 0, 0); } } } # order attribute: "order panel1name panel2name panel3name ..." # This re-assigns replica positions to be unique, contiguous numbers, # while keeping the order found, and reconstructs the order attribute. neworder(dr: ref Repl) { childs := pchilds(dr); s := "order "; for (i := 0; i < len childs; i++){ m:= i; for (j := i+1; j < len childs; j++) if (childs[j].t1.pos < childs[i].t1.pos) m = j; t := childs[i]; childs[i] = childs[m]; childs[m] = t; childs[i].t1.pos = i; s += childs[i].t0.name + " "; } dr.attrs[Aorder] = s; } # Relocate replica r to be at pos ([0:n]) newreplpos(r: ref Repl, pos: int) { # Renumber all the ones going after so that # we get the slot. (nil, dr) := pparent(r); childs := pchilds(dr); for (i := 0; i < len childs; i++) if (childs[i].t1.pos >= pos){ childs[i].t1.pos++; } r.pos = pos; neworder(dr); } chpos(nil: ref Panel, r: ref Repl, pos: int) { if (r.pos == pos) # nothing to do. return; newreplpos(r, pos); (dp, nil) := pparent(r); pchanged(dp, 0, 1); dp.vpost(nil, "update"); } mkcol(p: ref Panel, r: ref Repl) { if (!p.container) (p, r) = pparent(r); if (!p.container) panic("mkcol"); name := sprint("col:user.%d", rand->rand(10000)); np := pcreate(p, r, name); np.ctl(nil, "layout"); } mktree() { p := Panel.new("col:root"); r := p.newrepl("/", Trepl); fcreate(r.dirq, newdir(".", DMDIR|8r775, r.dirq)); slash = r.dirq; p = Panel.new("row:apps"); r = p.newrepl("/appl", Tappl); fcreate(slash, newdir("appl", DMDIR|8r775, r.dirq)); appl = r.dirq; } mkscreen(dp: ref Panel) { menu := array[] of { "Ox", "New", "Exit" }; # dp.ctl(nil, "notag"); ps := pcreate(dp, nil, "row:stats"); ps.ctl(nil, "layout"); pc := pcreate(ps, nil, "row:cmds"); pc.ctl(nil, "layout"); for (i := 0; i < len menu; i++) pcreate(pc, nil, "button:" + menu[i]); pw := pcreate(dp, nil, "row:wins"); pw.ctl(nil, "layout"); c1 := pcreate(pw, nil, "col:1"); c1.ctl(nil, "layout"); c2 := pcreate(pw, nil, "col:2"); c2.ctl(nil, "layout"); } Dent: adt { name: string; mode: int; qtype: int; }; dirents: array of Dent; # creates a replica of p within dr, and its file tree pcreaterepl(dr: ref Repl, p: ref Panel, sizes: array of int): ref Repl { if (dirents == nil) dirents = array[] of { Dent("data", 8r660, Qdata), Dent("ctl", 8r660, Qctl), Dent("image", 8r660, Qimage), }; path := names->rooted(dr.path, p.name); r := p.newrepl(path, dr.tree); if (r == nil) panic("rcreate: newrepl"); childs := pchilds(dr); r.pos = len childs; d := newdir(p.name, DMDIR|8r775, r.dirq); fcreate(dr.dirq, d); neworder(dr); for (i := 0; i < len dirents; i++){ q := mkqid(p.id, r.id, dirents[i].qtype); dir := newdir(dirents[i].name, dirents[i].mode, q); if (sizes != nil) dir.length = big sizes[i]; fcreate(r.dirq, dir); } return r; } # Creates the panel and its tree. pcreate(dp: ref Panel, dr: ref Repl, name: string): ref Panel { if (dr == nil) dr = dp.repl[0]; d := dr.dirq; uname := name; if (d == slash) uname = "col:" + name; p := Panel.new(uname); if (p == nil) return nil; p.aid = dp.aid; # inherit from parent p.pid = dp.pid; # inherit from parent if (d == slash) p.name = name; for (i := 0; i < len dp.repl; i++) if ((dr = dp.repl[i]) != nil){ r := pcreaterepl(dr, p, nil); if (d == slash){ p.ctl(r, "layout"); mkscreen(p); } else if (d != appl){ (drp, nil) := pparent(r); pchanged(drp, 0, 1); drp.vpost(nil, "update"); } } return p; } # removes a panel including all its replicas when r is the 0-th replica # only the replica r otherwise. premove(p: ref Panel, rr: ref Repl, depth: int) { if (p.container){ childs := pchilds(rr); for (i := 0; i < len childs; i++){ (cp, cr) := childs[i]; premove(cp, cr, depth+1); } } for (i := 0; i < len p.repl; i++) if ((r := p.repl[i]) != nil && (r == rr || rr.id == 0)){ (pp, pr) := pparent(r); fremove(r.dirq); # removes childs as well neworder(pr); pchanged(pp, 0, 1); pp.vpost(nil, "update"); } if (rr.id == 0) p.close(); else p.closerepl(rr); } # is f within d? fwithin(d: big, f: big): int { if (d == f) return 1; fh := findfile(d); if (fh == nil) return 0; for (cfh := fh.child; cfh != nil; cfh = cfh.sibling){ if (cfh.d.qid.path == f) return 1; if (cfh.d.qid.qtype&QTDIR) if (fwithin(cfh.d.qid.path, f)) return 1; } return 0; } movetarget(r: ref Repl, path: string, n: string): (ref Panel, ref Repl, string) { dp: ref Panel; dr: ref Repl; (pp, pr) := pparent(r); if (pr.dirq == slash) return (nil, nil, "source is a screen tree"); if (path == nil) (dp, dr) = (pp, pr); else { dq := fwalk(slash, path); if (dq == NOQID) return (nil, nil, "no such file: " + path); if (fwithin(r.dirq, dq)) return (nil, nil, "read the GEB first"); (dpid, drid, nil) := qid2ids(dq); (dp, dr) = Panel.lookup(dpid, drid); if (fwalk1(dr.dirq, n) != NOQID) return (nil, nil, Eexists); } if (dr.tree == Tappl || dr.dirq == slash) return (nil, nil, "target is not a screen tree"); return (dp, dr, nil); } fixmovedrepl(p: ref Panel, r: ref Repl, spath, dpath: string) { rel := names->relative(r.path, spath); r.path = rooted(dpath, rel); if (p.container){ childs := pchilds(r); for (i := 0; i < len childs; i++){ (cp, cr) := childs[i]; fixmovedrepl(cp, cr, spath, dpath); } } } moveto(p: ref Panel, r: ref Repl, path: string, pos: int): string { if (r.tree == Tappl) return "moveto: not from within /appl tree"; (dp, dr, e) := movetarget(r, path, p.name); if (e != nil) return e; (pp, pr) := pparent(r); fdetach(r.dirq); neworder(pr); fattach(dr.dirq, r.dirq); childs := pchilds(dr); r.pos = len childs; neworder(dr); # double-check newreplpos(r, pos); fixmovedrepl(p, r, pr.path, dr.path); pchanged(p, 0, 1); pchanged(dp, 0, 1); dp.vpost(nil, "update"); pchanged(pp, 0, 1); pp.vpost(nil, "update"); return nil; } _copyto(nil: ref Panel, dr: ref Repl, p: ref Panel, r: ref Repl): ref Repl { sizes := array[3] of int; sizes[0] = len p.data; sizes[1] = len p.ctlstr(p.repl[0]); sizes[2] = 0; nr := pcreaterepl(dr, p, sizes); if (p.container){ childs := pchilds(r); for (i := 0; i < len childs; i++){ (cp, cr) := childs[i]; _copyto(p, nr, cp, cr); } nr.attrs[Aorder] = r.attrs[Aorder]; } return nr; } copyto(p: ref Panel, r: ref Repl, path: string, pos: int): string { (dp, dr, e) := movetarget(r, path, p.name); if (e != nil) return e; nr := _copyto(dp, dr, p, r); newreplpos(nr, pos); pchanged(dp, 0, 1); pchanged(p, 0, 1); dp.vpost(nil, "update"); return nil; } hashfn(q: big, n: int): int { h := int (q % big n); if (h < 0) h += n; return h; } findfile(q: big): ref Fholder { for (fh := fstab[hashfn(q, len fstab)]; fh != nil; fh = fh.hash) if (fh.d.qid.path == q) return fh; return nil; } fgetpath(q: big): string { fh := findfile(q); if (fh == nil) return nil; if (fh.parentqid == fh.d.qid.path) return "/"; s: string; for(;;) { if (s == nil) s = fh.d.name; else if (fh.parentqid == fh.d.qid.path) return "/" + s; else s = fh.d.name + "/" + s; fh = findfile(fh.parentqid); if (fh == nil) panic("fgetpath:parent not in table"); } return nil; } fwalk(f: big, path: string): big { els := elements(path); if (hd els != "/") panic("fwalk: relative path"); for(els = tl els; els != nil && f != NOQID; els = tl els) f = fwalk1(f, hd els); return f; } fwalk1(q: big, name: string): big { fh := findfile(q); if (fh == nil) return NOQID; if (name == "..") return fh.parentqid; for (fh = fh.child; fh != nil; fh = fh.sibling) if (fh.d.name == name) return fh.d.qid.path; return NOQID; } # detach f from parent. But keep on table. to move it around. fdetach(f: big) { fh := findfile(f); if (fh == nil) panic("fdetach: nil fh"); parent := findfile(fh.parentqid); if (parent == nil) panic("fdetach: nil parent"); _fdetach(fh, parent); } _fdetach(fh: ref Fholder, parent: ref Fholder) { prev: ref Fholder; if (parent != nil) { prev = nil; for (sfh := parent.child; sfh != nil; sfh = sfh.sibling) { if (sfh == fh) break; prev = sfh; } if (sfh == nil) panic("fdetach: child not found in parent"); if (prev == nil) parent.child = fh.sibling; else prev.sibling = fh.sibling; } fh.sibling = nil; fh.parentqid = NOQID; } fattach(d: big, f: big) { parent := findfile(d); if (parent == nil) panic("fattach: no parent"); fh := findfile(f); if (fh == nil) panic("fattach: no fh"); fh.parentqid = d; # Attach at the end, so new panels show up last if (parent.child == nil) parent.child = fh; else { for (sf := parent.child; sf.sibling != nil; sf = sf.sibling) ; sf.sibling = fh; fh.sibling = nil; } } fremove(q: big): string { prev: ref Fholder; # remove from hash table slot := hashfn(q, len fstab); for (fh := fstab[slot]; fh != nil; fh = fh.hash) { if (fh.d.qid.path == q) break; prev = fh; } if (fh == nil) return Enotfound; if (prev == nil) fstab[slot] = fh.hash; else prev.hash = fh.hash; fh.hash = nil; # remove from parent's children parent := findfile(fh.parentqid); _fdetach(fh, parent); # now remove any descendents sibling: ref Fholder; for (sfh := fh.child; sfh != nil; sfh = sibling) { sibling = sfh.sibling; sfh.parentqid = sfh.d.qid.path; # make sure it doesn't disrupt things. fremove(sfh.d.qid.path); } return nil; } fcreate(q: big, d: Sys->Dir): string { if (findfile(d.qid.path) != nil) return Eexists; # allow creation of a root directory only if its parent is itself parent := findfile(q); if (parent == nil && d.qid.path != q) return Enotfound; fh: ref Fholder; if (parent == nil) fh = ref Fholder(q, d, nil, nil, nil); else { if (fwalk1(q, d.name) != NOQID) return Eexists; fh = ref Fholder(parent.d.qid.path, d, nil, nil, nil); # Attach at the end, so new panels show up last if (parent.child == nil) parent.child = fh; else { for (sf := parent.child; sf.sibling != nil; sf = sf.sibling) ; sf.sibling = fh; } } slot := hashfn(d.qid.path, len fstab); fh.hash = fstab[slot]; fstab[slot] = fh; return nil; } tabs : con "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; dumptree(fh: ref Fholder, t: int): int { n := 1; q := fh.d.qid.path; fprint(stderr, "%s%08bx\t%s\n", tabs[0:t], q, fgetpath(q)); for (fh = fh.child; fh != nil; fh = fh.sibling) n += dumptree(fh, t+1); return n; } dumphash(): int { n:= 0; for (i := 0; i < len fstab; i++) for (fh := fstab[i]; fh != nil; fh = fh.hash){ n++; #q := fh.d.qid.path; # fprint(stderr, "%s%08bx\t%s\n", tabs[0:t], q, fgetpath(q)); } return n; } dump() { fh := findfile(slash); if (fh == nil) panic("file not in tree"); n := dumptree(fh, 0); m := dumphash(); fprint(stderr, "%d in tree %d in hash\n", n, m); } fwstat(q: big, d: Sys->Dir): string { fh := findfile(q); if (fh == nil) return Enotfound; d = applydir(d, fh.d); # We don't allow renames if (d.name != fh.d.name) return "renames not allowed"; fh.d = d; fh.d.qid.path = q; # ensure the qid can't be changed return nil; } applydir(d: Sys->Dir, onto: Sys->Dir): Sys->Dir { if (d.name != nil) onto.name = d.name; if (d.uid != nil) onto.uid = d.uid; if (d.gid != nil) onto.gid = d.gid; if (d.muid != nil) onto.muid = d.muid; if (d.qid.vers != ~0) onto.qid.vers = d.qid.vers; if (d.qid.qtype != ~0) onto.qid.qtype = d.qid.qtype; if (d.mode != ~0) onto.mode = d.mode; if (d.atime != ~0) onto.atime = d.atime; if (d.mtime != ~0) onto.mtime = d.mtime; if (d.length != ~big 0) onto.length = d.length; if (d.dtype != ~0) onto.dtype = d.dtype; if (d.dev != ~0) onto.dev = d.dev; return onto; } newdir(name: string, perm: int, qid: big): Dir { d := sys->zerodir; d.name = name; d.uid = user; d.gid = user; d.qid.path = qid; if (perm & DMDIR) d.qid.qtype = QTDIR; else d.qid.qtype = QTFILE; d.mode = perm; return d; }