@include @const NOTAG = (uint16)~0; @const NOFID = (uint32)~0; @const BIT8SZ = 1; @const BIT16SZ = 2; @const BIT32SZ = 4; @const BIT64SZ = 8; @const QIDSZ = BIT8SZ+BIT32SZ+BIT64SZ; @const MAXWELEM = 16; @const STATMAX = 65535U; @const IOHDRSZ = 24; /* ample room for Twrite/Rread header (iounit) */ @const CNBITS = 12; @const CMASK = ((1< MAXWELEM) error("twalk: too many names"); sz = 4+1+2+4+4+2; for(i = 0; i < nwname; i++) sz += 2+length(wname[i]); s = mkstr(sz); p = charstar(ns9p, s); p = push4(p, sz); p = push1(p, dom9p`Twalk); p = push2(p, tag); p = push4(p, fid); p = push4(p, newfid); p = push2(p, nwname); for(i = 0; i < nwname; i++) p = pushn2(p, wname[i]); return s; } @define topen(tag, fid, mode) { @local sz, s, p; // size[4] Topen tag[2] fid[4] mode[1] sz = 4+1+2+4+1; s = mkstr(sz); p = charstar(ns9p, s); p = push4(p, sz); p = push1(p, dom9p`Topen); p = push2(p, tag); p = push4(p, fid); p = push1(p, mode); return s; } @define tcreate(tag, fid, name, perm, mode) { @local sz, s, p; // size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1] sz = 4+1+2+4+lenn2(name)+4+1; s = mkstr(sz); p = charstar(ns9p, s); p = push4(p, sz); p = push1(p, dom9p`Tcreate); p = push2(p, tag); p = push4(p, fid); p = pushn2(p, name); p = push4(p, perm); p = push1(p, mode); return s; } @define tremove(tag, fid) { @local sz, s, p; // size[4] Tremove tag[2] fid[4] sz = 4+1+2+4; s = mkstr(sz); p = charstar(ns9p, s); p = push4(p, sz); p = push1(p, dom9p`Tremove); p = push2(p, tag); p = push4(p, fid); return s; } @define tread(tag, fid, offset, count) { @local sz, s, p; // size[4] Tread tag[2] fid[4] offset[8] count[4] sz = 4+1+2+4+8+4; s = mkstr(sz); p = charstar(ns9p, s); p = push4(p, sz); p = push1(p, dom9p`Tread); p = push2(p, tag); p = push4(p, fid); p = push8(p, offset); p = push4(p, count); return s; } @define twrite(tag, fid, offset, data) { @local sz, s, p; // size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] sz = 4+1+2+4+8+lenn4(data); s = mkstr(sz); p = charstar(ns9p, s); p = push4(p, sz); p = push1(p, dom9p`Tread); p = push2(p, tag); p = push4(p, fid); p = push8(p, offset); p = pushn4(p, data); return s; } @define tflush(tag, oldtag) { @local sz, s, p; // size[4] Tflush tag[2] oldtag[2] sz = 4+1+2+2; s = mkstr(sz); p = charstar(ns9p, s); p = push4(p, sz); p = push1(p, dom9p`Tflush); p = push2(p, tag); p = push2(p, oldtag); return s; } @define fmtqid(qid) { return sprintfa("(%016x %u %x)", qid->path, qid->vers, qid->type); } @define rversion(sz, p) { @local tag, msize, len, version; // (size[4]) Rversion tag[2] msize[4] version[s] if(sz <= 1+2+4+2) error("Rversion is too short"); p += 1; // Rversion tag = *(uint16*)p; p += 2; msize = *(uint32*)p; p += 4; len = *(uint16*)p; p += 2; if(sz != 1+2+4+2+len) error("bad Rversion"); version = getbytes(p, len); if(0)printf("Rversion: tag=%u msize=%u version=%s\n", tag, msize, version); return [ msize, version ]; } @define rattach(sz, p) { @local tag, qid; // (size[4]) Rattach tag[2] qid[13] if(sz != 1+2+13) error("bad Rattach"); p += 1; // Rattach tag = *(uint16*)p; p += 2; qid = (Qid*)p; if(0)printf("Rattach: tag=%u qid=%s\n", tag, fmtqid(qid)); return [ qid ]; } @define convM2D(p) { @local s, q, r, slen, sz; sz = *(uint16*)p; p += 2; s = mkstr(sz+4*4+4); // four field ptrs, four NULL bytes in fields q = charstar(ns9p, s); memcpy(q, p, 0x27); // offsetof(Dir, name) p += 0x27; q = (Dir*)q; r = (char*)(q+1); slen = *(uint16*)p; p += 2; q->name = r; memcpy(r, p, slen); p += slen; r += slen; *r++ = 0; slen = *(uint16*)p; p += 2; q->uid = r; memcpy(r, p, slen); p += slen; r += slen; *r++ = 0; slen = *(uint16*)p; p += 2; q->gid = r; memcpy(r, p, slen); p += slen; r += slen; *r++ = 0; slen = *(uint16*)p; p += 2; q->muid = r; memcpy(r, p, slen); p += slen; r += slen; *r++ = 0; return q; } @define dumpstat(stat) { printf("\t name=%s len=%u mode=%o at=%u mt=%u " "uid=%s gid=%s muid=%s\n", stat->name, stat->length, stat->mode, stat->atime, stat->mtime, stat->uid, stat->gid, stat->muid); } @define rstat(sz, p) { @local tag, stat, len; // (size[4]) Rstat tag[2] stat[n] if(sz <= 1+2+2) error("Rstat is too short"); p += 1; // Rstat tag = *(uint16*)p; p += 2; len = *(uint16*)p; p += 2; if(sz != 1+2+2+len) error("bad Rstat"); stat = convM2D(p); if(0)printf("Rstat tag=%u\n", tag); // dumpstat(stat); return [ stat ]; } @define rwstat(sz, p) { @local tag; // (size[4]) Rwstat tag[2] if(sz != 1+2) error("bad Rwstat"); p += 1; // Rwstat tag = *(uint16*)p; if(0)printf("Rwstat: tag=%u\n", tag); return []; } @define rclunk(sz, p) { @local tag; // (size[4]) Rclunk tag[2] if(sz != 1+2) error("bad Rclunk"); p += 1; // Rclunk tag = *(uint16*)p; if(0)printf("Rclunk: tag=%u\n", tag); return []; } @define rflush(sz, p) { @local tag; // (size[4]) Rflush tag[2] if(sz != 1+2) error("bad Rflush"); p += 1; // Rflush tag = *(uint16*)p; if(0)printf("Rflush: tag=%u\n", tag); return []; } @define rwalk(sz, p) { @local tag, nwqid, qid, i, rv; // (size[4]) Rwalk tag[2] nwqid[2] nwqid*(qid[13]) if(sz < 1+2+2) error("Rwalk is too short"); p += 1; // Rwalk tag = *(uint16*)p; p += 2; nwqid = *(uint16*)p; p += 2; if(sz != 1+2+2+nwqid*13) error("bad Rwalk"); if(0)printf("Rwalk tag=%u nwqid=%u\n", tag, nwqid); qid = (Qid*)p; rv = []; for(i = 0; i < nwqid; i++){ // printf("\tqid[%u]=%s\n", i, fmtqid(qid)); append(rv, qid++); } return rv; } @define ropen(sz, p) { @local tag, qid, iounit; // (size[4]) Ropen tag[2] qid[13] iounit[4] if(sz != 1+2+13+4) error("bad Ropen"); p += 1; // Ropen tag = *(uint16*)p; p += 2; qid = (Qid*)p; p += sizeof(*qid); iounit = *(uint32*)p; if(0)printf("Ropen tag=%u qid=%s iounit=%u\n", tag, fmtqid(qid), iounit); return [ qid, iounit ]; } @define rcreate(sz, p) { @local tag, qid, iounit; // (size[4]) Rcreate tag[2] qid[13] iounit[4] if(sz != 1+2+13+4) error("bad Rcreate"); p += 1; // Rcreate tag = *(uint16*)p; p += 2; qid = (Qid*)p; p += sizeof(*qid); iounit = *(uint32*)p; if(0)printf("Rcreate tag=%u qid=%s iounit=%u\n", tag, fmtqid(qid), iounit); return [ qid, iounit ]; } @define rremove(sz, p) { @local tag; // (size[4]) Rremove tag[2] if(sz != 1+2) error("bad Rremove"); p += 1; // Rremove tag = *(uint16*)p; if(0)printf("Rremove: tag=%u\n", tag); return []; } @define rread(sz, p) { @local tag, count, data; // (size[4]) Rread tag[2] count[4] data[count] if(sz < 1+2+4) error("Rread is too short"); p += 1; // Rread tag = *(uint16*)p; p += 2; count = *(uint32*)p; p += 4; if(sz != 1+2+4+count) error("bad Rread"); data = getbytes(p, count); if(0)printf("Rread: tag=%u count=%u data='%B'\n", tag, count, data); return [ data ]; } @define rwrite(sz, p) { @local tag, count; // (size[4]) Rwrite tag[2] count[4] if(sz != 1+2+4) error("Rwrite is too short"); p += 1; // Rwrite tag = *(uint16*)p; p += 2; count = *(uint32*)p; p += 4; if(0)printf("Rwrite: tag=%u count=%u\n", tag, count); return [ count ]; } @define rerror(sz, p) { @local tag, len, ename; // (size[4]) Rerror tag[2] ename[s] if(sz <= 1+2+2) error("Rerror is too short"); p += 1; // Rerror tag = *(uint16*)p; p += 2; len = *(uint16*)p; p += 2; if(sz != 1+2+2+len) error("bad Rerror"); ename = getbytes(p, len); if(0)printf("Rerror: tag=%u ename=\"%s\"\n", tag, ename); return ename; } @define do9p(fd, t) { @local r, p, sz, op; p = charstar(ns9p, t); op = *(uint8*)(p+4); write(fd, t); r = read9pmsg(fd); if(r == nil) error("remote 9P hung up"); sz = length(r); p = charstar(ns9p, r); if(*p == dom9p`Rerror) return rerror(sz, p); if(*p != op+1) error("remote 9p returned %e (expected %e)", (enum dom9p`P9Pmsg)*p, (enum dom9p`P9Pmsg)(op+1)); switch(*p){ case dom9p`Rread: return rread(sz, p); case dom9p`Rwrite: return rwrite(sz, p); case dom9p`Rwalk: return rwalk(sz, p); case dom9p`Ropen: return ropen(sz, p); case dom9p`Rclunk: return rclunk(sz, p); case dom9p`Rstat: return rstat(sz, p); case dom9p`Rversion: return rversion(sz, p); case dom9p`Rauth: return rversion(sz, p); case dom9p`Rattach: return rattach(sz, p); case dom9p`Rflush: return rflush(sz, p); case dom9p`Rcreate: return rcreate(sz, p); case dom9p`Rremove: return rremove(sz, p); case dom9p`Rwstat: return rwstat(sz, p); } } @define session9p(fd) { @local m, p, sz; write(fd, tversion(NOTAG, 8192, "9P2000.u")); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rversion) rversion(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define attach9p(fd, uname) { @local m, p, sz; write(fd, tattach(0, 0, NOFID, uname, "")); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rattach) rattach(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define stat9p(fd, fid) { @local m, p, sz; write(fd, tstat(0, fid)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rstat) rstat(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define path2walk(path) { @local l, i, m, s, w; w = split(path, "/"); l = []; m = length(w); for(i = 0; i < m; i++){ s = w[i]; if(s == "") continue; if(s == ".") continue; append(l, s); } return l; } @define walk9p(fd, fid, newfid, path) { @local m, p, sz, wname; wname = path2walk(path); write(fd, twalk(0, fid, newfid, wname)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rwalk) rwalk(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define open9p(fd, fid, mode) { @local m, p, sz; write(fd, topen(0, fid, mode)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Ropen) ropen(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define create9p(fd, fid, name, perm, mode) { @local m, p, sz; write(fd, tcreate(0, fid, name, perm, mode)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rcreate) rcreate(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define remove9p(fd, fid) { @local m, p, sz; write(fd, tremove(0, fid)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rremove) rremove(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define read9p(fd, fid, offset, count) { @local m, p, sz; write(fd, tread(0, fid, offset, count)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rread) rread(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define write9p(fd, fid, offset, data) { @local m, p, sz; write(fd, twrite(0, fid, offset, data)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rwrite) rwrite(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define clunk9p(fd, fid) { @local m, p, sz; write(fd, tclunk(0, fid)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rclunk) rclunk(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @define flush9p(fd, oldtag) { @local m, p, sz; write(fd, tflush(0, oldtag)); m = read9pmsg(fd); if(m == nil) error("unexpected end-of-file"); sz = length(m); p = charstar(ns9p, m); if(*p == dom9p`Rflush) rflush(sz, p); else if(*p == dom9p`Rerror) rerror(sz, p); else error("i got %e", (enum dom9p`P9Pmsg)*p); } @record mnt9p { open, close, read, write, stat, fstat, dirread, mmap }; @record openfile { fid, path, off, qid, iounit }; @const maxfd = 128; @define mount9p(fd) { @local msize; @local rfid; @local allocfd, doread; @local xopen, xclose, xread, xstat, xfstat, xdirread; @local nextfid, rv, tag, ftab, mnt; tag = 0; ftab = mklist(maxfd); rv = do9p(fd, tversion(NOTAG, 8192, "9P2000.u")); if(isstring(rv)) error(rv); msize = rv[0]; rfid = 0; rv = do9p(fd, tattach(tag, rfid, NOFID, getenv("USER"), "")); if(isstring(rv)) error(rv); nextfid = rfid+1; @define allocfd() { @local i; for(i = 0; i < maxfd; i++) if(ftab[i] == nil) return i; error("out of file descriptors"); } @define xopen(path, mode) { @local fid, rv, xfd, wname; fid = nextfid++; wname = path2walk(path); rv = do9p(fd, twalk(tag, rfid, fid, wname)); if(isstring(rv)) error(rv); if(length(wname) != length(rv)) error(sprintfa("cannot walk to %s", wname[length(rv)])); rv = do9p(fd, topen(tag, fid, mode)); if(isstring(rv)) error(rv); xfd = allocfd(); ftab[xfd] = openfile(fid, path, 0, rv[0], rv[1]); return xfd; } @define xfstat(xfd) { @local rv; if(ftab[xfd] == nil) return nil; rv = do9p(fd, tstat(tag, ftab[xfd].fid)); if(isstring(rv)) error(rv); return rv[0]; } @define xstat(path) { @local fid, rv, wname; fid = nextfid++; wname = path2walk(path); rv = do9p(fd, twalk(tag, rfid, fid, wname)); if(isstring(rv)) error(rv); if(length(wname) != length(rv)) error(sprintfa("cannot walk to %s", wname[length(rv)])); rv = do9p(fd, tstat(tag, fid)); do9p(fd, tclunk(tag, fid)); if(isstring(rv)) error(rv); return rv[0]; } @define xclose(xfd) { @local rv; if(ftab[xfd] == nil) return nil; rv = do9p(fd, tclunk(tag, ftab[xfd].fid)); if(isstring(rv)) error(rv); ftab[xfd] = nil; return nil; } @define doread(fid, len, off) { @local rv; rv = do9p(fd, tread(tag, fid, off, len)); if(isstring(rv)) error(rv); return rv[0]; } @define xread(xfd, len) { @local of, rv; of = ftab[xfd]; if(of == nil) error("bad file descriptor: %a", xfd); if(of.iounit && len > of.iounit) error("read exceeds iounit"); rv = doread(of.fid, len, of.off); of.off += length(rv); return rv; } // @define xpread(xfd, len, off) // { // @local of, rv; // of = ftab[xfd]; // if(of == nil) // error("bad file descriptor: %a", xfd); // if(of.iounit && len > of.iounit) // error("read exceeds iounit"); // rv = doread(of.fid, len, off); // of.off = off+length(rv); // return rv; // } @defloc xmmap(xfd) { @local of, unit, st, cache, get, put, map, ma, ml; of = ftab[xfd]; if(of == nil) error("bad file descriptor: %a", xfd); unit = 4096; if(of.iounit && of.iounit < unit) error("unit too small"); /* not really */ cache = mktab(); st = xfstat(xfd); ma = st->length>>CNBITS; ml = st->length&CMASK; @define get(this, r) { @local b, l, a; b = rangebeg(r); l = rangelen(r); a = b>>CNBITS; if(a > ma){ if(a > ma+1 || ml == 0) error("access out of bounds"); if((b&CMASK)+l > ml) error("access out of bounds"); if(cache[a] == nil) cache[a] = doread(of.fid, ml, a<>CNBITS > a) /* spans block boundary; read directly */ return doread(of.fid, l, b); if(cache[a] == nil) cache[a] = doread(of.fid, unit, a<length)); } @define ismapped(this, r) { return isrinr(r, map(this)); } return mkas([ "get" : get, "put" : put, "map" : map, "ismapped" : ismapped ]); } @define xdirread(xfd) { @local of, rv, unit, buf, p, len, ss, sz; of = ftab[xfd]; if(of == nil) error("bad file descriptor: %a", xfd); unit = msize-IOHDRSZ; if(of.iounit && of.iounit < unit) unit = of.iounit; buf = ""; while(1){ rv = do9p(fd, tread(tag, of.fid, of.off, unit)); if(isstring(rv)) error(rv); rv = rv[0]; of.off += length(rv); if(length(rv) == 0) break; buf += rv; } len = length(buf); buf = charstar(ns9p, buf); p = buf; ss = []; while(p < buf+len){ sz = *(uint16*)p; append(ss, convM2D(p)); p += 2+sz; } return ss; } mnt = mnt9p(xopen, xclose, xread, nil, xstat, xfstat, xdirread, xmmap); // finalize(mnt, lambda(mnt) { close(fd); }); return mnt; } @define test() { @local fd, fds; fds = popen(getenv("HOME")+"/src/u9fs/u9fs", "-D", "-a", "none", "-n", "-u", getenv("USER"), 8); // 8==FullDuplex on fd 0 fd = fds[0]; session9p(fd); attach9p(fd, getenv("USER")); stat9p(fd, 0); walk9p(fd, 0, 1, "/etc/passwd"); stat9p(fd, 1); open9p(fd, 1, dom9p`OREAD); read9p(fd, 1, 0, 128); clunk9p(fd, 1); walk9p(fd, 0, 2, "/etc"); stat9p(fd, 2); open9p(fd, 2, dom9p`OREAD); read9p(fd, 2, 0, 128); clunk9p(fd, 2); clunk9p(fd, 0); close(fd); } @define test2() { @local fds, mnt, fd, st, p, as, s, i; fds = popen(getenv("HOME")+"/src/u9fs/u9fs", "-D", "-a", "none", "-n", "-u", getenv("USER"), 8); // 8==FullDuplex on fd 0 mnt = mount9p(fds[0]); fd = mnt.open("/etc/passwd", dom9p`OREAD); printf("%s\n", mnt.read(fd, 100)); mnt.close(fd); fd = mnt.open("/etc", dom9p`OREAD); // map(dumpstat, mnt.dirread(fd)); map(@lambda(st) { printf("\t%s\n", st->name); }, mnt.dirread(fd)); mnt.close(fd); dumpstat(mnt.stat(getenv("HOME"))); fd = mnt.open(getenv("HOME"), dom9p`OREAD); dumpstat(mnt.fstat(fd)); dumpstat(mnt.fstat(fd)); fd = mnt.open("/etc/bash_completion", dom9p`OREAD); as = mnt.mmap(fd); p = (unsigned char*){mkdom(c32le, as)}0; st = mnt.fstat(fd); printf("dumping file\n"); s = mkstr(st->length); for(i = 0; i < st->length; i++){ s[i] = *p++; // printf("%c", *p++); } mnt.close(fd); fd = open("/tmp/out", "w+"); write(fd, s); close(fd); }