# HG changeset patch # User Francisco J Ballesteros # Date 1329923092 -3600 # Node ID 104e7add5754e0f6c392e4307c1735dacd23dc82 # Parent d4a6be52c85c52e43d546c297d78d4f19f53fa5b creepy: both 9p and ix in place still ongoing work. not ready. As it is now, it's only useful for taking a look into it. R=nixiedev, nemo CC=nix-dev http://codereview.appspot.com/5687050 diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/9p.c --- a/sys/src/cmd/creepy/9p.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/9p.c Wed Feb 22 16:04:52 2012 +0100 @@ -9,66 +9,15 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" + /* * 9p server for creepy */ -typedef struct Fid Fid; -typedef struct Rpc Rpc; -typedef struct Cli Cli; - -enum -{ - Maxmdata = 8*KiB -}; - -/* - * One reference kept because of existence and another per req using it. - */ -struct Fid -{ - Ref; - QLock; - Fid *next; /* in hash or free list */ - void* clino; /* no is local to a client */ - int no; - Memblk *file; /* used by this fid */ - int omode; /* -1 if closed */ - int rclose; - int archived; - char *uid; - - uvlong loff; /* last offset, for dir reads */ - long lidx; /* next dir entry index to read */ -}; - -struct Rpc -{ - Cli *cli; - Rpc *next; /* in client or free list */ - Fid *fid; - Fcall t; - Fcall r; - uchar data[IOHDRSZ+Maxmdata]; -}; - -struct Cli -{ - Ref; - int fd; - int cfd; - char *addr; - int errors; - ulong msize; - - QLock wlk; /* lock for writing replies to the client */ - uchar wdata[IOHDRSZ+Maxmdata]; - - QLock rpclk; - Rpc *rpcs; -}; static void rflush(Rpc*), rversion(Rpc*), rauth(Rpc*), rattach(Rpc*), rwalk(Rpc*), @@ -93,141 +42,194 @@ [Twstat] rwstat, }; -static RWLock fidlk; +static RWLock fidhashlk; static Fid *fidhash[Fidhashsz]; -static Fid *fidfree; -static ulong nfids, nfreefids; +static uint fidgen; -static QLock rpclk; -static Rpc *rpcfree; -static ulong nrpcs, nfreerpcs; +static Alloc fidalloc = +{ + .elsz = sizeof(Fid), + .zeroing = 1, +}; +static Alloc rpcalloc = +{ + .elsz = sizeof(Largerpc), + .zeroing = 0, +}; +Alloc clialloc = +{ + .elsz = sizeof(Cli), + .zeroing = 1, +}; -static Rpc* +static QLock clientslk; +static Cli *clients; + +int +fidfmt(Fmt *fmt) +{ + Fid *fid; + + fid = va_arg(fmt->args, Fid*); + if(fid == nil) + return fmtprint(fmt, ""); + return fmtprint(fmt, "fid %#p no %d r%d, omode %d arch %d", + fid, fid->no, fid->ref, fid->omode, fid->archived); +} + +void +ninestats(void) +{ + print("fids:\t%4uld alloc %4uld free (%4uld bytes)\n", + fidalloc.nalloc, fidalloc.nfree, fidalloc.elsz); + print("rpcs:\t%4uld alloc %4uld free (%4uld bytes)\n", + rpcalloc.nalloc, rpcalloc.nfree, rpcalloc.elsz); + print("clis:\t%4uld alloc %4uld free (%4uld bytes)\n", + clialloc.nalloc, clialloc.nfree, clialloc.elsz); + +} + +Rpc* newrpc(void) { Rpc *rpc; - qlock(&rpclk); - if(rpcfree != nil){ - rpc = rpcfree; - rpcfree = rpc->next; - rpc->next = nil; - nfreerpcs--; + rpc = anew(&rpcalloc); + rpc->next = nil; + rpc->cli = nil; + rpc->fid = nil; + rpc->flushed = 0; + rpc->closed = 0; + rpc->chan = ~0; + rpc->rpc0 = nil; + /* ouch! union. */ + if(sizeof(Fcall) > sizeof(IXcall)){ + memset(&rpc->t, 0, sizeof rpc->t); + memset(&rpc->r, 0, sizeof rpc->r); }else{ - rpc = malloc(sizeof *rpc); - nrpcs++; + memset(&rpc->xt, 0, sizeof rpc->xt); + memset(&rpc->xr, 0, sizeof rpc->xr); } - qunlock(&rpclk); - rpc->next = nil; - rpc->fid = nil; - memset(&rpc->t, 0, sizeof rpc->t); - memset(&rpc->r, 0, sizeof rpc->r); - return rpc; + return rpc; } -static void +void freerpc(Rpc *rpc) { - qlock(&rpclk); - rpc->next = rpcfree; - rpcfree = rpc; - nfreerpcs++; - qunlock(&rpclk); + afree(&rpcalloc, rpc); } -static Fid* +Fid* newfid(void* clino, int no) { Fid *fid, **fidp; - wlock(&fidlk); + wlock(&fidhashlk); if(catcherror()){ - wunlock(&fidlk); + wunlock(&fidhashlk); error(nil); } + if(no < 0) + no = fidgen++; for(fidp = &fidhash[no%Fidhashsz]; *fidp != nil; fidp = &(*fidp)->next) if((*fidp)->clino == clino && (*fidp)->no == no) error("fid in use"); - if(fidfree != nil){ - fid = fidfree; - fidfree = fidfree->next; - nfreefids--; - }else{ - fid = mallocz(sizeof *fid, 1); - nfids++; - } + fid = anew(&fidalloc); *fidp = fid; fid->omode = -1; fid->no = no; - fid->rclose = 0; fid->clino = clino; fid->ref = 2; /* one for the caller; another because it's kept */ noerror(); - wunlock(&fidlk); + wunlock(&fidhashlk); + d9print("new fid %X\n", fid); return fid; } -static Fid* +Fid* getfid(void* clino, int no) { Fid *fid; - rlock(&fidlk); + rlock(&fidhashlk); if(catcherror()){ - runlock(&fidlk); + runlock(&fidhashlk); error(nil); } for(fid = fidhash[no%Fidhashsz]; fid != nil; fid = fid->next) if(fid->clino == clino && fid->no == no){ incref(fid); noerror(); - runlock(&fidlk); + runlock(&fidhashlk); return fid; } error("fid not found"); return fid; } -static void +void putfid(Fid *fid) { Fid **fidp; if(fid == nil || decref(fid) > 0) return; - mbput(fid->file); - fid->file = nil; + d9print("clunk fid %X\n", fid); + putpath(fid->p); free(fid->uid); - fid->uid = nil; - fid->rclose = fid->archived = 0; - fid->omode = -1; - fid->loff = 0; - fid->lidx = 0; - wlock(&fidlk); + wlock(&fidhashlk); if(catcherror()){ - wunlock(&fidlk); + wunlock(&fidhashlk); error(nil); } for(fidp = &fidhash[fid->no%Fidhashsz]; *fidp != nil; fidp = &(*fidp)->next) if(*fidp == fid){ *fidp = fid->next; - memset(fid, 0, sizeof *fid); - fid->next = fidfree; noerror(); - wunlock(&fidlk); + wunlock(&fidhashlk); + afree(&fidalloc, fid); return; } fatal("putfid: fid not found"); } -static void -putcli(Cli *c) +/* keeps addr, does not copy it */ +Cli* +newcli(char *addr, int fd, int cfd) { - if(decref(c) == 0){ - close(c->fd); - close(c->cfd); - free(c->addr); - free(c); + Cli *cli; + + cli = anew(&clialloc); + cli->fd = fd; + cli->cfd = cfd; + cli->addr = addr; + cli->ref = 1; + + qlock(&clientslk); + cli->next = clients; + clients = cli; + qunlock(&clientslk); + return cli; +} + +void +putcli(Cli *cli) +{ + Cli **cp; + + if(decref(cli) == 0){ + qlock(&clientslk); + for(cp = &clients; *cp != nil; cp = &(*cp)->next) + if(*cp == cli) + break; + if(*cp == nil) + fatal("client not found"); + *cp = cli->next; + qunlock(&clientslk); + close(cli->fd); + close(cli->cfd); + free(cli->addr); + afree(&clialloc, cli); } } @@ -245,6 +247,10 @@ q.type |= QTTMP; if(f->mf->mode&DMAPPEND) q.type |= QTAPPEND; + if(f->mf->mode&DMEXCL) + q.type |= QTEXCL; + if((q.type&QTEXCL) == 0) + q.type |= QTCACHE; return q; } @@ -252,20 +258,36 @@ rversion(Rpc *rpc) { rpc->r.msize = rpc->t.msize; - if(rpc->r.msize > sizeof rpc->data) - rpc->r.msize = sizeof rpc->data; + if(rpc->r.msize > Maxmdata) + rpc->r.msize = Maxmdata; rpc->cli->msize = rpc->r.msize; if(strncmp(rpc->t.version, "9P2000", 6) != 0) error("unknown protocol version"); rpc->r.version = "9P2000"; } +/* + * Served in the main client process. + */ static void -rflush(Rpc *) +rflush(Rpc *rpc) { - /* BUG: should reply to this after replying to the flushed request. - * Just look into rpc->c->rpcs - */ + Cli *cli; + Rpc *r; + + cli = rpc->cli; + qlock(&cli->wlk); /* nobody replies now */ + qlock(&rpc->cli->rpclk); + for(r = rpc->cli->rpcs; r != nil; r = r->next) + if(r->t.tag == rpc->t.oldtag) + break; + if(r != nil){ + r->flushed = 1; + if(r->t.type == Tread && r->fid->consopen) + consprint(""); /* in case it's waiting... */ + } + qunlock(&rpc->cli->rpclk); + qunlock(&cli->wlk); } static void @@ -275,40 +297,94 @@ error("no auth required"); } +void +attach(Fid *fid, char *aname, char *uname) +{ + Path *p; + + fid->uid = strdup(uname); + p = newpath(fs->root); + fid->p = p; + if(strcmp(aname, "active") == 0 || strcmp(aname, "main/active") == 0){ + addelem(&p, fs->active); + return; + } + fid->archived = 1; + if(strcmp(aname, "archive") == 0 || strcmp(aname, "main/archive") == 0) + addelem(&p, fs->archive); + else if(strcmp(aname, "main") != 0 && strcmp(aname, "") != 0) + error("unknown tree"); +} static void rattach(Rpc *rpc) { Fid *fid; + Path *p; + Memblk *f; fid = newfid(rpc->cli, rpc->t.fid); rpc->fid = fid; - fid->file = fs->active; - incref(fid->file); - rwlock(fid->file, Rd); - fid->uid = strdup(rpc->t.uname); - rpc->r.qid = mkqid(fid->file); - rwunlock(fid->file, Rd); + attach(fid, rpc->t.aname, rpc->t.uname); + p = fid->p; + f = p->f[p->nf-1]; + rwlock(f, Rd); + rpc->r.qid = mkqid(f); + rwunlock(f, Rd); } -static Fid* -clone(Rpc *rpc) +Fid* +clone(Cli *cli, Fid *fid, int no) { Fid *nfid; - nfid = newfid(rpc->cli, rpc->t.newfid); - nfid->file = rpc->fid->file; - incref(nfid->file); - nfid->uid = strdup(rpc->fid->uid); - nfid->archived = rpc->fid->archived; + nfid = newfid(cli, no); + nfid->p = clonepath(fid->p); + nfid->uid = strdup(fid->uid); + nfid->archived = fid->archived; + nfid->consopen = fid->consopen; return nfid; } +void +walk(Fid *fid, char *wname) +{ + Path *p; + Memblk *f, *nf; + + p = fid->p; + if(strcmp(wname, ".") == 0) + goto done; + if(strcmp(wname, "..") == 0){ + if(p->nf > 1) + p = dropelem(&fid->p); + goto done; + } + f = p->f[p->nf-1]; + rwlock(f, Rd); + if(catcherror()){ + rwunlock(f, Rd); + error(nil); + } + dfaccessok(f, fid->uid, AEXEC); + nf = dfwalk(f, wname, 0); + rwunlock(f, Rd); + p = addelem(&fid->p, nf); + decref(nf); +done: + f = p->f[p->nf-1]; + if(isro(f)) + fid->archived = f != fs->cons; + else if(f == fs->active) + fid->archived = 0; +} + static void rwalk(Rpc *rpc) { Fid *fid, *nfid; - Memblk *f; + Path *p; + Memblk *nf; int i; rpc->fid = getfid(rpc->cli, rpc->t.fid); @@ -317,39 +393,31 @@ error("can't walk like a clone without one"); nfid = nil; if(rpc->t.fid != rpc->t.newfid) - nfid = clone(rpc); + nfid = clone(rpc->cli, rpc->fid, rpc->t.newfid); if(catcherror()){ putfid(nfid); + putfid(nfid); /* clunk */ error(nil); } rpc->r.nwqid = 0; for(i=0; i < rpc->t.nwname; i++){ - rwlock(nfid->file, Rd); if(catcherror()){ - rwunlock(nfid->file, Rd); if(rpc->r.nwqid == 0) error(nil); break; } - dfaccessok(nfid->file, fid->uid, AEXEC); - f = dfwalk(nfid->file, rpc->t.wname[i], 0); - if(f == fs->archive) - fid->archived++; - else if(f == fs->active) - fid->archived = 0; - rwunlock(nfid->file, Rd); - mbput(nfid->file); - nfid->file = f; - noerror(); - rwlock(f, Rd); - rpc->r.wqid[i] = mkqid(f); - rwunlock(f, Rd); + walk(nfid, rpc->t.wname[i]); + p = nfid->p; + nf = p->f[p->nf-1]; + rwlock(nf, Rd); + rpc->r.wqid[i] = mkqid(nf); + rwunlock(nf, Rd); rpc->r.nwqid++; USED(rpc->r.nwqid); /* damn error()s */ } if(i < rpc->t.nwname){ putfid(nfid); - putfid(nfid); + putfid(nfid); /* clunk */ }else{ putfid(fid); rpc->fid = nfid; @@ -357,116 +425,158 @@ noerror(); } +void +fidopen(Fid *fid, int mode) +{ + int fmode, amode; + Memblk *f; + Path *p; + uvlong z; + + if(fid->omode != -1) + error("fid already open"); + + /* check this before we try to melt it */ + p = fid->p; + f = p->f[p->nf-1]; + if(mode != OREAD) + if(f == fs->root || f == fs->archive || fid->archived) + error("can't write archived or built-in files"); + amode = 0; + if((mode&3) != OREAD || (mode&OTRUNC) != 0) + amode |= AWRITE; + if((mode&3) != OWRITE) + amode |= AREAD; + if(amode != AREAD) + if(f == fs->cons) + rwlock(f, Wr); + else{ + p = dfmelt(&fid->p, fid->p->nf); + f = p->f[p->nf-1]; + } + else + rwlock(f, Rd); + if(catcherror()){ + rwunlock(f, (amode!=AREAD)?Wr:Rd); + error(nil); + } + fmode = f->mf->mode; + if(mode != OREAD){ + if(f != fs->root && p->f[p->nf-2]->mf->mode&DMAPPEND) + error("directory is append only"); + if((fmode&DMDIR) != 0) + error("wrong open mode for a directory"); + } + dfaccessok(f, fid->uid, amode); + if(mode&ORCLOSE){ + if(f == fs->active || f == fs->cons || fid->archived) + error("can't remove an archived or built-in file"); + dfaccessok(p->f[p->nf-2], fid->uid, AWRITE); + } + if(mode&ORCLOSE) + fid->rclose++; + if((fmode&DMEXCL) != 0 && f->mf->open) + if(f != fs->cons || amode != AWRITE) /* ok to write cons */ + error("exclusive use file already open"); + if((mode&OTRUNC) && f != fs->cons){ + z = 0; + dfwattr(f, "length", &z, sizeof z); + } + f->mf->open++; + fid->omode = mode&3; + fid->loff = 0; + fid->lidx = 0; + fid->consopen = f == fs->cons; + noerror(); + rwunlock(f, (amode!=AREAD)?Wr:Rd); +} + static void ropen(Rpc *rpc) { Fid *fid; Memblk *f; - int mode, fmode, amode; - uvlong z; rpc->fid = getfid(rpc->cli, rpc->t.fid); fid = rpc->fid; + rpc->r.iounit = rpc->cli->msize - IOHDRSZ; + fidopen(rpc->fid, rpc->t.mode); + f = fid->p->f[fid->p->nf-1]; + rwlock(f, Rd); + rpc->r.qid = mkqid(f); + rwunlock(f, Rd); +} + +void +fidcreate(Fid *fid, char *name, int mode, ulong perm) +{ + Path *p; + Memblk *f, *nf; + if(fid->omode != -1) error("fid already open"); - mode = rpc->t.mode; - rpc->r.iounit = rpc->cli->msize - IOHDRSZ; - amode = 0; - if((mode&3) != OREAD || (mode&OTRUNC) != 0) - amode |= AWRITE; - if((mode&3) != OWRITE) - amode |= AREAD; - if(mode != AREAD) - fid->file = dfmelt(fid->file); - else - rwlock(fid->file, Rd); - f = fid->file; + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error("that file name scares me"); + if(utfrune(name, '/') != nil) + error("that file name is too creepy"); + if((perm&DMDIR) != 0 && mode != OREAD) + error("wrong open mode for a directory"); + p = fid->p; + f = p->f[p->nf-1]; + if(fid->archived) + error("can't create in archived or built-in files"); + if((f->mf->mode&DMDIR) == 0) + error("not a directory"); + p = dfmelt(&fid->p, fid->p->nf); + f = p->f[p->nf-1]; if(catcherror()){ - rwunlock(f, amode != AREAD); + rwunlock(f, Wr); error(nil); } - fmode = f->mf->mode; - if(mode != OREAD){ - if((fmode&DMDIR) != 0) - error("wrong open mode for a directory"); - if(fid->archived) - error("can't write in /archive"); /* yes, we can! */ + dfaccessok(f, fid->uid, AWRITE); + if(!catcherror()){ + mbput(dfwalk(f, name, 0)); + error("file already exists"); } - rpc->r.qid = mkqid(f); - dfaccessok(f, fid->uid, amode); - if(mode&ORCLOSE) - dfaccessok(f->mf->parent, fid->uid, AWRITE); - if(mode&ORCLOSE) - fid->rclose++; - if((fmode&DMEXCL) != 0 &&f->mf->open) - error("exclusive use file already open"); - if(mode&OTRUNC){ - z = 0; - dfwattr(fid->file, "length", &z, sizeof z); - } - f->mf->open++; + nf = dfcreate(f, name, fid->uid, perm); + addelem(&fid->p, nf); + decref(nf); + nf->mf->open++; + noerror(); + rwunlock(f, Wr); fid->omode = mode&3; fid->loff = 0; fid->lidx = 0; - noerror(); - rwunlock(f, amode != AREAD); + if(mode&ORCLOSE) + fid->rclose++; } static void rcreate(Rpc *rpc) { Fid *fid; - Memblk *f, *nf; + Path *p; + Memblk *f; fid = getfid(rpc->cli, rpc->t.fid); rpc->fid = fid; - if(fid->omode != -1) - error("fid already open"); - fid->file = dfmelt(fid->file); - f = fid->file; - if(catcherror()){ - rwunlock(f, Wr); - error(nil); - } - if((f->mf->mode&DMDIR) == 0) - error("not a directory"); - dfaccessok(f, fid->uid, AWRITE); - if(strcmp(rpc->t.name, ".") == 0 || strcmp(rpc->t.name, "..") == 0) - error("that file name scares me"); - if(utfrune(rpc->t.name, '/') != nil) - error("that file name is too creepy"); - if((rpc->t.perm&DMDIR) != 0 && rpc->t.mode != OREAD) - error("wrong open mode for a directory"); - if(f == fs->root || f == fs->archive) - error("can't create there"); - if(fid->archived) - error("can't create in /archive"); /* yes, we can! */ - if(!catcherror()){ - mbput(dfwalk(f, rpc->t.name, 0)); - error("file already exists"); - } - nf = dfcreate(f, rpc->t.name, fid->uid, rpc->t.perm); - rpc->r.qid = mkqid(nf); + fidcreate(fid, rpc->t.name, rpc->t.mode, rpc->t.perm); + p = fid->p; + f = p->f[p->nf-1]; + rwlock(f, Rd); + rpc->r.qid = mkqid(f); + rwunlock(f, Rd); rpc->r.iounit = rpc->cli->msize-IOHDRSZ; - nf->mf->open++; - noerror(); - rwunlock(f, Wr); - mbput(fid->file); - fid->file = nf; - if(rpc->t.mode&ORCLOSE) - fid->rclose++; - fid->omode = rpc->t.mode&3; - fid->loff = 0; - fid->lidx = 0; } static ulong -readmf(Memblk *f, uchar *buf, int nbuf) +packmeta(Memblk *f, uchar *buf, int nbuf) { Dir d; + nulldir(&d); d.name = f->mf->name; d.qid = mkqid(f); d.mode = f->mf->mode; @@ -484,14 +594,14 @@ { Memblk *d, *f; ulong tot, nr; - - d = fid->file; + + d = fid->p->f[fid->p->nf-1]; for(tot = 0; tot+2 < ndata; tot += nr){ f = dfchild(d, fid->lidx); if(f == nil) break; - nr = readmf(f, data+tot, ndata-tot); + nr = packmeta(f, data+tot, ndata-tot); mbput(f); if(nr <= 2) break; @@ -500,154 +610,210 @@ return tot; } -static void -rread(Rpc *rpc) +long +fidread(Fid *fid, void *data, ulong count, vlong offset) { - Fid *fid; Memblk *f; - - fid = getfid(rpc->cli, rpc->t.fid); - rpc->fid = fid; - f = fid->file; + Path *p; if(fid->omode == -1) error("fid not open"); if(fid->omode == OWRITE) error("fid not open for reading"); - if(rpc->t.offset < 0) + if(offset < 0) error("negative offset"); - if(rpc->t.count > rpc->cli->msize-IOHDRSZ) - rpc->r.count = rpc->cli->msize-IOHDRSZ; - rpc->r.data = (char*)rpc->data; + p = fid->p; + f = p->f[p->nf-1]; + if(f == fs->cons) + return consread(data, count); rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); error(nil); } if(f->mf->mode&DMDIR){ - if(fid->loff != rpc->t.offset) - error("non-sequential dir read"); - rpc->r.count = readdir(fid, rpc->data, rpc->t.count, rpc->t.offset); - fid->loff += rpc->r.count; + if(fid->loff != offset) + error("non-sequential dir read not supported"); + count = readdir(fid, data, count, offset); + fid->loff += count; }else - rpc->r.count = dfpread(f, rpc->data, rpc->t.count, rpc->t.offset); + count = dfpread(f, data, count, offset); noerror(); rwunlock(f, Rd); + return count; +} + +static void +rread(Rpc *rpc) +{ + Fid *fid; + + fid = getfid(rpc->cli, rpc->t.fid); + rpc->fid = fid; + if(rpc->t.count > rpc->cli->msize-IOHDRSZ) + rpc->r.count = rpc->cli->msize-IOHDRSZ; + rpc->r.data = (char*)rpc->data; + rpc->r.count = fidread(fid, rpc->r.data, rpc->t.count, rpc->t.offset); + +} + +long +fidwrite(Fid *fid, void *data, ulong count, uvlong *offset) +{ + Memblk *f; + Path *p; + + if(fid->omode == -1) + error("fid not open"); + if(fid->omode == OREAD) + error("fid not open for writing"); + p = fid->p; + f = p->f[p->nf-1]; + if(f == fs->cons) + return conswrite(data, count); + p = dfmelt(&fid->p, fid->p->nf); + f = p->f[p->nf-1]; + if(catcherror()){ + rwunlock(f, Wr); + error(nil); + } + count = dfpwrite(f, data, count, offset); + rwunlock(f, Wr); + noerror(); + return count; } static void rwrite(Rpc *rpc) { Fid *fid; - ulong n; + uvlong off; + if(rpc->t.offset < 0) + error("negative offset"); fid = getfid(rpc->cli, rpc->t.fid); rpc->fid = fid; - - if(fid->omode == -1) - error("fid not open"); - if(fid->omode == OREAD) - error("fid not open for writing"); - if(rpc->t.offset < 0) - error("negative offset"); - n = rpc->t.count; - if(n > rpc->cli->msize) - n = rpc->cli->msize; /* hmmm */ - fid->file = dfmelt(fid->file); - if(catcherror()){ - rwunlock(fid->file, Wr); - error(nil); - } - rpc->r.count = dfpwrite(fid->file, rpc->t.data, n, rpc->t.offset); - noerror(); + off = rpc->t.offset; + rpc->r.count = fidwrite(fid, rpc->t.data, rpc->t.count, &off); } +void +fidclose(Fid *fid) +{ + Memblk *f, *fp; + Path *p; + + p = fid->p; + f = p->f[p->nf-1]; + rwlock(f, Wr); + f->mf->open--; + rwunlock(f, Wr); + fid->omode = -1; + if(fid->rclose){ + p = dfmelt(&fid->p, fid->p->nf-1); + fp = p->f[p->nf-2]; + rwlock(f, Wr); + if(catcherror()){ + rwunlock(f, Wr); + mbput(f); + }else{ + dfremove(fp, f); + fid->p->nf--; + noerror(); + } + rwunlock(fp, Wr); + } + putpath(fid->p); + fid->p = nil; + fid->consopen = 0; +} static void rclunk(Rpc *rpc) { Fid *fid; - Memblk *f, *p; fid = getfid(rpc->cli, rpc->t.fid); rpc->fid = fid; - f = fid->file; - if(fid->omode != -1){ - rwlock(f, Wr); - f->mf->open--; - rwunlock(f, Wr); - fid->omode = -1; - if(fid->rclose){ - f->mf->parent = dfmelt(f->mf->parent); - p = f->mf->parent; - rwlock(f, Wr); - if(catcherror()){ - rwunlock(f, Wr); - mbput(f); - }else{ - dfremove(p, f); - noerror(); - } - rwunlock(p, Wr); - } - fid->file = nil; - } + if(fid->omode != -1) + fidclose(fid); + d9print("clunking %X\n\n", fid); putfid(fid); putfid(fid); rpc->fid = nil; } +void +fidremove(Fid *fid) +{ + Memblk *f, *fp; + Path *p; + + p = fid->p; + f = p->f[p->nf-1]; + if(fid->archived || f == fs->cons || f == fs->active) + error("can't remove archived or built-in files"); + p = dfmelt(&fid->p, fid->p->nf-1); + fp = p->f[p->nf-2]; + f = p->f[p->nf-1]; + rwlock(f, Wr); + if(catcherror()){ + rwunlock(f, Wr); + rwunlock(fp, Wr); + error(nil); + } + if(fp->mf->mode&DMAPPEND) + error("directory is append only"); + dfaccessok(fp, fid->uid, AWRITE); + fid->omode = -1; + dfremove(fp, f); + fid->p->nf--; + noerror(); + rwunlock(fp, Wr); + putpath(fid->p); + fid->p = nil; +} static void rremove(Rpc *rpc) { Fid *fid; - Memblk *f, *p; fid = getfid(rpc->cli, rpc->t.fid); rpc->fid = fid; - f = fid->file; - if(f == fs->root || f == fs->active || f == fs->archive) - error("can't remove that"); - if(fid->archived) - error("can't remove in /archive"); /* yes, we can! */ - - f->mf->parent = dfmelt(f->mf->parent); - p = f->mf->parent; - rwlock(f, Wr); if(catcherror()){ - rwunlock(f, Wr); - rwunlock(p, Wr); + d9print("clunking %X\n\n", fid); + putfid(fid); + putfid(fid); + rpc->fid = nil; error(nil); } - dfaccessok(p, fid->uid, AWRITE); - fid->omode = -1; - dfremove(p, f); + fidremove(fid); noerror(); - rwunlock(p, Wr); - fid->file = nil; + d9print("clunking %X\n\n", fid); putfid(fid); putfid(fid); rpc->fid = nil; } - static void rstat(Rpc *rpc) { Fid *fid; Memblk *f; + Path *p; fid = getfid(rpc->cli, rpc->t.fid); rpc->fid = fid; - f = fid->file; + p = fid->p; + f = p->f[p->nf-1]; rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); error(nil); } rpc->r.stat = rpc->data; - rpc->r.nstat = readmf(f, rpc->data, sizeof rpc->data); + rpc->r.nstat = packmeta(f, rpc->data, rpc->cli->msize-IOHDRSZ); if(rpc->r.nstat <= 2) fatal("rstat: convD2M"); noerror(); @@ -671,25 +837,25 @@ { Fid *fid; Memblk *f; + Path *p; Dir d, *sd; u64int n; fid = getfid(rpc->cli, rpc->t.fid); rpc->fid = fid; - f = fid->file; - - - if(f == fs->root || f == fs->archive || fid->archived) - error("can't wstat there"); - fid->file = dfmelt(fid->file); + p = fid->p; + f = p->f[p->nf-1]; + if(fid->archived || f == fs->cons) + error("can't wstat archived or built-in files"); + p = dfmelt(&fid->p, fid->p->nf); + f = p->f[p->nf-1]; n = convM2D(rpc->t.stat, rpc->t.nstat, &d, nil); sd = malloc(n); if(catcherror()){ - rwunlock(fid->file, Wr); + rwunlock(f, Wr); free(sd); error(nil); } - f = fid->file; n = convM2D(rpc->t.stat, rpc->t.nstat, sd, (char*)&sd[1]); if(n <= BIT16SZ){ free(sd); @@ -703,9 +869,9 @@ sd->length = ~0; if(sd->name[0] && strcmp(f->mf->name, sd->name) != 0){ - if(f == fs->active) - error("can't rename /active"); - dfaccessok(f->mf->parent, fid->uid, AWRITE); + if(isro(f) || f == fs->active) + error("can't rename built-in files"); + dfaccessok(p->f[p->nf-2], fid->uid, AWRITE); if(!catcherror()){ mbput(dfwalk(f, sd->name, 0)); error("file already exists"); @@ -716,17 +882,21 @@ if(sd->uid[0] != 0 && strcmp(sd->uid, f->mf->uid) != 0){ if(!fs->config && strcmp(fid->uid, f->mf->uid) != 0) error("only the owner may donate a file"); + if(!fs->config && !member(sd->uid, fid->uid) != 0) + error("you are not in that group"); }else sd->uid[0] = 0; if(sd->gid[0] != 0 && strcmp(sd->gid, f->mf->gid) != 0){ if(!fs->config && strcmp(fid->uid, f->mf->uid) != 0) error("only the onwer may change group"); + if(!fs->config && !member(sd->gid, fid->uid) != 0) + error("you are not in that group"); }else sd->gid[0] = 0; if(sd->mode != ~0 && f->mf->mode != sd->mode){ if(!fs->config && strcmp(fid->uid, f->mf->uid) != 0 && - strcmp(fid->uid, f->mf->gid) != 0) - error("only the onwer may change mode"); + !member(f->mf->gid, fid->uid) != 0) + error("only the onwer or members may change mode"); }else sd->mode = ~0; @@ -753,7 +923,7 @@ } -static void +void replied(Rpc *rpc) { Rpc **rl; @@ -764,17 +934,17 @@ *rl = rpc->next; break; } + rpc->cli->nrpcs--; qunlock(&rpc->cli->rpclk); rpc->next = nil; putfid(rpc->fid); rpc->fid = nil; putcli(rpc->cli); rpc->cli = nil; - freerpc(rpc); } static char* -rpcworker(void *v, void**aux) +rpcworker9p(void *v, void**aux) { Rpc *rpc; Cli *cli; @@ -783,8 +953,8 @@ rpc = v; cli = rpc->cli; - threadsetname("cliproc %s rpc", cli->addr); - d9print("cliproc %s rpc starting\n", cli->addr); + threadsetname("rpcworker9p %s %R", cli->addr, rpc); + dPprint("%s starting\n", threadgetname()); if(*aux == nil){ errinit(Errstack); @@ -805,151 +975,83 @@ noerror(); out: - d9print("-> %F\n", &rpc->r); qlock(&cli->wlk); - n = convS2M(&rpc->r, cli->wdata, sizeof cli->wdata); - if(n == 0) - fatal("rpcworker: convS2M"); - if(write(cli->fd, cli->wdata, n) != n) - d9print("%s: %r\n", cli->addr); + if(rpc->flushed == 0){ + d9print("-> %F\n", &rpc->r); + n = convS2M(&rpc->r, cli->wdata, sizeof cli->wdata); + if(n == 0) + fatal("rpcworker: convS2M"); + if(write(cli->fd, cli->wdata, n) != n) + d9print("%s: %r\n", cli->addr); + }else + d9print("flushed: %F\n", &rpc->r); qunlock(&cli->wlk); - d9print("cliproc %s rpc exiting\n", cli->addr); replied(rpc); + freerpc(rpc); + dPprint("%s exiting\n", threadgetname()); return nil; } -static char* -cliworker(void *v, void**) +char* +cliworker9p(void *v, void**aux) { - Cli *c; + Cli *cli; long n; Rpc *rpc; - c = v; - threadsetname("cliproc %s", c->addr); - d9print("cliproc %s started\n", c->addr); + cli = v; + threadsetname("cliworker9p %s", cli->addr); + dPprint("%s started\n", threadgetname()); + if(*aux == nil){ + errinit(Errstack); + *aux = v; /* make it not nil */ + } + + if(catcherror()) + fatal("worker: uncatched: %r"); rpc = nil; for(;;){ if(rpc == nil) rpc = newrpc(); - n = read9pmsg(c->fd, rpc->data, sizeof rpc->data); + n = read9pmsg(cli->fd, rpc->data, Maxmdata+IOHDRSZ); if(n < 0){ - d9print("%s: read: %r\n", c->addr); + d9print("%s: read: %r\n", cli->addr); break; } if(n == 0) continue; if(convM2S(rpc->data, n, &rpc->t) == 0){ - d9print("%s: convM2S failed\n", c->addr); + d9print("%s: convM2S failed\n", cli->addr); continue; } if(rpc->t.type >= nelem(fcalls) || fcalls[rpc->t.type] == nil){ - d9print("%s: bad fcall type %d\n", c->addr, rpc->t.type); + d9print("%s: bad fcall type %d\n", cli->addr, rpc->t.type); continue; } - if(dbg['0']) - fprint(2, "<-%F\n", &rpc->t); - rpc->cli = c; - incref(c); + d9print("<-%F\n", &rpc->t); + rpc->cli = cli; + incref(cli); - qlock(&c->rpclk); - rpc->next = c->rpcs; - c->rpcs = rpc; - qunlock(&c->rpclk); + qlock(&cli->rpclk); + rpc->next = cli->rpcs; + cli->rpcs = rpc; + cli->nrpcs++; + qunlock(&cli->rpclk); - getworker(rpcworker, rpc, nil); + fspolicy(); + if(rpc->t.type == Tflush || + (Rpcspercli != 0 && cli->nrpcs >= Rpcspercli)) + rpcworker9p(rpc, aux); + else + getworker(rpcworker9p, rpc, nil); + rpc = nil; } - d9print("cliproc %s exiting\n", c->addr); - putcli(c); + putcli(cli); + noerror(); + dPprint("%s exiting\n", threadgetname()); return nil; }; -static char* -getremotesys(char *ndir) -{ - char buf[128], *serv, *sys; - int fd, n; - snprint(buf, sizeof buf, "%s/remote", ndir); - sys = nil; - fd = open(buf, OREAD); - if(fd >= 0){ - n = read(fd, buf, sizeof(buf)-1); - if(n>0){ - buf[n-1] = 0; - serv = strchr(buf, '!'); - if(serv) - *serv = 0; - sys = strdup(buf); - } - close(fd); - } - if(sys == nil) - sys = strdup("unknown"); - return sys; -} - -static void -postfd(char *name, int pfd) -{ - int fd; - - remove(name); - fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600); - if(fd < 0) - fatal("postfd: %r\n"); - if(fprint(fd, "%d", pfd) < 0){ - close(fd); - fatal("postfd: %r\n"); - } -} - -void -srv9p(char *srv) -{ - Cli *cli; - int fd[2]; - char *name; - - name = smprint("/srv/%s", srv); - if(pipe(fd) < 0) - fatal("pipe: %r"); - postfd(name, fd[1]); - cli = mallocz(sizeof *cli, 1); - cli->fd = fd[0]; - cli->cfd = -1; - cli->addr = name; - cli->ref = 1; - getworker(cliworker, cli, nil); -} - -void -listen9p(char *addr) -{ - Cli *cli; - char ndir[NETPATHLEN], dir[NETPATHLEN]; - int ctl, data, nctl; - - ctl = announce(addr, dir); - if(ctl < 0) - fatal("announce %s: %r", addr); - for(;;){ - nctl = listen(dir, ndir); - if(nctl < 0) - fatal("listen %s: %r", addr); - data = accept(nctl, ndir); - if(data < 0){ - fprint(2, "%s: accept %s: %r\n", argv0, ndir); - continue; - } - cli = mallocz(sizeof *cli, 1); - cli->fd = data; - cli->cfd = nctl; - cli->addr = getremotesys(ndir); - cli->ref = 1; - getworker(cliworker, cli, nil); - } -} - diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/9pix.c --- a/sys/src/cmd/creepy/9pix.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/9pix.c Wed Feb 22 16:04:52 2012 +0100 @@ -4,359 +4,154 @@ #include #include #include +#include #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" -enum +static void +postfd(char *name, int pfd) { - Nels = 64 -}; + int fd; -static char *fsdir; -static int verb; - -/* - * Walks elems starting at f. - * Ok if nelems is 0. - */ -static Memblk* -walkpath(Memblk *f, char *elems[], int nelems) -{ - int i; - Memblk *f0, *nf; - - isfile(f); - f0 = f; - for(i = 0; i < nelems; i++){ - if((f->mf->mode&DMDIR) == 0) - error("not a directory"); - rwlock(f, Rd); - if(catcherror()){ - if(f != f0) - mbput(f); - rwunlock(f, Rd); - error("walk: %r"); - } - nf = dfwalk(f, elems[i], 0); - rwunlock(f, Rd); - if(f != f0) - mbput(f); - f = nf; - USED(&f); /* in case of error() */ - noerror(); + remove(name); + fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600); + if(fd < 0) + fatal("postfd: %r\n"); + if(fprint(fd, "%d", pfd) < 0){ + close(fd); + fatal("postfd: %r\n"); } - if(f == f0) - incref(f); - return f; + close(pfd); } static char* -fsname(char *p) +getremotesys(char *ndir) { - if(p[0] == '/') - return strdup(p); - if(fsdir) - return smprint("%s/%s", fsdir, p); - return strdup(p); + char buf[128], *serv, *sys; + int fd, n; + + snprint(buf, sizeof buf, "%s/remote", ndir); + sys = nil; + fd = open(buf, OREAD); + if(fd >= 0){ + n = read(fd, buf, sizeof(buf)-1); + if(n>0){ + buf[n-1] = 0; + serv = strchr(buf, '!'); + if(serv) + *serv = 0; + sys = strdup(buf); + } + close(fd); + } + if(sys == nil) + sys = strdup("unknown"); + return sys; } -static Memblk* -walkto(char *a, char **lastp) +void +srv9pix(char *srv, char* (*cliworker)(void *arg, void **aux)) { - char *els[Nels], *path; - int nels; - Memblk *f; + Cli *cli; + int fd[2]; + char *name; - path = fsname(a); - nels = gettokens(path, els, Nels, "/"); - if(nels < 1){ - free(path); - error("invalid path"); - } - if(catcherror()){ - free(path); - error("walkpath: %r"); - } - if(lastp != nil){ - f = walkpath(fs->root, els, nels-1); - *lastp = a + strlen(a) - strlen(els[nels-1]); - }else - f = walkpath(fs->root, els, nels); - free(path); - noerror(); - if(verb) - print("walked to %H\n", f); - return f; + name = smprint("/srv/%s", srv); + if(pipe(fd) < 0) + fatal("pipe: %r"); + postfd(name, fd[0]); + consprint("listen %s\n", srv); + cli = newcli(name, fd[1], -1); + getworker(cliworker, cli, nil); } -static void -fscd(int, char *argv[]) +void +listen9pix(char *addr, char* (*cliworker)(void *arg, void **aux)) { - free(fsdir); - fsdir = strdup(argv[1]); -} + Cli *cli; + char ndir[NETPATHLEN], dir[NETPATHLEN]; + int ctl, data, nctl; -static void -fsput(int, char *argv[]) -{ - int fd; - char *fn; - Memblk *m, *f; - Dir *d; - char buf[4096]; - uvlong off; - long nw, nr; - - fd = open(argv[1], OREAD); - if(fd < 0) - error("open: %r\n"); - d = dirfstat(fd); - if(d == nil){ - error("dirfstat: %r\n"); + ctl = announce(addr, dir); + if(ctl < 0) + fatal("announce %s: %r", addr); + consprint("listen %s\n", addr); + for(;;){ + nctl = listen(dir, ndir); + if(nctl < 0) + fatal("listen %s: %r", addr); + data = accept(nctl, ndir); + if(data < 0){ + fprint(2, "%s: accept %s: %r\n", argv0, ndir); + continue; + } + cli = newcli(getremotesys(ndir), data, nctl); + getworker(cliworker, cli, nil); } - if(catcherror()){ - close(fd); - free(d); - error(nil); - } - m = walkto(argv[2], &fn); - m = dfmelt(m); - if(catcherror()){ - rwunlock(m, Wr); - mbput(m); - error(nil); - } - f = dfcreate(m, fn, d->uid, d->mode&(DMDIR|0777)); - rwlock(f, Wr); - if(catcherror()){ - rwunlock(f, Wr); - mbput(f); - error(nil); - } - if((d->mode&DMDIR) == 0){ - off = 0; - for(;;){ - nr = read(fd, buf, sizeof buf); - if(nr <= 0) - break; - nw = dfpwrite(f, buf, nr, off); - dDprint("wrote %ld of %ld bytes\n", nw, nr); - off += nr; - } - } - noerror(); - noerror(); - noerror(); - if(verb) - print("created %H\nat %H\n", f, m); - rwunlock(f, Wr); - rwunlock(m, Wr); - mbput(m); - mbput(f); - close(fd); - free(d); -} - -static void -fscat(int, char *argv[]) -{ - Memblk *f; - Mfile *m; - char buf[4096]; - uvlong off; - long nr; - - f = walkto(argv[2], nil); - rwlock(f, Rd); - if(catcherror()){ - rwunlock(f, Rd); - mbput(f); - error(nil); - } - m = f->mf; - print("cat %-30s\t%M\t%5ulld\t%s %ulld refs\n", - m->name, (ulong)m->mode, m->length, m->uid, dbgetref(f->addr)); - if((m->mode&DMDIR) == 0){ - off = 0; - for(;;){ - nr = dfpread(f, buf, sizeof buf, off); - if(nr <= 0) - break; - write(1, buf, nr); - off += nr; - } - } - noerror(); - rwunlock(f, Rd); - mbput(f); -} - -static void -fsget(int, char *argv[]) -{ - Memblk *f; - Mfile *m; - char buf[4096]; - uvlong off; - long nr; - int fd; - - fd = create(argv[1], OWRITE, 0664); - if(fd < 0) - error("create: %r\n"); - if(catcherror()){ - close(fd); - error(nil); - } - f = walkto(argv[2], nil); - rwlock(f, Rd); - if(catcherror()){ - rwunlock(f, Rd); - mbput(f); - error(nil); - } - m = f->mf; - print("get %-30s\t%M\t%5ulld\t%s %ulld refs\n", - m->name, (ulong)m->mode, m->length, m->uid, dbgetref(f->addr)); - if((m->mode&DMDIR) == 0){ - off = 0; - for(;;){ - nr = dfpread(f, buf, sizeof buf, off); - if(nr <= 0) - break; - if(write(fd, buf, nr) != nr){ - fprint(2, "%s: error: %r\n", argv[0]); - break; - } - off += nr; - } - } - close(fd); - rwunlock(f, Rd); - noerror(); - noerror(); - mbput(f); -} - -static void -fsls(int, char**) -{ - if(verb) - fsdump(1); - else - fslist(); -} - -static void -fssnap(int, char**) -{ - fssync(); -} - -static void -fsrcl(int, char**) -{ - fsreclaim(); -} - -static void -fsdmp(int, char**) -{ - fsdump(0); -} - -static void -fsdmpall(int, char**) -{ - fsdump(1); -} - -static void -fsdbg(int, char *argv[]) -{ - dbg['D'] = atoi(argv[1]); -} - -static void -fsout(int, char*[]) -{ - fslowmem(); -} - -static void -fsrm(int, char *argv[]) -{ - Memblk *f, *p; - - f = walkto(argv[1], nil); - if(catcherror()){ - mbput(f); - error(nil); - } - f->mf->parent = dfmelt(f->mf->parent); - p = f->mf->parent; - rwlock(f, Wr); - if(catcherror()){ - rwunlock(f, Wr); - rwunlock(p, Wr); - error(nil); - } - dfremove(p, f); - noerror(); - noerror(); - rwunlock(p, Wr); } static void usage(void) { - fprint(2, "usage: %s [-DFLAGS] [-v] [-s file] [-9 addr] disk\n", argv0); + fprint(2, "usage: %s [-DFLAGS] [-n addr] disk\n", argv0); exits("usage"); } +int mainstacksize = Stack; + void threadmain(int argc, char *argv[]) { char *addr, *dev, *srv; - addr = "tcp!localhost!6699"; - srv = "creepy"; + addr = nil; + srv = "9pix"; ARGBEGIN{ - case 'v': - verb++; - break; - case 's': - srv = EARGF(usage()); - break; - case '9': + case 'n': addr = EARGF(usage()); break; default: - if(ARGC() >= 'A' && ARGC() <= 'Z'){ + if(ARGC() >= 'A' && ARGC() <= 'Z' || ARGC() == '9'){ dbg['d'] = 1; dbg[ARGC()] = 1; }else usage(); + dbg['x'] = dbg['X']; }ARGEND; if(argc != 1) usage(); dev = argv[0]; - + + workerthreadcreate = proccreate; fmtinstall('H', mbfmt); fmtinstall('M', dirmodefmt); + fmtinstall('F', fcallfmt); + fmtinstall('G', ixcallfmt); + fmtinstall('X', fidfmt); + fmtinstall('R', rpcfmt); errinit(Errstack); if(catcherror()) - fatal("error: %r"); + fatal("uncatched error: %r"); rfork(RFNAMEG); + parseusers(defaultusers); fsopen(dev); if(srv != nil) - srv9p(srv); + srv9pix(srv, cliworker9p); if(addr != nil) - listen9p(addr); + listen9pix(addr, cliworker9p); + + /* + * fsstats(); + * ninestats(); + * ixstats(); + */ + consinit(); noerror(); - exits(nil); + threadexits(nil); } diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/attr.c --- a/sys/src/cmd/creepy/attr.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/attr.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,10 +8,16 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" /* * Attribute handling + * + * BUG: we only support the predefined attributes. + * Just store/parse a sequence of name[s] sz[2] value[sz] + * after predefined attributes. */ typedef struct Adef Adef; @@ -22,6 +28,7 @@ int sz; long (*wattr)(Memblk*, void*, long); long (*rattr)(Memblk*, void*, long); + void (*cattr)(Memblk*, int, void*, long, void*, long); }; long wname(Memblk*, void*, long); @@ -33,14 +40,27 @@ static long rmtime(Memblk*, void*, long); static long wlength(Memblk*, void*, long); static long rlength(Memblk*, void*, long); +static long wuid(Memblk*, void*, long); +static long ruid(Memblk*, void*, long); +static long wgid(Memblk*, void*, long); +static long rgid(Memblk*, void*, long); +static long wmuid(Memblk*, void*, long); +static long rmuid(Memblk*, void*, long); +static long rstar(Memblk*, void*, long); +static void cstring(Memblk*, int, void*, long, void*, long); +static void cu64int(Memblk*, int, void*, long, void*, long); static Adef adef[] = { - {"name", 0, wname, rname}, - {"id", BIT64SZ, nil, rid}, - {"atime", BIT64SZ, watime, ratime}, - {"mtime", BIT64SZ, wmtime, rmtime}, - {"length", BIT64SZ, wlength, rlength}, + {"name", 0, wname, rname, cstring}, + {"id", BIT64SZ, nil, rid, cu64int}, + {"atime", BIT64SZ, watime, ratime, cu64int}, + {"mtime", BIT64SZ, wmtime, rmtime, cu64int}, + {"length", BIT64SZ, wlength, rlength, cu64int}, + {"uid", 0, wuid, ruid, cstring}, + {"gid", 0, wgid, rgid, cstring}, + {"muid", 0, wuid, ruid, cstring}, + {"*", 0, nil, rstar, nil}, }; /* @@ -169,6 +189,66 @@ return sz; } +static void +ceval(int op, int v) +{ + switch(op){ + case CEQ: + if(v != 0) + error("false"); + break; + case CGE: + if(v < 0) + error("false"); + break; + case CGT: + if(v <= 0) + error("false"); + break; + case CLE: + if(v > 0) + error("false"); + break; + case CLT: + if(v >= 0) + error("false"); + case CNE: + if(v == 0) + error("false"); + break; + } +} + +static void +cstring(Memblk*, int op, void *buf, long, void *val, long len) +{ + char *p; + + p = val; + if(len < 1 || p[len-1] != 0) + error("value must end in \\0"); + ceval(op, strcmp(buf, val)); +} + +static void +cu64int(Memblk*, int op, void *buf, long, void *val, long) +{ + u64int v1, v2; + uchar *p1, *p2; + + p1 = buf; + p2 = val; + v1 = GBIT64(p1); + v2 = GBIT64(p2); + /* avoid overflow */ + if(v1 > v2) + ceval(op, 1); + else if(v1 < v2) + ceval(op, -1); + else + ceval(op, 0); +} + long wname(Memblk *f, void *buf, long len) { @@ -191,16 +271,124 @@ return len; } +static long +rstr(char *s, void *buf, long len) +{ + long l; + + l = strlen(s) + 1; + if(l > len) + error("buffer too short"); + strcpy(buf, s); + return l; +} + static long rname(Memblk *f, void *buf, long len) { - long l; + return rstr(f->mf->name, buf, len); +} - l = strlen(f->mf->name) + 1; - if(l > len) - error("buffer too short"); - strcpy(buf, f->mf->name); - return l; +static long +ruid(Memblk *f, void *buf, long len) +{ + return rstr(f->mf->uid, buf, len); +} + +static long +wuid(Memblk *f, void *buf, long len) +{ + char *p; + ulong maxsz; + Fmeta m; + + p = buf; + if(len < 1 || p[len-1] != 0) + error("name must end in \\0"); + maxsz = embedattrsz(f); + m = *f->mf; + m.uid = buf; + m.gid = strdup(m.gid); + m.muid = strdup(m.muid); + m.name = strdup(m.name); + if(metasize(&m) > maxsz){ + fprint(2, "%s: bug: no attribute block implemented\n", argv0); + error("no room to grow metadata"); + } + f->mf->Fmeta = m; + pmeta(f->d.embed, maxsz, f->mf); + free(m.gid); + free(m.muid); + free(m.name); + return len; +} + +static long +rgid(Memblk *f, void *buf, long len) +{ + return rstr(f->mf->gid, buf, len); +} + +static long +wgid(Memblk *f, void *buf, long len) +{ + char *p; + ulong maxsz; + Fmeta m; + + p = buf; + if(len < 1 || p[len-1] != 0) + error("name must end in \\0"); + maxsz = embedattrsz(f); + m = *f->mf; + m.uid = strdup(m.uid); + m.gid = buf; + m.muid = strdup(m.muid); + m.name = strdup(m.name); + if(metasize(&m) > maxsz){ + fprint(2, "%s: bug: no attribute block implemented\n", argv0); + error("no room to grow metadata"); + } + f->mf->Fmeta = m; + pmeta(f->d.embed, maxsz, f->mf); + free(m.uid); + free(m.muid); + free(m.name); + return len; +} + +static long +rmuid(Memblk *f, void *buf, long len) +{ + return rstr(f->mf->muid, buf, len); +} + +static long +wmuid(Memblk *f, void *buf, long len) +{ + char *p; + ulong maxsz; + Fmeta m; + + p = buf; + if(len < 1 || p[len-1] != 0) + error("name must end in \\0"); + maxsz = embedattrsz(f); + m = *f->mf; + m.uid = strdup(m.uid); + m.gid = strdup(m.gid); + m.muid = buf; + m.name = strdup(m.name); + if(metasize(&m) > maxsz){ + fprint(2, "%s: bug: no attribute block implemented\n", argv0); + error("no room to grow metadata"); + } + f->mf->Fmeta = m; + pmeta(f->d.embed, maxsz, f->mf); + free(m.uid); + free(m.gid); + free(m.name); + return len; } static long @@ -304,6 +492,22 @@ return BIT64SZ; } +static long +rstar(Memblk *, void *buf, long len) +{ + char *s, *e; + int i; + + s = buf; + e = s + len; + for(i = 0; i < nelem(adef); i++) + if(strcmp(adef[i].name, "*") != 0) + s = seprint(s, e, "%s ", adef[i].name); + if(s > buf) + *--s = 0; + return s - (char*)buf; +} + long dfwattr(Memblk *f, char *name, void *val, long nval) { @@ -320,7 +524,7 @@ if(strcmp(adef[i].name, name) == 0) break; if(i == nelem(adef)) - error("user defined attributes not yet implemented"); + error("bug: user defined attributes not yet implemented"); if(adef[i].wattr == nil) error("can't write %s", name); if(adef[i].sz != 0 && adef[i].sz != nval) @@ -351,11 +555,26 @@ return tot; } -static int -member(char *gid, char *uid) +void +dfcattr(Memblk *f, int op, char *name, void *val, long count) { - /* BUG: no groups */ - return strcmp(gid, uid) == 0; + int i; + long nbuf; + char buf[128]; + + isfile(f); + isrwlocked(f, Rd); + + nbuf = dfrattr(f, name, buf, sizeof buf); + + for(i = 0; i < nelem(adef); i++) + if(strcmp(adef[i].name, name) == 0) + break; + if(i == nelem(adef)) + error("no such attribute"); + if(adef[i].sz != 0 && count != adef[i].sz) + error("value size does not match"); + adef[i].cattr(f, op, buf, nbuf, val, count); } void diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/cfg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/creepy/cfg.c Wed Feb 22 16:04:52 2012 +0100 @@ -0,0 +1,419 @@ +#include +#include +#include +#include +#include +#include + +#include "conf.h" +#include "dbg.h" +#include "dk.h" +#include "ix.h" +#include "net.h" +#include "fns.h" + +/* + * CAUTION: We keep the format used in fossil, but, + * creepy rules are simpler: + * names are ignored. uids are names: + * Nemo is nemo and nobody else is nemo, + * new users should pick a different name. that one is taken. + * there is no leadership. + * members may chown files to the group, may chmod files in the group, + * and may chgrp files to the group. + * Plus, to donate a file, you must be the owner. + * + * sorry. 9 rules were too complex to remember. + */ + +typedef struct Usr Usr; +typedef struct Member Member; + +struct Member +{ + Member *next; + char *uid; + Usr *u; +}; + +struct Usr +{ + Usr *next; + char *uid; + char *lead; + Member *members; +}; + +char *defaultusers = + "adm:adm:adm:sys\n" + "elf:elf:elf:sys\n" + "none:none::\n" + "noworld:noworld::\n" + "sys:sys::glenda\n" + "glenda:glenda:glenda:\n"; + +static Usr *usrs[Uhashsz]; + +static uint +uhash(char* s) +{ + uchar *p; + u32int hash; + + hash = 0; + for(p = (uchar*)s; *p != '\0'; p++) + hash = hash*7 + *p; + + return hash % Uhashsz; +} + +static Usr* +findusr(char *uid) +{ + Usr *u; + + for(u = usrs[uhash(uid)]; u != nil; u = u->next) + if(strcmp(u->uid, uid) == 0) + return u; + return nil; +} + +int +member(char *uid, char *member) +{ + Usr *u; + Member *m; + + if(strcmp(uid, member) == 0) + return 1; + u = findusr(uid); + if(u == nil) + return 0; + for(m = u->members; m != nil; m = m->next) + if(strcmp(member, m->uid) == 0) + return 1; + return 0; +} + +static Usr* +mkusr(char *uid, char *lead) +{ + Usr *u; + uint h; + + h = uhash(uid); + for(u = usrs[h]; u != nil; u = u->next) + if(strcmp(u->uid, uid) == 0) + error("dup uid %s", uid); + + u = mallocz(sizeof *u, 1); + u->uid = strdup(uid); + if(lead != 0) + u->lead = strdup(lead); + u->next = usrs[h]; + usrs[h] = u; + return u; +} + +static void +addmember(Usr *u, char *n) +{ + Member *m; + + for(m = u->members; m != nil; m = m->next) + if(strcmp(m->uid, n) == 0) + error("%s already a member of %s", n, u->uid); + m = mallocz(sizeof *m, 1); + m->uid = strdup(n); + m->next = u->members; + u->members = m; +} + +static void +freemember(Member *m) +{ + if(m == nil) + return; + free(m->uid); + free(m); +} + +static void +freeusr(Usr *u) +{ + Member *m; + + if(u == nil) + return; + while(u->members != nil){ + m = u->members; + u->members = m->next; + freemember(m); + } + free(u->uid); + free(u->lead); + free(u); +} + +void +clearusers(void) +{ + Usr *u; + int i; + + for(i = 0; i < nelem(usrs); i++) + while(usrs[i] != nil){ + u = usrs[i]; + usrs[i] = u->next; + freeusr(u); + } +} + +static void +checkmembers(Usr *u) +{ + Member *m; + + d9print("checkmembers %s\n", u->uid); + for(m = u->members; m != nil; m = m->next) + if((m->u = findusr(m->uid)) == nil){ + fprint(2, "no user '%s'\n", m->uid); + consprint("no user '%s'\n", m->uid); + } +} + +void +parseusers(char *u) +{ + char *c, *nc, *p, *np, *args[5]; + int nargs, i; + Usr *usr; + + u = strdup(u); + if(catcherror()){ + free(u); + error(nil); + } + p = u; + do{ + np = utfrune(p, '\n'); + if(np != nil) + *np++ = 0; + c = utfrune(p, '#'); + if(c != nil) + *c = 0; + if(catcherror()){ + fprint(2, "users: %r\n"); + consprint("users: %r\n"); + continue; + } + if(*p == 0) + continue; + nargs = getfields(p, args, nelem(args), 0, ":"); + if(nargs != 4) + error("wrong number of fields %s", args[0]); + if(*args[0] == 0 || *args[1] == 0) + error("null uid or name"); + usr = mkusr(args[0], args[2]); + for(c = args[3]; c != nil; c = nc){ + if(*c == 0) + break; + nc = utfrune(c, ','); + if(nc != nil) + *nc++ = 0; + addmember(usr, c); + } + noerror(); + }while((p = np) != nil); + for(i = 0; i < nelem(usrs); i++) + for(usr = usrs[i]; usr != nil; usr = usr->next) + checkmembers(usr); + noerror(); + free(u); +} + +/* + * TODO: register multiple fids for the cons file by keeping a list + * of console channels. + * consread will have to read from its per-fid channel. + * conprint will have to bcast to all channels. + * + * With that, multiple users can share the same console. + * Although perhaps it would be easier to use C in that case. + */ + +void +consprint(char *fmt, ...) +{ + va_list arg; + char *s, *x; + + va_start(arg, fmt); + s = vsmprint(fmt, arg); + va_end(arg); + /* consume some message if the channel is full */ + while(nbsendp(fs->consc, s) == 0) + if((x = nbrecvp(fs->consc)) != nil) + free(x); +} + +long +consread(char *buf, long count) +{ + char *s; + int tot, nr; + + if(count <= 0) /* shouldn't happen */ + return 0; + s = recvp(fs->consc); + tot = 0; + do{ + nr = strlen(s); + if(tot + nr > count) + nr = count - tot; + memmove(buf+tot, s, nr); + tot += nr; + free(s); + }while((s = nbrecvp(fs->consc)) != nil && tot + 80 < count); + /* + * +80 to try to guarantee that we have enough room in the user + * buffer for the next received string, or we'd drop part of it. + * Most of the times each string is a rune typed by the user. + * Other times, it's the result of a consprint() call. + */ + return tot; +} + +static void +cdump(int, char *argv[]) +{ + fsdump(strcmp(argv[0], "dumpall") == 0); +} + +static void +csync(int, char**) +{ + fssync(); + consprint("synced\n"); +} + +static void +chalt(int, char**) +{ + fssync(); + threadexitsall(nil); +} + +static void +cusers(int argc, char *argv[]) +{ + int i; + Usr *u; + + switch(argc){ + case 1: + for(i = 0; i < nelem(usrs); i++) + for(u = usrs[i]; u != nil; u = u->next) + consprint("%s\n", u->uid); + break; + case 2: + if(strcmp(argv[1], "-r") == 0) + consprint("-r not implemented\n"); + else if(strcmp(argv[1], "-w") == 0) + consprint("-w not implemented\n"); + else + consprint("usage: users [-r|-w]\n"); + break; + default: + consprint("usage: users [-r|-w]\n"); + } +} + +static void chelp(int, char**); + +static Cmd cmds[] = +{ + {"dump", cdump, 1, "dump"}, + {"dumpall", cdump, 1, "dumpall"}, + {"sync", csync, 1, "sync"}, + {"halt", chalt, 1, "halt"}, + {"users", cusers, 0, "users [-r|-w]"}, + {"?", chelp, 1, "?"}, +}; + +static void +chelp(int, char**) +{ + int i; + + consprint("commands:\n"); + for(i = 0; i < nelem(cmds); i++) + if(strcmp(cmds[i].name, "?") != 0) + consprint("> %s\n", cmds[i].usage); +} + +void +consinit(void) +{ + consprint("creepy> "); +} + + +long +conswrite(char *ubuf, long count) +{ + char *c, *p, *np, *args[5]; + int nargs, i, nr; + Rune r; + static char buf[80]; + static char *s, *e; + + if(count <= 0) + return 0; + if(s == nil){ + s = buf; + e = buf + sizeof buf; + } + for(i = 0; i < count && s < e-UTFmax-1; i += nr){ + nr = chartorune(&r, ubuf+i); + memmove(s, ubuf+i, nr); + s += nr; + consprint("%C", r); + } + *s = 0; + if(s == e-1){ + s = buf; + *s = 0; + error("command is too large"); + } + if(utfrune(buf, '\n') == 0) + return count; + p = buf; + do{ + np = utfrune(p, '\n'); + if(np != nil) + *np++ = 0; + c = utfrune(p, '#'); + if(c != nil) + *c = 0; + nargs = tokenize(p, args, nelem(args)); + if(nargs < 1) + continue; + for(i = 0; i < nelem(cmds); i++){ + if(strcmp(args[0], cmds[i].name) != 0) + continue; + if(cmds[i].nargs != 0 && cmds[i].nargs != nargs) + consprint("usage: %s\n", cmds[i].usage); + else + cmds[i].f(nargs, args); + break; + } + if(i == nelem(cmds)) + consprint("'%s'?\n", args[0]); + }while((p = np) != nil); + s = buf; + *s = 0; + consprint("creepy> "); + return count; +} + diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/conf.h --- a/sys/src/cmd/creepy/conf.h Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/conf.h Wed Feb 22 16:04:52 2012 +0100 @@ -10,12 +10,8 @@ #ifdef TESTING Incr = 2, - Fsysmem = 1*MiB, /* size for in-memory block array */ + Fsysmem = 1*GiB , /* size for in-memory block array */ Dzerofree = 10, /* out of disk blocks */ - Dminfree = 100000, /* low on disk */ - Dmaxfree = 100000, /* high on disk */ - Mminfree = 1000000ULL, /* low on mem */ - Mmaxfree = 1000000ULL, /* high on mem */ /* disk parameters; don't change */ Dblksz = 512UL, /* disk block size */ @@ -26,10 +22,6 @@ Incr = 16, Fsysmem = 2*GiB, /* size for in-memory block array */ Dzerofree = 10, /* out of disk blocks */ - Dminfree = 1000, /* low on disk blocks */ - Dmaxfree = 1000, /* high on disk blocks */ - Mminfree = 50, /* low on mem blocks */ - Mmaxfree = 500, /* high on mem blocks */ /* disk parameters; don't change */ Dblksz = 16*KiB, /* disk block size */ @@ -38,6 +30,10 @@ Niptr = 4, /* # of indirect data pointers */ #endif + Dminfree = 1000, /* low on disk blocks */ + Dmaxfree = 1000, /* high on disk blocks */ + Mminfree = 50, /* low on mem blocks */ + Mmaxfree = 500, /* high on mem blocks */ Dminattrsz = Dblksz/2, /* min size for attributes */ /* @@ -60,6 +56,9 @@ Errstack = 64, /* max # of nested error labels */ Fhashsz = 7919, /* size of file hash (plan9 has 35454 files). */ Fidhashsz = 97, /* size of the fid hash size */ + Uhashsz = 97, + + Rpcspercli = 0, /* != 0 places a limit */ }; diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/dbg.h --- a/sys/src/cmd/creepy/dbg.h Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/dbg.h Wed Feb 22 16:04:52 2012 +0100 @@ -1,12 +1,20 @@ /* + * '9': 9p + * 'D': disk + * 'F': slices, indirects, dirnth + * 'M': mblk/dblk gets puts + * 'R': block read + * 'W': block write * 'd': general debug - * 'D': disk - * 'W': block write - * 'R': block read - * '9': 9p + * 'P': procs + * 'x': ix */ +#define d9print if(!dbg['9']){}else print #define dDprint if(!dbg['D']){}else print +#define dFprint if(!dbg['F']){}else print +#define dMprint if(!dbg['M']){}else print #define dRprint if(!dbg['R']){}else print #define dWprint if(!dbg['W']){}else print -#define d9print if(!dbg['9']){}else print +#define dxprint if(!dbg['x']){}else print +#define dPprint if(!dbg['P']){}else print extern char dbg[]; diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/dblk.c --- a/sys/src/cmd/creepy/dblk.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/dblk.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,6 +8,8 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" /* @@ -21,6 +23,7 @@ if(tag != TAG(addr,type)){ fprint(2, "%s: bad tag: %#ullx != %#ux d%#ullx pc = %#p\n", argv0, tag, type, addr, getcallerpc(&tag)); +abort(); error("bad tag"); } } @@ -113,7 +116,7 @@ goto Again; } fs->super->d.free = naddr; - fs->super->d.nfree -= 1; + fs->super->d.ndfree--; changed(fs->super); }else{ addr = 0; @@ -163,7 +166,7 @@ { Memblk *rb; u64int raddr, ref; - int i, flg; + int i; if(addr == 0) return 0; @@ -174,14 +177,14 @@ dDprint("dbsetref %#ullx = %d\n", addr, set); else if(delta != 0) dDprint("dbaddref %#ullx += %d\n", addr, delta); - flg = dbgclr('D'); + nodebug(); raddr = refaddr(addr, &i); rb = dbget(DBref, raddr); qlock(&fs->rlk); if(catcherror()){ mbput(rb); qunlock(&fs->rlk); - dbg['D'] = flg; + debug(); error(nil); } if(delta != 0 || set != 0){ @@ -191,12 +194,19 @@ else rb->d.ref[i] += delta; rb->dirty = 1; + if(delta < 0 && rb->d.ref[i] == 0){ + qlock(fs); + rb->d.ref[i] = fs->super->d.free; + fs->super->d.free = addr; + fs->super->d.ndfree++; + qunlock(fs); + } } ref = rb->d.ref[i]; noerror(); qunlock(&fs->rlk); mbput(rb); - dbg['D'] = flg; + debug(); return ref; } @@ -229,9 +239,9 @@ { Memblk *b; u64int addr; - int root, flg; + int root; - flg = dbgclr('D'); + nodebug(); root = (type == Noaddr); addr = Noaddr; @@ -243,17 +253,17 @@ b->d.tag = TAG(b->addr,type); if(catcherror()){ mbput(b); - dbg['D'] = flg; + debug(); error(nil); } changed(b); if(addr != Noaddr && addr >= Dblk0addr) dbsetref(addr, 1); if(type == DBfile) - b->mf = mfalloc(); + b->mf = anew(&mfalloc); b = mbhash(b); noerror(); - dbg['D'] = flg; + debug(); dDprint("dballoc %s -> %H\n", tname(type), b); return b; } @@ -337,14 +347,14 @@ { Memblk *b; - dDprint("dbget %s d%#ullx\n", tname(type), addr); + dMprint("dbget %s d%#ullx\n", tname(type), addr); okaddr(addr); b = mbget(addr, 1); if(b == nil) error("i/o error"); if(TAGTYPE(b->d.tag) != DBnew){ if(TAGTYPE(b->d.tag) != type) - fatal("dbget: bug"); + fatal("dbget: bug: type %d tag %#ullx", type, b->d.tag); return b; } @@ -360,7 +370,7 @@ checktag(b->d.tag, type, addr); if(type == DBfile){ assert(b->mf == nil); - b->mf = mfalloc(); + b->mf = anew(&mfalloc); gmeta(b->mf, b->d.embed, Embedsz); b->written = 1; } @@ -434,9 +444,12 @@ break; doff = embedattrsz(nb); dupdentries(nb->d.embed+doff, (Embedsz-doff)/sizeof(Dentry)); + /* + * no race: caller takes care. + */ if(b->frozen && b->mf->melted == nil){ incref(nb); - b->mf->melted = nb; /* XXX race *rlocked* */ + b->mf->melted = nb; } break; default: diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/dk.h --- a/sys/src/cmd/creepy/dk.h Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/dk.h Wed Feb 22 16:04:52 2012 +0100 @@ -13,6 +13,10 @@ typedef struct Dmeta Dmeta; typedef struct Blksl Blksl; typedef struct Mfile Mfile; +typedef struct Cmd Cmd; +typedef struct Path Path; +typedef struct Alloc Alloc; +typedef struct Next Next; /* * these are used by several functions that have flags to indicate @@ -39,16 +43,18 @@ * loaded in memory while unused. * - The hash ref also accounts for the lru list and list of DBref blocks. * - Disk refs count only references within the tree on disk. - * - Children imply new refs to the parents. But not vice-versa. - * + * - Children do not add refs to parents; parents do not add ref to children. + * - 9p, fscmd, ix, and other top-level shells for the fs are expected to + * keep Paths for files in use, so that each file in the path + * is referenced once by the path * Assumptions: * - /active is *never* found on disk, it's memory-only. * - b->addr is worm. - * - blocks are added to the end of the hash chain. + * - parents of files loaded in memory are also in memory. + * (but this does not hold for pointer and data blocks). * - We try not to hold more than one lock, using the * reference counters when we need to be sure that * an unlocked resource does not vanish. - * - parents of file blocks in memory are in memory (because of RCs) * - reference blocks are never removed from memory. * - disk refs are frozen while waiting to go to disk during a fs freeze. * in which case db*ref functions write the block in place and melt it. @@ -194,7 +200,7 @@ u64int free; /* first free block on list */ u64int eaddr; /* end of the assigned disk portion */ u64int root; /* address of /archive in disk */ - u64int nfree; /* # of blocks in free list */ + u64int ndfree; /* # of blocks in free list */ u64int dblksz; /* only for checking */ u64int nblkgrpsz; /* only for checking */ u64int dminattrsz; /* only for checking */ @@ -285,12 +291,9 @@ */ struct Mfile { + Mfile* next; /* in free list */ RWLock; Fmeta; - union{ - Memblk *parent; /* most recent parent */ - Mfile *next; /* in free Mfile list */ - }; Memblk* melted; /* next version for this one, if frozen */ ulong lastbno; /* help for RA */ @@ -344,8 +347,8 @@ Memblk *blk; /* static global array of memory blocks */ usize nblk; /* # of entries used */ usize nablk; /* # of entries allocated */ - usize nused; /* blocks in use */ - usize nfree; /* free blocks */ + usize nmused; /* blocks in use */ + usize nmfree; /* free blocks */ Memblk *free; /* free list of unused blocks in blk */ @@ -372,11 +375,54 @@ char *dev; /* name for disk */ int fd; /* of disk */ - usize limit; /* address for end of disk */ + u64int limit; /* address for end of disk */ int config; /* config mode enabled */ }; +/* + * Misc tools. + */ + +struct Cmd +{ + char *name; + void (*f)(int, char**); + int nargs; + char *usage; +}; + +struct Next +{ + Next *next; +}; + +struct Alloc +{ + QLock; + Next *free; + ulong nfree; + ulong nalloc; + usize elsz; + int zeroing; +}; + +/* + * Used to keep references to parents crossed to + * reach files, to be able to build a melted version of the + * children. Also to know the parent of a file for things like + * removals. + */ +struct Path +{ + Path* next; /* in free list */ + Ref; + Memblk** f; + int nf; + int naf; +}; + + #pragma varargck type "H" Memblk* typedef int(*Blkf)(Memblk*); @@ -384,4 +430,5 @@ extern Fsys*fs; extern uvlong maxfsz; - +extern char*defaultusers; +extern Alloc mfalloc; diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/fblk.c --- a/sys/src/cmd/creepy/fblk.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/fblk.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,6 +8,8 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" /* @@ -83,7 +85,6 @@ if(*addrp == 0){ b = dballoc(type); *addrp = b->addr; - incref(b); return b; } @@ -127,7 +128,6 @@ Memblk *b, *pb; u64int *addrp; - isrwlocked(f, mkit); isarch = f == fs->archive; if(isarch) f->frozen = 0; @@ -170,7 +170,7 @@ error("offset exceeds file capacity"); type = DBptr0+i; - dDprint("dfblk: indirect %s nblks %uld (ppb %ud) bno %uld\n", + dFprint("dfblk: indirect %s nblks %uld (ppb %ud) bno %uld\n", tname(type), nblks, Dptrperblk, bno); addrp = &f->d.iptr[i]; @@ -178,11 +178,9 @@ b = getmelted(isdir, isarch, type, addrp); else b = dbget(type, *addrp); - pb = f; - incref(pb); + pb = b; if(catcherror()){ mbput(pb); - mbput(b); error(nil); } @@ -193,9 +191,8 @@ * nblks: # of data blocks addressed by b */ for(nindir = i+1; nindir >= 0; nindir--){ - dDprint("indir %s d%#ullx nblks %uld ptrperblk %d bno %uld\n", + dFprint("indir %s d%#ullx nblks %uld ptrperblk %d bno %uld\n\n", tname(DBdata+nindir), *addrp, nblks, Dptrperblk, bno); - dDprint(" in %H\n", b); idx = 0; if(nindir > 0){ nblks /= Dptrperblk; @@ -211,9 +208,9 @@ else b = dbget(type, *addrp); addrp = &b->d.ptr[idx]; - mbput(pb); - pb = b; } + mbput(pb); + pb = b; USED(&b); /* force to memory in case of error */ USED(&pb); /* force to memory in case of error */ bno -= idx * nblks; @@ -351,16 +348,16 @@ sl.len = f->mf->length - off; done: if(sl.b == nil){ - dDprint("slice m%#p[%#ullx:+%#ulx]%c -> 0[%#ulx]\n", + dFprint("slice m%#p[%#ullx:+%#ulx]%c -> 0[%#ulx]\n", f, off, len, iswr?'w':'r', sl.len); return sl; } if(TAGTYPE(sl.b->d.tag) == DBfile) - dDprint("slice m%#p[%#ullx:+%#ulx]%c -> m%#p:e+%#uld[%#ulx]\n", + dFprint("slice m%#p[%#ullx:+%#ulx]%c -> m%#p:e+%#uld[%#ulx]\n", f, off, len, iswr?'w':'r', sl.b, (uchar*)sl.data - sl.b->d.embed, sl.len); else - dDprint("slice m%#p[%#ullx:+%#ulx]%c -> m%#p:%#uld[%#ulx]\n", + dFprint("slice m%#p[%#ullx:+%#ulx]%c -> m%#p:%#uld[%#ulx]\n", f, off, len, iswr?'w':'r', sl.b, (uchar*)sl.data - sl.b->d.data, sl.len); @@ -398,6 +395,7 @@ * naddr. If iswr, the entry is allocated if needed and the blocks * melted on demand. * Return the offset for the entry in the file or Noaddr + * Does not adjust disk refs. */ u64int dfchdentry(Memblk *d, u64int addr, u64int naddr, int iswr) @@ -464,7 +462,7 @@ for(i = 0; i < sl.len/sizeof(Dentry); i++) if(de[i].file != 0 && tot++ >= n){ mbput(sl.b); - dDprint("dfdirnth d%#ullx[%d] = d%#ullx\n", + dFprint("dfdirnth d%#ullx[%d] = d%#ullx\n", d->addr, n, de[i].file); return de[i].file; } @@ -486,11 +484,7 @@ b = mbget(addr, 0); if(b != nil || disktoo == 0) return b; - b = dbget(DBfile, addr); - b->mf->parent = f; - incref(f); - - return b; + return dbget(DBfile, addr); } Memblk* @@ -516,8 +510,6 @@ isdir(d); dfchdentry(d, 0, f->addr, Wr); - f->mf->parent = d; - incref(d); changed(d); } @@ -532,10 +524,6 @@ isdir(d); dfchdentry(d, f->addr, 0, Wr); - if(f->mf->parent == d){ /* f may be shared */ - mbput(f->mf->parent); - f->mf->parent = nil; - } changed(d); } @@ -543,21 +531,24 @@ * Walk to a child and return it referenced. * If iswr, d must not be frozen and the child is returned melted. */ -static Memblk* -xdfwalk(Memblk *d, char *name, int iswr) +Memblk* +dfwalk(Memblk *d, char *name, int iswr) { - Memblk *f, *nf; + Memblk *f; Blksl sl; Dentry *de; uvlong off; int i; dDprint("dfwalk '%s' at %H\n", name, d); + if(strcmp(name, "..") == 0) + fatal("dfwalk: '..'"); isdir(d); if(iswr) ismelted(d); off = 0; + f = nil; for(;;){ sl = dfslice(d, Dblkdatasz, off, 0); if(sl.len == 0) @@ -584,29 +575,7 @@ mbput(sl.b); if(!iswr || !f->frozen) goto done; - - /* It's for writing, and frozen: melt it and its ref. */ - if(catcherror()){ - mbput(f); - error(nil); - } - nf = dbdup(f); - if(!catcherror()){ - dbdecref(f->addr); - noerror(); - } - mbput(f); - f = nf; - USED(&f); - sl = dfslice(d, sizeof(Dentry), off+i*sizeof(Dentry), 1); - de = sl.data; - assert(sl.b); - de->file = f->addr; - mbput(sl.b); - noerror(); - changed(d); - goto done; - + fatal("dfwalk: frozen"); } noerror(); mbput(sl.b); @@ -618,154 +587,96 @@ return f; } -Memblk* -dfwalk(Memblk *d, char *name, int iswr) +/* + * Return the last version for *fp, wlocked, be it frozen or melted. + */ +static void +followmelted(Memblk **fp) { - Memblk *x; + Memblk *f; - isrwlocked(d, iswr); - if(strcmp(name, "..") == 0){ - x = d->mf->parent; - if(x == nil) - x = d; - incref(x); - }else - x = xdfwalk(d, name, iswr); - return x; -} - - -static char ** -dfrevpath(Memblk *f, int *nnamesp) -{ - Memblk *b, *pb; - char **names; - int nnames; - - isrwlocked(f, Rd); - names = nil; - nnames = 0; - for(b = f; b != nil; b = pb){ - if(b == fs->active || b == fs->archive) - break; - if(nnames%Incr == 0) - names = realloc(names, (nnames+Incr)*sizeof(char*)); - rwlock(b, Rd); - names[nnames++] = strdup(b->mf->name); - pb = b->mf->parent; - rwunlock(b, Rd); + f = *fp; + isfile(f); + rwlock(f, Wr); + while(f->mf->melted != nil){ + incref(f->mf->melted); + *fp = f->mf->melted; + rwunlock(f, Wr); + mbput(f); + f = *fp; + rwlock(f, Wr); + if(!f->frozen) + return; } - *nnamesp = nnames; - return names; -} - -static Memblk* -meltedactive(void) -{ - Memblk *b; - - for(;;){ - b = fs->active; - rwlock(b, Wr); - if(!b->frozen) - break; - rwunlock(b, Wr); - } - ismelted(b); - isrwlocked(b, Wr); - return b; } /* - * Want to write on f, make sure it's melted. - * Return the version of f that we must use, locked for writing and melted. - * (our reference to f is traded for the one returned). - * - * This function exploits that freezing a tree walks from the root down - * to the leaves, and requires an wlock for each file frozen, including active. - * Once active is melted and wlocked, no file can't be frozen after we melt it. + * Caller walked down p, and now requires the nth element to be + * melted, and wlocked for writing. (nth count starts at 1); + * + * Return the path with the version of f that we must use, + * locked for writing and melted. + * References kept in the path are traded for the ones returned. */ -Memblk* -dfmelt(Memblk *f) +Path* +dfmelt(Path **pp, int nth) { - char **names; - int nnames, i; - Memblk *b, *nb, *f0, *nf; + int i; + Memblk *f, **fp, *nf; + Path *p; + + ownpath(pp); + p = *pp; + assert(nth >= 1 && p->nf >= nth && p->nf >= 2); + assert(p->f[0] == fs->root); + fp = &p->f[nth-1]; /* - * 0. Try to get a melted version for f. - * Preserve f0 so we keep a ref upon errors. + * 1. Optimistic: Try to get a loaded melted version for f. */ - isfile(f); - f0 = f; - incref(f0); - rwlock(f0, Wr); - while(f->mf->melted != nil){ - incref(f->mf->melted); - nf = f->mf->melted; - mbput(f); - f = nf; - } - rwunlock(f0, Wr); - rwlock(f, Wr); - if(!f->frozen){ - mbput(f0); - return f; - } + followmelted(fp); + f = *fp; + if(!f->frozen) + return p; rwunlock(f, Wr); - if(catcherror()){ - mbput(f); /* both if f == f0 or f != f0 */ - error(nil); - } /* - * 1. travel up to a melted block or to the root, recording - * the names we will have to walk down to reach f. - * TODO: If we find a melted file we could stop there. + * 2. Realistic: + * walk down the path, melting every frozen thing until we + * reach f. Keep wlocks so melted files are not frozen while we walk. + * /active is special, because it's only frozen temporarily while + * creating a frozen version of the tree. Instead of melting it, + * we should just wait for it. */ - dDprint("dfmelt %H\n", f); - rwlock(f, Rd); - names = dfrevpath(f, &nnames); - rwunlock(f, Rd); - if(catcherror()){ - for(i = 0; i < nnames; i++) - free(names[i]); - free(names); - error(nil); + followmelted(&p->f[1]); + + for(i = 2; i < nth; i++){ + followmelted(&p->f[i]); + f = p->f[i]; + if(!f->frozen){ + rwunlock(p->f[i-1], Wr); + continue; + } + if(catcherror()){ + rwunlock(p->f[i-1], Wr); + rwunlock(p->f[i], Wr); + error(nil); + } + nf = dbdup(f); + rwlock(nf, Wr); + if(catcherror()){ + rwunlock(nf, Wr); + mbput(nf); + error(nil); + } + dfchdentry(p->f[i-1], f->addr, nf->addr, 1); + /* committed */ + rwunlock(f, Wr); + rwunlock(p->f[i-1], Wr); + if(!catcherror()){ + dbdecref(f->addr); + noerror(); + } } - - /* - * 2. walk down from active to f, ensuring everything is melted. - * be careful to hold wlocks so that things are not frozen - * again while we walk. - */ - b = meltedactive(); - incref(b); - if(catcherror()){ - rwunlock(b, Wr); - mbput(b); - error(nil); - } - for(i = nnames-1; i >= 0; i--){ - nb = xdfwalk(b, names[i], 1); - rwlock(nb, Wr); - rwunlock(b, Wr); - ismelted(nb); - mbput(b); - b = nb; - USED(&b); /* in case of error() */ - } - noerror(); - noerror(); - noerror(); - for(i = 0; i < nnames; i++) - free(names[i]); - free(names); - - mbput(f0); - - isrwlocked(b, Wr); - ismelted(b); - return b; + return p; } - diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/file.c --- a/sys/src/cmd/creepy/file.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/file.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,6 +8,8 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" /* @@ -34,7 +36,6 @@ wmtime(f, &t, sizeof t); } - Memblk* dfcreate(Memblk *parent, char *name, char *uid, ulong mode) { @@ -138,7 +139,7 @@ } ulong -dfpwrite(Memblk *f, void *a, ulong count, uvlong off) +dfpwrite(Memblk *f, void *a, ulong count, uvlong *off) { Blksl sl; ulong tot; @@ -150,8 +151,10 @@ isrwlocked(f, Wr); ismelted(f); p = a; + if(f->mf->mode&DMAPPEND) + *off = f->mf->length; for(tot = 0; tot < count; tot += sl.len){ - sl = dfslice(f, count-tot, off+tot, Wr); + sl = dfslice(f, count-tot, *off+tot, Wr); if(sl.len == 0 || sl.data == nil) fatal("dfpwrite: bug"); memmove(sl.data, p+tot, sl.len); @@ -201,6 +204,48 @@ return tot; } +static int +fdumpf(Memblk *f) +{ + extern int mbtab; + + isfile(f); + mbtab++; + return 0; +} + +static int +bdumpf(Memblk*) +{ + return 0; +} + +static int +fdumpedf(Memblk *) +{ + extern int mbtab; + + mbtab--; + return 0; +} + +/* + * XXX: We must get rid of dfmap. + * There are few uses and they are already too different. + * for example, for dfdump, we want to call fslowmem() now and then, + * so that if we read the entire disk to dump it, we have no problem. + */ +int +dfdump(Memblk *f, int disktoo) +{ + int n; + + incref(f); + n = dfmap(f, fdumpf, fdumpedf, bdumpf, disktoo, No); + decref(f); + return n; +} + int dfmap(Memblk *f, Blkf pre, Blkf post, Blkf bf, int isdisk, int lk) { @@ -208,6 +253,7 @@ Memblk *b; Memblk *(*child)(Memblk*, int); long tot; + extern int mbtab; isfile(f); rwlock(f, lk); @@ -225,7 +271,12 @@ for(i = 0; i < nelem(f->d.dptr); i++) tot += ptrmap(f->d.dptr[i], 0, bf, isdisk); for(i = 0; i < nelem(f->d.iptr); i++) - tot += ptrmap(f->d.dptr[i], i+1, bf, isdisk); + tot += ptrmap(f->d.iptr[i], i+1, bf, isdisk); + } + if(pre == fdumpf){ /* kludge */ + mbtab--; + print("%H\n", f); + mbtab++; } if((f->mf->mode&DMDIR) != 0){ child = dfchild; @@ -277,9 +328,6 @@ static int bsyncf(Memblk *b) { - if(!b->frozen) - fatal("bsyncf: not frozen\n%H\n", b); - if(b->dirty) dbwrite(b); b->dirty = 0; @@ -348,37 +396,6 @@ return dfmap(f, freclaimf, nil, breclaimf, Disk, Wr); } -static int -fdumpf(Memblk *f) -{ - extern int mbtab; - - isfile(f); - print("%H\n", f); - mbtab++; - return 0; -} - -static int -fdumpedf(Memblk *) -{ - extern int mbtab; - - mbtab--; - return 0; -} - -int -dfdump(Memblk *f, int disktoo) -{ - int n; - - incref(f); - n = dfmap(f, fdumpf, fdumpedf, nil, disktoo, No); - decref(f); - return n; -} - /* * DEBUG: no locks. */ @@ -407,4 +424,3 @@ if(ppath == nil) print("\n"); } - diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/fns.h --- a/sys/src/cmd/creepy/fns.h Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/fns.h Wed Feb 22 16:04:52 2012 +0100 @@ -1,23 +1,34 @@ +extern Path* addelem(Path **pp, Memblk *f); extern u64int addrofref(u64int refaddr, int idx); +extern void afree(Alloc *a, void *nd); +extern void* anew(Alloc *a); +extern void attach(Fid *fid, char *aname, char *uname); extern void changed(Memblk *b); extern void checktag(u64int tag, uint type, u64int addr); extern void clean(Memblk *b); +extern void clearusers(void); +extern char* cliworker9p(void *v, void**aux); +extern char* cliworkerix(void *v, void**aux); +extern Fid* clone(Cli *cli, Fid *fid, int no); +extern Path* clonepath(Path *p); +extern void consinit(void); extern void consprint(char *fmt, ...); extern long consread(char *buf, long count); -extern long conswrite(char *buf, long count); +extern long conswrite(char *ubuf, long count); extern Memblk* dballoc(uint type); extern void dbclear(u64int addr, int type); extern u64int dbdecref(u64int addr); extern Memblk* dbdup(Memblk *b); -extern int dbgclr(uchar flag); extern Memblk* dbget(uint type, u64int addr); extern u64int dbgetref(u64int addr); extern u64int dbincref(u64int addr); extern long dbread(Memblk *b); extern void dbsetref(u64int addr, int ref); extern long dbwrite(Memblk *b); +extern void debug(void); extern void dfaccessok(Memblk *f, char *uid, int bits); extern ulong dfbno(Memblk *f, uvlong off, ulong *boffp); +extern void dfcattr(Memblk *f, int op, char *name, void *val, long count); extern u64int dfchdentry(Memblk *d, u64int addr, u64int naddr, int iswr); extern Memblk* dfchild(Memblk *f, int n); extern Memblk* dfcreate(Memblk *parent, char *name, char *uid, ulong mode); @@ -27,9 +38,9 @@ extern void dflink(Memblk *d, Memblk *f); extern void dflist(Memblk *f, char *ppath); extern int dfmap(Memblk *f, Blkf pre, Blkf post, Blkf bf, int isdisk, int lk); -extern Memblk* dfmelt(Memblk *f); +extern Path* dfmelt(Path **pp, int nth); extern ulong dfpread(Memblk *f, void *a, ulong count, uvlong off); -extern ulong dfpwrite(Memblk *f, void *a, ulong count, uvlong off); +extern ulong dfpwrite(Memblk *f, void *a, ulong count, uvlong *off); extern long dfrattr(Memblk *f, char *name, void *val, long count); extern int dfreclaim(Memblk *f); extern void dfremove(Memblk *p, Memblk *f); @@ -39,27 +50,45 @@ extern Memblk* dfwalk(Memblk *d, char *name, int iswr); extern long dfwattr(Memblk *f, char *name, void *val, long nval); extern void disktohost(Memblk *b); +extern Path* dropelem(Path **pp); extern void dupdentries(void *p, int n); extern ulong embedattrsz(Memblk *f); extern void fatal(char *fmt, ...); +extern void fidclose(Fid *fid); +extern void fidcreate(Fid *fid, char *name, int mode, ulong perm); +extern int fidfmt(Fmt *fmt); +extern void fidopen(Fid *fid, int mode); +extern long fidread(Fid *fid, void *data, ulong count, vlong offset); +extern void fidremove(Fid *fid); +extern long fidwrite(Fid *fid, void *data, ulong count, uvlong *offset); +extern void freerpc(Rpc *rpc); extern void fsdump(int disktoo); extern void fsfmt(char *dev); extern Memblk* fsfreeze(void); extern int fsfull(void); extern void fslist(void); extern int fslowmem(void); +extern uvlong fsmemfree(void); extern void fsopen(char *dev); extern void fspolicy(void); extern int fsreclaim(void); +extern void fsstats(void); extern void fssync(void); +extern Fid* getfid(void* clino, int no); extern void gmeta(Fmeta *meta, void *buf, ulong nbuf); extern Memblk* hosttodisk(Memblk *b); extern void isdir(Memblk *f); extern void isfile(Memblk *f); extern void ismelted(Memblk *b); extern void isnotdir(Memblk *f); +extern int isro(Memblk *f); extern void isrwlocked(Memblk *f, int iswr); -extern void listen9p(char *addr); +extern int ixcallfmt(Fmt *fmt); +extern uint ixpack(IXcall *f, uchar *ap, uint nap); +extern uint ixpackedsize(IXcall *f); +extern void ixstats(void); +extern uint ixunpack(uchar *ap, uint nap, IXcall *f); +extern void listen9pix(char *addr, char* (*cliworker)(void *arg, void **aux)); extern Memblk* mballoc(u64int addr); extern Memblk* mbdup(Memblk *b); extern int mbfmt(Fmt *fmt); @@ -68,18 +97,31 @@ extern void mbput(Memblk *b); extern void mbunhash(Memblk *b); extern void meltedref(Memblk *rb); -extern Mfile* mfalloc(void); +extern int member(char *uid, char *member); extern Memblk* mfchild(Memblk *f, int n); -extern void mffree(Mfile *mf); extern u64int newblkaddr(void); +extern Cli* newcli(char *addr, int fd, int cfd); +extern Fid* newfid(void* clino, int no); +extern Path* newpath(Memblk *root); +extern Rpc* newrpc(void); +extern void ninestats(void); +extern void nodebug(void); extern uvlong now(void); extern void okaddr(u64int addr); +extern void ownpath(Path **pp); +extern void parseusers(char *u); extern ulong pmeta(void *buf, ulong nbuf, Fmeta *meta); +extern void putcli(Cli *cli); +extern void putfid(Fid *fid); +extern void putpath(Path *p); extern u64int refaddr(u64int addr, int *idx); +extern void replied(Rpc *rpc); +extern int rpcfmt(Fmt *fmt); extern void rwlock(Memblk *f, int iswr); extern void rwunlock(Memblk *f, int iswr); -extern void srv9p(char *srv); +extern void srv9pix(char *srv, char* (*cliworker)(void *arg, void **aux)); extern char* tname(int t); +extern void walk(Fid *fid, char *wname); extern long watime(Memblk *f, void *buf, long); extern long wmtime(Memblk *f, void *buf, long); extern long wname(Memblk *f, void *buf, long len); diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/fscmd.c --- a/sys/src/cmd/creepy/fscmd.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/fscmd.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,6 +8,8 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" enum @@ -22,35 +24,36 @@ * Walks elems starting at f. * Ok if nelems is 0. */ -static Memblk* +static Path* walkpath(Memblk *f, char *elems[], int nelems) { int i; - Memblk *f0, *nf; + Memblk *nf; + Path *p; + p = newpath(f); + if(catcherror()){ + putpath(p); + error(nil); + } isfile(f); - f0 = f; for(i = 0; i < nelems; i++){ if((f->mf->mode&DMDIR) == 0) error("not a directory"); rwlock(f, Rd); if(catcherror()){ - if(f != f0) - mbput(f); rwunlock(f, Rd); error("walk: %r"); } nf = dfwalk(f, elems[i], 0); rwunlock(f, Rd); - if(f != f0) - mbput(f); + addelem(&p, nf); f = nf; USED(&f); /* in case of error() */ noerror(); } - if(f == f0) - incref(f); - return f; + noerror(); + return p; } static char* @@ -63,12 +66,12 @@ return strdup(p); } -static Memblk* +static Path* walkto(char *a, char **lastp) { char *els[Nels], *path; int nels; - Memblk *f; + Path *p; path = fsname(a); nels = gettokens(path, els, Nels, "/"); @@ -81,15 +84,15 @@ error("walkpath: %r"); } if(lastp != nil){ - f = walkpath(fs->root, els, nels-1); + p = walkpath(fs->root, els, nels-1); *lastp = a + strlen(a) - strlen(els[nels-1]); }else - f = walkpath(fs->root, els, nels); + p = walkpath(fs->root, els, nels); free(path); noerror(); if(verb) - print("walked to %H\n", f); - return f; + print("walked to %H\n", p->f[p->nf-1]); + return p; } static void @@ -99,6 +102,13 @@ fsdir = strdup(argv[1]); } +/* + * This is unrealistic in that it keeps the file locked + * during the entire put. This means that we can only give + * fslowmem() a chance before each put, and not before each + * write, because everything is going to be in use and dirty if + * we run out of memory. + */ static void fsput(int, char *argv[]) { @@ -109,6 +119,7 @@ char buf[4096]; uvlong off; long nw, nr; + Path *p; fd = open(argv[1], OREAD); if(fd < 0) @@ -122,27 +133,36 @@ free(d); error(nil); } - m = walkto(argv[2], &fn); - m = dfmelt(m); + p = walkto(argv[2], &fn); + if(catcherror()){ + putpath(p); + error(nil); + } + dfmelt(&p, p->nf); + m = p->f[p->nf-1]; if(catcherror()){ rwunlock(m, Wr); mbput(m); error(nil); } f = dfcreate(m, fn, d->uid, d->mode&(DMDIR|0777)); + noerror(); + addelem(&p, f); + decref(f); /* kept now in p */ rwlock(f, Wr); + rwunlock(m, Wr); if(catcherror()){ rwunlock(f, Wr); - mbput(f); error(nil); } if((d->mode&DMDIR) == 0){ off = 0; for(;;){ + fslowmem(); nr = read(fd, buf, sizeof buf); if(nr <= 0) break; - nw = dfpwrite(f, buf, nr, off); + nw = dfpwrite(f, buf, nr, &off); dDprint("wrote %ld of %ld bytes\n", nw, nr); off += nr; } @@ -153,9 +173,7 @@ if(verb) print("created %H\nat %H\n", f, m); rwunlock(f, Wr); - rwunlock(m, Wr); - mbput(m); - mbput(f); + putpath(p); close(fd); free(d); } @@ -168,12 +186,14 @@ char buf[4096]; uvlong off; long nr; + Path *p; - f = walkto(argv[2], nil); + p = walkto(argv[2], nil); + f = p->f[p->nf-1]; rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); - mbput(f); + putpath(p); error(nil); } m = f->mf; @@ -182,6 +202,7 @@ if((m->mode&DMDIR) == 0){ off = 0; for(;;){ + fslowmem(); nr = dfpread(f, buf, sizeof buf, off); if(nr <= 0) break; @@ -191,7 +212,7 @@ } noerror(); rwunlock(f, Rd); - mbput(f); + putpath(p); } static void @@ -203,6 +224,7 @@ uvlong off; long nr; int fd; + Path *p; fd = create(argv[1], OWRITE, 0664); if(fd < 0) @@ -211,11 +233,12 @@ close(fd); error(nil); } - f = walkto(argv[2], nil); + p = walkto(argv[2], nil); + f = p->f[p->nf-1]; rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); - mbput(f); + putpath(p); error(nil); } m = f->mf; @@ -224,6 +247,7 @@ if((m->mode&DMDIR) == 0){ off = 0; for(;;){ + fslowmem(); nr = dfpread(f, buf, sizeof buf, off); if(nr <= 0) break; @@ -235,10 +259,10 @@ } } close(fd); + noerror(); + noerror(); rwunlock(f, Rd); - noerror(); - noerror(); - mbput(f); + putpath(p); } static void @@ -289,25 +313,37 @@ static void fsrm(int, char *argv[]) { - Memblk *f, *p; + Memblk *f, *pf; + Path *p; - f = walkto(argv[1], nil); + p = walkto(argv[1], nil); if(catcherror()){ - mbput(f); + putpath(p); error(nil); } - f->mf->parent = dfmelt(f->mf->parent); - p = f->mf->parent; + if(p->nf < 2) + error("short path for rm"); + dfmelt(&p, p->nf-1); + f = p->f[p->nf-1]; + pf = p->f[p->nf-2]; rwlock(f, Wr); if(catcherror()){ rwunlock(f, Wr); - rwunlock(p, Wr); + rwunlock(pf, Wr); error(nil); } - dfremove(p, f); + dfremove(pf, f); + p->f[p->nf-1] = nil; noerror(); noerror(); - rwunlock(p, Wr); + rwunlock(pf, Wr); + putpath(p); +} + +static void +fsst(int, char**) +{ + fsstats(); } static void @@ -317,13 +353,7 @@ exits("usage"); } -static struct -{ - char *name; - void (*f)(int, char**); - int nargs; - char *usage; -} cmds[] = +static Cmd cmds[] = { {"cd", fscd, 2, "cd!where"}, {"put", fsput, 3, "put!src!dst"}, @@ -337,6 +367,7 @@ {"dbg", fsdbg, 2, "dbg!n"}, {"out", fsout, 1, "out"}, {"rm", fsrm, 2, "rm!what"}, + {"stats", fsst, 1, "stats"}, }; void @@ -381,9 +412,9 @@ continue; if(cmds[j].nargs != 0 && cmds[j].nargs != nargs) print("usage: %s\n", cmds[j].usage); - else{ + else cmds[j].f(nargs, args); - } + fspolicy(); break; } noerror(); diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/fsfmt.c --- a/sys/src/cmd/creepy/fsfmt.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/fsfmt.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,6 +8,8 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" static void diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/fsys.c --- a/sys/src/cmd/creepy/fsys.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/fsys.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,6 +8,8 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" /* @@ -29,7 +31,7 @@ fprint(2, "\n"); if(fatalaborts) abort(); - exits("fatal"); + threadexitsall("fatal"); } uvlong @@ -45,23 +47,26 @@ error("okaddr %#ullx", addr); } +int +isro(Memblk *f) +{ + return f == fs->archive || f == fs->root || f == fs->cons; +} + /* * NO LOCKS. debug only */ void fsdump(int disktoo) { - int i, flg; + int i; Memblk *b; u64int a; - flg = dbg['D']; - dbg['D'] = 0; + nodebug(); if(fs != nil){ - print("\n\nfsys '%s' limit %#ulx super m%#p root m%#p:\n", + print("\n\nfsys '%s' limit %#ullx super m%#p root m%#p:\n", fs->dev, fs->limit, fs->super, fs->root); - print("nblk %uld nablk %uld used %uld free %uld\n", - fs->nblk, fs->nablk, fs->nused, fs->nfree); print("%H\n", fs->super); dfdump(fs->root, disktoo); for(b = fs->refs; b != nil; b = b->next) @@ -83,29 +88,19 @@ for(b = fs->mru; b != nil; b = b->lnext) print(" d%#ullx", b->addr); print("\n"); - print("Fsysmem\t= %uld\n", Fsysmem); - print("Dminfree\t= %d\n", Dminfree); - print("Dblksz\t= %uld\n", Dblksz); - print("Dminattrsz\t= %uld\n", Dminattrsz); - print("Nblkgrpsz\t= %uld\n", Nblkgrpsz); - print("Dblkdatasz\t= %d\n", Dblkdatasz); - print("Embedsz\t= %d\n", Embedsz); - print("Dentryperblk\t= %d\n", Dblkdatasz/sizeof(Dentry)); - print("Dptrperblk\t= %d\n\n", Dptrperblk); - dbg['D'] = flg; + fsstats(); + debug(); } void fslist(void) { - int flg; - - flg = dbgclr('D'); + nodebug(); print("fsys '%s' blksz %ulld maxfsz %ulld:\n", fs->dev, fs->super->d.dblksz, maxfsz); dflist(fs->root, nil); print("\n"); - dbg['D'] = flg; + debug(); } static usize @@ -241,7 +236,6 @@ Memblk *na, *oa, *arch; char name[50]; - /* call fslowmem? */ qlock(&fs->fzlk); if(catcherror()){ /* @@ -315,11 +309,13 @@ if(nblk > 0 && nblk < fs->nablk) fs->nablk = nblk; fs->limit = disksize(fs->fd); - if(fs->nablk > fs->limit/Dblksz) - fs->nablk = fs->limit/Dblksz; - fs->limit = fs->nablk * Dblksz; + fs->limit = fs->limit/Dblksz*Dblksz; if(fs->limit < 10*Dblksz) fatal("buy a larger disk"); + if(fs->nablk > fs->limit/Dblksz){ + fs->nablk = fs->limit/Dblksz; + print("%s: using only %uld blocks (small disk)\n", argv0, fs->nablk); + } fs->blk = malloc(fs->nablk * sizeof fs->blk[0]); dDprint("fsys '%s' init\n", fs->dev); } @@ -372,8 +368,10 @@ * TODO: If active has not changed and we are just going * to dump a new archive for no change, do nothing. */ + dDprint("syncing\n"); fsfreeze(); fswrite(); + dDprint("synced\n"); } /* @@ -430,20 +428,22 @@ noerror(); } -static uvlong +uvlong fsmemfree(void) { uvlong nfree; qlock(fs); nfree = fs->nablk - fs->nblk; - nfree += fs->nfree; + nfree += fs->nmfree; qunlock(fs); return nfree; } /* - * This should be called if fs->nblk == fs->nablk && fs->nfree < some number. + * Check if we are low on memory and move some blocks out in that case. + * This does not acquire locks on blocks, so it's safe to call it while + * keeping some files/blocks locked. */ int fslowmem(void) @@ -454,59 +454,63 @@ if(fsmemfree() > Mminfree) return 0; - - /* - * We are low on memory, try to make a snapshot so that - * dirty blocks are moved to disk and we can release them if we want. - */ - dDprint("low on memory: syncing\n"); - fssync(); - + dDprint("low on memory\n"); tot = 0; do{ if(fsmemfree() > Mmaxfree) break; qlock(&fs->fzlk); + qlock(&fs->llk); if(catcherror()){ + qunlock(&fs->llk); qunlock(&fs->fzlk); fprint(2, "%s: fslowmem: %r\n", argv0); break; } + Again: n = 0; for(b = fs->lru; b != nil && tot < Mmaxfree; b = bprev){ bprev = b->lprev; type = TAGTYPE(b->d.tag); switch(type){ + case DBfree: + goto Again; case DBsuper: case DBref: - dDprint("out: ignored: %H\n", b); + dDprint("out: ignored: m%#p\n", b); continue; case DBfile: if(b == fs->root || b == fs->active || b == fs->archive){ - dDprint("out: ignored: %H\n", b); + dDprint("out: ignored: m%#p\n", b); continue; } break; } if(b->dirty || b->ref > 1){ - dDprint("out: ignored: %H\n", b); + dDprint("out: ignored: m%#p\n", b); continue; } /* - * Blocks have one ref because of the hash table. - * Those that have exactly 1 ref are not used: - * we have a clean unused block: throw it away. + * Blocks here have one ref because of the hash table, + * which means they are are not used. + * We release the hash ref to let them go. + * bprev might move while we put b, but it would + * only go to another place in the lru list, or to + * the free list, but that's ok. */ + qunlock(&fs->llk); dDprint("block out: m%#p d%#ullx\n", b, b->addr); mbput(b); + qlock(&fs->llk); n++; tot++; } noerror(); + qunlock(&fs->llk); qunlock(&fs->fzlk); }while(n > 0); if(tot == 0) - fprint(2, "%s: out: everything in use or dirty.\n", argv0); + fprint(2, "%s: low mem and everything in use or dirty.\n", argv0); else dDprint("out: %uld blocks\n", tot); return 1; @@ -518,7 +522,7 @@ uvlong nfree; qlock(fs); - nfree = fs->super->d.nfree; + nfree = fs->super->d.ndfree; nfree += (fs->limit - fs->super->d.eaddr)/Dblksz; qunlock(fs); return nfree; @@ -532,7 +536,15 @@ int fsfull(void) { - return fsdiskfree() < Dzerofree; + if(fsdiskfree() > Dzerofree) + return 0; + + if(1){ + fprint(2, "file system full:\n"); + fsdump(0); + fatal("aborting"); + } + return 1; } /* @@ -551,6 +563,7 @@ if(fsdiskfree() > Dminfree) return 0; + fprint(2, "%s: low on disk: reclaiming...\n", argv0); qlock(&fs->fzlk); arch = fs->archive; rwlock(arch, Wr); @@ -634,49 +647,9 @@ * If low on memory, move some blocks out. * Otherwise, reclaim old snapshots if low on disk. */ - if(!fslowmem()) + if(fslowmem()) + fssync(); + else fsreclaim(); } -void -consprint(char *fmt, ...) -{ - va_list arg; - char *s, *x; - - va_start(arg, fmt); - s = vsmprint(fmt, arg); - va_end(arg); - /* consume some message if the channel is full */ - while(nbsendp(fs->consc, s) == 0) - if((x = nbrecvp(fs->consc)) != nil) - free(x); -} - -long -consread(char *buf, long count) -{ - char *s; - - s = recvp(fs->consc); - if(count > strlen(s)) - count = strlen(s); - memmove(buf, s, count); - free(s); - return count; -} - -/* - * XXX: conswrite should take a look to the command and process it, - * the reply must be issued by calling consprint(), - * the writer should be also reading the file. - */ -long -conswrite(char *buf, long count) -{ - if(count <= 1) - return 0; - buf[count-1] = 0; - consprint("??\n"); - return count; -} diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/ix.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/creepy/ix.c Wed Feb 22 16:04:52 2012 +0100 @@ -0,0 +1,571 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "conf.h" +#include "dbg.h" +#include "dk.h" +#include "ix.h" +#include "net.h" +#include "fns.h" + +/* + * ix server for creepy + */ + +enum +{ + Tfirst = 0x8000U, + Tlast = 0x4000U, + Tmask = 0x3FFFU +}; + +static void rversion(Rpc*), rfid(Rpc*), rclone(Rpc*), + rattach(Rpc*), rwalk(Rpc*), + ropen(Rpc*), rcreate(Rpc*), + rread(Rpc*), rwrite(Rpc*), rclunk(Rpc*), + rremove(Rpc*), rattr(Rpc*), rwattr(Rpc*), + rcond(Rpc*), rmove(Rpc*); + +static int reply(Rpc*); + +static void (*ixcalls[])(Rpc*) = +{ + [IXTversion] rversion, + [IXTattach] rattach, + [IXTfid] rfid, + [IXTclone] rclone, + [IXTwalk] rwalk, + [IXTopen] ropen, + [IXTcreate] rcreate, + [IXTread] rread, + [IXTwrite] rwrite, + [IXTclunk] rclunk, + [IXTremove] rremove, + [IXTattr] rattr, + [IXTwattr] rwattr, + [IXTcond] rcond, + [IXTmove] rmove, +}; + +/* + * we consider T/R msgs that include uids, errors, and attributes + * as short. That places a limit on things like user names, element + * names, and error messages. The limit is lower than Minmdata. + * Declaring them here as large requests will remove those limits. + * For large messages, the request buffer is used instead of the + * per-client write buffer, and data is copied by the rpc code, + * to save an extra copy. + */ +static int largeix[IXTmax] = +{ + [IXTread] 1, /* uses its buf for the reply */ + [IXTwrite] 1, +}; + +Alloc srpcalloc = +{ + .elsz = sizeof(Shortrpc), + .zeroing = 0, +}; + +static int ixrreadhdrsz; + +void +ixstats(void) +{ + print("srpcs:\t%4uld alloc %4uld free (%4uld bytes)\n", + srpcalloc.nalloc, srpcalloc.nfree, srpcalloc.elsz); +} + + +static Rpc* +newsrpc(void) +{ + Rpc *rpc; + + rpc = anew(&srpcalloc); + rpc->next = nil; + rpc->cli = nil; + rpc->fid = nil; + rpc->flushed = 0; + rpc->closed = 0; + rpc->chan = ~0; + rpc->rpc0 = nil; + memset(&rpc->xt, 0, sizeof rpc->xt); + memset(&rpc->xr, 0, sizeof rpc->xr); + return rpc; +} + +static void +freesrpc(Rpc *rpc) +{ + afree(&srpcalloc, rpc); +} + +static void +freeixrpc(Rpc *rpc) +{ + rpc->closed = 0; + rpc->flushed = 0; + if(largeix[rpc->xt.type]) + freerpc(rpc); + else + freesrpc(rpc); +} + +static void +rversion(Rpc *rpc) +{ + rpc->xr.msize = rpc->xt.msize; + if(rpc->xr.msize > Maxmdata) + rpc->xr.msize = Maxmdata; + rpc->cli->msize = rpc->xr.msize; + if(strncmp(rpc->xt.version, "IX", 2) != 0) + error("unknown protocol version"); + rpc->xr.version = "IX"; +} + +static void +rattach(Rpc *rpc) +{ + putfid(rpc->fid); + rpc->rpc0->fid = newfid(rpc->cli, -1); + attach(rpc->rpc0->fid, rpc->xt.aname, rpc->xt.uname); +} + +static void +rfid(Rpc *rpc) +{ + + putfid(rpc->rpc0->fid); + rpc->rpc0->fid = getfid(rpc->cli, rpc->xt.fid); +} + +static void +rclone(Rpc *rpc) +{ + Fid *nfid; + + if(rpc->rpc0->fid == nil) + error("fid not set"); + nfid = clone(rpc->cli, rpc->rpc0->fid, -1); + putfid(rpc->rpc0->fid); + rpc->rpc0->fid = nfid; + nfid->cflags = rpc->xt.cflags; +} + +static void +rwalk(Rpc *rpc) +{ + if(rpc->rpc0->fid == nil) + error("fid not set"); + walk(rpc->rpc0->fid, rpc->xt.wname); +} + +static void +ropen(Rpc *rpc) +{ + int cflags; + + if(rpc->rpc0->fid == nil) + error("fid not set"); + cflags = rpc->xt.mode&(OCERR|OCEND); + fidopen(rpc->rpc0->fid, rpc->xt.mode &~cflags); + rpc->rpc0->fid->cflags = cflags; +} + +static void +rcreate(Rpc *rpc) +{ + int cflags; + + if(rpc->rpc0->fid == nil) + error("fid not set"); + cflags = rpc->xt.mode&(OCERR|OCEND); + fidcreate(rpc->rpc0->fid, rpc->xt.name, rpc->xt.mode, rpc->xt.perm); + rpc->rpc0->fid->cflags = cflags; +} + +static void +rread(Rpc *rpc) +{ + vlong off; + Fid *fid; + + fid = rpc->rpc0->fid; + if(fid == nil) + error("fid not set"); + if(rpc->xt.count > rpc->cli->msize-ixrreadhdrsz) + rpc->xt.count = rpc->cli->msize-ixrreadhdrsz; + rpc->xr.data = rpc->data + ixrreadhdrsz; + + /* + * send all but the last reply, if we are given permissiong to + * send multiple replies back. + * Errors, eof, and flush terminate the sequence. + * As usual, the caller sends the last reply when we return. + */ + off = rpc->xt.offset; + for(;;){ + rpc->xr.count = fidread(fid, rpc->xr.data, rpc->xt.count, off); + if(rpc->xr.count == 0) + break; + if(rpc->xt.nmsg-- <= 0) + break; + if(reply(rpc) < 0) + break; + if(rpc != rpc->rpc0) + freeixrpc(rpc); + off += rpc->xr.count; + } +} + +static void +rwrite(Rpc *rpc) +{ + Fid *fid; + + fid = rpc->rpc0->fid; + if(fid == nil) + error("fid not set"); + rpc->xr.offset = rpc->xt.offset; + rpc->xr.count = fidwrite(fid, rpc->xt.data, rpc->xt.count, &rpc->xr.offset); +} + +static void +rclunk(Rpc *rpc) +{ + Fid *fid; + + fid = rpc->rpc0->fid; + if(fid == nil) + error("fid not set"); + if(fid->omode != -1) + fidclose(fid); + fid->cflags = 0; + putfid(fid); + putfid(fid); + rpc->rpc0->fid = nil; +} + +static void +rremove(Rpc *rpc) +{ + Fid *fid; + + fid = rpc->rpc0->fid; + if(fid == nil) + error("fid not set"); + fidremove(fid); + fid->cflags = 0; + putfid(fid); + putfid(fid); + rpc->rpc0->fid = nil; +} + +static void +rattr(Rpc *rpc) +{ + Fid *fid; + Path *p; + Memblk *f; + uchar buf[128]; + + fid = rpc->rpc0->fid; + if(fid == nil) + error("fid not set"); + p = fid->p; + f = p->f[p->nf-1]; + rwlock(f, Rd); + if(catcherror()){ + rwunlock(f, Rd); + error(nil); + } + rpc->xr.value = buf; + rpc->xr.nvalue = dfrattr(f, rpc->xt.attr, buf, sizeof buf); + rwunlock(f, Rd); + noerror(); +} + +static void +rwattr(Rpc *rpc) +{ + Fid *fid; + Path *p; + Memblk *f; + + fid = rpc->rpc0->fid; + if(fid == nil) + error("fid not set"); + p = fid->p; + f = p->f[p->nf-1]; + if(isro(f) || fid->archived) + error("can't wattr archived or built-in files"); + p = dfmelt(&fid->p, fid->p->nf); + f = p->f[p->nf-1]; + if(catcherror()){ + rwunlock(f, Wr); + error(nil); + } + dfwattr(f, rpc->xt.attr, rpc->xt.value, rpc->xt.nvalue); + rwunlock(f, Wr); +} + +static void +rcond(Rpc *rpc) +{ + Fid *fid; + Path *p; + Memblk *f; + + fid = rpc->rpc0->fid; + if(fid == nil) + error("fid not set"); + p = fid->p; + f = p->f[p->nf-1]; + rwlock(f, Rd); + if(catcherror()){ + rwunlock(f, Rd); + error(nil); + } + dfcattr(f, rpc->xt.op, rpc->xt.attr, rpc->xt.value, rpc->xt.nvalue); + rwunlock(f, Rd); + noerror(); +} + +static void +rmove(Rpc *rpc) +{ + if(rpc->rpc0->fid == nil) + error("fid not set"); + error("move not yet implemented"); +} + +/* + * Read a short or large rpc and return it. + * Shouldn't we use bio, or at least a buffer? + */ +static Rpc* +readix(int fd) +{ + uchar hdr[BIT16SZ+BIT16SZ+BIT8SZ]; + long nhdr, nr; + ulong sz; + uint type; + Rpc *rpc; + + nhdr = readn(fd, hdr, sizeof hdr); + if(nhdr < 0){ + dxprint("readix: %r\n"); + return nil; + } + if(nhdr == 0){ + werrstr("eof"); + return nil; + } + sz = GBIT16(hdr); + if(sz > IOHDRSZ+Maxmdata){ + /* don't read it; the entire stream will fail */ + werrstr("msg too large"); + return nil; + } + if(sz < BIT16SZ+BIT8SZ){ + /* don't read it; the entire stream will fail */ + werrstr("msg too small"); + return nil; + } + type = GBIT8(hdr+BIT16SZ+BIT16SZ); + if(type >= IXTmax){ + werrstr("wrong message type"); + rpc = newrpc(); + }else if(largeix[type]) + rpc = newrpc(); + else + rpc = newsrpc(); + rpc->chan = GBIT16(hdr+BIT16SZ); + rpc->xt.type = type; + PBIT8(rpc->data, type); + nr = readn(fd, rpc->data+BIT8SZ, sz-(BIT16SZ+BIT8SZ)); + if(nr < 0){ + freeixrpc(rpc); + return nil; + } + if(nr != sz){ + werrstr("short msg data"); + freeixrpc(rpc); + return nil; + } + if(ixunpack(rpc->data, sz-BIT16SZ, &rpc->xt) != sz-BIT16SZ){ + freeixrpc(rpc); + return nil; + } + return rpc; +} + +static int +reply(Rpc *rpc) +{ + ulong sz, max; + uchar *p, *buf; + Cli *cli; + u16int chan; + + cli = rpc->cli; + chan = rpc->chan&Tmask; + if(rpc->xr.type == IXRerror || (rpc->chan&Tlast) != 0) + chan |= Tlast; + qlock(&cli->wlk); + if(largeix[rpc->xt.type]) + buf = rpc->data; + else + buf = cli->wdata; + max = IOHDRSZ+Maxmdata; + p = buf; + p += BIT16SZ; + PBIT16(p, chan); + p += BIT16SZ; + sz = ixpack(&rpc->xr, p, max-BIT16SZ-BIT16SZ); + if(sz == 0) + fatal("writeix: message too large or ixpack failed"); + PBIT16(buf, sz); + p += sz; + + if(rpc->rpc0->flushed){ + qunlock(&cli->wlk); + werrstr("flushed"); + dxprint("write: flushed"); + return -1; + } + dxprint("-> %G\n", &rpc->xr); + if(write(cli->fd, buf, p-buf) != p-buf){ + qunlock(&cli->wlk); + dxprint("write: %r"); + return -1; + } + qunlock(&cli->wlk); + return p-buf; +} + +static char* +rpcworkerix(void *v, void**aux) +{ + Rpc *rpc, *rpc0; + Cli *cli; + Channel *c; + char err[128]; + long nw; + + c = v; + if(*aux == nil){ + errinit(Errstack); + *aux = v; /* make it not nil */ + } + + err[0] = 0; + rpc = recvp(c); + rpc0 = rpc; + cli = rpc->cli; + threadsetname("rpcworkerix %s chan %d", cli->addr, rpc0->chan); + dPprint("%s started\n", threadgetname()); + do{ + rpc->xr.type = rpc->xt.type + 1; + rpc->rpc0 = rpc0; + if(catcherror()){ + rpc->xr.type = Rerror; + rpc->xr.ename = err; + rerrstr(err, sizeof err); + if(rpc0->fid != nil && (rpc0->fid->cflags&OCERR) != 0) + rpc0->fid->cflags |= OCEND; + }else{ + ixcalls[rpc->xt.type](rpc); + noerror(); + } + nw = reply(rpc); + if(rpc != rpc0) + freeixrpc(rpc); + }while(!rpc0->closed && nw > 0 && err[0] == 0 && (rpc = recvp(c)) != nil); + if(rpc0->fid != nil && (rpc0->fid->cflags&OCEND) != 0) + putfid(rpc0->fid); /* clunk */ + while((rpc = nbrecvp(c)) != nil) + freeixrpc(rpc); + replied(rpc0); + freeixrpc(rpc0); + dPprint("%s exiting\n", threadgetname()); + return nil; +} + +static void +ixinit(void) +{ + IXcall xt; + if(ixrreadhdrsz != 0) + return; + xt.type = IXRread; + ixrreadhdrsz = ixpackedsize(&xt) + BIT16SZ + BIT16SZ; +} + +char* +cliworkerix(void *v, void**aux) +{ + Cli *cli; + Rpc *rpc, *r; + + cli = v; + threadsetname("cliworkerix %s", cli->addr); + dPprint("%s started\n", threadgetname()); + + ixinit(); + for(;;){ + loop: rpc = readix(cli->fd); + if(rpc == nil){ + dxprint("%s: read: %r\n", cli->addr); + break; + } + rpc->cli = cli; + incref(cli); + + qlock(&cli->rpclk); + for(r = cli->rpcs; r != nil; r = r->next) + if((r->chan&Tmask) == (rpc->chan&Tmask)){ + if(rpc->chan&Tlast) + if(r->closed) + r->flushed = 1; + else + r->closed = 1; + sendp(r->c, rpc); + qunlock(&cli->rpclk); + goto loop; + } + if((rpc->chan&Tfirst) == 0){ /* it's channel is gone */ + freeixrpc(rpc); + qunlock(&cli->rpclk); + goto loop; + } + + /* new channel */ + rpc->next = cli->rpcs; + cli->rpcs = rpc; + if(rpc->c == nil) + rpc->c = chancreate(sizeof(Rpc*), 64); + cli->nrpcs++; + qunlock(&cli->rpclk); + + fspolicy(); + + if(rpc->chan&Tlast) + rpc->closed = 1; + sendp(rpc->c, rpc); + if(Rpcspercli != 0 && cli->nrpcs >= Rpcspercli) + rpcworkerix(rpc->c, aux); + else + getworker(rpcworkerix, rpc->c, nil); + } + putcli(cli); + dPprint("%s exiting\n", threadgetname()); + return nil; +} diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/ix.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/creepy/ix.h Wed Feb 22 16:04:52 2012 +0100 @@ -0,0 +1,174 @@ +/* + * The protocol packs requests within transport channels. + * + * The format in the wire is len[2] tag[2] msg[] + * where len is the number of bytes of the message and + * tag is a channel number. + * + * there two special bits in tags: Tfirst, and Tlast. + * The first message in a channel must set Tfirst + * the last one must set Tlast. + * Tfirst = 0x8000, Tlast = 0x4000 + * + * A channel is duplex and is closed when both directions have + * exchanged messages with Tlast set. + * An error reply always has Tlast set. + * + * Authentication is fully left out of the protocol. + * The underlying transport must be secured, in a way that + * provides trust between both parties. + * + * This is the set of individual requests or messages: + * Here, data[] means what's left of the message (i.e., count is implied). + * + * IXTversion msize[4] version[s] + * IXRversion msize[4] version[s] + * IXTattach uname[s] aname[s] + * IXRattach fid[4] // fid becomes the current fid + * IXTfid fid[4] + * IXRfid + * IXRerror ename[s] + * IXTclone cflags[1] + * IXRclone fid[4] // fid becomes the current fid + * IXTwalk wname[s] + * IXRwalk + * IXTopen mode[1] // mode includes cflags as well + * IXRopen + * IXTcreate name[s] perm[4] mode[1] // mode includes cflags as well + * IXRcreate + * IXTread nmsg[2] offset[8] count[4] + * IXRread data[] + * IXTwrite offset[8] endoffset[8] data[] + * IXRwrite offset[8] count[4] + * IXTclunk + * IXRclunk + * IXTremove + * IXRremove + * IXTattr attr[s] //"*" means: return attr name list + * IXRattr value[] + * IXTwattr attr[s] value[] // perhaps: cacheable "y" + * IXRwattr + * IXTcond op[1] attr[s] value[] + * IXRcond + * IXTmove dirfid[4] newname[s] + * IXRmove + * + * There is no flush. Flushing is done by flushing the channel. + */ + +enum{ + IXTversion = 50, + IXRversion, + IXTattach, + IXRattach, + IXTfid, + IXRfid, + __IXunused__, + IXRerror, + IXTclone, + IXRclone, + IXTwalk, + IXRwalk, + IXTopen, + IXRopen, + IXTcreate, + IXRcreate, + IXTread, + IXRread, + IXTwrite, + IXRwrite, + IXTclunk, + IXRclunk, + IXTremove, + IXRremove, + IXTattr, + IXRattr, + IXTwattr, + IXRwattr, + IXTcond, + IXRcond, + IXTmove, + IXRmove, + IXTmax, + + /* + * flags used in Tclone, Topen, Tcreate + */ + OCEND = 0x4, /* clunk on end of rpc */ + OCERR = 0x8, /* clunk on error */ + + CEQ = 0, /* Tcond.op */ + CGE, + CGT, + CLE, + CLT, + CNE, + CMAX, +}; + + +typedef struct IXcall IXcall; + +/* + * len[2] tag[2] prepended by the transport. + * This is an individual call request.T + * Fids are selected by the server and handed to the client. + */ +struct IXcall +{ + uchar type; + union{ + struct{ /* Tversion, Rversion */ + u32int msize; + char *version; + }; + struct{ + u32int fid; /* Tfid, Rattach, Rclone */ + }; + struct{ + char *uname; /* Tattach */ + char *aname; /* Tattach */ + }; + struct{ + char *ename; /* Rerror */ + }; + struct{ + uchar cflags; /* Tclone (OCEND|OCERR) */ + }; + struct{ + uchar mode; /* Topen, Tcreate */ + u32int perm; /* Tcreate */ + char *name; /* Tcreate */ + }; + struct{ /* Twalk */ + char *wname; + }; + struct{ + u16int nmsg; /* Tread */ + uvlong offset; /* Tread, Twrite, Rwrite */ + uvlong endoffset; /* Twrite */ + u32int count; /* Tread, Rread, Twrite, Rwrite */ + uchar *data; /* Twrite, Rread */ + }; + struct{ + uchar op; /* Tcond */ + char *attr; /* Tattr, Twattr, Tcond */ + uchar *value; /* Rattr, Twattr, Tcond */ + u32int nvalue; /* Twattr, Rattr*/ + }; + struct{ /* Tmove */ + u32int dirfid; + char *newname; + }; + /* With struct{}: + * Rfid, + * Rwalk, Ropen, Rcreate, Tclunk, Rclunk, Tclose, Rclose, + * Tremove, Rremove, Rwattr, Rcond, Rmove + */ + }; +}; + +/* 8c bug: varargck does not like IX */ +typedef IXcall COMPILERBUGpcall; +#pragma varargck type "G" COMPILERBUGpcall* + diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/ixcall.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/creepy/ixcall.c Wed Feb 22 16:04:52 2012 +0100 @@ -0,0 +1,802 @@ +#include +#include +#include +#include + +#include "conf.h" +#include "dk.h" +#include "ix.h" +#include "net.h" + +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 uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +/* + * Does NOT count the data bytes added past the packed + * message for IXRread, IXTwrite. This is so to save copying. + * The caller is expected to copy the final data in-place and + * adjust the total message length. + */ +uint +ixpackedsize(IXcall *f) +{ + uint n; + + n = BIT8SZ; /* type */ + + switch(f->type){ + case IXTversion: + case IXRversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case IXTattach: + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + case IXRattach: + n += BIT32SZ; + break; + + case IXTfid: + n += BIT32SZ; + break; + case IXRfid: + break; + + case IXRerror: + n += stringsz(f->ename); + break; + + case IXTclone: + n += BIT8SZ; + break; + case IXRclone: + n += BIT32SZ; + break; + + case IXTwalk: + n += stringsz(f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + n += BIT8SZ; + break; + case IXRopen: + break; + + case IXTcreate: + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + case IXRcreate: + break; + + case IXTread: + n += BIT16SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + case IXRread: + /* data follows; not counted */ + break; + + case IXTwrite: + n += BIT64SZ; + n += BIT64SZ; + /* data follows; not counted */ + break; + case IXRwrite: + n += BIT64SZ; + n += BIT32SZ; + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + n += stringsz(f->attr); + break; + case IXRattr: + n += f->nvalue; + break; + + case IXTwattr: + n += stringsz(f->attr); + n += f->nvalue; + break; + case IXRwattr: + break; + + case IXTcond: + n += BIT8SZ; + n += stringsz(f->attr); + n += f->nvalue; + break; + case IXRcond: + break; + + case IXTmove: + n += BIT32SZ; + n += stringsz(f->newname); + break; + case IXRmove: + break; + + default: + sysfatal("packedsize: unknown type %d", f->type); + + } + return n; +} + +uint +ixpack(IXcall *f, uchar *ap, uint nap) +{ + uchar *p; + uint size; + + size = ixpackedsize(f); + if(size == 0 || size > nap) + return 0; + + p = (uchar*)ap; + + PBIT8(p, f->type); + p += BIT8SZ; + + switch(f->type){ + case IXTversion: + case IXRversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case IXTattach: + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + case IXRattach: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case IXTfid: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + case IXRfid: + break; + + case IXRerror: + p = pstring(p, f->ename); + break; + + case IXTclone: + PBIT8(p, f->cflags); + p += BIT8SZ; + break; + case IXRclone: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case IXTwalk: + p = pstring(p, f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + PBIT8(p, f->mode); + p += BIT8SZ; + break; + case IXRopen: + break; + + case IXTcreate: + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + case IXRcreate: + break; + + case IXTread: + PBIT16(p, f->nmsg); + p += BIT16SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + case IXRread: + /* data follows; not packed */ + break; + + case IXTwrite: + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT64(p, f->endoffset); + p += BIT64SZ; + /* data follows; not packed */ + break; + case IXRwrite: + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + p = pstring(p, f->attr); + break; + case IXRattr: + memmove(p, f->value, f->nvalue); + p += f->nvalue; + break; + + case IXTwattr: + p = pstring(p, f->attr); + memmove(p, f->value, f->nvalue); + p += f->nvalue; + break; + case IXRwattr: + break; + + case IXTcond: + if(f->op >= CMAX){ + werrstr("unknown cond op"); + return 0; + } + PBIT8(p, f->op); + p += BIT8SZ; + p = pstring(p, f->attr); + memmove(p, f->value, f->nvalue); + p += f->nvalue; + break; + case IXRcond: + break; + + case IXTmove: + PBIT32(p, f->dirfid); + p += BIT32SZ; + p = pstring(p, f->newname); + break; + case IXRmove: + 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; +} + +uint +ixunpack(uchar *ap, uint nap, IXcall *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){ + case IXTversion: + case IXRversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case IXTattach: + p = gstring(p, ep, &f->uname); + if(p == nil) + return 0; + p = gstring(p, ep, &f->aname); + break; + case IXRattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case IXTfid: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + case IXRfid: + break; + + case IXRerror: + p = gstring(p, ep, &f->ename); + break; + + case IXTclone: + if(p+BIT8SZ > ep) + return 0; + f->cflags = GBIT8(p); + p += BIT8SZ; + break; + case IXRclone: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case IXTwalk: + p = gstring(p, ep, &f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + if(p+BIT8SZ > ep) + return 0; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + case IXRopen: + break; + + case IXTcreate: + 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 IXRcreate: + break; + + case IXTread: + if(p+BIT16SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->nmsg = GBIT16(p); + p += BIT16SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + case IXRread: + f->data = p; + f->count = ep - p; + break; + + case IXTwrite: + if(p+BIT64SZ > ep) + return 0; + f->offset = GBIT64(p); + p += BIT64SZ; + f->endoffset = GBIT64(p); + p += BIT64SZ; + f->data = p; + f->count = ep - p; + break; + case IXRwrite: + if(p+BIT32SZ+BIT64SZ > ep) + return 0; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + p = gstring(p, ep, &f->attr); + break; + case IXRattr: + f->value = p; + f->nvalue = ep - p; + break; + + case IXTwattr: + p = gstring(p, ep, &f->attr); + if(p == nil) + return 0; + f->value = p; + f->nvalue = ep - p; + break; + case IXRwattr: + break; + + case IXTcond: + if(p+BIT8SZ > ep) + return 0; + f->op = GBIT8(p); + if(f->op >= CMAX){ + werrstr("unknown cond op"); + return 0; + } + p += BIT8SZ; + p = gstring(p, ep, &f->attr); + if(p == nil) + return 0; + f->value = p; + f->nvalue = ep - p; + break; + case IXRcond: + break; + + case IXTmove: + if(p+BIT32SZ > ep) + return 0; + f->dirfid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->newname); + break; + case IXRmove: + break; + + default: + werrstr("unpack: unknown type %d", f->type); + return 0; + } + + if(p==nil || p>ep || p == ap){ + werrstr("unpack: p %#p ep %#p", p, ep); + return 0; + } + return p - ap; +} + +static char* cname[CMAX] = +{ + [CEQ] "==", + [CGE] ">=", + [CGT] "> ", + [CLT] "< ", + [CLE] "<=", + [CNE] "!=", +}; + +static char* tname[] = +{ + /* ix requests */ + [IXTversion] "Tversion", + [IXRversion] "Rversion", + [IXTattach] "Tattach", + [IXRattach] "Rattach", + [IXTfid] "Tfid", + [IXRfid] "Rfid", + [__IXunused__] "__IXunused__", + [IXRerror] "Rerror", + [IXTclone] "Tclone", + [IXRclone] "Rclone", + [IXTwalk] "Twalk", + [IXRwalk] "Rwalk", + [IXTopen] "Topen", + [IXRopen] "Ropen", + [IXTcreate] "Tcreate", + [IXRcreate] "Rcreate", + [IXTread] "Tread", + [IXRread] "Rread", + [IXTwrite] "Twrite", + [IXRwrite] "Rwrite", + [IXTclunk] "Tclunk", + [IXRclunk] "Rclunk", + [IXTremove] "Tremove", + [IXRremove] "Rremove", + [IXTattr] "Tattr", + [IXRattr] "Rattr", + [IXTwattr] "Twattr", + [IXRwattr] "Rwattr", + [IXTcond] "Tcond", + [IXRcond] "Rcond", + [IXTmove] "Tmove", + [IXRmove] "Rmove", + + /* 9p requests */ + [Tversion] "Tversion", + [Rversion] "Rversion", + [Tauth] "Tauth", + [Rauth] "Rauth", + [Tattach] "Tattach", + [Rattach] "Rattach", + [Terror] "Terror", + [Rerror] "Rerror", + [Tflush] "Tflush", + [Rflush] "Rflush", + [Twalk] "Twalk", + [Rwalk] "Rwalk", + [Topen] "Topen", + [Ropen] "Ropen", + [Tcreate] "Tcreate", + [Rcreate] "Rcreate", + [Tread] "Tread", + [Rread] "Rread", + [Twrite] "Twrite", + [Rwrite] "Rwrite", + [Tclunk] "Tclunk", + [Rclunk] "Rclunk", + [Tremove] "Tremove", + [Rremove] "Rremove", + [Tstat] "Tstat", + [Rstat] "Rstat", + [Twstat] "Twstat", + [Rwstat] "Rwstat", +}; + +int +rpcfmt(Fmt *fmt) +{ + Rpc *rpc; + + rpc = va_arg(fmt->args, Rpc*); + if(rpc == nil) + return fmtprint(fmt, ""); + if(rpc->t.type == 0) + return fmtprint(fmt, "Tnull"); + if(rpc->t.type < nelem(tname) && tname[rpc->t.type]) + return fmtprint(fmt, "%s tag %ud", tname[rpc->t.type], rpc->t.tag); + return fmtprint(fmt, "type=%d??? tag %ud", rpc->t.type, rpc->t.tag); +} + + +/* + * 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, void *b, long count) +{ + int i, printable; + char *p; + char *buf; + + buf = b; + 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; +} + +/* + * Uses a buffer so prints are not mixed with other debug prints. + */ +int +ixcallfmt(Fmt *fmt) +{ + IXcall *f; + int type; + char buf[512]; + char *e, *s; + + e = buf+sizeof(buf); + f = va_arg(fmt->args, IXcall*); + type = f->type; + if(type < IXTversion || type >= IXTmax) + return fmtprint(fmt, "", type); + s = seprint(buf, e, "%s", tname[type]); + switch(type){ + case IXTversion: + case IXRversion: + seprint(s, e, " msize %ud version '%s'", f->msize, f->version); + break; + break; + + case IXTattach: + seprint(s, e, " uname '%s' aname '%s'", f->uname, f->aname); + break; + case IXRattach: + seprint(s, e, " fid %d", f->fid); + break; + + case IXTfid: + seprint(s, e, " fid %ud", f->fid); + break; + case IXRfid: + break; + + case IXRerror: + seprint(s, e, " ename '%s'", f->ename); + break; + + case IXTclone: + seprint(s, e, " cflags %#x", f->cflags); + break; + case IXRclone: + seprint(s, e, " fid %d", f->fid); + break; + + case IXTwalk: + seprint(s, e, " '%s'", f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + seprint(s, e, " mode %d", f->mode); + break; + case IXRopen: + break; + + case IXTcreate: + seprint(s, e, " name '%s' perm %M mode %d", + f->name, (ulong)f->perm, f->mode); + break; + case IXRcreate: + break; + + case IXTread: + seprint(s, e, " nmsg %d offset %lld count %ud", + f->nmsg, f->offset, f->count); + break; + case IXRread: + s = seprint(s, e, " count %ud ", f->count); + dumpsome(s, e, f->data, f->count); + break; + + case IXTwrite: + s = seprint(s, e, " offset %lld endoffset %lld count %ud ", + f->offset, f->endoffset, f->count); + dumpsome(s, e, f->data, f->count); + break; + case IXRwrite: + seprint(s, e, " offset %lld count %ud", f->offset, f->count); + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + seprint(s, e, " attr '%s'", f->attr); + break; + case IXRattr: + s = seprint(s, e, " value "); + dumpsome(s, e, f->value, f->nvalue); + break; + + case IXTwattr: + s = seprint(s, e, " attr '%s' value ", f->attr); + dumpsome(s, e, f->value, f->nvalue); + break; + case IXRwattr: + break; + + case IXTcond: + s = "??"; + if(f->op < CMAX) + s = cname[f->op]; + s = seprint(s, e, " op '%s'", s); + s = seprint(s, e, "attr '%s' value ", f->attr); + dumpsome(s, e, f->value, f->nvalue); + break; + case IXRcond: + break; + + case IXTmove: + seprint(s, e, " dirfid %d newname '%s'", f->dirfid, f->newname); + break; + case IXRmove: + break; + + } + return fmtstrcpy(fmt, buf); +} + diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/mblk.c --- a/sys/src/cmd/creepy/mblk.c Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/mblk.c Wed Feb 22 16:04:52 2012 +0100 @@ -8,6 +8,8 @@ #include "conf.h" #include "dbg.h" #include "dk.h" +#include "ix.h" +#include "net.h" #include "fns.h" /* @@ -20,6 +22,12 @@ * (debug dump functions may be an exception). */ +Alloc mfalloc = +{ + .elsz = sizeof(Mfile), + .zeroing = 1, +}; + char* tname(int t) { @@ -70,8 +78,8 @@ fmttab(fmt, mbtab); fmtprint(fmt, " %s[%d] = d%#ullx \n", tag, n, addr); }else{ - decref(b); fmtprint(fmt, "%H", b); + mbput(b); } } static void @@ -81,7 +89,7 @@ u64int *p; int i; - if(b->mf->length == 0) + if(b->mf->length == 0 || (b->mf->mode&DMDIR) == 0) return; doff = embedattrsz(b); if(doff < Embedsz){ @@ -97,15 +105,14 @@ mbfmt(Fmt *fmt) { Memblk *b; - int type, i, n, xdbg; + int type, i, n; b = va_arg(fmt->args, Memblk*); if(b == nil) return fmtprint(fmt, "\n"); + nodebug(); type = TAGTYPE(b->d.tag); fmttab(fmt, mbtab); - xdbg = dbg['D']; - dbg['D'] = 0; fmtprint(fmt, "m%#p d%#ullx", b, b->addr); if(b->frozen) fmtprint(fmt, " FZ"); @@ -114,7 +121,8 @@ if(b->written) fmtprint(fmt, " WR"); fmtprint(fmt, " %s r%d", tname(type), b->ref); - fmtprint(fmt, " tag %#ullx epoch %#ullx", EP(b->d.tag), EP(b->d.epoch)); + fmtprint(fmt, " tag %#ullx", EP(b->d.tag)); + if(0)fmtprint(fmt, " epoch %#ullx", EP(b->d.epoch)); switch(type){ case DBfree: fmtprint(fmt, "\n"); @@ -152,8 +160,7 @@ EP(b->mf->id), (ulong)b->mf->mode, EP(b->mf->mtime), b->mf->length, b->mf->uid); fmttab(fmt, mbtab); - fmtprint(fmt, " parent m%#p nr%d nw%d\n", - b->mf->parent, b->mf->readers, b->mf->writer); + fmtprint(fmt, " nr%d nw%d\n", b->mf->readers, b->mf->writer); dumpsomedata(fmt, b); mbtab++; for(i = 0; i < nelem(b->d.dptr); i++) @@ -178,7 +185,7 @@ mbtab--; break; } - dbg['D'] = xdbg; + debug(); return 0; } @@ -264,7 +271,6 @@ hv = b->addr%nelem(fs->fhash); qlock(&fs->fhash[hv]); - fs->nused++; ob = nil; for(h = &fs->fhash[hv].b; *h != nil; h = &(*h)->next) if((*h)->addr == b->addr) @@ -297,7 +303,6 @@ fatal("mbunhash: dup"); *h = b->next; b->next = nil; - fs->nused--; qlock(&fs->llk); lruunlink(b); qunlock(&fs->llk); @@ -330,19 +335,16 @@ mf = b->mf; b->mf = nil; mbput(mf->melted); - mf->melted = nil; - mbput(mf->parent); - mf->parent = nil; - mf->next = nil; assert(mf->writer == 0 && mf->readers == 0); - mffree(mf); + afree(&mfalloc, mf); } b->d.tag = DBfree; b->frozen = b->written = b->dirty = 0; b->addr = 0; qlock(fs); - fs->nfree++; + fs->nmused--; + fs->nmfree++; b->next = fs->free; fs->free = b; qunlock(fs); @@ -360,11 +362,12 @@ else if(fs->free != nil){ b = fs->free; fs->free = b->next; - fs->nfree--; + fs->nmfree--; }else{ qunlock(fs); - fatal("mballoc: evict block not implemented"); + fatal("mballoc: no free blocks"); } + fs->nmused++; qunlock(fs); memset(b, 0, sizeof *b); b->addr = addr; @@ -404,13 +407,13 @@ dDprint("mbget %#ullx -> i/o error\n", addr); return nil; /* i/o error reading it */ } - dDprint("mbget %#ullx -> waited for m%#p\n", addr, b); + dMprint("mbget %#ullx -> waited for m%#p\n", addr, b); return b; } qunlock(&fs->fhash[hv]); if(b != nil) mbused(b); - dDprint("mbget %#ullx -> m%#p\n", addr, b); + dMprint("mbget %#ullx -> m%#p\n", addr, b); return b; } @@ -419,7 +422,7 @@ { if(b == nil) return; - dDprint("mbput m%#p pc=%#p\n", b, getcallerpc(&b)); + dMprint("mbput m%#p pc=%#p\n", b, getcallerpc(&b)); if(decref(b) == 0) mbfree(b); } @@ -434,31 +437,3 @@ return nb; } -Mfile* -mfalloc(void) -{ - Mfile *mf; - - qlock(&fs->mlk); - mf = fs->mfree; - if(mf != nil){ - fs->mfree = mf->next; - mf->next = nil; - } - qunlock(&fs->mlk); - if(mf == nil) - mf = mallocz(sizeof *mf, 1); - return mf; -} - -void -mffree(Mfile *mf) -{ - if(mf == nil) - return; - qlock(&fs->mlk); - mf->next = fs->mfree; - fs->mfree = mf; - qunlock(&fs->mlk); -} - diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/mkfile --- a/sys/src/cmd/creepy/mkfile Mon Feb 20 10:44:18 2012 +0100 +++ b/sys/src/cmd/creepy/mkfile Wed Feb 22 16:04:52 2012 +0100 @@ -15,6 +15,12 @@ attr.$O\ fsys.$O\ file.$O\ + cfg.$O\ + tools.$O\ + +IXOFILES=\ + ixcall.$O\ + ix.$O\ 9p.$O\ HFILES=\ @@ -22,11 +28,17 @@ conf.h\ dk.h\ fns.h\ + ix.h\ + net.h\ # not ready for install BIN=. fns.h + diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/net.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/creepy/net.h Wed Feb 22 16:04:52 2012 +0100 @@ -0,0 +1,97 @@ +typedef struct Fid Fid; +typedef struct Rpc Rpc; +typedef struct Shortrpc Shortrpc; +typedef struct Largerpc Largerpc; +typedef struct Cli Cli; + +enum +{ + Maxmdata = 8*KiB, + Minmdata = 128, + + QTCACHE = 0x02, /* experiment */ +}; + +/* + * One reference kept because of existence and another per req using it. + */ +struct Fid +{ + Fid *next; /* in hash or free list */ + Ref; + QLock; + void* clino; /* no is local to a client */ + int no; + Path* p; + int omode; /* -1 if closed */ + int rclose; + int archived; + int cflags; /* OCERR|OCEND */ + int consopen; /* for flush. has /cons open? */ + char *uid; + + uvlong loff; /* last offset, for dir reads */ + long lidx; /* next dir entry index to read */ +}; + +struct Rpc +{ + Rpc *next; /* in client or free list */ + Cli *cli; + Fid *fid; + union{ + Fcall t; + IXcall xt; + }; + union{ + Fcall r; + IXcall xr; + }; + + /* these are for ix */ + Rpc* rpc0; /* where to get fid, c, closed, flushed */ + u16int chan; /* channel # (ix) */ + Channel* c; /* to worker (ix) */ + int closed; /* got last rpc in chan */ + + int flushed; + uchar data[1]; +}; + +/* + * 9p, and ix if carrying data. + */ +struct Largerpc +{ + Rpc; + uchar buf[IOHDRSZ+Maxmdata]; +}; + +/* + * ix requests that do not carry much data. + */ +struct Shortrpc +{ + Rpc; + uchar buf[IOHDRSZ+Minmdata]; +}; + +struct Cli +{ + Cli *next; /* in list of clients or free list*/ + Ref; + int fd; + int cfd; + char *addr; + ulong msize; + + QLock wlk; /* lock for writing replies to the client */ + uchar wdata[IOHDRSZ+Maxmdata]; + + QLock rpclk; + ulong nrpcs; /* should we limit the max # per client? */ + Rpc *rpcs; +}; + +#pragma varargck type "X" Fid* +#pragma varargck type "R" Rpc* diff -r d4a6be52c85c -r 104e7add5754 sys/src/cmd/creepy/tools.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/creepy/tools.c Wed Feb 22 16:04:52 2012 +0100 @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include + +#include "conf.h" +#include "dbg.h" +#include "dk.h" +#include "ix.h" +#include "net.h" +#include "fns.h" + + +/* + * Misc tools. + */ + +static Alloc pathalloc = +{ + .elsz = sizeof(Path), + .zeroing = 0, +}; + +void +fsstats(void) +{ + print("blks:\t%4uld nblk %4uld nablk %4uld mused %4uld mfree %4ulld dfree\n", + fs->nblk, fs->nablk, fs->nmused, fs->nmfree, fs->super->d.ndfree); + print("paths:\t%4uld alloc %4uld free (%4uld bytes)\n", + pathalloc.nalloc, pathalloc.nfree, pathalloc.elsz); + print("mfs:\t%4uld alloc %4uld free (%4uld bytes)\n", + mfalloc.nalloc, mfalloc.nfree, mfalloc.elsz); + print("\n"); + print("Fsysmem:\t%uld\n", Fsysmem); + print("Dminfree:\t%d\n", Dminfree); + print("Dblksz: \t%uld\n", Dblksz); + print("Mblksz: \t%ud\n", sizeof(Memblk)); + print("Dminattrsz:\t%uld\n", Dminattrsz); + print("Nblkgrpsz:\t%uld\n", Nblkgrpsz); + print("Dblkdatasz:\t%d\n", Dblkdatasz); + print("Embedsz:\t%d\n", Embedsz); + print("Dentryperblk:\t%d\n", Dblkdatasz/sizeof(Dentry)); + print("Dptrperblk:\t%d\n\n", Dptrperblk); +} + +void* +anew(Alloc *a) +{ + Next *n; + + assert(a->elsz > 0); + qlock(a); + n = a->free; + if(n != nil){ + a->free = n->next; + a->nfree--; + }else{ + a->nalloc++; + n = mallocz(a->elsz, !a->zeroing); + } + qunlock(a); + if(a->zeroing) + memset(n, 0, a->elsz); + return n; + +} + +void +afree(Alloc *a, void *nd) +{ + Next *n; + + if(nd == nil) + return; + n = nd; + qlock(a); + n->next = a->free; + a->free = n; + a->nfree++; + qunlock(a); +} + +static void +xaddelem(Path *p, Memblk *f) +{ + if(p->nf == p->naf){ + p->naf += Incr; + p->f = realloc(p->f, p->naf*sizeof p->f[0]); + } + p->f[p->nf++] = f; + incref(f); +} + +static Path* +duppath(Path *p) +{ + Path *np; + int i; + + np = newpath(p->f[0]); + for(i = 1; i < p->nf; i++) + xaddelem(np, p->f[i]); + return np; +} + +void +ownpath(Path **pp) +{ + Path *p; + + p = *pp; + if(p->ref > 1){ + *pp = duppath(p); + putpath(p); + } +} + +Path* +addelem(Path **pp, Memblk *f) +{ + Path *p; + + ownpath(pp); + p = *pp; + xaddelem(p, f); + return p; +} + +Path* +dropelem(Path **pp) +{ + Path *p; + + ownpath(pp); + p = *pp; + if(p->nf > 0) + mbput(p->f[--p->nf]); + return p; +} + +Path* +newpath(Memblk *root) +{ + Path *p; + + p = anew(&pathalloc); + p->ref = 1; + xaddelem(p, root); + return p; +} + +void +putpath(Path *p) +{ + int i; + + if(p == nil || decref(p) > 0) + return; + for(i = 0; i < p->nf; i++) + mbput(p->f[i]); + p->nf = 0; + afree(&pathalloc, p); +} + +Path* +clonepath(Path *p) +{ + incref(p); + return p; +} +