var Qid = ["i1:type", "i4:ver", "i8:path"]; var QTDIR = 0x80; var debug9p = 0; var packets = { 100: {name: "Tversion", fmt: ["i4:size", "i1:type", "i2:tag", "i4:msize", "S2:version"], handler: Tversion}, 101: {name: "Rversion", fmt: ["i4:size", "i1:type", "i2:tag", "i4:msize", "S2:version"]}, 102: {name: "Tauth", fmt: ["i4:size", "i1:type", "i2:tag", "S2:uname", "S2:aname"], handler: Tauth}, 104: {name: "Tattach", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i4:afid", "S2:uname", "S2:aname"], handler: Tattach}, 105: {name: "Rattach", fmt: ["i4:size", "i1:type", "i2:tag", "b13:qid"]}, 107: {name: "Rerror", fmt: ["i4:size", "i1:type", "i2:tag", "S2:ename"]}, 108: {name: "Tflush", fmt: ["i4:size", "i1:type", "i2:tag", "i2:oldtag"], handler: Tflush}, 109: {name: "Rflush", fmt: ["i4:size", "i1:type", "i2:tag"]}, 110: {name: "Twalk", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i4:newfid", "i2:nwname", "R:wname"], handler: Twalk}, 111: {name: "Rwalk", fmt: ["i4:size", "i1:type", "i2:tag", "i2:nwqid", "R:qids"]}, 112: {name: "Topen", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i1:mode"], handler: Topen}, 113: {name: "Ropen", fmt: ["i4:size", "i1:type", "i2:tag", "b13:qid", "i4:iounit"]}, 114: {name: "Tcreate", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "S2:name", "i4:perm", "i1:mode"], handler: Tcreate}, 116: {name: "Tread", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i8:offset", "i4:count"], handler: Tread}, 117: {name: "Rread", fmt: ["i4:size", "i1:type", "i2:tag", "S4:data"]}, 118: {name: "Twrite", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i8:offset", "S4:data"], handler: Twrite}, 119: {name: "Rwrite", fmt: ["i4:size", "i1:type", "i2:tag", "i4:count"]}, 120: {name: "Tclunk", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"], handler: Tclunk}, 121: {name: "Rclunk", fmt: ["i4:size", "i1:type", "i2:tag"]}, 122: {name: "Tremove", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"], handler: Tremove}, 124: {name: "Tstat", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"], handler: Tstat}, 125: {name: "Rstat", fmt: ["i4:size", "i1:type", "i2:tag", "S2:stat"]}, 126: {name: "Twstat", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "S2:stat"], handler: Twstat} }; var fids = {}; var tree = {name: "/", qid: {type: QTDIR, ver: 0, path: 0}, children: {}, nchildren: []}; var tagflush = {}; var lastqid = 0; tree.parent = tree; var msgtype = {}; for(i in packets) msgtype[packets[i].name] = i; function got9praw() { var header, packet, t; if(cpubuf < 7) return -1; header = unpack(cpubuf, ["i4:size", "i1:type", "i2:tag"]); if(cpubuf.length < header.size) return -1; t = packets[header.type]; if(t == undefined) fatal("unknown message type " + header.type); packet = unpack(cpubuf.substring(0, header.size), t.fmt); cpubuf = cpubuf.substring(header.size); if(debug9p) print("<- " + JSON.stringify(packet) + "\n"); t.handler(packet); return 1; } function send9p(p) { p.size = 0; p.size = pack(p, packets[p.type].fmt).length; if(debug9p) print("-> " + JSON.stringify(p) + " " + btoa(pack(p, packets[p.type].fmt)) + "\n"); conn.send(btoa(pack(p, packets[p.type].fmt))); } function error9p(tag, s) { send9p({type: msgtype.Rerror, tag:tag, ename:s}) } function Tversion(p) { p.type = msgtype.Rversion; send9p(p); } function Tauth(p) { error9p(p.tag, "no auth necessary"); } function Tattach(p) { if(fids[p.fid] != undefined) return error9p(p.tag, "fid already in use"); fids[p.fid] = {f: tree, open: false}; send9p({type: msgtype.Rattach, tag: p.tag, qid: pack(tree.qid, Qid)}); } function Twalk(p) { var f, i, s, n, qids; if(fids[p.fid] == undefined) return error9p(p.tag, "fid not in use"); if(fids[p.newfid] != undefined) return error9p(p.tag, "fid already in use"); f = fids[p.fid].f; s = p.wname; qids = ""; for(i = 0; i < p.nwname; qids += pack(f.qid, Qid), i++){ n = unpack(s, ["S2:p"]).p; s = s.substring(2 + n.length); if(n == ".") continue; if(n == ".."){ f = f.parent; continue; } if((f.qid.type & QTDIR) == 0){ if(i == 0) return error9p(p.tag, "not a directory"); return send9p({type: msgtype.Rwalk, tag: p.tag, nwqid: i, qids: qids}); } if(f.children[n] == undefined){ if(i == 0) return error9p(p.tag, "no such file or directory"); return send9p({type: msgtype.Rwalk, tag: p.tag, nwqid: i, qids: qids}); } f = f.children[n]; } fids[p.newfid] = {f: f, open: false}; send9p({type: msgtype.Rwalk, tag: p.tag, nwqid: i, qids: qids}); } function Tclunk(p) { if(fids[p.fid].open == true && fids[p.fid].f.close != undefined) fids[p.fid].f.close(fids[p.fid]); fids[p.fid] = undefined; send9p({type: msgtype.Rclunk, tag: p.tag}); } function Topen(p) { var f, s; if(fids[p.fid] == undefined) return error9p(p.tag, "no such fid"); f = fids[p.fid].f; fids[p.fid].mode = p.mode; if(f.open != undefined){ s = f.open(fids[p.fid]); if(s != undefined && s != "") return error9p(p.tag, s); } if(fids[p.fid].f.qid.type & QTDIR){ if((p.mode & 3) != 0) return error9p(p.tag, "permission denied"); fids[p.fid].bloc = 0; fids[p.fid].nloc = 0; fids[p.fid].open = true; return send9p({type: msgtype.Ropen, tag: p.tag, qid: pack(f.qid, Qid), iounit: 0x10000}) } switch(p.mode & 3){ case 2: if(f.write == undefined) return error9p(p.tag, "permission denied"); case 0: if(f.read == undefined) return error9p(p.tag, "permission denied"); break; case 1: if(f.write == undefined) return error9p(p.tag, "permission denied"); break; case 3: return error9p(p.tag, "permission denied"); } fids[p.fid].open = true; send9p({type: msgtype.Ropen, tag: p.tag, qid: pack(f.qid, Qid), iounit: 0x10000}) } function Tcreate(p) { error9p(p.tag, "create prohibited"); } function Tremove(p) { if(fids[p.fid].open == true && fids[p.fid].f.close != undefined) fids[p.fid].f.close(fids[p.fid]); fids[p.fid] = undefined; error9p(p.tag, "remove prohibited"); } function Twstat(p) { error9p(p.tag, "wstat prohibited"); } function invalidop(f, p) { error9p(p.tag, "no."); } function Tread(p) { var f, s; f = fids[p.fid]; if(f == undefined) return error9p(p.tag, "no such fid"); if(f.open != true) return error9p(p.tag, "fid not open"); if(f.f.qid.type & QTDIR){ if(p.offset == 0) f.bloc = f.nloc = 0; if(p.offset != f.bloc) return error9p(p.tag, "seek in directory illegal"); if(p.count < 2) return error9p(p.tag, "read too short"); if(f.nloc >= f.f.nchildren.length) return send9p({type: msgtype.Rread, tag: p.tag, data: ""}) s = dirent(f.f.nchildren[f.nloc]); if(p.count < s.length) return send9p({type: msgtype.Rread, tag: p.tag, data: s.substring(0, 2)}); f.bloc += s.length; f.nloc++; return send9p({type: msgtype.Rread, tag: p.tag, data: s}); } if(f.f.read == undefined) return error9p(p.tag, "permission denied"); f.f.read(f, p) } function Twrite(p) { if(fids[p.fid] == undefined) return error9p(p.tag, "no such fid"); if(fids[p.fid].open != true) return error9p(p.tag, "fid not open"); if(fids[p.fid].f.write == undefined) return error9p(p.tag, "permission denied"); fids[p.fid].f.write(fids[p.fid], p) } function dirent(f) { s = {"type":0, "dev":0, "qid": pack(f.qid, Qid), "mode": 0, "atime":0, "mtime":0, "length":0, "name":f.name, "uid":"js", "gid":"js", "muid":"js"}; s.atime = s.mtime = new Date().getTime() / 1000; if(f.qid.type & QTDIR) s.mode |= 0111; if(f.write) s.mode |= 0222; if(f.read) s.mode |= 0444; if(f.qid.type & QTDIR) s.mode |= 0x80000000; s = pack(s, ["i2:type", "i4:dev", "b13:qid", "i4:mode", "i4:atime", "i4:mtime", "i8:length", "S2:name", "S2:uid", "S2:gid", "S2:muid"]); return pack({a:s.length}, ["i2:a"]) + s; } function Tstat(p) { var s, f; if(fids[p.fid] == undefined) return error9p(p.tag, "no such fid"); f = fids[p.fid].f; s = dirent(f); send9p({type: msgtype.Rstat, tag: p.tag, stat: s}); } function Tflush(p) { if(tagflush[p.oldtag] != undefined){ tagflush[p.oldtag](p.oldtag); tagflush[p.oldtag] = undefined; } error9p(p.oldtag, "interrupted") send9p({type: msgtype.Rflush, tag: p.tag}) } function lookupfile(path, last) { var f, s, n, x; f = tree; s = path.split('/'); for(x in s){ if(!last && x == s.length - 1) break; n = s[x]; if(n == "" || n == ".") continue; if(n == "..") f = f.parent; else { f = f.children[n]; if(f == undefined) throw path + " not found"; } } return f; } function mkdir(path) { var f, n; try { f = lookupfile(path, 1); f.children = {}; f.nchildren = []; } catch { f = lookupfile(path, 0); path = path.split('/'); n = path[path.length - 1]; f.children[n] = {name: n, parent: f, children: {}, nchildren: [], qid: {type: QTDIR, ver: 0, path: ++lastqid}}; f.nchildren.push(f.children[n]); } } function mkfile(path, open, read, write, close) { var f, n; try { f = lookupfile(path, 1); rmfile(path); } catch(err) { } f = lookupfile(path, 0); path = path.split('/'); n = path[path.length - 1]; f.children[n] = {name: n, parent: f, qid: {type: 0, ver: 0, path: ++lastqid}, open: open, read: read, write: write, close: close}; f.nchildren.push(f.children[n]); } function rmfile(path) { var f, n; f = lookupfile(path, 0); path = path.split('/'); n = path[path.length - 1]; if (n == '*') { f.children = {}; f.nchildren = []; return; } f.children[n] = undefined; for (var i = 0; i < f.nchildren.length; i++) { if (f.nchildren[i].name == n) { f.nchildren.splice(i, 1); } } } function respond(p, n) { tagflush[p.tag] = undefined; switch(p.type){ case 116: send9p({type: msgtype.Rread, tag: p.tag, data: n}); break; case 118: if(n < 0) n = p.data.length; send9p({type: msgtype.Rwrite, tag: p.tag, count: n}); break; default: throw "respond does not handle " + p.type; } } function readstr(p, n) { return send9p({type: msgtype.Rread, tag: p.tag, data: n.substr(p.offset, p.count)}) } function onflush(t, f) { tagflush[t] = f; } mkdir("/dev"); mkfile("/dev/cons", undefined, function(f, p) { document.getElementById('terminal').terminal.readterminal(p.count, function(l) {respond(p, l);}, p.tag); }, function(f, p) { document.getElementById('terminal').terminal.writeterminal(p.data); respond(p, -1); }); mkfile("/dev/consctl", undefined, invalidop, function(f, p) { if(p.data.substr(0, 5) == "rawon") rawmode = true; if(p.data.substr(0, 5) == "rawoff") rawmode = false; respond(p, -1); }, function(f) { rawmode = false; }); mkfile("/dev/cpunote", undefined, function(f, p) { document.getElementById('terminal').terminal.onnote.push(function(l) { respond(p, l);}); }); //mkfile("/dev/js", function(f, p){ f.text = ""; }, undefined, function(f, p) { f.text += p.data; respond(p, -1); }, function(f, p) { eval(f.text); });