# HG changeset patch # User Francisco J Ballesteros # Date 1316278017 -7200 # Node ID dd7abca1a2520b405af70ad58902f0a5dc7084e4 # Parent 2116c13e63f53f6c8036d3da390929c3f7fe8832 ix: experimental, ongoing work for a long distance protocol. This adds to the main tree what I have regarding IX. There's a server (ix), a test client (ixget) and most of a client (ixc). Files GET,PUT, and SRV.out can give you an idea of the protocol. In short, there's general purpose code (not just IX specific) to multiplex a TCP (or similar) stream into channels. upon that, the rest of the code relies on creating channels and sending messages through them. Messages are built similar to those in the nix zio framework, because I expect them to rely on those in the future. Now, the server exports its own namespace speaking IX as a server. The client (ixc, not the testing client) speaks IX as a client and maintains a local on disk cache; it also speaks 9p as a server. You get the picture. This is just an experiment. It's not ready for use AT ALL. I'm opening it in the hope it would be useful for nix. R=nix-dev, nemo, noah.evans, rminnich, john CC=nix-dev http://codereview.appspot.com/5038043 diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/GET.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/GET.out Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,40 @@ +dial tcp!localhost!9999 +session[24fd8]: new tcp!localhost!9999 +rproc[24fd8]: started +wproc[24fd8]: started +cmuxproc[4b738]: starting +cmuxproc[4b738]: new ch0 0x4e720 +chwproc[4e720]: started +-ch0-> Tversion msize 8192 version 'ix' +|ch0-> Tattach afid -1 uname nemo aname main +chwproc[4e720]: exiting +<-ch0- Rversion msize 8190 version 'ix' +msize 8190 +cmuxproc[4b738]:2: del ch0 +<-ch0| Rattach fid 0 qid (0000000000000054 692693930 d) +cmuxproc[4b738]: new ch0 0x51710 +chwproc[51710]: started +-ch0-> Tfid fid 0 cflags 0 +-ch0-> Tclone cflags 3 +-ch0-> Twalk wname acme.dump +-ch0-> Tstat +-ch0-> Topen mode 0 +|ch0-> Tread nmsg -1 offset 0 count 8190 +<-ch0- Rfid +chwproc[51710]: exiting +<-ch0- Rclone newfid 1 +<-ch0- Rwalk wqid (0000000000efd4d2 100 ) +<-ch0- Rstat stat 'acme.dump' 'nemo' 'nemo' 'nemo' q (0000000000efd4d2 100 ) m 0600 at 1313355326 mt 1310326405 l 33686 t 77 d 13 +<-ch0- Ropen qid (0000000000efd4d2 100 ) iounit 0 +<-ch0- Rread count 8185 '/usr/nemo /lib/font/bit/inferno/charon/plain.normal.font /lib/fo' +<-ch0- Rread count 8185 ' rm -f *.[5687qv] *.a[5687qv] y.tab.? lex.yy.c y.debug y.output ' +<-ch0- Rread count 8185 '57.6 tcp.6 ioapic.6 devsrv.6 ipaux.6 devenv.6 devdup.6 devpipe.6' +<-ch0- Rread count 8185 ' $names 6c -FTVw -I. -D'_DBGC_=''p''' ../port/page.c r - printst' +<-ch0- Rread count 946 ' ethermii.6 etherigbe.6 devproc.6 devkprof.6 netlog.6 nullmedium' +<-ch0| Rread count 0 '' +cmuxproc[4b738]:2: del ch0 +closemux 4b738 +abortconn[24fd8]: hanging up +wproc[24fd8]: exiting +cmuxproc[4b738]: done +rproc[24fd8]: exiting diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/PUT.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/PUT.out Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,41 @@ +dial tcp!localhost!9999 +session[24fd8]: new tcp!localhost!9999 +rproc[24fd8]: started +wproc[24fd8]: started +cmuxproc[4b738]: starting +cmuxproc[4b738]: new ch0 0x4e720 +chwproc[4e720]: started +-ch0-> Tversion msize 8192 version 'ix' +|ch0-> Tattach afid -1 uname nemo aname main +chwproc[4e720]: exiting +<-ch0- Rversion msize 8190 version 'ix' +msize 8190 +cmuxproc[4b738]:2: del ch0 +<-ch0| Rattach fid 0 qid (0000000000000054 692693930 d) +cmuxproc[4b738]: new ch0 0x517d0 +chwproc[517d0]: started +-ch0-> Tfid fid 0 cflags 0 +-ch0-> Tclone cflags 3 +-ch0-> Tcreate name adump perm %M% mode 384 +<-ch0- Rfid +<-ch0- Rclone newfid 1 +<-ch0- Rcreate qid (00000000026b920f 0 ) iounit 0 +-ch0-> Twrite offset 0 count 8177 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000' +-ch0-> Twrite offset 8177 count 8177 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000' +-ch0-> Twrite offset 16354 count 8177 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000' +-ch0-> Twrite offset 24531 count 8177 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000' +-ch0-> Twrite offset 32708 count 978 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0b000080' +closech ch0 +chwproc[517d0]: ch0: nil msg +<-ch0- Rwrite count 8177 +<-ch0- Rwrite count 8177 +<-ch0- Rwrite count 8177 +<-ch0- Rwrite count 8177 +<-ch0- Rwrite count 978 +cmuxproc[4b738]:1: del ch0 +closemux 4b738 +chwproc[517d0]: exiting +abortconn[24fd8]: hanging up +wproc[24fd8]: exiting +cmuxproc[4b738]: done +rproc[24fd8]: exiting diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/SRV.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/SRV.out Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,69 @@ +announce tcp!*!9999 +session[2aa50]: new tcp!*!9999 +fssrvproc: 2cbe8 tcp!*!9999 +fsrpc[2aa50!0]: new chan tcp!*!9999 +<-ch0- Tversion msize 8192 version 'ix' +-ch0-> Rversion msize 8190 version 'ix' +<-ch0| Tattach afid -1 uname nemo aname main +|ch0-> Rattach fid 0 qid (0000000000000054 692693930 d) +fsrpc[2aa50!0]: done +cmuxproc[537e8]:1: del ch0 +fsrpc[2aa50!0]: new chan tcp!*!9999 +<-ch0- Tfid fid 0 cflags 0 +-ch0-> Rfid +<-ch0- Tclone cflags 3 +-ch0-> Rclone newfid 1 +<-ch0- Twalk wname acme.dump +-ch0-> Rwalk wqid (0000000000efd4d2 100 ) +<-ch0- Tstat +-ch0-> Rstat stat 'acme.dump' 'nemo' 'nemo' 'nemo' q (0000000000efd4d2 100 ) m 0600 at 1313355326 mt 1310326405 l 33686 t 77 d 13 +<-ch0- Topen mode 0 +-ch0-> Ropen qid (0000000000efd4d2 100 ) iounit 0 +<-ch0| Tread nmsg -1 offset 0 count 8190 +-ch0-> Rread count 8185 '/usr/nemo /lib/font/bit/inferno/charon/plain.normal.font /lib/fo' +-ch0-> Rread count 8185 ' rm -f *.[5687qv] *.a[5687qv] y.tab.? lex.yy.c y.debug y.output ' +-ch0-> Rread count 8185 '57.6 tcp.6 ioapic.6 devsrv.6 ipaux.6 devenv.6 devdup.6 devpipe.6' +-ch0-> Rread count 8185 ' $names 6c -FTVw -I. -D'_DBGC_=''p''' ../port/page.c r - printst' +-ch0-> Rread count 946 ' ethermii.6 etherigbe.6 devproc.6 devkprof.6 netlog.6 nullmedium' +|ch0-> Rread count 0 '' +cmuxproc[537e8]:1: del ch0 +fsrpc[2aa50!0]: done +cmuxproc[537e8]: eof +closemux 537e8 +abortconn[2aa50]: hanging up +fssrvproc[2cbe8]: done +cmuxproc[537e8]: done +session[2aaf0]: new tcp!*!9999 +fssrvproc: 2cb48 tcp!*!9999 +fsrpc[2aaf0!0]: new chan tcp!*!9999 +<-ch0- Tversion msize 8192 version 'ix' +-ch0-> Rversion msize 8190 version 'ix' +<-ch0| Tattach afid -1 uname nemo aname main +|ch0-> Rattach fid 0 qid (0000000000000054 692693930 d) +fsrpc[2aaf0!0]: done +cmuxproc[5b130]:1: del ch0 +fsrpc[2aaf0!0]: new chan tcp!*!9999 +<-ch0- Tfid fid 0 cflags 0 +-ch0-> Rfid +<-ch0- Tclone cflags 3 +-ch0-> Rclone newfid 1 +<-ch0- Tcreate name adump perm --rw------- mode 1 +-ch0-> Rcreate qid (00000000026b920f 0 ) iounit 0 +<-ch0- Twrite offset 0 count 8177 '/usr/nemo /lib/font/bit/inferno/charon/plain.normal.font /lib/fo' +-ch0-> Rwrite count 8177 +<-ch0- Twrite offset 8177 count 8177 'EANFILES rm -f *.[5687qv] *.a[5687qv] y.tab.? lex.yy.c y.debug y' +-ch0-> Rwrite count 8177 +<-ch0- Twrite offset 16354 count 8177 'edium.6 ether82557.6 tcp.6 ioapic.6 devsrv.6 ipaux.6 devenv.6 de' +-ch0-> Rwrite count 8177 +<-ch0- Twrite offset 24531 count 8177 'oot/libboot.a6 $names rm $names 6c -FTVw -I. -D'_DBGC_=''p''' ..' +-ch0-> Rwrite count 8177 +<-ch0- Twrite offset 32708 count 978 '6 ether82563.6 devip.6 devuart.6 ethermii.6 etherigbe.6 devproc.' +-ch0-> Rwrite count 978 +ch0: eof +fsrpc[2aaf0!0]: done +cmuxproc[5b130]:1: del ch0 +cmuxproc[5b130]: eof +closemux 5b130 +abortconn[2aaf0]: hanging up +cmuxproc[5b130]: done +fssrvproc[2cb48]: done diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/catixd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/catixd.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include + + +static char* +qidtype(char *s, uchar t) +{ + char *p; + + p = s; + if(t & QTDIR) + *p++ = 'd'; + if(t & QTAPPEND) + *p++ = 'a'; + if(t & QTEXCL) + *p++ = 'l'; + if(t & QTAUTH) + *p++ = 'A'; + *p = '\0'; + return s; +} + + +static void +fdirconv(char *buf, char *e, Dir *d) +{ + char tmp[16]; + + seprint(buf, e, "%s '%s' " + "(%llux %lud %s) %#luo l %lld", d->name, + d->uid, + d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode, + d->length); +} + +static int +shortdirfmt(Fmt *fmt) +{ + char buf[160]; + + fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*)); + return fmtstrcpy(fmt, buf); +} + +static Dir* +readdir(Biobuf *bin, char *p) +{ + static uchar buf[DIRMAX]; + ulong sz; + Dir *d; + + if(Bread(bin, buf, BIT16SZ) != BIT16SZ) + sysfatal("%s: eof", p); + sz = GBIT16(buf); + if(BIT16SZ + sz > sizeof buf) + sysfatal("%s: dir too long", p); + if(Bread(bin, buf + BIT16SZ, sz) != sz) + sysfatal("%s: read failed", p); + d = malloc(sizeof *d + sz); + if(convM2D(buf, sizeof buf, d, (char*)(d+1)) <= 0) + sysfatal("%s: convM2D failed", p); + return d; +} + +static int +loaddir(Biobuf *bin, char *parent) +{ + Dir *d, *sd; + int nchild; + char *p, *path; + char tmp[16]; + + p = "/"; + if(parent != nil) + p = parent; + d = readdir(bin, p); + if(d == nil) + return -1; + sd = readdir(bin, p); + if(sd == nil){ + free(d); + return -1; + } + if(parent == nil) + path = "."; + else + path = smprint("%s/%s", parent, d->name); + if(sd->qid.path == ~0ULL && sd->qid.vers == ~0 && sd->qid.type == 0xFF) + print("'%s' sq nulldir %D\n", path, d); + else + print("'%s' sq (%llux %lud %s) %D\n", + path, sd->qid.path, sd->qid.vers, + qidtype(tmp, sd->qid.type), d); + if(d->qid.type&QTDIR){ + nchild = d->length; + d->length = 0; + while(nchild-- > 0) + if(loaddir(bin, path) < 0) + break; + } + free(d); + free(sd); + return 0; +} + +static void +catixd(char *fname) +{ + Biobuf *bin; + + bin = Bopen(fname, OREAD); + if(bin == nil) + sysfatal("%s: %r", fname); + loaddir(bin, nil); + Bterm(bin); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [file...]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + int i; + + ARGBEGIN{ + default: + usage(); + }ARGEND; + fmtinstall('M', dirmodefmt); + fmtinstall('D', shortdirfmt); + if(argc == 0) + catixd("/fd/0"); + else + for(i = 0; i < argc; i++){ + print("%s:\n", argv[i]); + catixd(argv[i]); + } + exits(0); +} + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/cfile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/cfile.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,656 @@ +#include +#include +#include +#include +#include + +#include "conf.h" +#include "msg.h" +#include "ch.h" +#include "mpool.h" +#include "tses.h" +#include "fs.h" +#include "file.h" +#include "cfile.h" +#include "ixreqs.h" +#include "dbg.h" + +/* + * Conflict policy: + * upon conflicts, we report the conflict and save the local + * change in the local cache. The saved file is ignored for pulls/pushes. + * The user may resolve by removing the saved copy or using it to + * update the server's file. + * + * Removed files: + * - removed files must stay with the removed flag until + * any conflict is resolved: + * - removed in the server: + * the cache file is renamed and we are back in sync. + * - removed in the client: + * there's no local version to keep; just report + * and we are back in sync. + */ + +static void updatefile(File *f); +static int condpull(File *f); +static int condremove(File *f); + +static Cmux *cmux; +static File *root; +static int disc; +static int rootfid; +static int twritehdrsz; + +extern ulong msz; + +File* +crootfile(void) +{ + incref(root); + return root; +} + +void +cputfile(File *f) +{ + putfile(f); +} + +static void +disconnected(void) +{ + if(disc++) + return; + fprint(2, "%s: disconnected: %r\n", argv0); +} + +/* + * There's a conflict in f due to concurrent changes in server and client. + * Copy our local version to a conflicting name and pull the server version. + */ +static void +conflict(File *f) +{ + Dir d; + + nulldir(&d); + d.name = esmprint("%s.!!", f->d->name); + if(dirwstat(f->path, &d) < 0) + fprint(2, "conflict in %s. not copied.\n", f->path); + else + fprint(2, "conflict in %s. copied to %s.!!\n", f->path, f->path); + free(d.name); + + free(f->sd); + f->sd = nil; + condpull(f); +} + +static long +gotdents(File *f, uchar *a, long n) +{ + Dir d; + char buf[512]; + int ns, fd; + File *nf; + + while(n > 0){ + ns = convM2D(a, n, &d, buf); + if(ns <= 0){ + dcprint("%s: gotdents: %s: convM2D\n", argv0, f->path); + return -1; + } + a += ns; + n -= ns; + nf = getchild(f, d.name); + if(nf == nil){ + nf = f; + incref(f); + fd = createfile(&nf, d.name, OREAD, d.mode); + if(fd < 0){ + putfile(f); + dcprint("%s: gotdents: %s/%s: %r\n", + argv0, f->path, d.name); + }else + close(fd); + /* f->sd is nil, and it will be retrieved by updatefile */ + }else{ + if(nf->sremoved){ /* it came back to life */ + free(nf->sd); + nf->sd = nil; + } + if(nf->sd == nil) + nf->sd = dupdir(&d); + } + nf->visited = 1; + /* don't putfile(nf); gotdir releases all child refs */ + } + return 0; +} + +static void +gotdir(File *f) +{ + File *cf; + int chg; + + rlock(f); + for(cf = f->child; cf != nil; cf = cf->next){ + if(cf->sd == nil) + updatefile(cf); + else if(cf->visited == 0) + cf->sremoved = 1; + cf->visited = 0; + putfile(cf); + } +Again: + for(cf = f->child; cf != nil; cf = cf->next) + if(cf->sremoved){ + chg = filechanged(cf); + if(chg && (chg&Gone) == 0) + conflict(cf); + else{ + runlock(f); + removefile(cf); + rlock(f); + goto Again; + } + } + runlock(f); +} + +static int +walkpath(Ch *ch, File *f) +{ + int n; + + if(f->parent == nil) + return 0; + n = walkpath(ch, f->parent); + xtwalk(ch, f->d->name, 0); + return n + 1; +} + +/* + * Get a chan and send fid, clone, and walk requests to get to the file. + * it's ok if f is nil, and that means also / + */ +static int +walktofile(File *f, Ch **chp, int *nwalksp, char *why) +{ + Ch *ch; + + if(disc) + return -1; + ch = newch(cmux); + if(ch == nil){ + disconnected(); + dcprint("%s %s: newch %r\n", why, f ? f->path : "/"); + return -1; + } + xtfid(ch, root->fid, 0); + xtclone(ch, OCEND|OCERR, 0); + if(f == nil) + *nwalksp = 0; + else + *nwalksp = walkpath(ch, f); + *chp = ch; + return 0; +} + +/* + * Get the file, cond that it has changed. + * Save it at a temporary place and replace the cached + * one with the new one. + * Return 0 if ok or disconnected. 1 if file changed. -1 upon errors. + */ +static int +condpull(File *f) +{ + Ch *ch; + int fd, i, nwalks; + Dir d, wd; + char *fn, buf[512]; + uvlong offset; + long nr; + Msg *m; + + if(walktofile(f, &ch, &nwalks, "pull") < 0) + return 0; + /* + * If we got it from server, cond that it changed or we are done. + */ + if(f->sd != nil) + xtcond(ch, CNE, f->sd, 0); + xtstat(ch, 0); + /* + * If we got it from server, cond that data changed or we are done. + */ + if(f->sd != nil){ + nulldir(&wd); + wd.qid = f->sd->qid; + wd.mtime = f->sd->mtime; + xtcond(ch, CNE, &wd, 0); + } + xtopen(ch, OREAD, 0); + xtread(ch, -1, 0ULL, msz, 1); + + fn = nil; + fd = -1; + if(xrfid(ch) < 0 || xrclone(ch) < 0) + goto Fail; + for(i = 0; i < nwalks; i++) + if(xrwalk(ch, nil) < 0){ + rerrstr(buf, sizeof buf); + if(strstr(buf, "not exist") != 0){ + f->sremoved = 1; + goto Done; + } + goto Fail; + } + + if(f->sd != nil) + if(xrcond(ch) < 0) + return 0; /* file is up to date; we are done */ + if(xrstat(ch, &d, buf) < 0) + goto Fail; + + if(f->sd != nil) + if(xrcond(ch) < 0) /* data didn't change; we are done */ + goto Done; + + if(xropen(ch) < 0) + goto Fail; + + /* + * create the directory in case it does not exist, or a temporary + * file to retrieve the new version from the server. + */ + if((d.qid.type&QTDIR) != 0){ + fd = create(f->path, OREAD, d.mode); /* ignore errors */ + close(fd); + fd = -1; + }else{ + fn = tmpfile(f->path); + fd = create(fn, OWRITE, d.mode); + if(fd < 0){ + abortch(ch); + goto Fail; + } + } + + /* + * Gather data or directory entries or file data. + */ + offset = 0ULL; + do{ + m = xrread(ch); + if(m == nil){ + dcprint("%s: read: %r\n", f->path); + goto Fail; + } + nr = IOLEN(m->io); + if(nr > 0){ + if(d.qid.type&QTDIR) + nr = gotdents(f, m->io->rp, nr); + else + nr = pwrite(fd, m->io->rp, nr, offset); + if(nr < 0){ + abortch(ch); + freemsg(m); + goto Fail; + } + } + offset += nr; + freemsg(m); + }while(nr > 0); + + /* + * Got everything, merge dir entries or move data into place. + */ + if(d.qid.type&QTDIR){ + gotdir(f); + } + if(wstatfile(f, &d) < 0) + goto Fail; +Done: + free(f->sd); + f->sd = dupdir(&d); + if(fn != nil){ + remove(fn); /* mail fail, if we could rename it */ + free(fn); + } + if(fd >= 0) + close(fd); + return 1; + +Fail: + if(fn != nil){ + remove(fn); + free(fn); + } + if(fd >= 0) + close(fd); + return -1; +} + +/* + * Push the file, cond that it has not changed. + * Return 0 if ok or disconnected, 1 if changed, -1 upon errors. + * + * XXX: Shouldn't we push metadata changes and then data cond that + * data has also changed? + */ +static int +condpush(File *f) +{ + Ch *ch; + int fd, i, nwalks; + long nw, nr; + Msg *m; + uvlong offset; + char buf[ERRMAX]; + + if(f->sd != nil){ + if(walktofile(f, &ch, &nwalks, "push") < 0) + return 0; + xtcond(ch, CEQ, f->sd, 0); + xtwalk(ch, "..", 0); + }else + if(walktofile(f->parent, &ch, &nwalks, "push") < 0) + return 0; + xtcreate(ch, f->d->name, OWRITE, f->d->mode, ISDIR(f)); + if(xrfid(ch) < 0 || xrclone(ch) < 0){ + if(!ISDIR(f)) + closech(ch); + return -1; + } + for(i = 0; i < nwalks; i++) + if(xrwalk(ch, nil) < 0){ + if(!ISDIR(f)) + closech(ch); + rerrstr(buf, sizeof buf); + return -1; + } + if(f->sd != nil){ + if(xrcond(ch) < 0){ /* changed in server */ + if(!ISDIR(f)) + closech(ch); + return 1; /* shouldn't we ignore and push? */ + } + if(xrwalk(ch, nil) < 0){ + if(!ISDIR(f)) + closech(ch); + return -1; + } + } + if(xrcreate(ch) < 0){ + if(!ISDIR(f)) + closech(ch); + return -1; + } + if(ISDIR(f)) + return 0; + fd = open(f->path, OREAD); + if(fd < 0){ + fprint(2, "%s: %r\n", f->path); + xtclunk(ch, 1); + xrclunk(ch); + return -1; + } + nw = 0; + offset = 0ULL; + for(;;){ + m = newmsg(pool); + nr = IOCAP(m->io) - Chhdrsz - twritehdrsz; + m->io->wp += twritehdrsz; + nr = read(fd, m->io->wp, nr); + if(nr <= 0){ + if(nr < 0) + fprint(2, "%s: read: %r", f->path); + xtclunk(ch, 1); + break; + } + m->io->wp += nr; + if(xtwrite(ch, m, nr, offset, 0) < 0){ + fprint(2, "%s: write: %r\n", f->path); + closech(ch); + drainch(ch); + close(fd); + return -1; + } + nw++; + offset += nr; + + /* read replies so that no more than + * 10 outstanding writes are going. + */ + if(nw > Maxwritewin){ + nw--; + if(xrwrite(ch) < 0){ + fprint(2, "%s: write: %r\n", f->path); + closech(ch); + close(fd); + return -1; + } + } + } + close(fd); + while(nw-- > 0) + if(xrwrite(ch) < 0){ + fprint(2, "%s: write: %r\n", f->path); + return -1; + } + + xrclunk(ch); + return 0; +} + +/* + * Remove the file, cond that it has not changed. + * Return 0 if ok or disconnected, 1 if changed, -1 upon errors. + */ +static int +condremove(File *f) +{ + Ch *ch; + int i, nwalks; + char buf[ERRMAX]; + + if(walktofile(f, &ch, &nwalks, "remove") < 0) + return 0; + + /* + * If got it from server, cond that it has not changed or conflict. + */ + if(f->sd != nil) + xtcond(ch, CEQ, f->sd, 0); + xtremove(ch, 1); + if(xrfid(ch) < 0 || xrclone(ch) < 0) + return -1; + if(xrfid(ch) < 0 || xrclone(ch) < 0) + return -1; + for(i = 0; i < nwalks; i++) + if(xrwalk(ch, nil) < 0){ + rerrstr(buf, sizeof buf); + if(strstr(buf, "not exist") != 0){ + f->sremoved = 1; + return 0; + } + return -1; + } + if(f->sd != nil) + if(xrcond(ch) < 0) + return 1; /* file changed in server! */ + if(xrremove(ch) < 0) + return -1; + return 0; +} + +/* + * Try to sync file: + * This can be done with a cond push: + * 1- if local changes & out of date -> conflict + * 2- if local changes and up to date -> push + * + * These can be done with a cond pull: + * 3- if no local changes & out of date -> pull + * 4- if no local changes & up to date -> done. + * + * For directories, recur to check out inner files. + */ +static void +updatefile(File *f) +{ + int chg; + + chg = filechanged(f); + if(chg){ + if(chg&Gone){ + f->cremoved = 1; + if(condremove(f) == 1) + conflict(f); + }else + if(condpush(f) == 1) + conflict(f); + }else + condpull(f); + if(ISDIR(f)) + childmap(f, updatefile); + +} + +static void +settwritehdrsz(void) +{ + Fscall t; + + t.type = Twrite; + twritehdrsz = packedsize(&t); +} + +void +cfileinit(Cmux *cm) +{ + Ch *ch; + int fid; + + settwritehdrsz(); + cmux = cm; + fid = -1; + if(cm == nil){ + werrstr("no cmux"); + disconnected(); + }else{ + ch = newch(cmux); + xtversion(ch, 0); + xtattach(ch, getuser(), "main", 1); + if(xrversion(ch, &msz) < 0) + disconnected(); + else if(xrattach(ch, &fid) < 0) + disconnected(); + } + root = rootfile(); + root->fid = fid; + updatefile(root); +} + +int +cwstatfile(File *f, Dir *d) +{ + return -1; +/* send wstat + if failed + return -1; + return wstatfile(f, d); + */ +} + +Dir* +cstatfile(File *f, int refresh) +{ +return nil; + return statfile(f, 0); +} + +int +cwalkfile(File **fp, char *elem) +{ +return -1; + return walkfile(fp, elem); +} + +int +copenfile(File *f, int mode) +{ +return -1; +/* be sure mode is 3|OTRUNC at most. + + + send the open, cond to our version, + if it's for reading, we want to get the file + and send local changes after the cond + if it's for writing + - truncating, nothing else + - otherwise, we want to get the file + and leave the chan open if its a write, to send + delayed writes. + + + return openfile(f, mode); + */ +} + +void +cclosefile(File *f, int fd) +{ +/* + closefile(f, fd); + if it was an update, send stat and update f->sd + release the chan +*/ +} + + +int +ccreatefile(File **fp, char *elem, int mode, int perm) +{ +return -1; +/* + change locally + send to server + + return createfile(fp, elem, mode, perm); +*/ +} + +long +cpreadfile(File *f, int fd, void *a, ulong count, uvlong offset) +{ +return -1; +/* + return cpreadfile(f, fd, a, count, offset); +*/ +} + +long +cpwritefile(File *f, int fd, void *a, ulong count, uvlong offset) +{ +return -1; +/* + send to server + return pwritefile(f, fd, a, count, offset); +*/ +} + + +int +cremovefile(File *f) +{ +return -1; +/* send to server + + return removefile(f); +*/ +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/cfile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/cfile.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,14 @@ + +/* |c/f2p cfile.c */ +extern void cclosefile(File *f, int fd); +extern int ccreatefile(File **fp, char *elem, int mode, int perm); +extern void cfileinit(Cmux *cm); +extern int copenfile(File *f, int mode); +extern long cpreadfile(File *f, int fd, void *a, ulong count, uvlong offset); +extern void cputfile(File *f); +extern long cpwritefile(File *f, int fd, void *a, ulong count, uvlong offset); +extern int cremovefile(File *f); +extern File* crootfile(void); +extern Dir* cstatfile(File *f, int refresh); +extern int cwalkfile(File **fp, char *elem); +extern int cwstatfile(File *f, Dir *d); diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/ch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/ch.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,552 @@ +/* + * A single session is multiplexed among channels + * + * Clients create channels, send messages through channels, + * and receive new channels and gone channels. + * Messages must be received from returned channels by clients. + * (NB: where channel has type Ch*) + * + * + * Termination: + * chwproc is a client for a tses (writer) + * cmuxproc is a client for a tses (reader) + * + * - If the session gets and eof or error during read + * 1. nil is sent to cmux + * 2. it informs the client (for both cmux and all chans) + * 3. the client closes the chans and the mux + * 4. cmux sends nil to tses wc. + * - If the client terminates the session + * 1. nil is sent to chwproc + * 3. it informs cmux, which will also get nil from tses + * and playes the tses close protocol. + * + * Sending a message with last releases the chan for writing. + * receving a message with last releases the chan for reading. + * The message may have no data, in which case it's used just to + * notify that the channel is being released. + * A channel is gone only when it's released both for reading and + * writing. + * Normal termination is: + * send last and recv last + * recv last and send last + * flushing: + * abortch + * early close: + * closech + * drainch + * eof: + * send last (null msg ok) + */ +#include +#include +#include +#include +#include /* *BIG* macros */ +#include "conf.h" +#include "msg.h" +#include "ch.h" +#include "dbg.h" + +typedef struct Chmsg Chmsg; + +struct Chmsg +{ + Msg *m; + int last; +}; + +static void +cmdump(Cmux *cm) +{ + int i; + + if(dbg['s'] == 0) + return; + print("cm %p: %d chs:", cm, cm->nchs); + for(i = 0; i < cm->nchs; i++) + if(cm->chs[i] != nil){ + if(cm->chs[i]->dead == 0) + print(" %d", i); + else + print(" %dd", i); + assert(cm->chs[i]->id == i); + } + print("\n"); +} + +extern void dumpmsg(Msg*); + +/* + * mux messages sent to ch->wc into cm->swc + * terminates when a nil is received. + */ +static void +chwproc(void *a) +{ + Ch *ch; + int nmsgs; + uint flags; + uchar *buf; + Chmsg chm; + Cmux *cm; + Msg *m; + + ch = a; + threadsetname("chwproc %p", a); + dpprint("chwproc[%p]: started\n", a); +Resurrection: + assert(ch->id <= CFidmask); + cm = ch->cm; + for(nmsgs = 0; recv(ch->wc, &chm) == 1; nmsgs++){ + m = chm.m; + if(m == nil){ + dsprint("chwproc[%p]: ch%d: nil msg\n", a, ch->id); + break; + } + flags = 0; + if(chm.last) + flags |= CFend; + if(nmsgs == 0 && ch->notnew == 0) + flags |= CFnew; + buf = msgpushhdr(m, BIT16SZ); + PBIT16(buf, flags|ch->id); + if(dbg['s']>1) + print("chwproc[%p]: hdr %#x\n", a, flags|ch->id); + if(sendp(cm->swc, m) != 1){ + dprint("chwproc[%p]: send failed", a); + freemsg(m); + break; + } + if(chm.last != 0){ + while(nbrecv(ch->wc, &chm) == 1){ + dprint("chwproc[%p]: extra msg\n", a); + freemsg(chm.m); + } + break; + } + } + sendp(cm->endc, ch); + dpprint("chwproc[%p]: limbo\n", a); + if(recvul(ch->dc) != 0){ + dpprint("chwproc[%p]: resurrection\n", a); + goto Resurrection; + } + dpprint("chwproc[%p]: exiting\n", a); + chanfree(ch->dc); +} + + +/* + * A channel might have been terminated for writing, yet the + * peer may be still busy processing outgoing requests. + * If the channel is aborted, we send hup request to the server + * to let it know that we will drop any further reply to this channel. + */ +static void +wendch(Ch *ch) +{ + Msg *m; + uchar *buf; + + m = emalloc(sizeof *m); + buf = msgpushhdr(m, BIT16SZ); + PBIT16(buf, CFend|ch->id); + sendp(ch->cm->swc, m); +} + +static Ch* +mkch(Cmux *cm, int i, int notnew) +{ + Ch *ch; + + ch = cm->chs[i]; + if(ch != nil){ + assert(ch->dead != 0); + ch->notnew = notnew; + ch->rclosed = ch->wclosed = ch->flushing = 0; + sendul(ch->dc, 1); /* resurrect chwproc */ + ch->dead = 0; + return ch; + } + ch = emalloc(sizeof *ch); + cm->chs[i] = ch; + ch->id = i; + ch->rc = echancreate(sizeof(Chmsg), 0); + ch->wc = echancreate(sizeof(Chmsg), 0); + ch->dc = echancreate(sizeof(ulong), 0); + ch->cm = cm; + ch->notnew = notnew; + threadcreate(chwproc, ch, Stack); + return ch; +} + +static void +grow(Cmux *cm, int n) +{ + int elsz; + + if(n > CFidmask) + sysfatal("growto: no more channel ids"); + if(cm->nachs < n){ + elsz = sizeof cm->chs[0]; + cm->chs = erealloc(cm->chs, n * elsz); + memset(&cm->chs[cm->nachs], 0, (n - cm->nachs) * elsz); + cm->nachs = n; + } +} + +static Ch* +addch(Cmux *cm, int i, int notnew) +{ + if(i < 0) + for(i =0 ; i < cm->nchs; i++) + if(cm->chs[i] == nil || cm->chs[i]->dead) + break; + if(i >= cm->nchs){ + grow(cm, i+1); + cm->nchs = i+1; + } + if(cm->chs[i] != nil && cm->chs[i]->dead == 0){ + werrstr("channel %d in use", i); + return nil; + } + cm->nuse++; + dsprint("cmuxproc[%p]: addch: ch%d (%d chs)\n", cm, i, cm->nuse); + return mkch(cm, i, notnew); +} + +enum{Free = 0, Keep = 1}; + +static void +delch(Cmux *cm, Ch *ch, int keep) +{ + + dsprint("cmux[%p]: delch: ch%d keep=%d (%d chs)\n", cm, ch->id, keep, cm->nuse); + if(ch->dead == 0 || keep != 0) + cm->nuse--; + if(keep) + ch->dead = 1; + else{ + chanfree(ch->rc); + chanfree(ch->wc); + sendul(ch->dc, 0); + ch->rc = ch->wc = nil; + assert(cm->chs[ch->id] == ch); + cm->chs[ch->id] = nil; + free(ch); + } +} + +/* + * Demux message already received in cm->rc and return + * the Ch for it, perhaps a new Ch. + */ +static Ch* +cdemux(Cmux *cm, Msg *m, int *lastp) +{ + uint id, flags; + uchar *hdr; + + Ch *ch; + + if(msglen(m) < BIT16SZ){ + werrstr("short message header"); + return nil; + } + hdr = msgpophdr(m, BIT16SZ); + assert(hdr != nil); + flags = GBIT16(hdr); + if(dbg['s']>1) + print("cdemux: hdr %#x\n", flags); + id = flags & CFidmask; + if(flags&CFnew){ + ch = addch(cm, id, 1); + if(ch == nil) + return nil; + sendp(cm->newc, ch); + }else + if(id >= cm->nchs || cm->chs[id] == nil || cm->chs[id]->dead){ + werrstr("no channel %d", id); + return nil; + } + *lastp = flags&CFend; + return cm->chs[id]; +} + +static void +allclosed(Cmux *cm) +{ + int i; + + for(i = 0; i < cm->nchs; i++) + if(cm->chs[i] != nil) + if(cm->chs[i]->dead) + delch(cm, cm->chs[i], Free); + else{ + fprint(2,"abort: cmuxproc: terminated but ch%d open", i); + abort(); + } +} + +static void +allabort(Cmux *cm) +{ + int i; + Chmsg chm; + + for(i = 0; i < cm->nchs; i++) + if(cm->chs[i] != nil) + if(cm->chs[i]->dead == 0){ + if(!cm->chs[i]->rclosed){ + chm.m = nil; + chm.last = 1; + send(cm->chs[i]->rc, &chm); + cm->chs[i]->rclosed = 1; + } + if(cm->chs[i]->wclosed) + delch(cm, cm->chs[i], Free); + }else + delch(cm, cm->chs[i], Free); +} + +static void +cmuxproc(void *a) +{ + enum{Mkc, Endc, Rc}; + Cmux *cm; + ulong dummy; + char *err; + Chmsg chm; + Msg *m; + Ch *ch; + Alt alts[] = { + {nil, &dummy, CHANRCV}, + {nil, &ch, CHANRCV}, + {nil, &m, CHANRCV}, + {nil, nil, CHANEND}, + }; + + cm = a; + threadsetname("cmuxproc %p", a); + dpprint("cmuxproc[%p]: starting\n", a); + alts[0].c = cm->mkc; + alts[1].c = cm->endc; + alts[2].c = cm->src; + for(;;) + switch(alt(alts)){ + case Mkc: + ch = addch(cm, -1, 0); + sendp(cm->mkrc, ch); + break; + case Endc: + if(ch == nil){ + allclosed(cm); + sendp(cm->swc, nil); + while((m = recvp(cm->src)) != nil) + freemsg(m); + free(recvp(cm->sec)); + goto Done; + } + if(ch->wclosed){ + dsprint("cmuxproc[%p]: ch%d flushing\n", a, ch->id); + ch->flushing = 1; + wendch(ch); + }else + ch->wclosed = 1; + if(ch->rclosed) + delch(cm, ch, Keep); + break; + case Rc: + if(m == nil){ + err = recvp(cm->sec); + if(err == nil) + dsprint("cmuxproc[%p]: eof (%d chs)\n", a, cm->nuse); + else + dsprint("cmuxproc[%p]: %s\n", a, err); + free(err); + sendp(cm->newc, nil); + if(cm->nuse > 0){ + allabort(cm); + break; + } + sendp(cm->swc, nil); + recvp(cm->endc); + goto Done; + } + chm.m = m; + ch = cdemux(cm, m, &chm.last); + if(ch == nil){ + dsprint("cdemux: drop msg: %r\n"); + cmdump(cm); + freemsg(m); + break; + } + if(!ch->rclosed && !ch->flushing) + send(ch->rc, &chm); + else + freemsg(m); + if(chm.last){ + if(ch->rclosed){ + dsprint("cmuxproc[%p]: ch%d flush\n", a, ch->id); + ch->flushing = 1; + } + ch->rclosed = 1; + if(ch->wclosed) + delch(cm, ch, Keep); + } + break; + default: + sysfatal("cmuxproc[%p]: alt", a); + } +Done: + allclosed(cm); + dsprint("cmuxproc[%p]: done\n", a); + chanfree(cm->newc); + chanfree(cm->mkc); + chanfree(cm->mkrc); + chanfree(cm->endc); + free(cm->chs); + free(cm); + threadexits(nil); +} + +Cmux* +muxses(Channel *src, Channel *swc, Channel *sec) +{ + Cmux *cm; + + cm = emalloc(sizeof *cm); + cm->src = src; + cm->swc = swc; + cm->sec = sec; + cm->newc = echancreate(sizeof(Ch*), 0); + cm->mkc = echancreate(sizeof(ulong), 0); + cm->mkrc = echancreate(sizeof(Ch*), 0); + cm->endc = echancreate(sizeof(ulong), 0); + /* + * Could be a thread, but then the caller + * would have to be aware and don't block the process. + */ + proccreate(cmuxproc, cm, Stack); + return cm; +} + +void +closemux(Cmux *cm) +{ + dsprint("closemux %p\n", cm); + sendp(cm->endc, nil); +} + +Ch* +newch(Cmux *cm) +{ + sendul(cm->mkc, 0); + return recvp(cm->mkrc); +} + +int +chsend(Ch *ch, Msg *m, int last) +{ + Chmsg chm; + + assert(m != nil); + chm.m = m; + chm.last = last; + if(ch->flushing){ + freemsg(m); + chm.m = nil; + chm.last = 1; + werrstr("chsend: ch%d flushing", ch->id); + send(ch->wc, &chm); + + /* + * client is sending which means that the last + * message was not sent. + * We are flushing and asking the wproc to exit, + * so we must notify the peer that we are done. + */ + wendch(ch); + + return -1; + } + send(ch->wc, &chm); + return 0; +} + +Msg* +chrecv(Ch *ch, int *last) +{ + Chmsg chm; + + recv(ch->rc, &chm); + *last = chm.last; + return chm.m; +} + +/* + * Close a Ch without sending a msg with the last request. + * The caller is expected to read until learning that the peer is + * done, unless it knows the peer is already done. + */ +void +closech(Ch *ch) +{ + Chmsg chm; + + dsprint("closech: ch%d\n", ch->id); + chm.m = nil; + chm.last = 1; + send(ch->wc, &chm); + wendch(ch); +} + +/* + * Close a Ch after having sent the last msg through it. + * Note that cmuxproc might be trying to send a reply to our caller. + */ +void +abortch(Ch *ch) +{ + Chmsg chm; + + Alt alts[] = { + {ch->cm->endc, &ch, CHANSND}, + {ch->rc, &chm, CHANRCV}, + {nil, nil, CHANEND} + }; + + dsprint("abortch: ch%d\n", ch->id); + for(;;){ + switch(alt(alts)){ + case 0: + /* could notify our termination request */ + return; + case 1: + freemsg(chm.m); + if(chm.last){ + /* got the last msg; ch is gone */ + return; + } + break; + default: + sysfatal("alt"); + } + } +} + +void +drainch(Ch *ch) +{ + Msg *m; + int last; + + dsprint("drainch: ch%d\n", ch->id); + while((m = chrecv(ch, &last)) != nil){ + freemsg(m); + if(last) + break; + } +} + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/ch.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/ch.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,57 @@ +typedef struct Ch Ch; +typedef struct Cmux Cmux; + + +enum +{ + /* Ch flags */ + CFnew = 0x8000, + CFend = 0x4000, + CFidmask = 0x3FFF, + + Chhdrsz = BIT16SZ, +}; + +struct Ch +{ + ushort id; + Channel*rc; /* read channel (of Chmsg) */ + Channel*wc; /* write channel (of Chmsg) */ + + /* implementation */ + Cmux *cm; + int notnew; /* already created because of peer */ + int rclosed; + int wclosed; + int flushing; + + int dead; /* gone channel, waiting to be reused */ + Channel *dc; +}; + +struct Cmux +{ + Channel*newc; /* of Ch*; to report new Ch's created */ + + /* implementation*/ + Channel*src; /* multiplexed session read channel */ + Channel*swc; /* multiplexed session write channel */ + Channel*sec; /* multiplexed session error channel */ + Channel*mkc; /* of ulong; request a new ch */ + Channel*mkrc; /* of Ch*; reply with new ch */ + Channel*endc; /* of Ch*; to release ch */ + Ch** chs; /* array of channels in this session */ + int nuse; /* # of channels in use */ + int nchs; /* number of channels used */ + int nachs; /* number of channels allocated */ +}; + +/* |c/f2p ch.c */ +extern void abortch(Ch *ch); +extern Msg* chrecv(Ch *ch, int *last); +extern int chsend(Ch *ch, Msg *m, int last); +extern void closech(Ch *ch); +extern void closemux(Cmux *cm); +extern void drainch(Ch *ch); +extern Cmux* muxses(Channel *src, Channel *swc, Channel *sec); +extern Ch* newch(Cmux *cm); diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/conf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/conf.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,20 @@ +enum +{ + KiB = 1024, + Stack = 8*KiB, /* stack size for threads */ + + Hbufsz = 8, /* room for message headers */ + Msgsz = 8*KiB, /* message size */ + Smsgsz = 64, /* small message size */ + Nmsgs = 16, /* # of messages per session */ + + Maxid = 255, /* # of channels per session */ + Nses = 512, /* max # of sessions */ + + Nchs = 64, /* max # of chans in ix */ + + Nels = 64, /* max # of elems in path */ + + Maxwritewin = 10, /* max # of outstanding writes */ + Incr = 16, +}; diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/dbg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/dbg.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,3 @@ + +char dbg[256]; + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/dbg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/dbg.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,21 @@ +/* + * 'P': mpools + * 'd': general debug + * 'f': fs + * 'm': messages + * 'p': processes + * 's': session debug + * 't': test client + * 'n': nsfile + * 'c': cache + */ +#define dPprint if(!dbg['P']){}else print +#define dcprint if(!dbg['c']){}else print +#define dfprint if(!dbg['f']){}else print +#define dmprint if(!dbg['m']){}else print +#define dnprint if(!dbg['n']){}else print +#define dpprint if(!dbg['p']){}else print +#define dprint if(!dbg['d']){}else print +#define dsprint if(!dbg['s']){}else print +#define dtprint if(!dbg['t']){}else print +extern char dbg[]; diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/file.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/file.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,58 @@ +typedef struct File File; + +enum +{ + None = 0, + Meta = 1, + Data = 2, + Gone = 4, +}; + +struct File +{ + Ref; + int nopens; + RWLock; + char *path; + Dir *d; + Dir *sd; /* dir in server */ + File *parent; + File *child; + int nchild; + File *next; /* in child list */ + int visited; + int cremoved; /* file removed on client */ + int sremoved; /* file removed on server */ + /* used by ixc */ + int fid; +}; + +#pragma varargck type "T" File* +#define ISDIR(f) ((f)->d->qid.type&QTDIR) + + +/* |c/f2p nsfile.c */ +extern void childmap(File *f, void(*fn)(File*)); +extern void closefile(File *f, int fd); +extern int createfile(File **fp, char *elem, int mode, int perm); +extern void dumptree(void); +extern Dir* dupdir(Dir *d); +extern int filechanged(File *f); +extern int filefmt(Fmt *fmt); +extern void fileinit(char *path, int udb); +extern Qid fileqid(File *f); +extern int filesync(void); +extern File* getchild(File *parent, char *name); +extern File* newfile(File *parent, char *path, Dir *d); +extern int openfile(File *f, int mode); +extern int perm(File *f, char *user, int p); +extern long preadfile(File *, int fd, void *a, ulong count, uvlong offset); +extern void putfile(File *f); +extern long pwritefile(File *, int fd, void *a, ulong count, uvlong offset); +extern int removefile(File *f); +extern File* rootfile(void); +extern int shortdirfmt(Fmt *fmt); +extern Dir* statfile(File *f, int refresh); +extern char* tmpfile(char *name); +extern int walkfile(File **fp, char *elem); +extern int wstatfile(File *f, Dir *d); diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/fmt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/fmt.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,256 @@ +#include +#include +#include + +#include "fs.h" + +static uint dumpsome(char*, char*, char*, long); +static void fdirconv(char*, char*, Dir*); +static char *qidtype(char*, uchar); + +#define QIDFMT "(%.16llux %lud %s)" + +static char* cname[CMAX] = +{ + [CEQ] "==", + [CGE] ">=", + [CGT] "> ", + [CLT] "< ", + [CLE] "<=", + [CNE] "!=", +}; + +int +fscallfmt(Fmt *fmt) +{ + Fscall *f; + int type; + char buf[512], tmp[200]; + char *p, *e, *s; + Dir *d; + Qid *q; + + e = buf+sizeof(buf); + f = va_arg(fmt->args, Fscall*); + type = f->type; + switch(type){ + case Tcond: + if(f->cond >= CMAX) + s = "??"; + else + s = cname[f->cond]; + p = seprint(buf, e, "Tcond %s", s); + if(f->nstat > sizeof tmp) + seprint(p, e, " stat(%d bytes)", f->nstat); + else{ + d = (Dir*)tmp; + convM2D(f->stat, f->nstat, d, (char*)(d+1)); + seprint(p, e, " stat "); + fdirconv(p+6, e, d); + } + break; + + case Rcond: + seprint(buf, e, "Rcond"); + break; + case Tfid: + seprint(buf, e, "Tfid fid %ud cflags %d", f->fid, f->cflags); + break; + case Rfid: + seprint(buf, e, "Rfid"); + break; + case Tclone: + seprint(buf, e, "Tclone cflags %d", f->cflags); + break; + case Rclone: + seprint(buf, e, "Rclone newfid %d", f->newfid); + break; + case Tversion: /* 100 */ + seprint(buf, e, "Tversion msize %ud version '%s'", f->msize, f->version); + break; + case Rversion: + seprint(buf, e, "Rversion msize %ud version '%s'", f->msize, f->version); + break; + case Tauth: /* 102 */ + seprint(buf, e, "Tauth afid %d uname %s aname %s", + f->afid, f->uname, f->aname); + break; + case Rauth: + seprint(buf, e, "Rauth qid " QIDFMT, + f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type)); + break; + case Tattach: /* 104 */ + seprint(buf, e, "Tattach afid %d uname %s aname %s", + f->afid, f->uname, f->aname); + break; + case Rattach: + seprint(buf, e, "Rattach fid %d qid " QIDFMT, + f->fid, f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type)); + break; + case Rerror: /* 107; 106 (Terror) illegal */ + seprint(buf, e, "Rerror ename %s", f->ename); + break; + case Twalk: /* 110 */ + seprint(buf, e, "Twalk wname %s", f->wname); + break; + case Rwalk: + q = &f->wqid; + seprint(buf, e, "Rwalk wqid " QIDFMT, + q->path, q->vers, qidtype(tmp, q->type)); + break; + case Topen: /* 112 */ + seprint(buf, e, "Topen mode %d", f->mode); + break; + case Ropen: + seprint(buf, e, "Ropen qid " QIDFMT " iounit %ud ", + f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit); + break; + case Tcreate: /* 114 */ + seprint(buf, e, "Tcreate name %s perm %M mode %d", f->name, (ulong)f->perm, f->mode); + break; + case Rcreate: + seprint(buf, e, "Rcreate qid " QIDFMT " iounit %ud ", + f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit); + break; + case Tread: /* 116 */ + seprint(buf, e, "Tread nmsg %d offset %lld count %ud", + f->nmsg, f->offset, f->count); + break; + case Rread: + p = seprint(buf, e, "Rread count %ud ", f->count); + dumpsome(p, e, f->data, f->count); + break; + case Twrite: /* 118 */ + p = seprint(buf, e, "Twrite offset %lld count %ud ", + f->offset, f->count); + dumpsome(p, e, f->data, f->count); + break; + case Rwrite: + seprint(buf, e, "Rwrite count %ud", f->count); + break; + case Tclunk: /* 120 */ + seprint(buf, e, "Tclunk"); + break; + case Rclunk: + seprint(buf, e, "Rclunk"); + break; + case Tremove: /* 122 */ + seprint(buf, e, "Tremove"); + break; + case Rremove: + seprint(buf, e, "Rremove"); + break; + case Tstat: /* 124 */ + seprint(buf, e, "Tstat"); + break; + case Rstat: + p = seprint(buf, e, "Rstat "); + if(f->nstat > sizeof tmp) + seprint(p, e, " stat(%d bytes)", f->nstat); + else{ + d = (Dir*)tmp; + convM2D(f->stat, f->nstat, d, (char*)(d+1)); + seprint(p, e, " stat "); + fdirconv(p+6, e, d); + } + break; + case Twstat: /* 126 */ + p = seprint(buf, e, "Twstat"); + if(f->nstat > sizeof tmp) + seprint(p, e, " stat(%d bytes)", f->nstat); + else{ + d = (Dir*)tmp; + convM2D(f->stat, f->nstat, d, (char*)(d+1)); + seprint(p, e, " stat "); + fdirconv(p+6, e, d); + } + break; + case Rwstat: + seprint(buf, e, "Rwstat"); + break; + default: + seprint(buf, e, "unknown type %d", type); + } + return fmtstrcpy(fmt, buf); +} + +static char* +qidtype(char *s, uchar t) +{ + char *p; + + p = s; + if(t & QTDIR) + *p++ = 'd'; + if(t & QTAPPEND) + *p++ = 'a'; + if(t & QTEXCL) + *p++ = 'l'; + if(t & QTAUTH) + *p++ = 'A'; + *p = '\0'; + return s; +} + +static void +fdirconv(char *buf, char *e, Dir *d) +{ + char tmp[16]; + + seprint(buf, e, "'%s' '%s' '%s' '%s' " + "q " QIDFMT " m %#luo " + "at %ld mt %ld l %lld " + "t %d d %d", + d->name, d->uid, d->gid, d->muid, + d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode, + d->atime, d->mtime, d->length, + d->type, d->dev); +} + +/* + * dump out count (or DUMPL, if count is bigger) bytes from + * buf to ans, as a string if they are all printable, + * else as a series of hex bytes + */ +#define DUMPL 64 + +static uint +dumpsome(char *ans, char *e, char *buf, long count) +{ + int i, printable; + char *p; + + if(buf == nil){ + seprint(ans, e, ""); + return strlen(ans); + } + printable = 1; + if(count > DUMPL) + count = DUMPL; + for(i=0; i127) + printable = 0; + p = ans; + *p++ = '\''; + if(printable){ + if(count > e-p-2) + count = e-p-2; + for(; count > 0; count--, p++, buf++) + if(*buf == '\n' || *buf == '\t') + *p = ' '; + else + *p = *buf; + }else{ + if(2*count > e-p-2) + count = (e-p-2)/2; + for(i=0; i0 && i%4==0) + *p++ = ' '; + sprint(p, "%2.2ux", (uchar)buf[i]); + p += 2; + } + } + *p++ = '\''; + *p = 0; + return p - ans; +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/fs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/fs.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,834 @@ +#include +#include +#include +#include +#include +#include + +#include "conf.h" +#include "msg.h" +#include "mpool.h" +#include "tses.h" +#include "ch.h" +#include "dbg.h" +#include "fs.h" +#include "file.h" + +/* + * Mbuf convention: + * Msg.io[0] always refers to the message buffer. + */ + +/* + * Flushing is done by aborting the Ch where the RPC is being sent. + * When ch is flushing, it will not deliver more messages to the + * reader. + * Thus, the client should not flush before receiving replies to + * requests like Tclone or Topen which allocate new resources. + * It should flush only when replies received from us make it clear + * what's the state on its behalf. + * + * This makes flush processing a lot simpler than it could be. + */ + +typedef struct Fs Fs; +typedef struct Rpc Rpc; +typedef struct Fid Fid; + + +struct Fs +{ + QLock; + Fid **fids; + int nfids; + Fid *free; + + Ses *s; /* session (i.e., tcp connection) */ + Cmux *cm; /* channel multiplexor */ + Channel *argc; /* to pass args to workers */ + ulong msize; /* maximum data size */ +}; + +struct Fid +{ + Ref; + Fid *next; /* in free list */ + File *file; + int id; + int fd; /* meaningful to File only */ + int cflags; /* OCEND|OCERR */ + int omode; +}; + +struct Rpc +{ + Fs *fs; + Ch *ch; + Msg *m; + Fid *fid; + Fscall t, r; + int last; +}; + +enum +{ + Large = 0, + Small, +}; + +static Ssrv *ssrv; +static Channel *poolc[2]; +static int npools[2]; +static ulong poolmsz[2] = +{ + [Large] Msgsz, + [Small] Smsgsz, +}; + + +static Fid* +newfid(Fs *fs) +{ + Fid *fid; + + qlock(fs); + if(fs->free != nil){ + fid = fs->free; + fs->free = fid->next; + qunlock(fs); + return fid; + } + if((fs->nfids%Incr) == 0) + fs->fids = erealloc(fs->fids, (fs->nfids+Incr)*sizeof(Fid*)); + fid = emalloc(sizeof *fid); + fs->fids[fs->nfids] = fid; + fid->id = fs->nfids++; + fid->ref = 1; + fid->omode = -1; + fid->fd = -1; + qunlock(fs); + return fid; +} + +static void +putfid(Fs *fs, Fid *fid) +{ + if(decref(fid) == 0){ + qlock(fs); + fid->next = fs->free; + fs->free = fid; + if(fid->file != nil){ + if(fid->fd != -1) + closefile(fid->file, fid->fd); + putfile(fid->file); + } + fid->file = nil; + fid->fd = -1; + fid->omode = -1; + qunlock(fs); + } +} + +static Fid* +getfid(Fs *fs, int id) +{ + Fid *fid; + + qlock(fs); + if(id < 0 || id >= fs->nfids || fs->fids[id]->file == nil){ + qunlock(fs); + return nil; + } + fid = fs->fids[id]; + incref(fid); + qunlock(fs); + return fid; +} + +static void +putpool(Mpool *mp) +{ + poolstats(mp); + sendp(poolc[Large], mp); +} + +static void +putspool(Mpool *mp) +{ + poolstats(mp); + sendp(poolc[Small], mp); +} + +static Mpool* +getpool(int sz) +{ + Mpool *mp; + + mp = nbrecvp(poolc[sz]); + if(mp != nil) + return mp; + + if(ainc(&npools[sz]) < Nses){ + mp = newpool(poolmsz[sz], Nmsgs); + if(mp == nil){ + adec(&npools[sz]); + return nil; + } + mp->freepool = putpool; + if(sz == Small) + mp->freepool = putspool; + return mp; + } + return recvp(poolc[sz]); +} + +static int +fsreply(Rpc *rpc) +{ + Msg *m; + Io *io; + ulong n; + int rc; + + m = rpc->m; + m->hdr = nil; + io = &m->io[0]; + /* + * Rread reads data directly after the packed reply Fscall + * and sets io's rp and wp by itself. + */ + dfprint("%cch%d-> %G\n", rpc->last?'|':'-', rpc->ch->id, &rpc->r); + if(rpc->r.type == Rread) + n = pack(&rpc->r, io->bp, packedsize(&rpc->r)); + else{ + ioreset(io); + n = pack(&rpc->r, io->wp, IOCAP(io)); + io->wp += n; + } + if(n <= 0) + sysfatal("fsreply: pack (Smsgsz too small?)"); + rc = chsend(rpc->ch, rpc->m, rpc->last); + if(rc < 0){ + dfprint("fsreply: %r\n"); + rpc->last = 1; + } + if(rpc->last != 0 && rpc->fid != nil && (rpc->fid->cflags&OCEND) != 0){ + putfid(rpc->fs, rpc->fid); /* held by client */ + putfid(rpc->fs, rpc->fid); /* held by rpc */ + rpc->fid = nil; + } + return rc; +} + +static int +fserror(Rpc *rpc, char *e) +{ + rpc->r.type = Rerror; + rpc->r.ename = e; + rpc->last = 1; + fsreply(rpc); + return -1; +} + +static int +fssyserror(Rpc *rpc) +{ + char *e; + + e = smprint("%r"); + rpc->r.type = Rerror; + rpc->r.ename = e; + rpc->last = 1; + if(rpc->fid != nil && (rpc->fid->cflags&OCERR) != 0) + rpc->fid->cflags |= OCEND; + fsreply(rpc); + free(e); + return -1; +} + +static int +fsversion(Rpc *rpc) +{ + if(rpc->t.msize < rpc->fs->msize){ + rpc->fs->msize = rpc->t.msize; + dfprint("fsversion: msize %uld\n", rpc->fs->msize); + } + rpc->r.msize = rpc->fs->msize; + if(strcmp(rpc->t.version, "ix") != 0) + return fserror(rpc, "wrong version"); + rpc->r.version = "ix"; + return fsreply(rpc); +} + +static int +fsauth(Rpc *rpc) +{ + return fserror(rpc, "no auth required"); +} + +static int +fsattach(Rpc *rpc) +{ + if(strcmp(rpc->t.uname, getuser()) != 0) + return fserror(rpc, "bad user in attach"); + /* + * rpc->t.aname ignored + * rpc->t.fid is not used + */ + if(rpc->t.afid != NOFID) + return fserror(rpc, "auth not supported"); + rpc->fid = newfid(rpc->fs); + incref(rpc->fid); /* client's reference */ + rpc->r.fid = rpc->fid->id; + rpc->fid->file = rootfile(); + rpc->r.qid = fileqid(rpc->fid->file); + return fsreply(rpc); +} + + +static int +fsclone(Rpc *rpc) +{ + Fid *nfid; + Fid *fid; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + nfid = newfid(rpc->fs); /* ref held by client */ + rpc->r.newfid = nfid->id; + nfid->omode = fid->omode; + nfid->file = fid->file; + incref(nfid->file); + putfid(rpc->fs, fid); /* held by rpc */ + rpc->fid = nfid; + nfid->cflags = rpc->t.cflags; + incref(nfid); /* held by rpc */ + return fsreply(rpc); +} + +static int +fswalk(Rpc *rpc) +{ + Fid *fid; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(fid->omode != -1) + return fserror(rpc, "can't walk an open fid"); + if(walkfile(&fid->file, rpc->t.wname) < 0) + return fssyserror(rpc); + rpc->r.wqid = fileqid(fid->file); + return fsreply(rpc); +} + +static int +fsopen(Rpc *rpc) +{ + Fid *fid; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(fid->omode != -1) + return fserror(rpc, "fid already open"); + fid->fd = openfile(fid->file, rpc->t.mode); + if(fid->fd < 0) + return fssyserror(rpc); + fid->omode = rpc->t.mode; + rpc->r.qid = fileqid(fid->file); + rpc->r.iounit = 0; + return fsreply(rpc); +} + +static int +fscreate(Rpc *rpc) +{ + int fd; + Fid *fid; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(fid->omode != -1) + return fserror(rpc, "fid already open"); + fd = createfile(&fid->file, rpc->t.name, rpc->t.mode, rpc->t.perm); + if(fd < 0) + return fssyserror(rpc); + fid->fd = fd; + fid->omode = rpc->t.mode; + rpc->r.qid = fileqid(fid->file); + rpc->r.iounit = 0; + return fsreply(rpc); +} + +static int +fsread(Rpc *rpc) +{ + long nr; + Io *io; + Fid *fid; + int i, saved, last; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(fid->omode < 0 || (fid->omode&3) == OWRITE) + return fserror(rpc, "fid not open for reading"); + if(rpc->t.nmsg == 0) + rpc->t.nmsg = 1; + /* + * Get a large buffer; we are probably using a small one. + */ + freemsg(rpc->m); + rpc->m = newmsg(rpc->fs->s->pool); + for(i = 0; i != rpc->t.nmsg; i++){ + io = &rpc->m->io[0]; + nr = rpc->t.count; + rpc->m->hdr = nil; + ioreset(io); + io->wp += packedsize(&rpc->r); + if(nr < 0 || nr > IOCAP(io)) + nr = IOCAP(io); + if(IOLEN(io)+nr > rpc->fs->msize) + nr = rpc->fs->msize - IOLEN(io); + rpc->r.data = (char*)io->wp; + nr = preadfile(fid->file, fid->fd, io->wp, nr, rpc->t.offset); + if(nr < 0) + return fssyserror(rpc); + io->wp += nr; + rpc->t.offset += nr; + + rpc->r.count = nr; + saved = rpc->last; + rpc->last = 0; + last = saved && (rpc->last = i == rpc->t.nmsg || nr == 0); + if(fsreply(rpc) < 0) + return -1; + rpc->last = saved; + if(last) + break; + rpc->m = newmsg(rpc->fs->s->pool); + } + return 0; +} + +static int +fswrite(Rpc *rpc) +{ + long nw; + Fid *fid; + Fscall *t; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(fid->omode < 0 || (fid->omode&3) == OREAD) + return fserror(rpc, "fid not open for writing"); + t = &rpc->t; + nw = pwritefile(fid->file, fid->fd, t->data, t->count, t->offset); + if(nw < 0) + return fssyserror(rpc); + rpc->r.count = nw; + return fsreply(rpc); +} + +static int +fsclunk(Rpc *rpc) +{ + Fid * fid; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + putfid(rpc->fs, fid); + rpc->fid = nil; + return fsreply(rpc); +} + +static int +fsremove(Rpc *rpc) +{ + Fid * fid; + int r; + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(fid->omode >= 0){ + closefile(fid->file, fid->fd); + fid->fd = -1; + fid->omode = -1; + } + putfid(rpc->fs, fid); /* held by client */ + + /* file goes when rpc->fid reference is released later */ + r = removefile(fid->file); + + putfid(rpc->fs, fid); /* held by rpc */ + rpc->fid = nil; + if(r < 0) + return fssyserror(rpc); + else + return fsreply(rpc); +} + +static int +fsstat(Rpc *rpc) +{ + Fid *fid; + Dir *d; + uchar buf[512]; /* bug: fixes stat size */ + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + d = statfile(fid->file, 1); + if(d == nil) + return fssyserror(rpc); + rpc->r.nstat = convD2M(d, buf, sizeof buf); + if(rpc->r.nstat <= BIT32SZ) + sysfatal("fsstat: buf too short"); + rpc->r.stat = buf; + free(d); + + /* + * We are probably using a small buffer, get a large + * one for the reply. + */ + freemsg(rpc->m); + rpc->m = newmsg(rpc->fs->s->pool); + return fsreply(rpc); +} + +static int +fswstat(Rpc *rpc) +{ + Fid *fid; + Dir d; + char buf[512]; /* bug: fixes stat size */ + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(convM2D(rpc->t.stat, rpc->t.nstat, &d, buf) <= BIT32SZ) + return fserror(rpc, "bad stat data"); + if(wstatfile(fid->file, &d) < 0) + return fssyserror(rpc); + return fsreply(rpc); +} + +static int +cmpulong(uvlong u1, uvlong u2, int cond) +{ + switch(cond){ + case CEQ: + return u1 == u2; + case CNE: + return u1 != u2; + case CLE: + return u1 >= u2; + case CLT: + return u1 > u2; + case CGT: + return u1 < u2; + case CGE: + return u2 <= u2; + default: + return 0; + } +} + +static int +cmpstr(char *s1, char *s2, int cond) +{ + switch(cond){ + case CEQ: + return strcmp(s1, s2) == 0; + case CNE: + return strcmp(s1, s2) != 0; + case CLE: + return strcmp(s1, s2) >= 0; + case CLT: + return strcmp(s1, s2) > 0; + case CGT: + return strcmp(s1, s2) < 0; + case CGE: + return strcmp(s1, s2) <= 0; + default: + return 0; + } +} + +static int +fscond(Rpc *rpc) +{ + Fid *fid; + Dir cd, *d; + char buf[512]; /* bug: fixes stat size */ + + fid = rpc->fid; + if(fid == nil) + return fserror(rpc, "fid not set"); + if(convM2D(rpc->t.stat, rpc->t.nstat, &cd, buf) <= BIT32SZ) + sysfatal("fswstat: bug"); + d = statfile(fid->file, 1); + if(d == nil) + return fssyserror(rpc); + if(cd.qid.path != ~0ULL && cmpulong(cd.qid.path, d->qid.path, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false qid.path"); + } + if(cd.qid.vers != ~0UL && cmpulong(cd.qid.vers, d->qid.vers, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false qid.vers"); + } + if(cd.mode != ~0 && cmpulong(cd.mode, d->mode, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false mode"); + } + if(cd.atime != ~0 && cmpulong(cd.atime, d->atime, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false atime"); + } + if(cd.mtime != ~0 && cmpulong(cd.mtime, d->mtime, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false mtime"); + } + if(cd.length != ~0ULL && cmpulong(cd.length, d->length, rpc->t.cond)==0){ + free(d); + return fserror(rpc, "false length"); + } + if(cd.name != nil && cmpstr(cd.name, d->name, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false name"); + } + if(cd.uid != nil && cmpstr(cd.uid, d->uid, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false uid"); + } + if(cd.gid != nil && cmpstr(cd.gid, d->gid, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false gid"); + } + if(cd.muid != nil && cmpstr(cd.muid, d->muid, rpc->t.cond) == 0){ + free(d); + return fserror(rpc, "false muid"); + } + free(d); + return fsreply(rpc); +} + +static int +fsfid(Rpc *rpc) +{ + if(rpc->fid != nil) + putfid(rpc->fs, rpc->fid); + rpc->fid = getfid(rpc->fs, rpc->t.fid); + if(rpc->fid == nil) + return fserror(rpc, "no such fid"); + rpc->fid->cflags = rpc->t.cflags; + return fsreply(rpc); +} + +typedef int (*Rpcfn)(Rpc*); + +static Rpcfn reqfn[Tmax] = { +[Tversion] fsversion, +[Tauth] fsauth, +[Tattach] fsattach, +[Tclone] fsclone, +[Twalk] fswalk, +[Topen] fsopen, +[Tcreate] fscreate, +[Tread] fsread, +[Twrite] fswrite, +[Tclunk] fsclunk, +[Tremove] fsremove, +[Tstat] fsstat, +[Twstat] fswstat, +[Tfid] fsfid, +[Tcond] fscond, +}; + +static int +fsrpc(Rpc *rpc) +{ + Msg *m; + int r; + + m = rpc->m; + if(IOLEN(&m->io[0]) == 0){ + dfprint("ch%d: eof\n", rpc->ch->id); + goto Fail; + } + if(unpack(m->io[0].rp, IOLEN(&m->io[0]), &rpc->t) <= 0){ + dfprint("ch%d: unknown message: %r\n", rpc->ch->id); + goto Fail; + } + dfprint("<-ch%d%c %G\n", rpc->ch->id, rpc->last?'|':'-', &rpc->t); + if(rpc->t.type >= nelem(reqfn) || reqfn[rpc->t.type] == nil){ + dfprint("bad msg type %d\n", rpc->t.type); + goto Fail; + } + rpc->r.type = rpc->t.type+1; + r = reqfn[rpc->t.type](rpc); + return r; +Fail: + m->hdr = nil; + ioreset(m->io); + rpc->last = 1; + chsend(rpc->ch, m, 1); + return -1; +} + +static void +fsrpcproc(void *a) +{ + Channel *c; + int id, waslast; + Rpc rpc; + + c = a; + rpc.fid = nil; + rpc.fs = recvp(c); + rpc.ch = recvp(c); + rpc.last = 0; + threadsetname("fsrpcproc fs %p ch %p", rpc.fs, rpc.ch); + id = rpc.ch->id; + dfprint("fsrpc[%p!%d]: new chan %s\n", rpc.fs->s, id, rpc.fs->s->addr); + while((rpc.m = chrecv(rpc.ch, &rpc.last)) != nil){ + waslast = rpc.last; + if(fsrpc(&rpc) < 0 && !waslast){ + drainch(rpc.ch); + break; + } + if(rpc.last) + break; + } + if(rpc.fid != nil) + putfid(rpc.fs, rpc.fid); + dfprint("fsrpc[%p!%d]: done\n", rpc.fs->s, id); + threadexits(nil); +} + +static void +fssrvproc(void *a) +{ + Fs fs; + Ch *ch; + int i; + + threadsetname("fssrvproc %p", a); + dfprint("fssrvproc[%p]: started\n", &fs); + memset(&fs, 0, sizeof fs); + fs.s = a; + fs.msize = Msgsz - Chhdrsz; + startses(fs.s, getpool(Large), getpool(Small)); + fs.argc = echancreate(sizeof(void*), 0); + fs.cm = muxses(fs.s->rc, fs.s->wc, fs.s->ec); + dfprint("fssrvproc: %p %s\n", &fs, fs.s->addr); + while((ch = recvp(fs.cm->newc)) != nil){ + threadcreate(fsrpcproc, fs.argc, Stack); + sendp(fs.argc, &fs); + sendp(fs.argc, ch); + } + closemux(fs.cm); + chanfree(fs.argc); + /* mux released by itself */ + for(i = 0; i < fs.nfids; i++) + free(fs.fids[i]); + free(fs.fids); + dfprint("fssrvproc[%p]: done\n", &fs); + threadexits(nil); +} + + +static int consfd = -1; +static int constid = -1; +static Biobuf bcmd; + +static void +consproc(void *a) +{ + int fd; + char *cmd; + + threadsetname("consproc"); + dfprint("consproc started\n"); + fd = (int)a; + Binit(&bcmd, fd, OREAD); + constid = threadid(); + for(;;){ + fprint(fd, "> "); + cmd = Brdstr(&bcmd, '\n', 1); + if(cmd == nil) + break; + if(cmd[0] == 0){ + free(cmd); + continue; + } + dfprint("cmd: %s\n", cmd); + if(strcmp(cmd, "sync") == 0){ + filesync(); + fprint(fd, "synced\n"); + fprint(2, "synced\n"); + }else if(strcmp(cmd, "halt") == 0){ + free(cmd); + break; + }else + fprint(fd, "%s?\n", cmd); + free(cmd); + } + fprint(2, "consproc: halting\n"); + filesync(); + fprint(fd, "halted\n"); + fprint(2, "halted\n"); + dfprint("consproc done\n"); + threadexitsall(nil); +} + +static int +cons(char *srv) +{ + char *fn; + int p[2], fd; + + fn = esmprint("/srv/%s", srv); + fd = create(fn, OWRITE|ORCLOSE|OCEXEC, 0660); + if(fd < 0){ + free(fn); + return -1; + } + if(pipe(p) < 0) + sysfatal("pipe: %r"); + fprint(fd, "%d", p[0]); + consfd = p[0]; + proccreate(consproc, (void*)p[1], Stack); + free(fn); + return 0; +} + +void +fsinit(char *addr, char *srv) +{ + ssrv = newsrv(addr); + if(ssrv == nil) + sysfatal("fsinit: newsrv: %r"); + if(cons(srv) < 0) + sysfatal("cons: %r"); + poolc[0] = echancreate(sizeof(Mpool*), Nses); + poolc[1] = echancreate(sizeof(Mpool*), Nses); +} + +void +fssrv(void) +{ + Ses *s; + + while((s = recvp(ssrv->newc)) != nil) + threadcreate(fssrvproc, s, Stack); + fprint(consfd, "halt\n"); + threadexitsall(nil); +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/fs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/fs.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,139 @@ +enum{ + Tfid = Tversion-6, + Rfid, + Tclone, /* unused numbers in fcall.h */ + Rclone, /* following T&R conventions */ + Tcond, + Rcond, + + OCEND = 1, /* clunk on end of rpc */ + OCERR = 2, /* clunk on error */ + + CEQ = 0, /* Tcond.cond */ + CGE, + CGT, + CLE, + CLT, + CNE, + CMAX, +}; + +/* + Protocol: + Tversion msize[4] version[s] + Rversion msize[4] version[s] + Tauth afid[4] uname[s] aname[s] + Rauth aqid[13] + Rerror ename[s] + Tattach afid[4] uname[s] aname[s] + Rattach fid[4] qid[13] + Tfid fid[4] cflags[1] + Rfid + Tclone cflags[1] + Rclone newfid[4] + Twalk wname[s] + Rwalk wqid[13] + Topen mode[1] + Ropen qid[13] iounit[4] + Tcreate name[s] perm[4] mode[1] + Rcreate qid[13] iounit[4] + Tread nmsg[4] offset[8] count[4] + Rread count[4] data[count] may be repeated + Twrite offset[8] count[4] data[count] + Rwrite count[4] + Tclunk + Rclunk + Tremove + Rremove + Tstat + Rstat stat[n] + Twstat stat[n] + Rwstat + Tcond cond[1] stat[n] + Rcond + + The largest packed messages, not counting strings and data, are + Rattach, Ropen, and Rcreate, with 18 bytes. + Plus ch header (2 bytes), plus message size (2 bytes). + Could create pools for small messages with, say, 64 bytes, + and use them for messages that we know that fit there. + Probably only Rstat, Tcond, Twstat, Rread, and Twrite won't fit. + Perhaps also some Twalk for a long name element. + */ + +typedef +struct Fscall +{ + uchar type; + u32int fid; /* Tattach, Tfid */ + union { + struct { + u32int msize; /* Tversion, Rversion */ + char *version; /* Tversion, Rversion */ + }; + struct { + char *ename; /* Rerror */ + }; + struct { + Qid qid; /* Rattach, Ropen, Rcreate */ + u32int iounit; /* Ropen, Rcreate */ + }; + struct { + Qid aqid; /* Rauth */ + }; + struct { + u32int afid; /* Tauth, Tattach */ + char *uname; /* Tauth, Tattach */ + char *aname; /* Tauth, Tattach */ + }; + struct { + u32int perm; /* Tcreate */ + char *name; /* Tcreate */ + uchar mode; /* Tcreate, Topen */ + }; + struct { + char *wname; /* Twalk */ + }; + struct { + Qid wqid; /* Rwalk */ + }; + struct { + u32int newfid; /* Rclone */ + }; + struct { + u32int nmsg; /* Tread */ + vlong offset; /* Tread, Twrite */ + u32int count; /* Tread, Twrite, Rread */ + char *data; /* Twrite, Rread */ + }; + struct { + uchar cond; /* Tcond */ + ushort nstat; /* Tcond, Twstat, Rstat */ + uchar *stat; /* Tcond, Twstat, Rstat */ + }; + struct { + uchar cflags; /* Tfid, Tclone */ + }; + }; +} Fscall; + +#pragma varargck type "G" Fscall* + +/* + * |c/f2p fs.c + */ +extern void fsinit(char *addr, char *srv); +extern void fssrv(void); + +/* + * |c/f2p pack.c + */ +extern uint pack(Fscall *f, uchar *ap, uint nap); +extern uint packedsize(Fscall *f); +extern uint unpack(uchar *ap, uint nap, Fscall *f); + +/* + * |c/f2p fmt.c + */ +extern int fscallfmt(Fmt *fmt); + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/ix.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/ix.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include "conf.h" +#include "msg.h" +#include "mpool.h" +#include "tses.h" +#include "ch.h" +#include "dbg.h" +#include "fs.h" +#include "file.h" + +#define SRVADDR "tcp!*!9999" + +int mainstacksize = Stack; + +static void +usage(void) +{ + fprint(2, "usage: %s [-d] [-D flags] [-s srv] [addr]\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *addr, *flags, *srv; + + addr = SRVADDR; + srv = "ix"; + + ARGBEGIN{ + case 'd': + dbg['d']++; + break; + case 'D': + flags = EARGF(usage()); + for(;*flags != 0; flags++) + dbg[*flags]++; + dbg['d']++; + break; + case 's': + srv = EARGF(usage()); + break; + default: + usage(); + }ARGEND; + if(argc == 1) + addr = argv[1]; + else if(argc > 1) + usage(); + + fmtinstall('G', fscallfmt); + fmtinstall('D', dirfmt); + fmtinstall('M', dirmodefmt); + fmtinstall('T', filefmt); + fileinit("/usr/nemo/bin", 0); + fsinit(addr, srv); + fssrv(); + dtprint("testsrv: exiting\n"); + threadexits(nil); +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/ixc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/ixc.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,719 @@ +#include +#include +#include +#include +#include +#include +#include "conf.h" +#include "msg.h" +#include "ch.h" +#include "mpool.h" +#include "tses.h" +#include "fs.h" +#include "file.h" +#include "cfile.h" +#include "dbg.h" + +/* + 8.ix -Dfn +! slay 8.ix|rc + 8.ixc -Dfn cache tcp!localhost!9999 + + */ + +typedef struct Cfid Cfid; +typedef struct Rpc Rpc; + +struct Cfid +{ + Cfid *next; + short busy; + int omode; + int fid; + int fd; + char *user; + File* file; +}; + +struct Rpc +{ + Fcall t; + Fcall r; + Msg *m; + Cfid *fid; + int fd; +}; + +static Cfid *fids; +static QLock fidslk; + +ulong msz = Msgsz; + +static void io(int fd); + +static int rflush(Rpc*), rversion(Rpc*), rauth(Rpc*), + rattach(Rpc*), rwalk(Rpc*), + ropen(Rpc*), rcreate(Rpc*), + rread(Rpc*), rwrite(Rpc*), rclunk(Rpc*), + rremove(Rpc*), rstat(Rpc*), rwstat(Rpc*); + +static int (*fcalls[])(Rpc*) = { + [Tversion] rversion, + [Tflush] rflush, + [Tauth] rauth, + [Tattach] rattach, + [Twalk] rwalk, + [Topen] ropen, + [Tcreate] rcreate, + [Tread] rread, + [Twrite] rwrite, + [Tclunk] rclunk, + [Tremove] rremove, + [Tstat] rstat, + [Twstat] rwstat, +}; + +static char Enofid[] = "fid not found"; +static char Eperm[] = "permission denied"; +static char Enotdir[] = "not a directory"; +static char Enoauth[] = "ramfs: authentication not required"; +static char Enotexist[] = "file does not exist"; +static char Einuse[] = "file in use"; +static char Eexist[] = "file exists"; +static char Eisdir[] = "file is a directory"; +static char Enotowner[] = "not owner"; +static char Eisopen[] = "file already open for I/O"; +static char Enotopen[] = "file not open for I/O"; +static char Excl[] = "exclusive use file already open"; +static char Ename[] = "illegal name"; +static char Eversion[] = "unknown 9P version"; +static char Enotempty[] = "directory not empty"; + +static Ses *ses; +static Cmux *cmux; +Mpool *pool, *spool; + +static char *cdir; + +void +notifyf(void *a, char *s) +{ + USED(a); + if(strncmp(s, "interrupt", 9) == 0) + noted(NCONT); + noted(NDFLT); +} + +static Cfid* +newfid(int fid, int mk) +{ + Cfid *f, *ff; + + qlock(&fidslk); + ff = 0; + for(f = fids; f; f = f->next) + if(f->fid == fid) + return f; + else if(!ff && !f->busy) + ff = f; + if(mk == 0) + return nil; + if(ff){ + ff->busy = 1; + ff->fid = fid; + ff->file = nil; + ff->omode = -1; + free(ff->user); + ff->user = nil; + qunlock(&fidslk); + return ff; + } + f = emalloc(sizeof *f); + f->fid = fid; + f->next = fids; + fids = f; + f->busy = 1; + f->omode = -1; + qunlock(&fidslk); + return f; +} + +static int +p9reply(Rpc *rpc) +{ + int n; + Io *io; + + io = rpc->m->io; + ioreset(io); + rpc->m->hdr = nil; + n = convS2M(&rpc->r, io->wp, IOCAP(io)); + if(n <= 0) + sysfatal("p9reply: convS2M"); + io->wp += n; + if(write(rpc->fd, io->rp, IOLEN(io)) != IOLEN(io)) + return -1; + freemsg(rpc->m); + rpc->m = nil; + return 0; +} + +static int +p9error(Rpc *rpc, char *e) +{ + rpc->r.type = Rerror; + rpc->r.ename = e; + return p9reply(rpc); +} + +static int +p9syserror(Rpc *rpc) +{ + char buf[128]; + + rerrstr(buf, sizeof buf); + buf[sizeof buf - 1] = 0; + return p9error(rpc, buf); +} + +static int +rversion(Rpc *rpc) +{ + if(rpc->t.msize > msz) + rpc->t.msize = msz; + else + msz = rpc->t.msize; + rpc->r.msize = msz; + if(strncmp(rpc->t.version, "9P2000", 6) != 0) + return p9error(rpc, Eversion); + rpc->r.version = "9P2000"; + return p9reply(rpc); +} + +static int +rauth(Rpc *rpc) +{ + return p9error(rpc, "no authentication required"); +} + +static int +rflush(Rpc *rpc) +{ + return p9reply(rpc); +} + +static int +rattach(Rpc *rpc) +{ + Cfid *fid; + + fid = newfid(rpc->t.fid, 1); + /* no authentication! */ + fid->file = crootfile(); + if(rpc->t.uname[0]) + fid->user = estrdup(rpc->t.uname); + else + fid->user = estrdup("none"); + rpc->r.qid = fileqid(fid->file); + return p9reply(rpc); +} + +static Cfid* +clone(Cfid *f, int nfid) +{ + Cfid *nf; + + nf = newfid(nfid, 1); + nf->busy = 1; + nf->omode = -1; + nf->file = f->file; + incref(f->file); + nf->user = estrdup(f->user); + return nf; +} + +static int +rwalk(Rpc *rpc) +{ + File *file; + char *name; + Cfid *fid, *nfid; + Qid q; + char *err; + int i; + + nfid = nil; + rpc->r.nwqid = 0; + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + if(fid->omode >= 0) + return p9error(rpc, Eisopen); + if(rpc->t.newfid != NOFID){ + nfid = clone(fid, rpc->t.newfid); + if(nfid == nil) + return p9syserror(rpc); + fid = nfid; /* walk the new fid */ + } + file = fid->file; + err = nil; + incref(file); /* temp. ref used during walk */ + if(rpc->t.nwname > 0){ + for(i=0; it.nwname && it.wname[i]; + if(!perm(file, fid->user, AEXEC)){ + err = Eperm; + break; + } + if(cwalkfile(&file, name) < 0){ + err = Enotexist; + break; + } + rpc->r.nwqid++; + rpc->r.wqid[i] = q; + } + if(i==0 && err == nil) + err = Enotexist; + } + if(nfid != nil && (err!=nil || rpc->r.nwqid < rpc->t.nwname)){ + /* clunk the new fid, which is the one we walked */ + nfid->busy = 0; + cputfile(nfid->file); + nfid->file = nil; + } + if(rpc->r.nwqid > 0) + err = nil; /* didn't get everything in 9P2000 right! */ + if(rpc->r.nwqid == rpc->t.nwname){ + /* update the fid after a successful walk */ + cputfile(fid->file); + fid->file = file; + }else + cputfile(file); /* temp ref used during walk */ + if(err != nil) + return p9error(rpc, err); + return p9reply(rpc); +} + +static int +ropen(Rpc *rpc) +{ + Cfid *fid; + int mode, omode; + File *file; + Dir *d; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + if(fid->omode >= 0) + return p9error(rpc, Eisopen); + file = fid->file; + d = cstatfile(file, 0); + mode = rpc->t.mode; + if(d->qid.type & QTDIR){ + if(mode != OREAD) + return p9error(rpc, Eperm); + rpc->r.qid = d->qid; + free(d); + return 0; + } + if((d->mode&DMEXCL) && file->nopens){ + free(d); + return p9error(rpc, Excl); + } + free(d); + if(mode & ORCLOSE){ + /* can't remove root; must be able to write parent */ + if(file->parent == nil || !perm(file, fid->user, AWRITE)) + return p9error(rpc, Eperm); + } + omode = mode&3; + if(omode != OREAD || (mode&OTRUNC)) + if(!perm(file, fid->user, AWRITE)) + return p9error(rpc, Eperm); + if(omode != OWRITE) + if(!perm(file, fid->user, AREAD)) + return p9error(rpc, Eperm); + fid->fd = copenfile(file, mode); + if(fid->fd < 0) + return p9syserror(rpc); + fid->omode = mode; + rpc->r.qid = fileqid(file); + rpc->r.iounit = msz - Chhdrsz - IOHDRSZ; + return p9reply(rpc); +} + + +static int +rcreate(Rpc *rpc) +{ + Cfid *fid; + File *file; + Dir *d; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + if(fid->omode >= 0) + return p9error(rpc, Eisopen); + file = fid->file; + d = cstatfile(file, 0); + if((d->qid.type&QTDIR) == 0){ + free(d); + return p9error(rpc, Enotdir); + } + /* must be able to write parent */ + if(!perm(file, fid->user, AWRITE)){ + free(d); + return p9error(rpc, Eperm); + } + rpc->t.perm &= d->mode; + free(d); + fid->fd = ccreatefile(&file, rpc->t.name, rpc->t.mode, rpc->t.perm); + if(fid->fd < 0) + return p9syserror(rpc); + fid->omode = rpc->t.mode; + fid->file = file; + + rpc->r.qid = fileqid(file); + rpc->r.iounit = msz-Chhdrsz-IOHDRSZ; + return p9reply(rpc); +} + +static int +rread(Rpc *rpc) +{ + Cfid *fid; + File *file; + Io *io; + long fcsz, nr; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + if(fid->omode < 0) + return p9error(rpc, Enotopen); + if((fid->omode&3) == OWRITE) + return p9error(rpc, "file not open for reading"); + file = fid->file; + + io = rpc->m->io; + ioreset(io); + /* + * read the data directly in the place where convS2M will + * copy it (it should not do the memmove in that case). + */ + rpc->r.count = 0; + fcsz = sizeS2M(&rpc->r); + rpc->r.data = (char*)io->wp+fcsz; + if(rpc->t.count > IOCAP(io)) + rpc->t.count = IOCAP(io); + if(rpc->t.count > msz) + rpc->t.count = msz; + nr = cpreadfile(file, fid->fd, rpc->r.data, rpc->t.count, rpc->t.offset); + if(nr < 0) + return p9syserror(rpc); + rpc->r.count = nr; + return p9reply(rpc); +} + +static int +rwrite(Rpc *rpc) +{ + Cfid *fid; + File *file; + long nw; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + if(fid->omode < 0) + return p9error(rpc, Enotopen); + if((fid->omode&3) == OREAD) + return p9error(rpc, "file not open for writing"); + file = fid->file; + + nw = cpwritefile(file, fid->fd, rpc->t.data, rpc->t.count, rpc->t.offset); + if(nw < 0) + return p9syserror(rpc); + rpc->r.count = nw; + return p9reply(rpc); +} + +static int +rclunk(Rpc *rpc) +{ + Cfid *fid; + File *file; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + file = fid->file; + if(fid->omode >= 0){ + cclosefile(file, fid->fd); + fid->fd = -1; + if(fid->omode&ORCLOSE) + cremovefile(file); + fid->omode = 0; + } + cputfile(file); + fid->file = nil; + return p9reply(rpc); +} + +static int +rremove(Rpc *rpc) +{ + Cfid *fid; + File *file; + int rc; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + file = fid->file; + if(fid->omode >= 0){ + cclosefile(file, fid->fd); + fid->fd = -1; + fid->omode = 0; + } + if(file->parent != nil && perm(file->parent, fid->user, AWRITE)) + rc = cremovefile(file); + else{ + werrstr(Eperm); + rc = -1; + } + cputfile(file); + fid->file = nil; + if(rc < 0) + return p9syserror(rpc); + else + return p9reply(rpc); +} + +static int +rstat(Rpc *rpc) +{ + Cfid *fid; + File *file; + Dir *d; + uchar buf[512]; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + file = fid->file; + d = cstatfile(file, 1); + rpc->r.nstat = convD2M(d, buf, sizeof buf); + rpc->r.stat = buf; + free(d); + return p9reply(rpc); +} + +static int +rwstat(Rpc *rpc) +{ + Cfid *fid; + File *file; + Dir wd, *d; + char buf[512]; + + fid = newfid(rpc->t.fid, 0); + if(fid == nil) + return p9error(rpc, Enofid); + file = fid->file; + d = cstatfile(file, 0); + convM2D(rpc->t.stat, rpc->t.nstat, &wd, buf); + + + /* + * To change length, must have write permission on file. + */ + if(wd.length!=~0 && d->length!=wd.length) + if(!perm(file, fid->user, AWRITE)){ + free(d); + return p9error(rpc, Eperm); + } + + /* + * To change name, must have write permission in parent + * and name must be unique. + */ + if(wd.name[0]!='\0' && strcmp(wd.name, d->name)!=0) + if(!file->parent || !perm(file->parent, fid->user, AWRITE)){ + free(d); + return p9error(rpc, Eperm); + } + + /* + * To change mode, must be owner or group leader. + * Because of lack of users file, leader=>group itself. + */ + if(wd.mode!=~0 && wd.mode!=d->mode && + strcmp(fid->user, d->uid) != 0 && strcmp(fid->user, d->gid) != 0){ + free(d); + return p9error(rpc, Enotowner); + } + + /* + * To change group, must be owner and member of new group, + * or leader of current group and leader of new group. + */ + if(wd.gid[0]!='\0' && strcmp(wd.gid, d->gid)!=0 && + strcmp(fid->user, d->uid) != 0){ + free(d); + return p9error(rpc, Enotowner); + } + free(d); + /* all ok; do it */ + if(cwstatfile(file, &wd) < 0) + return p9syserror(rpc); + return p9reply(rpc); +} + +static void +p9io(int fd) +{ + char buf[40]; + int n; + Rpc rpc; + + for(;;){ + /* + * reading from a pipe or a network device + * will give an error after a few eof reads. + * however, we cannot tell the difference + * between a zero-length read and an interrupt + * on the processes writing to us, + * so we wait for the error. + */ + memset(&rpc, 0, sizeof rpc); + rpc.m = newmsg(pool); + rpc.fd = fd; + n = read9pmsg(fd, rpc.m->io->wp, IOCAP(rpc.m->io)); + if(n < 0){ + rerrstr(buf, sizeof buf); + if(buf[0]=='\0' || strstr(buf, "hungup")) + exits(""); + sysfatal("mount read: %r"); + } + if(n == 0){ + freemsg(rpc.m); + continue; + } + rpc.m->io->wp += n; + if(convM2S(rpc.m->io->rp, IOLEN(rpc.m->io), &rpc.t) == 0){ + freemsg(rpc.m); + continue; + } + dfprint("-9-> %F\n", &rpc.t); + + if(rpc.t.type>=nelem(fcalls) || fcalls[rpc.t.type] == nil){ + if(p9error(&rpc, "bad fcall type") < 0) + sysfatal("p9error: %r"); + continue; + } + rpc.r.tag = rpc.t.tag; + rpc.r.type = rpc.t.type+1; + if(fcalls[rpc.t.type](&rpc) < 0) + sysfatal("fscalls[%d]: %r", rpc.t.type); + dfprint("<-9- %F\n", &rpc.r); + } +} + +static void +fsproc(void *a) +{ + int *p; + + p = a; + close(p[1]); + fileinit(".", 1); + pool = newpool(Msgsz, Nmsgs); + spool = newpool(Smsgsz, Nmsgs); + startses(ses, pool, spool); + cmux = muxses(ses->rc, ses->wc, ses->ec); + cfileinit(cmux); + p9io(p[0]); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [-D flags] [-s] [-m mnt] [-S srv] dir addr\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *defmnt, *service, *addr, *flags; + int p[2]; + int fd; + + service = "ix"; + defmnt = "/n/ix"; + + ARGBEGIN{ + case 'm': + defmnt = EARGF(usage()); + break; + case 's': + defmnt = 0; + break; + case 'D': + flags = EARGF(usage()); + for(;*flags != 0; flags++) + dbg[*flags]++; + dbg['d']++; + break; + case 'S': + defmnt = 0; + service = EARGF(usage()); + break; + default: + usage(); + }ARGEND + if(argc != 2) + usage(); + cdir = cleanname(argv[0]); + addr = argv[1]; + if(chdir(cdir) < 0) + sysfatal("%s: %r", cdir); + if(pipe(p) < 0) + sysfatal("pipe failed: %r"); + if(defmnt == 0){ + char buf[64]; + snprint(buf, sizeof buf, "#s/%s", service); + fd = create(buf, OWRITE|ORCLOSE, 0666); + if(fd < 0) + sysfatal("create failed: %r"); + sprint(buf, "%d", p[0]); + if(write(fd, buf, strlen(buf)) < 0) + sysfatal("writing service file: %r"); + } + + notify(notifyf); + + fmtinstall('G', fscallfmt); + fmtinstall('F', fcallfmt); + fmtinstall('D', shortdirfmt); + fmtinstall('M', dirmodefmt); + fmtinstall('T', filefmt); + + ses = dialsrv(addr); + if(ses == nil) + sysfatal("dialsrv: %r"); + procrfork(fsproc, p, Stack, RFFDG|RFNAMEG|RFNOTEG); + close(p[0]); /* don't deadlock if child fails */ + if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) + sysfatal("mount failed: %r"); + threadexits(0); +} + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/ixget.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/ixget.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,506 @@ +/* + * Test program for ix. + * Get files or directories. + * + X/\[/D + ! mk all + ! slay 8.ix 8.ixget|rc + ! slay 8.ixget|rc + ! 8.ix -Dfn + ! 8.ix -Dfsn >/tmp/LOG >[2=1] & + tail -F /tmp/LOG & + >/tmp/SRV.out >[2=1] + ! ls -lq /usr/nemo/acme.dump /tmp/acme.dump + ! cd /tmp ; /usr/nemo/src/tram/8.ixget -fDsf acme.dump >[2=1] + ! cd /tmp ; /usr/nemo/src/tram/8.ixget -cDsf acme.dump>[2=1] + >/tmp/GET.out>[2=1] + ! cd /tmp ; /usr/nemo/src/tram/8.ixget -Df acme.dump + ! touch $home/acme.dump + >/tmp/PUT.out>[2=1] + ! tstack 8.ix + ! tstack 8.ixget + ! leak -s 8.ix|rc|acid 8.ix + ! leak -s 8.ixget|rc|acid 8.ixget + ! unmount /tmp/acme.dump /usr/nemo/acme.dump + ! cmp /tmp/ohist /usr/nemo/ohist + ! cmp /tmp/adump /usr/nemo/adump + ! rm -f /tmp/^(acme.dump PULL ohist) /usr/nemo/adump + * + * Issues: + * - test unexpected server errors + * - test unexpected client errors + * - adapt to test cond + * e.g.: get if newer, get if vers newer + * and clunk + */ + +#include +#include +#include +#include +#include + +#include "conf.h" +#include "msg.h" +#include "mpool.h" +#include "tses.h" +#include "ch.h" +#include "dbg.h" +#include "fs.h" +#include "ixreqs.h" + +#define CLIADDR "tcp!localhost!9999" + +int mainstacksize = Stack; + +static Cmux *cm; +static Ses *ses; +Mpool *pool, *spool; +static Channel *wc; +static int rootfid = -1; +static ulong msz; +static int doflush; +static int twritehdrsz; + +static long +wdata(int fd, void *a, long cnt, uvlong off) +{ + + return pwrite(fd, a, cnt, off); +} + +static long +wdent(int fd, void *a, long cnt, uvlong) +{ + Dir d; + int n; + char buf[512]; + uchar *data; + long tot; + + data = a; + for(tot = 0; tot < cnt; tot += n){ + n = convM2D(data+tot, cnt-tot, &d, buf); + if(n <= BIT32SZ) + break; + fprint(fd, "%D\n", &d); + } + return cnt; +} + +/* + * If doflush, this will test a flush (abortch) before + * retrieving all data. + * Because ch does not convey more data to the receiver when it + * enters the flushing state, we can only flush when we are sure + * regarding the state of the server (i.e., if we sent a clone, + * should either clone OCERR|OCEND or wait until receiving the Rclone, + * to learn of the allocated fid, before flushing). + */ +static void +getproc(void *a) +{ + char *path, *els[64], *fn; + int nels, fd, i; + Ch *ch; + Dir d; + char buf[512]; + long nr; + uvlong offset; + Msg *m; + + path = a; + threadsetname("getproc %s", path); + if(*path == '/') + path++; + path = estrdup(path); + nels = getfields(path, els, nelem(els), 1, "/"); + if(nels < 1) + sysfatal("short path"); + fn = els[nels-1]; + ch = newch(cm); + xtfid(ch, rootfid, 0); + xtclone(ch, OCEND|OCERR, 0); + for(i = 0; i < nels; i++) + xtwalk(ch, els[i], 0); + xtstat(ch, 0); + xtopen(ch, OREAD, 0); + xtread(ch, -1, 0ULL, msz, 1); + /* fid automatically clunked on errors and eof */ + + fd = -1; + if(xrfid(ch) < 0){ + fprint(2, "%s: fid: %r\n", a); + goto Done; + } + if(xrclone(ch) < 0){ + fprint(2, "%s: clone: %r\n", a); + goto Done; + } + for(i = 0; i < nels; i++) + if(xrwalk(ch, nil) < 0){ + fprint(2, "%s: walk[%s]: %r\n", a, els[i]); + goto Done; + } + if(xrstat(ch, &d, buf) < 0){ + fprint(2, "%s: stat: %r\n", a); + goto Done; + } + dtprint("get: %D\n", &d); + remove(fn); /* ignore errors */ + fd = create(fn, OWRITE, d.mode&~DMDIR); + if(fd < 0){ + fprint(2, "create %s: %r\n", fn); + goto Done; + } + if(xropen(ch) < 0){ + fprint(2, "%s: open: %r\n", a); + goto Done; + } + offset = 0ULL; + do{ + if(doflush){ /* flush testing */ + doflush = 0; + abortch(ch); + goto Done; + } + m = xrread(ch); + if(m == nil){ + fprint(2, "%s: read: %r\n", a); + goto Done; + } + nr = IOLEN(m->io); + if(nr > 0) + if(d.qid.type&QTDIR) + nr = wdent(fd, m->io->rp, nr, offset); + else + nr = wdata(fd, m->io->rp, nr, offset); + offset += nr; + freemsg(m); + }while(nr > 0); + close(fd); + fd = -1; + +Done: + if(fd >= 0){ + close(fd); + remove(fn); + } + sendul(wc, 0); + free(path); + threadexits(nil); + close(fd); +} + +static void +condproc(void *a) +{ + char *path, *els[64], *fn; + int nels, fd, i; + Ch *ch; + Dir d; + char buf[512]; + long nr; + uvlong offset; + Dir *ld; + Msg *m; + + path = a; + threadsetname("condproc %s", path); + if(*path == '/') + path++; + path = estrdup(path); + nels = getfields(path, els, nelem(els), 1, "/"); + if(nels < 1) + sysfatal("short path"); + fn = els[nels-1]; + ld = dirstat(fn); + if(ld == nil) + sysfatal("%s: dirstat: %r", fn); + ch = newch(cm); + xtfid(ch, rootfid, 0); + xtclone(ch, OCEND|OCERR, 0); + for(i = 0; i < nels; i++) + xtwalk(ch, els[i], 0); + nulldir(&d); + d.qid = ld->qid; + xtcond(ch, CNE, &d, 0); + free(ld); + xtstat(ch, 0); + xtopen(ch, OREAD, 0); + xtread(ch, -1, 0ULL, msz, 1); + /* fid automatically clunked on errors and eof */ + + fd = -1; + if(xrfid(ch) < 0){ + fprint(2, "%s: fid: %r\n", a); + goto Done; + } + if(xrclone(ch) < 0){ + fprint(2, "%s: clone: %r\n", a); + goto Done; + } + for(i = 0; i < nels; i++) + if(xrwalk(ch, nil) < 0){ + fprint(2, "%s: walk[%s]: %r\n", a, els[i]); + goto Done; + } + if(xrcond(ch) < 0){ + fprint(2, "%s: cond: %r\n", a); + goto Done; + } + if(xrstat(ch, &d, buf) < 0){ + fprint(2, "%s: stat: %r\n", a); + goto Done; + } + dtprint("get: %D\n", &d); + remove(fn); /* ignore errors */ + fd = create(fn, OWRITE, d.mode&~DMDIR); + if(fd < 0){ + fprint(2, "create %s: %r\n", fn); + goto Done; + } + if(xropen(ch) < 0){ + fprint(2, "%s: open: %r\n", a); + goto Done; + } + offset = 0ULL; + do{ + m = xrread(ch); + if(m == nil){ + fprint(2, "%s: read: %r\n", a); + goto Done; + } + nr = IOLEN(m->io); + if(nr > 0) + if(d.qid.type&QTDIR) + nr = wdent(fd, m->io->rp, nr, offset); + else + nr = wdata(fd, m->io->rp, nr, offset); + offset += nr; + freemsg(m); + }while(nr > 0); + close(fd); + fd = -1; + +Done: + if(fd >= 0){ + close(fd); + remove(fn); + } + sendul(wc, 0); + free(path); + threadexits(nil); + close(fd); +} +static void +settwritehdrsz(void) +{ + Fscall t; + + t.type = Twrite; + twritehdrsz = packedsize(&t); +} + +static void +putproc(void *a) +{ + char *path, *els[64], *fn; + int nels, fd, i, nw; + Ch *ch; + Dir *d; + long nr; + Msg *m; + uvlong offset; + + path = a; + threadsetname("putproc %s", path); + settwritehdrsz(); + if(*path == '/') + path++; + path = estrdup(path); + nels = getfields(path, els, nelem(els), 1, "/"); + if(nels < 1) + sysfatal("short path"); + fn = els[nels-1]; + fd = open(fn, OREAD); + if(fd < 0) + sysfatal("%s: %r", fn); + d = dirfstat(fd); + if(d == nil) + sysfatal("%s: %r", fn); + ch = newch(cm); + xtfid(ch, rootfid, 0); + xtclone(ch, OCEND|OCERR, 0); + for(i = 0; i < nels - 1; i++) + xtwalk(ch, els[i], 0); + if(d->qid.type&QTDIR) + xtcreate(ch, els[nels-1], OREAD, d->mode, 1); + else + xtcreate(ch, els[nels-1], OWRITE, d->mode, 0); + /* fid automatically clunked on errors and eof */ + + if(xrfid(ch) < 0){ + fprint(2, "%s: fid: %r\n", a); + closech(ch); + goto Done; + } + if(xrclone(ch) < 0){ + fprint(2, "%s: clone: %r\n", a); + closech(ch); + goto Done; + } + for(i = 0; i < nels-1; i++) + if(xrwalk(ch, nil) < 0){ + fprint(2, "%s: walk[%s]: %r\n", a, els[i]); + closech(ch); + goto Done; + } + if(xrcreate(ch) < 0){ + fprint(2, "%s: create: %r\n", a); + closech(ch); + goto Done; + } + if((d->qid.type&QTDIR) == 0){ + nw = 0; + offset = 0ULL; + for(;;){ + m = newmsg(pool); + nr = IOCAP(m->io) - Chhdrsz - twritehdrsz; + m->io->wp += twritehdrsz; + nr = read(fd, m->io->wp, nr); + if(nr <= 0){ + if(nr < 0) + fprint(2, "%s: read: %r", fn); + xtclunk(ch, 1); + break; + } + m->io->wp += nr; + if(xtwrite(ch, m, nr, offset, 0) < 0){ + closech(ch); + drainch(ch); + goto Done; + } + nw++; + offset += nr; + + /* read replies so that no more than + * 10 outstanding writes are going. + */ + if(nw > 10){ + nw--; + if(xrwrite(ch) < 0){ + fprint(2, "%s: write: %r\n", a); + closech(ch); + goto Done; + } + } + } + while(nw-- > 0) + if(xrwrite(ch) < 0){ + fprint(2, "%s: write: %r\n", a); + closech(ch); + goto Done; + } + xrclunk(ch); + } +Done: + if(fd >= 0) + close(fd); + sendul(wc, 0); + free(path); + threadexits(nil); + close(fd); +} + +static void +ixattach(char *user) +{ + Ch *ch; + + ch = newch(cm); + xtversion(ch, 0); + xtattach(ch, user, "main", 1); + if(xrversion(ch, &msz) < 0) + sysfatal("wrong ix version: %r"); + if(xrattach(ch, &rootfid) < 0) + sysfatal("can't attach: %r"); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [-fhpc] [-D flags] [-m msz] [-a addr] file...\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *addr, *flags; + int i, dosleep, doput, docond; + + addr = CLIADDR; + dosleep = doput = docond = 0; + ARGBEGIN{ + case 'D': + flags = EARGF(usage()); + for(;*flags != 0; flags++) + dbg[*flags]++; + dbg['d']++; + break; + case 'a': + addr = EARGF(usage()); + break; + case 'm': + msz = atoi(EARGF(usage())); + if(msz < 1) + sysfatal("message size too short"); + break; + case 's': + dosleep = 1; + break; + case 'f': + doflush = 1; + break; + case 'p': + doput = 1; + break; + case 'c': + docond = 1; + break; + default: + usage(); + }ARGEND; + if(argc == 0 || doput + docond + doflush> 1) + usage(); + fmtinstall('G', fscallfmt); + fmtinstall('M', dirmodefmt); + fmtinstall('D', dirfmt); + ses = dialsrv(addr); + if(ses == nil) + sysfatal("dialsrv: %r"); + pool = newpool(Msgsz, argc*Nmsgs); + spool = newpool(Smsgsz, argc*Nmsgs); + startses(ses, pool, spool); + cm = muxses(ses->rc, ses->wc, ses->ec); + ixattach(getuser()); + wc = chancreate(sizeof(ulong), argc); + for(i = 0; i < argc; i++) + if(doput) + proccreate(putproc, argv[i], Stack); + else if(docond) + proccreate(condproc, argv[i], Stack); + else + proccreate(getproc, argv[i], Stack); + for(i = 0; i < argc; i++) + recvul(wc); + chanfree(wc); + closemux(cm); + if(dosleep){ + print("%s: sleeping forever\n", argv0); + for(;;) + sleep(3600); + } + threadexits(nil); +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/ixreqs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/ixreqs.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,457 @@ +#include +#include +#include +#include +#include + +#include "conf.h" +#include "msg.h" +#include "mpool.h" +#include "tses.h" +#include "ch.h" +#include "dbg.h" +#include "fs.h" +#include "ixreqs.h" + +static int +sendreq(Ch *ch, Fscall *t, Msg *m, int last) +{ + int n; + + /* + * Twrite reads data directly after the packed T call, + * and sets io's rp and wp by itself. + * Requests using strings like Tattach, Twalk, may not + * fit in a small Msg if strings are large, in which case + * we try using a large buffer before failing. + */ + dfprint("%cch%d-> %G\n", last?'|':'-', ch->id, t); + if(t->type != Twrite){ + n = pack(t, m->io->wp, IOCAP(m->io)); + if(n < 0 && IOCAP(m->io) < Msgsz){ + /* try with a large buffer */ + freemsg(m); + m = newmsg(pool); + n = pack(t, m->io->wp, IOCAP(m->io)); + } + }else + n = pack(t, m->io->bp, packedsize(t)); + if(n <= 0) + sysfatal("sendreq: pack"); + if(t->type != Twrite) + m->io->wp += n; + return chsend(ch, m, last); +} + + +int +xtversion(Ch *ch, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tversion; + t.msize = Msgsz; + t.version = "ix"; + return sendreq(ch, &t, m, last); +} + +int +xtattach(Ch *ch, char *uname, char *aname, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tattach; + t.afid = -1; + t.uname = uname; + t.aname = aname; + return sendreq(ch, &t, m, last); +} + +int +xtfid(Ch *ch, int fid, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tfid; + t.fid = fid; + t.cflags = 0; + return sendreq(ch, &t, m, last); +} + +int +xtclone(Ch *ch, int cflags, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tclone; + t.cflags = cflags; + return sendreq(ch, &t, m, last); +} + +int +xtclunk(Ch *ch, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tclunk; + return sendreq(ch, &t, m, last); +} + +int +xtremove(Ch *ch, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tremove; + return sendreq(ch, &t, m, last); +} + +int +xtwalk(Ch *ch, char *elem, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Twalk; + t.wname = elem; + return sendreq(ch, &t, m, last); +} + + +int +xtstat(Ch *ch, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tstat; + return sendreq(ch, &t, m, last); +} + +int +xtopen(Ch *ch, int mode, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Topen; + t.mode = mode; + return sendreq(ch, &t, m, last); +} + +int +xtread(Ch *ch, long count, uvlong offset, long msz, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tread; + t.offset = offset; + t.count = count; + if(msz != 0 && t.count > msz) + t.count = msz; + t.nmsg = -1; + return sendreq(ch, &t, m, last); +} + +int +xtcreate(Ch *ch, char *name, int mode, int perm, int last) +{ + Fscall t; + Msg *m; + + m = newmsg(spool); + t.type = Tcreate; + t.name = name; + t.perm = perm; + t.mode = mode; + return sendreq(ch, &t, m, last); +} + +int +xtwrite(Ch *ch, Msg *m, long count, uvlong offset, int last) +{ + Fscall t; + + t.type = Twrite; + t.offset = offset; + t.count = count; + return sendreq(ch, &t, m, last); +} + +int +xtcond(Ch *ch, int op, Dir *d, int last) +{ + Fscall t; + Msg *m; + uchar buf[512]; + + m = newmsg(pool); + t.type = Tcond; + t.cond = op; + t.nstat = convD2M(d, buf, sizeof buf); + t.stat = buf; + return sendreq(ch, &t, m, last); +} + +int +xtwstat(Ch *ch, Dir *d, int last) +{ + Fscall t; + Msg *m; + uchar buf[512]; + + m = newmsg(pool); + t.type = Twstat; + t.nstat = convD2M(d, buf, sizeof buf); + t.stat = buf; + return sendreq(ch, &t, m, last); +} + +static Msg* +getreply(Ch *ch, int type, Fscall *r) +{ + Msg *m; + int last, id, n; + + id = ch->id; /* ch is released if last */ + m = chrecv(ch, &last); + if(m == nil){ + werrstr("eof"); + dfprint("getreply: %r\n"); + return nil; + } + n = unpack(m->io->rp, IOLEN(m->io), r); + if(n <= 0){ + werrstr("getreply: rp %#p wp %#p ep %#p %r", m->io->rp, m->io->wp, m->io->ep); + dfprint("getreply: %r\n"); + freemsg(m); + return nil; + } + m->io->rp += n; + dfprint("<-ch%d%c %G\n", id, last?'|':'-', r); + if(r->type == type) + return m; + if(r->type == Rerror) + werrstr("%s", r->ename); + else + werrstr("wrong reply type %d", r->type); + freemsg(m); + return nil; +} + +int +xrversion(Ch *ch, ulong *mszp) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rversion, &r); + if(m == nil) + return -1; + if(strcmp(r.version, "ix") != 0){ + werrstr("wrong version %s", r.version); + freemsg(m); + return -1; + } + if(mszp != nil && (*mszp == 0 || r.msize < *mszp)){ + *mszp = r.msize; + dfprint("msize %uld\n", *mszp); + } + freemsg(m); + return 0; +} + +int +xrattach(Ch *ch, int *fidp) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rattach, &r); + if(m == nil) + return -1; + *fidp = r.fid; + freemsg(m); + return 0; +} + +int +xrfid(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rfid, &r); + if(m == nil) + return -1; + freemsg(m); + return 0; +} + +int +xrclunk(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rclunk, &r); + if(m == nil) + return -1; + freemsg(m); + return 0; +} + +int +xrremove(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rremove, &r); + if(m == nil) + return -1; + freemsg(m); + return 0; +} + +int +xrclone(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rclone, &r); + if(m == nil) + return -1; + freemsg(m); + return r.fid; +} + +int +xrwalk(Ch *ch, Qid *qp) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rwalk, &r); + if(m == nil) + return -1; + freemsg(m); + if(qp != nil) + *qp = r.wqid; + return 0; +} + +int +xropen(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Ropen, &r); + if(m == nil) + return -1; + freemsg(m); + return 0; +} + +int +xrstat(Ch *ch, Dir *d, char buf[]) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rstat, &r); + if(m == nil) + return -1; + if(convM2D(r.stat, r.nstat, d, buf) <= BIT32SZ) + return -1; + freemsg(m); + return 0; +} + + +Msg* +xrread(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rread, &r); + if(m == nil) + return nil; + m->io->rp -= r.count; + return m; +} + +int +xrcreate(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rcreate, &r); + if(m == nil) + return -1; + freemsg(m); + return 0; +} + +long +xrwrite(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rwrite, &r); + if(m == nil) + return -1; + freemsg(m); + return r.count; +} + +int +xrcond(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rcond, &r); + if(m == nil) + return -1; + freemsg(m); + return 0; +} + +int +xrwstat(Ch *ch) +{ + Msg *m; + Fscall r; + + m = getreply(ch, Rwstat, &r); + if(m == nil) + return -1; + freemsg(m); + return 0; +} + + + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/ixreqs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/ixreqs.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,33 @@ + + +/* |c/f2p ixreqs.c */ +extern int xrattach(Ch *ch, int *fidp); +extern int xrclone(Ch *ch); +extern int xrclunk(Ch *ch); +extern int xrcond(Ch *ch); +extern int xrcreate(Ch *ch); +extern int xrfid(Ch *ch); +extern int xropen(Ch *ch); +extern Msg* xrread(Ch *ch); +extern int xrremove(Ch *ch); +extern int xrstat(Ch *ch, Dir *d, char buf[]); +extern int xrversion(Ch *ch, ulong *mszp); +extern int xrwalk(Ch *ch, Qid *qp); +extern long xrwrite(Ch *ch); +extern int xrwstat(Ch *ch); +extern int xtattach(Ch *ch, char *uname, char *aname, int last); +extern int xtclone(Ch *ch, int cflags, int last); +extern int xtclunk(Ch *ch, int last); +extern int xtcond(Ch *ch, int op, Dir *d, int last); +extern int xtcreate(Ch *ch, char *name, int mode, int perm, int last); +extern int xtfid(Ch *ch, int fid, int last); +extern int xtopen(Ch *ch, int mode, int last); +extern int xtread(Ch *ch, long count, uvlong offset, long msz, int last); +extern int xtremove(Ch *ch, int last); +extern int xtstat(Ch *ch, int last); +extern int xtversion(Ch *ch, int last); +extern int xtwalk(Ch *ch, char *elem, int last); +extern int xtwrite(Ch *ch, Msg *m, long count, uvlong offset, int last); +extern int xtwstat(Ch *ch, Dir *d, int last); + +extern Mpool *pool, *spool; diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/mkfile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/mkfile Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,44 @@ + +#include +#include /* chancreate */ +#include +#include +#include "conf.h" +#include "msg.h" +#include "mpool.h" +#include "dbg.h" + +static Mbuf *bufs; +static Lock bufslck; +static int nbufs; +static int nmaxbufs; + +void +freepoolmsg(Msg *m) +{ + int i; + Mbuf *mb; + Mpool *mp; + + mb = m->aux; + assert(&mb->m == m); + + dPprint("freepoolmsg %#p pc %#ulx\n", mb, getcallerpc(&m)); + mp = mb->mp; + mb->m.hdr = nil; + mb->m.io[0].bp = mb->buf; + mb->m.io[0].rp = mb->buf; + mb->m.io[0].wp = mb->buf; + mb->m.io[0].ep = mb->m.io[0].bp + mp->msize; + for(i = 1; i < nelem(mb->m.io); i++){ + mb->m.io[i].bp = nil; + mb->m.io[i].rp = nil; + mb->m.io[i].wp = nil; + mb->m.io[i].ep = nil; + } + sendp(mp->bc, mb); + lock(mp); + mp->nfree++; + mp->nfrees++; + unlock(mp); +} + +Mpool* +newpool(ulong msize, int nmsg) +{ + Mpool *mp; + Mbuf *mb; + int i; + + mp = emalloc(sizeof *mp); + mp->msize = msize; + mp->bc = echancreate(sizeof(Mbuf*), nmsg); + for(i = 0; i < nmsg; i++){ + mb = nil; + if(bufs != nil){ + lock(&bufslck); + if(bufs != nil){ + mb = bufs; + bufs = bufs->next; + nbufs--; + } + unlock(&bufslck); + } + if(mb == nil) + mb = malloc(sizeof(Mbuf) + msize); + if(mb == nil) + break; + memset(mb, 0, sizeof *mb); + mb->mp = mp; + mb->m.io[0].bp = mb->buf; + mb->m.io[0].rp = mb->buf; + mb->m.io[0].wp = mb->buf; + mb->m.io[0].ep = mb->buf + msize; + mb->m.aux = mb; + mb->m.free = freepoolmsg; + mp->nmsg++; + mp->nfree++; + sendp(mp->bc, mb); + } + if(i == 0){ + chanfree(mp->bc); + free(mp); + return nil; + } + return mp; +} + +void +poolstats(Mpool *mp) +{ + dprint("pool %p: nmsg %d nfree %d nminfree %d nallocs %uld nfrees %uld\n", + mp, mp->nmsg, mp->nfree, mp->nminfree, mp->nallocs, mp->nfrees); + dprint("nbufs %d nmaxbufs %d\n", nbufs, nmaxbufs); +} + +/* + * To free a pool we must collect all messages allocated + * from it. + * Note that the caller might have given a message to other process + * which might free it after freepool is called. + * e.g., a client may terminate a session and release its pool, but + * the session read process may be still terminating and holding + * its last message buffer. + */ +void +freepool(Mpool *mp) +{ + Mbuf *mb; + int i; + + if(mp == nil) + return; + if(mp->freepool != nil){ + mp->freepool(mp); + return; + } + for(i = 0; i < mp->nmsg; i++){ + mb = recvp(mp->bc); + lock(&bufslck); + mb->next = bufs; + mb->mp = nil; + mb->m.free = nil; + bufs = mb; + nbufs++; + if(nmaxbufs < nbufs) + nmaxbufs = nbufs; + unlock(&bufslck); + } + poolstats(mp); + chanfree(mp->bc); + memset(mp, 9, sizeof *mp); + free(mp); +} + +Msg* +newmsg(Mpool *mp) +{ + Mbuf *mb; + + if(mp == nil) + sysfatal("newmsg: nil pool"); + + mb = recvp(mp->bc); + setmalloctag(mb, getcallerpc(&mp)); + dPprint("newmsg %#p pc %#ulx\n", mb, getcallerpc(&mp)); + lock(mp); + mp->nfree--; + mp->nallocs++; + if(mp->nfree < mp->nminfree) + mp->nminfree = mp->nfree; + unlock(mp); + return &mb->m; +} + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/mpool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/mpool.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,32 @@ + +typedef struct Mpool Mpool; +typedef struct Mbuf Mbuf; + +struct Mbuf +{ + Msg m; + Mpool *mp; + Mbuf* next; + uchar buf[]; +}; + +struct Mpool +{ + Lock; /* stats */ + int nmsg; + ulong msize; + int nfree; + ulong nallocs; + ulong nfrees; + int nminfree; + + void (*freepool)(Mpool*); + Channel *bc; +}; + +/* |c/f2p mpool.c */ +extern void poolstats(Mpool *mp); +extern void freepool(Mpool *mp); +extern void freepoolmsg(Msg *m); +extern Msg* newmsg(Mpool *mp); +extern Mpool* newpool(ulong msize, int nmsg); diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/msg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/msg.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,225 @@ +/* + * Gather/scatter messages with protocol headers + */ +#include +#include +#include /* chancreate */ +#include +#include +#include "conf.h" +#include "msg.h" +#include "mpool.h" +#include "dbg.h" + +void +ioreset(Io *io) +{ + io->rp = io->bp; + io->wp = io->bp; +} + +static int +hdrlen(Msg *m) +{ + if(m->hdr == nil) + return 0; + return (m->hbuf + sizeof m->hbuf) - m->hdr; +} + +Channel* +echancreate(int elsz, int n) +{ + Channel *c; + + c = chancreate(elsz, n); + if(c == nil) + sysfatal("chancreate"); + setmalloctag(c, getcallerpc(&elsz)); + return c; +} + +long +msglen(Msg *m) +{ + Io *io; + long tot; + int i; + + tot = hdrlen(m); + for(i = 0; i < nelem(m->io); i++){ + io = &m->io[i]; + if(io->bp == nil) + break; + tot += IOLEN(io); + } + return tot; +} + +void* +msgpushhdr(Msg *m, long sz) +{ + if(m->hdr == nil) + m->hdr = m->hbuf + sizeof m->hbuf; + if(m->hdr - m->hbuf < sz){ + werrstr("no room for headers"); + return nil; + } + m->hdr -= sz; + return m->hdr; +} + +void* +msgpophdr(Msg *m, long sz) +{ + void *p; + + if(m->hdr != nil){ + if(hdrlen(m) < sz){ + werrstr("short header"); + return nil; + } + p = m->hbuf; + m->hdr += sz; + return p; + } + /* + * no headers in hbuf, probably they were read along with + * the body, take them from there. + */ + if(m->io[0].rp != nil && IOLEN(&m->io[0]) >= sz){ + p = m->io[0].rp; + m->io[0].rp += sz; + return p; + } + werrstr("short header"); + return nil; +} + +int +msgput(Msg *m, void *p, long sz) +{ + int i; + Io *io; + + for(i = 0; i < nelem(m->io); i++){ + io = &m->io[i]; + if(io->bp == nil) + break; + } + if(i == nelem(m->io)) + sysfatal("msgputtd: msg full"); + m->io[i].bp = p; + m->io[i].rp = p; + m->io[i].wp = m->io[i].rp + sz; + m->io[i].ep = m->io[i].wp; + return i; +} + +int +readmsg(int fd, Msg *m) +{ + uchar hdr[BIT16SZ]; + long nhdr, nr; + ulong sz; + + werrstr("eof"); + nhdr = readn(fd, hdr, sizeof hdr); + if(nhdr < 0){ + dmprint("readmsg: %r\n"); + return -1; + } + if(nhdr == 0){ + dmprint("readmsg: eof\n"); + return 0; + } + if(nhdr < sizeof hdr){ + werrstr("short header %r"); + return -1; + } + sz = GBIT16(hdr); + if(sz > IOCAP(&m->io[0])){ + /* + * Message too long. + * We could read it and skip it, but let's fail + * so the user knows. + */ + fprint(2, "readmsg: message too long (%uld > %d)\n", + sz, Msgsz); + werrstr("message too long"); + return -1; + } + nr = readn(fd, m->io->wp, sz); + dmprint("readmsg: %ld+%ld bytes sz %ld\n", nhdr, nr, sz); + if(nr != sz){ + werrstr("short msg data"); + return -1; + } + m->io->wp += nr; + return nr; +} + +long +writemsg(int fd, Msg *m) +{ + Io *io; + long len, hlen, iol; + uchar *hdr; + int i; + + len = msglen(m); + if(len > 0xFFFF){ + werrstr("message too long"); + return -1; + } + hdr = msgpushhdr(m, BIT16SZ); + if(hdr == nil) + return -1; + PBIT16(hdr, len); + hlen = hdrlen(m); + if(write(fd, hdr, hlen) != hlen) + goto Fail; + for(i = 0; i < nelem(m->io); i++){ + io = &m->io[i]; + if(io->bp == nil) + break; + iol = IOLEN(io); + if(write(fd, io->rp, iol) != iol) + goto Fail; + dmprint("writemsg: %ld bytes\n", iol); + } + msgpophdr(m, BIT16SZ); + return len; +Fail: + msgpophdr(m, BIT16SZ); + return -1; +} + +void +freemsg(Msg *m) +{ + Io *io; + int i; + + if(m == nil) + return; + if(m->free != nil){ + dPprint("freemsg pc %#p\n", getcallerpc(&m)); + m->free(m); + return; + } + for(i = 0; i < nelem(m->io); i++){ + io = &m->io[i]; + if(io->bp == nil) + break; + free(io->bp); + } + free(m); +} + +void +dumpmsg(Msg *m) +{ + print("msg %#p hdr %#p ehdr %#p bp %#p rp %#p wp %#p ep %#p\n", + m, m->hdr, m->hbuf + sizeof m->hbuf, + m->io->bp, m->io->rp, m->io->wp, m->io->ep); +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/msg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/msg.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,38 @@ +typedef struct Msg Msg; +typedef struct Io Io; + +enum +{ + Nio = 1, +}; + +struct Io +{ + uchar *bp; /* buffer pointer */ + uchar *rp; /* read pointer */ + uchar *wp; /* write pointer */ + uchar *ep; /* end pointer */ +}; + +struct Msg +{ + uchar hbuf[Hbufsz]; + uchar* hdr; /* headers go from hdr to &hbuf[Hdrsz] */ + Io io[Nio]; + void* aux; + void (*free)(Msg*); +}; + +#define IOLEN(io) ((io)->wp - (io)->rp) +#define IOCAP(io) ((io)->ep - (io)->wp) + +/* |c/f2p msg.c */ +extern Channel* echancreate(int elsz, int n); +extern void freemsg(Msg *m); +extern void ioreset(Io *io); +extern long msglen(Msg *m); +extern void* msgpophdr(Msg *m, long sz); +extern void* msgpushhdr(Msg *m, long sz); +extern int msgput(Msg *m, void *p, long sz); +extern int readmsg(int fd, Msg *m); +extern long writemsg(int fd, Msg *m); diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/nsfile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/nsfile.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,849 @@ +/* + * File tree kept in an external file system. + * Metadata is kept in an index file. + * External changes are detected. + * Suitable for use as a cache of a remote file tree. + */ + +#include +#include +#include +#include +#include +#include +#include "conf.h" +#include "file.h" +#include "dbg.h" + +static File *root; +static char *dir; +static int usedb; + +static char* +qidtype(char *s, uchar t) +{ + char *p; + + p = s; + if(t & QTDIR) + *p++ = 'd'; + if(t & QTAPPEND) + *p++ = 'a'; + if(t & QTEXCL) + *p++ = 'l'; + if(t & QTAUTH) + *p++ = 'A'; + *p = '\0'; + return s; +} + + +static void +fdirconv(char *buf, char *e, Dir *d) +{ + char tmp[16]; + + seprint(buf, e, "%s '%s' " + "(%llux %lud %s) %#luo l %lld mt %uld", d->name, + d->uid, + d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode, + d->length, d->mtime); +} + +int +shortdirfmt(Fmt *fmt) +{ + char buf[160]; + + fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*)); + return fmtstrcpy(fmt, buf); +} + +Dir* +dupdir(Dir *d) +{ + int sz; + Dir *nd; + char *s; + + sz = strlen(d->name) + strlen(d->uid) + + strlen(d->gid) + strlen(d->muid) + 4; + nd = emalloc(sizeof *nd + sz); + *nd = *d; + nd->name = (char*)(nd+1); + s = strecpy(nd->name, nd->name+sz, d->name); + nd->uid = s+1; + s = strecpy(nd->uid, nd->name+sz, d->uid); + nd->gid = s+1; + s = strecpy(nd->gid, nd->name+sz, d->gid); + nd->muid = s+1; + strecpy(nd->muid, nd->name+sz, d->muid); + return nd; +} + +int +filefmt(Fmt *fmt) +{ + File *f; + Dir *sd; + char tmp[16]; + + f = va_arg(fmt->args, File*); + if(f == nil) + return fmtprint(fmt, ""); + if((sd = f->sd) != nil) + return fmtprint(fmt, "'%s' r=%ld sq (%llux %lud %s) %D", + f->path, f->ref, + sd->qid.path, sd->qid.vers, qidtype(tmp, sd->qid.type), + f->d); + else + return fmtprint(fmt, "'%s' r=%ld %D", + f->path, f->ref, f->d); +} + +static void +dumpfile(File *f) +{ + print("%T\n", f); + for(f = f->child; f != nil; f = f->next) + dumpfile(f); +} + +/* + * BEWARE: no locks. + */ +void +dumptree(void) +{ + dumpfile(root); +} + +static void +delchild(File *parent, File *f) +{ + File **fp; + + for(fp = &parent->child; *fp != nil; fp = &(*fp)->next) + if(*fp == f) + break; + if(*fp == nil) + sysfatal("delchild"); + *fp = f->next; + f->next = nil; + parent->nchild--; + f->parent = nil; +} + +static void +addchild(File *parent, File *f) +{ + assert(f->next == nil); + f->next = parent->child; + parent->child = f; + parent->nchild++; + f->parent = parent; +} + +File* +getchild(File *parent, char *name) +{ + File *f; + + for(f = parent->child; f != nil; f = f->next) + if(strcmp(f->d->name, name) == 0){ + incref(f); + return f; + } + return nil; +} + +void +putfile(File *f) +{ + File *parent; + + if(decref(f) > 0) + return; + parent = f->parent; + if(parent != nil){ + wlock(parent); + delchild(parent, f); + wunlock(parent); + } + while(f->child != nil) + putfile(f->child); + free(f->path); + free(f->d); + free(f); +} + +/* + * parent must be wlocked by caller. + */ +static void +unlinkfile(File *f) +{ + if(f->parent != nil) + delchild(f->parent, f); + putfile(f); +} + +/* + * keeps ptr to path within new file. + */ +File* +newfile(File *parent, char *path, Dir *d) +{ + File *f; + + if(d == nil){ + dnprint("dirstat %s (newfile)\n", path); + d = dirstat(path); + } + if(d == nil) + return nil; + + f = emalloc(sizeof *f); + f->ref = 1; + f->path = path; + if(parent != nil) + addchild(parent, f); + f->d = d; + return f; +} + +static Dir* +readdir(Biobuf *bin, char *p) +{ + static uchar buf[DIRMAX]; + ulong sz; + Dir *d; + + if(Bread(bin, buf, BIT16SZ) != BIT16SZ){ + fprint(2, "%s: %s: eof\n", argv0, p); + return nil; + } + sz = GBIT16(buf); + if(BIT16SZ + sz > sizeof buf){ + fprint(2, "%s: %s: dir too long\n", argv0, p); + return nil; + } + if(Bread(bin, buf + BIT16SZ, sz) != sz){ + fprint(2, "%s: %s: read failed\n", argv0, p); + return nil; + } + d = emalloc(sizeof *d + sz); + if(convM2D(buf, sizeof buf, d, (char*)(d+1)) <= 0){ + fprint(2, "%s: %s: convM2D failed\n", argv0, p); + free(d); + return nil; + } + return d; +} + +static File* +loaddir(Biobuf *bin, File *parent) +{ + File *f; + Dir *d, *sd; + int nchild; + char *p; + + p = "/"; + if(parent != nil) + p = parent->path; + d = readdir(bin, p); + if(d == nil) + return nil; + sd = readdir(bin, p); + if(sd == nil){ + free(d); + return nil; + } + f = emalloc(sizeof *f); + f->ref = 1; + f->d = d; + f->sd = sd; + if(parent == nil) + f->path = estrdup("."); + else{ + f->path = smprint("%s/%s", parent->path, f->d->name); + addchild(parent, f); + } + if(ISDIR(f)){ + nchild = f->d->length; + f->d->length = 0; + while(nchild-- > 0) + if(loaddir(bin, f) == nil) + break; + } + if(dbg['n'] > 1) + dnprint("loaded %T\n", f); + return f; +} + +static int +changed(Dir *d1, Dir *d2) +{ + if((d1->qid.type&QTDIR) != 0 && (d2->qid.type&QTDIR) != 0) + return 0; + return d1->length != d2->length || d1->mtime != d2->mtime || + d1->qid.path != d2->qid.path || d1->qid.vers != d2->qid.vers || + d1->qid.type != d2->qid.type; +} + +static int +mchanged(Dir *d1, Dir *d2) +{ + return d1->mode != d2->mode || strcmp(d1->uid, d2->uid) != 0 || + strcmp(d1->gid, d2->gid) != 0; +} + +char* +tmpfile(char *name) +{ + return esmprint("%s.!.", name); +} + +static int +ignorename(char *name) +{ + int len; + + if(strcmp(name, ".ixd") == 0 || strcmp(name, ".ixd.new") == 0) + return 1; + len = strlen(name); + if(len > 3 && strcmp(&name[len-3], ".!.") == 0) + return 1; + if(len > 3 && strcmp(&name[len-3], ".!!") == 0) + return 1; + return 0; +} + + +static int +scandir(File *f, Dir *ud, int recur) +{ + Dir *d, *cd; + File *cf; + int i, nd, fd, mustrecur; + char *s; + + wlock(f); + if(dbg['n']>1) + dnprint("scan %s\n", f->path); + d = ud; + if(d == nil){ + dnprint("dirstat %s (scandir)\n", f->path); + d = dirstat(f->path); + } + if(d == nil){ + wunlock(f); + return 0; + } + if((f->d->qid.type&QTDIR) != (d->qid.type&QTDIR)){ + /* a file became a dir or a dir became a file + * get rid of the old data and scan it according to + * its new nature. + */ + dnprint("gone and came: %T\n", f); + if(ISDIR(f)){ + while(f->child != nil){ + unlinkfile(f->child); + } + f->d->qid.type &= ~QTDIR; + }else + f->d->qid.type |= QTDIR; + } + if(mchanged(f->d, d) || changed(f->d, d)){ + free(f->d); + if(d == ud) + d = dupdir(ud); + f->d = d; + dnprint("changed: %T\n", f); + } + wunlock(f); + rlock(f); + if(ISDIR(f)){ + fd = open(f->path, OREAD); + if(fd < 0){ + runlock(f); + free(d); + return -1; + } + dnprint("dirreadall %s\n", f->path); + nd = dirreadall(fd, &cd); + close(fd); + for(i = 0; i < nd; i++){ + if(ignorename(cd[i].name)) + continue; + cf = getchild(f, cd[i].name); + if(cf == nil){ + mustrecur = 1; + s = smprint("%s/%s", f->path, cd[i].name); + cf = newfile(f, s, dupdir(&cd[i])); + if(cf == nil){ + fprint(2, "%s: %s: %r\n", argv0, s); + free(s); + continue; + } + dnprint("came: %T\n", cf); + }else{ + mustrecur = recur; + putfile(cf); /* ref won't be zero */ + } + cf->visited = 1; + if(mustrecur ||!ISDIR(cf)){ + runlock(f); + scandir(cf, &cd[i], mustrecur); + rlock(f); + } + } + runlock(f); + wlock(f); + Again: + for(cf = f->child; cf != nil; cf = cf->next) + if(cf->visited == 0){ + /* + * If it's dirty, and has been removed, + * there's nothing we can do. + * let the client notice. + */ + dnprint("gone: %T\n", cf); + incref(cf); + unlinkfile(cf); + putfile(cf); + goto Again; /* list has changed */ + } + for(cf = f->child; cf != nil; cf = cf->next) + cf->visited = 0; + wunlock(f); + free(cd); + }else + runlock(f); + return 1; +} + +static int +writedir(Biobuf *bout, File *f) +{ + File *cf; + static uchar buf[DIRMAX]; + int n; + Dir d; + + wlock(f); + if(ISDIR(f)) + f->d->length = f->nchild; + n = convD2M(f->d, buf, sizeof buf); + if(ISDIR(f)) + f->d->length = 0; + if(Bwrite(bout, buf, n) != n){ + wunlock(f); + fprint(2, "%s: writedir: %r\n", f->path); + return -1; + } + if(f->sd == nil){ + nulldir(&d); + n = convD2M(&d, buf, sizeof buf); + }else + n = convD2M(f->sd, buf, sizeof buf); + if(Bwrite(bout, buf, n) != n){ + wunlock(f); + fprint(2, "%s: writedir2: %r\n", f->path); + return -1; + } + if(dbg['n'] > 1) + dnprint("written: %T\n", f); + wunlock(f); + rlock(f); + for(cf = f->child; cf != nil; cf = cf->next) + if(writedir(bout, cf) < 0){ + runlock(f); + return -1; + } + runlock(f); + return 0; +} + +void +childmap(File *f, void(*fn)(File*)) +{ + File *cf; + + rlock(f); + for(cf = f->child; cf != nil; cf = cf->next) + fn(cf); + wunlock(f); +} + +static int +writetree(File *f) +{ + Biobuf *bout; + Dir d; + int rc; + + bout = Bopen(".ixd.new", OWRITE); + if(bout == nil){ + fprint(2, "%s: writetree: %r\n", argv0); + return -1; + } + rc = writedir(bout, f); + if(Bterm(bout) < 0) + rc = -1; + if(rc < 0){ + remove(".ixd.new"); + return -1; + } + nulldir(&d); + d.name = ".ixd"; + if(dirwstat(".ixd.new", &d) < 0){ + fprint(2, "%s: rename .ixd: %r\n", argv0); + remove(".ixd.new"); + return -1; + } + return 0; +} + +static File* +loadtree(void) +{ + Biobuf *bin; + File *f; + + dnprint("loadtree:\n"); + bin = Bopen(".ixd", OREAD); + if(bin != nil){ + f = loaddir(bin, nil); + Bterm(bin); + }else + f = newfile(nil, ".", nil); + if(scandir(f, nil, 1) <= 0) + sysfatal("loadtree: can't scan root"); + if(bin == nil) + writetree(f); + return f; +} + +File* +rootfile(void) +{ + incref(root); + return root; +} + +void +fileinit(char *path, int udb) +{ + if(chdir(path) < 0) + sysfatal("chdir %s: %r", path); + usedb = udb; + if(usedb) + root = loadtree(); + else{ + root = newfile(nil, ".", nil); + if(scandir(root, nil, 1) <= 0) + sysfatal("can't scan %s", path); + } + if(root == nil) + sysfatal("fileinit: %r"); +} + +int +filesync(void) +{ + dnprint("filesync\n"); + if(usedb) + return writetree(root); + return 0; +} + +int +filechanged(File *f) +{ + Dir *nd; + int r; + + wlock(f); + r = None; + dnprint("dirstat %s\n", f->path); + nd = dirstat(f->path); + if(nd == nil){ + wunlock(f); + return Meta|Data|Gone; + } + if(changed(f->d, nd)) + r |= Data; + if(mchanged(f->d, nd)) + r |= Meta; + + free(f->d); + f->d = nd; + wunlock(f); + return r; +} + +int +wstatfile(File *f, Dir *d) +{ + Dir *nd; + Dir wd; + + dnprint("wstat: %T\n", f); + if(d->name[0] != 0 && strcmp(d->name, f->d->name) != 0 && + f->parent == nil){ + werrstr("can't rename /"); + return -1; + } + wlock(f); + if(dirwstat(f->path, d) < 0){ + nulldir(&wd); + wd.mode = d->mode; + if(dirwstat(f->path, &wd) < 0){ + wunlock(f); + return -1; + } + fprint(2, "%s: can't change uid/mtime/...: %r\n", f->path); + } + + if(d->name[0] != 0 && strcmp(d->name, f->d->name) != 0){ + free(f->path); + f->path = smprint("%s/%s", f->parent->path, d->name); + } + dnprint("dirstat %s\n", f->path); + nd = dirstat(f->path); + if(nd == nil) + sysfatal("wstatfile: dirstat bug"); + free(f->d); + f->d = nd; + wunlock(f); + return 0; +} + +Qid +fileqid(File *f) +{ + Qid q; + + rlock(f); + q = f->d->qid; + runlock(f); + return q; +} + +Dir* +statfile(File *f, int refresh) +{ + Dir *d; + + if(refresh){ + wlock(f); + dnprint("stat: %T\n", f); + d = dirstat(f->path); + if(d == nil){ + wunlock(f); + return nil; + } + free(f->d); + f->d = d; + d = dupdir(f->d); + wunlock(f); + }else{ + rlock(f); + d = dupdir(f->d); + runlock(f); + } + return d; +} + +int +walkfile(File **fp, char *elem) +{ + File *f, *nf; + + f = *fp; + dnprint("walk %s %s\n", f->path, elem); + if(utfrune(elem, '/') != nil){ + werrstr("'/' not allowed in file name"); + return -1; + } + if(strcmp(elem, ".") == 0) + return 0; + if(strcmp(elem, "..") == 0){ + if(f != root){ + incref(f->parent); + *fp = f->parent; + putfile(f); + } + return 0; + } + rlock(f); + nf = getchild(f, elem); + runlock(f); + if(nf == nil){ + werrstr("file does not exist"); + return -1; + } + putfile(f); + *fp = nf; + return 0; +} + +int +openfile(File *f, int mode) +{ + int fd; + + dnprint("open: mode %#x %T\n", mode, f); + if((mode&~(OTRUNC|3)) != 0){ + werrstr("bad mode for openfile"); + return -1; + } + wlock(f); + fd = open(f->path, mode); + if(fd >= 0) + f->nopens++; + wunlock(f); + return fd; +} + +void +closefile(File *f, int fd) +{ + wlock(f); + dnprint("close: %T\n", f); + close(fd); + if(--f->nopens == 0){ + dnprint("dirstat %s\n", f->path); + free(f->d); + f->d = dirstat(f->path); + if(f->d == nil) + sysfatal("closefile: dirstat bug"); + } + wunlock(f); +} + +int +createfile(File **fp, char *elem, int mode, int perm) +{ + char *s; + File *f, *nf; + int fd; + + f = *fp; + dnprint("create: %s in %T\n", elem, f); + if(utfrune(elem, '/') != nil){ + werrstr("'/' not allowed in file name"); + return -1; + } + if(strcmp(elem, ".") == 0 || strcmp(elem, "..") == 0){ + werrstr("'%s' can't be created", elem); + return -1; + } + if(mode&~(DMDIR|3)){ + werrstr("createfile: bad mode"); + return -1; + } + scandir(f, nil, 0); + wlock(f); + nf = getchild(f, elem); + if(nf != nil){ + /* + * truncate, actually. + */ + wlock(nf); + fd = create(nf->path, mode, perm); + if(fd >= 0) + nf->nopens++; + wunlock(nf); + wunlock(f); + putfile(*fp); + *fp = nf; + return fd; + } + s = smprint("%s/%s", f->path, elem); + fd = create(s, mode, perm); + if(fd < 0){ + free(s); + wunlock(f); + return -1; + } + nf = newfile(f, s, nil); + nf->nopens++; + wunlock(f); + incref(nf); + putfile(*fp); + *fp = nf; + return fd; +} + +long +preadfile(File *, int fd, void *a, ulong count, uvlong offset) +{ + return pread(fd, a, count, offset); +} + +long +pwritefile(File *, int fd, void *a, ulong count, uvlong offset) +{ + return pwrite(fd, a, count, offset); +} + +int +removefile(File *f) +{ + File *parent; + + dnprint("remove: %T\n", f); + parent = f->parent; + if(parent == nil){ + werrstr("can't remove /"); + return -1; + } +Again: + wlock(f); + incref(f); /* keep it here while we use it */ + parent = f->parent; + + if(!canwlock(parent)){ /* lock order: parent, then child */ + wunlock(f); + yield(); + goto Again; + } + if(remove(f->path) < 0){ + wunlock(f); + wunlock(parent); + putfile(f); + return -1; + } + unlinkfile(f); + + wunlock(f); + wunlock(parent); + /* + * We don't put the ref given by the caller. + * When he calls putfile() the file should go. + */ + return 0; +} + +enum +{ + Pother = 1, + Pgroup = 8, + Powner = 64, +}; + +int +perm(File *f, char *user, int p) +{ + if((p*Pother) & f->d->mode) + return 1; + if(strcmp(user, f->d->gid)==0 && ((p*Pgroup) & f->d->mode)) + return 1; + if(strcmp(user, f->d->uid)==0 && ((p*Powner) & f->d->mode)) + return 1; + return 0; +} + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/nstest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/nstest.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,126 @@ +/* + * Test program for nsfile. + * + X/\[/D + ! mk 8.nstest + ! rm /tmp/tmeasure/new + ! rm /tmp/tmeasure/.ixd + ! 8.nstest -Dn /tmp/tmeasure >[2=1] + ! slay 8.nstest | rc + */ + +#include +#include +#include +#include +#include + +#include "conf.h" +#include "dbg.h" +#include "file.h" + + +static void +usage(void) +{ + fprint(2, "usage: %s [-s] [-D flags] dir\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *flags; + int dosleep; + File *f; + Dir *d; + Dir wd; + int fd, n; + char buf[128]; + + dosleep = 0; + ARGBEGIN{ + case 'D': + flags = EARGF(usage()); + for(;*flags != 0; flags++) + dbg[*flags]++; + dbg['d']++; + break; + case 's': + dosleep = 1; + break; + }ARGEND; + if(argc != 1) + usage(); + + fmtinstall('M', dirmodefmt); + fmtinstall('D', shortdirfmt); + fileinit(argv[0], 1); + f = rootfile(); + walkfile(&f, "plotnumb"); + walkfile(&f, "resultclose"); + d = statfile(f, 0); + print("got %D\n", d); + free(d); + fd = openfile(f, OWRITE); + if(fd < 0) + print("got %r\n"); + else{ + pwritefile(f, fd, "hola", 4, 0ULL); + print("file %T\n", f); + closefile(f, fd); + } + print("file %T\n", f); + putfile(f); + f = rootfile(); + if(walkfile(&f, "foo") < 0) + print("walk failed; ok\n"); + print("file %T\n", f); + walkfile(&f, "plotnumb"); + walkfile(&f, "resultclose"); + fd = openfile(f, OREAD); + if(fd < 0) + sysfatal("openfile: %r"); + n = preadfile(f, fd, buf, 4, 0ULL); + if(n != 4) + sysfatal("preadfile: %r"); + buf[4] = 0; + print("read: '%s'\n", buf); + closefile(f, fd); + print("file %T\n", f); + putfile(f); + f = rootfile(); + if((fd = createfile(&f, "new", OREAD, 0775|DMDIR)) < 0) + print("create: %r\n"); + else + closefile(f, fd); + print("created: %T\n", f); + putfile(f); + f = rootfile(); + walkfile(&f, "new"); + removefile(f); + print("file is %T\n", f); + putfile(f); + f = rootfile(); + if(removefile(f) < 0) + print("remove root: %r\n"); + putfile(f); + nulldir(&wd); + wd.mode = 0700; + f = rootfile(); + if(walkfile(&f, "hola") < 0) + print("can't walk to hola\n"); + else if(wstatfile(f, &wd) < 0) + print("wstat: %r\n"); + print("file is %T\n", f); + putfile(f); + filesync(); + dumptree(); + if(dosleep){ + print("%s: sleeping forever\n", argv0); + for(;;) + sleep(3600); + } + + threadexits(nil); +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/pack.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/pack.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,691 @@ +#include +#include +#include + +#include "fs.h" + +/* + * This is convS2M, convM2S from libc, but: + * - without copying bytes in the case of packing a Rread or Twrite request. + * - Tattach as no fid, Rattach has a fid. + * - Twalk has an uchar (bool) to ask for a newfid, and has only wname + * - Rwalk has newfid and only a wqid. + */ + + +static +uchar* +pstring(uchar *p, char *s) +{ + uint n; + + if(s == nil){ + PBIT16(p, 0); + p += BIT16SZ; + return p; + } + + n = strlen(s); + /* + * We are moving the string before the length, + * so you can S2M a struct into an existing message + */ + memmove(p + BIT16SZ, s, n); + PBIT16(p, n); + p += n + BIT16SZ; + return p; +} + +static +uchar* +pqid(uchar *p, Qid *q) +{ + PBIT8(p, q->type); + p += BIT8SZ; + PBIT32(p, q->vers); + p += BIT32SZ; + PBIT64(p, q->path); + p += BIT64SZ; + return p; +} + +static +uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +uint +packedsize(Fscall *f) +{ + uint n; + + n = 0; + n += BIT8SZ; /* type */ + + switch(f->type) + { + case Tcond: + n += BIT8SZ; + n += BIT16SZ; + n += f->nstat; + break; + + case Tfid: + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tclone: + n += BIT8SZ; + break; + + case Tversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Tauth: + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Tattach: + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Twalk: + n += stringsz(f->wname); + break; + + case Topen: + n += BIT8SZ; + break; + + case Tcreate: + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tread: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + + case Twrite: + n += BIT64SZ; + n += BIT32SZ; + /* n += f->count; */ + break; + + case Tclunk: + case Tremove: + case Tstat: + break; + + case Twstat: + n += BIT16SZ; + n += f->nstat; + break; +/* + */ + case Rcond: + case Rfid: + break; + + case Rclone: + n += BIT32SZ; + break; + + case Rversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Rerror: + n += stringsz(f->ename); + break; + + case Rauth: + n += QIDSZ; + break; + + case Rattach: + n += BIT32SZ; + n += QIDSZ; + break; + + case Rwalk: + n += QIDSZ; + break; + + case Ropen: + case Rcreate: + n += QIDSZ; + n += BIT32SZ; + break; + + case Rread: + n += BIT32SZ; + /* n += f->count; */ + break; + + case Rwrite: + n += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + n += BIT16SZ; + n += f->nstat; + break; + + case Rwstat: + break; + + default: + sysfatal("packedsize: type %d", f->type); + + } + return n; +} + +uint +pack(Fscall *f, uchar *ap, uint nap) +{ + uchar *p; + uint size; + + size = packedsize(f); + if(size == 0) + return 0; + if(size > nap) + return 0; + + p = (uchar*)ap; + + PBIT8(p, f->type); + p += BIT8SZ; + + switch(f->type) + { + case Tcond: + PBIT8(p, f->cond); + p += BIT8SZ; + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; + + case Tfid: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT8(p, f->cflags); + p += BIT8SZ; + break; + + case Tclone: + PBIT8(p, f->cflags); + p += BIT8SZ; + break; + + case Tversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Tauth: + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Tattach: + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Twalk: + p = pstring(p, f->wname); + break; + + case Topen: + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tcreate: + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tread: + PBIT32(p, f->nmsg); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Twrite: + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + /* + * Data added by the caller. + * memmove(p, f->data, f->count); + * p += f->count; + */ + break; + + case Tclunk: + case Tremove: + case Tstat: + break; + + case Twstat: + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; +/* + * replies + */ + + case Rcond: + case Rfid: + break; + + case Rclone: + PBIT32(p, f->newfid); + p += BIT32SZ; + break; + + case Rversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Rerror: + p = pstring(p, f->ename); + break; + + case Rauth: + p = pqid(p, &f->aqid); + break; + + case Rattach: + PBIT32(p, f->fid); + p += BIT32SZ; + p = pqid(p, &f->qid); + break; + + case Rwalk: + p = pqid(p, &f->wqid); + break; + + case Ropen: + case Rcreate: + p = pqid(p, &f->qid); + PBIT32(p, f->iounit); + p += BIT32SZ; + break; + + case Rread: + PBIT32(p, f->count); + p += BIT32SZ; + /* + * data is added by the caller. + * memmove(p, f->data, f->count); + * p += f->count; + */ + break; + + case Rwrite: + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; + + case Rwstat: + break; + + default: + sysfatal("pack: type %d", f->type); + + } + if(size != p-ap) + return 0; + return size; +} + +static +uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ - 1; + if(p+n+1 > ep) + return nil; + /* move it down, on top of count, to make room for '\0' */ + memmove(p, p + 1, n); + p[n] = '\0'; + *s = (char*)p; + p += n+1; + return p; +} + +static +uchar* +gqid(uchar *p, uchar *ep, Qid *q) +{ + if(p+QIDSZ > ep) + return nil; + q->type = GBIT8(p); + p += BIT8SZ; + q->vers = GBIT32(p); + p += BIT32SZ; + q->path = GBIT64(p); + p += BIT64SZ; + return p; +} + +/* + * no syntactic checks. + * three causes for error: + * 1. message size field is incorrect + * 2. input buffer too short for its own data (counts too long, etc.) + * 3. too many names or qids + * gqid() and gstring() return nil if they would reach beyond buffer. + * main switch statement checks range and also can fall through + * to test at end of routine. + */ +uint +unpack(uchar *ap, uint nap, Fscall *f) +{ + uchar *p, *ep; + + p = ap; + ep = p + nap; + + if(p+BIT8SZ > ep){ + werrstr("msg too short"); + return 0; + } + + f->type = GBIT8(p); + p += BIT8SZ; + + switch(f->type) + { + default: + werrstr("unknown type %d", f->type); + return 0; + + case Tcond: + if(p+BIT8SZ+BIT16SZ > ep) + return 0; + f->cond = GBIT8(p); + p += BIT8SZ; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + + case Tfid: + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->cflags = GBIT8(p); + p += BIT8SZ; + break; + + case Tclone: + if(p+BIT8SZ > ep) + return 0; + f->cflags = GBIT8(p); + p += BIT8SZ; + break; + + case Tversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Tauth: + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Tattach: + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Twalk: + p = gstring(p, ep, &f->wname); + if(p == nil) + break; + break; + + case Topen: + if(p+BIT8SZ > ep) + return 0; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tcreate: + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tread: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->nmsg = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Twrite: + if(p+BIT64SZ+BIT32SZ > ep) + return 0; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Tclunk: + case Tremove: + case Tstat: + break; + + case Twstat: + if(p+BIT16SZ > ep) + return 0; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + +/* + */ + case Rfid: + case Rcond: + break; + + case Rclone: + if(p+BIT32SZ > ep) + return 0; + f->newfid = GBIT32(p); + p += BIT32SZ; + break; + + case Rversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Rerror: + p = gstring(p, ep, &f->ename); + break; + + case Rauth: + p = gqid(p, ep, &f->aqid); + if(p == nil) + break; + break; + + case Rattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + break; + + case Rwalk: + p = gqid(p, ep, &f->wqid); + if(p == nil) + break; + break; + + case Ropen: + case Rcreate: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + if(p+BIT32SZ > ep) + return 0; + f->iounit = GBIT32(p); + p += BIT32SZ; + break; + + case Rread: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Rwrite: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + if(p+BIT16SZ > ep) + return 0; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + + case Rwstat: + break; + } + + if(p==nil || p>ep || p == ap){ + werrstr("unpack: p %#p ep %#p", p, ep); + return 0; + } + return p - ap; +} diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/tses.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/tses.c Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,313 @@ +/* + * Tcp-like transports, wrapped in a Ses structure + * to send and receive messages. + * + * Termination: + * - rproc gets and error or eof: + * 1. rproc sends nil to client (e.g., cmux) and exits + * 2. client is noted and writes nil to wproc + * it may look at s->err for the error condition. + * 3. wproc intrs. rproc (harmless). + * + * - client terminates: + * 1. client writes nil to wproc + * 2. wproc intrs. rproc and terminates + * 3. rproc is noted and sends nil to client + * + * Concurrent termination works ok, because rproc + * only sends nil once to the client. + */ +#include +#include +#include +#include + +#include "conf.h" +#include "msg.h" +#include "mpool.h" +#include "tses.h" +#include "dbg.h" + +static void +abortconn(Ses *s) +{ + if(s->cfd < 0) + return; + dsprint("abortconn[%p]: hanging up\n", s); +// write(s->cfd, "hangup", 6); + close(s->cfd); + close(s->dfd); + s->cfd = s->dfd = -1; +} + +static void +freeses(Ses *s) +{ + dsprint("free session %p\n", s); + free(s->addr); + free(s->err); + if(s->rc != nil) + chanfree(s->rc); + if(s->wc != nil) + chanfree(s->wc); + if(s->ec != nil) + chanfree(s->ec); + freepool(s->pool); + freepool(s->spool); + memset(s, 6, sizeof *s); /* poison */ + free(s); +} + +static void +putses(Ses *s) +{ + if(decref(s) == 0) + freeses(s); +} + +static void +syserr(Ses *s) +{ + /* + * BUG: could leak if concurrent errors. + */ + if(s->err == nil) + s->err = smprint("%r"); +} + +static void +rproc(void *a) +{ + Ses *s; + Msg *m, *sm; + long nr; + + s = a; + threadsetname("rproc[%p]", a); + dpprint("rproc[%p]: started\n", a); + s->rtid = threadid(); + m = newmsg(s->pool); + sm = newmsg(s->spool); + for(;;){ + if(m == nil) + sysfatal("rproc: newmsg"); + nr = readmsg(s->dfd, m); + if(nr < 0){ + syserr(s); + dmprint("rproc[%p]: readmsg: %r\n", a); + break; + } + if(nr == 0){ + dmprint("rproc[%p]: eof\n", a); + break; + } + if(IOLEN(m->io) <= IOCAP(sm->io)){ + memmove(sm->io->wp, m->io->rp, IOLEN(m->io)); + sm->io->wp += IOLEN(m->io); + m->hdr = nil; + ioreset(m->io); + sendp(s->rc, sm); + sm = newmsg(s->spool); + }else{ + sendp(s->rc, m); + m = newmsg(s->pool); + } + } + sendp(s->rc, nil); + sendp(s->ec, s->err); + s->err = nil; + freemsg(m); + freemsg(sm); + putses(s); + dpprint("rproc[%p]: exiting\n", a); +} + +static void +wproc(void *a) +{ + Ses *s; + Msg *m; + + s = a; + threadsetname("wproc[%p]", a); + dpprint("wproc[%p]: started\n", a); + for(;;){ + m = recvp(s->wc); + if(m == nil){ + dmprint("wproc[%p]: got nil msg\n", a); + abortconn(s); + break; + } + if(writemsg(s->dfd, m) < 0){ + syserr(s); + dmprint("wproc[%p]: snd len %uld: %r\n", a, msglen(m)); + freemsg(m); + abortconn(s); + break; + } + dmprint("wproc[%p]: sent %uld bytes\n", a, msglen(m)); + freemsg(m); + } + threadint(s->rtid); + while((m = nbrecvp(s->wc)) != nil) + freemsg(m); + putses(s); + dpprint("wproc[%p]: exiting\n", a); +} + +void +startses(Ses *s, Mpool *mp, Mpool *smp) +{ + s->pool = mp; + s->spool = smp; + s->ec = echancreate(sizeof(char*), 0); + s->rc = echancreate(sizeof(Msg*), 0); + incref(s); + proccreate(rproc, s, Stack); + s->wc = echancreate(sizeof(Msg*), 0); + incref(s); + proccreate(wproc, s, Stack); +} + +static void +ssrvproc(void *a) +{ + enum{New, End}; + + Ssrv *ss; + Ses *sl, **nl, *s; + Alt alts[] = { + [New] {nil, &s, CHANRCV}, + [End] {nil, &s, CHANRCV}, + {nil, nil, CHANEND}, + }; + + ss = a; + alts[New].c = ss->listenc; + alts[End].c = ss->endc; + sl = nil; + threadsetname("ssrvproc %p", a); + dpprint("ssrvproc[%p]: started\n", a); + for(;;){ + switch(alt(alts)){ + case New: + dsprint("session[%p]: new %s\n", s, s->addr); + s->next = sl; + sl = s; + incref(s); + sendp(ss->newc, s); + break; + + case End: + dsprint("session[%p]: done %s\n", s, s->addr); + for(nl = &sl; *nl != nil; nl = &(*nl)->next) + if(s == *nl) + break; + if(*nl == nil) + sysfatal("ssrvproc[%p]: no session", a); + *nl = s->next; + putses(s); + break; + + default: + sysfatal("alt"); + } + } +} + +static void +listenproc(void *a) +{ + Ssrv *ss; + Ses *s; + char ldir[40]; + + ss = a; + threadsetname("listenproc %p", a); + dpprint("listenproc[%p]: started\n", a); + for(;;){ + s = emalloc(sizeof *s); + s->addr = estrdup(ss->addr); /* not needed in srv */ + s->cfd = s->dfd = -1; + s->ref = 1; + s->cfd = listen(ss->adir, ldir); + if(s->cfd < 0){ + dprint("listen: %r"); + goto Fail; + } + s->dfd = accept(s->cfd, ldir); + if(s->dfd < 0){ + dprint("accept: %r"); + goto Fail; + } + sendp(ss->listenc, s); + continue; + Fail: + if(s->cfd >= 0) + close(s->cfd); + if(s->dfd >= 0) + close(s->dfd); + s->cfd = s->dfd = -1; + freeses(s); + fprint(2, "%s: listenproc: failed: %r\n", argv0); + sleep(1000); /* avoid retrying too fast */ + } +} + +Ssrv* +newsrv(char *addr) +{ + Ssrv *ss; + + ss = emalloc(sizeof *ss); + dprint("announce %s\n", addr); + ss->afd = announce(addr, ss->adir); + if(ss->afd < 0){ + free(ss); + return nil; + } + ss->addr = estrdup(addr); + ss->listenc = echancreate(sizeof(Ses*), 0); + ss->newc = echancreate(sizeof(Ses*), 0); + ss->endc = echancreate(sizeof(Ses*), 0); + proccreate(listenproc, ss, Stack); + threadcreate(ssrvproc, ss, Stack); + return ss; +} + +Ses* +dialsrv(char *addr) +{ + Ses *s; + + s = emalloc(sizeof *s); + dprint("dial %s\n", addr); + s->dfd = dial(addr, nil, nil, &s->cfd); + if(s->dfd < 0){ + free(s); + return nil; + } + s->ref = 1; + s->addr = estrdup(addr); + dsprint("session[%p]: new %s\n", s, s->addr); + return s; +} + +int +closeses(Ses *s) +{ + Msg *m; + char *e; + + sendp(s->wc, nil); + while((m = recvp(s->rc)) != nil) + freemsg(m); + e = recvp(s->ec); + if(e != nil){ + werrstr(e); + free(e); + return -1; + } + return 0; +} + diff -r 2116c13e63f5 -r dd7abca1a252 sys/src/cmd/ix/tses.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/ix/tses.h Sat Sep 17 18:46:57 2011 +0200 @@ -0,0 +1,40 @@ +typedef struct Ses Ses; +typedef struct Ssrv Ssrv; + +struct Ses +{ + Channel*rc; /* read channel (of Msg*) */ + Channel*wc; /* write channel (of Msg*) */ + Channel*ec; /* error channel (of char*) */ + + + /* implementation */ + Mpool*pool; /* message pool */ + Mpool*spool; /* small message pool */ + char* err; + Ref; /* used by reader, writer, client */ + Ses *next; /* in list of sessions */ + char* addr; /* debug */ + int cfd; /* control fd */ + int dfd; /* data fd */ + int rtid; /* reader thread id */ +}; + +struct Ssrv +{ + Channel*newc; /* of Ses* */ + + /* implementation*/ + char* addr; + char adir[40]; + int afd; + int lfd; + Channel*endc; /* of Ses* */ + Channel*listenc; /* of ses* */ +}; + +/* |c/f2p tses.c */ +extern int closeses(Ses *s); +extern Ses* dialsrv(char *addr); +extern Ssrv* newsrv(char *addr); +extern void startses(Ses *s, Mpool *p, Mpool *sp);